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
|
@@ -4,30 +4,37 @@ export function createObjectArrayGlobals(options) {
|
|
|
4
4
|
return {
|
|
5
5
|
Object: {
|
|
6
6
|
keys: createSandboxClosure({
|
|
7
|
+
sandbox: true,
|
|
7
8
|
call: ([value]) => budgetSandboxValue(getOwnEnumerableKeys(value), options.budget),
|
|
8
9
|
name: "keys"
|
|
9
10
|
}),
|
|
10
11
|
values: createSandboxClosure({
|
|
12
|
+
sandbox: true,
|
|
11
13
|
call: ([value]) => budgetSandboxValue(getOwnEnumerableValues(value), options.budget),
|
|
12
14
|
name: "values"
|
|
13
15
|
}),
|
|
14
16
|
entries: createSandboxClosure({
|
|
17
|
+
sandbox: true,
|
|
15
18
|
call: ([value]) => budgetSandboxValue(getOwnEnumerableEntries(value), options.budget),
|
|
16
19
|
name: "entries"
|
|
17
20
|
}),
|
|
18
21
|
hasOwn: createSandboxClosure({
|
|
22
|
+
sandbox: true,
|
|
19
23
|
call: ([value, key]) => Reflect.apply(Object.hasOwn, Object, [value, key]),
|
|
20
24
|
name: "hasOwn"
|
|
21
25
|
}),
|
|
22
26
|
is: createSandboxClosure({
|
|
27
|
+
sandbox: true,
|
|
23
28
|
call: ([left, right]) => Reflect.apply(Object.is, Object, [left, right]),
|
|
24
29
|
name: "is"
|
|
25
30
|
}),
|
|
26
31
|
fromEntries: createSandboxClosure({
|
|
32
|
+
sandbox: true,
|
|
27
33
|
call: ([value]) => budgetSandboxValue(Reflect.apply(Object.fromEntries, Object, [value]), options.budget),
|
|
28
34
|
name: "fromEntries"
|
|
29
35
|
}),
|
|
30
36
|
freeze: createSandboxClosure({
|
|
37
|
+
sandbox: true,
|
|
31
38
|
call: ([value]) => {
|
|
32
39
|
if (typeof value === "object" && value !== null) {
|
|
33
40
|
Object.freeze(value);
|
|
@@ -37,76 +44,93 @@ export function createObjectArrayGlobals(options) {
|
|
|
37
44
|
name: "freeze"
|
|
38
45
|
}),
|
|
39
46
|
isFrozen: createSandboxClosure({
|
|
47
|
+
sandbox: true,
|
|
40
48
|
call: ([value]) => Object.isFrozen(value),
|
|
41
49
|
name: "isFrozen"
|
|
42
50
|
}),
|
|
43
51
|
assign: createSandboxClosure({
|
|
52
|
+
sandbox: true,
|
|
44
53
|
call: ([target, ...sources]) => assignSandboxValues(target, sources),
|
|
45
54
|
name: "assign"
|
|
46
55
|
})
|
|
47
56
|
},
|
|
48
57
|
Array: createSandboxClosure({
|
|
58
|
+
sandbox: true,
|
|
49
59
|
call: (args) => createArrayFromConstructorArgs(args, options.budget),
|
|
50
60
|
construct: (args) => createArrayFromConstructorArgs(args, options.budget),
|
|
51
61
|
name: "Array",
|
|
52
62
|
properties: {
|
|
53
63
|
isArray: createSandboxClosure({
|
|
64
|
+
sandbox: true,
|
|
54
65
|
call: ([value]) => Array.isArray(value),
|
|
55
66
|
name: "isArray"
|
|
56
67
|
}),
|
|
57
68
|
from: createSandboxClosure({
|
|
69
|
+
sandbox: true,
|
|
58
70
|
call: (args) => arrayFromSandboxValues(args, options.budget),
|
|
59
71
|
name: "from"
|
|
60
72
|
}),
|
|
61
73
|
of: createSandboxClosure({
|
|
74
|
+
sandbox: true,
|
|
62
75
|
call: (args) => budgetSandboxValue(Reflect.apply(Array.of, Array, [...args]), options.budget),
|
|
63
76
|
name: "of"
|
|
64
77
|
})
|
|
65
78
|
}
|
|
66
79
|
}),
|
|
67
80
|
String: createSandboxClosure({
|
|
81
|
+
sandbox: true,
|
|
68
82
|
call: ([value]) => options.budget.allocateString(String(value)),
|
|
69
83
|
name: "String",
|
|
70
84
|
properties: {
|
|
71
85
|
raw: createSandboxClosure({
|
|
86
|
+
sandbox: true,
|
|
72
87
|
call: (args) => stringRaw(args, options.budget),
|
|
73
88
|
name: "raw"
|
|
74
89
|
}),
|
|
75
90
|
fromCharCode: createSandboxClosure({
|
|
91
|
+
sandbox: true,
|
|
76
92
|
call: (args) => options.budget.allocateString(Reflect.apply(String.fromCharCode, String, [...args])),
|
|
77
93
|
name: "fromCharCode"
|
|
78
94
|
}),
|
|
79
95
|
fromCodePoint: createSandboxClosure({
|
|
96
|
+
sandbox: true,
|
|
80
97
|
call: (args) => options.budget.allocateString(Reflect.apply(String.fromCodePoint, String, [...args])),
|
|
81
98
|
name: "fromCodePoint"
|
|
82
99
|
})
|
|
83
100
|
}
|
|
84
101
|
}),
|
|
85
102
|
Number: createSandboxClosure({
|
|
103
|
+
sandbox: true,
|
|
86
104
|
call: ([value]) => Number(value),
|
|
87
105
|
name: "Number",
|
|
88
106
|
properties: {
|
|
89
107
|
isFinite: createSandboxClosure({
|
|
108
|
+
sandbox: true,
|
|
90
109
|
call: ([value]) => typeof value === "number" && Number.isFinite(value),
|
|
91
110
|
name: "isFinite"
|
|
92
111
|
}),
|
|
93
112
|
isNaN: createSandboxClosure({
|
|
113
|
+
sandbox: true,
|
|
94
114
|
call: ([value]) => typeof value === "number" && Number.isNaN(value),
|
|
95
115
|
name: "isNaN"
|
|
96
116
|
}),
|
|
97
117
|
isInteger: createSandboxClosure({
|
|
118
|
+
sandbox: true,
|
|
98
119
|
call: ([value]) => typeof value === "number" && Number.isInteger(value),
|
|
99
120
|
name: "isInteger"
|
|
100
121
|
}),
|
|
101
122
|
parseInt: createSandboxClosure({
|
|
123
|
+
sandbox: true,
|
|
102
124
|
call: (args) => Reflect.apply(Number.parseInt, Number, [...args]),
|
|
103
125
|
name: "parseInt"
|
|
104
126
|
}),
|
|
105
127
|
parseFloat: createSandboxClosure({
|
|
128
|
+
sandbox: true,
|
|
106
129
|
call: (args) => Reflect.apply(Number.parseFloat, Number, [...args]),
|
|
107
130
|
name: "parseFloat"
|
|
108
131
|
}),
|
|
109
132
|
isSafeInteger: createSandboxClosure({
|
|
133
|
+
sandbox: true,
|
|
110
134
|
call: ([value]) => typeof value === "number" && Number.isSafeInteger(value),
|
|
111
135
|
name: "isSafeInteger"
|
|
112
136
|
}),
|
|
@@ -118,6 +142,7 @@ export function createObjectArrayGlobals(options) {
|
|
|
118
142
|
}
|
|
119
143
|
}),
|
|
120
144
|
Boolean: createSandboxClosure({
|
|
145
|
+
sandbox: true,
|
|
121
146
|
call: ([value]) => Boolean(value),
|
|
122
147
|
name: "Boolean"
|
|
123
148
|
})
|
|
@@ -2,6 +2,6 @@ import { createSandboxClosure, createSandboxRegex } from "../values.js";
|
|
|
2
2
|
export function createRegexGlobals() {
|
|
3
3
|
const construct = (args) => createSandboxRegex(String(args[0] ?? ""), String(args[1] ?? ""));
|
|
4
4
|
return {
|
|
5
|
-
RegExp: createSandboxClosure({ name: "RegExp", call: construct, construct })
|
|
5
|
+
RegExp: createSandboxClosure({ sandbox: true, name: "RegExp", call: construct, construct })
|
|
6
6
|
};
|
|
7
7
|
}
|
|
@@ -46,8 +46,11 @@ function wrapCallerInjectedFunction(name, value, options, state) {
|
|
|
46
46
|
const callable = value;
|
|
47
47
|
return createSandboxClosure({
|
|
48
48
|
...(isAsyncFunction(callable) ? { async: true } : {}),
|
|
49
|
+
cancellationSignal: options.signal,
|
|
49
50
|
call: (args, context) => {
|
|
50
51
|
try {
|
|
52
|
+
if (options.signal?.aborted)
|
|
53
|
+
throw readAbortReason(options.signal);
|
|
51
54
|
const stackFrames = context?.stack ?? [];
|
|
52
55
|
const callbacks = {
|
|
53
56
|
journal: options.hostCalls,
|
|
@@ -183,12 +186,12 @@ function executeHostCall(record, restored, invoke, stackFrames, options, span, c
|
|
|
183
186
|
}
|
|
184
187
|
})();
|
|
185
188
|
void promise.catch(() => undefined);
|
|
186
|
-
return createSandboxPromise(promise);
|
|
189
|
+
return createSandboxPromise(promise, { hostCall: record, hostCallJournal: hostCalls });
|
|
187
190
|
}
|
|
188
191
|
if (replayed !== undefined) {
|
|
189
192
|
restoreReplayedHostState(record, replayed, onReplay);
|
|
190
193
|
if (record.asynchronous)
|
|
191
|
-
return createReplayedHostCallResult(replayed);
|
|
194
|
+
return createReplayedHostCallResult(replayed, record, hostCalls);
|
|
192
195
|
if (replayed.status === "rejected")
|
|
193
196
|
throw replayed.reason;
|
|
194
197
|
return replayed.value;
|
|
@@ -197,7 +200,7 @@ function executeHostCall(record, restored, invoke, stackFrames, options, span, c
|
|
|
197
200
|
record.policy === "read-side-effect" &&
|
|
198
201
|
record.lifecycle === "consumed" &&
|
|
199
202
|
record.outcome !== undefined) {
|
|
200
|
-
return createReplayedHostCallResult(record.outcome);
|
|
203
|
+
return createReplayedHostCallResult(record.outcome, record, hostCalls);
|
|
201
204
|
}
|
|
202
205
|
if (restored &&
|
|
203
206
|
record.policy === "read-side-effect" &&
|
|
@@ -285,12 +288,12 @@ function restoreReplayedHostState(record, outcome, onReplay) {
|
|
|
285
288
|
throw error;
|
|
286
289
|
}
|
|
287
290
|
}
|
|
288
|
-
function createReplayedHostCallResult(outcome) {
|
|
291
|
+
function createReplayedHostCallResult(outcome, record, hostCalls) {
|
|
289
292
|
const promise = outcome.status === "fulfilled"
|
|
290
293
|
? Promise.resolve(outcome.value)
|
|
291
294
|
: Promise.reject(outcome.reason);
|
|
292
295
|
promise.catch(() => undefined);
|
|
293
|
-
return createSandboxPromise(promise);
|
|
296
|
+
return createSandboxPromise(promise, { hostCall: record, hostCallJournal: hostCalls });
|
|
294
297
|
}
|
|
295
298
|
function createHostCallPromise(record, outcomePromise, hostCalls) {
|
|
296
299
|
const promise = outcomePromise.then((outcome) => {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { promiseReplayContext } from "./promise-replay.js";
|
|
2
|
+
import { assertPromiseExecutionAllowed } from "./promise-tracker.js";
|
|
2
3
|
import { SandboxJobQueue, suspendJob } from "./jobs.js";
|
|
4
|
+
import { withCancellationSignal } from "./cancel.js";
|
|
3
5
|
import { attachErrorSpan, formatErrorStack, readErrorSpan, replaceErrorStack } from "../error/shape.js";
|
|
4
6
|
import { evaluateArrowFunctionExpression, evaluateFunctionExpression, evaluateAwaitExpression, emitResumeBreakpoint, createInterpretedClosure, normalizeClosureResult } from "./async.js";
|
|
5
7
|
import { HostCallResumabilityError } from "./host-call.js";
|
|
@@ -9,7 +11,7 @@ import { callArrayMethod, getArrayMember, isArrayMethodName } from "./methods/ar
|
|
|
9
11
|
import { getFunctionMember } from "./methods/function.js";
|
|
10
12
|
import { callMapMethod, getMapMember, isMapMethodName } from "./methods/map.js";
|
|
11
13
|
import { callNumberMethod, getNumberMember, isNumberMethodName } from "./methods/number.js";
|
|
12
|
-
import { getPromiseMember } from "./promise.js";
|
|
14
|
+
import { getPromiseMember, isSandboxPromiseConstructor } from "./promise.js";
|
|
13
15
|
import { getSandboxIterator } from "./iteration.js";
|
|
14
16
|
import { assertCollectionMutable } from "./running-state.js";
|
|
15
17
|
import { getGeneratorMember } from "./methods/generator.js";
|
|
@@ -108,7 +110,7 @@ export async function interpret(node, options = {}) {
|
|
|
108
110
|
generatorYield: options.generatorYield,
|
|
109
111
|
resumeTarget: { nodeId: options.snapshot?.resumeNodeId }
|
|
110
112
|
};
|
|
111
|
-
const evaluation = await jobs.run(() => evaluateNode(node, context));
|
|
113
|
+
const evaluation = await withCancellationSignal(options.signal, () => jobs.run(() => evaluateNode(node, context)));
|
|
112
114
|
await jobs.drain();
|
|
113
115
|
const snapshot = scope.snapshot();
|
|
114
116
|
reconcileDataBudget(budget, stats, scope, "hasValue" in evaluation && evaluation.hasValue ? evaluation.value : undefined);
|
|
@@ -154,6 +156,7 @@ async function evaluateNode(node, context) {
|
|
|
154
156
|
const replayWait = promiseReplayContext.getStore()?.beforeNode(node.nodeId);
|
|
155
157
|
if (replayWait !== undefined)
|
|
156
158
|
await suspendJob(replayWait);
|
|
159
|
+
assertPromiseExecutionAllowed();
|
|
157
160
|
context.budget.visitNode();
|
|
158
161
|
context.stats.nodeVisits += 1;
|
|
159
162
|
if (node.nodeId !== undefined && context.resumeTarget?.nodeId === node.nodeId) {
|
|
@@ -800,7 +803,9 @@ function isParseResult(value) {
|
|
|
800
803
|
Object.hasOwn(value, "span"));
|
|
801
804
|
}
|
|
802
805
|
function createBlockContext(node, context) {
|
|
803
|
-
const scope = node === context.rootNode ||
|
|
806
|
+
const scope = node === context.rootNode ||
|
|
807
|
+
node === context.functionBody ||
|
|
808
|
+
context.generatorResume !== undefined
|
|
804
809
|
? context.scope
|
|
805
810
|
: context.scope.child();
|
|
806
811
|
const blockContext = {
|
|
@@ -813,15 +818,15 @@ function createBlockContext(node, context) {
|
|
|
813
818
|
return blockContext;
|
|
814
819
|
}
|
|
815
820
|
function predeclareBlockBindings(node, context) {
|
|
816
|
-
predeclareStatementListBindings(node.body, context);
|
|
821
|
+
predeclareStatementListBindings(node.body, context, node === context.functionBody || node === context.rootNode);
|
|
817
822
|
}
|
|
818
|
-
function predeclareStatementListBindings(statements, context) {
|
|
823
|
+
function predeclareStatementListBindings(statements, context, functionBody = false) {
|
|
819
824
|
const { scope } = context;
|
|
820
825
|
const names = new Set();
|
|
821
826
|
for (const statement of statements) {
|
|
822
827
|
if (statement.type === "FunctionDeclaration") {
|
|
823
828
|
const name = statement.id.name;
|
|
824
|
-
if (names.has(name)) {
|
|
829
|
+
if (names.has(name) && !(functionBody && scope.getOwnBindingKind(name) === "var")) {
|
|
825
830
|
throw new Error(`Cannot redeclare binding '${name}' in the same scope.`);
|
|
826
831
|
}
|
|
827
832
|
const closure = createInterpretedClosure(statement, context, evaluateNode);
|
|
@@ -835,7 +840,7 @@ function predeclareStatementListBindings(statements, context) {
|
|
|
835
840
|
throw new Error(`Cannot redeclare binding '${name}' in the same scope.`);
|
|
836
841
|
}
|
|
837
842
|
names.add(name);
|
|
838
|
-
scope.declare(name, "
|
|
843
|
+
scope.declare(name, functionBody ? "var" : "let", closure);
|
|
839
844
|
continue;
|
|
840
845
|
}
|
|
841
846
|
if (statement.type !== "VariableDeclaration" || statement.kind === "var") {
|
|
@@ -1597,7 +1602,7 @@ async function evaluateMemberExpression(node, context) {
|
|
|
1597
1602
|
return {
|
|
1598
1603
|
kind: "normal",
|
|
1599
1604
|
hasValue: true,
|
|
1600
|
-
value: getPromiseMember(member.
|
|
1605
|
+
value: getPromiseMember(member.property, context.budget)
|
|
1601
1606
|
};
|
|
1602
1607
|
}
|
|
1603
1608
|
if (isSandboxRegex(member.object)) {
|
|
@@ -1634,35 +1639,19 @@ async function evaluateNewExpression(node, context) {
|
|
|
1634
1639
|
return callee;
|
|
1635
1640
|
}
|
|
1636
1641
|
const name = getConstructorName(node.callee);
|
|
1637
|
-
if (!isSandboxClosure(callee.value) || callee.value.construct === undefined) {
|
|
1638
|
-
throw new TypeError(`${name} is not a constructor.`);
|
|
1639
|
-
}
|
|
1640
1642
|
const args = await evaluateCallArguments(node.arguments, context);
|
|
1641
1643
|
if (!args.ok) {
|
|
1642
1644
|
return args.result;
|
|
1643
1645
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
try {
|
|
1647
|
-
return {
|
|
1648
|
-
kind: "normal",
|
|
1649
|
-
hasValue: true,
|
|
1650
|
-
value: await wrapHostResult(callee.value.construct(args.value, {
|
|
1651
|
-
stack,
|
|
1652
|
-
thisValue: undefined,
|
|
1653
|
-
span: node.span
|
|
1654
|
-
}), stack)
|
|
1655
|
-
};
|
|
1656
|
-
}
|
|
1657
|
-
catch (error) {
|
|
1658
|
-
if (isFatalSandboxError(error)) {
|
|
1659
|
-
throw error;
|
|
1660
|
-
}
|
|
1661
|
-
throw captureException(error, stack);
|
|
1662
|
-
}
|
|
1663
|
-
finally {
|
|
1664
|
-
leaveCall();
|
|
1646
|
+
if (!isSandboxClosure(callee.value) || callee.value.construct === undefined) {
|
|
1647
|
+
throw new TypeError(`${name} is not a constructor.`);
|
|
1665
1648
|
}
|
|
1649
|
+
const stack = [...context.callStack, formatStackFrame(node, callee.value.name ?? name)];
|
|
1650
|
+
return {
|
|
1651
|
+
kind: "normal",
|
|
1652
|
+
hasValue: true,
|
|
1653
|
+
value: await invokeSandboxClosure(callee.value, args.value, context, stack, node.span, undefined, true)
|
|
1654
|
+
};
|
|
1666
1655
|
}
|
|
1667
1656
|
function getConstructorName(callee) {
|
|
1668
1657
|
if (callee.type === "Identifier") {
|
|
@@ -1839,7 +1828,7 @@ async function evaluateMemberCallExpression(node, context) {
|
|
|
1839
1828
|
return evaluateResolvedCallExpression(node, memberValue, context, member.object);
|
|
1840
1829
|
}
|
|
1841
1830
|
if (isSandboxPromise(member.object)) {
|
|
1842
|
-
return evaluateResolvedCallExpression(node, getPromiseMember(member.
|
|
1831
|
+
return evaluateResolvedCallExpression(node, getPromiseMember(member.property, context.budget), context, member.object);
|
|
1843
1832
|
}
|
|
1844
1833
|
if (isSandboxRegex(member.object) && isRegexMethodName(member.property)) {
|
|
1845
1834
|
return evaluateResolvedCallExpression(node, getRegexMember(member.object, member.property), context, member.object);
|
|
@@ -2021,6 +2010,10 @@ function applyBinaryOperator(node, left, right, context) {
|
|
|
2021
2010
|
case ">>>":
|
|
2022
2011
|
return toNumber(left) >>> toNumber(right);
|
|
2023
2012
|
case "instanceof":
|
|
2013
|
+
while (isSandboxClosure(right) && right.boundTarget !== undefined)
|
|
2014
|
+
right = right.boundTarget;
|
|
2015
|
+
if (isSandboxPromiseConstructor(right))
|
|
2016
|
+
return isSandboxPromise(left);
|
|
2024
2017
|
if (isSandboxMapConstructor(right) && isSandboxMap(left)) {
|
|
2025
2018
|
return true;
|
|
2026
2019
|
}
|
|
@@ -2322,13 +2315,16 @@ function createSetMethodOptions(context) {
|
|
|
2322
2315
|
}
|
|
2323
2316
|
function createFunctionMethodOptions(context) {
|
|
2324
2317
|
return {
|
|
2325
|
-
callClosure: (closure, args, stack, thisValue) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue)
|
|
2318
|
+
callClosure: (closure, args, stack, thisValue, construct) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue, construct)
|
|
2326
2319
|
};
|
|
2327
2320
|
}
|
|
2328
|
-
async function invokeSandboxClosure(callee, args, context, stack, span, thisValue = undefined) {
|
|
2321
|
+
async function invokeSandboxClosure(callee, args, context, stack, span, thisValue = undefined, construct = false) {
|
|
2329
2322
|
const leaveCall = context.budget.enterCall();
|
|
2330
2323
|
try {
|
|
2331
|
-
const
|
|
2324
|
+
const invoke = construct ? callee.construct : callee.call;
|
|
2325
|
+
if (invoke === undefined)
|
|
2326
|
+
throw new TypeError("Value is not a constructor.");
|
|
2327
|
+
const result = Reflect.apply(invoke, undefined, [
|
|
2332
2328
|
args,
|
|
2333
2329
|
{
|
|
2334
2330
|
stack,
|
|
@@ -2339,7 +2335,7 @@ async function invokeSandboxClosure(callee, args, context, stack, span, thisValu
|
|
|
2339
2335
|
if (isSandboxPromise(result) && result.synchronousPrefix !== undefined) {
|
|
2340
2336
|
await result.synchronousPrefix;
|
|
2341
2337
|
}
|
|
2342
|
-
return callee.async === true
|
|
2338
|
+
return !construct && callee.async === true
|
|
2343
2339
|
? normalizeClosureResult(wrapHostResult(result, stack), context.budget)
|
|
2344
2340
|
: await wrapHostResult(result, stack);
|
|
2345
2341
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type SandboxValue } from "./values.js";
|
|
2
2
|
export type SandboxIterator = {
|
|
3
3
|
readonly generator?: true;
|
|
4
|
-
next(value?: SandboxValue): Promise<IteratorResult<SandboxValue>>;
|
|
5
|
-
return?(value?: SandboxValue): Promise<IteratorResult<SandboxValue>>;
|
|
6
|
-
throw?(error?: SandboxValue): Promise<IteratorResult<SandboxValue>>;
|
|
4
|
+
next(value?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
|
|
5
|
+
return?(value?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
|
|
6
|
+
throw?(error?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
|
|
7
7
|
};
|
|
8
8
|
export declare function getSandboxIterator(value: SandboxValue): SandboxIterator | undefined;
|
|
@@ -47,22 +47,33 @@ function generatorIterator(generator) {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
function syncIterator(iterator) {
|
|
50
|
-
const
|
|
50
|
+
const next = iterator.next;
|
|
51
|
+
const invoke = (method, args) => {
|
|
51
52
|
const leaveRunning = enterRunningState(iterator);
|
|
52
53
|
try {
|
|
53
|
-
return
|
|
54
|
+
return Reflect.apply(method, iterator, args);
|
|
54
55
|
}
|
|
55
56
|
finally {
|
|
56
57
|
leaveRunning();
|
|
57
58
|
}
|
|
58
59
|
};
|
|
59
60
|
return {
|
|
60
|
-
next: (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
next: (...args) => invoke(next, args),
|
|
62
|
+
get return() {
|
|
63
|
+
const method = iterator.return;
|
|
64
|
+
if (method === undefined || method === null)
|
|
65
|
+
return undefined;
|
|
66
|
+
if (typeof method !== "function")
|
|
67
|
+
throw new TypeError("Iterator return must be callable.");
|
|
68
|
+
return (...args) => invoke(method, args);
|
|
69
|
+
},
|
|
70
|
+
get throw() {
|
|
71
|
+
const method = iterator.throw;
|
|
72
|
+
if (method === undefined || method === null)
|
|
73
|
+
return undefined;
|
|
74
|
+
if (typeof method !== "function")
|
|
75
|
+
throw new TypeError("Iterator throw must be callable.");
|
|
76
|
+
return (...args) => invoke(method, args);
|
|
77
|
+
}
|
|
67
78
|
};
|
|
68
79
|
}
|
|
@@ -45,6 +45,7 @@ export function getArrayMember(value, property, options) {
|
|
|
45
45
|
}
|
|
46
46
|
if (isArrayMethodName(property)) {
|
|
47
47
|
return createSandboxClosure({
|
|
48
|
+
sandbox: true,
|
|
48
49
|
name: `Array#${property}`,
|
|
49
50
|
call: (args, context) => callArrayMethod(value, property, args, options, context?.stack ?? [])
|
|
50
51
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SandboxClosure, type SandboxValue } from "../values.js";
|
|
2
2
|
export type FunctionMethodOptions = {
|
|
3
|
-
callClosure: (closure: SandboxClosure, args: readonly SandboxValue[], stack: readonly string[], thisValue: SandboxValue) => Promise<SandboxValue> | SandboxValue;
|
|
3
|
+
callClosure: (closure: SandboxClosure, args: readonly SandboxValue[], stack: readonly string[], thisValue: SandboxValue, construct?: boolean) => Promise<SandboxValue> | SandboxValue;
|
|
4
4
|
};
|
|
5
5
|
export declare function getFunctionMember(target: SandboxClosure, property: string | number, options: FunctionMethodOptions): SandboxValue | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSandboxClosure } from "../values.js";
|
|
2
|
-
const functionMethodNames = new Set(["apply", "call"]);
|
|
2
|
+
const functionMethodNames = new Set(["apply", "bind", "call"]);
|
|
3
3
|
export function getFunctionMember(target, property, options) {
|
|
4
4
|
const propertyValue = target.properties?.[String(property)];
|
|
5
5
|
if (propertyValue !== undefined || Object.hasOwn(target.properties ?? {}, String(property))) {
|
|
@@ -9,6 +9,7 @@ export function getFunctionMember(target, property, options) {
|
|
|
9
9
|
return undefined;
|
|
10
10
|
}
|
|
11
11
|
return createSandboxClosure({
|
|
12
|
+
sandbox: true,
|
|
12
13
|
name: `Function#${property}`,
|
|
13
14
|
call: (args, context) => callFunctionMethod(target, property, args, options, context?.stack ?? [])
|
|
14
15
|
});
|
|
@@ -18,6 +19,21 @@ function isFunctionMethodName(property) {
|
|
|
18
19
|
}
|
|
19
20
|
function callFunctionMethod(target, methodName, args, options, stack) {
|
|
20
21
|
const thisValue = args[0];
|
|
22
|
+
if (methodName === "bind") {
|
|
23
|
+
const boundArgs = args.slice(1);
|
|
24
|
+
return createSandboxClosure({
|
|
25
|
+
sandbox: true,
|
|
26
|
+
name: `bound ${target.name ?? ""}`,
|
|
27
|
+
boundTarget: target,
|
|
28
|
+
retainedValues: () => [target, thisValue, ...boundArgs],
|
|
29
|
+
call: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], thisValue),
|
|
30
|
+
...(target.construct === undefined
|
|
31
|
+
? {}
|
|
32
|
+
: {
|
|
33
|
+
construct: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], undefined, true)
|
|
34
|
+
})
|
|
35
|
+
});
|
|
36
|
+
}
|
|
21
37
|
if (methodName === "call") {
|
|
22
38
|
return options.callClosure(target, args.slice(1), stack, thisValue);
|
|
23
39
|
}
|
|
@@ -20,7 +20,7 @@ export async function bindPattern(pattern, value, target, scope, context) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
function bindIdentifier(pattern, value, target, scope) {
|
|
23
|
-
if ("assign" in target || target.kind === "var") {
|
|
23
|
+
if ("assign" in target || (target.kind === "var" && target.initialize !== true)) {
|
|
24
24
|
if ("assign" in target) {
|
|
25
25
|
const binding = scope.lookup(pattern.name);
|
|
26
26
|
if (!binding.found) {
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import type { ErrorSourceSpan } from "../error/shape.js";
|
|
2
|
+
import { SandboxError } from "./budget.js";
|
|
2
3
|
import type { SandboxPromise, SandboxValue } from "./values.js";
|
|
3
4
|
export declare class SandboxPromiseRejectionTracker {
|
|
4
5
|
private readonly records;
|
|
5
6
|
private readonly recordsByPromise;
|
|
7
|
+
private fatalRejection;
|
|
8
|
+
private readonly fatalWaiters;
|
|
9
|
+
get failure(): SandboxError | undefined;
|
|
10
|
+
onFatalRejection(interrupt: (error: SandboxError) => void): () => void;
|
|
11
|
+
interruptOnFatalRejection(pending: Promise<SandboxValue>): Promise<SandboxValue>;
|
|
6
12
|
observe(promise: SandboxPromise): void;
|
|
7
|
-
track(promise: SandboxPromise): void;
|
|
13
|
+
track(promise: SandboxPromise, outcome?: Promise<SandboxValue>): void;
|
|
8
14
|
findUnhandledRejection(): Promise<{
|
|
9
15
|
reason: SandboxValue | undefined;
|
|
10
16
|
span: ErrorSourceSpan | undefined;
|
|
11
17
|
} | undefined>;
|
|
12
18
|
}
|
|
19
|
+
export declare function assertPromiseExecutionAllowed(): void;
|
|
20
|
+
export declare function onFatalPromiseRejection(interrupt: (error: SandboxError) => void): (() => void) | undefined;
|
|
21
|
+
export declare function interruptOnFatalPromiseRejection(pending: Promise<SandboxValue>): Promise<SandboxValue>;
|
|
22
|
+
export declare function withFatalPromiseCleanup<TResult>(callback: () => TResult): TResult;
|
|
13
23
|
export declare function createSandboxPromiseRejectionTracker(): SandboxPromiseRejectionTracker;
|
|
14
24
|
export declare function observeSandboxPromise(promise: SandboxPromise): void;
|
|
15
|
-
export declare function trackSandboxPromise(promise: SandboxPromise): void;
|
|
25
|
+
export declare function trackSandboxPromise(promise: SandboxPromise, outcome?: Promise<SandboxValue>): void;
|
|
16
26
|
export declare function withSandboxPromiseRejectionTracker<TResult>(tracker: SandboxPromiseRejectionTracker, callback: () => TResult): TResult;
|