poe-code 14.0.62 → 14.0.63
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 +929 -504
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safe-js/dist/chunks/{chunk-F7TJSPDZ.js → chunk-KMTTD3BL.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-VKGWGZBH.js → chunk-RU2BCN4G.js} +898 -521
- package/packages/safe-js/dist/chunks/chunk-RU2BCN4G.js.map +7 -0
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.js +1 -1
- package/packages/safe-js/dist/graph-depth.js +3 -0
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/boxed.d.ts +14 -0
- package/packages/safe-js/dist/interp/boxed.js +50 -0
- package/packages/safe-js/dist/interp/globals/collections.js +4 -4
- package/packages/safe-js/dist/interp/globals/console-json.js +15 -18
- package/packages/safe-js/dist/interp/globals/misc.js +1 -1
- package/packages/safe-js/dist/interp/globals/object-array.js +19 -14
- package/packages/safe-js/dist/interp/globals/object.js +10 -5
- package/packages/safe-js/dist/interp/globals/primitives.d.ts +8 -0
- package/packages/safe-js/dist/interp/globals/primitives.js +69 -0
- package/packages/safe-js/dist/interp/host-bridge.js +22 -0
- package/packages/safe-js/dist/interp/interpreter.js +172 -120
- package/packages/safe-js/dist/interp/iteration.d.ts +5 -3
- package/packages/safe-js/dist/interp/iteration.js +22 -1
- package/packages/safe-js/dist/interp/methods/array.js +6 -3
- package/packages/safe-js/dist/interp/methods/number.d.ts +1 -0
- package/packages/safe-js/dist/interp/methods/number.js +4 -1
- package/packages/safe-js/dist/interp/methods/string.d.ts +2 -0
- package/packages/safe-js/dist/interp/methods/string.js +2 -2
- package/packages/safe-js/dist/interp/object-model.d.ts +5 -0
- package/packages/safe-js/dist/interp/object-model.js +41 -3
- package/packages/safe-js/dist/interp/promise.js +68 -61
- package/packages/safe-js/dist/interp/string-coercion.js +4 -1
- package/packages/safe-js/dist/interp/values.d.ts +1 -0
- package/packages/safe-js/dist/interp/values.js +78 -3
- package/packages/safe-js/dist/migrate.d.ts +2 -2
- package/packages/safe-js/dist/migration-file.d.ts +4 -4
- package/packages/safe-js/dist/snapshot/boxed.d.ts +16 -0
- package/packages/safe-js/dist/snapshot/boxed.js +52 -0
- package/packages/safe-js/dist/snapshot/dump-format.js +18 -5
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +5 -0
- package/packages/safe-js/dist/snapshot/replay-data.js +24 -0
- package/packages/safe-js/dist/snapshot/restore.js +8 -0
- package/packages/safe-js/dist/snapshot/serialize.d.ts +4 -2
- package/packages/safe-js/dist/snapshot/serialize.js +18 -10
- package/packages/safe-js/dist/snapshot/validation.js +22 -1
- package/packages/safe-js/dist/chunks/chunk-VKGWGZBH.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-F7TJSPDZ.js.map → chunk-KMTTD3BL.js.map} +0 -0
|
@@ -14,7 +14,8 @@ import { CompileScope, RegexCompileGuard } from "./regex/compile-guard.js";
|
|
|
14
14
|
import { createCapturedException, createThrowCompletion, evaluateThrowStatement as evaluateThrowStatementResult, evaluateTryStatement as evaluateTryStatementResult, isCapturedException, isInterpreterError, surfaceThrownValue } from "./exceptions.js";
|
|
15
15
|
import { callArrayMethod, getArrayMember, isArrayMethodName } from "./methods/array.js";
|
|
16
16
|
import { getFunctionMember } from "./methods/function.js";
|
|
17
|
-
import { getGuestFunctionProperty, getSandboxPrototype, isGuestClosure, materializeFunctionProperties, setSandboxPrototype } from "./object-model.js";
|
|
17
|
+
import { getBoxedPrototype, getGuestFunctionProperty, getSandboxPrototype, isDefaultBoxedMethod, isGuestClosure, materializeFunctionProperties, setSandboxPrototype } from "./object-model.js";
|
|
18
|
+
import { getStringIndex } from "./methods/string.js";
|
|
18
19
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
19
20
|
import { callMapMethod, getMapMember, isMapMethodName } from "./methods/map.js";
|
|
20
21
|
import { callNumberMethod, getNumberMember, isNumberMethodName } from "./methods/number.js";
|
|
@@ -34,6 +35,7 @@ import { getCollectionIteratorMember } from "./methods/collection-iterator.js";
|
|
|
34
35
|
import { getFloat32Member, isFloat32ArrayConstructor, setFloat32Member } from "./globals/float32array.js";
|
|
35
36
|
import { isFloat32Array } from "./float32.js";
|
|
36
37
|
import { dateString, dateTime, isSandboxDate } from "./date.js";
|
|
38
|
+
import { isSandboxBox } from "./boxed.js";
|
|
37
39
|
import { getDateMember, getDatePrototype, isDateConstructor } from "./globals/date.js";
|
|
38
40
|
import { createSandboxRegex, allocateProducedSandboxValue, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet, reconcileCompiledValues } from "./values.js";
|
|
39
41
|
import { Scope } from "./scope.js";
|
|
@@ -466,22 +468,29 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
466
468
|
if (left.kind !== "normal") {
|
|
467
469
|
return left;
|
|
468
470
|
}
|
|
471
|
+
let leftValue = left.value;
|
|
469
472
|
let rightValue;
|
|
470
|
-
const release = retainValues(context.budget, () => [
|
|
473
|
+
const release = retainValues(context.budget, () => [leftValue, rightValue]);
|
|
471
474
|
try {
|
|
472
475
|
const right = await evaluateNode(node.right, context);
|
|
473
476
|
if (right.kind !== "normal") {
|
|
474
477
|
return right;
|
|
475
478
|
}
|
|
476
479
|
rightValue = right.value;
|
|
477
|
-
let leftValue = left.value;
|
|
478
480
|
if (node.operator === "in") {
|
|
479
481
|
if (typeof right.value !== "object" || right.value === null) {
|
|
480
482
|
throw new TypeError("Right-hand side of 'in' must be an object.");
|
|
481
483
|
}
|
|
482
484
|
leftValue = await toPropertyKey(leftValue, context.budget, createCoercionContext(context));
|
|
483
485
|
}
|
|
484
|
-
|
|
486
|
+
if (!["in", "instanceof", "===", "!=="].includes(node.operator)) {
|
|
487
|
+
const equality = node.operator === "==" || node.operator === "!=";
|
|
488
|
+
if (isSandboxBox(leftValue) && (!equality || (rightValue !== null && rightValue !== undefined && typeof rightValue !== "object")))
|
|
489
|
+
leftValue = await toNumericPrimitive(leftValue, context);
|
|
490
|
+
if (isSandboxBox(rightValue) && (!equality || (left.value !== null && left.value !== undefined && typeof left.value !== "object")))
|
|
491
|
+
rightValue = await toNumericPrimitive(rightValue, context);
|
|
492
|
+
}
|
|
493
|
+
const value = applyBinaryOperator(node, leftValue, rightValue, context);
|
|
485
494
|
return {
|
|
486
495
|
kind: "normal",
|
|
487
496
|
hasValue: true,
|
|
@@ -1079,62 +1088,68 @@ async function evaluateForOfStatement(node, context) {
|
|
|
1079
1088
|
};
|
|
1080
1089
|
}
|
|
1081
1090
|
async function evaluateForOfIterator(node, value, context, restoredEntry) {
|
|
1082
|
-
const iterator = getSandboxIterator(value, context.budget);
|
|
1091
|
+
const iterator = getSandboxIterator(value, context.budget, createCoercionContext(context));
|
|
1083
1092
|
if (iterator === undefined) {
|
|
1084
1093
|
throw new TypeError(`${String(value)} is not a supported iterable`);
|
|
1085
1094
|
}
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const iteration = restoredEntry ?? (await iterator.next());
|
|
1099
|
-
restoredEntry = undefined;
|
|
1100
|
-
if (typeof iteration !== "object" || iteration === null) {
|
|
1101
|
-
throw new TypeError("Iterator result must be an object.");
|
|
1102
|
-
}
|
|
1103
|
-
if (iteration.done) {
|
|
1104
|
-
context.activeLoopIterations.delete(nodeId);
|
|
1105
|
-
return normalEmptyResult();
|
|
1106
|
-
}
|
|
1107
|
-
context.activeLoopIterations.set(nodeId, iterator.snapshotIndex === undefined
|
|
1108
|
-
? index
|
|
1109
|
-
: {
|
|
1110
|
-
get index() {
|
|
1111
|
-
return iterator.snapshotIndex();
|
|
1112
|
-
},
|
|
1113
|
-
values: [value, iteration.value]
|
|
1114
|
-
});
|
|
1115
|
-
const scope = context.scope.child();
|
|
1116
|
-
const binding = await bindForOfLoopVariable(node.left, iteration.value, scope, context);
|
|
1117
|
-
if (!binding.ok) {
|
|
1118
|
-
return binding.result;
|
|
1119
|
-
}
|
|
1120
|
-
const iterationContext = createLoopIterationContext(context, scope);
|
|
1121
|
-
emitLoopIterationBreakpoint(node, iterationContext);
|
|
1122
|
-
const result = await evaluateNode(node.body, iterationContext);
|
|
1123
|
-
if (isMatchingBreak(result, loopLabels(node))) {
|
|
1124
|
-
context.activeLoopIterations.delete(nodeId);
|
|
1125
|
-
await closeIterator(iterator);
|
|
1126
|
-
return normalEmptyResult();
|
|
1095
|
+
const releaseIterator = retainValues(context.budget, () => [value, iterator.retainedValue]);
|
|
1096
|
+
try {
|
|
1097
|
+
const nodeId = node.nodeId ?? -1;
|
|
1098
|
+
let index = consumeRestoredLoopIterationIndex(node, context);
|
|
1099
|
+
for (let skipped = 0; skipped < index; skipped += 1) {
|
|
1100
|
+
const skippedIteration = await iterator.next();
|
|
1101
|
+
if (typeof skippedIteration !== "object" || skippedIteration === null) {
|
|
1102
|
+
throw new TypeError("Iterator result must be an object.");
|
|
1103
|
+
}
|
|
1104
|
+
if (skippedIteration.done) {
|
|
1105
|
+
return normalEmptyResult();
|
|
1106
|
+
}
|
|
1127
1107
|
}
|
|
1128
|
-
|
|
1108
|
+
while (true) {
|
|
1109
|
+
const iteration = restoredEntry ?? (await iterator.next());
|
|
1110
|
+
restoredEntry = undefined;
|
|
1111
|
+
if (typeof iteration !== "object" || iteration === null) {
|
|
1112
|
+
throw new TypeError("Iterator result must be an object.");
|
|
1113
|
+
}
|
|
1114
|
+
if (iteration.done) {
|
|
1115
|
+
context.activeLoopIterations.delete(nodeId);
|
|
1116
|
+
return normalEmptyResult();
|
|
1117
|
+
}
|
|
1118
|
+
context.activeLoopIterations.set(nodeId, iterator.snapshotIndex === undefined
|
|
1119
|
+
? index
|
|
1120
|
+
: {
|
|
1121
|
+
get index() {
|
|
1122
|
+
return iterator.snapshotIndex();
|
|
1123
|
+
},
|
|
1124
|
+
values: [value, iteration.value]
|
|
1125
|
+
});
|
|
1126
|
+
const scope = context.scope.child();
|
|
1127
|
+
const binding = await bindForOfLoopVariable(node.left, iteration.value, scope, context);
|
|
1128
|
+
if (!binding.ok) {
|
|
1129
|
+
return binding.result;
|
|
1130
|
+
}
|
|
1131
|
+
const iterationContext = createLoopIterationContext(context, scope);
|
|
1132
|
+
emitLoopIterationBreakpoint(node, iterationContext);
|
|
1133
|
+
const result = await evaluateNode(node.body, iterationContext);
|
|
1134
|
+
if (isMatchingBreak(result, loopLabels(node))) {
|
|
1135
|
+
context.activeLoopIterations.delete(nodeId);
|
|
1136
|
+
await closeIterator(iterator);
|
|
1137
|
+
return normalEmptyResult();
|
|
1138
|
+
}
|
|
1139
|
+
if (isMatchingContinue(result, loopLabels(node))) {
|
|
1140
|
+
index += 1;
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
if (result.kind !== "normal") {
|
|
1144
|
+
context.activeLoopIterations.delete(nodeId);
|
|
1145
|
+
await closeIterator(iterator);
|
|
1146
|
+
return result;
|
|
1147
|
+
}
|
|
1129
1148
|
index += 1;
|
|
1130
|
-
continue;
|
|
1131
|
-
}
|
|
1132
|
-
if (result.kind !== "normal") {
|
|
1133
|
-
context.activeLoopIterations.delete(nodeId);
|
|
1134
|
-
await closeIterator(iterator);
|
|
1135
|
-
return result;
|
|
1136
1149
|
}
|
|
1137
|
-
|
|
1150
|
+
}
|
|
1151
|
+
finally {
|
|
1152
|
+
releaseIterator();
|
|
1138
1153
|
}
|
|
1139
1154
|
}
|
|
1140
1155
|
async function evaluateForInStatement(node, context) {
|
|
@@ -1523,49 +1538,58 @@ async function evaluateYieldDelegate(node, context) {
|
|
|
1523
1538
|
if (argument.kind !== "normal") {
|
|
1524
1539
|
return argument;
|
|
1525
1540
|
}
|
|
1526
|
-
const iterator = getSandboxIterator(argument.value, context.budget);
|
|
1541
|
+
const iterator = getSandboxIterator(argument.value, context.budget, createCoercionContext(context));
|
|
1527
1542
|
if (iterator === undefined) {
|
|
1528
1543
|
throw new TypeError(`${String(argument.value)} is not a supported iterable`);
|
|
1529
1544
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1545
|
+
const releaseIterator = retainValues(context.budget, () => [
|
|
1546
|
+
argument.value,
|
|
1547
|
+
iterator.retainedValue
|
|
1548
|
+
]);
|
|
1549
|
+
try {
|
|
1550
|
+
let completion = {
|
|
1551
|
+
type: "normal",
|
|
1552
|
+
value: undefined
|
|
1553
|
+
};
|
|
1554
|
+
const replay = context.generatorResume?.sent ?? [];
|
|
1555
|
+
let replayIndex = 0;
|
|
1556
|
+
while (true) {
|
|
1557
|
+
const method = completion.type === "normal" ? "next" : completion.type;
|
|
1558
|
+
const iteratorMethod = iterator[method];
|
|
1559
|
+
if (iteratorMethod === undefined) {
|
|
1560
|
+
if (completion.type === "throw") {
|
|
1561
|
+
throw completion.value;
|
|
1562
|
+
}
|
|
1563
|
+
return generatorCompletionResult(completion);
|
|
1542
1564
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
return
|
|
1565
|
+
const result = await iteratorMethod(completion.value);
|
|
1566
|
+
if (result.done) {
|
|
1567
|
+
if (completion.type === "return") {
|
|
1568
|
+
return generatorCompletionResult({ type: "return", value: result.value });
|
|
1569
|
+
}
|
|
1570
|
+
return {
|
|
1571
|
+
kind: "normal",
|
|
1572
|
+
hasValue: true,
|
|
1573
|
+
value: result.value
|
|
1574
|
+
};
|
|
1549
1575
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1576
|
+
if (replayIndex < replay.length - 1) {
|
|
1577
|
+
completion = replay[replayIndex + 1];
|
|
1578
|
+
replayIndex += 1;
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
const completionPromise = context.generatorYield(allocateProducedSandboxValue(result.value, context.budget), node.nodeId);
|
|
1582
|
+
emitResumeBreakpoint(context, {
|
|
1583
|
+
kind: "generator-yield",
|
|
1584
|
+
nodeId: node.nodeId,
|
|
1585
|
+
span: node.span
|
|
1586
|
+
});
|
|
1587
|
+
completion = (await completionPromise);
|
|
1588
|
+
context.generatorResume = undefined;
|
|
1560
1589
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
nodeId: node.nodeId,
|
|
1565
|
-
span: node.span
|
|
1566
|
-
});
|
|
1567
|
-
completion = (await completionPromise);
|
|
1568
|
-
context.generatorResume = undefined;
|
|
1590
|
+
}
|
|
1591
|
+
finally {
|
|
1592
|
+
releaseIterator();
|
|
1569
1593
|
}
|
|
1570
1594
|
}
|
|
1571
1595
|
function generatorCompletionResult(completion) {
|
|
@@ -1706,6 +1730,14 @@ async function evaluateMemberExpression(node, context) {
|
|
|
1706
1730
|
function getPropertyValue(target, property, context) {
|
|
1707
1731
|
if (isGuestHostObject(target))
|
|
1708
1732
|
return getHostObjectMember(target, String(property));
|
|
1733
|
+
if (typeof target === "string" || typeof target === "number" || typeof target === "boolean") {
|
|
1734
|
+
const prototype = getBoxedPrototype(target, context.budget);
|
|
1735
|
+
if (prototype !== undefined) {
|
|
1736
|
+
if (typeof target === "string" && (property === "length" || getStringIndex(property) !== undefined))
|
|
1737
|
+
return getStringMember(target, property, context.budget);
|
|
1738
|
+
return getMemberValue(prototype, property, context);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1709
1741
|
if (typeof target === "string")
|
|
1710
1742
|
return getStringMember(target, property, context.budget);
|
|
1711
1743
|
if (typeof target === "number")
|
|
@@ -1886,6 +1918,16 @@ async function evaluateMemberCallExpression(node, context) {
|
|
|
1886
1918
|
...reference,
|
|
1887
1919
|
property: await toPropertyKey(reference.property, context.budget, createCoercionContext(context))
|
|
1888
1920
|
};
|
|
1921
|
+
if ((typeof member.object === "string" || typeof member.object === "number" || typeof member.object === "boolean") &&
|
|
1922
|
+
getBoxedPrototype(member.object, context.budget) !== undefined) {
|
|
1923
|
+
if (isDefaultBoxedMethod(member.object, member.property, context.budget)) {
|
|
1924
|
+
if (typeof member.object === "string" && isStringMethodName(member.property))
|
|
1925
|
+
return evaluateStringMethodCall(node, member.object, member.property, context);
|
|
1926
|
+
if (typeof member.object === "number" && isNumberMethodName(member.property))
|
|
1927
|
+
return evaluateNumberMethodCall(node, member.object, member.property, context);
|
|
1928
|
+
}
|
|
1929
|
+
return evaluateResolvedCallExpression(node, getPropertyValue(member.object, member.property, context), context, member.object);
|
|
1930
|
+
}
|
|
1889
1931
|
if (typeof member.object === "string" && isStringMethodName(member.property)) {
|
|
1890
1932
|
return evaluateStringMethodCall(node, member.object, member.property, context);
|
|
1891
1933
|
}
|
|
@@ -2190,33 +2232,43 @@ function hasSandboxProperty(value, key, context) {
|
|
|
2190
2232
|
return false;
|
|
2191
2233
|
}
|
|
2192
2234
|
async function applyCompoundAssignmentOperator(operator, left, right, context) {
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2235
|
+
const convertingObject = typeof left === "object" && left !== null;
|
|
2236
|
+
let convertedLeft;
|
|
2237
|
+
const release = retainValues(context.budget, () => [convertedLeft]);
|
|
2238
|
+
try {
|
|
2239
|
+
left = await toNumericPrimitive(left, context);
|
|
2240
|
+
if (convertingObject)
|
|
2241
|
+
convertedLeft = left;
|
|
2242
|
+
right = await toNumericPrimitive(right, context);
|
|
2243
|
+
switch (operator) {
|
|
2244
|
+
case "+=":
|
|
2245
|
+
return applyAdditionOperator(left, right, context);
|
|
2246
|
+
case "-=":
|
|
2247
|
+
return toNumber(left) - toNumber(right);
|
|
2248
|
+
case "*=":
|
|
2249
|
+
return toNumber(left) * toNumber(right);
|
|
2250
|
+
case "/=":
|
|
2251
|
+
return toNumber(left) / toNumber(right);
|
|
2252
|
+
case "%=":
|
|
2253
|
+
return toNumber(left) % toNumber(right);
|
|
2254
|
+
case "**=":
|
|
2255
|
+
return toNumber(left) ** toNumber(right);
|
|
2256
|
+
case "&=":
|
|
2257
|
+
return toNumber(left) & toNumber(right);
|
|
2258
|
+
case "|=":
|
|
2259
|
+
return toNumber(left) | toNumber(right);
|
|
2260
|
+
case "^=":
|
|
2261
|
+
return toNumber(left) ^ toNumber(right);
|
|
2262
|
+
case "<<=":
|
|
2263
|
+
return toNumber(left) << toNumber(right);
|
|
2264
|
+
case ">>=":
|
|
2265
|
+
return toNumber(left) >> toNumber(right);
|
|
2266
|
+
case ">>>=":
|
|
2267
|
+
return toNumber(left) >>> toNumber(right);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
finally {
|
|
2271
|
+
release();
|
|
2220
2272
|
}
|
|
2221
2273
|
}
|
|
2222
2274
|
function applyAdditionOperator(left, right, context) {
|
|
@@ -2626,12 +2678,12 @@ async function evaluateSpreadElement(node, context) {
|
|
|
2626
2678
|
result: value
|
|
2627
2679
|
};
|
|
2628
2680
|
}
|
|
2629
|
-
const iterator = getSandboxIterator(value.value, context.budget);
|
|
2681
|
+
const iterator = getSandboxIterator(value.value, context.budget, createCoercionContext(context));
|
|
2630
2682
|
if (iterator === undefined) {
|
|
2631
2683
|
throw new TypeError("Spread arguments must evaluate to an iterable.");
|
|
2632
2684
|
}
|
|
2633
2685
|
const spreadValues = [];
|
|
2634
|
-
const release = retainValues(context.budget, () => [value.value, ...spreadValues]);
|
|
2686
|
+
const release = retainValues(context.budget, () => [value.value, iterator.retainedValue, ...spreadValues]);
|
|
2635
2687
|
try {
|
|
2636
2688
|
while (true) {
|
|
2637
2689
|
const next = await iterator.next();
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { type SandboxValue } from "./values.js";
|
|
2
|
-
import
|
|
1
|
+
import { type SandboxCallContext, type SandboxValue } from "./values.js";
|
|
2
|
+
import { Budget } from "./budget.js";
|
|
3
3
|
export type SandboxIterator = {
|
|
4
4
|
readonly generator?: true;
|
|
5
|
+
readonly asynchronous?: true;
|
|
6
|
+
readonly retainedValue?: SandboxValue;
|
|
5
7
|
snapshotIndex?(): number;
|
|
6
8
|
next(value?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
|
|
7
9
|
return?(value?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
|
|
8
10
|
throw?(error?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
|
|
9
11
|
};
|
|
10
|
-
export declare function getSandboxIterator(value: SandboxValue, budget?: Budget): SandboxIterator | undefined;
|
|
12
|
+
export declare function getSandboxIterator(value: SandboxValue, budget?: Budget, context?: SandboxCallContext): SandboxIterator | undefined;
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import { isSandboxGenerator, isSandboxMap, isSandboxSet } from "./values.js";
|
|
2
2
|
import { enterRunningState } from "./running-state.js";
|
|
3
3
|
import { isFloat32Array } from "./float32.js";
|
|
4
|
+
import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
4
5
|
import { getHostObjectIterator, isGuestHostObject } from "./host-capabilities.js";
|
|
5
6
|
import { isSandboxCollectionIterator, nextCollectionIterator } from "./collection-iterator.js";
|
|
6
|
-
|
|
7
|
+
import { Budget } from "./budget.js";
|
|
8
|
+
import { sandboxString } from "./string-coercion.js";
|
|
9
|
+
export function getSandboxIterator(value, budget, context) {
|
|
10
|
+
if (isSandboxBox(value) && typeof boxedValue(value) === "string") {
|
|
11
|
+
const primitive = boxedValue(value);
|
|
12
|
+
let text;
|
|
13
|
+
let iterator;
|
|
14
|
+
let initialized;
|
|
15
|
+
return {
|
|
16
|
+
asynchronous: true,
|
|
17
|
+
get retainedValue() { return text === primitive ? undefined : text; },
|
|
18
|
+
next: async () => {
|
|
19
|
+
initialized ??= Promise.resolve(sandboxString(value, budget ?? new Budget(), context)).then(converted => {
|
|
20
|
+
text = converted;
|
|
21
|
+
iterator = syncIterator(converted[Symbol.iterator]());
|
|
22
|
+
});
|
|
23
|
+
await initialized;
|
|
24
|
+
return iterator.next();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
7
28
|
if (isSandboxCollectionIterator(value))
|
|
8
29
|
return { next: () => nextCollectionIterator(value, budget), snapshotIndex: () => 0 };
|
|
9
30
|
if (isGuestHostObject(value))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createSandboxBox } from "../boxed.js";
|
|
2
|
+
import { createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet, measureSandboxData } from "../values.js";
|
|
2
3
|
import { assertCollectionMutable, enterRunningState } from "../running-state.js";
|
|
3
4
|
import { getSandboxDataProperty, getSandboxPrototype } from "../object-model.js";
|
|
4
5
|
import { sandboxNumber } from "../string-coercion.js";
|
|
@@ -75,8 +76,10 @@ export function isArrayMethodName(property) {
|
|
|
75
76
|
export async function callArrayMethod(receiver, methodName, args, options, stack = []) {
|
|
76
77
|
if (receiver === null || receiver === undefined)
|
|
77
78
|
throw new TypeError("Array method requires a receiver.");
|
|
78
|
-
if (typeof receiver !== "object")
|
|
79
|
-
|
|
79
|
+
if (typeof receiver !== "object") {
|
|
80
|
+
receiver = createSandboxBox(receiver);
|
|
81
|
+
options.budget.chargeDataUsage(measureSandboxData([receiver]));
|
|
82
|
+
}
|
|
80
83
|
const retainedReceiver = {};
|
|
81
84
|
options.budget.setRetainedValues(retainedReceiver, () => [receiver]);
|
|
82
85
|
try {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Budget } from "../budget.js";
|
|
2
2
|
import { type SandboxCallContext, type SandboxValue } from "../values.js";
|
|
3
3
|
export type NumberMethodName = "toExponential" | "toFixed" | "toPrecision" | "toString";
|
|
4
|
+
export declare const numberMethodNames: Set<NumberMethodName>;
|
|
4
5
|
export declare function getNumberMember(property: string | number, budget: Budget): SandboxValue | undefined;
|
|
5
6
|
export declare function isNumberMethodName(property: string | number): property is NumberMethodName;
|
|
6
7
|
export declare function callNumberMethod(value: SandboxValue, methodName: NumberMethodName, args: readonly SandboxValue[], budget: Budget, context?: SandboxCallContext): string | Promise<string>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { sandboxNumber } from "../string-coercion.js";
|
|
2
|
+
import { isSandboxBox, boxedValue } from "../boxed.js";
|
|
2
3
|
import { createSandboxClosure } from "../values.js";
|
|
3
|
-
const numberMethodNames = new Set([
|
|
4
|
+
export const numberMethodNames = new Set([
|
|
4
5
|
"toExponential",
|
|
5
6
|
"toFixed",
|
|
6
7
|
"toPrecision",
|
|
@@ -20,6 +21,8 @@ export function isNumberMethodName(property) {
|
|
|
20
21
|
return typeof property === "string" && numberMethodNames.has(property);
|
|
21
22
|
}
|
|
22
23
|
export function callNumberMethod(value, methodName, args, budget, context) {
|
|
24
|
+
if (isSandboxBox(value))
|
|
25
|
+
value = boxedValue(value);
|
|
23
26
|
if (typeof value !== "number") {
|
|
24
27
|
throw new TypeError(`Number#${methodName} requires a number receiver.`);
|
|
25
28
|
}
|
|
@@ -3,7 +3,9 @@ import { Budget } from "../budget.js";
|
|
|
3
3
|
import { CompileScope } from "../regex/compile-guard.js";
|
|
4
4
|
import { type SandboxCallContext, type SandboxClosure, type SandboxValue } from "../values.js";
|
|
5
5
|
type StringMethodName = "at" | "charAt" | "charCodeAt" | "codePointAt" | "concat" | "endsWith" | "includes" | "indexOf" | "isWellFormed" | "lastIndexOf" | "localeCompare" | "match" | "matchAll" | "normalize" | "padEnd" | "padStart" | "repeat" | "replace" | "replaceAll" | "slice" | "search" | "split" | "startsWith" | "substr" | "substring" | "toLowerCase" | "toUpperCase" | "toWellFormed" | "trim" | "trimEnd" | "trimStart";
|
|
6
|
+
export declare const stringMethodNames: Set<StringMethodName>;
|
|
6
7
|
export declare function getStringMember(value: string, property: string | number, budget: Budget): SandboxValue | undefined;
|
|
8
|
+
export declare function getStringIndex(property: string | number): number | undefined;
|
|
7
9
|
export declare function isStringMethodName(property: string | number): property is StringMethodName;
|
|
8
10
|
export declare function validateStringMethodArguments(_methodName: StringMethodName, _args: readonly Expression[]): void;
|
|
9
11
|
export declare function callStringMethod(value: string, methodName: StringMethodName, args: readonly SandboxValue[], budget: Budget, callClosure?: (closure: SandboxClosure, args: readonly SandboxValue[]) => Promise<SandboxValue>, parent?: CompileScope, context?: SandboxCallContext): SandboxValue | Promise<SandboxValue>;
|
|
@@ -6,7 +6,7 @@ import { retainValues } from "../resources.js";
|
|
|
6
6
|
import { createSandboxClosure, createSandboxRegex, deepCopyFromSandbox, isSandboxClosure, isSandboxRegex } from "../values.js";
|
|
7
7
|
import { executeRegex, toMatchArray } from "./regex.js";
|
|
8
8
|
const SPLIT_STRING_MESSAGE = "String#split only supports string separator values.";
|
|
9
|
-
const stringMethodNames = new Set([
|
|
9
|
+
export const stringMethodNames = new Set([
|
|
10
10
|
"at",
|
|
11
11
|
"charAt",
|
|
12
12
|
"charCodeAt",
|
|
@@ -72,7 +72,7 @@ export function getStringMember(value, property, budget) {
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
function getStringIndex(property) {
|
|
75
|
+
export function getStringIndex(property) {
|
|
76
76
|
const index = typeof property === "number" ? property : Number(property);
|
|
77
77
|
if (!Number.isInteger(index) || index < 0 || String(index) !== String(property)) {
|
|
78
78
|
return undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Budget } from "./budget.js";
|
|
2
|
+
import { type BoxedPrimitive } from "./boxed.js";
|
|
2
3
|
import { type SandboxClosure, type SandboxObject, type SandboxValue } from "./values.js";
|
|
3
4
|
export declare function registerGuestClosure(closure: SandboxClosure): void;
|
|
4
5
|
export declare function isGuestClosure(value: unknown): value is SandboxClosure;
|
|
@@ -6,6 +7,10 @@ export declare function getGuestFunctionProperties(closure: SandboxClosure): San
|
|
|
6
7
|
export declare function materializeFunctionProperties(closure: SandboxClosure): SandboxObject;
|
|
7
8
|
export declare function getGuestFunctionProperty(closure: SandboxClosure, key: string): SandboxValue;
|
|
8
9
|
export declare function installObjectPrototype(budget: Budget, prototype: SandboxObject, constructor: SandboxClosure): void;
|
|
10
|
+
export declare function installBoxedPrototype(budget: Budget, prototype: SandboxObject, constructor: SandboxClosure): void;
|
|
11
|
+
export declare function getBoxedPrototype(value: BoxedPrimitive, budget: Budget): SandboxObject | undefined;
|
|
12
|
+
export declare function isDefaultBoxedMethod(value: BoxedPrimitive, key: string, budget: Budget): boolean;
|
|
13
|
+
export declare function isIntrinsicConstructor(value: object): boolean;
|
|
9
14
|
export declare function releaseObjectPrototype(budget: Budget): void;
|
|
10
15
|
export declare function getSandboxPrototype(value: object, budget?: Budget): object | null;
|
|
11
16
|
export declare function hasExplicitSandboxPrototype(value: object): boolean;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
2
2
|
import { getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
|
|
3
|
+
import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
3
4
|
import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "./values.js";
|
|
4
5
|
const guestClosures = new WeakSet();
|
|
5
6
|
const functionProperties = new WeakMap();
|
|
6
7
|
const prototypes = new WeakMap();
|
|
7
8
|
const intrinsicPrototypes = new WeakMap();
|
|
9
|
+
const boxedPrototypes = new WeakMap();
|
|
10
|
+
const intrinsicPrototypeRoots = new WeakMap();
|
|
8
11
|
const intrinsicConstructors = new WeakMap();
|
|
12
|
+
const initialBoxedMethods = new WeakMap();
|
|
9
13
|
const descriptorObjects = new WeakSet();
|
|
10
14
|
export function registerGuestClosure(closure) {
|
|
11
15
|
guestClosures.add(closure);
|
|
@@ -61,10 +65,36 @@ export function getGuestFunctionProperty(closure, key) {
|
|
|
61
65
|
export function installObjectPrototype(budget, prototype, constructor) {
|
|
62
66
|
prototypes.set(prototype, null);
|
|
63
67
|
intrinsicPrototypes.set(budget, prototype);
|
|
68
|
+
registerIntrinsicPrototype(budget, prototype, constructor);
|
|
69
|
+
}
|
|
70
|
+
export function installBoxedPrototype(budget, prototype, constructor) {
|
|
71
|
+
let state = boxedPrototypes.get(budget);
|
|
72
|
+
if (state === undefined)
|
|
73
|
+
boxedPrototypes.set(budget, state = new Map());
|
|
74
|
+
state.set(typeof boxedValue(prototype), prototype);
|
|
75
|
+
initialBoxedMethods.set(prototype, new Map(Object.entries(Object.getOwnPropertyDescriptors(prototype)).map(([key, descriptor]) => [key, descriptor.value])));
|
|
76
|
+
registerIntrinsicPrototype(budget, prototype, constructor);
|
|
77
|
+
}
|
|
78
|
+
export function getBoxedPrototype(value, budget) {
|
|
79
|
+
return boxedPrototypes.get(budget)?.get(typeof value);
|
|
80
|
+
}
|
|
81
|
+
export function isDefaultBoxedMethod(value, key, budget) {
|
|
82
|
+
const prototype = getBoxedPrototype(value, budget);
|
|
83
|
+
return prototype !== undefined && initialBoxedMethods.get(prototype)?.has(key) === true &&
|
|
84
|
+
Object.getOwnPropertyDescriptor(prototype, key)?.value === initialBoxedMethods.get(prototype)?.get(key);
|
|
85
|
+
}
|
|
86
|
+
export function isIntrinsicConstructor(value) {
|
|
87
|
+
return intrinsicConstructors.has(value);
|
|
88
|
+
}
|
|
89
|
+
function registerIntrinsicPrototype(budget, prototype, constructor) {
|
|
90
|
+
let roots = intrinsicPrototypeRoots.get(budget);
|
|
91
|
+
if (roots === undefined)
|
|
92
|
+
intrinsicPrototypeRoots.set(budget, roots = new Set());
|
|
93
|
+
roots.add(prototype);
|
|
64
94
|
const records = [prototype, materializeFunctionProperties(constructor)].map(value => ({
|
|
65
95
|
value, descriptors: new Map(Object.entries(Object.getOwnPropertyDescriptors(value)))
|
|
66
96
|
}));
|
|
67
|
-
const unchanged = (before, after) => before !== undefined && after !== undefined && before.value
|
|
97
|
+
const unchanged = (before, after) => before !== undefined && after !== undefined && Object.is(before.value, after.value) &&
|
|
68
98
|
before.writable === after.writable && before.configurable === after.configurable && before.enumerable === after.enumerable;
|
|
69
99
|
intrinsicConstructors.set(constructor, () => records.every(({ value, descriptors }) => {
|
|
70
100
|
const current = Object.getOwnPropertyDescriptors(value);
|
|
@@ -74,14 +104,20 @@ export function installObjectPrototype(budget, prototype, constructor) {
|
|
|
74
104
|
budget.setRetainedValues(prototype, () => records.flatMap(({ value, descriptors }) => Object.entries(Object.getOwnPropertyDescriptors(value)).flatMap(([key, descriptor]) => unchanged(descriptors.get(key), descriptor) ? [] : [key, descriptor.value])));
|
|
75
105
|
}
|
|
76
106
|
export function releaseObjectPrototype(budget) {
|
|
77
|
-
const prototype
|
|
78
|
-
if (prototype !== undefined)
|
|
107
|
+
for (const prototype of intrinsicPrototypeRoots.get(budget) ?? [])
|
|
79
108
|
budget.setRetainedValues(prototype, undefined);
|
|
109
|
+
intrinsicPrototypeRoots.delete(budget);
|
|
110
|
+
boxedPrototypes.delete(budget);
|
|
80
111
|
intrinsicPrototypes.delete(budget);
|
|
81
112
|
}
|
|
82
113
|
export function getSandboxPrototype(value, budget) {
|
|
83
114
|
if (prototypes.has(value))
|
|
84
115
|
return prototypes.get(value) ?? null;
|
|
116
|
+
if (budget !== undefined && isSandboxBox(value)) {
|
|
117
|
+
const prototype = getBoxedPrototype(boxedValue(value), budget);
|
|
118
|
+
if (prototype !== undefined && prototype !== value)
|
|
119
|
+
return prototype;
|
|
120
|
+
}
|
|
85
121
|
return budget !== undefined && isPrototypeRecord(value)
|
|
86
122
|
? intrinsicPrototypes.get(budget) ?? null
|
|
87
123
|
: null;
|
|
@@ -167,6 +203,8 @@ export function hasGuestObjectState(value) {
|
|
|
167
203
|
return true;
|
|
168
204
|
if (functionProperties.has(value) || prototypes.has(value))
|
|
169
205
|
return true;
|
|
206
|
+
if (isSandboxBox(value))
|
|
207
|
+
return false;
|
|
170
208
|
return (descriptorObjects.has(value) &&
|
|
171
209
|
Object.values(Object.getOwnPropertyDescriptors(value)).some((descriptor) => !descriptor.enumerable || !descriptor.configurable || !descriptor.writable));
|
|
172
210
|
}
|