poe-code 5.0.4 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +3457 -3542
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safejs/dist/interp/async.d.ts +2 -1
- package/packages/safejs/dist/interp/async.js +56 -33
- package/packages/safejs/dist/interp/cancel.d.ts +7 -2
- package/packages/safejs/dist/interp/cancel.js +128 -92
- package/packages/safejs/dist/interp/exceptions.js +7 -3
- package/packages/safejs/dist/interp/globals/collections.js +2 -0
- package/packages/safejs/dist/interp/globals/console-json.js +4 -0
- package/packages/safejs/dist/interp/globals/error.js +1 -5
- package/packages/safejs/dist/interp/globals/math.js +2 -4
- package/packages/safejs/dist/interp/globals/misc.js +5 -0
- package/packages/safejs/dist/interp/globals/object-array.js +25 -0
- package/packages/safejs/dist/interp/globals/regex.js +1 -1
- package/packages/safejs/dist/interp/host-bridge.js +8 -5
- package/packages/safejs/dist/interp/interpreter.js +33 -37
- package/packages/safejs/dist/interp/iteration.d.ts +3 -3
- package/packages/safejs/dist/interp/iteration.js +20 -9
- package/packages/safejs/dist/interp/methods/array.js +1 -0
- package/packages/safejs/dist/interp/methods/function.d.ts +1 -1
- package/packages/safejs/dist/interp/methods/function.js +17 -1
- package/packages/safejs/dist/interp/methods/generator.js +1 -0
- package/packages/safejs/dist/interp/methods/map.js +1 -0
- package/packages/safejs/dist/interp/methods/number.js +1 -0
- package/packages/safejs/dist/interp/methods/regex.js +1 -0
- package/packages/safejs/dist/interp/methods/set.js +1 -0
- package/packages/safejs/dist/interp/methods/string.js +1 -0
- package/packages/safejs/dist/interp/patterns.d.ts +1 -0
- package/packages/safejs/dist/interp/patterns.js +1 -1
- package/packages/safejs/dist/interp/promise-tracker.d.ts +12 -2
- package/packages/safejs/dist/interp/promise-tracker.js +65 -7
- package/packages/safejs/dist/interp/promise.d.ts +7 -5
- package/packages/safejs/dist/interp/promise.js +531 -148
- package/packages/safejs/dist/interp/values.d.ts +6 -0
- package/packages/safejs/dist/interp/values.js +25 -5
- package/packages/safejs/dist/interp/var-hoist.js +5 -135
- package/packages/safejs/dist/lint/index.js +0 -4
- package/packages/safejs/dist/lint/rules/AS001.js +4 -9
- package/packages/safejs/dist/lint/rules/AS003.js +47 -7
- package/packages/safejs/dist/lint/rules/AS006-007.js +48 -7
- package/packages/safejs/dist/parse/bindings.d.ts +6 -0
- package/packages/safejs/dist/parse/bindings.js +90 -0
- package/packages/safejs/dist/parse/parser.js +70 -37
- package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -1
- package/packages/safejs/dist/snapshot/dump-format.js +1 -1
- package/packages/safejs/dist/snapshot/replay-data.js +3 -0
- package/packages/safejs/dist/lint/rules/AS-await-non-promise.d.ts +0 -13
- package/packages/safejs/dist/lint/rules/AS-await-non-promise.js +0 -301
- package/packages/safejs/dist/lint/rules/AS008.d.ts +0 -13
- package/packages/safejs/dist/lint/rules/AS008.js +0 -386
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ArrowFunctionExpression, AwaitExpression, FunctionDeclaration, FunctionExpression, ParseResult, SourceSpan } from "../parse.js";
|
|
1
|
+
import type { ArrowFunctionExpression, AwaitExpression, BlockStatement, FunctionDeclaration, FunctionExpression, ParseResult, SourceSpan } from "../parse.js";
|
|
2
2
|
import { type GeneratorCompletion } from "./generator.js";
|
|
3
3
|
import { type OtelSpan } from "../observability/otel.js";
|
|
4
4
|
import type { Budget } from "./budget.js";
|
|
@@ -25,6 +25,7 @@ export type AsyncEvaluationContext = {
|
|
|
25
25
|
onSuspend?: () => void;
|
|
26
26
|
captureReplayState?: () => unknown;
|
|
27
27
|
rootNode?: ParseResult;
|
|
28
|
+
functionBody?: BlockStatement;
|
|
28
29
|
restoredLoopIterations: Map<number, LoopIterationSnapshot>;
|
|
29
30
|
resumeTarget?: {
|
|
30
31
|
nodeId?: number;
|
|
@@ -5,7 +5,8 @@ import { getThenable, resolveSandboxValue } from "./promise.js";
|
|
|
5
5
|
import { runAsyncPrefix, suspendJob } from "./jobs.js";
|
|
6
6
|
import { awaitSandboxValue } from "./cancel.js";
|
|
7
7
|
import { hoistVarDeclarations } from "./var-hoist.js";
|
|
8
|
-
import {
|
|
8
|
+
import { boundIdentifiers, containsParameterExpression, hoistedVarDeclarations } from "../parse/bindings.js";
|
|
9
|
+
import { createSandboxArguments, createSandboxClosure, createSandboxGenerator, createSandboxPromise, allocateProducedSandboxValue, isSandboxPromise } from "./values.js";
|
|
9
10
|
export function emitResumeBreakpoint(context, breakpoint) {
|
|
10
11
|
context.onYield?.({
|
|
11
12
|
...breakpoint,
|
|
@@ -53,6 +54,7 @@ export function createInterpretedClosure(node, context, evaluateNode) {
|
|
|
53
54
|
}
|
|
54
55
|
: undefined;
|
|
55
56
|
return createSandboxClosure({
|
|
57
|
+
sandbox: true,
|
|
56
58
|
...(node.async ? { async: true } : {}),
|
|
57
59
|
...(node.type === "FunctionDeclaration" || node.type === "FunctionExpression"
|
|
58
60
|
? node.id === undefined
|
|
@@ -72,16 +74,34 @@ export function createInterpretedClosure(node, context, evaluateNode) {
|
|
|
72
74
|
const synchronousPrefix = new Promise((resolve) => {
|
|
73
75
|
completePrefix = resolve;
|
|
74
76
|
});
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
const promise = new Promise((resolve, reject) => {
|
|
78
|
+
runAsyncPrefix(async () => {
|
|
79
|
+
try {
|
|
80
|
+
const value = await executeClosure(node, args, callContext?.thisValue, { ...invocationContext, onSuspend: completePrefix }, evaluateNode);
|
|
81
|
+
resolve(isSandboxPromise(value) || getThenable(value) !== undefined
|
|
82
|
+
? awaitSandboxValue(createSandboxPromise(resolveSandboxValue(value, { budget: context.budget }), {
|
|
83
|
+
trackReplay: false
|
|
84
|
+
}), context.signal, context.budget)
|
|
85
|
+
: allocateProducedSandboxValue(value, context.budget));
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
reject(error);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
completePrefix();
|
|
92
|
+
}
|
|
93
|
+
}).catch((error) => {
|
|
94
|
+
reject(error);
|
|
95
|
+
completePrefix();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
return createSandboxPromise(promise, { synchronousPrefix });
|
|
80
99
|
}
|
|
81
100
|
});
|
|
82
101
|
}
|
|
83
102
|
function createGeneratorClosure(node, context, evaluateNode) {
|
|
84
103
|
return createSandboxClosure({
|
|
104
|
+
sandbox: true,
|
|
85
105
|
...(node.id === undefined ? {} : { name: node.id.name }),
|
|
86
106
|
retainedValues: () => context.scope.retainedValues(),
|
|
87
107
|
call: async (args, callContext) => {
|
|
@@ -93,6 +113,7 @@ function createGeneratorClosure(node, context, evaluateNode) {
|
|
|
93
113
|
const channel = createGeneratorChannel(async (generatorYield) => {
|
|
94
114
|
const result = await evaluateNode(node.body, {
|
|
95
115
|
...closureContext,
|
|
116
|
+
functionBody: node.body,
|
|
96
117
|
generatorYield: (value, yieldNodeId) => {
|
|
97
118
|
generator.state = "suspended";
|
|
98
119
|
return generatorYield(value, yieldNodeId);
|
|
@@ -136,7 +157,7 @@ export async function evaluateAwaitExpression(node, context, evaluateNode) {
|
|
|
136
157
|
return {
|
|
137
158
|
kind: "normal",
|
|
138
159
|
hasValue: true,
|
|
139
|
-
value: await suspendJob(awaitSandboxValue(argument.value, context.signal))
|
|
160
|
+
value: await suspendJob(awaitSandboxValue(argument.value, context.signal, context.budget))
|
|
140
161
|
};
|
|
141
162
|
}
|
|
142
163
|
finally {
|
|
@@ -147,6 +168,7 @@ async function executeClosure(node, args, thisValue, context, evaluateNode) {
|
|
|
147
168
|
const scope = await createClosureScope(node, args, thisValue, context, evaluateNode);
|
|
148
169
|
const result = await evaluateNode(node.body, {
|
|
149
170
|
...context,
|
|
171
|
+
functionBody: node.body.type === "BlockStatement" ? node.body : undefined,
|
|
150
172
|
scope
|
|
151
173
|
});
|
|
152
174
|
if (result.kind === "error") {
|
|
@@ -168,16 +190,32 @@ async function createClosureScope(node, args, thisValue, context, evaluateNode)
|
|
|
168
190
|
scope.declare("arguments", "let", createSandboxArguments(args));
|
|
169
191
|
}
|
|
170
192
|
await bindParameters(node.params, args, scope, context, evaluateNode);
|
|
171
|
-
|
|
172
|
-
|
|
193
|
+
const bodyScope = node.params.some(containsParameterExpression)
|
|
194
|
+
? scope.child({}, { functionBoundary: true })
|
|
195
|
+
: scope;
|
|
196
|
+
hoistVarDeclarations(node.body, bodyScope);
|
|
197
|
+
if (bodyScope !== scope) {
|
|
198
|
+
for (const declaration of hoistedVarDeclarations([node.body])) {
|
|
199
|
+
for (const declarator of declaration.declarations) {
|
|
200
|
+
for (const identifier of boundIdentifiers(declarator.id)) {
|
|
201
|
+
if (!scope.hasOwnBinding(identifier.name))
|
|
202
|
+
continue;
|
|
203
|
+
const parameter = scope.lookup(identifier.name);
|
|
204
|
+
if (parameter.found)
|
|
205
|
+
bodyScope.assign(identifier.name, parameter.value);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return bodyScope;
|
|
173
211
|
}
|
|
174
212
|
function isBlockBody(body) {
|
|
175
213
|
return body.type === "BlockStatement";
|
|
176
214
|
}
|
|
177
215
|
async function bindParameters(params, args, scope, context, evaluateNode) {
|
|
178
216
|
for (const param of params) {
|
|
179
|
-
for (const
|
|
180
|
-
scope.predeclare(name, "
|
|
217
|
+
for (const identifier of boundIdentifiers(param)) {
|
|
218
|
+
scope.predeclare(identifier.name, "var");
|
|
181
219
|
}
|
|
182
220
|
}
|
|
183
221
|
for (let index = 0; index < params.length; index += 1) {
|
|
@@ -185,7 +223,7 @@ async function bindParameters(params, args, scope, context, evaluateNode) {
|
|
|
185
223
|
if (param.type === "RestElement") {
|
|
186
224
|
const rest = args.slice(index);
|
|
187
225
|
context.budget.allocateArrayLength(rest.length);
|
|
188
|
-
const binding = await bindPattern(param, rest, { kind: "
|
|
226
|
+
const binding = await bindPattern(param, rest, { kind: "var", initialize: true }, scope, {
|
|
189
227
|
evaluate: (defaultNode) => evaluateNode(defaultNode, { ...context, scope })
|
|
190
228
|
});
|
|
191
229
|
if (!binding.ok) {
|
|
@@ -198,7 +236,7 @@ async function bindParameters(params, args, scope, context, evaluateNode) {
|
|
|
198
236
|
}
|
|
199
237
|
return;
|
|
200
238
|
}
|
|
201
|
-
const binding = await bindPattern(param, args[index], { kind: "
|
|
239
|
+
const binding = await bindPattern(param, args[index], { kind: "var", initialize: true }, scope, {
|
|
202
240
|
evaluate: (defaultNode) => evaluateNode(defaultNode, { ...context, scope })
|
|
203
241
|
});
|
|
204
242
|
if (!binding.ok) {
|
|
@@ -211,27 +249,12 @@ async function bindParameters(params, args, scope, context, evaluateNode) {
|
|
|
211
249
|
}
|
|
212
250
|
}
|
|
213
251
|
}
|
|
214
|
-
function getParameterBindingNames(pattern) {
|
|
215
|
-
switch (pattern.type) {
|
|
216
|
-
case "Identifier":
|
|
217
|
-
return [pattern.name];
|
|
218
|
-
case "MemberExpression":
|
|
219
|
-
return [];
|
|
220
|
-
case "AssignmentPattern":
|
|
221
|
-
return getParameterBindingNames(pattern.left);
|
|
222
|
-
case "RestElement":
|
|
223
|
-
return getParameterBindingNames(pattern.argument);
|
|
224
|
-
case "ArrayPattern":
|
|
225
|
-
return pattern.elements.flatMap((element) => element === null ? [] : getParameterBindingNames(element));
|
|
226
|
-
case "ObjectPattern":
|
|
227
|
-
return pattern.properties.flatMap((property) => property.type === "RestElement"
|
|
228
|
-
? getParameterBindingNames(property)
|
|
229
|
-
: getParameterBindingNames(property.value));
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
252
|
export function normalizeClosureResult(result, budget) {
|
|
233
|
-
if (isSandboxPromise(result)
|
|
253
|
+
if (isSandboxPromise(result) &&
|
|
254
|
+
(result.synchronousPrefix !== undefined || result.hostCall !== undefined)) {
|
|
234
255
|
return result;
|
|
235
256
|
}
|
|
236
|
-
return createSandboxPromise(resolveSandboxValue(result, { budget })
|
|
257
|
+
return createSandboxPromise(resolveSandboxValue(result, { budget }), {
|
|
258
|
+
...(isSandboxPromise(result) ? { synchronousPrefix: Promise.resolve() } : {})
|
|
259
|
+
});
|
|
237
260
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import { type SandboxValue } from "./values.js";
|
|
1
|
+
import { type SandboxCallContext, type SandboxClosure, type SandboxPromise, type SandboxValue } from "./values.js";
|
|
2
|
+
import type { Budget } from "./budget.js";
|
|
3
|
+
export declare function withCancellationSignal<T>(signal: AbortSignal | undefined, task: () => T): T;
|
|
4
|
+
export declare function invokeCancelableClosure(closure: SandboxClosure, operation: SandboxClosure["call"], args: readonly SandboxValue[], context?: SandboxCallContext, construct?: boolean): SandboxValue | Promise<SandboxValue>;
|
|
2
5
|
export declare function wrapCancelableBindings(bindings: Record<string, SandboxValue>, signal?: AbortSignal): Record<string, SandboxValue>;
|
|
3
|
-
export declare function
|
|
6
|
+
export declare function registerPromiseCancellation(promise: SandboxPromise): void;
|
|
7
|
+
export declare function readPromiseCancellation(promise: SandboxPromise, original: Promise<SandboxValue>): Promise<SandboxValue>;
|
|
8
|
+
export declare function awaitSandboxValue(value: SandboxValue, signal?: AbortSignal, budget?: Budget): Promise<SandboxValue>;
|
|
@@ -1,90 +1,139 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { createSandboxPromise, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet } from "./values.js";
|
|
3
|
+
import { assertPromiseExecutionAllowed, interruptOnFatalPromiseRejection, onFatalPromiseRejection, observeSandboxPromise, trackSandboxPromise } from "./promise-tracker.js";
|
|
3
4
|
import { replaceErrorStack } from "../error/shape.js";
|
|
4
|
-
import { resolveSandboxValue } from "./promise.js";
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
return wrapCancelableValue(bindings, signal, new WeakMap());
|
|
5
|
+
import { consumeSettledHostCall, resolveSandboxValue } from "./promise.js";
|
|
6
|
+
const activeCancellation = new AsyncLocalStorage();
|
|
7
|
+
const sandboxPromises = new WeakSet();
|
|
8
|
+
const cancelableOutcomes = new WeakMap();
|
|
9
|
+
export function withCancellationSignal(signal, task) {
|
|
10
|
+
return activeCancellation.run({ signal, host: false }, task);
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
export function invokeCancelableClosure(closure, operation, args, context, construct = false) {
|
|
13
|
+
assertPromiseExecutionAllowed();
|
|
14
|
+
const active = activeCancellation.getStore();
|
|
15
|
+
if (active === undefined)
|
|
16
|
+
return operation(args, context);
|
|
17
|
+
if (closure.sandbox === true) {
|
|
18
|
+
return activeCancellation.run({ signal: active.signal, host: false }, () => operation(args, context));
|
|
15
19
|
}
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
const signal = active.signal;
|
|
21
|
+
const managed = signal !== undefined && closure.cancellationSignal === signal;
|
|
22
|
+
if (signal?.aborted && !managed) {
|
|
23
|
+
if (!construct && closure.async === true)
|
|
24
|
+
return createRejectedSandboxPromise(readAbortReason(signal));
|
|
25
|
+
throw readAbortReason(signal);
|
|
19
26
|
}
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
return activeCancellation.run({ signal, host: true }, () => {
|
|
28
|
+
const result = operation(args, context);
|
|
29
|
+
if (signal === undefined || managed)
|
|
30
|
+
return result;
|
|
31
|
+
if (isPromiseLike(result))
|
|
32
|
+
return awaitWithSignal(result, signal).then((value) => {
|
|
33
|
+
registerCancelablePromises(value, signal);
|
|
34
|
+
return value;
|
|
35
|
+
});
|
|
36
|
+
registerCancelablePromises(result, signal);
|
|
37
|
+
return result;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export function wrapCancelableBindings(bindings, signal) {
|
|
41
|
+
if (signal !== undefined)
|
|
42
|
+
registerCancelablePromises(bindings, signal);
|
|
43
|
+
return bindings;
|
|
44
|
+
}
|
|
45
|
+
export function registerPromiseCancellation(promise) {
|
|
46
|
+
const active = activeCancellation.getStore();
|
|
47
|
+
if (active?.host === false)
|
|
48
|
+
sandboxPromises.add(promise);
|
|
49
|
+
else if (active?.signal !== undefined)
|
|
50
|
+
managePromiseCancellation(promise, active.signal);
|
|
51
|
+
}
|
|
52
|
+
export function readPromiseCancellation(promise, original) {
|
|
53
|
+
const signal = activeCancellation.getStore()?.signal;
|
|
54
|
+
return signal === undefined
|
|
55
|
+
? original
|
|
56
|
+
: (cancelableOutcomes.get(promise)?.get(signal) ?? original);
|
|
57
|
+
}
|
|
58
|
+
function managePromiseCancellation(promise, signal) {
|
|
59
|
+
let outcomes = cancelableOutcomes.get(promise);
|
|
60
|
+
if (outcomes?.has(signal))
|
|
61
|
+
return;
|
|
62
|
+
if (outcomes === undefined) {
|
|
63
|
+
outcomes = new WeakMap();
|
|
64
|
+
cancelableOutcomes.set(promise, outcomes);
|
|
65
|
+
}
|
|
66
|
+
const outcome = awaitWithSignal(promise.promise, signal);
|
|
67
|
+
outcomes.set(signal, outcome);
|
|
68
|
+
trackSandboxPromise(promise, outcome);
|
|
69
|
+
}
|
|
70
|
+
function registerCancelablePromises(value, signal) {
|
|
71
|
+
const seen = new WeakSet();
|
|
72
|
+
const pending = [value];
|
|
73
|
+
while (pending.length > 0) {
|
|
74
|
+
const current = pending.pop();
|
|
75
|
+
if (typeof current !== "object" || current === null || seen.has(current))
|
|
76
|
+
continue;
|
|
77
|
+
seen.add(current);
|
|
78
|
+
if (isSandboxPromise(current)) {
|
|
79
|
+
if (!sandboxPromises.has(current))
|
|
80
|
+
managePromiseCancellation(current, signal);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (isSandboxClosure(current)) {
|
|
84
|
+
if (current.properties !== undefined)
|
|
85
|
+
pending.push(current.properties);
|
|
86
|
+
}
|
|
87
|
+
else if (isSandboxMap(current)) {
|
|
88
|
+
for (const [key, entry] of current.entries)
|
|
89
|
+
pending.push(key, entry);
|
|
90
|
+
}
|
|
91
|
+
else if (isSandboxSet(current)) {
|
|
92
|
+
for (const entry of current.values)
|
|
93
|
+
pending.push(entry);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(current))) {
|
|
97
|
+
if ("value" in descriptor)
|
|
98
|
+
pending.push(descriptor.value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
36
101
|
}
|
|
102
|
+
}
|
|
103
|
+
export function awaitSandboxValue(value, signal, budget) {
|
|
104
|
+
let resolved;
|
|
37
105
|
if (isSandboxPromise(value)) {
|
|
38
|
-
// The wrapper delivers this promise's rejection to the sandbox in its place, so
|
|
39
|
-
// the sandbox never awaits the original and it would otherwise read as unhandled.
|
|
40
106
|
observeSandboxPromise(value);
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
107
|
+
const outcome = signal === undefined
|
|
108
|
+
? value.promise
|
|
109
|
+
: (cancelableOutcomes.get(value)?.get(signal) ?? value.promise);
|
|
110
|
+
resolved = new Promise((resolve, reject) => {
|
|
111
|
+
const detach = onFatalPromiseRejection(reject);
|
|
112
|
+
const complete = (state, result) => {
|
|
113
|
+
detach?.();
|
|
114
|
+
try {
|
|
115
|
+
consumeSettledHostCall(value);
|
|
116
|
+
if (state === "fulfilled")
|
|
117
|
+
resolve(result);
|
|
118
|
+
else
|
|
119
|
+
reject(result);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
reject(error);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
outcome.then((result) => complete("fulfilled", result), (reason) => complete("rejected", reason));
|
|
49
126
|
});
|
|
50
|
-
seen.set(value, wrapped);
|
|
51
|
-
cancellationSignals.set(wrapped, signal);
|
|
52
|
-
return wrapped;
|
|
53
|
-
}
|
|
54
|
-
if (Array.isArray(value)) {
|
|
55
|
-
const wrapped = new Array(value.length);
|
|
56
|
-
seen.set(value, wrapped);
|
|
57
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
58
|
-
wrapped[index] = wrapCancelableValue(value[index], signal, seen);
|
|
59
|
-
}
|
|
60
|
-
return wrapped;
|
|
61
127
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
65
|
-
wrapped[key] = wrapCancelableValue(entry, signal, seen);
|
|
128
|
+
else {
|
|
129
|
+
resolved = resolveSandboxValue(value, { budget });
|
|
66
130
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!isPromiseLike(result)) {
|
|
71
|
-
return wrapCancelableValue(result, signal, seen);
|
|
72
|
-
}
|
|
73
|
-
return wrapCancelablePromise(result, signal, seen);
|
|
74
|
-
}
|
|
75
|
-
function wrapCancelablePromise(promise, signal, seen) {
|
|
76
|
-
return awaitWithSignal(promise, signal).then((value) => wrapCancelableValue(value, signal, seen), (reason) => {
|
|
77
|
-
if (signal.aborted && reason === signal.reason)
|
|
78
|
-
throw reason;
|
|
79
|
-
throw wrapCancelableUnknown(reason, signal, seen);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
export function awaitSandboxValue(value, signal) {
|
|
83
|
-
const resolved = resolveSandboxValue(value);
|
|
84
|
-
return isSandboxPromise(value) &&
|
|
85
|
-
(value.synchronousPrefix !== undefined || cancellationSignals.get(value) === signal)
|
|
131
|
+
const pending = isSandboxPromise(value) &&
|
|
132
|
+
(value.synchronousPrefix !== undefined ||
|
|
133
|
+
(signal !== undefined && cancelableOutcomes.get(value)?.has(signal)))
|
|
86
134
|
? resolved
|
|
87
135
|
: awaitWithSignal(resolved, signal);
|
|
136
|
+
return isSandboxPromise(value) ? pending : interruptOnFatalPromiseRejection(pending);
|
|
88
137
|
}
|
|
89
138
|
function awaitWithSignal(promise, signal) {
|
|
90
139
|
if (signal === undefined)
|
|
@@ -125,22 +174,6 @@ function awaitWithSignal(promise, signal) {
|
|
|
125
174
|
});
|
|
126
175
|
});
|
|
127
176
|
}
|
|
128
|
-
function wrapCancelableUnknown(value, signal, seen) {
|
|
129
|
-
if (value === null ||
|
|
130
|
-
value === undefined ||
|
|
131
|
-
typeof value === "string" ||
|
|
132
|
-
typeof value === "number" ||
|
|
133
|
-
typeof value === "boolean") {
|
|
134
|
-
return value;
|
|
135
|
-
}
|
|
136
|
-
if (isSandboxClosure(value) ||
|
|
137
|
-
isSandboxPromise(value) ||
|
|
138
|
-
Array.isArray(value) ||
|
|
139
|
-
(typeof value === "object" && value !== null)) {
|
|
140
|
-
return wrapCancelableValue(value, signal, seen);
|
|
141
|
-
}
|
|
142
|
-
return value;
|
|
143
|
-
}
|
|
144
177
|
function readAbortReason(signal) {
|
|
145
178
|
return signal.reason ?? createAbortError();
|
|
146
179
|
}
|
|
@@ -161,5 +194,8 @@ function createRejectedSandboxPromise(reason) {
|
|
|
161
194
|
return createSandboxPromise(promise);
|
|
162
195
|
}
|
|
163
196
|
function isPromiseLike(value) {
|
|
164
|
-
return typeof value === "object" &&
|
|
197
|
+
return (typeof value === "object" &&
|
|
198
|
+
value !== null &&
|
|
199
|
+
"then" in value &&
|
|
200
|
+
typeof value.then === "function");
|
|
165
201
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { attachErrorSpan, attachWrappedErrorCause, describeThrownValue, formatErrorStack, readErrorCause, readErrorSpan } from "../error/shape.js";
|
|
2
2
|
import { SandboxError } from "./budget.js";
|
|
3
3
|
import { HostCallResumabilityError } from "./host-call.js";
|
|
4
|
+
import { withFatalPromiseCleanup } from "./promise-tracker.js";
|
|
4
5
|
import { deepCopyToSandbox } from "./values.js";
|
|
5
6
|
const capturedExceptionBrand = Symbol("CapturedException");
|
|
6
7
|
const sandboxErrorBrand = Symbol("SandboxError");
|
|
@@ -50,9 +51,12 @@ export async function evaluateTryStatement(node, context, evaluateNode) {
|
|
|
50
51
|
if (node.finalizer === undefined || tryOrCatchResult.kind === "error") {
|
|
51
52
|
return tryOrCatchResult;
|
|
52
53
|
}
|
|
53
|
-
const
|
|
54
|
-
?
|
|
55
|
-
:
|
|
54
|
+
const evaluateFinalizer = () => fatalBudgetError?.budget === "deadline"
|
|
55
|
+
? evaluateWithoutDeadlineChecks(context, () => evaluateBlockCompletion(node.finalizer, context, evaluateNode))
|
|
56
|
+
: evaluateBlockCompletion(node.finalizer, context, evaluateNode);
|
|
57
|
+
const finalizerResult = await (fatalBudgetError === undefined
|
|
58
|
+
? evaluateFinalizer()
|
|
59
|
+
: withFatalPromiseCleanup(evaluateFinalizer));
|
|
56
60
|
if (fatalBudgetError !== undefined) {
|
|
57
61
|
throw fatalBudgetError;
|
|
58
62
|
}
|
|
@@ -4,6 +4,7 @@ const mapConstructors = new WeakSet();
|
|
|
4
4
|
const setConstructors = new WeakSet();
|
|
5
5
|
export function createCollectionGlobals(options) {
|
|
6
6
|
const mapConstructor = createSandboxClosure({
|
|
7
|
+
sandbox: true,
|
|
7
8
|
call: () => {
|
|
8
9
|
throw new TypeError("Constructor Map requires 'new'.");
|
|
9
10
|
},
|
|
@@ -17,6 +18,7 @@ export function createCollectionGlobals(options) {
|
|
|
17
18
|
name: "Map"
|
|
18
19
|
});
|
|
19
20
|
const setConstructor = createSandboxClosure({
|
|
21
|
+
sandbox: true,
|
|
20
22
|
call: () => {
|
|
21
23
|
throw new TypeError("Constructor Set requires 'new'.");
|
|
22
24
|
},
|
|
@@ -5,10 +5,12 @@ export function createConsoleJsonGlobals(options) {
|
|
|
5
5
|
return {
|
|
6
6
|
JSON: {
|
|
7
7
|
parse: createSandboxClosure({
|
|
8
|
+
sandbox: true,
|
|
8
9
|
call: async ([text]) => parseJson(text, options.budget),
|
|
9
10
|
name: "parse"
|
|
10
11
|
}),
|
|
11
12
|
stringify: createSandboxClosure({
|
|
13
|
+
sandbox: true,
|
|
12
14
|
call: async ([value, replacer, indent]) => stringifyJson(value, replacer, indent, options.budget),
|
|
13
15
|
name: "stringify"
|
|
14
16
|
})
|
|
@@ -16,6 +18,7 @@ export function createConsoleJsonGlobals(options) {
|
|
|
16
18
|
console: options.hostCalls === undefined
|
|
17
19
|
? {
|
|
18
20
|
error: createSandboxClosure({
|
|
21
|
+
sandbox: true,
|
|
19
22
|
call: async (args) => {
|
|
20
23
|
sink.error(...args.map((value) => deepCopyFromSandbox(value)));
|
|
21
24
|
return undefined;
|
|
@@ -23,6 +26,7 @@ export function createConsoleJsonGlobals(options) {
|
|
|
23
26
|
name: "error"
|
|
24
27
|
}),
|
|
25
28
|
log: createSandboxClosure({
|
|
29
|
+
sandbox: true,
|
|
26
30
|
call: async (args) => {
|
|
27
31
|
sink.log(...args.map((value) => deepCopyFromSandbox(value)));
|
|
28
32
|
return undefined;
|
|
@@ -27,11 +27,7 @@ export function isSandboxErrorConstructor(value) {
|
|
|
27
27
|
}
|
|
28
28
|
function createErrorConstructor(name, budget) {
|
|
29
29
|
const call = (args, context) => createSubsetError(name, args, context?.stack ?? [], budget);
|
|
30
|
-
const closure = createSandboxClosure({
|
|
31
|
-
call,
|
|
32
|
-
construct: call,
|
|
33
|
-
name
|
|
34
|
-
});
|
|
30
|
+
const closure = createSandboxClosure({ sandbox: true, call, construct: call, name });
|
|
35
31
|
errorConstructorNames.set(closure, name);
|
|
36
32
|
return closure;
|
|
37
33
|
}
|
|
@@ -46,13 +46,11 @@ export function createMathGlobals(options = {}) {
|
|
|
46
46
|
PI: Math.PI,
|
|
47
47
|
SQRT1_2: Math.SQRT1_2,
|
|
48
48
|
SQRT2: Math.SQRT2,
|
|
49
|
-
random: createSandboxClosure({
|
|
50
|
-
call: () => random(),
|
|
51
|
-
name: "random"
|
|
52
|
-
})
|
|
49
|
+
random: createSandboxClosure({ sandbox: true, call: () => random(), name: "random" })
|
|
53
50
|
};
|
|
54
51
|
for (const [name, method] of Object.entries(mathMethods)) {
|
|
55
52
|
mathObject[name] = createSandboxClosure({
|
|
53
|
+
sandbox: true,
|
|
56
54
|
call: (args) => Reflect.apply(method, Math, args),
|
|
57
55
|
name
|
|
58
56
|
});
|
|
@@ -3,22 +3,27 @@ import { allocateProducedSandboxValue, cloneSandboxValue, createSandboxClosure,
|
|
|
3
3
|
export function createMiscGlobals(options) {
|
|
4
4
|
return {
|
|
5
5
|
structuredClone: createSandboxClosure({
|
|
6
|
+
sandbox: true,
|
|
6
7
|
call: ([value]) => structuredCloneSandboxValue(value, options.budget),
|
|
7
8
|
name: "structuredClone"
|
|
8
9
|
}),
|
|
9
10
|
parseInt: createSandboxClosure({
|
|
11
|
+
sandbox: true,
|
|
10
12
|
call: (args) => Reflect.apply(globalThis.parseInt, globalThis, [...args]),
|
|
11
13
|
name: "parseInt"
|
|
12
14
|
}),
|
|
13
15
|
parseFloat: createSandboxClosure({
|
|
16
|
+
sandbox: true,
|
|
14
17
|
call: (args) => Reflect.apply(globalThis.parseFloat, globalThis, [...args]),
|
|
15
18
|
name: "parseFloat"
|
|
16
19
|
}),
|
|
17
20
|
isNaN: createSandboxClosure({
|
|
21
|
+
sandbox: true,
|
|
18
22
|
call: ([value]) => globalThis.isNaN(value),
|
|
19
23
|
name: "isNaN"
|
|
20
24
|
}),
|
|
21
25
|
isFinite: createSandboxClosure({
|
|
26
|
+
sandbox: true,
|
|
22
27
|
call: ([value]) => globalThis.isFinite(value),
|
|
23
28
|
name: "isFinite"
|
|
24
29
|
})
|