poe-code 14.0.79 → 14.0.81
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 +772 -341
- 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-B3AL4KJ3.js → chunk-2LNBLFJ3.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-BGSPSHLP.js → chunk-ZSYBRZU6.js} +644 -292
- package/packages/safe-js/dist/chunks/chunk-ZSYBRZU6.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 +2 -1
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/async.d.ts +1 -1
- package/packages/safe-js/dist/interp/async.js +65 -43
- package/packages/safe-js/dist/interp/bound-function-state.d.ts +7 -0
- package/packages/safe-js/dist/interp/bound-function-state.js +1 -0
- package/packages/safe-js/dist/interp/bound-function.d.ts +4 -0
- package/packages/safe-js/dist/interp/bound-function.js +18 -0
- package/packages/safe-js/dist/interp/generator-properties.d.ts +3 -0
- package/packages/safe-js/dist/interp/generator-properties.js +9 -0
- package/packages/safe-js/dist/interp/generator-prototypes.d.ts +6 -0
- package/packages/safe-js/dist/interp/generator-prototypes.js +1 -0
- package/packages/safe-js/dist/interp/globals/function.d.ts +2 -0
- package/packages/safe-js/dist/interp/globals/function.js +32 -0
- package/packages/safe-js/dist/interp/globals/generator.d.ts +2 -0
- package/packages/safe-js/dist/interp/globals/generator.js +51 -0
- package/packages/safe-js/dist/interp/globals/object-array.js +28 -7
- package/packages/safe-js/dist/interp/globals/object.js +2 -1
- package/packages/safe-js/dist/interp/globals.d.ts +1 -0
- package/packages/safe-js/dist/interp/globals.js +4 -0
- package/packages/safe-js/dist/interp/instanceof.d.ts +4 -0
- package/packages/safe-js/dist/interp/instanceof.js +58 -0
- package/packages/safe-js/dist/interp/interpreter.js +16 -47
- package/packages/safe-js/dist/interp/iteration.js +76 -5
- package/packages/safe-js/dist/interp/methods/function.d.ts +5 -2
- package/packages/safe-js/dist/interp/methods/function.js +55 -42
- package/packages/safe-js/dist/interp/methods/generator.d.ts +4 -1
- package/packages/safe-js/dist/interp/methods/generator.js +12 -6
- package/packages/safe-js/dist/interp/object-model.d.ts +1 -0
- package/packages/safe-js/dist/interp/object-model.js +69 -17
- package/packages/safe-js/dist/interp/string-coercion.js +3 -2
- package/packages/safe-js/dist/interp/values.js +21 -3
- package/packages/safe-js/dist/snapshot/guest-heap-validation.js +27 -3
- package/packages/safe-js/dist/snapshot/guest-heap.d.ts +9 -0
- package/packages/safe-js/dist/snapshot/guest-heap.js +11 -0
- package/packages/safe-js/dist/snapshot/restore.js +61 -7
- package/packages/safe-js/dist/chunks/chunk-BGSPSHLP.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-B3AL4KJ3.js.map → chunk-2LNBLFJ3.js.map} +0 -0
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
2
|
+
import { getGeneratorProperties } from "./generator-properties.js";
|
|
3
|
+
import { generatorPrototypes } from "./generator-prototypes.js";
|
|
4
|
+
import { runResources } from "./resources.js";
|
|
2
5
|
import { getIntrinsicIdentity, registerBuiltinIdentities, releaseIntrinsicIdentities } from "./intrinsics.js";
|
|
3
6
|
import { releaseTemplateObjects } from "./template-objects.js";
|
|
4
7
|
import { isSandboxDate } from "./date.js";
|
|
@@ -8,6 +11,7 @@ import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
|
8
11
|
import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, getPromiseProperties, isSandboxRegex, getRegexProperties, getCollectionProperties, isSandboxSet } from "./values.js";
|
|
9
12
|
const guestClosures = new WeakSet();
|
|
10
13
|
const functionProperties = new WeakMap();
|
|
14
|
+
const functionPropertyRevisions = new WeakMap();
|
|
11
15
|
const prototypes = new WeakMap();
|
|
12
16
|
const intrinsicPrototypes = new WeakMap();
|
|
13
17
|
const boxedPrototypes = new WeakMap();
|
|
@@ -16,6 +20,7 @@ const collectionPrototypes = new WeakMap();
|
|
|
16
20
|
const promisePrototypes = new WeakMap();
|
|
17
21
|
const datePrototypes = new WeakMap();
|
|
18
22
|
const arrayPrototypes = new WeakMap();
|
|
23
|
+
const functionPrototypes = new WeakMap();
|
|
19
24
|
const initialArrayMethods = new WeakMap();
|
|
20
25
|
const initialRegexDescriptors = new WeakMap();
|
|
21
26
|
const intrinsicPrototypeRoots = new WeakMap();
|
|
@@ -53,9 +58,26 @@ export function materializeFunctionProperties(closure) {
|
|
|
53
58
|
descriptorObjects.add(prototype);
|
|
54
59
|
Object.defineProperty(properties, "prototype", { value: prototype, writable: true });
|
|
55
60
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
// Never expose the raw table: native callers must invalidate captures too.
|
|
62
|
+
const state = { revision: 0 };
|
|
63
|
+
const tracked = new Proxy(properties, {
|
|
64
|
+
defineProperty(target, key, descriptor) {
|
|
65
|
+
const changed = Reflect.defineProperty(target, key, descriptor);
|
|
66
|
+
if (changed)
|
|
67
|
+
state.revision++;
|
|
68
|
+
return changed;
|
|
69
|
+
},
|
|
70
|
+
deleteProperty(target, key) {
|
|
71
|
+
const changed = Reflect.deleteProperty(target, key);
|
|
72
|
+
if (changed)
|
|
73
|
+
state.revision++;
|
|
74
|
+
return changed;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
functionPropertyRevisions.set(tracked, state);
|
|
78
|
+
descriptorObjects.add(tracked);
|
|
79
|
+
functionProperties.set(closure, tracked);
|
|
80
|
+
return tracked;
|
|
59
81
|
}
|
|
60
82
|
export function getGuestFunctionProperty(closure, key) {
|
|
61
83
|
let properties = functionProperties.get(closure);
|
|
@@ -79,6 +101,12 @@ export function installObjectPrototype(budget, prototype, constructor) {
|
|
|
79
101
|
intrinsicPrototypes.set(budget, prototype);
|
|
80
102
|
registerIntrinsicPrototype(budget, prototype, constructor);
|
|
81
103
|
}
|
|
104
|
+
export function installFunctionPrototype(budget, prototype) {
|
|
105
|
+
functionPrototypes.set(budget, prototype);
|
|
106
|
+
registerBuiltinIdentities(budget, { "%FunctionPrototype%": prototype });
|
|
107
|
+
registerIntrinsicFunction(budget, prototype);
|
|
108
|
+
registerIntrinsicObject(budget, materializeFunctionProperties(prototype));
|
|
109
|
+
}
|
|
82
110
|
export function installBoxedPrototype(budget, prototype, constructor, kind = typeof boxedValue(prototype)) {
|
|
83
111
|
let state = boxedPrototypes.get(budget);
|
|
84
112
|
if (state === undefined)
|
|
@@ -181,6 +209,9 @@ function trackIntrinsicState(budget, root, owner, targets) {
|
|
|
181
209
|
}))
|
|
182
210
|
.map((record) => ({
|
|
183
211
|
...record,
|
|
212
|
+
revision: functionPropertyRevisions.get(record.value),
|
|
213
|
+
capturedRevision: -1,
|
|
214
|
+
captured: [],
|
|
184
215
|
extensible: Object.isExtensible(record.value),
|
|
185
216
|
descriptors: new Map(Reflect.ownKeys(record.value).map(key => [key, Object.getOwnPropertyDescriptor(record.value, key)]))
|
|
186
217
|
}));
|
|
@@ -206,16 +237,24 @@ function trackIntrinsicState(budget, root, owner, targets) {
|
|
|
206
237
|
budget.setRetainedValues(root, () => {
|
|
207
238
|
// Capture every change before measurement invokes retained-value callbacks.
|
|
208
239
|
const retained = [];
|
|
209
|
-
for (const
|
|
240
|
+
for (const record of records) {
|
|
241
|
+
const { target, value, descriptors, prototype: parent, revision } = record;
|
|
210
242
|
const currentPrototype = getSandboxPrototype(target);
|
|
211
243
|
if (currentPrototype !== parent)
|
|
212
244
|
retained.push(currentPrototype);
|
|
213
|
-
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
245
|
+
if (revision === undefined || revision.revision !== record.capturedRevision) {
|
|
246
|
+
const captured = [];
|
|
247
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
248
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
249
|
+
if (unchanged(descriptors.get(key), descriptor))
|
|
250
|
+
continue;
|
|
251
|
+
captured.push(key, descriptor.value, ...retainedAccessorClosures(descriptor));
|
|
252
|
+
}
|
|
253
|
+
record.captured = captured;
|
|
254
|
+
record.capturedRevision = revision?.revision ?? -1;
|
|
218
255
|
}
|
|
256
|
+
for (const item of record.captured)
|
|
257
|
+
retained.push(item);
|
|
219
258
|
}
|
|
220
259
|
return retained;
|
|
221
260
|
});
|
|
@@ -232,12 +271,16 @@ export function releaseObjectPrototype(budget) {
|
|
|
232
271
|
promisePrototypes.delete(budget);
|
|
233
272
|
datePrototypes.delete(budget);
|
|
234
273
|
arrayPrototypes.delete(budget);
|
|
274
|
+
functionPrototypes.delete(budget);
|
|
275
|
+
generatorPrototypes.delete(budget);
|
|
235
276
|
initialRegexDescriptors.delete(budget);
|
|
236
277
|
intrinsicPrototypes.delete(budget);
|
|
237
278
|
}
|
|
238
279
|
export function getSandboxPrototype(value, budget) {
|
|
239
280
|
if (prototypes.has(value))
|
|
240
281
|
return prototypes.get(value) ?? null;
|
|
282
|
+
if (budget !== undefined && isGuestClosure(value) && runResources.getStore()?.functionSourceText !== false)
|
|
283
|
+
return functionPrototypes.get(budget) ?? null;
|
|
241
284
|
if (budget !== undefined && Array.isArray(value))
|
|
242
285
|
return arrayPrototypes.get(budget) ?? null;
|
|
243
286
|
if (budget !== undefined && isSandboxDate(value))
|
|
@@ -273,9 +316,14 @@ export function getSandboxPropertyDescriptor(value, key, budget) {
|
|
|
273
316
|
let depth = 0;
|
|
274
317
|
while (typeof current === "object" &&
|
|
275
318
|
current !== null &&
|
|
276
|
-
(Array.isArray(current) || isSandboxDate(current) || isSandboxPromise(current) || isSandboxRegex(current) || isSandboxMap(current) || isSandboxSet(current) || isPrototypeRecord(current))) {
|
|
277
|
-
const properties =
|
|
319
|
+
(Array.isArray(current) || isSandboxGenerator(current) || isSandboxDate(current) || isSandboxPromise(current) || isSandboxRegex(current) || isSandboxMap(current) || isSandboxSet(current) || isPrototypeRecord(current))) {
|
|
320
|
+
const properties = isSandboxGenerator(current) ? getGeneratorProperties(current) : isSandboxPromise(current) ? getPromiseProperties(current) : isGuestClosure(current)
|
|
321
|
+
? key === "prototype" && current.construct !== undefined && current.boundTarget === undefined
|
|
322
|
+
? materializeFunctionProperties(current) : getGuestFunctionProperties(current)
|
|
278
323
|
: isSandboxRegex(current) ? getRegexProperties(current) : isSandboxMap(current) || isSandboxSet(current) ? getCollectionProperties(current) : current;
|
|
324
|
+
if (properties === undefined && isGuestClosure(current) && (key === "name" || key === "length"))
|
|
325
|
+
return { value: key === "name" ? current.name ?? "" : current.length ?? 0,
|
|
326
|
+
writable: false, enumerable: false, configurable: true };
|
|
279
327
|
const descriptor = properties === undefined ? undefined : Object.getOwnPropertyDescriptor(properties, key);
|
|
280
328
|
if (descriptor !== undefined)
|
|
281
329
|
return descriptor;
|
|
@@ -297,6 +345,11 @@ export function getSandboxDataProperty(value, key, budget) {
|
|
|
297
345
|
return Object.getOwnPropertyDescriptor(getRegexProperties(current), key)?.value;
|
|
298
346
|
if (isSandboxPromise(current))
|
|
299
347
|
return Object.getOwnPropertyDescriptor(getPromiseProperties(current), key)?.value;
|
|
348
|
+
if (isSandboxGenerator(current)) {
|
|
349
|
+
const descriptor = Object.getOwnPropertyDescriptor(getGeneratorProperties(current), key);
|
|
350
|
+
if (descriptor !== undefined)
|
|
351
|
+
return descriptor.value;
|
|
352
|
+
}
|
|
300
353
|
if (isSandboxMap(current) || isSandboxSet(current))
|
|
301
354
|
return Object.getOwnPropertyDescriptor(getCollectionProperties(current), key)?.value;
|
|
302
355
|
if (isGuestClosure(current)) {
|
|
@@ -313,10 +366,9 @@ export function getSandboxDataProperty(value, key, budget) {
|
|
|
313
366
|
if (isSandboxMap(current) ||
|
|
314
367
|
isSandboxSet(current) ||
|
|
315
368
|
isSandboxPromise(current) ||
|
|
316
|
-
isSandboxRegex(current)
|
|
317
|
-
isSandboxGenerator(current))
|
|
369
|
+
isSandboxRegex(current))
|
|
318
370
|
return undefined;
|
|
319
|
-
if (!isSandboxClosure(current) && Object.hasOwn(current, key))
|
|
371
|
+
if (!isSandboxClosure(current) && !isSandboxGenerator(current) && Object.hasOwn(current, key))
|
|
320
372
|
return current[key];
|
|
321
373
|
current = getSandboxPrototype(current, budget);
|
|
322
374
|
if (current !== null) {
|
|
@@ -330,8 +382,8 @@ export function setSandboxPrototype(value, prototype, budget) {
|
|
|
330
382
|
if (budget !== undefined && intrinsicPrototypes.get(budget) === value && prototype !== null) {
|
|
331
383
|
throw new TypeError("Object.prototype has an immutable null prototype.");
|
|
332
384
|
}
|
|
333
|
-
if ((!Array.isArray(value) && !isSandboxDate(value) && !isSandboxPromise(value) && !isSandboxRegex(value) && !isSandboxMap(value) && !isSandboxSet(value) && !isPrototypeRecord(value)) ||
|
|
334
|
-
(prototype !== null && !Array.isArray(prototype) && !isSandboxDate(prototype) && !isSandboxPromise(prototype) && !isSandboxRegex(prototype) && !isSandboxMap(prototype) && !isSandboxSet(prototype) && !isPrototypeRecord(prototype))) {
|
|
385
|
+
if ((!Array.isArray(value) && !isSandboxGenerator(value) && !isSandboxDate(value) && !isSandboxPromise(value) && !isSandboxRegex(value) && !isSandboxMap(value) && !isSandboxSet(value) && !isPrototypeRecord(value)) ||
|
|
386
|
+
(prototype !== null && !Array.isArray(prototype) && !isSandboxGenerator(prototype) && !isSandboxDate(prototype) && !isSandboxPromise(prototype) && !isSandboxRegex(prototype) && !isSandboxMap(prototype) && !isSandboxSet(prototype) && !isPrototypeRecord(prototype))) {
|
|
335
387
|
throw new TypeError("Prototype links require supported sandbox objects.");
|
|
336
388
|
}
|
|
337
389
|
if (getSandboxPrototype(value, budget) === prototype) {
|
|
@@ -340,7 +392,7 @@ export function setSandboxPrototype(value, prototype, budget) {
|
|
|
340
392
|
prototypes.set(value, null);
|
|
341
393
|
return;
|
|
342
394
|
}
|
|
343
|
-
if (!Object.isExtensible(isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxPromise(value) ? getPromiseProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : isSandboxMap(value) || isSandboxSet(value) ? getCollectionProperties(value) : value)) {
|
|
395
|
+
if (!Object.isExtensible(isSandboxGenerator(value) ? getGeneratorProperties(value) : isGuestClosure(value) ? materializeFunctionProperties(value) : isSandboxPromise(value) ? getPromiseProperties(value) : isSandboxRegex(value) ? getRegexProperties(value) : isSandboxMap(value) || isSandboxSet(value) ? getCollectionProperties(value) : value)) {
|
|
344
396
|
throw new TypeError("Cannot change the prototype of a non-extensible object.");
|
|
345
397
|
}
|
|
346
398
|
let depth = 0;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sandboxErrorTypes } from "../error/shape.js";
|
|
2
|
+
import { getGeneratorProperties } from "./generator-properties.js";
|
|
2
3
|
import { readPropertyDescriptor } from "./accessors.js";
|
|
3
4
|
import { dateString, dateTime, isSandboxDate } from "./date.js";
|
|
4
5
|
import { invokeBuiltinClosure } from "./builtin-call.js";
|
|
@@ -115,7 +116,7 @@ function conversionHook(value, name, budget, context) {
|
|
|
115
116
|
((Array.isArray(value) && getSandboxPrototype(value, budget) === null) ||
|
|
116
117
|
isFloat32Array(value) ||
|
|
117
118
|
sandboxErrorTypes.has(value) ||
|
|
118
|
-
isSandboxClosure(value) ||
|
|
119
|
+
(isSandboxClosure(value) && getSandboxPrototype(value, budget) === null) ||
|
|
119
120
|
isSandboxMap(value) ||
|
|
120
121
|
isSandboxSet(value) ||
|
|
121
122
|
isSandboxCollectionIterator(value) ||
|
|
@@ -127,7 +128,7 @@ function conversionHook(value, name, budget, context) {
|
|
|
127
128
|
let current = value;
|
|
128
129
|
let depth = 0;
|
|
129
130
|
while (current !== null) {
|
|
130
|
-
const properties = isSandboxPromise(current) ? getPromiseProperties(current) : isGuestClosure(current) ? getGuestFunctionProperties(current)
|
|
131
|
+
const properties = isSandboxGenerator(current) ? getGeneratorProperties(current) : isSandboxPromise(current) ? getPromiseProperties(current) : isGuestClosure(current) ? getGuestFunctionProperties(current)
|
|
131
132
|
: isSandboxRegex(current) ? getRegexProperties(current)
|
|
132
133
|
: isSandboxMap(current) || isSandboxSet(current) ? getCollectionProperties(current) : current;
|
|
133
134
|
const descriptor = properties === undefined ? undefined : Object.getOwnPropertyDescriptor(properties, name);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
|
|
2
2
|
import { internalSymbols } from "./internal-symbols.js";
|
|
3
|
+
import { getGeneratorProperties } from "./generator-properties.js";
|
|
3
4
|
import { getRegexProperties, regexGuestProperties } from "./regexp-properties.js";
|
|
4
5
|
import { getCollectionProperties, collectionGuestProperties, copyCollectionProperties } from "./collection-properties.js";
|
|
5
6
|
export { getCollectionProperties } from "./collection-properties.js";
|
|
@@ -118,6 +119,8 @@ export function ownEnumerableSandboxEntries(value, excludedKeys) {
|
|
|
118
119
|
entries = Object.entries(getRegexProperties(value));
|
|
119
120
|
else if (isSandboxPromise(value))
|
|
120
121
|
entries = Object.entries(getPromiseProperties(value));
|
|
122
|
+
else if (isSandboxGenerator(value))
|
|
123
|
+
entries = Object.entries(getGeneratorProperties(value));
|
|
121
124
|
else if (isSandboxMap(value) || isSandboxSet(value))
|
|
122
125
|
entries = Object.entries(getCollectionProperties(value));
|
|
123
126
|
else if (isSandboxClosure(value) || isSandboxGenerator(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxPromise(value) || isSandboxRegex(value))
|
|
@@ -137,15 +140,15 @@ export function ownSandboxSymbolKeys(value) {
|
|
|
137
140
|
value = getRegexProperties(value);
|
|
138
141
|
else if (isSandboxPromise(value))
|
|
139
142
|
value = getPromiseProperties(value);
|
|
143
|
+
else if (isSandboxGenerator(value))
|
|
144
|
+
value = getGeneratorProperties(value);
|
|
140
145
|
else if (isSandboxMap(value) || isSandboxSet(value))
|
|
141
146
|
value = getCollectionProperties(value);
|
|
142
|
-
else if (isSandboxGenerator(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxPromise(value) || isSandboxRegex(value))
|
|
143
|
-
return [];
|
|
144
147
|
return Object.getOwnPropertySymbols(Object(value)).filter(key => !internalSymbols.has(key));
|
|
145
148
|
}
|
|
146
149
|
export function ownEnumerableSandboxKeys(value, includeSymbols = false) {
|
|
147
150
|
if (includeSymbols) {
|
|
148
|
-
const properties = isSandboxClosure(value) ? value.properties ?? {} : isSandboxPromise(value) ? getPromiseProperties(value) : isSandboxRegex(value) ? getRegexProperties(value)
|
|
151
|
+
const properties = isSandboxClosure(value) ? value.properties ?? {} : isSandboxGenerator(value) ? getGeneratorProperties(value) : isSandboxPromise(value) ? getPromiseProperties(value) : isSandboxRegex(value) ? getRegexProperties(value)
|
|
149
152
|
: isSandboxMap(value) || isSandboxSet(value) ? getCollectionProperties(value) : Object(value);
|
|
150
153
|
return [...ownEnumerableSandboxKeys(value), ...ownSandboxSymbolKeys(value).filter(key => Object.getOwnPropertyDescriptor(properties, key)?.enumerable === true)];
|
|
151
154
|
}
|
|
@@ -157,6 +160,8 @@ export function ownEnumerableSandboxKeys(value, includeSymbols = false) {
|
|
|
157
160
|
return Object.keys(value.properties ?? {});
|
|
158
161
|
if (isSandboxPromise(value))
|
|
159
162
|
return Object.keys(getPromiseProperties(value));
|
|
163
|
+
if (isSandboxGenerator(value))
|
|
164
|
+
return Object.keys(getGeneratorProperties(value));
|
|
160
165
|
if (isSandboxRegex(value))
|
|
161
166
|
return Object.keys(getRegexProperties(value));
|
|
162
167
|
if (isSandboxMap(value) || isSandboxSet(value))
|
|
@@ -213,6 +218,7 @@ export function createSandboxGenerator(channel, metadata = undefined) {
|
|
|
213
218
|
enumerable: false,
|
|
214
219
|
value: true
|
|
215
220
|
});
|
|
221
|
+
getGeneratorProperties(generator);
|
|
216
222
|
return generator;
|
|
217
223
|
}
|
|
218
224
|
export function createSandboxMap(entries = []) {
|
|
@@ -501,6 +507,18 @@ export function measureSandboxData(values, options = {}) {
|
|
|
501
507
|
return;
|
|
502
508
|
}
|
|
503
509
|
if (isSandboxGenerator(value)) {
|
|
510
|
+
const descriptors = Object.getOwnPropertyDescriptors(getGeneratorProperties(value));
|
|
511
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
512
|
+
const descriptor = descriptors[key];
|
|
513
|
+
usage += typeof key === "string" ? key.length + 1 : 1;
|
|
514
|
+
if (typeof key === "symbol")
|
|
515
|
+
visit(key, depth + 1);
|
|
516
|
+
if ("value" in descriptor)
|
|
517
|
+
visit(descriptor.value, depth + 1);
|
|
518
|
+
else
|
|
519
|
+
for (const closure of retainedAccessorClosures(descriptor))
|
|
520
|
+
visit(closure, depth + 1);
|
|
521
|
+
}
|
|
504
522
|
const snapshot = value.channel.snapshot();
|
|
505
523
|
usage += snapshot.sent.length;
|
|
506
524
|
for (const completion of snapshot.sent)
|
|
@@ -4,6 +4,7 @@ import { createBuiltinBindings } from "../interp/globals.js";
|
|
|
4
4
|
import { getIntrinsicIdentity, listIntrinsicIdentities, resolveIntrinsicIdentity } from "../interp/intrinsics.js";
|
|
5
5
|
import { releaseObjectPrototype } from "../interp/object-model.js";
|
|
6
6
|
import { isSandboxClosure } from "../interp/values.js";
|
|
7
|
+
import { assertSnapshotDataDepth } from "../graph-depth.js";
|
|
7
8
|
let intrinsicKinds;
|
|
8
9
|
function intrinsicCatalogue() {
|
|
9
10
|
if (intrinsicKinds !== undefined)
|
|
@@ -59,7 +60,7 @@ export function validateGuestHeapNode(raw, heap, maxArrayLength = 0xffffffff) {
|
|
|
59
60
|
createRawJson(node.text);
|
|
60
61
|
return true;
|
|
61
62
|
}
|
|
62
|
-
if (!["intrinsic", "guest-function", "guest-class", "guest-generator", "scope-frame", "guest-object", "guest-array", "array-iterator", "map", "set"].includes(String(node.kind)))
|
|
63
|
+
if (!["intrinsic", "bound-function", "guest-function", "guest-class", "guest-generator", "scope-frame", "guest-object", "guest-array", "array-iterator", "map", "set"].includes(String(node.kind)))
|
|
63
64
|
return false;
|
|
64
65
|
const reference = (value, kinds) => {
|
|
65
66
|
const ref = record(value);
|
|
@@ -74,7 +75,7 @@ export function validateGuestHeapNode(raw, heap, maxArrayLength = 0xffffffff) {
|
|
|
74
75
|
const callable = (value) => {
|
|
75
76
|
if (absent(value))
|
|
76
77
|
return;
|
|
77
|
-
const target = reference(value, ["intrinsic", "guest-function", "guest-class"]);
|
|
78
|
+
const target = reference(value, ["intrinsic", "bound-function", "guest-function", "guest-class"]);
|
|
78
79
|
if (target.kind === "intrinsic" && intrinsicCatalogue().get(String(target.id)) !== true)
|
|
79
80
|
throw new TypeError("Guest accessor reference is not callable.");
|
|
80
81
|
};
|
|
@@ -199,6 +200,27 @@ export function validateGuestHeapNode(raw, heap, maxArrayLength = 0xffffffff) {
|
|
|
199
200
|
throw new TypeError("Invalid class prototype descriptor.");
|
|
200
201
|
reference(descriptor.value, ["object", "guest-object"]);
|
|
201
202
|
}
|
|
203
|
+
else if (node.kind === "bound-function") {
|
|
204
|
+
fields(node, ["kind", "target", "thisValue", "args", "length", "state"], ["name"]);
|
|
205
|
+
if (Object.hasOwn(node, "name") && typeof node.name !== "string")
|
|
206
|
+
throw new TypeError("Invalid bound function name.");
|
|
207
|
+
if (array(node.args).length > maxArrayLength)
|
|
208
|
+
throw new TypeError("Bound arguments exceed allocation limit.");
|
|
209
|
+
if (!absent(node.length) && !(typeof node.length === "number" && node.length >= 0) &&
|
|
210
|
+
!(record(node.length).kind === "number" && record(node.length).value === "Infinity"))
|
|
211
|
+
throw new TypeError("Invalid bound function length.");
|
|
212
|
+
const visited = new Set([raw]);
|
|
213
|
+
let current = node;
|
|
214
|
+
while (current.kind === "bound-function") {
|
|
215
|
+
assertSnapshotDataDepth(visited.size, "<bound-target>");
|
|
216
|
+
callable(current.target);
|
|
217
|
+
current = reference(current.target);
|
|
218
|
+
if (visited.has(current))
|
|
219
|
+
throw new TypeError("Cyclic bound function target.");
|
|
220
|
+
visited.add(current);
|
|
221
|
+
}
|
|
222
|
+
state(node.state);
|
|
223
|
+
}
|
|
202
224
|
else if (node.kind === "guest-function") {
|
|
203
225
|
fields(node, ["kind", "astNodeId", "scope", "state"], ["name", "environment"]);
|
|
204
226
|
if (integer(node.astNodeId) < 1)
|
|
@@ -217,7 +239,9 @@ export function validateGuestHeapNode(raw, heap, maxArrayLength = 0xffffffff) {
|
|
|
217
239
|
state(node.state);
|
|
218
240
|
}
|
|
219
241
|
else if (node.kind === "guest-generator") {
|
|
220
|
-
fields(node, ["kind", "state", "astNodeId", "async", "scope", "closureScope", "sent"], ["suspendedScope", "yieldNodeId", "environment", "blockScopes", "finallyCompletions", "expressionStates"]);
|
|
242
|
+
fields(node, ["kind", "state", "astNodeId", "async", "scope", "closureScope", "sent"], ["suspendedScope", "yieldNodeId", "environment", "blockScopes", "finallyCompletions", "expressionStates", "objectState"]);
|
|
243
|
+
if (Object.hasOwn(node, "objectState"))
|
|
244
|
+
state(node.objectState);
|
|
221
245
|
if (!["start", "running", "suspended", "done"].includes(String(node.state)) || typeof node.async !== "boolean" || integer(node.astNodeId) < 1)
|
|
222
246
|
throw new TypeError("Invalid generator state.");
|
|
223
247
|
reference(node.scope, ["scope-frame"]);
|
|
@@ -13,6 +13,14 @@ export type GuestObjectState<T> = {
|
|
|
13
13
|
prototype?: T;
|
|
14
14
|
};
|
|
15
15
|
export type GuestHeapNode<T> = {
|
|
16
|
+
kind: "bound-function";
|
|
17
|
+
target: T;
|
|
18
|
+
thisValue: T;
|
|
19
|
+
args: T[];
|
|
20
|
+
name?: string;
|
|
21
|
+
length: T;
|
|
22
|
+
state: GuestObjectState<T>;
|
|
23
|
+
} | {
|
|
16
24
|
kind: "array-iterator";
|
|
17
25
|
source: T;
|
|
18
26
|
index: number;
|
|
@@ -61,6 +69,7 @@ export type GuestHeapNode<T> = {
|
|
|
61
69
|
homeObject?: T;
|
|
62
70
|
newTarget?: T;
|
|
63
71
|
};
|
|
72
|
+
objectState?: GuestObjectState<T>;
|
|
64
73
|
} | {
|
|
65
74
|
kind: "guest-object";
|
|
66
75
|
state: GuestObjectState<T>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getClosureOrigin, getGeneratorOrigin } from "../interp/closure-origin.js";
|
|
2
|
+
import { boundFunctionStates } from "../interp/bound-function-state.js";
|
|
3
|
+
import { getGeneratorProperties } from "../interp/generator-properties.js";
|
|
2
4
|
import { getIntrinsicIdentity } from "../interp/intrinsics.js";
|
|
3
5
|
import { getSandboxPrototype, hasExplicitSandboxPrototype, hasGuestObjectState, isGuestClosure, materializeFunctionProperties } from "../interp/object-model.js";
|
|
4
6
|
import { isLiveCapability } from "../interp/host-capabilities.js";
|
|
@@ -26,6 +28,12 @@ export function captureGuestHeapNode(value, encode) {
|
|
|
26
28
|
const state = captureObjectState(value, encode);
|
|
27
29
|
return { kind: "intrinsic", id: intrinsic, ...(state === undefined ? {} : { state }) };
|
|
28
30
|
}
|
|
31
|
+
const bound = boundFunctionStates.get(value);
|
|
32
|
+
if (bound !== undefined && isSandboxClosure(value)) {
|
|
33
|
+
return { kind: "bound-function", target: encode(bound.target), thisValue: encode(bound.thisValue),
|
|
34
|
+
args: bound.args.map(encode), length: encode(value.length),
|
|
35
|
+
...(value.name === undefined ? {} : { name: value.name }), state: captureObjectState(value, encode) };
|
|
36
|
+
}
|
|
29
37
|
if (isSandboxMap(value))
|
|
30
38
|
return { kind: "map", entries: [...value.entries].map(([key, entry]) => [encode(key), encode(entry)]), ...serializeCollectionProperties(value, encode) };
|
|
31
39
|
if (isSandboxSet(value))
|
|
@@ -65,6 +73,7 @@ export function captureGuestHeapNode(value, encode) {
|
|
|
65
73
|
const channel = value.channel.snapshot();
|
|
66
74
|
return {
|
|
67
75
|
kind: "guest-generator", state: value.state, astNodeId: origin.node.nodeId, async: value.async === true,
|
|
76
|
+
objectState: captureObjectState(value, encode),
|
|
68
77
|
scope: encode(origin.scope), closureScope: encode(origin.closureScope),
|
|
69
78
|
...(value.state !== "suspended" || origin.suspendedScope === undefined ? {} : { suspendedScope: encode(origin.suspendedScope) }),
|
|
70
79
|
...(value.state !== "suspended" || origin.blockScopes === undefined ? {} : {
|
|
@@ -164,6 +173,8 @@ export function captureGuestHeapNode(value, encode) {
|
|
|
164
173
|
}
|
|
165
174
|
function captureObjectState(value, encode) {
|
|
166
175
|
let properties = value;
|
|
176
|
+
if (isSandboxGenerator(value))
|
|
177
|
+
properties = getGeneratorProperties(value);
|
|
167
178
|
if (isSandboxClosure(value)) {
|
|
168
179
|
properties = value.properties;
|
|
169
180
|
if (isGuestClosure(value))
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Budget, SandboxError } from "../interp/budget.js";
|
|
2
|
+
import { createBoundFunction } from "../interp/bound-function.js";
|
|
3
|
+
import { invokeBuiltinClosure } from "../interp/builtin-call.js";
|
|
4
|
+
import { getGeneratorProperties } from "../interp/generator-properties.js";
|
|
2
5
|
import { restoreRegexProperties } from "./regexp-properties.js";
|
|
3
6
|
import { classOrigins, createClassConstructor } from "../interp/classes.js";
|
|
4
7
|
import { mapIteratorSnapshot } from "../interp/iteration.js";
|
|
@@ -375,6 +378,29 @@ function isSerializedRegexValue(value) {
|
|
|
375
378
|
typeof value.flags === "string" &&
|
|
376
379
|
typeof value.lastIndex === "number");
|
|
377
380
|
}
|
|
381
|
+
function initializeIntrinsicRealm(state) {
|
|
382
|
+
if (state.intrinsicsInitialized)
|
|
383
|
+
return;
|
|
384
|
+
const prototype = Object.values(state.heap).find(node => node.kind === "intrinsic" && node.id === '["%FunctionPrototype%"]');
|
|
385
|
+
// Older heaps could omit this property or contain a guest-defined hook.
|
|
386
|
+
// Do not preinstall a nonconfigurable property over their captured state.
|
|
387
|
+
const functionHasInstance = prototype?.kind !== "intrinsic" || prototype.state === undefined ||
|
|
388
|
+
prototype.state.properties.properties.some(([key, descriptor]) => {
|
|
389
|
+
if (key === null || typeof key !== "object" || !("kind" in key) || key.kind !== "ref")
|
|
390
|
+
return false;
|
|
391
|
+
const symbol = state.heap[String(key.id)];
|
|
392
|
+
if (symbol?.kind !== "symbol" || symbol.wellKnown !== "hasInstance" || descriptor.kind !== "data" ||
|
|
393
|
+
descriptor.writable || descriptor.enumerable || descriptor.configurable)
|
|
394
|
+
return false;
|
|
395
|
+
const value = descriptor.value;
|
|
396
|
+
if (value === null || typeof value !== "object" || !("kind" in value) || value.kind !== "ref")
|
|
397
|
+
return false;
|
|
398
|
+
const method = state.heap[String(value.id)];
|
|
399
|
+
return method?.kind === "intrinsic" && method.id === '["%FunctionPrototype%",{"symbol":"hasInstance"}]';
|
|
400
|
+
});
|
|
401
|
+
createBuiltinBindings({ budget: state.budget, compileOwner: state.compilation.owner, functionHasInstance });
|
|
402
|
+
state.intrinsicsInitialized = true;
|
|
403
|
+
}
|
|
378
404
|
function restoreHeapValue(id, state) {
|
|
379
405
|
const existing = state.heapValueById.get(id);
|
|
380
406
|
if (existing !== undefined) {
|
|
@@ -521,15 +547,36 @@ function restoreHeapValue(id, state) {
|
|
|
521
547
|
if (serialized.kind === "guest-generator") {
|
|
522
548
|
const generator = restoreGuestGenerator(serialized, state);
|
|
523
549
|
state.heapValueById.set(id, generator);
|
|
550
|
+
const objectState = serialized.objectState;
|
|
551
|
+
if (objectState !== undefined)
|
|
552
|
+
state.initializeIterators.push(() => {
|
|
553
|
+
if (objectState.prototype !== undefined)
|
|
554
|
+
setSandboxPrototype(generator, deserializeValue(objectState.prototype, state), state.budget);
|
|
555
|
+
restorePropertyDescriptors(getGeneratorProperties(generator), objectState.properties, entry => deserializeValue(entry, state));
|
|
556
|
+
});
|
|
524
557
|
return generator;
|
|
525
558
|
}
|
|
526
|
-
if (serialized.kind === "intrinsic" || serialized.kind === "guest-function" || serialized.kind === "guest-class" || serialized.kind === "guest-object" || serialized.kind === "guest-array" || serialized.kind === "array-iterator") {
|
|
559
|
+
if (serialized.kind === "intrinsic" || serialized.kind === "bound-function" || serialized.kind === "guest-function" || serialized.kind === "guest-class" || serialized.kind === "guest-object" || serialized.kind === "guest-array" || serialized.kind === "array-iterator") {
|
|
527
560
|
let value;
|
|
528
|
-
if (serialized.kind === "
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
561
|
+
if (serialized.kind === "bound-function") {
|
|
562
|
+
initializeIntrinsicRealm(state);
|
|
563
|
+
const target = deserializeValue(serialized.target, state);
|
|
564
|
+
if (!isSandboxClosure(target))
|
|
565
|
+
throw new TypeError("Invalid bound function target.");
|
|
566
|
+
const boundState = { target, thisValue: undefined, args: [] };
|
|
567
|
+
const length = deserializeValue(serialized.length, state);
|
|
568
|
+
if (length !== undefined && typeof length !== "number")
|
|
569
|
+
throw new TypeError("Invalid bound function length.");
|
|
570
|
+
value = createBoundFunction(boundState, serialized.name, length, (callee, args, stack, thisValue, construct, newTarget) => invokeBuiltinClosure(callee, args, state.budget, { stack, thisValue, newTarget }, thisValue, construct));
|
|
571
|
+
// Allocate every bound identity before decoding arguments and receivers:
|
|
572
|
+
// either may point back to this function, including through another bind.
|
|
573
|
+
state.initializeIterators.push(() => {
|
|
574
|
+
boundState.thisValue = deserializeValue(serialized.thisValue, state);
|
|
575
|
+
boundState.args = serialized.args.map(entry => deserializeValue(entry, state));
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
else if (serialized.kind === "intrinsic") {
|
|
579
|
+
initializeIntrinsicRealm(state);
|
|
533
580
|
value = resolveIntrinsicIdentity(state.budget, serialized.id);
|
|
534
581
|
}
|
|
535
582
|
else if (serialized.kind === "guest-class") {
|
|
@@ -570,12 +617,19 @@ function restoreHeapValue(id, state) {
|
|
|
570
617
|
homeObject: serialized.environment.homeObject === undefined ? undefined : deserializeValue(serialized.environment.homeObject, state),
|
|
571
618
|
newTarget: serialized.environment.newTarget === undefined ? undefined : deserializeValue(serialized.environment.newTarget, state)
|
|
572
619
|
};
|
|
620
|
+
// Current generator functions need their realm before creation, not only
|
|
621
|
+
// when a later prototype reference is decoded. Earlier heaps can lack the
|
|
622
|
+
// native nonconfigurable prototype property and retain that legacy shape.
|
|
623
|
+
const initializeGeneratorPrototype = node.type !== "ArrowFunctionExpression" && node.generator &&
|
|
624
|
+
serialized.state.properties.properties.some(([key, descriptor]) => key === "prototype" && descriptor.kind === "data" && !descriptor.configurable);
|
|
625
|
+
if (initializeGeneratorPrototype)
|
|
626
|
+
initializeIntrinsicRealm(state);
|
|
573
627
|
value = createInterpretedClosure(node, {
|
|
574
628
|
scope, budget: state.budget, compilation: state.compilation, rootNode: state.rootNode,
|
|
575
629
|
signal: state.signal, inferredName: serialized.name, functionEnvironment: environment,
|
|
576
630
|
callStack: [], activeLoopIterations: new Map(), restoredLoopIterations: new Map(),
|
|
577
631
|
stats: { currentDataSize: 0, nodeVisits: 0, peakDataSize: 0 }
|
|
578
|
-
}, evaluateNode, environment?.homeObject);
|
|
632
|
+
}, evaluateNode, environment?.homeObject, initializeGeneratorPrototype);
|
|
579
633
|
}
|
|
580
634
|
else
|
|
581
635
|
value = serialized.kind === "guest-array" ? [] : Object.create(null);
|