poe-code 14.0.61 → 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 +1147 -574
- 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-VAYQP2VZ.js → chunk-KMTTD3BL.js} +7 -3
- package/packages/safe-js/dist/chunks/chunk-KMTTD3BL.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-ZTGKRKQH.js → chunk-RU2BCN4G.js} +1069 -577
- 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/async.js +3 -0
- 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/function-string.d.ts +2 -0
- package/packages/safe-js/dist/interp/function-string.js +21 -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 +12 -13
- package/packages/safe-js/dist/interp/globals/numeric-parsers.d.ts +5 -0
- package/packages/safe-js/dist/interp/globals/numeric-parsers.js +47 -0
- package/packages/safe-js/dist/interp/globals/object-array.js +21 -24
- 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 +173 -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/function.d.ts +2 -0
- package/packages/safe-js/dist/interp/methods/function.js +10 -1
- 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/resources.d.ts +1 -0
- package/packages/safe-js/dist/interp/string-coercion.js +7 -1
- package/packages/safe-js/dist/interp/values.d.ts +4 -0
- package/packages/safe-js/dist/interp/values.js +81 -3
- package/packages/safe-js/dist/migrate.d.ts +2 -2
- package/packages/safe-js/dist/migrate.js +1 -1
- package/packages/safe-js/dist/migration-file.d.ts +4 -4
- package/packages/safe-js/dist/parse/function-source.d.ts +7 -0
- package/packages/safe-js/dist/parse/function-source.js +1 -0
- package/packages/safe-js/dist/parse/hash.d.ts +2 -2
- package/packages/safe-js/dist/parse/hash.js +23 -9
- package/packages/safe-js/dist/parse/parser.js +36 -23
- package/packages/safe-js/dist/restore.js +2 -1
- package/packages/safe-js/dist/run.js +16 -5
- 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.d.ts +1 -1
- package/packages/safe-js/dist/snapshot/dump-format.js +19 -6
- package/packages/safe-js/dist/snapshot/migration.js +2 -2
- 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 +10 -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-VAYQP2VZ.js.map +0 -7
- package/packages/safe-js/dist/chunks/chunk-ZTGKRKQH.js.map +0 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { normalizeClosureResult } from "./async.js";
|
|
2
2
|
import { copyNativeDate } from "./date.js";
|
|
3
|
+
import { boxedDataProperties, createSandboxBox, nativeBoxedValue } from "./boxed.js";
|
|
3
4
|
import { exportHostCapability, importHostCapability, isLiveCapability } from "./host-capabilities.js";
|
|
4
5
|
import { attachErrorSpan, replaceErrorStack } from "../error/shape.js";
|
|
5
6
|
import { SandboxError } from "./budget.js";
|
|
@@ -670,6 +671,27 @@ export function copyHostValueToSandbox(value, stackFrames, options, state, path)
|
|
|
670
671
|
: path
|
|
671
672
|
}, state);
|
|
672
673
|
}
|
|
674
|
+
const primitive = nativeBoxedValue(value);
|
|
675
|
+
if (primitive !== undefined) {
|
|
676
|
+
if (typeof primitive === "string")
|
|
677
|
+
budget.allocateString(primitive);
|
|
678
|
+
const original = value;
|
|
679
|
+
const existing = state.seen.get(original);
|
|
680
|
+
if (existing !== undefined)
|
|
681
|
+
return existing;
|
|
682
|
+
const copy = createSandboxBox(primitive);
|
|
683
|
+
state.seen.set(original, copy);
|
|
684
|
+
budget.chargeDataUsage(measureSandboxData([copy]));
|
|
685
|
+
for (const [key, descriptor] of boxedDataProperties(original)) {
|
|
686
|
+
if (!("value" in descriptor))
|
|
687
|
+
throw new TypeError(`Unsupported sandbox value at ${joinPath(path, key)}: accessor property`);
|
|
688
|
+
Object.defineProperty(copy, budget.allocateString(key), {
|
|
689
|
+
...descriptor,
|
|
690
|
+
value: copyHostValueToSandbox(descriptor.value, stackFrames, { ...options, capabilityPath: [...(options.capabilityPath ?? []), key] }, state, joinPath(path, key))
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
return copy;
|
|
694
|
+
}
|
|
673
695
|
const date = copyNativeDate(value);
|
|
674
696
|
if (date !== undefined) {
|
|
675
697
|
const existing = state.seen.get(value);
|
|
@@ -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) {
|
|
@@ -2546,6 +2598,7 @@ function createSetMethodOptions(context) {
|
|
|
2546
2598
|
}
|
|
2547
2599
|
function createFunctionMethodOptions(context) {
|
|
2548
2600
|
return {
|
|
2601
|
+
budget: context.budget,
|
|
2549
2602
|
callClosure: (closure, args, stack, thisValue, construct) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue, construct)
|
|
2550
2603
|
};
|
|
2551
2604
|
}
|
|
@@ -2625,12 +2678,12 @@ async function evaluateSpreadElement(node, context) {
|
|
|
2625
2678
|
result: value
|
|
2626
2679
|
};
|
|
2627
2680
|
}
|
|
2628
|
-
const iterator = getSandboxIterator(value.value, context.budget);
|
|
2681
|
+
const iterator = getSandboxIterator(value.value, context.budget, createCoercionContext(context));
|
|
2629
2682
|
if (iterator === undefined) {
|
|
2630
2683
|
throw new TypeError("Spread arguments must evaluate to an iterable.");
|
|
2631
2684
|
}
|
|
2632
2685
|
const spreadValues = [];
|
|
2633
|
-
const release = retainValues(context.budget, () => [value.value, ...spreadValues]);
|
|
2686
|
+
const release = retainValues(context.budget, () => [value.value, iterator.retainedValue, ...spreadValues]);
|
|
2634
2687
|
try {
|
|
2635
2688
|
while (true) {
|
|
2636
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,5 +1,7 @@
|
|
|
1
1
|
import { type SandboxClosure, type SandboxValue } from "../values.js";
|
|
2
|
+
import type { Budget } from "../budget.js";
|
|
2
3
|
export type FunctionMethodOptions = {
|
|
4
|
+
budget?: Budget;
|
|
3
5
|
callClosure: (closure: SandboxClosure, args: readonly SandboxValue[], stack: readonly string[], thisValue: SandboxValue, construct?: boolean) => Promise<SandboxValue> | SandboxValue;
|
|
4
6
|
};
|
|
5
7
|
export declare function getFunctionMember(target: SandboxClosure, property: string | number, options: FunctionMethodOptions): SandboxValue | undefined;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createSandboxClosure, isSandboxClosure } from "../values.js";
|
|
2
2
|
import { getGuestFunctionProperty, isGuestClosure } from "../object-model.js";
|
|
3
|
-
|
|
3
|
+
import { functionString } from "../function-string.js";
|
|
4
|
+
import { runResources } from "../resources.js";
|
|
5
|
+
const functionMethodNames = new Set(["apply", "bind", "call", "toString"]);
|
|
4
6
|
export function getFunctionMember(target, property, options) {
|
|
5
7
|
if (isGuestClosure(target)) {
|
|
6
8
|
const value = getGuestFunctionProperty(target, String(property));
|
|
@@ -16,12 +18,15 @@ export function getFunctionMember(target, property, options) {
|
|
|
16
18
|
if (property === "length") {
|
|
17
19
|
return target.length;
|
|
18
20
|
}
|
|
21
|
+
if (property === "toString" && runResources.getStore()?.functionSourceText === false)
|
|
22
|
+
return undefined;
|
|
19
23
|
if (!isFunctionMethodName(property)) {
|
|
20
24
|
return undefined;
|
|
21
25
|
}
|
|
22
26
|
return createSandboxClosure({
|
|
23
27
|
sandbox: true,
|
|
24
28
|
name: `Function#${property}`,
|
|
29
|
+
...(property === "toString" ? { length: 0 } : {}),
|
|
25
30
|
call: (args, context) => callFunctionMethod(context?.thisValue, property, args, options, context?.stack ?? [])
|
|
26
31
|
});
|
|
27
32
|
}
|
|
@@ -32,6 +37,10 @@ function callFunctionMethod(target, methodName, args, options, stack) {
|
|
|
32
37
|
if (!isSandboxClosure(target)) {
|
|
33
38
|
throw new TypeError(`Function#${methodName} requires a callable receiver.`);
|
|
34
39
|
}
|
|
40
|
+
if (methodName === "toString") {
|
|
41
|
+
const text = functionString(target);
|
|
42
|
+
return options.budget?.allocateString(text) ?? text;
|
|
43
|
+
}
|
|
35
44
|
const thisValue = args[0];
|
|
36
45
|
if (methodName === "bind") {
|
|
37
46
|
const boundArgs = args.slice(1);
|
|
@@ -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;
|