poe-code 14.0.61 → 14.0.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1147 -574
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safe-js/dist/chunks/{chunk-VAYQP2VZ.js → chunk-KMTTD3BL.js} +7 -3
- package/packages/safe-js/dist/chunks/chunk-KMTTD3BL.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-ZTGKRKQH.js → chunk-RU2BCN4G.js} +1069 -577
- package/packages/safe-js/dist/chunks/chunk-RU2BCN4G.js.map +7 -0
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.js +1 -1
- package/packages/safe-js/dist/graph-depth.js +3 -0
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/async.js +3 -0
- package/packages/safe-js/dist/interp/boxed.d.ts +14 -0
- package/packages/safe-js/dist/interp/boxed.js +50 -0
- package/packages/safe-js/dist/interp/function-string.d.ts +2 -0
- package/packages/safe-js/dist/interp/function-string.js +21 -0
- package/packages/safe-js/dist/interp/globals/collections.js +4 -4
- package/packages/safe-js/dist/interp/globals/console-json.js +15 -18
- package/packages/safe-js/dist/interp/globals/misc.js +12 -13
- package/packages/safe-js/dist/interp/globals/numeric-parsers.d.ts +5 -0
- package/packages/safe-js/dist/interp/globals/numeric-parsers.js +47 -0
- package/packages/safe-js/dist/interp/globals/object-array.js +21 -24
- package/packages/safe-js/dist/interp/globals/object.js +10 -5
- package/packages/safe-js/dist/interp/globals/primitives.d.ts +8 -0
- package/packages/safe-js/dist/interp/globals/primitives.js +69 -0
- package/packages/safe-js/dist/interp/host-bridge.js +22 -0
- package/packages/safe-js/dist/interp/interpreter.js +173 -120
- package/packages/safe-js/dist/interp/iteration.d.ts +5 -3
- package/packages/safe-js/dist/interp/iteration.js +22 -1
- package/packages/safe-js/dist/interp/methods/array.js +6 -3
- package/packages/safe-js/dist/interp/methods/function.d.ts +2 -0
- package/packages/safe-js/dist/interp/methods/function.js +10 -1
- package/packages/safe-js/dist/interp/methods/number.d.ts +1 -0
- package/packages/safe-js/dist/interp/methods/number.js +4 -1
- package/packages/safe-js/dist/interp/methods/string.d.ts +2 -0
- package/packages/safe-js/dist/interp/methods/string.js +2 -2
- package/packages/safe-js/dist/interp/object-model.d.ts +5 -0
- package/packages/safe-js/dist/interp/object-model.js +41 -3
- package/packages/safe-js/dist/interp/promise.js +68 -61
- package/packages/safe-js/dist/interp/resources.d.ts +1 -0
- package/packages/safe-js/dist/interp/string-coercion.js +7 -1
- package/packages/safe-js/dist/interp/values.d.ts +4 -0
- package/packages/safe-js/dist/interp/values.js +81 -3
- package/packages/safe-js/dist/migrate.d.ts +2 -2
- package/packages/safe-js/dist/migrate.js +1 -1
- package/packages/safe-js/dist/migration-file.d.ts +4 -4
- package/packages/safe-js/dist/parse/function-source.d.ts +7 -0
- package/packages/safe-js/dist/parse/function-source.js +1 -0
- package/packages/safe-js/dist/parse/hash.d.ts +2 -2
- package/packages/safe-js/dist/parse/hash.js +23 -9
- package/packages/safe-js/dist/parse/parser.js +36 -23
- package/packages/safe-js/dist/restore.js +2 -1
- package/packages/safe-js/dist/run.js +16 -5
- package/packages/safe-js/dist/snapshot/boxed.d.ts +16 -0
- package/packages/safe-js/dist/snapshot/boxed.js +52 -0
- package/packages/safe-js/dist/snapshot/dump-format.d.ts +1 -1
- package/packages/safe-js/dist/snapshot/dump-format.js +19 -6
- package/packages/safe-js/dist/snapshot/migration.js +2 -2
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +5 -0
- package/packages/safe-js/dist/snapshot/replay-data.js +24 -0
- package/packages/safe-js/dist/snapshot/restore.js +10 -0
- package/packages/safe-js/dist/snapshot/serialize.d.ts +4 -2
- package/packages/safe-js/dist/snapshot/serialize.js +18 -10
- package/packages/safe-js/dist/snapshot/validation.js +22 -1
- package/packages/safe-js/dist/chunks/chunk-VAYQP2VZ.js.map +0 -7
- package/packages/safe-js/dist/chunks/chunk-ZTGKRKQH.js.map +0 -7
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
2
2
|
import { getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
|
|
3
|
+
import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
3
4
|
import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "./values.js";
|
|
4
5
|
const guestClosures = new WeakSet();
|
|
5
6
|
const functionProperties = new WeakMap();
|
|
6
7
|
const prototypes = new WeakMap();
|
|
7
8
|
const intrinsicPrototypes = new WeakMap();
|
|
9
|
+
const boxedPrototypes = new WeakMap();
|
|
10
|
+
const intrinsicPrototypeRoots = new WeakMap();
|
|
8
11
|
const intrinsicConstructors = new WeakMap();
|
|
12
|
+
const initialBoxedMethods = new WeakMap();
|
|
9
13
|
const descriptorObjects = new WeakSet();
|
|
10
14
|
export function registerGuestClosure(closure) {
|
|
11
15
|
guestClosures.add(closure);
|
|
@@ -61,10 +65,36 @@ export function getGuestFunctionProperty(closure, key) {
|
|
|
61
65
|
export function installObjectPrototype(budget, prototype, constructor) {
|
|
62
66
|
prototypes.set(prototype, null);
|
|
63
67
|
intrinsicPrototypes.set(budget, prototype);
|
|
68
|
+
registerIntrinsicPrototype(budget, prototype, constructor);
|
|
69
|
+
}
|
|
70
|
+
export function installBoxedPrototype(budget, prototype, constructor) {
|
|
71
|
+
let state = boxedPrototypes.get(budget);
|
|
72
|
+
if (state === undefined)
|
|
73
|
+
boxedPrototypes.set(budget, state = new Map());
|
|
74
|
+
state.set(typeof boxedValue(prototype), prototype);
|
|
75
|
+
initialBoxedMethods.set(prototype, new Map(Object.entries(Object.getOwnPropertyDescriptors(prototype)).map(([key, descriptor]) => [key, descriptor.value])));
|
|
76
|
+
registerIntrinsicPrototype(budget, prototype, constructor);
|
|
77
|
+
}
|
|
78
|
+
export function getBoxedPrototype(value, budget) {
|
|
79
|
+
return boxedPrototypes.get(budget)?.get(typeof value);
|
|
80
|
+
}
|
|
81
|
+
export function isDefaultBoxedMethod(value, key, budget) {
|
|
82
|
+
const prototype = getBoxedPrototype(value, budget);
|
|
83
|
+
return prototype !== undefined && initialBoxedMethods.get(prototype)?.has(key) === true &&
|
|
84
|
+
Object.getOwnPropertyDescriptor(prototype, key)?.value === initialBoxedMethods.get(prototype)?.get(key);
|
|
85
|
+
}
|
|
86
|
+
export function isIntrinsicConstructor(value) {
|
|
87
|
+
return intrinsicConstructors.has(value);
|
|
88
|
+
}
|
|
89
|
+
function registerIntrinsicPrototype(budget, prototype, constructor) {
|
|
90
|
+
let roots = intrinsicPrototypeRoots.get(budget);
|
|
91
|
+
if (roots === undefined)
|
|
92
|
+
intrinsicPrototypeRoots.set(budget, roots = new Set());
|
|
93
|
+
roots.add(prototype);
|
|
64
94
|
const records = [prototype, materializeFunctionProperties(constructor)].map(value => ({
|
|
65
95
|
value, descriptors: new Map(Object.entries(Object.getOwnPropertyDescriptors(value)))
|
|
66
96
|
}));
|
|
67
|
-
const unchanged = (before, after) => before !== undefined && after !== undefined && before.value
|
|
97
|
+
const unchanged = (before, after) => before !== undefined && after !== undefined && Object.is(before.value, after.value) &&
|
|
68
98
|
before.writable === after.writable && before.configurable === after.configurable && before.enumerable === after.enumerable;
|
|
69
99
|
intrinsicConstructors.set(constructor, () => records.every(({ value, descriptors }) => {
|
|
70
100
|
const current = Object.getOwnPropertyDescriptors(value);
|
|
@@ -74,14 +104,20 @@ export function installObjectPrototype(budget, prototype, constructor) {
|
|
|
74
104
|
budget.setRetainedValues(prototype, () => records.flatMap(({ value, descriptors }) => Object.entries(Object.getOwnPropertyDescriptors(value)).flatMap(([key, descriptor]) => unchanged(descriptors.get(key), descriptor) ? [] : [key, descriptor.value])));
|
|
75
105
|
}
|
|
76
106
|
export function releaseObjectPrototype(budget) {
|
|
77
|
-
const prototype
|
|
78
|
-
if (prototype !== undefined)
|
|
107
|
+
for (const prototype of intrinsicPrototypeRoots.get(budget) ?? [])
|
|
79
108
|
budget.setRetainedValues(prototype, undefined);
|
|
109
|
+
intrinsicPrototypeRoots.delete(budget);
|
|
110
|
+
boxedPrototypes.delete(budget);
|
|
80
111
|
intrinsicPrototypes.delete(budget);
|
|
81
112
|
}
|
|
82
113
|
export function getSandboxPrototype(value, budget) {
|
|
83
114
|
if (prototypes.has(value))
|
|
84
115
|
return prototypes.get(value) ?? null;
|
|
116
|
+
if (budget !== undefined && isSandboxBox(value)) {
|
|
117
|
+
const prototype = getBoxedPrototype(boxedValue(value), budget);
|
|
118
|
+
if (prototype !== undefined && prototype !== value)
|
|
119
|
+
return prototype;
|
|
120
|
+
}
|
|
85
121
|
return budget !== undefined && isPrototypeRecord(value)
|
|
86
122
|
? intrinsicPrototypes.get(budget) ?? null
|
|
87
123
|
: null;
|
|
@@ -167,6 +203,8 @@ export function hasGuestObjectState(value) {
|
|
|
167
203
|
return true;
|
|
168
204
|
if (functionProperties.has(value) || prototypes.has(value))
|
|
169
205
|
return true;
|
|
206
|
+
if (isSandboxBox(value))
|
|
207
|
+
return false;
|
|
170
208
|
return (descriptorObjects.has(value) &&
|
|
171
209
|
Object.values(Object.getOwnPropertyDescriptors(value)).some((descriptor) => !descriptor.enumerable || !descriptor.configurable || !descriptor.writable));
|
|
172
210
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SandboxError } from "./budget.js";
|
|
2
2
|
import { coerceThrownValue, createSubsetErrorValue } from "./exceptions.js";
|
|
3
3
|
import { getSandboxIterator } from "./iteration.js";
|
|
4
|
+
import { retainValues } from "./resources.js";
|
|
4
5
|
import { runPromiseJob } from "./jobs.js";
|
|
5
6
|
import { observeSandboxPromise } from "./promise-tracker.js";
|
|
6
7
|
import { createSandboxClosure, createSandboxPromise, isSandboxClosure, isSandboxPromise } from "./values.js";
|
|
@@ -337,76 +338,82 @@ async function settleIterable(iterable, method, budget, constructor, context) {
|
|
|
337
338
|
const promiseResolve = readPromiseReceiverProperty(constructor, "resolve", prototype);
|
|
338
339
|
if (!isSandboxClosure(promiseResolve))
|
|
339
340
|
throw new TypeError("Promise constructor requires a callable resolve.");
|
|
340
|
-
const iterator = getSandboxIterator(iterable, budget);
|
|
341
|
+
const iterator = getSandboxIterator(iterable, budget, context);
|
|
341
342
|
if (iterator === undefined)
|
|
342
343
|
throw new TypeError("Promise helpers require an iterable.");
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
344
|
+
const releaseIterator = retainValues(budget, () => [iterator.retainedValue]);
|
|
345
|
+
try {
|
|
346
|
+
let index = 0;
|
|
347
|
+
while (true) {
|
|
348
|
+
budget.visitNode();
|
|
349
|
+
const next = iterator.generator || iterator.asynchronous
|
|
350
|
+
? await iterator.next()
|
|
351
|
+
: iterator.next();
|
|
352
|
+
if (typeof next !== "object" || next === null)
|
|
353
|
+
throw new TypeError("Iterator result must be an object.");
|
|
354
|
+
if (next.done)
|
|
355
|
+
break;
|
|
356
|
+
const value = next.value;
|
|
357
|
+
try {
|
|
358
|
+
budget.allocateArrayLength(index + 1);
|
|
359
|
+
const entryIndex = index++;
|
|
360
|
+
if (method !== "race")
|
|
361
|
+
values.push(undefined);
|
|
362
|
+
const entry = await callPromiseClosure(promiseResolve, [value], constructor, budget, context);
|
|
363
|
+
let called = false;
|
|
364
|
+
const handlers = ["fulfilled", "rejected"].map((state) => {
|
|
365
|
+
if (method === "race" || (method === "any" && state === "fulfilled"))
|
|
366
|
+
return state === "fulfilled" ? capability.resolve : capability.reject;
|
|
367
|
+
if (method === "all" && state === "rejected")
|
|
368
|
+
return capability.reject;
|
|
369
|
+
return createSandboxClosure({
|
|
370
|
+
sandbox: true,
|
|
371
|
+
retainedValues: () => [
|
|
372
|
+
capability.promise,
|
|
373
|
+
capability.resolve,
|
|
374
|
+
capability.reject,
|
|
375
|
+
values
|
|
376
|
+
],
|
|
377
|
+
call: async ([settlement]) => {
|
|
378
|
+
if (called)
|
|
379
|
+
return undefined;
|
|
380
|
+
called = true;
|
|
381
|
+
values[entryIndex] =
|
|
382
|
+
method === "allSettled"
|
|
383
|
+
? state === "fulfilled"
|
|
384
|
+
? { status: state, value: settlement }
|
|
385
|
+
: { status: state, reason: settlement }
|
|
386
|
+
: settlement;
|
|
387
|
+
remaining--;
|
|
388
|
+
await complete();
|
|
376
389
|
return undefined;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
method === "allSettled"
|
|
380
|
-
? state === "fulfilled"
|
|
381
|
-
? { status: state, value: settlement }
|
|
382
|
-
: { status: state, reason: settlement }
|
|
383
|
-
: settlement;
|
|
384
|
-
remaining--;
|
|
385
|
-
await complete();
|
|
386
|
-
return undefined;
|
|
387
|
-
}
|
|
390
|
+
}
|
|
391
|
+
});
|
|
388
392
|
});
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
await callPromiseClosure(then, handlers, entry, budget, context);
|
|
395
|
-
}
|
|
396
|
-
catch (error) {
|
|
397
|
-
try {
|
|
398
|
-
const closed = iterator.return?.();
|
|
399
|
-
if (iterator.generator)
|
|
400
|
-
await closed;
|
|
393
|
+
remaining++;
|
|
394
|
+
const then = readPromiseReceiverProperty(entry, "then", prototype);
|
|
395
|
+
if (!isSandboxClosure(then))
|
|
396
|
+
throw new TypeError("Promise resolver result requires a callable then.");
|
|
397
|
+
await callPromiseClosure(then, handlers, entry, budget, context);
|
|
401
398
|
}
|
|
402
|
-
catch {
|
|
399
|
+
catch (error) {
|
|
400
|
+
try {
|
|
401
|
+
const closed = iterator.return?.();
|
|
402
|
+
if (iterator.generator || iterator.asynchronous)
|
|
403
|
+
await closed;
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
403
408
|
throw error;
|
|
404
409
|
}
|
|
405
|
-
throw error;
|
|
406
410
|
}
|
|
411
|
+
remaining--;
|
|
412
|
+
await complete();
|
|
413
|
+
}
|
|
414
|
+
finally {
|
|
415
|
+
releaseIterator();
|
|
407
416
|
}
|
|
408
|
-
remaining--;
|
|
409
|
-
await complete();
|
|
410
417
|
}
|
|
411
418
|
catch (error) {
|
|
412
419
|
if (error instanceof SandboxError &&
|
|
@@ -8,6 +8,8 @@ import { collectionIteratorState, isSandboxCollectionIterator } from "./collecti
|
|
|
8
8
|
import { getGuestFunctionProperties, getSandboxPrototype, hasExplicitSandboxPrototype, isGuestClosure } from "./object-model.js";
|
|
9
9
|
import { getRegexMember } from "./methods/regex.js";
|
|
10
10
|
import { retainValues } from "./resources.js";
|
|
11
|
+
import { functionString } from "./function-string.js";
|
|
12
|
+
import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
11
13
|
import { isSandboxClosure, isSandboxPromise, isSandboxMap, isSandboxSet, isSandboxRegex, isSandboxGenerator } from "./values.js";
|
|
12
14
|
const defaultStringHook = Symbol("defaultStringHook");
|
|
13
15
|
const defaultValueHook = Symbol("defaultValueHook");
|
|
@@ -39,7 +41,7 @@ async function objectToPrimitive(value, budget, context, joining, hint) {
|
|
|
39
41
|
result = await defaultToString(value, budget, context, joining);
|
|
40
42
|
}
|
|
41
43
|
else if (hook === defaultValueHook) {
|
|
42
|
-
result = isSandboxDate(value) ? dateTime(value) : value;
|
|
44
|
+
result = isSandboxDate(value) ? dateTime(value) : isSandboxBox(value) ? boxedValue(value) : value;
|
|
43
45
|
}
|
|
44
46
|
else {
|
|
45
47
|
if (!isSandboxClosure(hook))
|
|
@@ -96,6 +98,10 @@ function conversionHook(value, name, budget) {
|
|
|
96
98
|
return undefined;
|
|
97
99
|
}
|
|
98
100
|
async function defaultToString(value, budget, context, joining) {
|
|
101
|
+
if (isSandboxBox(value))
|
|
102
|
+
return budget.allocateString(String(boxedValue(value)));
|
|
103
|
+
if (isSandboxClosure(value))
|
|
104
|
+
return budget.allocateString(functionString(value));
|
|
99
105
|
if (isSandboxMap(value))
|
|
100
106
|
return "[object Map]";
|
|
101
107
|
if (isSandboxSet(value))
|
|
@@ -4,6 +4,7 @@ import type { Budget, CompileTicket } from "./budget.js";
|
|
|
4
4
|
import { CompileScope } from "./regex/compile-guard.js";
|
|
5
5
|
import type { GeneratorChannel } from "./generator.js";
|
|
6
6
|
import { type RegexPattern } from "./regex/parse.js";
|
|
7
|
+
import type { FunctionSource } from "../parse/function-source.js";
|
|
7
8
|
export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
|
|
8
9
|
export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
|
|
9
10
|
declare const sandboxClosureBrand: unique symbol;
|
|
@@ -57,6 +58,7 @@ export type SandboxCallContext = {
|
|
|
57
58
|
readonly invokeClosure?: (closure: SandboxClosure, args: readonly SandboxValue[], thisValue: SandboxValue, construct?: boolean) => Promise<SandboxValue>;
|
|
58
59
|
};
|
|
59
60
|
export type SandboxClosure = {
|
|
61
|
+
readonly sourceRange?: FunctionSource;
|
|
60
62
|
readonly async?: true;
|
|
61
63
|
readonly generator?: true;
|
|
62
64
|
readonly sandbox?: true;
|
|
@@ -94,6 +96,7 @@ type CopyFromSandboxOptions = {
|
|
|
94
96
|
compilation?: CompileScope;
|
|
95
97
|
};
|
|
96
98
|
export declare function createSandboxClosure(input: {
|
|
99
|
+
sourceRange?: FunctionSource;
|
|
97
100
|
guest?: boolean;
|
|
98
101
|
async?: boolean;
|
|
99
102
|
generator?: boolean;
|
|
@@ -131,6 +134,7 @@ export declare function deepCopyToSandbox(value: unknown): SandboxValue;
|
|
|
131
134
|
export declare function cloneSandboxValue(value: SandboxValue, options?: {
|
|
132
135
|
compilation?: CompileScope;
|
|
133
136
|
resetRegexLastIndex?: boolean;
|
|
137
|
+
structuredClone?: boolean;
|
|
134
138
|
}): SandboxValue;
|
|
135
139
|
export declare function allocateProducedSandboxValue(value: SandboxValue, budget: Budget): SandboxValue;
|
|
136
140
|
export declare function measureSandboxData(values: Iterable<unknown>, options?: {
|
|
@@ -2,6 +2,7 @@ import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
|
|
|
2
2
|
import { isSandboxMap, isSandboxSet, sandboxMapBrand, sandboxSetBrand } from "./collection-brands.js";
|
|
3
3
|
import { collectionIteratorState, isSandboxCollectionIterator, restoreSandboxCollectionIterator, snapshotCollectionIterator } from "./collection-iterator.js";
|
|
4
4
|
import { copyNativeDate, exportDate, isSandboxDate } from "./date.js";
|
|
5
|
+
import { boxedDataProperties, boxedValue, createSandboxBox, isSandboxBox, nativeBoxedValue } from "./boxed.js";
|
|
5
6
|
import { getHostObjectKeys, getHostObjectMember, hasHostObjectMember, measureHostObjectData, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
|
|
6
7
|
import { types as nodeTypes } from "node:util";
|
|
7
8
|
import { CompileScope, RegexCompileGuard, regexCompiledData } from "./regex/compile-guard.js";
|
|
@@ -13,7 +14,7 @@ import { invokeCancelableClosure, readPromiseCancellation, registerPromiseCancel
|
|
|
13
14
|
import { parseRegex } from "./regex/parse.js";
|
|
14
15
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
15
16
|
import { sandboxErrorTypes } from "../error/shape.js";
|
|
16
|
-
import { getGuestFunctionProperties, getSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, isGuestClosure, registerGuestClosure } from "./object-model.js";
|
|
17
|
+
import { getGuestFunctionProperties, getSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, isGuestClosure, isIntrinsicConstructor, registerGuestClosure } from "./object-model.js";
|
|
17
18
|
import { copySandboxArgumentProperties, createSandboxArguments, getSandboxArgumentEntries, isSandboxArguments } from "./arguments.js";
|
|
18
19
|
export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
|
|
19
20
|
export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
|
|
@@ -39,6 +40,9 @@ export function createSandboxClosure(input) {
|
|
|
39
40
|
enumerable: false,
|
|
40
41
|
value: true
|
|
41
42
|
});
|
|
43
|
+
if (input.sourceRange !== undefined) {
|
|
44
|
+
Object.defineProperty(closure, "sourceRange", { value: input.sourceRange });
|
|
45
|
+
}
|
|
42
46
|
if (input.sandbox === true) {
|
|
43
47
|
Object.defineProperty(closure, "sandbox", { value: true });
|
|
44
48
|
}
|
|
@@ -240,6 +244,19 @@ export function measureSandboxData(values, options = {}) {
|
|
|
240
244
|
assertSandboxDataDepth(depth);
|
|
241
245
|
seen.add(value);
|
|
242
246
|
usage += 1;
|
|
247
|
+
if (isSandboxBox(value)) {
|
|
248
|
+
const primitive = boxedValue(value);
|
|
249
|
+
usage += typeof primitive === "string" ? primitive.length : 8;
|
|
250
|
+
const prototype = getSandboxPrototype(value);
|
|
251
|
+
if (prototype !== null)
|
|
252
|
+
visit(prototype, depth + 1);
|
|
253
|
+
for (const [key, descriptor] of boxedDataProperties(value)) {
|
|
254
|
+
usage += key.length + 1;
|
|
255
|
+
if ("value" in descriptor)
|
|
256
|
+
visit(descriptor.value, depth + 1);
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
243
260
|
if (isSandboxDate(value)) {
|
|
244
261
|
usage += 8;
|
|
245
262
|
return;
|
|
@@ -308,8 +325,19 @@ export function measureSandboxData(values, options = {}) {
|
|
|
308
325
|
if (isSandboxClosure(value)) {
|
|
309
326
|
if (options.ignoreClosures)
|
|
310
327
|
return;
|
|
311
|
-
if (value.properties !== undefined)
|
|
312
|
-
|
|
328
|
+
if (value.properties !== undefined) {
|
|
329
|
+
if (isIntrinsicConstructor(value)) {
|
|
330
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value.properties))) {
|
|
331
|
+
if (key === "prototype" || key === "name" || key === "length")
|
|
332
|
+
continue;
|
|
333
|
+
usage += key.length + 1;
|
|
334
|
+
if ("value" in descriptor)
|
|
335
|
+
visit(descriptor.value, depth + 1);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else
|
|
339
|
+
visit(value.properties, depth + 1);
|
|
340
|
+
}
|
|
313
341
|
if (!options.ignoreClosureCaptures)
|
|
314
342
|
for (const retained of value[sandboxRetainedValues]?.() ?? [])
|
|
315
343
|
visit(retained, depth + 1);
|
|
@@ -454,6 +482,26 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
|
|
|
454
482
|
if (typeof value === "object" && value !== null && hasGuestObjectState(value)) {
|
|
455
483
|
throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
|
|
456
484
|
}
|
|
485
|
+
const primitive = nativeBoxedValue(value);
|
|
486
|
+
if (primitive !== undefined) {
|
|
487
|
+
const original = value;
|
|
488
|
+
const existing = state.seen.get(original);
|
|
489
|
+
if (existing !== undefined)
|
|
490
|
+
return existing;
|
|
491
|
+
const copy = createSandboxBox(primitive);
|
|
492
|
+
state.seen.set(original, copy);
|
|
493
|
+
if (!state.structuredClone) {
|
|
494
|
+
for (const [key, descriptor] of boxedDataProperties(original)) {
|
|
495
|
+
if (!("value" in descriptor))
|
|
496
|
+
throw new TypeError("Boxed data cannot contain accessors.");
|
|
497
|
+
Object.defineProperty(copy, key, {
|
|
498
|
+
...descriptor,
|
|
499
|
+
value: copyToSandbox(descriptor.value, state, joinPath(path, key), cloneSandboxCollections, depth + 1)
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return copy;
|
|
504
|
+
}
|
|
457
505
|
if (isSandboxMap(value)) {
|
|
458
506
|
if (!cloneSandboxCollections)
|
|
459
507
|
return value;
|
|
@@ -595,6 +643,22 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
|
|
|
595
643
|
if (!isSandboxClosure(value) && hasGuestObjectState(value)) {
|
|
596
644
|
throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
|
|
597
645
|
}
|
|
646
|
+
if (isSandboxBox(value)) {
|
|
647
|
+
const existing = state.seen.get(value);
|
|
648
|
+
if (existing !== undefined)
|
|
649
|
+
return existing;
|
|
650
|
+
const copy = Object(boxedValue(value));
|
|
651
|
+
state.seen.set(value, copy);
|
|
652
|
+
for (const [key, descriptor] of boxedDataProperties(value)) {
|
|
653
|
+
if (!("value" in descriptor))
|
|
654
|
+
throw new TypeError("Boxed data cannot contain accessors.");
|
|
655
|
+
Object.defineProperty(copy, key, {
|
|
656
|
+
...descriptor,
|
|
657
|
+
value: copyFromSandbox(descriptor.value, state, joinPath(path, key), options, depth + 1)
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
return copy;
|
|
661
|
+
}
|
|
598
662
|
const regexBrand = Object.getOwnPropertyDescriptor(value, sandboxRegexBrand);
|
|
599
663
|
if (regexBrand !== undefined) {
|
|
600
664
|
if (!("value" in regexBrand) || regexBrand.value !== true) {
|
|
@@ -756,6 +820,20 @@ function allocateSandboxValue(value, budget, seen) {
|
|
|
756
820
|
budget.allocateString(value);
|
|
757
821
|
return;
|
|
758
822
|
}
|
|
823
|
+
if (isSandboxBox(value)) {
|
|
824
|
+
if (seen.has(value))
|
|
825
|
+
return;
|
|
826
|
+
seen.add(value);
|
|
827
|
+
const primitive = boxedValue(value);
|
|
828
|
+
if (typeof primitive === "string")
|
|
829
|
+
budget.allocateString(primitive);
|
|
830
|
+
for (const [key, descriptor] of boxedDataProperties(value)) {
|
|
831
|
+
budget.allocateString(key);
|
|
832
|
+
if ("value" in descriptor)
|
|
833
|
+
allocateSandboxValue(descriptor.value, budget, seen);
|
|
834
|
+
}
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
759
837
|
if (isFloat32Array(value)) {
|
|
760
838
|
if (seen.has(value))
|
|
761
839
|
return;
|
|
@@ -13,14 +13,14 @@ export declare function inspectSnapshotMigration(snapshot: SafeJSSnapshot, optio
|
|
|
13
13
|
checkpointDigest: string;
|
|
14
14
|
sourceHash: string;
|
|
15
15
|
executionSemantics: string;
|
|
16
|
-
calls: (Omit<import("./index.js").HostCallRecord, "
|
|
16
|
+
calls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
17
17
|
asynchronous: boolean;
|
|
18
18
|
outcome?: {
|
|
19
19
|
status: "fulfilled" | "rejected";
|
|
20
20
|
data: import("./snapshot/replay-data.js").ReplayData;
|
|
21
21
|
};
|
|
22
22
|
})[];
|
|
23
|
-
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "
|
|
23
|
+
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
24
24
|
asynchronous: boolean;
|
|
25
25
|
outcome?: {
|
|
26
26
|
status: "fulfilled" | "rejected";
|
|
@@ -12,7 +12,7 @@ export function inspectSnapshotMigration(snapshot, options) {
|
|
|
12
12
|
validateSnapshotData(snapshot);
|
|
13
13
|
validateDumpEnvelope(snapshot);
|
|
14
14
|
validateMigrationSemantics(snapshot.executionSemantics);
|
|
15
|
-
const sourceHash = hashSource(options.source);
|
|
15
|
+
const sourceHash = hashSource(options.source, undefined, snapshot.executionSemantics === EXECUTION_SEMANTICS);
|
|
16
16
|
if (snapshot.sourceHash !== sourceHash)
|
|
17
17
|
throw new TypeError(`Migration requires the original executable source (checkpoint hash ${snapshot.sourceHash}, supplied ${sourceHash}). Preserve the checkpoint; do not replace its hash.`);
|
|
18
18
|
validateSnapshotMigration(snapshot.migration, sourceHash);
|
|
@@ -13,14 +13,14 @@ export declare function migrateSnapshotFile(options: SnapshotMigrationFileOption
|
|
|
13
13
|
checkpointDigest: string;
|
|
14
14
|
sourceHash: string;
|
|
15
15
|
executionSemantics: string;
|
|
16
|
-
calls: (Omit<import("./index.js").HostCallRecord, "
|
|
16
|
+
calls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
17
17
|
asynchronous: boolean;
|
|
18
18
|
outcome?: {
|
|
19
19
|
status: "fulfilled" | "rejected";
|
|
20
20
|
data: import("./snapshot/replay-data.js").ReplayData;
|
|
21
21
|
};
|
|
22
22
|
})[];
|
|
23
|
-
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "
|
|
23
|
+
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
24
24
|
asynchronous: boolean;
|
|
25
25
|
outcome?: {
|
|
26
26
|
status: "fulfilled" | "rejected";
|
|
@@ -35,14 +35,14 @@ export declare function migrateSnapshotFile(options: SnapshotMigrationFileOption
|
|
|
35
35
|
checkpointDigest: string;
|
|
36
36
|
sourceHash: string;
|
|
37
37
|
executionSemantics: string;
|
|
38
|
-
calls: (Omit<import("./index.js").HostCallRecord, "
|
|
38
|
+
calls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
39
39
|
asynchronous: boolean;
|
|
40
40
|
outcome?: {
|
|
41
41
|
status: "fulfilled" | "rejected";
|
|
42
42
|
data: import("./snapshot/replay-data.js").ReplayData;
|
|
43
43
|
};
|
|
44
44
|
})[];
|
|
45
|
-
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "
|
|
45
|
+
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
46
46
|
asynchronous: boolean;
|
|
47
47
|
outcome?: {
|
|
48
48
|
status: "fulfilled" | "rejected";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const functionSources = new WeakMap();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type Module, type ParseResult } from "./parser.js";
|
|
2
2
|
import { type CompileOwner } from "../interp/budget.js";
|
|
3
|
-
export declare function hashSource(source: string, owner?: CompileOwner): string;
|
|
4
|
-
export declare function hashParsedAst(ast: Module | ParseResult): string;
|
|
3
|
+
export declare function hashSource(source: string, owner?: CompileOwner, includeFunctionSource?: boolean): string;
|
|
4
|
+
export declare function hashParsedAst(ast: Module | ParseResult, includeFunctionSource?: boolean): string;
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { parse, parseModule } from "./parser.js";
|
|
2
2
|
import { SandboxError } from "../interp/budget.js";
|
|
3
|
+
import { functionSources } from "./function-source.js";
|
|
3
4
|
const FNV_OFFSET_BASIS = 0x811c9dc5;
|
|
4
5
|
const FNV_PRIME = 0x01000193;
|
|
5
6
|
const IGNORED_KEYS = new Set(["nodeId", "raw", "span"]);
|
|
6
|
-
export function hashSource(source, owner) {
|
|
7
|
+
export function hashSource(source, owner, includeFunctionSource = true) {
|
|
7
8
|
try {
|
|
8
|
-
return hashParsedAst(parse(source, "<input>", owner));
|
|
9
|
+
return hashParsedAst(parse(source, "<input>", owner), includeFunctionSource);
|
|
9
10
|
}
|
|
10
11
|
catch (error) {
|
|
11
12
|
if (error instanceof SandboxError)
|
|
12
13
|
throw error;
|
|
13
|
-
return hashParsedAst(parseModule(source, "<input>", owner));
|
|
14
|
+
return hashParsedAst(parseModule(source, "<input>", owner), includeFunctionSource);
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
|
-
export function hashParsedAst(ast) {
|
|
17
|
+
export function hashParsedAst(ast, includeFunctionSource = true) {
|
|
17
18
|
let hash = FNV_OFFSET_BASIS;
|
|
18
19
|
visit(ast);
|
|
19
20
|
return hash.toString(16).padStart(8, "0");
|
|
20
|
-
function visit(value, includeTemplateRaw = false) {
|
|
21
|
+
function visit(value, includeTemplateRaw = false, enclosingSource) {
|
|
21
22
|
if (value === null) {
|
|
22
23
|
write("null");
|
|
23
24
|
return;
|
|
@@ -29,7 +30,7 @@ export function hashParsedAst(ast) {
|
|
|
29
30
|
if (Array.isArray(value)) {
|
|
30
31
|
write("[");
|
|
31
32
|
for (const entry of value) {
|
|
32
|
-
visit(entry, includeTemplateRaw);
|
|
33
|
+
visit(entry, includeTemplateRaw, enclosingSource);
|
|
33
34
|
write(",");
|
|
34
35
|
}
|
|
35
36
|
write("]");
|
|
@@ -47,6 +48,19 @@ export function hashParsedAst(ast) {
|
|
|
47
48
|
return;
|
|
48
49
|
case "object": {
|
|
49
50
|
const record = value;
|
|
51
|
+
const source = includeFunctionSource
|
|
52
|
+
? functionSources.get(value)
|
|
53
|
+
: undefined;
|
|
54
|
+
if (source !== undefined &&
|
|
55
|
+
!(enclosingSource !== undefined &&
|
|
56
|
+
source.text === enclosingSource.text &&
|
|
57
|
+
source.start >= enclosingSource.start &&
|
|
58
|
+
source.end <= enclosingSource.end)) {
|
|
59
|
+
write("function-source:");
|
|
60
|
+
write(String(source.end - source.start));
|
|
61
|
+
write(":");
|
|
62
|
+
write(source.text, source.start, source.end);
|
|
63
|
+
}
|
|
50
64
|
const keys = Object.keys(value)
|
|
51
65
|
.filter((key) => shouldHashKey(record, key, includeTemplateRaw))
|
|
52
66
|
.sort();
|
|
@@ -54,7 +68,7 @@ export function hashParsedAst(ast) {
|
|
|
54
68
|
for (const key of keys) {
|
|
55
69
|
write(JSON.stringify(key));
|
|
56
70
|
write(":");
|
|
57
|
-
visit(record[key], shouldIncludeTemplateRaw(record, key, includeTemplateRaw));
|
|
71
|
+
visit(record[key], shouldIncludeTemplateRaw(record, key, includeTemplateRaw), source ?? enclosingSource);
|
|
58
72
|
write(",");
|
|
59
73
|
}
|
|
60
74
|
write("}");
|
|
@@ -64,8 +78,8 @@ export function hashParsedAst(ast) {
|
|
|
64
78
|
throw new TypeError(`Unsupported AST value type: ${typeof value}`);
|
|
65
79
|
}
|
|
66
80
|
}
|
|
67
|
-
function write(chunk) {
|
|
68
|
-
for (let index =
|
|
81
|
+
function write(chunk, start = 0, end = chunk.length) {
|
|
82
|
+
for (let index = start; index < end; index += 1) {
|
|
69
83
|
hash ^= chunk.charCodeAt(index);
|
|
70
84
|
hash = Math.imul(hash, FNV_PRIME) >>> 0;
|
|
71
85
|
}
|