poe-code 14.0.81 → 14.0.83

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 (44) hide show
  1. package/dist/index.js +933 -353
  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-2LNBLFJ3.js → chunk-37V6Q4K3.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-ZSYBRZU6.js → chunk-PT7PBLVT.js} +764 -224
  7. package/packages/safe-js/dist/chunks/chunk-PT7PBLVT.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/index.js +2 -2
  11. package/packages/safe-js/dist/interp/error-prototypes.d.ts +3 -0
  12. package/packages/safe-js/dist/interp/error-prototypes.js +1 -0
  13. package/packages/safe-js/dist/interp/exceptions.d.ts +1 -0
  14. package/packages/safe-js/dist/interp/exceptions.js +39 -15
  15. package/packages/safe-js/dist/interp/float32-prototypes.d.ts +3 -0
  16. package/packages/safe-js/dist/interp/float32-prototypes.js +1 -0
  17. package/packages/safe-js/dist/interp/float32.d.ts +1 -0
  18. package/packages/safe-js/dist/interp/float32.js +12 -4
  19. package/packages/safe-js/dist/interp/globals/error.d.ts +2 -0
  20. package/packages/safe-js/dist/interp/globals/error.js +104 -4
  21. package/packages/safe-js/dist/interp/globals/float32array.d.ts +3 -2
  22. package/packages/safe-js/dist/interp/globals/float32array.js +371 -65
  23. package/packages/safe-js/dist/interp/globals/object-array.js +3 -3
  24. package/packages/safe-js/dist/interp/globals.d.ts +2 -0
  25. package/packages/safe-js/dist/interp/globals.js +8 -4
  26. package/packages/safe-js/dist/interp/host-bridge.js +4 -2
  27. package/packages/safe-js/dist/interp/instanceof.js +4 -3
  28. package/packages/safe-js/dist/interp/interpreter.js +2 -4
  29. package/packages/safe-js/dist/interp/iteration.js +15 -6
  30. package/packages/safe-js/dist/interp/object-model.js +27 -1
  31. package/packages/safe-js/dist/interp/values.js +59 -6
  32. package/packages/safe-js/dist/parse/parser.js +8 -3
  33. package/packages/safe-js/dist/parse/tokenizer.js +45 -3
  34. package/packages/safe-js/dist/snapshot/float32array.d.ts +3 -0
  35. package/packages/safe-js/dist/snapshot/float32array.js +20 -1
  36. package/packages/safe-js/dist/snapshot/guest-heap-validation.js +4 -1
  37. package/packages/safe-js/dist/snapshot/guest-heap.d.ts +2 -0
  38. package/packages/safe-js/dist/snapshot/guest-heap.js +6 -2
  39. package/packages/safe-js/dist/snapshot/restore.js +33 -3
  40. package/packages/safe-js/dist/snapshot/serialize.d.ts +2 -1
  41. package/packages/safe-js/dist/snapshot/serialize.js +10 -9
  42. package/packages/safe-js/dist/snapshot/validation.js +11 -1
  43. package/packages/safe-js/dist/chunks/chunk-ZSYBRZU6.js.map +0 -7
  44. /package/packages/safe-js/dist/chunks/{chunk-2LNBLFJ3.js.map → chunk-37V6Q4K3.js.map} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxSet } from "./values.js";
2
2
  import { enterRunningState } from "./running-state.js";
3
3
  import { isFloat32Array } from "./float32.js";
4
+ import { float32Prototypes } from "./float32-prototypes.js";
4
5
  import { boxedValue, isSandboxBox } from "./boxed.js";
5
6
  import { getHostObjectIterator, isGuestHostObject } from "./host-capabilities.js";
6
7
  import { isSandboxCollectionIterator, nextCollectionIterator } from "./collection-iterator.js";
@@ -98,6 +99,9 @@ export async function acquireSandboxIterator(value, budget, context, asyncProtoc
98
99
  const iterator = await invokeBuiltinClosure(factory, [], budget, context, value);
99
100
  if ((typeof iterator !== "object" && typeof iterator !== "function") || iterator === null)
100
101
  throw new TypeError("Iterator must be an object.");
102
+ if (!asyncProtocol && isSandboxGenerator(iterator) && !iterator.async &&
103
+ !hasExplicitSandboxPrototype(iterator) && getSandboxPropertyDescriptor(iterator, "next", budget) === undefined)
104
+ return getSandboxIterator(iterator, budget, context);
101
105
  const next = await context.getProperty(iterator, "next");
102
106
  return guestIterator(iterator, next, asyncProtocol, budget, context, signal);
103
107
  }
