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
@@ -22,7 +22,7 @@ import {
22
22
  resolveFsConfig,
23
23
  splitFrontmatter,
24
24
  withBrokenPipeGuard
25
- } from "./chunks/chunk-VAYQP2VZ.js";
25
+ } from "./chunks/chunk-KMTTD3BL.js";
26
26
  import {
27
27
  Budget,
28
28
  SandboxError,
@@ -34,7 +34,7 @@ import {
34
34
  replaceErrorStack,
35
35
  restore,
36
36
  run
37
- } from "./chunks/chunk-ZTGKRKQH.js";
37
+ } from "./chunks/chunk-RU2BCN4G.js";
38
38
  import "./chunks/chunk-UXMKEUH3.js";
39
39
  import "./chunks/chunk-5DYEOCT5.js";
40
40
  import "./chunks/chunk-4YIDX44G.js";
@@ -5,7 +5,7 @@ import {
5
5
  defineExtension,
6
6
  lint,
7
7
  run
8
- } from "./chunks/chunk-ZTGKRKQH.js";
8
+ } from "./chunks/chunk-RU2BCN4G.js";
9
9
  import "./chunks/chunk-4YIDX44G.js";
10
10
  export {
11
11
  Budget,
@@ -3,6 +3,7 @@ import { SandboxError } from "./interp/budget.js";
3
3
  import { getSandboxArgumentEntries, isSandboxArguments } from "./interp/arguments.js";
4
4
  import { collectionIteratorState, isSandboxCollectionIterator } from "./interp/collection-iterator.js";
5
5
  import { isSandboxMap, isSandboxSet } from "./interp/collection-brands.js";
6
+ import { boxedDataProperties, isSandboxBox } from "./interp/boxed.js";
6
7
  export const MAX_DATA_DEPTH = 1_024;
7
8
  export class SnapshotBudgetError extends Error {
8
9
  code = "budgetExceeded";
@@ -63,6 +64,8 @@ function walkGraphDepth(root, rootPath, assertDepth) {
63
64
  }
64
65
  }
65
66
  function graphEntries(value) {
67
+ if (isSandboxBox(value))
68
+ return boxedDataProperties(value).map(([key, descriptor]) => [`.${key}`, descriptor.value]);
66
69
  if (isSandboxArguments(value)) {
67
70
  return getSandboxArgumentEntries(value).map(([key, entry]) => [`.${key}`, entry]);
68
71
  }
@@ -26,7 +26,7 @@ import {
26
26
  runWithSpawnUsageAccumulator,
27
27
  splitFrontmatter,
28
28
  supportsSpawnMode
29
- } from "./chunks/chunk-VAYQP2VZ.js";
29
+ } from "./chunks/chunk-KMTTD3BL.js";
30
30
  import {
31
31
  Budget,
32
32
  FileSnapshotBackend,
@@ -50,7 +50,7 @@ import {
50
50
  registerPendingHostCallPolicy,
51
51
  restore,
52
52
  run
53
- } from "./chunks/chunk-ZTGKRKQH.js";
53
+ } from "./chunks/chunk-RU2BCN4G.js";
54
54
  import "./chunks/chunk-UXMKEUH3.js";
55
55
  import "./chunks/chunk-5DYEOCT5.js";
56
56
  import "./chunks/chunk-4YIDX44G.js";
@@ -8,6 +8,7 @@ import { awaitSandboxValue } from "./cancel.js";
8
8
  import { hoistVarDeclarations } from "./var-hoist.js";
9
9
  import { createPatternContext } from "./interpreter.js";
10
10
  import { getGuestFunctionProperty, setSandboxPrototype } from "./object-model.js";
11
+ import { functionSources } from "../parse/function-source.js";
11
12
  import { boundIdentifiers, containsParameterExpression, getFunctionLength, hoistedVarDeclarations } from "../parse/bindings.js";
12
13
  import { createSandboxArguments, createSandboxClosure, createSandboxGenerator, createSandboxPromise, allocateProducedSandboxValue, reconcileCompiledValues, isSandboxPromise } from "./values.js";
13
14
  export function emitResumeBreakpoint(context, breakpoint) {
@@ -62,6 +63,7 @@ export function createInterpretedClosure(node, context, evaluateNode) {
62
63
  }
63
64
  : undefined;
64
65
  const closure = createSandboxClosure({
66
+ sourceRange: functionSources.get(node),
65
67
  guest: true,
66
68
  sandbox: true,
67
69
  length: getFunctionLength(node.params),
@@ -116,6 +118,7 @@ export function executeAsyncFunction(execute, budget, signal) {
116
118
  }
117
119
  function createGeneratorClosure(node, context, evaluateNode) {
118
120
  return createSandboxClosure({
121
+ sourceRange: functionSources.get(node),
119
122
  guest: true,
120
123
  generator: true,
121
124
  sandbox: true,
@@ -0,0 +1,14 @@
1
+ import type { SandboxObject } from "./values.js";
2
+ export type BoxedPrimitive = string | number | boolean;
3
+ export type BoxedKind = "string" | "number" | "boolean";
4
+ declare const boxedPrimitiveBrand: unique symbol;
5
+ export type SandboxBox = SandboxObject & {
6
+ readonly [boxedPrimitiveBrand]: true;
7
+ };
8
+ export declare function nativeBoxedValue(value: unknown): BoxedPrimitive | undefined;
9
+ export declare function createSandboxBox(value: unknown): SandboxBox;
10
+ export declare function isSandboxBox(value: unknown): value is SandboxBox;
11
+ export declare function boxedValue(value: object): BoxedPrimitive;
12
+ export declare function primitiveReceiver(value: unknown, kind: BoxedKind): BoxedPrimitive;
13
+ export declare function boxedDataProperties(value: object): Array<[string, PropertyDescriptor]>;
14
+ export {};
@@ -0,0 +1,50 @@
1
+ import { types } from "node:util";
2
+ const boxes = new WeakSet();
3
+ const numberValue = Number.prototype.valueOf;
4
+ const stringValue = String.prototype.valueOf;
5
+ const booleanValue = Boolean.prototype.valueOf;
6
+ export function nativeBoxedValue(value) {
7
+ if (types.isNumberObject(value))
8
+ return Reflect.apply(numberValue, value, []);
9
+ if (types.isStringObject(value))
10
+ return Reflect.apply(stringValue, value, []);
11
+ if (types.isBooleanObject(value))
12
+ return Reflect.apply(booleanValue, value, []);
13
+ return undefined;
14
+ }
15
+ export function createSandboxBox(value) {
16
+ if (typeof value !== "number" && typeof value !== "string" && typeof value !== "boolean")
17
+ throw new TypeError("Invalid boxed primitive payload.");
18
+ const box = Object(value);
19
+ Object.setPrototypeOf(box, null);
20
+ boxes.add(box);
21
+ return box;
22
+ }
23
+ export function isSandboxBox(value) {
24
+ return typeof value === "object" && value !== null && boxes.has(value);
25
+ }
26
+ export function boxedValue(value) {
27
+ if (!boxes.has(value))
28
+ throw new TypeError("Expected a sandbox boxed primitive.");
29
+ return nativeBoxedValue(value);
30
+ }
31
+ export function primitiveReceiver(value, kind) {
32
+ const primitive = isSandboxBox(value) ? boxedValue(value) : value;
33
+ if (typeof primitive !== kind)
34
+ throw new TypeError(`${kind} method requires a ${kind} receiver.`);
35
+ return primitive;
36
+ }
37
+ export function boxedDataProperties(value) {
38
+ const primitive = nativeBoxedValue(value);
39
+ return Object.entries(Object.getOwnPropertyDescriptors(value)).filter(([key]) => {
40
+ if (typeof primitive !== "string")
41
+ return true;
42
+ if (key === "length")
43
+ return false;
44
+ const index = Number(key);
45
+ return !(Number.isInteger(index) &&
46
+ index >= 0 &&
47
+ index < primitive.length &&
48
+ String(index) === key);
49
+ });
50
+ }
@@ -0,0 +1,2 @@
1
+ import type { SandboxClosure } from "./values.js";
2
+ export declare function functionString(value: SandboxClosure): string;
@@ -0,0 +1,21 @@
1
+ import { runResources } from "./resources.js";
2
+ export function functionString(value) {
3
+ if (runResources.getStore()?.functionSourceText === false)
4
+ return "[object Object]";
5
+ const source = value.sourceRange;
6
+ if (source !== undefined)
7
+ return source.text.slice(source.start, source.end);
8
+ let name = value.boundTarget === undefined ? (value.name?.split("#").at(-1) ?? "") : "";
9
+ for (let index = 0; index < name.length; index++) {
10
+ const code = name.charCodeAt(index);
11
+ if (!((code >= 65 && code <= 90) ||
12
+ (code >= 97 && code <= 122) ||
13
+ code === 36 ||
14
+ code === 95 ||
15
+ (index > 0 && code >= 48 && code <= 57))) {
16
+ name = "";
17
+ break;
18
+ }
19
+ }
20
+ return `function ${name}() { [native code] }`;
21
+ }
@@ -71,13 +71,13 @@ function populateCollection(source, collection, { name, budget, context, append,
71
71
  budget.allocateCollectionEntries(0);
72
72
  if (source === undefined || source === null)
73
73
  return collection;
74
- const iterator = getSandboxIterator(source, budget);
74
+ const iterator = getSandboxIterator(source, budget, context);
75
75
  if (iterator === undefined)
76
76
  throw new TypeError(`${name} constructor requires an iterable.`);
77
77
  let entry;
78
78
  let failure;
79
79
  const retained = {};
80
- budget.setRetainedValues(retained, () => [source, collection, entry, failure, ...(retainedValues?.() ?? [])]);
80
+ budget.setRetainedValues(retained, () => [source, iterator.retainedValue, collection, entry, failure, ...(retainedValues?.() ?? [])]);
81
81
  const checkData = createDataCheckpoint(budget, context);
82
82
  const closeOnThrow = (error) => {
83
83
  failure = isCapturedException(error) ? error.reason : error;
@@ -88,7 +88,7 @@ function populateCollection(source, collection, { name, budget, context, append,
88
88
  };
89
89
  try {
90
90
  const closing = iterator.return?.();
91
- if (iterator.generator)
91
+ if (iterator.generator || iterator.asynchronous)
92
92
  return Promise.resolve(closing).then(() => { throw error; }, rethrow);
93
93
  }
94
94
  catch (closeError) {
@@ -113,7 +113,7 @@ function populateCollection(source, collection, { name, budget, context, append,
113
113
  }
114
114
  return false;
115
115
  };
116
- if (iterator.generator) {
116
+ if (iterator.generator || iterator.asynchronous) {
117
117
  return (async () => {
118
118
  try {
119
119
  checkData(collection, 0, true);
@@ -1,4 +1,6 @@
1
1
  import { dateMethods, isSandboxDate } from "../date.js";
2
+ import { boxedValue, isSandboxBox } from "../boxed.js";
3
+ import { sandboxNumber, sandboxString } from "../string-coercion.js";
2
4
  import { CompileScope } from "../regex/compile-guard.js";
3
5
  import { wrapCallerInjectedBindings } from "../host-bridge.js";
4
6
  import { allocateProducedSandboxValue, createSandboxClosure, deepCopyFromSandbox, isSandboxClosure, isSandboxPromise } from "../values.js";
@@ -8,12 +10,15 @@ export function createConsoleJsonGlobals(options) {
8
10
  JSON: {
9
11
  parse: createSandboxClosure({
10
12
  sandbox: true,
11
- call: async ([text]) => parseJson(text, options.budget),
13
+ call: async ([text], context) => {
14
+ const converted = sandboxString(text, options.budget, context);
15
+ return copyJsonToSandbox(JSON.parse(converted instanceof Promise ? await converted : converted), options.budget);
16
+ },
12
17
  name: "parse"
13
18
  }),
14
19
  stringify: createSandboxClosure({
15
20
  sandbox: true,
16
- call: async ([value, replacer, indent]) => stringifyJson(value, replacer, indent, options.budget),
21
+ call: async ([value, replacer, indent], context) => stringifyJson(value, replacer, indent, options.budget, context),
17
22
  name: "stringify"
18
23
  })
19
24
  },
@@ -69,11 +74,7 @@ export function createConsoleJsonGlobals(options) {
69
74
  })
70
75
  };
71
76
  }
72
- function parseJson(input, budget) {
73
- const text = budget.allocateString(toJsonParseText(input));
74
- return copyJsonToSandbox(JSON.parse(text), budget);
75
- }
76
- async function stringifyJson(value, replacer, indent, budget) {
77
+ async function stringifyJson(value, replacer, indent, budget, context) {
77
78
  if (replacer !== undefined && replacer !== null && !isSandboxClosure(replacer)) {
78
79
  throw new TypeError("JSON.stringify(value, replacer, indent) only supports function, null, or undefined replacers.");
79
80
  }
@@ -84,6 +85,7 @@ async function stringifyJson(value, replacer, indent, budget) {
84
85
  defineDataProperty(holder, "", value);
85
86
  const output = await stringifyProperty("", holder, {
86
87
  budget,
88
+ context,
87
89
  gap: normalizeStringifyGap(indent),
88
90
  replacer: isSandboxClosure(replacer) ? replacer : undefined,
89
91
  stack: []
@@ -93,17 +95,6 @@ async function stringifyJson(value, replacer, indent, budget) {
93
95
  }
94
96
  return budget.allocateString(output);
95
97
  }
96
- function toJsonParseText(input) {
97
- if (Array.isArray(input)) {
98
- return input
99
- .map((entry) => (entry === null || entry === undefined ? "" : toJsonParseText(entry)))
100
- .join(",");
101
- }
102
- if (typeof input === "object" && input !== null) {
103
- return "[object Object]";
104
- }
105
- return String(input);
106
- }
107
98
  async function stringifyProperty(key, holder, state, indent = "") {
108
99
  let value = getOwnDataValue(holder, key);
109
100
  if (isSandboxDate(value)) {
@@ -121,6 +112,12 @@ async function stringifyProperty(key, holder, state, indent = "") {
121
112
  return stringifyValue(value, state, indent);
122
113
  }
123
114
  async function stringifyValue(value, state, indent) {
115
+ if (isSandboxBox(value)) {
116
+ const primitive = boxedValue(value);
117
+ value = typeof primitive === "number" ? await sandboxNumber(value, state.budget, state.context)
118
+ : typeof primitive === "string" ? await sandboxString(value, state.budget, state.context)
119
+ : primitive;
120
+ }
124
121
  if (value === null) {
125
122
  return "null";
126
123
  }
@@ -1,6 +1,8 @@
1
1
  import { isSandboxCollectionIterator } from "../collection-iterator.js";
2
2
  import { assertSandboxGraphDepth } from "../../graph-depth.js";
3
3
  import { CompileScope } from "../regex/compile-guard.js";
4
+ import { createNumericParsers } from "./numeric-parsers.js";
5
+ import { sandboxNumber } from "../string-coercion.js";
4
6
  import { allocateProducedSandboxValue, cloneSandboxValue, createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet, reconcileCompiledValues } from "../values.js";
5
7
  export function createMiscGlobals(options) {
6
8
  return {
@@ -9,24 +11,21 @@ export function createMiscGlobals(options) {
9
11
  call: ([value], context) => structuredCloneSandboxValue(value, options.budget, context?.compilation),
10
12
  name: "structuredClone"
11
13
  }),
12
- parseInt: createSandboxClosure({
13
- sandbox: true,
14
- call: (args) => Reflect.apply(globalThis.parseInt, globalThis, [...args]),
15
- name: "parseInt"
16
- }),
17
- parseFloat: createSandboxClosure({
18
- sandbox: true,
19
- call: (args) => Reflect.apply(globalThis.parseFloat, globalThis, [...args]),
20
- name: "parseFloat"
21
- }),
14
+ ...createNumericParsers(options.budget),
22
15
  isNaN: createSandboxClosure({
23
16
  sandbox: true,
24
- call: ([value]) => globalThis.isNaN(value),
17
+ call: ([value], context) => {
18
+ const number = sandboxNumber(value, options.budget, context);
19
+ return typeof number === "number" ? Number.isNaN(number) : number.then(Number.isNaN);
20
+ },
25
21
  name: "isNaN"
26
22
  }),
27
23
  isFinite: createSandboxClosure({
28
24
  sandbox: true,
29
- call: ([value]) => globalThis.isFinite(value),
25
+ call: ([value], context) => {
26
+ const number = sandboxNumber(value, options.budget, context);
27
+ return typeof number === "number" ? Number.isFinite(number) : number.then(Number.isFinite);
28
+ },
30
29
  name: "isFinite"
31
30
  })
32
31
  };
@@ -35,7 +34,7 @@ function structuredCloneSandboxValue(value, budget, parent) {
35
34
  const operation = budget.acquireCompileOwner(false, parent?.owner);
36
35
  const compilation = parent?.owner === operation.owner ? parent : new CompileScope(operation.owner);
37
36
  try {
38
- const clone = cloneSandboxValue(value, { compilation, resetRegexLastIndex: true });
37
+ const clone = cloneSandboxValue(value, { compilation, resetRegexLastIndex: true, structuredClone: true });
39
38
  assertSandboxGraphDepth(clone);
40
39
  assertStructuredCloneable(clone, new WeakSet());
41
40
  allocateProducedSandboxValue(clone, budget);
@@ -0,0 +1,5 @@
1
+ import type { Budget } from "../budget.js";
2
+ export declare function createNumericParsers(budget: Budget): {
3
+ parseInt: import("../values.js").SandboxClosure;
4
+ parseFloat: import("../values.js").SandboxClosure;
5
+ };
@@ -0,0 +1,47 @@
1
+ import { retainValues } from "../resources.js";
2
+ import { sandboxNumber, sandboxString } from "../string-coercion.js";
3
+ import { createSandboxClosure } from "../values.js";
4
+ export function createNumericParsers(budget) {
5
+ return {
6
+ parseInt: createSandboxClosure({
7
+ sandbox: true,
8
+ name: "parseInt",
9
+ call: ([value, radix], context) => {
10
+ const parse = (text) => {
11
+ const release = retainValues(budget, () => [text]);
12
+ let convertedRadix;
13
+ try {
14
+ convertedRadix = sandboxNumber(radix, budget, context);
15
+ }
16
+ catch (error) {
17
+ release();
18
+ throw error;
19
+ }
20
+ if (typeof convertedRadix === "number") {
21
+ try {
22
+ return globalThis.parseInt(text, convertedRadix);
23
+ }
24
+ finally {
25
+ release();
26
+ }
27
+ }
28
+ return convertedRadix
29
+ .then((number) => globalThis.parseInt(text, number))
30
+ .finally(release);
31
+ };
32
+ const text = sandboxString(value, budget, context);
33
+ return typeof text === "string" ? parse(text) : text.then(parse);
34
+ }
35
+ }),
36
+ parseFloat: createSandboxClosure({
37
+ sandbox: true,
38
+ name: "parseFloat",
39
+ call: ([value], context) => {
40
+ const text = sandboxString(value, budget, context);
41
+ return typeof text === "string"
42
+ ? globalThis.parseFloat(text)
43
+ : text.then(globalThis.parseFloat);
44
+ }
45
+ })
46
+ };
47
+ }
@@ -3,6 +3,7 @@ import { invokeBuiltinClosure } from "../builtin-call.js";
3
3
  import { createDataCheckpoint } from "../data-checkpoint.js";
4
4
  import { isCapturedException } from "../exceptions.js";
5
5
  import { isSandboxDate } from "../date.js";
6
+ import { createSandboxBox } from "../boxed.js";
6
7
  import { getDatePrototype } from "./date.js";
7
8
  import { createObjectGlobal, hasOwnSandboxProperty } from "./object.js";
8
9
  import { getHostObjectKeys, isGuestHostObject } from "../host-capabilities.js";
@@ -10,6 +11,8 @@ import { isFloat32Array } from "../float32.js";
10
11
  import { setSandboxProperty } from "../interpreter.js";
11
12
  import { getSandboxIterator } from "../iteration.js";
12
13
  import { sandboxNumber, sandboxString } from "../string-coercion.js";
14
+ import { createNumericParsers } from "./numeric-parsers.js";
15
+ import { createPrimitiveConstructor } from "./primitives.js";
13
16
  import { getSandboxDataProperty, getSandboxPrototype, isGuestClosure, markDescriptorObject, materializeFunctionProperties, setSandboxPrototype } from "../object-model.js";
14
17
  import { allocateProducedSandboxValue, createSandboxClosure, deepCopyToSandbox, defineOwnDataProperty, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet, measureSandboxData, ownEnumerableSandboxEntries as getOwnEnumerableEntries } from "../values.js";
15
18
  export function createObjectArrayGlobals(options) {
@@ -81,6 +84,8 @@ export function createObjectArrayGlobals(options) {
81
84
  call: ([value]) => {
82
85
  if (isSandboxDate(value))
83
86
  return getDatePrototype(value, options.budget, options.compileOwner);
87
+ if (value !== null && value !== undefined && typeof value !== "object")
88
+ value = createSandboxBox(value);
84
89
  objectProperties(value);
85
90
  return getSandboxPrototype(value, options.budget);
86
91
  },
@@ -122,11 +127,11 @@ export function createObjectArrayGlobals(options) {
122
127
  fromEntries: createSandboxClosure({
123
128
  sandbox: true,
124
129
  call: ([value], context) => {
125
- const iterator = getSandboxIterator(value, options.budget);
130
+ const iterator = getSandboxIterator(value, options.budget, context);
126
131
  if (iterator === undefined) {
127
132
  throw new TypeError("Object.fromEntries requires an iterable.");
128
133
  }
129
- if (context === undefined && !iterator.generator) {
134
+ if (context === undefined && !iterator.generator && !iterator.asynchronous) {
130
135
  // The direct host adapter preserves synchronous results and native hooks.
131
136
  return allocateProducedSandboxValue(Object.setPrototypeOf(Reflect.apply(Object.fromEntries, Object, [{ [Symbol.iterator]: () => iterator }]), null), options.budget);
132
137
  }
@@ -180,8 +185,7 @@ export function createObjectArrayGlobals(options) {
180
185
  })
181
186
  }
182
187
  }),
183
- String: createSandboxClosure({
184
- sandbox: true,
188
+ String: createPrimitiveConstructor({
185
189
  call: (args, context) => sandboxString(args.length === 0 ? "" : args[0], options.budget, context),
186
190
  name: "String",
187
191
  properties: {
@@ -201,9 +205,8 @@ export function createObjectArrayGlobals(options) {
201
205
  name: "fromCodePoint"
202
206
  })
203
207
  }
204
- }),
205
- Number: createSandboxClosure({
206
- sandbox: true,
208
+ }, options.budget),
209
+ Number: createPrimitiveConstructor({
207
210
  call: (args, context) => sandboxNumber(args.length === 0 ? 0 : args[0], options.budget, context),
208
211
  name: "Number",
209
212
  properties: {
@@ -222,16 +225,7 @@ export function createObjectArrayGlobals(options) {
222
225
  call: ([value]) => typeof value === "number" && Number.isInteger(value),
223
226
  name: "isInteger"
224
227
  }),
225
- parseInt: createSandboxClosure({
226
- sandbox: true,
227
- call: (args) => Reflect.apply(Number.parseInt, Number, [...args]),
228
- name: "parseInt"
229
- }),
230
- parseFloat: createSandboxClosure({
231
- sandbox: true,
232
- call: (args) => Reflect.apply(Number.parseFloat, Number, [...args]),
233
- name: "parseFloat"
234
- }),
228
+ ...createNumericParsers(options.budget),
235
229
  isSafeInteger: createSandboxClosure({
236
230
  sandbox: true,
237
231
  call: ([value]) => typeof value === "number" && Number.isSafeInteger(value),
@@ -246,12 +240,11 @@ export function createObjectArrayGlobals(options) {
246
240
  NEGATIVE_INFINITY: Number.NEGATIVE_INFINITY,
247
241
  POSITIVE_INFINITY: Number.POSITIVE_INFINITY
248
242
  }
249
- }),
250
- Boolean: createSandboxClosure({
251
- sandbox: true,
243
+ }, options.budget),
244
+ Boolean: createPrimitiveConstructor({
252
245
  call: ([value]) => Boolean(value),
253
246
  name: "Boolean"
254
- })
247
+ }, options.budget)
255
248
  };
256
249
  }
257
250
  async function objectFromSandboxEntries(items, iterator, budget, context) {
@@ -261,7 +254,7 @@ async function objectFromSandboxEntries(items, iterator, budget, context) {
261
254
  let value;
262
255
  let failure;
263
256
  const retained = {};
264
- budget.setRetainedValues(retained, () => [items, object, entry, key, value, failure]);
257
+ budget.setRetainedValues(retained, () => [items, iterator.retainedValue, object, entry, key, value, failure]);
265
258
  const checkData = createDataCheckpoint(budget, context);
266
259
  const closeOnThrow = async (error) => {
267
260
  failure = isCapturedException(error) ? error.reason : error;
@@ -323,6 +316,10 @@ function assignSandboxValues(target, sources, budget) {
323
316
  if (target === null || target === undefined) {
324
317
  throw new TypeError("Object.assign(target, ...sources) requires a non-null target.");
325
318
  }
319
+ if (typeof target !== "object") {
320
+ target = createSandboxBox(target);
321
+ budget.chargeDataUsage(measureSandboxData([target]));
322
+ }
326
323
  if (!isGuestClosure(target) && !isAssignableSandboxTarget(target)) {
327
324
  throw new TypeError("Object.assign(target, ...sources) requires an object or array target.");
328
325
  }
@@ -411,13 +408,13 @@ async function arrayFromSandboxValues(args, budget, context) {
411
408
  const read = (property) => context?.getProperty !== undefined
412
409
  ? context.getProperty(items, property)
413
410
  : getSandboxDataProperty(items, property, budget);
414
- const iterator = getSandboxIterator(items, budget);
411
+ const iterator = getSandboxIterator(items, budget, context);
415
412
  const constructor = context?.thisValue;
416
413
  let result;
417
414
  let currentValue;
418
415
  let failure;
419
416
  const retained = {};
420
- budget.setRetainedValues(retained, () => [items, mapFn, constructor, result, currentValue, failure]);
417
+ budget.setRetainedValues(retained, () => [items, iterator?.retainedValue, mapFn, constructor, result, currentValue, failure]);
421
418
  const checkData = createDataCheckpoint(budget, context);
422
419
  const closeOnThrow = async (error) => {
423
420
  failure = isCapturedException(error) ? error.reason : error;
@@ -1,5 +1,6 @@
1
1
  import { assertSandboxDataDepth } from "../../graph-depth.js";
2
2
  import { isSandboxDate } from "../date.js";
3
+ import { boxedValue, createSandboxBox, isSandboxBox } from "../boxed.js";
3
4
  import { isSandboxErrorConstructorInstance } from "../exceptions.js";
4
5
  import { isFloat32Array } from "../float32.js";
5
6
  import { hasHostObjectMember, isGuestHostObject } from "../host-capabilities.js";
@@ -7,14 +8,18 @@ import { collectionIteratorState, isSandboxCollectionIterator } from "../collect
7
8
  import { getSandboxPrototype, installObjectPrototype, isGuestClosure, markDescriptorObject, materializeFunctionProperties } from "../object-model.js";
8
9
  import { sandboxString } from "../string-coercion.js";
9
10
  import { createSandboxClosure, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "../values.js";
11
+ import { measureSandboxData } from "../values.js";
10
12
  export function createObjectGlobal(methods, budget) {
11
13
  const construct = ([value]) => {
12
14
  if (value === null || value === undefined) {
13
15
  budget.chargeDataUsage(1);
14
16
  return Object.create(null);
15
17
  }
16
- if (typeof value !== "object")
17
- throw new TypeError("Object primitive boxing is not supported.");
18
+ if (typeof value !== "object") {
19
+ const box = createSandboxBox(value);
20
+ budget.chargeDataUsage(measureSandboxData([box]));
21
+ return box;
22
+ }
18
23
  return value;
19
24
  };
20
25
  const constructor = createSandboxClosure({
@@ -44,9 +49,7 @@ export function createObjectGlobal(methods, budget) {
44
49
  length: 0,
45
50
  call: (_args, context) => {
46
51
  const value = requireReceiver(context?.thisValue);
47
- if (typeof value !== "object")
48
- throw new TypeError("Object primitive boxing is not supported.");
49
- return value;
52
+ return construct([value]);
50
53
  }
51
54
  }),
52
55
  hasOwnProperty: createSandboxClosure({
@@ -117,6 +120,8 @@ export function hasOwnSandboxProperty(value, key, enumerable) {
117
120
  return descriptor !== undefined && (!enumerable || descriptor.enumerable === true);
118
121
  }
119
122
  function typeTag(value) {
123
+ if (isSandboxBox(value))
124
+ value = boxedValue(value);
120
125
  if (value === undefined)
121
126
  return "Undefined";
122
127
  if (value === null)
@@ -0,0 +1,8 @@
1
+ import type { Budget } from "../budget.js";
2
+ import { type BoxedPrimitive } from "../boxed.js";
3
+ import { type SandboxCallContext, type SandboxClosure, type SandboxObject, type SandboxValue } from "../values.js";
4
+ export declare function createPrimitiveConstructor(options: {
5
+ name: "Number" | "String" | "Boolean";
6
+ call(args: readonly SandboxValue[], context?: SandboxCallContext): BoxedPrimitive | Promise<BoxedPrimitive>;
7
+ properties?: SandboxObject;
8
+ }, budget: Budget): SandboxClosure;
@@ -0,0 +1,69 @@
1
+ import { createSandboxBox, primitiveReceiver } from "../boxed.js";
2
+ import { installBoxedPrototype, materializeFunctionProperties } from "../object-model.js";
3
+ import { getNumberMember, numberMethodNames } from "../methods/number.js";
4
+ import { getStringMember, stringMethodNames } from "../methods/string.js";
5
+ import { createSandboxClosure, isSandboxClosure, measureSandboxData } from "../values.js";
6
+ export function createPrimitiveConstructor(options, budget) {
7
+ const initial = { Number: 0, String: "", Boolean: false }[options.name];
8
+ const kind = typeof initial;
9
+ const prototype = createSandboxBox(initial);
10
+ const allocate = (value) => {
11
+ const box = createSandboxBox(value);
12
+ budget.chargeDataUsage(measureSandboxData([box]));
13
+ return box;
14
+ };
15
+ const constructor = createSandboxClosure({
16
+ guest: true,
17
+ sandbox: true,
18
+ name: options.name,
19
+ length: 1,
20
+ call: options.call,
21
+ construct: (args, context) => {
22
+ const value = options.call(args, context);
23
+ return value instanceof Promise ? value.then(allocate) : allocate(value);
24
+ }
25
+ });
26
+ const properties = materializeFunctionProperties(constructor);
27
+ Object.defineProperty(properties, "prototype", { value: prototype, writable: false });
28
+ Object.defineProperty(prototype, "constructor", {
29
+ value: constructor,
30
+ writable: true,
31
+ configurable: true
32
+ });
33
+ for (const [name, value] of Object.entries(options.properties ?? {}))
34
+ Object.defineProperty(properties, name, {
35
+ value,
36
+ writable: isSandboxClosure(value),
37
+ configurable: isSandboxClosure(value)
38
+ });
39
+ const methods = new Map([
40
+ [
41
+ "valueOf",
42
+ createSandboxClosure({
43
+ sandbox: true,
44
+ name: "valueOf",
45
+ length: 0,
46
+ call: (_args, context) => primitiveReceiver(context?.thisValue, kind)
47
+ })
48
+ ]
49
+ ]);
50
+ if (kind === "number") {
51
+ for (const name of numberMethodNames)
52
+ methods.set(name, getNumberMember(name, budget));
53
+ }
54
+ else {
55
+ methods.set("toString", createSandboxClosure({
56
+ sandbox: true,
57
+ name: "toString",
58
+ length: 0,
59
+ call: (_args, context) => budget.allocateString(String(primitiveReceiver(context?.thisValue, kind)))
60
+ }));
61
+ if (kind === "string")
62
+ for (const name of stringMethodNames)
63
+ methods.set(name, getStringMember("", name, budget));
64
+ }
65
+ for (const [name, value] of methods)
66
+ Object.defineProperty(prototype, name, { value, writable: true, configurable: true });
67
+ installBoxedPrototype(budget, prototype, constructor);
68
+ return constructor;
69
+ }