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
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveFsConfig,
|
|
23
23
|
splitFrontmatter,
|
|
24
24
|
withBrokenPipeGuard
|
|
25
|
-
} from "./chunks/chunk-
|
|
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-
|
|
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";
|
|
@@ -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-
|
|
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-
|
|
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,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]) =>
|
|
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
|
|
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
|
-
|
|
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]) =>
|
|
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]) =>
|
|
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,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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|