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.
Files changed (67) hide show
  1. package/dist/index.js +1147 -574
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-VAYQP2VZ.js → chunk-KMTTD3BL.js} +7 -3
  6. package/packages/safe-js/dist/chunks/chunk-KMTTD3BL.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-ZTGKRKQH.js → chunk-RU2BCN4G.js} +1069 -577
  8. package/packages/safe-js/dist/chunks/chunk-RU2BCN4G.js.map +7 -0
  9. package/packages/safe-js/dist/cli.js +2 -2
  10. package/packages/safe-js/dist/core.js +1 -1
  11. package/packages/safe-js/dist/graph-depth.js +3 -0
  12. package/packages/safe-js/dist/index.js +2 -2
  13. package/packages/safe-js/dist/interp/async.js +3 -0
  14. package/packages/safe-js/dist/interp/boxed.d.ts +14 -0
  15. package/packages/safe-js/dist/interp/boxed.js +50 -0
  16. package/packages/safe-js/dist/interp/function-string.d.ts +2 -0
  17. package/packages/safe-js/dist/interp/function-string.js +21 -0
  18. package/packages/safe-js/dist/interp/globals/collections.js +4 -4
  19. package/packages/safe-js/dist/interp/globals/console-json.js +15 -18
  20. package/packages/safe-js/dist/interp/globals/misc.js +12 -13
  21. package/packages/safe-js/dist/interp/globals/numeric-parsers.d.ts +5 -0
  22. package/packages/safe-js/dist/interp/globals/numeric-parsers.js +47 -0
  23. package/packages/safe-js/dist/interp/globals/object-array.js +21 -24
  24. package/packages/safe-js/dist/interp/globals/object.js +10 -5
  25. package/packages/safe-js/dist/interp/globals/primitives.d.ts +8 -0
  26. package/packages/safe-js/dist/interp/globals/primitives.js +69 -0
  27. package/packages/safe-js/dist/interp/host-bridge.js +22 -0
  28. package/packages/safe-js/dist/interp/interpreter.js +173 -120
  29. package/packages/safe-js/dist/interp/iteration.d.ts +5 -3
  30. package/packages/safe-js/dist/interp/iteration.js +22 -1
  31. package/packages/safe-js/dist/interp/methods/array.js +6 -3
  32. package/packages/safe-js/dist/interp/methods/function.d.ts +2 -0
  33. package/packages/safe-js/dist/interp/methods/function.js +10 -1
  34. package/packages/safe-js/dist/interp/methods/number.d.ts +1 -0
  35. package/packages/safe-js/dist/interp/methods/number.js +4 -1
  36. package/packages/safe-js/dist/interp/methods/string.d.ts +2 -0
  37. package/packages/safe-js/dist/interp/methods/string.js +2 -2
  38. package/packages/safe-js/dist/interp/object-model.d.ts +5 -0
  39. package/packages/safe-js/dist/interp/object-model.js +41 -3
  40. package/packages/safe-js/dist/interp/promise.js +68 -61
  41. package/packages/safe-js/dist/interp/resources.d.ts +1 -0
  42. package/packages/safe-js/dist/interp/string-coercion.js +7 -1
  43. package/packages/safe-js/dist/interp/values.d.ts +4 -0
  44. package/packages/safe-js/dist/interp/values.js +81 -3
  45. package/packages/safe-js/dist/migrate.d.ts +2 -2
  46. package/packages/safe-js/dist/migrate.js +1 -1
  47. package/packages/safe-js/dist/migration-file.d.ts +4 -4
  48. package/packages/safe-js/dist/parse/function-source.d.ts +7 -0
  49. package/packages/safe-js/dist/parse/function-source.js +1 -0
  50. package/packages/safe-js/dist/parse/hash.d.ts +2 -2
  51. package/packages/safe-js/dist/parse/hash.js +23 -9
  52. package/packages/safe-js/dist/parse/parser.js +36 -23
  53. package/packages/safe-js/dist/restore.js +2 -1
  54. package/packages/safe-js/dist/run.js +16 -5
  55. package/packages/safe-js/dist/snapshot/boxed.d.ts +16 -0
  56. package/packages/safe-js/dist/snapshot/boxed.js +52 -0
  57. package/packages/safe-js/dist/snapshot/dump-format.d.ts +1 -1
  58. package/packages/safe-js/dist/snapshot/dump-format.js +19 -6
  59. package/packages/safe-js/dist/snapshot/migration.js +2 -2
  60. package/packages/safe-js/dist/snapshot/replay-data.d.ts +5 -0
  61. package/packages/safe-js/dist/snapshot/replay-data.js +24 -0
  62. package/packages/safe-js/dist/snapshot/restore.js +10 -0
  63. package/packages/safe-js/dist/snapshot/serialize.d.ts +4 -2
  64. package/packages/safe-js/dist/snapshot/serialize.js +18 -10
  65. package/packages/safe-js/dist/snapshot/validation.js +22 -1
  66. package/packages/safe-js/dist/chunks/chunk-VAYQP2VZ.js.map +0 -7
  67. 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, () => [left.value, rightValue]);
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
- const value = applyBinaryOperator(node, leftValue, right.value, context);
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 nodeId = node.nodeId ?? -1;
1087
- let index = consumeRestoredLoopIterationIndex(node, context);
1088
- for (let skipped = 0; skipped < index; skipped += 1) {
1089
- const skippedIteration = await iterator.next();
1090
- if (typeof skippedIteration !== "object" || skippedIteration === null) {
1091
- throw new TypeError("Iterator result must be an object.");
1092
- }
1093
- if (skippedIteration.done) {
1094
- return normalEmptyResult();
1095
- }
1096
- }
1097
- while (true) {
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
- if (isMatchingContinue(result, loopLabels(node))) {
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
- index += 1;
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
- let completion = {
1531
- type: "normal",
1532
- value: undefined
1533
- };
1534
- const replay = context.generatorResume?.sent ?? [];
1535
- let replayIndex = 0;
1536
- while (true) {
1537
- const method = completion.type === "normal" ? "next" : completion.type;
1538
- const iteratorMethod = iterator[method];
1539
- if (iteratorMethod === undefined) {
1540
- if (completion.type === "throw") {
1541
- throw completion.value;
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
- return generatorCompletionResult(completion);
1544
- }
1545
- const result = await iteratorMethod(completion.value);
1546
- if (result.done) {
1547
- if (completion.type === "return") {
1548
- return generatorCompletionResult({ type: "return", value: result.value });
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
- return {
1551
- kind: "normal",
1552
- hasValue: true,
1553
- value: result.value
1554
- };
1555
- }
1556
- if (replayIndex < replay.length - 1) {
1557
- completion = replay[replayIndex + 1];
1558
- replayIndex += 1;
1559
- continue;
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
- const completionPromise = context.generatorYield(allocateProducedSandboxValue(result.value, context.budget), node.nodeId);
1562
- emitResumeBreakpoint(context, {
1563
- kind: "generator-yield",
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
- left = await toNumericPrimitive(left, context);
2194
- right = await toNumericPrimitive(right, context);
2195
- switch (operator) {
2196
- case "+=":
2197
- return applyAdditionOperator(left, right, context);
2198
- case "-=":
2199
- return toNumber(left) - toNumber(right);
2200
- case "*=":
2201
- return toNumber(left) * toNumber(right);
2202
- case "/=":
2203
- return toNumber(left) / toNumber(right);
2204
- case "%=":
2205
- return toNumber(left) % toNumber(right);
2206
- case "**=":
2207
- return toNumber(left) ** toNumber(right);
2208
- case "&=":
2209
- return toNumber(left) & toNumber(right);
2210
- case "|=":
2211
- return toNumber(left) | toNumber(right);
2212
- case "^=":
2213
- return toNumber(left) ^ toNumber(right);
2214
- case "<<=":
2215
- return toNumber(left) << toNumber(right);
2216
- case ">>=":
2217
- return toNumber(left) >> toNumber(right);
2218
- case ">>>=":
2219
- return toNumber(left) >>> toNumber(right);
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 type { Budget } from "./budget.js";
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
- export function getSandboxIterator(value, budget) {
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 { createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet } from "../values.js";
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
- throw new TypeError("Object primitive boxing is not supported.");
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
- const functionMethodNames = new Set(["apply", "bind", "call"]);
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;