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.
Files changed (47) hide show
  1. package/dist/index.js +772 -341
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-B3AL4KJ3.js → chunk-2LNBLFJ3.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-BGSPSHLP.js → chunk-ZSYBRZU6.js} +644 -292
  7. package/packages/safe-js/dist/chunks/chunk-ZSYBRZU6.js.map +7 -0
  8. package/packages/safe-js/dist/cli.js +2 -2
  9. package/packages/safe-js/dist/core.js +1 -1
  10. package/packages/safe-js/dist/graph-depth.js +2 -1
  11. package/packages/safe-js/dist/index.js +2 -2
  12. package/packages/safe-js/dist/interp/async.d.ts +1 -1
  13. package/packages/safe-js/dist/interp/async.js +65 -43
  14. package/packages/safe-js/dist/interp/bound-function-state.d.ts +7 -0
  15. package/packages/safe-js/dist/interp/bound-function-state.js +1 -0
  16. package/packages/safe-js/dist/interp/bound-function.d.ts +4 -0
  17. package/packages/safe-js/dist/interp/bound-function.js +18 -0
  18. package/packages/safe-js/dist/interp/generator-properties.d.ts +3 -0
  19. package/packages/safe-js/dist/interp/generator-properties.js +9 -0
  20. package/packages/safe-js/dist/interp/generator-prototypes.d.ts +6 -0
  21. package/packages/safe-js/dist/interp/generator-prototypes.js +1 -0
  22. package/packages/safe-js/dist/interp/globals/function.d.ts +2 -0
  23. package/packages/safe-js/dist/interp/globals/function.js +32 -0
  24. package/packages/safe-js/dist/interp/globals/generator.d.ts +2 -0
  25. package/packages/safe-js/dist/interp/globals/generator.js +51 -0
  26. package/packages/safe-js/dist/interp/globals/object-array.js +28 -7
  27. package/packages/safe-js/dist/interp/globals/object.js +2 -1
  28. package/packages/safe-js/dist/interp/globals.d.ts +1 -0
  29. package/packages/safe-js/dist/interp/globals.js +4 -0
  30. package/packages/safe-js/dist/interp/instanceof.d.ts +4 -0
  31. package/packages/safe-js/dist/interp/instanceof.js +58 -0
  32. package/packages/safe-js/dist/interp/interpreter.js +16 -47
  33. package/packages/safe-js/dist/interp/iteration.js +76 -5
  34. package/packages/safe-js/dist/interp/methods/function.d.ts +5 -2
  35. package/packages/safe-js/dist/interp/methods/function.js +55 -42
  36. package/packages/safe-js/dist/interp/methods/generator.d.ts +4 -1
  37. package/packages/safe-js/dist/interp/methods/generator.js +12 -6
  38. package/packages/safe-js/dist/interp/object-model.d.ts +1 -0
  39. package/packages/safe-js/dist/interp/object-model.js +69 -17
  40. package/packages/safe-js/dist/interp/string-coercion.js +3 -2
  41. package/packages/safe-js/dist/interp/values.js +21 -3
  42. package/packages/safe-js/dist/snapshot/guest-heap-validation.js +27 -3
  43. package/packages/safe-js/dist/snapshot/guest-heap.d.ts +9 -0
  44. package/packages/safe-js/dist/snapshot/guest-heap.js +11 -0
  45. package/packages/safe-js/dist/snapshot/restore.js +61 -7
  46. package/packages/safe-js/dist/chunks/chunk-BGSPSHLP.js.map +0 -7
  47. /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
- descriptorObjects.add(properties);
57
- functionProperties.set(closure, properties);
58
- return properties;
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 { target, value, descriptors, prototype: parent } of records) {
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
- for (const key of Reflect.ownKeys(value)) {
214
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
215
- if (unchanged(descriptors.get(key), descriptor))
216
- continue;
217
- retained.push(key, descriptor.value, ...retainedAccessorClosures(descriptor));
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 = isSandboxPromise(current) ? getPromiseProperties(current) : isGuestClosure(current) ? getGuestFunctionProperties(current)
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 === "intrinsic") {
529
- if (!state.intrinsicsInitialized) {
530
- createBuiltinBindings({ budget: state.budget, compileOwner: state.compilation.owner });
531
- state.intrinsicsInitialized = true;
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);