@@ -137,7 +141,7 @@ export async function readIteratorResult(iterator, result, property) {
137
141
  export function getSandboxAsyncIterator(value, budget, context, signal) {
138
142
  if (isSandboxGenerator(value) && value.async) {
139
143
  return hasExplicitSandboxPrototype(value) ? context?.getProperty === undefined
140
- ? generatorProtocolAdapter(value, budget, context, true, signal) : undefined
144
+ ? guestProtocolAdapter(value, budget, context, true, signal) : undefined
141
145
  : { ...generatorObjectIterator(value, budget, context), asyncProtocol: true };
142
146
  }
143
147
  if (value !== null &&
@@ -293,6 +297,11 @@ export function getSandboxIterator(value, budget, context) {
293
297
  if (isGuestHostObject(value))
294
298
  return getHostObjectIterator(value);
295
299
  if (isFloat32Array(value)) {
300
+ if (hasExplicitSandboxPrototype(value) || (budget !== undefined && float32Prototypes.has(budget))) {
301
+ if (getSandboxPropertyDescriptor(value, Symbol.iterator, budget) === undefined)
302
+ return undefined;
303
+ return guestProtocolAdapter(value, budget ?? new Budget(), context, false);
304
+ }
296
305
  return syncIterator(Float32Array.prototype.values.call(value));
297
306
  }
298
307
  if (isSandboxGenerator(value)) {
@@ -300,7 +309,7 @@ export function getSandboxIterator(value, budget, context) {
300
309
  return undefined;
301
310
  if (hasExplicitSandboxPrototype(value))
302
311
  return context?.getProperty === undefined
303
- ? generatorProtocolAdapter(value, budget ?? new Budget(), context, false) : undefined;
312
+ ? guestProtocolAdapter(value, budget ?? new Budget(), context, false) : undefined;
304
313
  return generatorObjectIterator(value, budget, context);
305
314
  }
306
315
  if (typeof value === "string") {
@@ -386,9 +395,9 @@ function collectionIterator(collection, value) {
386
395
  return adapter;
387
396
  }
388
397
  const asyncGeneratorRequests = new WeakMap();
389
- function generatorProtocolAdapter(value, budget, context, asyncProtocol, signal) {
390
- // Direct host adapters still need guest property reads, receiver binding and
391
- // cached next methods, but retain their asynchronous generator result channel.
398
+ function guestProtocolAdapter(value, budget, context, asyncProtocol, signal) {
399
+ // Direct adapters still need guest property reads, receiver binding and
400
+ // cached next methods without exposing native iterator objects.
392
401
  const bridge = {
393
402
  ...context,
394
403
  stack: context?.stack ?? [],
@@ -403,7 +412,7 @@ function generatorProtocolAdapter(value, budget, context, asyncProtocol, signal)
403
412
  let pending;
404
413
  const initialize = () => pending ??= acquireSandboxIterator(value, budget, bridge, asyncProtocol, signal).then(iterator => {
405
414
  if (iterator === undefined)
406
- throw new TypeError("Generator does not provide an iterator protocol.");
415
+ throw new TypeError("Value does not provide an iterator protocol.");
407
416
  return resolved = iterator;
408
417
  });
409
418
  return {
@@ -7,6 +7,10 @@ import { releaseTemplateObjects } from "./template-objects.js";
7
7
  import { isSandboxDate } from "./date.js";
8
8
  import { retainedAccessorClosures } from "./accessors.js";
9
9
  import { getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
10
+ import { errorPrototypes } from "./error-prototypes.js";
11
+ import { float32Properties, isFloat32Array, isFloat32Index } from "./float32.js";
12
+ import { float32Prototypes } from "./float32-prototypes.js";
13
+ import { sandboxErrorTypes } from "../error/shape.js";
10
14
  import { boxedValue, isSandboxBox } from "./boxed.js";
11
15
  import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, getPromiseProperties, isSandboxRegex, getRegexProperties, getCollectionProperties, isSandboxSet } from "./values.js";
12
16
  const guestClosures = new WeakSet();
@@ -273,12 +277,24 @@ export function releaseObjectPrototype(budget) {
273
277
  arrayPrototypes.delete(budget);
274
278
  functionPrototypes.delete(budget);
275
279
  generatorPrototypes.delete(budget);
280
+ errorPrototypes.delete(budget);
281
+ float32Prototypes.delete(budget);
276
282
  initialRegexDescriptors.delete(budget);
277
283
  intrinsicPrototypes.delete(budget);
278
284
  }
279
285
  export function getSandboxPrototype(value, budget) {
280
286
  if (prototypes.has(value))
281
287
  return prototypes.get(value) ?? null;
288
+ if (isFloat32Array(value))
289
+ return budget === undefined ? null : float32Prototypes.get(budget) ?? null;
290
+ // Host transport records are data-only. Resolve their default prototype in
291
+ // the receiving realm without persisting executable intrinsic graphs.
292
+ const errorType = sandboxErrorTypes.get(value);
293
+ if (budget !== undefined && errorType !== undefined) {
294
+ const prototype = errorPrototypes.get(budget)?.get(errorType);
295
+ if (prototype !== undefined)
296
+ return prototype;
297
+ }
282
298
  if (budget !== undefined && isGuestClosure(value) && runResources.getStore()?.functionSourceText !== false)
283
299
  return functionPrototypes.get(budget) ?? null;
284
300
  if (budget !== undefined && Array.isArray(value))
@@ -317,6 +333,10 @@ export function getSandboxPropertyDescriptor(value, key, budget) {
317
333
  while (typeof current === "object" &&
318
334
  current !== null &&
319
335
  (Array.isArray(current) || isSandboxGenerator(current) || isSandboxDate(current) || isSandboxPromise(current) || isSandboxRegex(current) || isSandboxMap(current) || isSandboxSet(current) || isPrototypeRecord(current))) {
336
+ // An integer-indexed object stops numeric-key lookup even when it occurs
337
+ // inside another object's prototype chain and the index is invalid.
338
+ if (isFloat32Array(current) && typeof key !== "symbol" && isFloat32Index(String(key)))
339
+ return Object.getOwnPropertyDescriptor(current, key);
320
340
  const properties = isSandboxGenerator(current) ? getGeneratorProperties(current) : isSandboxPromise(current) ? getPromiseProperties(current) : isGuestClosure(current)
321
341
  ? key === "prototype" && current.construct !== undefined && current.boundTarget === undefined
322
342
  ? materializeFunctionProperties(current) : getGuestFunctionProperties(current)
@@ -339,6 +359,8 @@ export function getSandboxDataProperty(value, key, budget) {
339
359
  let current = value;
340
360
  let depth = 0;
341
361
  while (typeof current === "object" && current !== null) {
362
+ if (isFloat32Array(current) && typeof key !== "symbol" && isFloat32Index(String(key)))
363
+ return Object.getOwnPropertyDescriptor(current, key)?.value;
342
364
  if (isGuestHostObject(current))
343
365
  return typeof key === "symbol" ? undefined : getHostObjectMember(current, String(key));
344
366
  if (isSandboxRegex(current))
@@ -405,6 +427,8 @@ export function setSandboxPrototype(value, prototype, budget) {
405
427
  prototypes.set(value, prototype);
406
428
  }
407
429
  function isPrototypeRecord(value) {
430
+ if (isFloat32Array(value))
431
+ return true;
408
432
  if (isGuestClosure(value))
409
433
  return true;
410
434
  if (isGuestHostObject(value))
@@ -436,5 +460,7 @@ export function hasGuestObjectState(value) {
436
460
  if (isSandboxBox(value) || isSandboxDate(value))
437
461
  return false;
438
462
  return (descriptorObjects.has(value) &&
439
- Object.values(Object.getOwnPropertyDescriptors(value)).some((descriptor) => !descriptor.enumerable || !descriptor.configurable || !descriptor.writable));
463
+ (isFloat32Array(value)
464
+ ? float32Properties(value).map(([, descriptor]) => descriptor)
465
+ : Object.values(Object.getOwnPropertyDescriptors(value))).some((descriptor) => !descriptor.enumerable || !descriptor.configurable || !descriptor.writable));
440
466
  }
@@ -1,5 +1,6 @@
1
1
  import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
2
2
  import { internalSymbols } from "./internal-symbols.js";
3
+ import { getIntrinsicIdentity } from "./intrinsics.js";
3
4
  import { getGeneratorProperties } from "./generator-properties.js";
4
5
  import { getRegexProperties, regexGuestProperties } from "./regexp-properties.js";
5
6
  import { getCollectionProperties, collectionGuestProperties, copyCollectionProperties } from "./collection-properties.js";
@@ -16,7 +17,7 @@ import { boxedDataProperties, boxedValue, createSandboxBox, isSandboxBox, native
16
17
  import { getHostObjectKeys, getHostObjectMember, hasHostObjectMember, measureHostObjectData, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
17
18
  import { types as nodeTypes } from "node:util";
18
19
  import { CompileScope, RegexCompileGuard, regexCompiledData } from "./regex/compile-guard.js";
19
- import { copyFloat32Storage, float32DataProperties, float32Storage, isFloat32Array } from "./float32.js";
20
+ import { copyFloat32Storage, float32DataProperties, float32Properties, float32Storage, isFloat32Array } from "./float32.js";
20
21
  import { SandboxError } from "./budget.js";
21
22
  import { observeSandboxPromise, trackSandboxPromise } from "./promise-tracker.js";
22
23
  import { promiseReplayContext } from "./promise-replay.js";
@@ -24,7 +25,7 @@ import { invokeCancelableClosure, readPromiseCancellation, registerPromiseCancel
24
25
  import { parseRegex } from "./regex/parse.js";
25
26
  import { assertSandboxDataDepth } from "../graph-depth.js";
26
27
  import { sandboxErrorTypes } from "../error/shape.js";
27
- import { getGuestFunctionProperties, getSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, hasNullObjectPrototype, isGuestClosure, isIntrinsicFunction, registerGuestClosure, setSandboxPrototype } from "./object-model.js";
28
+ import { getGuestFunctionProperties, getSandboxPrototype, hasExplicitSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, hasNullObjectPrototype, isGuestClosure, isIntrinsicFunction, registerGuestClosure, setSandboxPrototype } from "./object-model.js";
28
29
  import { copySandboxArgumentProperties, createSandboxArguments, getSandboxArgumentEntries, isSandboxArguments } from "./arguments.js";
29
30
  export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
30
31
  export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
@@ -404,9 +405,17 @@ export function measureSandboxData(values, options = {}) {
404
405
  seen.add(storage.buffer);
405
406
  usage += storage.byteLength;
406
407
  }
407
- for (const [key, descriptor] of float32DataProperties(value)) {
408
- usage += key.length + 1;
409
- visit(descriptor.value, depth + 1);
408
+ for (const [key, descriptor] of float32Properties(value)) {
409
+ usage += 1;
410
+ if (typeof key === "string")
411
+ usage += key.length;
412
+ else
413
+ visit(key, depth + 1);
414
+ if ("value" in descriptor)
415
+ visit(descriptor.value, depth + 1);
416
+ else
417
+ for (const closure of retainedAccessorClosures(descriptor))
418
+ visit(closure, depth + 1);
410
419
  }
411
420
  return;
412
421
  }
@@ -578,7 +587,7 @@ export function measureSandboxData(values, options = {}) {
578
587
  if (descriptor !== undefined)
579
588
  descriptors.push([key, descriptor]);
580
589
  }
581
- const includeNonEnumerable = isSandboxDate(value) || hasManagedDescriptors(value);
590
+ const includeNonEnumerable = isSandboxDate(value) || sandboxErrorTypes.has(value) || hasManagedDescriptors(value);
582
591
  for (const [key, descriptor] of descriptors) {
583
592
  if (!descriptor.enumerable && !includeNonEnumerable)
584
593
  continue;
@@ -908,6 +917,50 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
908
917
  }
909
918
  if (nodeTypes.isProxy(value))
910
919
  throw new TypeError("Unsupported proxy sandbox value.");
920
+ if (sandboxErrorTypes.has(value) && hasExplicitSandboxPrototype(value)) {
921
+ const prototype = getSandboxPrototype(value);
922
+ let nativePrototype = null;
923
+ if (prototype !== null) {
924
+ const identity = getIntrinsicIdentity(prototype);
925
+ const nativePrototypes = {
926
+ Error: Error.prototype, TypeError: TypeError.prototype, RangeError: RangeError.prototype,
927
+ ReferenceError: ReferenceError.prototype, SyntaxError: SyntaxError.prototype,
928
+ URIError: URIError.prototype, EvalError: EvalError.prototype, AggregateError: AggregateError.prototype
929
+ };
930
+ const path = identity === undefined ? [] : JSON.parse(identity);
931
+ if (path.length !== 2 || typeof path[0] !== "string" || path[1] !== "prototype" || !Object.hasOwn(nativePrototypes, path[0]))
932
+ throw new TypeError("Custom Error prototype links cannot be copied as data.");
933
+ nativePrototype = nativePrototypes[path[0]];
934
+ let current = prototype;
935
+ let prototypeDepth = 0;
936
+ while (current !== null) {
937
+ assertSandboxDataDepth(++prototypeDepth);
938
+ const objectPrototype = getIntrinsicIdentity(current) === '["Object","prototype"]';
939
+ const owner = objectPrototype ? Object.getOwnPropertyDescriptor(current, "constructor")?.value : current;
940
+ if (owner === null || typeof owner !== "object" ||
941
+ (objectPrototype && getIntrinsicIdentity(owner) !== '["Object"]') || hasGuestObjectState(owner))
942
+ throw new TypeError("Modified Error prototype links cannot be copied as data.");
943
+ current = getSandboxPrototype(current);
944
+ }
945
+ }
946
+ const existing = state.seen.get(value);
947
+ if (existing !== undefined)
948
+ return existing;
949
+ const descriptors = Reflect.ownKeys(value).filter(key => typeof key !== "symbol" || !internalSymbols.has(key))
950
+ .map(key => [key, Object.getOwnPropertyDescriptor(value, key)]);
951
+ if (descriptors.some(([, descriptor]) => !("value" in descriptor)))
952
+ throw new TypeError("Error accessor properties cannot be copied as data.");
953
+ const copy = new Error();
954
+ delete copy.stack;
955
+ Object.setPrototypeOf(copy, nativePrototype);
956
+ state.seen.set(value, copy);
957
+ for (const [key, descriptor] of descriptors)
958
+ Object.defineProperty(copy, key, { ...descriptor,
959
+ value: copyFromSandbox(descriptor.value, state, joinPath(path, key), options, depth + 1) });
960
+ if (!Object.isExtensible(value))
961
+ Object.preventExtensions(copy);
962
+ return copy;
963
+ }
911
964
  if (!isSandboxClosure(value) && hasGuestObjectState(value)) {
912
965
  throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
913
966
  }
@@ -2709,6 +2709,7 @@ class Parser {
2709
2709
  return undefined;
2710
2710
  }
2711
2711
  findTopLevelForIterationOperator(startIndex) {
2712
+ let operator;
2712
2713
  let depth = 0;
2713
2714
  let previousToken;
2714
2715
  for (let index = startIndex; index < this.tokens.length; index += 1) {
@@ -2719,7 +2720,7 @@ class Parser {
2719
2720
  }
2720
2721
  else if (token.value === ")" || token.value === "]" || token.value === "}") {
2721
2722
  if (depth === 0 && token.value === ")") {
2722
- return undefined;
2723
+ return operator;
2723
2724
  }
2724
2725
  depth -= 1;
2725
2726
  }
@@ -2730,9 +2731,12 @@ class Parser {
2730
2731
  if (depth === 0 &&
2731
2732
  token.type === "keyword" &&
2732
2733
  (token.value === "of" || token.value === "in") &&
2734
+ index !== startIndex &&
2735
+ !(token.value === "of" && index === startIndex + 1 &&
2736
+ ["const", "let", "var"].includes(previousToken?.value ?? "")) &&
2733
2737
  previousToken?.value !== "." &&
2734
2738
  previousToken?.value !== "?.") {
2735
- return token;
2739
+ operator ??= token;
2736
2740
  }
2737
2741
  previousToken = token;
2738
2742
  }
@@ -3752,7 +3756,8 @@ function isLiteralPropertyKey(token) {
3752
3756
  return token.type === "numeric" || token.type === "string";
3753
3757
  }
3754
3758
  function isIdentifierLikeToken(token) {
3755
- return token.type === "identifier" || (token.type === "keyword" && token.value === "async");
3759
+ return token.type === "identifier" ||
3760
+ (token.type === "keyword" && (token.value === "async" || token.value === "of"));
3756
3761
  }
3757
3762
  function isNewToken(token) {
3758
3763
  return token.value === "new";
@@ -358,7 +358,7 @@ class Lexer {
358
358
  }
359
359
  if (char === "/" &&
360
360
  this.peekChar(1) !== "=" &&
361
- shouldRejectRegexLiteral(tokens[tokens.length - 1], lastClosedControlParenthesis)) {
361
+ shouldRejectRegexLiteral(tokens[tokens.length - 1], lastClosedControlParenthesis, tokens)) {
362
362
  if (!this.options.allowRegexLiterals) {
363
363
  this.syntaxError("Regular expression literals are not supported", this.position());
364
364
  }
@@ -633,7 +633,7 @@ class Lexer {
633
633
  readSlashOrPunctuator(start) {
634
634
  if (this.currentChar() === "/" &&
635
635
  this.peekChar(1) !== "=" &&
636
- shouldRejectRegexLiteral(this.lastSignificantToken(), this.lastClosedControlParenthesis)) {
636
+ shouldRejectRegexLiteral(this.lastSignificantToken(), this.lastClosedControlParenthesis, this.tokens)) {
637
637
  if (!this.options.allowRegexLiterals) {
638
638
  this.syntaxError("Regular expression literals are not supported", start);
639
639
  }
@@ -919,7 +919,7 @@ function matchPunctuator(source, index) {
919
919
  return PUNCTUATORS.find((punctuator) => source.startsWith(punctuator, index) &&
920
920
  (punctuator !== "?." || !isDecimalDigit(source[index + 2] ?? "")));
921
921
  }
922
- function shouldRejectRegexLiteral(previousToken, lastClosedControlParenthesis) {
922
+ function shouldRejectRegexLiteral(previousToken, lastClosedControlParenthesis, tokens) {
923
923
  if (previousToken === undefined) {
924
924
  return true;
925
925
  }
@@ -930,6 +930,8 @@ function shouldRejectRegexLiteral(previousToken, lastClosedControlParenthesis) {
930
930
  return false;
931
931
  }
932
932
  if (previousToken.type === "keyword") {
933
+ if (previousToken.value === "of")
934
+ return endsWithForOfSeparator(tokens);
933
935
  return !EXPRESSION_ENDING_KEYWORDS.has(previousToken.value);
934
936
  }
935
937
  if (previousToken.value === ")") {
@@ -937,6 +939,46 @@ function shouldRejectRegexLiteral(previousToken, lastClosedControlParenthesis) {
937
939
  }
938
940
  return !isExpressionEndingPunctuator(previousToken.value);
939
941
  }
942
+ function endsWithForOfSeparator(tokens) {
943
+ let depth = 0;
944
+ let start = -1;
945
+ for (let index = tokens.length - 2; index >= 0; index--) {
946
+ const token = tokens[index];
947
+ if (token.type !== "punctuator")
948
+ continue;
949
+ if ([")", "]", "}"].includes(token.value))
950
+ depth++;
951
+ else if (["(", "[", "{"].includes(token.value)) {
952
+ if (depth > 0) {
953
+ depth--;
954
+ continue;
955
+ }
956
+ if (token.value === "(" && (tokens[index - 1]?.value === "for" ||
957
+ (tokens[index - 1]?.value === "await" && tokens[index - 2]?.value === "for")))
958
+ start = index + 1;
959
+ break;
960
+ }
961
+ else if (depth === 0 && token.value === ";")
962
+ break;
963
+ }
964
+ if (start < 0)
965
+ return false;
966
+ depth = 0;
967
+ for (let index = start; index < tokens.length; index++) {
968
+ const token = tokens[index];
969
+ if (token.type === "punctuator") {
970
+ if (["(", "[", "{"].includes(token.value))
971
+ depth++;
972
+ else if ([")", "]", "}"].includes(token.value))
973
+ depth--;
974
+ }
975
+ if (depth === 0 && token.type === "keyword" && token.value === "of" &&
976
+ index !== start && tokens[index - 1]?.value !== "." && tokens[index - 1]?.value !== "?." &&
977
+ !(index === start + 1 && ["const", "let", "var"].includes(tokens[start].value)))
978
+ return index === tokens.length - 1;
979
+ }
980
+ return false;
981
+ }
940
982
  function updateGroupingState(groupingStack, previousToken, punctuator) {
941
983
  if (punctuator === "(") {
942
984
  groupingStack.push({
@@ -1,3 +1,6 @@
1
+ import type { GuestObjectState } from "./guest-heap.js";
2
+ export declare function captureFloat32State<T>(value: Float32Array, encode: (value: unknown) => T): GuestObjectState<T>;
3
+ export declare function restoreFloat32Properties<T>(value: Float32Array, state: GuestObjectState<T>, decode: (value: T) => unknown): void;
1
4
  export type Float32Data<TReference> = {
2
5
  kind: "float32array";
3
6
  byteOffset: number;
@@ -1,4 +1,23 @@
1
- import { float32Storage, isFloat32Array } from "../interp/float32.js";
1
+ import { float32Properties, float32Storage, isFloat32Array, isFloat32Index } from "../interp/float32.js";
2
+ import { getSandboxPrototype, hasExplicitSandboxPrototype } from "../interp/object-model.js";
3
+ import { restorePropertyDescriptors, serializePropertyDescriptors } from "./property-descriptors.js";
4
+ export function captureFloat32State(value, encode) {
5
+ const metadata = Object.defineProperties(Object.create(null), Object.fromEntries(float32Properties(value)));
6
+ if (!Object.isExtensible(value))
7
+ Object.preventExtensions(metadata);
8
+ return { properties: serializePropertyDescriptors(metadata, encode),
9
+ ...(hasExplicitSandboxPrototype(value) ? { prototype: encode(getSandboxPrototype(value)) } : {}) };
10
+ }
11
+ export function restoreFloat32Properties(value, state, decode) {
12
+ const metadata = Object.create(null);
13
+ restorePropertyDescriptors(metadata, state.properties, decode);
14
+ const descriptors = Object.getOwnPropertyDescriptors(metadata);
15
+ if (Object.keys(descriptors).some(isFloat32Index))
16
+ throw new TypeError("Float32Array metadata cannot replace numeric storage.");
17
+ Object.defineProperties(value, descriptors);
18
+ if (!Object.isExtensible(metadata))
19
+ Object.preventExtensions(value);
20
+ }
2
21
  export function encodeFloat32Storage(value, id, buffers, reference) {
3
22
  const storage = float32Storage(value);
4
23
  const existing = buffers.get(storage.buffer);
@@ -1,4 +1,5 @@
1
1
  import { Budget } from "../interp/budget.js";
2
+ import { sandboxErrorNames } from "../error/shape.js";
2
3
  import { createRawJson } from "../interp/raw-json.js";
3
4
  import { createBuiltinBindings } from "../interp/globals.js";
4
5
  import { getIntrinsicIdentity, listIntrinsicIdentities, resolveIntrinsicIdentity } from "../interp/intrinsics.js";
@@ -146,7 +147,9 @@ export function validateGuestHeapNode(raw, heap, maxArrayLength = 0xffffffff) {
146
147
  state(node.state);
147
148
  }
148
149
  else if (node.kind === "guest-object" || node.kind === "guest-array") {
149
- fields(node, ["kind", "state"], node.kind === "guest-array" ? ["templateNodeId", "templateOwner"] : []);
150
+ fields(node, ["kind", "state"], node.kind === "guest-array" ? ["templateNodeId", "templateOwner"] : ["errorType"]);
151
+ if (Object.hasOwn(node, "errorType") && !sandboxErrorNames.includes(node.errorType))
152
+ throw new TypeError("Invalid guest error type.");
150
153
  if (Object.hasOwn(node, "templateOwner"))
151
154
  reference(node.templateOwner, ["guest-array"]);
152
155
  if (Object.hasOwn(node, "templateNodeId") && integer(node.templateNodeId) < 1)
@@ -1,3 +1,4 @@
1
+ import { type SandboxErrorName } from "../error/shape.js";
1
2
  import { type ScopeFrame } from "../interp/scope.js";
2
3
  import { type PropertyDescriptorData } from "./property-descriptors.js";
3
4
  import type { CompletionResult } from "../interp/exceptions.js";
@@ -73,6 +74,7 @@ export type GuestHeapNode<T> = {
73
74
  } | {
74
75
  kind: "guest-object";
75
76
  state: GuestObjectState<T>;
77
+ errorType?: SandboxErrorName;
76
78
  } | {
77
79
  kind: "guest-array";
78
80
  state: GuestObjectState<T>;
@@ -1,5 +1,6 @@
1
1
  import { getClosureOrigin, getGeneratorOrigin } from "../interp/closure-origin.js";
2
2
  import { boundFunctionStates } from "../interp/bound-function-state.js";
3
+ import { sandboxErrorTypes } from "../error/shape.js";
3
4
  import { getGeneratorProperties } from "../interp/generator-properties.js";
4
5
  import { getIntrinsicIdentity } from "../interp/intrinsics.js";
5
6
  import { getSandboxPrototype, hasExplicitSandboxPrototype, hasGuestObjectState, isGuestClosure, materializeFunctionProperties } from "../interp/object-model.js";
@@ -151,8 +152,11 @@ export function captureGuestHeapNode(value, encode) {
151
152
  isSandboxRegExpIterator(value))
152
153
  return undefined;
153
154
  const prototype = Object.getPrototypeOf(value);
154
- if ((prototype === null || prototype === Object.prototype) && hasGuestObjectState(value))
155
- return { kind: "guest-object", state: captureObjectState(value, encode) };
155
+ if ((prototype === null || prototype === Object.prototype) && hasGuestObjectState(value)) {
156
+ const errorType = sandboxErrorTypes.get(value);
157
+ return { kind: "guest-object", state: captureObjectState(value, encode),
158
+ ...(errorType === undefined ? {} : { errorType }) };
159
+ }
156
160
  return undefined;
157
161
  }
158
162
  if (origin.node.nodeId === undefined)
@@ -17,12 +17,12 @@ import { awaitSandboxValue } from "../interp/cancel.js";
17
17
  import { runAsyncPrefix } from "../interp/jobs.js";
18
18
  import { toPropertyKey } from "../interp/property-key.js";
19
19
  import { CompileScope } from "../interp/regex/compile-guard.js";
20
- import { decodeFloat32Storage } from "./float32array.js";
20
+ import { decodeFloat32Storage, restoreFloat32Properties } from "./float32array.js";
21
21
  import { restoreDateTime } from "../interp/date.js";
22
22
  import { createRawJson } from "../interp/raw-json.js";
23
23
  import { createSandboxBox } from "../interp/boxed.js";
24
24
  import { restoreBoxedProperties } from "./boxed.js";
25
- import { sandboxErrorTypes } from "../error/shape.js";
25
+ import { sandboxErrorNames, sandboxErrorTypes } from "../error/shape.js";
26
26
  import { SnapshotMismatchError } from "../restore.js";
27
27
  import { evaluateNode, Scope, setSandboxProperty } from "../interp/interpreter.js";
28
28
  import { getGuestFunctionProperties, getGuestFunctionProperty, getSandboxDataProperty, isGuestClosure, materializeFunctionProperties, registerGuestClosure, setSandboxPrototype } from "../interp/object-model.js";
@@ -398,7 +398,24 @@ function initializeIntrinsicRealm(state) {
398
398
  const method = state.heap[String(value.id)];
399
399
  return method?.kind === "intrinsic" && method.id === '["%FunctionPrototype%",{"symbol":"hasInstance"}]';
400
400
  });
401
- createBuiltinBindings({ budget: state.budget, compileOwner: state.compilation.owner, functionHasInstance });
401
+ const errorConstructors = Object.values(state.heap).filter(node => node.kind === "intrinsic" && sandboxErrorNames.some(name => node.id === JSON.stringify([name])));
402
+ const errorPrototypes = errorConstructors.every(node => node.kind === "intrinsic" &&
403
+ node.state?.properties.properties.some(([key, descriptor]) => {
404
+ if (key !== "prototype" || descriptor.kind !== "data" || descriptor.writable ||
405
+ descriptor.enumerable || descriptor.configurable)
406
+ return false;
407
+ const value = descriptor.value;
408
+ if (value === null || typeof value !== "object" || !("kind" in value) || value.kind !== "ref")
409
+ return false;
410
+ const prototype = state.heap[String(value.id)];
411
+ return prototype?.kind === "intrinsic" &&
412
+ prototype.id === JSON.stringify([...JSON.parse(node.id), "prototype"]);
413
+ }));
414
+ const float32 = Object.values(state.heap).find(node => node.kind === "intrinsic" && node.id === '["Float32Array"]');
415
+ const float32Prototypes = float32?.kind !== "intrinsic" ||
416
+ float32.state?.properties.properties.some(([key, descriptor]) => key === "prototype" && descriptor.kind === "data" &&
417
+ !descriptor.writable && !descriptor.enumerable && !descriptor.configurable) === true;
418
+ createBuiltinBindings({ budget: state.budget, compileOwner: state.compilation.owner, functionHasInstance, errorPrototypes, float32Prototypes });
402
419
  state.intrinsicsInitialized = true;
403
420
  }
404
421
  function restoreHeapValue(id, state) {
@@ -440,8 +457,19 @@ function restoreHeapValue(id, state) {
440
457
  return value;
441
458
  }
442
459
  if (serialized.kind === "float32array") {
460
+ if (serialized.state !== undefined)
461
+ initializeIntrinsicRealm(state);
443
462
  const value = decodeFloat32Storage(serialized, (reference) => deserializeValue(reference, state));
444
463
  state.heapValueById.set(id, value);
464
+ if (serialized.state !== undefined) {
465
+ const objectState = serialized.state;
466
+ state.initializeIterators.push(() => {
467
+ if (objectState.prototype !== undefined)
468
+ setSandboxPrototype(value, deserializeValue(objectState.prototype, state), state.budget);
469
+ restoreFloat32Properties(value, objectState, entry => deserializeValue(entry, state));
470
+ });
471
+ return value;
472
+ }
445
473
  for (const [key, entry] of Object.entries(serialized.entries)) {
446
474
  Object.defineProperty(value, key, {
447
475
  value: deserializeValue(entry, state),
@@ -653,6 +681,8 @@ function restoreHeapValue(id, state) {
653
681
  throw new TypeError(`Missing restored function properties for ${serialized.kind === "intrinsic" ? serialized.id : serialized.kind}.`);
654
682
  if (objectState.prototype !== undefined)
655
683
  setSandboxPrototype(value, deserializeValue(objectState.prototype, state), state.budget);
684
+ if (serialized.kind === "guest-object" && serialized.errorType !== undefined)
685
+ sandboxErrorTypes.set(value, serialized.errorType);
656
686
  restorePropertyDescriptors(target, objectState.properties, entry => deserializeValue(entry, state));
657
687
  if (serialized.kind === "guest-array" && serialized.templateNodeId !== undefined) {
658
688
  const node = state.nodeById.get(serialized.templateNodeId);
@@ -4,7 +4,7 @@ import { type SerializedSymbol, type SerializedSymbolProperty } from "./symbols.
4
4
  import { type CollectionIterationMethod, type SandboxCollectionIterator } from "../interp/collection-iterator.js";
5
5
  import { type SandboxRegExpIterator } from "../interp/regexp-iterator.js";
6
6
  import { type SandboxErrorName } from "../error/shape.js";
7
- import { type GuestHeapNode } from "./guest-heap.js";
7
+ import { type GuestHeapNode, type GuestObjectState } from "./guest-heap.js";
8
8
  import { type SerializedArguments } from "./arguments.js";
9
9
  import { type SerializedArray } from "./arrays.js";
10
10
  import { type Float32Data } from "./float32array.js";
@@ -75,6 +75,7 @@ export type SerializedHeapValue = GuestHeapNode<SerializedSnapshotValue> | {
75
75
  entries: Record<string, SerializedSnapshotValue>;
76
76
  } | SerializedDate<SerializedSnapshotValue> | (Float32Data<SerializedReferenceValue> & {
77
77
  entries: Record<string, SerializedSnapshotValue>;
78
+ state?: GuestObjectState<SerializedSnapshotValue>;
78
79
  }) | SerializedArguments<SerializedSnapshotValue> | SerializedArray<SerializedSnapshotValue> | {
79
80
  kind: "object";
80
81
  sandboxNullPrototype?: true;
@@ -11,8 +11,8 @@ import { captureGuestHeapNode } from "./guest-heap.js";
11
11
  import { getGeneratorOrigin } from "../interp/closure-origin.js";
12
12
  import { serializeArguments } from "./arguments.js";
13
13
  import { requiresArrayEntries, serializeArray } from "./arrays.js";
14
- import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
15
- import { encodeFloat32Storage } from "./float32array.js";
14
+ import { isFloat32Array } from "../interp/float32.js";
15
+ import { captureFloat32State, encodeFloat32Storage } from "./float32array.js";
16
16
  import { dateDataProperties, isSandboxDate } from "../interp/date.js";
17
17
  import { serializeDate } from "./date-properties.js";
18
18
  import { boxedDataProperties, isSandboxBox } from "../interp/boxed.js";
@@ -104,7 +104,7 @@ function serializeValue(value, path, state) {
104
104
  }
105
105
  return { kind: "ref", id };
106
106
  }
107
- if (typeof value === "object" && value !== null && hasGuestObjectState(value) && !isSandboxMap(value) && !isSandboxSet(value)) {
107
+ if (typeof value === "object" && value !== null && hasGuestObjectState(value) && !isSandboxMap(value) && !isSandboxSet(value) && !isFloat32Array(value)) {
108
108
  throw new TypeError("Guest function properties and prototype links cannot be serialized.");
109
109
  }
110
110
  if (value === null || typeof value === "string" || typeof value === "boolean") {
@@ -265,10 +265,8 @@ function serializeHeapReference(value, path, state) {
265
265
  kind: "ref",
266
266
  id
267
267
  }));
268
- const entries = Object.create(null);
269
- state.heap[String(id)] = { ...storage, entries };
270
- for (const [key, descriptor] of float32DataProperties(value))
271
- entries[key] = serializeValue(descriptor.value, `${path}.${key}`, state);
268
+ state.heap[String(id)] = { ...storage, entries: {},
269
+ state: captureFloat32State(value, entry => serializeValue(entry, `${path}.<typed-array>`, state)) };
272
270
  }
273
271
  else if (isSandboxArguments(value)) {
274
272
  state.heap[String(id)] = serializeArguments(value, (entry, key) => serializeValue(entry, `${path}.${key}`, state));
@@ -431,9 +429,12 @@ function collectContainerStats(value, stats, ancestors, guestValues, depth = 0)
431
429
  stat.expanded = true;
432
430
  ancestors.add(value);
433
431
  const guestEntries = [];
434
- const guest = captureGuestHeapNode(value, entry => { guestEntries.push(entry); return null; });
432
+ const guest = isFloat32Array(value)
433
+ ? { kind: "float32array", state: captureFloat32State(value, entry => { guestEntries.push(entry); return null; }) }
434
+ : captureGuestHeapNode(value, entry => { guestEntries.push(entry); return null; });
435
435
  if (guest !== undefined) {
436
- guestValues.add(value);
436
+ if (!isFloat32Array(value))
437
+ guestValues.add(value);
437
438
  for (const entry of guestEntries) {
438
439
  collectContainerStats(entry, stats, ancestors, guestValues, depth + 1);
439
440
  if (entry !== null && typeof entry === "object") {
@@ -120,6 +120,11 @@ function validateDumpHeap(root, state) {
120
120
  if (entry.kind === "float32array") {
121
121
  validateFloat32Storage(entry);
122
122
  requireRecord(entry.entries, `${path}.entries`);
123
+ if (Object.hasOwn(entry, "state")) {
124
+ if (Object.keys(entry.entries).length !== 0)
125
+ fail("invalidValue", path, "ambiguous Float32Array property state");
126
+ validateGuestHeapNode({ kind: "guest-object", state: entry.state }, heap, state.limits.maxEntries);
127
+ }
123
128
  continue;
124
129
  }
125
130
  if (entry.kind === "arguments") {
@@ -551,6 +556,11 @@ function validateHeapValue(value, path, state, heap) {
551
556
  if (record.kind === "float32array") {
552
557
  validateFloat32Storage(record);
553
558
  requireRecord(record.entries, `${path}.entries`);
559
+ if (Object.hasOwn(record, "state")) {
560
+ if (Object.keys(record.entries).length !== 0)
561
+ fail("invalidValue", path, "ambiguous Float32Array property state");
562
+ validateGuestHeapNode({ kind: "guest-object", state: record.state }, heap, state.limits.maxEntries);
563
+ }
554
564
  }
555
565
  if (record.kind === "map") {
556
566
  const entries = requireArray(record.entries, `${path}.entries`, state);
@@ -706,7 +716,7 @@ function validateArrayHeap(record, path, state) {
706
716
  function validateErrorType(record, path) {
707
717
  if (!Object.hasOwn(record, "errorType"))
708
718
  return;
709
- if (record.kind !== "object" ||
719
+ if ((record.kind !== "object" && record.kind !== "guest-object") ||
710
720
  !sandboxErrorNames.includes(record.errorType)) {
711
721
  fail("invalidValue", `${path}.errorType`, "invalid error metadata");
712
722
  }