poe-code 14.0.51 → 14.0.52
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 +287 -90
- 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-7AIY5C3F.js → chunk-DVWTNFTV.js} +208 -48
- package/packages/safe-js/dist/chunks/chunk-DVWTNFTV.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-MQU3AETK.js → chunk-XIBH4BH2.js} +2 -2
- 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 +10 -4
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/cancel.js +3 -0
- package/packages/safe-js/dist/interp/collection-brands.d.ts +5 -0
- package/packages/safe-js/dist/interp/collection-brands.js +8 -0
- package/packages/safe-js/dist/interp/collection-iterator.d.ts +27 -0
- package/packages/safe-js/dist/interp/collection-iterator.js +61 -0
- package/packages/safe-js/dist/interp/globals/collections.js +1 -1
- package/packages/safe-js/dist/interp/globals/misc.js +3 -2
- package/packages/safe-js/dist/interp/globals/object-array.js +2 -2
- package/packages/safe-js/dist/interp/globals/object.js +3 -0
- package/packages/safe-js/dist/interp/interpreter.js +15 -10
- package/packages/safe-js/dist/interp/iteration.d.ts +2 -1
- package/packages/safe-js/dist/interp/iteration.js +4 -1
- package/packages/safe-js/dist/interp/methods/collection-iterator.d.ts +4 -0
- package/packages/safe-js/dist/interp/methods/collection-iterator.js +19 -0
- package/packages/safe-js/dist/interp/methods/map.js +3 -4
- package/packages/safe-js/dist/interp/methods/set.js +3 -3
- package/packages/safe-js/dist/interp/patterns.d.ts +2 -0
- package/packages/safe-js/dist/interp/patterns.js +20 -2
- package/packages/safe-js/dist/interp/promise.js +1 -1
- package/packages/safe-js/dist/interp/string-coercion.js +4 -0
- package/packages/safe-js/dist/interp/values.d.ts +4 -5
- package/packages/safe-js/dist/interp/values.js +42 -10
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +10 -0
- package/packages/safe-js/dist/snapshot/replay-data.js +32 -0
- package/packages/safe-js/dist/snapshot/replay-inputs.js +15 -0
- package/packages/safe-js/dist/snapshot/restore.js +16 -0
- package/packages/safe-js/dist/snapshot/serialize.d.ts +10 -1
- package/packages/safe-js/dist/snapshot/serialize.js +25 -9
- package/packages/safe-js/dist/snapshot/validation.js +14 -1
- package/packages/safe-js/dist/chunks/chunk-7AIY5C3F.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-MQU3AETK.js.map → chunk-XIBH4BH2.js.map} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
|
|
2
|
+
import { isSandboxMap, isSandboxSet, sandboxMapBrand, sandboxSetBrand } from "./collection-brands.js";
|
|
3
|
+
import { collectionIteratorState, isSandboxCollectionIterator, restoreSandboxCollectionIterator, snapshotCollectionIterator } from "./collection-iterator.js";
|
|
2
4
|
import { copyNativeDate, exportDate, isSandboxDate } from "./date.js";
|
|
3
5
|
import { getHostObjectKeys, getHostObjectMember, hasHostObjectMember, measureHostObjectData, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
|
|
4
6
|
import { types as nodeTypes } from "node:util";
|
|
@@ -14,13 +16,12 @@ import { sandboxErrorTypes } from "../error/shape.js";
|
|
|
14
16
|
import { getGuestFunctionProperties, getSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, isGuestClosure, registerGuestClosure } from "./object-model.js";
|
|
15
17
|
import { copySandboxArgumentProperties, createSandboxArguments, getSandboxArgumentEntries, isSandboxArguments } from "./arguments.js";
|
|
16
18
|
export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
|
|
19
|
+
export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
|
|
17
20
|
const sandboxClosureBrand = Symbol("SandboxClosure");
|
|
18
21
|
const sandboxGeneratorBrand = Symbol("SandboxGenerator");
|
|
19
|
-
const sandboxMapBrand = Symbol("SandboxMap");
|
|
20
22
|
const sandboxPromiseBrand = Symbol("SandboxPromise");
|
|
21
23
|
const sandboxRegexBrand = Symbol("SandboxRegex");
|
|
22
24
|
const sandboxRegexPattern = Symbol("SandboxRegexPattern");
|
|
23
|
-
const sandboxSetBrand = Symbol("SandboxSet");
|
|
24
25
|
const sandboxRetainedValues = Symbol("SandboxRetainedValues");
|
|
25
26
|
export function createSandboxClosure(input) {
|
|
26
27
|
const closure = {
|
|
@@ -195,15 +196,9 @@ export function getSandboxRegexPattern(regex) {
|
|
|
195
196
|
export function isSandboxClosure(value) {
|
|
196
197
|
return typeof value === "object" && value !== null && sandboxClosureBrand in value;
|
|
197
198
|
}
|
|
198
|
-
export function isSandboxMap(value) {
|
|
199
|
-
return typeof value === "object" && value !== null && sandboxMapBrand in value;
|
|
200
|
-
}
|
|
201
199
|
export function isSandboxPromise(value) {
|
|
202
200
|
return typeof value === "object" && value !== null && sandboxPromiseBrand in value;
|
|
203
201
|
}
|
|
204
|
-
export function isSandboxSet(value) {
|
|
205
|
-
return typeof value === "object" && value !== null && sandboxSetBrand in value;
|
|
206
|
-
}
|
|
207
202
|
export function isSandboxGenerator(value) {
|
|
208
203
|
return typeof value === "object" && value !== null && sandboxGeneratorBrand in value;
|
|
209
204
|
}
|
|
@@ -216,9 +211,14 @@ export function deepCopyToSandbox(value) {
|
|
|
216
211
|
});
|
|
217
212
|
}
|
|
218
213
|
export function cloneSandboxValue(value) {
|
|
219
|
-
|
|
220
|
-
|
|
214
|
+
const initializeIterators = [];
|
|
215
|
+
const copy = copyToSandbox(value, {
|
|
216
|
+
seen: new WeakMap(),
|
|
217
|
+
initializeIterators
|
|
221
218
|
}, "<root>", true);
|
|
219
|
+
for (const initialize of initializeIterators)
|
|
220
|
+
initialize();
|
|
221
|
+
return copy;
|
|
222
222
|
}
|
|
223
223
|
export function allocateProducedSandboxValue(value, budget) {
|
|
224
224
|
allocateSandboxValue(value, budget, new WeakSet());
|
|
@@ -289,6 +289,15 @@ export function measureSandboxData(values, options = {}) {
|
|
|
289
289
|
}
|
|
290
290
|
return;
|
|
291
291
|
}
|
|
292
|
+
if (isSandboxCollectionIterator(value)) {
|
|
293
|
+
visit(collectionIteratorState(value).collection, depth + 1);
|
|
294
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
295
|
+
usage += key.length + 1;
|
|
296
|
+
if ("value" in descriptor)
|
|
297
|
+
visit(descriptor.value, depth + 1);
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
292
301
|
if (isSandboxSet(value)) {
|
|
293
302
|
usage += value.values.size;
|
|
294
303
|
for (const entry of value.values)
|
|
@@ -409,6 +418,26 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
|
|
|
409
418
|
isSandboxPromise(value)) {
|
|
410
419
|
return value;
|
|
411
420
|
}
|
|
421
|
+
if (isSandboxCollectionIterator(value)) {
|
|
422
|
+
if (hasGuestObjectState(value))
|
|
423
|
+
throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
|
|
424
|
+
if (!cloneSandboxCollections)
|
|
425
|
+
return value;
|
|
426
|
+
const existing = state.seen.get(value);
|
|
427
|
+
if (existing !== undefined)
|
|
428
|
+
return existing;
|
|
429
|
+
const snapshot = snapshotCollectionIterator(value);
|
|
430
|
+
const copy = restoreSandboxCollectionIterator({ ...snapshot, collection: undefined, index: 0, exhausted: true });
|
|
431
|
+
state.seen.set(value, copy);
|
|
432
|
+
const collection = copyToSandbox(snapshot.collection, state, `${path}.<collection>`, true, depth + 1);
|
|
433
|
+
if (collection !== undefined && !isSandboxMap(collection) && !isSandboxSet(collection))
|
|
434
|
+
throw new TypeError("Invalid cloned collection iterator source.");
|
|
435
|
+
state.initializeIterators.push(() => { restoreSandboxCollectionIterator({ ...snapshot, collection }, copy); });
|
|
436
|
+
for (const entry of getEnumerableObjectEntries(value, path)) {
|
|
437
|
+
defineOwnDataProperty(copy, entry.key, copyToSandbox(entry.value, state, joinPath(path, entry.key), true, depth + 1));
|
|
438
|
+
}
|
|
439
|
+
return copy;
|
|
440
|
+
}
|
|
412
441
|
if (typeof value === "object" && value !== null && hasGuestObjectState(value)) {
|
|
413
442
|
throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
|
|
414
443
|
}
|
|
@@ -634,6 +663,9 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
|
|
|
634
663
|
if (isSandboxGenerator(value)) {
|
|
635
664
|
throw new TypeError("Sandbox generators cannot cross into host values.");
|
|
636
665
|
}
|
|
666
|
+
if (isSandboxCollectionIterator(value)) {
|
|
667
|
+
throw new TypeError("Sandbox collection iterators cannot cross into host values.");
|
|
668
|
+
}
|
|
637
669
|
if (isSandboxRegex(value)) {
|
|
638
670
|
throw new TypeError("Invalid sandbox RegExp brand.");
|
|
639
671
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CollectionIterationMethod } from "../interp/collection-iterator.js";
|
|
1
2
|
import { CompileScope } from "../interp/regex/compile-guard.js";
|
|
2
3
|
import { type Float32Data } from "./float32array.js";
|
|
3
4
|
import { type SandboxErrorName } from "../error/shape.js";
|
|
@@ -25,6 +26,15 @@ type Properties = Record<string, {
|
|
|
25
26
|
writable: boolean;
|
|
26
27
|
}>;
|
|
27
28
|
type DataNode = {
|
|
29
|
+
kind: "collection-iterator";
|
|
30
|
+
collectionKind: "map" | "set";
|
|
31
|
+
method: CollectionIterationMethod;
|
|
32
|
+
collection: Atom;
|
|
33
|
+
index: number;
|
|
34
|
+
exhausted: boolean;
|
|
35
|
+
properties: Properties;
|
|
36
|
+
extensible: boolean;
|
|
37
|
+
} | {
|
|
28
38
|
kind: "date";
|
|
29
39
|
time: number | null;
|
|
30
40
|
} | (Float32Data<Atom> & {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MAX_DATA_DEPTH } from "../graph-depth.js";
|
|
2
|
+
import { isSandboxCollectionIterator, restoreSandboxCollectionIterator, snapshotCollectionIterator } from "../interp/collection-iterator.js";
|
|
2
3
|
import { hasGuestObjectState } from "../interp/object-model.js";
|
|
3
4
|
import { CompileScope } from "../interp/regex/compile-guard.js";
|
|
4
5
|
import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
|
|
@@ -83,6 +84,16 @@ export function encodeReplayData(value, options = {}) {
|
|
|
83
84
|
}
|
|
84
85
|
nodes[id] = { ...storage, properties, extensible: Object.isExtensible(entry) };
|
|
85
86
|
}
|
|
87
|
+
else if (isSandboxCollectionIterator(entry)) {
|
|
88
|
+
const snapshot = snapshotCollectionIterator(entry);
|
|
89
|
+
const properties = Object.create(null);
|
|
90
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(entry))) {
|
|
91
|
+
if (!("value" in descriptor))
|
|
92
|
+
throw new TypeError(`Cannot record replay data accessor '${key}'.`);
|
|
93
|
+
properties[key] = { value: child(descriptor.value, JSON.stringify(["property", key])), configurable: descriptor.configurable === true, enumerable: descriptor.enumerable === true, writable: descriptor.writable === true };
|
|
94
|
+
}
|
|
95
|
+
nodes[id] = { kind: "collection-iterator", collectionKind: snapshot.collectionKind, method: snapshot.method, collection: child(snapshot.collection, "<collection>"), index: snapshot.index, exhausted: snapshot.exhausted, properties, extensible: Object.isExtensible(entry) };
|
|
96
|
+
}
|
|
86
97
|
else if (isSandboxMap(entry)) {
|
|
87
98
|
nodes[id] = {
|
|
88
99
|
kind: "map",
|
|
@@ -146,6 +157,7 @@ export function decodeReplayData(input, options = {}, parent) {
|
|
|
146
157
|
const graph = record(input);
|
|
147
158
|
const nodes = list(own(graph, "nodes"));
|
|
148
159
|
const restored = new Map();
|
|
160
|
+
const initializeIterators = [];
|
|
149
161
|
const decode = (entry, depth = 0) => {
|
|
150
162
|
if (depth > MAX_DATA_DEPTH)
|
|
151
163
|
throw new TypeError("Replay data exceeds the nesting limit.");
|
|
@@ -252,6 +264,24 @@ export function decodeReplayData(input, options = {}, parent) {
|
|
|
252
264
|
Object.preventExtensions(result);
|
|
253
265
|
return result;
|
|
254
266
|
}
|
|
267
|
+
if (kind === "collection-iterator") {
|
|
268
|
+
const collectionKind = own(node, "collectionKind");
|
|
269
|
+
const method = own(node, "method");
|
|
270
|
+
const index = own(node, "index");
|
|
271
|
+
const exhausted = own(node, "exhausted");
|
|
272
|
+
if ((collectionKind !== "map" && collectionKind !== "set") || (method !== "keys" && method !== "values" && method !== "entries") || typeof index !== "number" || !Number.isSafeInteger(index) || index < 0 || typeof exhausted !== "boolean" || typeof node.extensible !== "boolean")
|
|
273
|
+
throw new TypeError("Invalid replay collection iterator.");
|
|
274
|
+
const result = restoreSandboxCollectionIterator({ collection: undefined, collectionKind, method, index: 0, exhausted: true });
|
|
275
|
+
restored.set(id, result);
|
|
276
|
+
const collection = child(own(node, "collection"));
|
|
277
|
+
if (collection !== undefined && !isSandboxMap(collection) && !isSandboxSet(collection))
|
|
278
|
+
throw new TypeError("Invalid replay collection iterator source.");
|
|
279
|
+
initializeIterators.push(() => { restoreSandboxCollectionIterator({ collection, collectionKind, method, index, exhausted }, result); });
|
|
280
|
+
defineProperties(result, record(own(node, "properties")), child);
|
|
281
|
+
if (!node.extensible)
|
|
282
|
+
Object.preventExtensions(result);
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
255
285
|
if (kind === "map") {
|
|
256
286
|
const result = createSandboxMap();
|
|
257
287
|
restored.set(id, result);
|
|
@@ -320,6 +350,8 @@ export function decodeReplayData(input, options = {}, parent) {
|
|
|
320
350
|
return result;
|
|
321
351
|
};
|
|
322
352
|
const result = decode(own(graph, "root"));
|
|
353
|
+
for (const initialize of initializeIterators)
|
|
354
|
+
initialize();
|
|
323
355
|
if (parent !== undefined)
|
|
324
356
|
compilation.forward(compilation.tickets, parent);
|
|
325
357
|
return result;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { collectionIteratorState, isSandboxCollectionIterator } from "../interp/collection-iterator.js";
|
|
1
2
|
import { createSandboxPromise, isSandboxClosure, isSandboxMap, isSandboxSet, isSandboxPromise } from "../interp/values.js";
|
|
2
3
|
import { decodeReplayData, encodeReplayData } from "./replay-data.js";
|
|
3
4
|
import { CompileScope } from "../interp/regex/compile-guard.js";
|
|
@@ -41,6 +42,18 @@ export function prepareReplayInputs(current, saved, preparePromise, onCapability
|
|
|
41
42
|
throw new TypeError("Invalid replay input capability path.");
|
|
42
43
|
let value = current;
|
|
43
44
|
for (const key of path) {
|
|
45
|
+
if (isSandboxCollectionIterator(value)) {
|
|
46
|
+
if (key === "<collection>")
|
|
47
|
+
value = collectionIteratorState(value).collection;
|
|
48
|
+
else {
|
|
49
|
+
const property = JSON.parse(key);
|
|
50
|
+
if (!Array.isArray(property) || property.length !== 2 || property[0] !== "property" || typeof property[1] !== "string")
|
|
51
|
+
throw new TypeError("Invalid replay input iterator capability path.");
|
|
52
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, property[1]);
|
|
53
|
+
value = descriptor !== undefined && "value" in descriptor ? descriptor.value : undefined;
|
|
54
|
+
}
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
44
57
|
if (isSandboxMap(value)) {
|
|
45
58
|
const [kind, ordinal] = key.split(":");
|
|
46
59
|
const index = Number(ordinal);
|
|
@@ -104,6 +117,7 @@ function assertReplayInputShape(restored) {
|
|
|
104
117
|
Array.isArray(restored) ||
|
|
105
118
|
isSandboxClosure(restored) ||
|
|
106
119
|
isSandboxPromise(restored) ||
|
|
120
|
+
isSandboxCollectionIterator(restored) ||
|
|
107
121
|
isSandboxMap(restored) ||
|
|
108
122
|
isSandboxSet(restored))
|
|
109
123
|
throw new TypeError("Invalid replay inputs.");
|
|
@@ -114,6 +128,7 @@ function assertReplayInputShape(restored) {
|
|
|
114
128
|
Array.isArray(values[key]) ||
|
|
115
129
|
isSandboxClosure(values[key]) ||
|
|
116
130
|
isSandboxPromise(values[key]) ||
|
|
131
|
+
isSandboxCollectionIterator(values[key]) ||
|
|
117
132
|
isSandboxMap(values[key]) ||
|
|
118
133
|
isSandboxSet(values[key]))
|
|
119
134
|
throw new TypeError(`Invalid replay input ${key}.`);
|
|
@@ -9,6 +9,8 @@ import { SnapshotMismatchError } from "../restore.js";
|
|
|
9
9
|
import { Scope, setSandboxProperty } from "../interp/interpreter.js";
|
|
10
10
|
import { getGuestFunctionProperties, getGuestFunctionProperty, getSandboxDataProperty, registerGuestClosure, setSandboxPrototype } from "../interp/object-model.js";
|
|
11
11
|
import { wrapCallerInjectedBindings } from "../interp/host-bridge.js";
|
|
12
|
+
import { restoreSandboxCollectionIterator } from "../interp/collection-iterator.js";
|
|
13
|
+
import { isSandboxMap, isSandboxSet } from "../interp/values.js";
|
|
12
14
|
import { createSandboxArguments, createSandboxClosure, createSandboxGenerator, createSandboxMap, createSandboxPromise, createSandboxRegex, createSandboxSet, reconcileCompiledValues } from "../interp/values.js";
|
|
13
15
|
import { createGeneratorChannel, restoreGeneratorChannel } from "../interp/generator.js";
|
|
14
16
|
import { hashSource } from "../parse/hash.js";
|
|
@@ -45,6 +47,7 @@ export function restore(snapshot, options, owner) {
|
|
|
45
47
|
throw new Error(`Snapshot references unknown AST node ${snapshot.currentAstNodeId}.`);
|
|
46
48
|
}
|
|
47
49
|
const state = {
|
|
50
|
+
initializeIterators: [],
|
|
48
51
|
budget,
|
|
49
52
|
compilation,
|
|
50
53
|
heap: snapshot.heap ?? {},
|
|
@@ -71,6 +74,8 @@ export function restore(snapshot, options, owner) {
|
|
|
71
74
|
throw new Error(`Snapshot references unknown scope ${String(currentScopeId)}.`);
|
|
72
75
|
}
|
|
73
76
|
const callStack = snapshot.callStack.map((frame) => restoreCallFrame(frame, state));
|
|
77
|
+
for (const initialize of state.initializeIterators)
|
|
78
|
+
initialize();
|
|
74
79
|
reconcileCompiledValues(budget, [
|
|
75
80
|
...currentScope.retainedValues(),
|
|
76
81
|
...pendingPromises.flatMap((pending) => Object.values(pending).filter(isSandboxSnapshotValue))
|
|
@@ -397,6 +402,17 @@ function restoreHeapValue(id, state) {
|
|
|
397
402
|
Object.preventExtensions(args);
|
|
398
403
|
return args;
|
|
399
404
|
}
|
|
405
|
+
if (serialized.kind === "collection-iterator") {
|
|
406
|
+
const iterator = restoreSandboxCollectionIterator({ collection: undefined, collectionKind: serialized.collectionKind, method: serialized.method, index: 0, exhausted: true });
|
|
407
|
+
state.heapValueById.set(id, iterator);
|
|
408
|
+
const collection = deserializeValue(serialized.collection, state);
|
|
409
|
+
if (collection !== undefined && !isSandboxMap(collection) && !isSandboxSet(collection))
|
|
410
|
+
throw new TypeError("Invalid collection iterator source.");
|
|
411
|
+
state.initializeIterators.push(() => { restoreSandboxCollectionIterator({ ...serialized, collection }, iterator); });
|
|
412
|
+
for (const [key, entry] of Object.entries(serialized.entries))
|
|
413
|
+
Object.defineProperty(iterator, key, { value: deserializeValue(entry, state), enumerable: true, configurable: true, writable: true });
|
|
414
|
+
return iterator;
|
|
415
|
+
}
|
|
400
416
|
if (serialized.kind === "map") {
|
|
401
417
|
const map = createSandboxMap();
|
|
402
418
|
state.heapValueById.set(id, map);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CollectionIterationMethod, type SandboxCollectionIterator } from "../interp/collection-iterator.js";
|
|
1
2
|
import { type SandboxErrorName } from "../error/shape.js";
|
|
2
3
|
import { type SerializedArguments } from "./arguments.js";
|
|
3
4
|
import { type SerializedArray } from "./arrays.js";
|
|
@@ -41,6 +42,14 @@ export type SerializedReferenceValue = {
|
|
|
41
42
|
id: number;
|
|
42
43
|
};
|
|
43
44
|
export type SerializedHeapValue = {
|
|
45
|
+
kind: "collection-iterator";
|
|
46
|
+
collectionKind: "map" | "set";
|
|
47
|
+
method: CollectionIterationMethod;
|
|
48
|
+
collection: SerializedSnapshotValue;
|
|
49
|
+
index: number;
|
|
50
|
+
exhausted: boolean;
|
|
51
|
+
entries: Record<string, SerializedSnapshotValue>;
|
|
52
|
+
} | {
|
|
44
53
|
kind: "date";
|
|
45
54
|
time: number | null;
|
|
46
55
|
} | (Float32Data<SerializedReferenceValue> & {
|
|
@@ -75,7 +84,7 @@ export type RuntimePromiseValue = {
|
|
|
75
84
|
id: SnapshotId;
|
|
76
85
|
promise?: Promise<unknown>;
|
|
77
86
|
};
|
|
78
|
-
export type RuntimeSnapshotValue = Date | Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxGenerator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
|
|
87
|
+
export type RuntimeSnapshotValue = Date | Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxGenerator | SandboxCollectionIterator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
|
|
79
88
|
[key: string]: RuntimeSnapshotValue;
|
|
80
89
|
};
|
|
81
90
|
export type RuntimeScopeFrame = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { hashSource } from "../parse/hash.js";
|
|
2
|
+
import { collectionIteratorState, isSandboxCollectionIterator, snapshotCollectionIterator } from "../interp/collection-iterator.js";
|
|
2
3
|
import { hasGuestObjectState } from "../interp/object-model.js";
|
|
3
4
|
import { sandboxErrorTypes } from "../error/shape.js";
|
|
4
5
|
import { assertSnapshotGraphDepth } from "../graph-depth.js";
|
|
@@ -162,7 +163,7 @@ function serializeValue(value, path, state) {
|
|
|
162
163
|
if (isSandboxRegex(value)) {
|
|
163
164
|
return { kind: "regex", source: value.source, flags: value.flags, lastIndex: value.lastIndex };
|
|
164
165
|
}
|
|
165
|
-
if (isSandboxDate(value) || isSandboxMap(value) || isSandboxSet(value) || isFloat32Array(value)) {
|
|
166
|
+
if (isSandboxDate(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxCollectionIterator(value) || isFloat32Array(value)) {
|
|
166
167
|
const reference = serializeHeapReference(value, path, state);
|
|
167
168
|
if (reference === undefined) {
|
|
168
169
|
throw new TypeError(`Cannot serialize collection without a heap reference at ${path}.`);
|
|
@@ -191,7 +192,18 @@ function serializeHeapReference(value, path, state) {
|
|
|
191
192
|
}
|
|
192
193
|
if (!state.serializedHeapIds.has(id)) {
|
|
193
194
|
state.serializedHeapIds.add(id);
|
|
194
|
-
if (
|
|
195
|
+
if (isSandboxCollectionIterator(value)) {
|
|
196
|
+
const snapshot = snapshotCollectionIterator(value);
|
|
197
|
+
const entries = Object.create(null);
|
|
198
|
+
state.heap[String(id)] = {
|
|
199
|
+
kind: "collection-iterator", collectionKind: snapshot.collectionKind, method: snapshot.method,
|
|
200
|
+
collection: serializeValue(snapshot.collection, `${path}.<collection>`, state),
|
|
201
|
+
index: snapshot.index, exhausted: snapshot.exhausted, entries
|
|
202
|
+
};
|
|
203
|
+
for (const [key, entry] of Object.entries(value))
|
|
204
|
+
entries[key] = serializeValue(entry, `${path}.${key}`, state);
|
|
205
|
+
}
|
|
206
|
+
else if (isSandboxDate(value)) {
|
|
195
207
|
state.heap[String(id)] = { kind: "date", time: serializedDateTime(value) };
|
|
196
208
|
}
|
|
197
209
|
else if (isFloat32Array(value)) {
|
|
@@ -306,6 +318,7 @@ function indexHeapContainers(input) {
|
|
|
306
318
|
(Array.isArray(value) && requiresArrayEntries(value)) ||
|
|
307
319
|
sandboxErrorTypes.has(value) ||
|
|
308
320
|
isSandboxArguments(value) ||
|
|
321
|
+
isSandboxCollectionIterator(value) ||
|
|
309
322
|
isSandboxMap(value) ||
|
|
310
323
|
isSandboxSet(value)) {
|
|
311
324
|
heapIds.set(value, nextId);
|
|
@@ -327,6 +340,7 @@ function collectContainerStats(value, stats, ancestors) {
|
|
|
327
340
|
!isSandboxDate(value) &&
|
|
328
341
|
!isFloat32Array(value) &&
|
|
329
342
|
!isSandboxMap(value) &&
|
|
343
|
+
!isSandboxCollectionIterator(value) &&
|
|
330
344
|
!isSandboxSet(value)) {
|
|
331
345
|
return;
|
|
332
346
|
}
|
|
@@ -349,13 +363,15 @@ function collectContainerStats(value, stats, ancestors) {
|
|
|
349
363
|
}
|
|
350
364
|
stat.expanded = true;
|
|
351
365
|
ancestors.add(value);
|
|
352
|
-
const entries =
|
|
353
|
-
?
|
|
354
|
-
:
|
|
355
|
-
?
|
|
356
|
-
:
|
|
357
|
-
? [...value.
|
|
358
|
-
:
|
|
366
|
+
const entries = isSandboxCollectionIterator(value)
|
|
367
|
+
? [collectionIteratorState(value).collection, ...Object.values(value)]
|
|
368
|
+
: isSandboxArguments(value)
|
|
369
|
+
? Object.values(Object.getOwnPropertyDescriptors(value)).flatMap((descriptor) => "value" in descriptor ? [descriptor.value] : [])
|
|
370
|
+
: isSandboxMap(value)
|
|
371
|
+
? [...value.entries].flatMap(([key, entry]) => [key, entry])
|
|
372
|
+
: isSandboxSet(value)
|
|
373
|
+
? [...value.values]
|
|
374
|
+
: Object.values(value);
|
|
359
375
|
for (const entry of entries) {
|
|
360
376
|
collectContainerStats(entry, stats, ancestors);
|
|
361
377
|
}
|
|
@@ -364,6 +364,7 @@ function validateTaggedValue(record, path, state) {
|
|
|
364
364
|
case "object":
|
|
365
365
|
case "map":
|
|
366
366
|
case "set":
|
|
367
|
+
case "collection-iterator":
|
|
367
368
|
return;
|
|
368
369
|
}
|
|
369
370
|
}
|
|
@@ -407,7 +408,7 @@ function validateGeneratorShape(record, path, state) {
|
|
|
407
408
|
function validateHeapValue(value, path, state) {
|
|
408
409
|
const record = requireRecord(value, path);
|
|
409
410
|
validateErrorType(record, path);
|
|
410
|
-
if (!["arguments", "array", "object", "map", "set", "float32array", "date"].includes(String(record.kind)))
|
|
411
|
+
if (!["arguments", "array", "object", "map", "set", "float32array", "date", "collection-iterator"].includes(String(record.kind)))
|
|
411
412
|
fail("unknownTag", `${path}.kind`, "unknown heap tag");
|
|
412
413
|
validateValue(record, path, 1, state);
|
|
413
414
|
if (record.kind === "arguments")
|
|
@@ -431,6 +432,18 @@ function validateHeapValue(value, path, state) {
|
|
|
431
432
|
}
|
|
432
433
|
if (record.kind === "set")
|
|
433
434
|
requireArray(record.values, `${path}.values`, state);
|
|
435
|
+
if (record.kind === "collection-iterator") {
|
|
436
|
+
if (record.collectionKind !== "map" && record.collectionKind !== "set")
|
|
437
|
+
fail("invalidValue", `${path}.collectionKind`, "invalid iterator brand");
|
|
438
|
+
if (record.method !== "keys" && record.method !== "values" && record.method !== "entries")
|
|
439
|
+
fail("invalidValue", `${path}.method`, "invalid iteration method");
|
|
440
|
+
if (typeof record.exhausted !== "boolean")
|
|
441
|
+
fail("invalidValue", `${path}.exhausted`, "invalid iterator exhaustion");
|
|
442
|
+
requireSafeInteger(record.index, `${path}.index`, 0);
|
|
443
|
+
if (!Object.hasOwn(record, "collection"))
|
|
444
|
+
fail("invalidValue", `${path}.collection`, "missing iterator source");
|
|
445
|
+
requireRecord(record.entries, `${path}.entries`);
|
|
446
|
+
}
|
|
434
447
|
}
|
|
435
448
|
function validateDateRecord(record, path) {
|
|
436
449
|
if (Object.keys(record).length !== 2)
|