poe-code 14.0.55 → 14.0.57

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 (26) hide show
  1. package/dist/index.js +150 -53
  2. package/dist/index.js.map +3 -3
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-G5LQ37D4.js → chunk-H4O5OPM2.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-6LFWVA64.js → chunk-N3CN2ACZ.js} +145 -53
  7. package/packages/safe-js/dist/chunks/chunk-N3CN2ACZ.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/globals/misc.js +20 -7
  12. package/packages/safe-js/dist/interp/methods/regex.d.ts +4 -4
  13. package/packages/safe-js/dist/interp/methods/regex.js +47 -8
  14. package/packages/safe-js/dist/interp/methods/string.js +59 -26
  15. package/packages/safe-js/dist/interp/regex/engine.d.ts +1 -0
  16. package/packages/safe-js/dist/interp/regex/engine.js +3 -3
  17. package/packages/safe-js/dist/interp/values.d.ts +6 -3
  18. package/packages/safe-js/dist/interp/values.js +19 -6
  19. package/packages/safe-js/dist/snapshot/replay-data.d.ts +1 -1
  20. package/packages/safe-js/dist/snapshot/replay-data.js +4 -4
  21. package/packages/safe-js/dist/snapshot/restore.js +8 -0
  22. package/packages/safe-js/dist/snapshot/serialize.d.ts +6 -1
  23. package/packages/safe-js/dist/snapshot/serialize.js +14 -1
  24. package/packages/safe-js/dist/snapshot/validation.js +8 -2
  25. package/packages/safe-js/dist/chunks/chunk-6LFWVA64.js.map +0 -7
  26. /package/packages/safe-js/dist/chunks/{chunk-G5LQ37D4.js.map → chunk-H4O5OPM2.js.map} +0 -0
@@ -22,7 +22,7 @@ import {
22
22
  resolveFsConfig,
23
23
  splitFrontmatter,
24
24
  withBrokenPipeGuard
25
- } from "./chunks/chunk-G5LQ37D4.js";
25
+ } from "./chunks/chunk-H4O5OPM2.js";
26
26
  import {
27
27
  Budget,
28
28
  SandboxError,
@@ -34,7 +34,7 @@ import {
34
34
  replaceErrorStack,
35
35
  restore,
36
36
  run
37
- } from "./chunks/chunk-6LFWVA64.js";
37
+ } from "./chunks/chunk-N3CN2ACZ.js";
38
38
  import "./chunks/chunk-UXMKEUH3.js";
39
39
  import "./chunks/chunk-5DYEOCT5.js";
40
40
  import "./chunks/chunk-4YIDX44G.js";
@@ -5,7 +5,7 @@ import {
5
5
  defineExtension,
6
6
  lint,
7
7
  run
8
- } from "./chunks/chunk-6LFWVA64.js";
8
+ } from "./chunks/chunk-N3CN2ACZ.js";
9
9
  import "./chunks/chunk-4YIDX44G.js";
10
10
  export {
11
11
  Budget,
@@ -26,7 +26,7 @@ import {
26
26
  runWithSpawnUsageAccumulator,
27
27
  splitFrontmatter,
28
28
  supportsSpawnMode
29
- } from "./chunks/chunk-G5LQ37D4.js";
29
+ } from "./chunks/chunk-H4O5OPM2.js";
30
30
  import {
31
31
  Budget,
32
32
  FileSnapshotBackend,
@@ -50,7 +50,7 @@ import {
50
50
  registerPendingHostCallPolicy,
51
51
  restore,
52
52
  run
53
- } from "./chunks/chunk-6LFWVA64.js";
53
+ } from "./chunks/chunk-N3CN2ACZ.js";
54
54
  import "./chunks/chunk-UXMKEUH3.js";
55
55
  import "./chunks/chunk-5DYEOCT5.js";
56
56
  import "./chunks/chunk-4YIDX44G.js";
@@ -1,11 +1,12 @@
1
1
  import { isSandboxCollectionIterator } from "../collection-iterator.js";
2
2
  import { assertSandboxGraphDepth } from "../../graph-depth.js";
3
- import { allocateProducedSandboxValue, cloneSandboxValue, createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet } from "../values.js";
3
+ import { CompileScope } from "../regex/compile-guard.js";
4
+ import { allocateProducedSandboxValue, cloneSandboxValue, createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet, reconcileCompiledValues } from "../values.js";
4
5
  export function createMiscGlobals(options) {
5
6
  return {
6
7
  structuredClone: createSandboxClosure({
7
8
  sandbox: true,
8
- call: ([value]) => structuredCloneSandboxValue(value, options.budget),
9
+ call: ([value], context) => structuredCloneSandboxValue(value, options.budget, context?.compilation),
9
10
  name: "structuredClone"
10
11
  }),
11
12
  parseInt: createSandboxClosure({
@@ -30,11 +31,23 @@ export function createMiscGlobals(options) {
30
31
  })
31
32
  };
32
33
  }
33
- function structuredCloneSandboxValue(value, budget) {
34
- assertSandboxGraphDepth(value);
35
- const clone = cloneSandboxValue(value);
36
- assertStructuredCloneable(clone, new WeakSet());
37
- return allocateProducedSandboxValue(clone, budget);
34
+ function structuredCloneSandboxValue(value, budget, parent) {
35
+ const operation = budget.acquireCompileOwner(false, parent?.owner);
36
+ const compilation = parent?.owner === operation.owner ? parent : new CompileScope(operation.owner);
37
+ try {
38
+ const clone = cloneSandboxValue(value, { compilation, resetRegexLastIndex: true });
39
+ assertSandboxGraphDepth(clone);
40
+ assertStructuredCloneable(clone, new WeakSet());
41
+ allocateProducedSandboxValue(clone, budget);
42
+ if (compilation !== parent)
43
+ reconcileCompiledValues(budget, [clone], compilation);
44
+ return clone;
45
+ }
46
+ finally {
47
+ if (compilation !== parent)
48
+ compilation.dispose();
49
+ operation.release();
50
+ }
38
51
  }
39
52
  function assertStructuredCloneable(value, seen) {
40
53
  if (isSandboxClosure(value) || isSandboxPromise(value) || isSandboxCollectionIterator(value)) {
@@ -1,10 +1,10 @@
1
- import { type SandboxRegex, type SandboxValue } from "../values.js";
1
+ import { type SandboxCallContext, type SandboxRegex, type SandboxValue } from "../values.js";
2
2
  import { type RegexMatch } from "../regex/engine.js";
3
3
  import type { Budget } from "../budget.js";
4
4
  export type RegexMethodName = "exec" | "test";
5
5
  export declare function isRegexMethodName(property: string | number): property is RegexMethodName;
6
- export declare function getRegexMember(target: SandboxRegex, property: string | number, budget?: Budget): SandboxValue | undefined;
6
+ export declare function getRegexMember(target: SandboxRegex, property: string | number, budget: Budget): SandboxValue | undefined;
7
7
  export declare function setRegexMember(target: SandboxRegex, property: string | number, value: SandboxValue): void;
8
- export declare function callRegexMethod(target: SandboxRegex, methodName: RegexMethodName, args: readonly SandboxValue[]): SandboxValue;
9
- export declare function executeRegex(target: SandboxRegex, input: string): RegexMatch | null;
8
+ export declare function callRegexMethod(target: SandboxValue, methodName: RegexMethodName, args: readonly SandboxValue[], budget: Budget, context?: SandboxCallContext): Promise<SandboxValue>;
9
+ export declare function executeRegex(target: SandboxRegex, input: string, lastIndex: number): RegexMatch | null;
10
10
  export declare function toMatchArray(match: RegexMatch | null, input: string): SandboxValue;
@@ -1,6 +1,13 @@
1
- import { createSandboxClosure, getSandboxRegexPattern } from "../values.js";
1
+ import { createSandboxClosure, getSandboxRegexPattern, isSandboxClosure, isSandboxRegex } from "../values.js";
2
2
  import { matchRegex } from "../regex/engine.js";
3
+ import { invokeBuiltinClosure } from "../builtin-call.js";
4
+ import { getSandboxDataProperty } from "../object-model.js";
5
+ import { sandboxNumber, sandboxString } from "../string-coercion.js";
3
6
  const regexMethodNames = new Set(["exec", "test"]);
7
+ const regexFlagProperties = {
8
+ hasIndices: "d", global: "g", ignoreCase: "i", multiline: "m",
9
+ dotAll: "s", unicode: "u", unicodeSets: "v", sticky: "y"
10
+ };
4
11
  export function isRegexMethodName(property) {
5
12
  return typeof property === "string" && regexMethodNames.has(property);
6
13
  }
@@ -13,13 +20,15 @@ export function getRegexMember(target, property, budget) {
13
20
  }
14
21
  if (property === "lastIndex")
15
22
  return target.lastIndex;
23
+ if (Object.hasOwn(regexFlagProperties, property))
24
+ return target.flags.includes(regexFlagProperties[property]);
16
25
  if (!isRegexMethodName(property)) {
17
26
  return undefined;
18
27
  }
19
28
  return createSandboxClosure({
20
29
  sandbox: true,
21
30
  name: `RegExp#${property}`,
22
- call: (args) => callRegexMethod(target, property, args)
31
+ call: (args, context) => callRegexMethod(context?.thisValue, property, args, budget, context)
23
32
  });
24
33
  }
25
34
  function escapeRegexSource(source, budget) {
@@ -61,15 +70,45 @@ export function setRegexMember(target, property, value) {
61
70
  if (property !== "lastIndex") {
62
71
  throw new TypeError(`RegExp#${String(property)} is not writable.`);
63
72
  }
64
- target.lastIndex = Number(value);
73
+ target.lastIndex = value;
65
74
  }
66
- export function callRegexMethod(target, methodName, args) {
67
- const match = executeRegex(target, String(args[0]));
68
- return methodName === "test" ? match !== null : toMatchArray(match, String(args[0]));
75
+ export async function callRegexMethod(target, methodName, args, budget, context) {
76
+ if (target === null || typeof target !== "object" || (methodName === "exec" && !isSandboxRegex(target))) {
77
+ throw new TypeError(`RegExp#${methodName} requires ${methodName === "exec" ? "a regex" : "an object"} receiver.`);
78
+ }
79
+ const retained = {};
80
+ let cursor;
81
+ let convertedInput;
82
+ budget.setRetainedValues(retained, () => [target, ...args, cursor, convertedInput]);
83
+ try {
84
+ const input = await sandboxString(args[0], budget, context);
85
+ if (methodName === "test") {
86
+ const exec = context?.getProperty === undefined
87
+ ? getSandboxDataProperty(target, "exec", budget) : context.getProperty(target, "exec");
88
+ if (isSandboxClosure(exec)) {
89
+ const result = await invokeBuiltinClosure(exec, [input], budget, context, target);
90
+ if (result !== null && typeof result !== "object") {
91
+ throw new TypeError("RegExp#test exec must return an object or null.");
92
+ }
93
+ return result !== null;
94
+ }
95
+ }
96
+ if (!isSandboxRegex(target))
97
+ throw new TypeError("RegExp execution requires a regex receiver.");
98
+ if (typeof args[0] !== "string")
99
+ convertedInput = input;
100
+ cursor = target.lastIndex;
101
+ const lastIndex = await sandboxNumber(cursor, budget, context);
102
+ const match = executeRegex(target, input, lastIndex);
103
+ return methodName === "test" ? match !== null : toMatchArray(match, input);
104
+ }
105
+ finally {
106
+ budget.setRetainedValues(retained, undefined);
107
+ }
69
108
  }
70
- export function executeRegex(target, input) {
109
+ export function executeRegex(target, input, lastIndex) {
71
110
  const pattern = getSandboxRegexPattern(target);
72
- const match = matchRegex(pattern, input, target.lastIndex);
111
+ const match = matchRegex(pattern, input, lastIndex);
73
112
  if (pattern.flags.global) {
74
113
  target.lastIndex = match === null ? 0 : match.index + match.text.length;
75
114
  }
@@ -1,6 +1,7 @@
1
1
  import { invokeBuiltinClosure } from "../builtin-call.js";
2
2
  import { CompileScope } from "../regex/compile-guard.js";
3
- import { sandboxString } from "../string-coercion.js";
3
+ import { normalizeLastIndex } from "../regex/engine.js";
4
+ import { sandboxNumber, sandboxString } from "../string-coercion.js";
4
5
  import { createSandboxClosure, createSandboxRegex, deepCopyFromSandbox, isSandboxClosure, isSandboxRegex } from "../values.js";
5
6
  import { executeRegex, toMatchArray } from "./regex.js";
6
7
  const SPLIT_STRING_MESSAGE = "String#split only supports string separator values.";
@@ -120,7 +121,13 @@ export function callStringMethod(value, methodName, args, budget, callClosure =
120
121
  return budget.allocateString(result + value.slice(spanStart));
121
122
  }
122
123
  if (methodName === "replace" || methodName === "replaceAll") {
123
- return callReplaceLikeMethod(value, methodName, args, budget, callClosure);
124
+ return callReplaceLikeMethod(value, methodName, args, budget, callClosure, context);
125
+ }
126
+ const regex = args[0];
127
+ if (isSandboxRegex(regex) && regex.lastIndex !== null && typeof regex.lastIndex === "object" &&
128
+ ((methodName === "match" && !regex.flags.includes("g")) ||
129
+ (methodName === "matchAll" && regex.flags.includes("g")))) {
130
+ return callStringRegexCursor(value, methodName, regex, budget, parent, context);
124
131
  }
125
132
  if ((methodName === "match" || methodName === "matchAll" || methodName === "search") && !isSandboxRegex(args[0])) {
126
133
  return callStringPattern(value, methodName, args[0], budget, parent, context);
@@ -232,7 +239,7 @@ export function callStringMethod(value, methodName, args, budget, callClosure =
232
239
  operation.release();
233
240
  }
234
241
  }
235
- function callReplaceLikeMethod(value, methodName, args, budget, callClosure) {
242
+ function callReplaceLikeMethod(value, methodName, args, budget, callClosure, context) {
236
243
  const search = args[0];
237
244
  const replacement = args[1];
238
245
  if ((!isSandboxRegex(search) && typeof search !== "string") ||
@@ -243,7 +250,7 @@ function callReplaceLikeMethod(value, methodName, args, budget, callClosure) {
243
250
  if (methodName === "replaceAll" && !search.flags.includes("g")) {
244
251
  throw new TypeError("String#replaceAll requires a global regex.");
245
252
  }
246
- return replaceRegex(value, search, replacement, methodName === "replaceAll", budget, callClosure);
253
+ return replaceRegex(value, search, replacement, methodName === "replaceAll", budget, callClosure, context);
247
254
  }
248
255
  if (typeof replacement === "string") {
249
256
  return budget.allocateString(methodName === "replace"
@@ -252,22 +259,31 @@ function callReplaceLikeMethod(value, methodName, args, budget, callClosure) {
252
259
  }
253
260
  return replaceWithClosure(value, search, replacement, methodName === "replaceAll", budget, callClosure);
254
261
  }
255
- async function replaceRegex(value, regex, replacement, replaceAll, budget, callClosure) {
262
+ async function replaceRegex(value, regex, replacement, replaceAll, budget, callClosure, context) {
256
263
  if (regex.flags.includes("g"))
257
264
  regex.lastIndex = 0;
258
- const matches = collectRegexMatches(regex, value, replaceAll || regex.flags.includes("g"));
259
- let result = "";
260
- let copiedThrough = 0;
261
- for (const match of matches) {
262
- result += value.slice(copiedThrough, match.index);
263
- result +=
264
- typeof replacement === "string"
265
- ? expandReplacement(replacement, match.text, match.captures, value, match.index)
266
- : String(await callClosure(replacement, [match.text, ...match.captures, match.index, value]));
267
- copiedThrough = match.index + match.text.length;
265
+ const cursor = regex.lastIndex;
266
+ const retained = {};
267
+ budget.setRetainedValues(retained, () => [value, regex, cursor, replacement]);
268
+ try {
269
+ const lastIndex = await sandboxNumber(cursor, budget, context);
270
+ const matches = collectRegexMatches(regex, value, replaceAll || regex.flags.includes("g"), undefined, lastIndex);
271
+ let result = "";
272
+ let copiedThrough = 0;
273
+ for (const match of matches) {
274
+ result += value.slice(copiedThrough, match.index);
275
+ result +=
276
+ typeof replacement === "string"
277
+ ? expandReplacement(replacement, match.text, match.captures, value, match.index)
278
+ : String(await callClosure(replacement, [match.text, ...match.captures, match.index, value]));
279
+ copiedThrough = match.index + match.text.length;
280
+ }
281
+ result += value.slice(copiedThrough);
282
+ return budget.allocateString(result);
283
+ }
284
+ finally {
285
+ budget.setRetainedValues(retained, undefined);
268
286
  }
269
- result += value.slice(copiedThrough);
270
- return budget.allocateString(result);
271
287
  }
272
288
  function expandReplacement(replacement, match, captures, input, matchIndex) {
273
289
  let result = "";
@@ -386,7 +402,7 @@ async function callStringPattern(value, methodName, pattern, budget, parent, con
386
402
  const operation = budget.acquireCompileOwner(false, parent?.owner);
387
403
  const compilation = new CompileScope(operation.owner);
388
404
  const retainedPattern = {};
389
- budget.setRetainedValues(retainedPattern, () => [pattern]);
405
+ budget.setRetainedValues(retainedPattern, () => [value, pattern]);
390
406
  try {
391
407
  const source = pattern === undefined ? "" : await sandboxString(pattern, budget, context);
392
408
  const regex = createSandboxRegex(source, methodName === "matchAll" ? "g" : "", 0, compilation);
@@ -398,7 +414,23 @@ async function callStringPattern(value, methodName, pattern, budget, parent, con
398
414
  operation.release();
399
415
  }
400
416
  }
401
- function callMatchLikeMethod(value, methodName, args, compilation) {
417
+ async function callStringRegexCursor(value, methodName, regex, budget, parent, context) {
418
+ const operation = budget.acquireCompileOwner(false, parent?.owner);
419
+ const compilation = new CompileScope(operation.owner);
420
+ const cursor = regex.lastIndex;
421
+ const retained = {};
422
+ budget.setRetainedValues(retained, () => [value, regex, cursor]);
423
+ try {
424
+ const lastIndex = await sandboxNumber(cursor, budget, context);
425
+ return callMatchLikeMethod(value, methodName, [regex], compilation, lastIndex);
426
+ }
427
+ finally {
428
+ budget.setRetainedValues(retained, undefined);
429
+ compilation.dispose();
430
+ operation.release();
431
+ }
432
+ }
433
+ function callMatchLikeMethod(value, methodName, args, compilation, lastIndex) {
402
434
  const regex = args[0];
403
435
  if (!isSandboxRegex(regex))
404
436
  throw new TypeError(`String#${methodName} requires a regex argument.`);
@@ -406,7 +438,7 @@ function callMatchLikeMethod(value, methodName, args, compilation) {
406
438
  const lastIndex = regex.lastIndex;
407
439
  if (!Object.is(lastIndex, 0))
408
440
  regex.lastIndex = 0;
409
- const match = executeRegex(regex, value);
441
+ const match = executeRegex(regex, value, 0);
410
442
  if (!Object.is(regex.lastIndex, lastIndex))
411
443
  regex.lastIndex = lastIndex;
412
444
  return match?.index ?? -1;
@@ -414,29 +446,30 @@ function callMatchLikeMethod(value, methodName, args, compilation) {
414
446
  if (methodName === "matchAll" && !regex.flags.includes("g"))
415
447
  throw new TypeError("String#matchAll requires a global regex.");
416
448
  if (methodName === "match" && !regex.flags.includes("g"))
417
- return toMatchArray(executeRegex(regex, value), value);
449
+ return toMatchArray(executeRegex(regex, value, lastIndex ?? Number(regex.lastIndex)), value);
418
450
  if (methodName === "match")
419
451
  regex.lastIndex = 0;
420
452
  const matcher = methodName === "matchAll"
421
- ? createSandboxRegex(regex.source, regex.flags, regex.lastIndex, compilation)
453
+ ? createSandboxRegex(regex.source, regex.flags, normalizeLastIndex(lastIndex ?? Number(regex.lastIndex)), compilation)
422
454
  : regex;
423
- const matches = collectRegexMatches(matcher, value, true, compilation.owner?.budget);
455
+ const matches = collectRegexMatches(matcher, value, true, compilation.owner?.budget, Number(matcher.lastIndex));
424
456
  if (methodName === "match" && matches.length === 0)
425
457
  return null;
426
458
  return methodName === "match"
427
459
  ? matches.map((match) => match.text)
428
460
  : matches.map((match) => toMatchArray(match, value));
429
461
  }
430
- function collectRegexMatches(regex, value, all, budget) {
462
+ function collectRegexMatches(regex, value, all, budget, lastIndex = 0) {
431
463
  const matches = [];
432
464
  do {
433
- const match = executeRegex(regex, value);
465
+ const match = executeRegex(regex, value, lastIndex);
434
466
  if (match === null)
435
467
  break;
436
468
  budget?.allocateArrayLength(matches.length + 1);
437
469
  matches.push(match);
470
+ lastIndex = match.index + match.text.length;
438
471
  if (all && match.text.length === 0)
439
- regex.lastIndex += 1;
472
+ regex.lastIndex = ++lastIndex;
440
473
  } while (all);
441
474
  return matches;
442
475
  }
@@ -6,3 +6,4 @@ export type RegexMatch = {
6
6
  };
7
7
  export declare function matchRegex(pattern: RegexPattern, input: string, lastIndex?: number): RegexMatch | null;
8
8
  export declare function matchRegexFrom(pattern: RegexPattern, input: string, startIndex: number): RegexMatch | null;
9
+ export declare function normalizeLastIndex(lastIndex: number): number;
@@ -202,11 +202,11 @@ function charge(context) {
202
202
  context.steps += 1;
203
203
  allocateRegexSteps(context.steps);
204
204
  }
205
- function normalizeLastIndex(lastIndex) {
206
- if (!Number.isFinite(lastIndex) || lastIndex <= 0) {
205
+ export function normalizeLastIndex(lastIndex) {
206
+ if (Number.isNaN(lastIndex) || lastIndex <= 0) {
207
207
  return 0;
208
208
  }
209
- return Math.floor(lastIndex);
209
+ return Math.min(Math.floor(lastIndex), Number.MAX_SAFE_INTEGER);
210
210
  }
211
211
  function charactersEqual(left, right, ignoreCase) {
212
212
  return left !== undefined && foldCharacter(left, ignoreCase) === foldCharacter(right, ignoreCase);
@@ -32,7 +32,7 @@ export type SandboxRegex = {
32
32
  readonly kind: "regex";
33
33
  readonly source: string;
34
34
  readonly flags: string;
35
- lastIndex: number;
35
+ lastIndex: SandboxValue;
36
36
  readonly [sandboxRegexBrand]: true;
37
37
  readonly [sandboxRegexPattern]: RegexPattern;
38
38
  };
@@ -121,14 +121,17 @@ export declare function createSandboxGenerator(channel: GeneratorChannel, metada
121
121
  } | undefined): SandboxGenerator;
122
122
  export declare function createSandboxMap(entries?: Iterable<readonly [SandboxValue, SandboxValue]>): SandboxMap;
123
123
  export declare function createSandboxSet(values?: Iterable<SandboxValue>): SandboxSet;
124
- export declare function createSandboxRegex(source: string, flags?: string, lastIndex?: number, compilation?: CompileScope): SandboxRegex;
124
+ export declare function createSandboxRegex(source: string, flags?: string, lastIndex?: SandboxValue, compilation?: CompileScope): SandboxRegex;
125
125
  export declare function getSandboxRegexPattern(regex: SandboxRegex): RegexPattern;
126
126
  export declare function isSandboxClosure(value: unknown): value is SandboxClosure;
127
127
  export declare function isSandboxPromise(value: unknown): value is SandboxPromise;
128
128
  export declare function isSandboxGenerator(value: unknown): value is SandboxGenerator;
129
129
  export declare function isSandboxRegex(value: unknown): value is SandboxRegex;
130
130
  export declare function deepCopyToSandbox(value: unknown): SandboxValue;
131
- export declare function cloneSandboxValue(value: SandboxValue): SandboxValue;
131
+ export declare function cloneSandboxValue(value: SandboxValue, options?: {
132
+ compilation?: CompileScope;
133
+ resetRegexLastIndex?: boolean;
134
+ }): SandboxValue;
132
135
  export declare function allocateProducedSandboxValue(value: SandboxValue, budget: Budget): SandboxValue;
133
136
  export declare function measureSandboxData(values: Iterable<unknown>, options?: {
134
137
  ignoreClosures?: boolean;
@@ -210,11 +210,12 @@ export function deepCopyToSandbox(value) {
210
210
  seen: new WeakMap()
211
211
  });
212
212
  }
213
- export function cloneSandboxValue(value) {
213
+ export function cloneSandboxValue(value, options = {}) {
214
214
  const initializeIterators = [];
215
215
  const copy = copyToSandbox(value, {
216
216
  seen: new WeakMap(),
217
- initializeIterators
217
+ initializeIterators,
218
+ ...options
218
219
  }, "<root>", true);
219
220
  for (const initialize of initializeIterators)
220
221
  initialize();
@@ -324,7 +325,7 @@ export function measureSandboxData(values, options = {}) {
324
325
  if (isSandboxPromise(value))
325
326
  return;
326
327
  if (isSandboxRegex(value)) {
327
- const { source, flags } = captureRegexData(value);
328
+ const { source, flags, lastIndex } = captureRegexData(value);
328
329
  const compiled = regexCompiledData(getSandboxRegexPattern(value));
329
330
  const staged = compiled.ticket === undefined
330
331
  ? 0
@@ -332,6 +333,7 @@ export function measureSandboxData(values, options = {}) {
332
333
  usage += Math.max(6 + source.length + flags.length + compiled.units, staged - 1);
333
334
  if (compiled.ticket !== undefined)
334
335
  options.compileTickets?.add(compiled.ticket);
336
+ visit(lastIndex, depth + 1);
335
337
  return;
336
338
  }
337
339
  if (isSandboxArguments(value)) {
@@ -412,9 +414,20 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
412
414
  }
413
415
  if (isLiveCapability(value))
414
416
  throw new TypeError("Live capabilities require their owning realm bridge.");
417
+ if (isSandboxRegex(value)) {
418
+ if (!cloneSandboxCollections)
419
+ return value;
420
+ const existing = state.seen.get(value);
421
+ if (existing !== undefined)
422
+ return existing;
423
+ const copy = createSandboxRegex(value.source, value.flags, 0, state.compilation);
424
+ state.seen.set(value, copy);
425
+ if (!state.resetRegexLastIndex)
426
+ copy.lastIndex = copyToSandbox(value.lastIndex, state, `${path}.lastIndex`, true, depth + 1);
427
+ return copy;
428
+ }
415
429
  if (isSandboxClosure(value) ||
416
430
  isSandboxGenerator(value) ||
417
- isSandboxRegex(value) ||
418
431
  isSandboxPromise(value)) {
419
432
  return value;
420
433
  }
@@ -596,9 +609,9 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
596
609
  return existing;
597
610
  guard.allocate(1 + source.length + flags.length);
598
611
  const regex = new RegExp(source, flags);
599
- regex.lastIndex = lastIndex;
600
- guard.retainScratch();
601
612
  state.seen.set(value, regex);
613
+ Reflect.set(regex, "lastIndex", copyFromSandbox(lastIndex, state, `${path}.lastIndex`, options, depth + 1));
614
+ guard.retainScratch();
602
615
  return regex;
603
616
  }
604
617
  finally {
@@ -63,7 +63,7 @@ type DataNode = {
63
63
  kind: "regex";
64
64
  source: string;
65
65
  flags: string;
66
- lastIndex: number;
66
+ lastIndex: Atom;
67
67
  };
68
68
  export type ReplayData = {
69
69
  root: Atom;
@@ -114,7 +114,7 @@ export function encodeReplayData(value, options = {}) {
114
114
  kind: "regex",
115
115
  source: entry.source,
116
116
  flags: entry.flags,
117
- lastIndex: entry.lastIndex
117
+ lastIndex: child(entry.lastIndex, "lastIndex")
118
118
  };
119
119
  }
120
120
  else if (isSandboxArguments(entry)) {
@@ -303,12 +303,12 @@ export function decodeReplayData(input, options = {}, parent) {
303
303
  if (kind === "regex") {
304
304
  if (typeof node.source !== "string" ||
305
305
  typeof node.flags !== "string" ||
306
- typeof node.lastIndex !== "number" ||
307
- !Number.isFinite(node.lastIndex)) {
306
+ !Object.hasOwn(node, "lastIndex")) {
308
307
  throw new TypeError("Invalid replay regular expression.");
309
308
  }
310
- const result = createSandboxRegex(node.source, node.flags, node.lastIndex, compilation);
309
+ const result = createSandboxRegex(node.source, node.flags, 0, compilation);
311
310
  restored.set(id, result);
311
+ result.lastIndex = child(own(node, "lastIndex"));
312
312
  return result;
313
313
  }
314
314
  if (kind === "arguments") {
@@ -231,6 +231,8 @@ function deserializeValue(value, state) {
231
231
  }
232
232
  if (isSerializedNonFiniteNumberValue(value)) {
233
233
  switch (value.value) {
234
+ case "-0":
235
+ return -0;
234
236
  case "NaN":
235
237
  return Number.NaN;
236
238
  case "Infinity":
@@ -351,6 +353,12 @@ function restoreHeapValue(id, state) {
351
353
  state.heapValueById.set(id, value);
352
354
  return value;
353
355
  }
356
+ if (serialized.kind === "regex-object") {
357
+ const value = createSandboxRegex(serialized.source, serialized.flags, 0, state.compilation);
358
+ state.heapValueById.set(id, value);
359
+ value.lastIndex = deserializeValue(serialized.lastIndex, state);
360
+ return value;
361
+ }
354
362
  if (serialized.kind === "float32array") {
355
363
  const value = decodeFloat32Storage(serialized, (reference) => deserializeValue(reference, state));
356
364
  state.heapValueById.set(id, value);
@@ -10,7 +10,7 @@ type SerializedUndefinedValue = {
10
10
  };
11
11
  type SerializedNonFiniteNumber = {
12
12
  kind: "number";
13
- value: "-Infinity" | "Infinity" | "NaN";
13
+ value: "-Infinity" | "Infinity" | "NaN" | "-0";
14
14
  };
15
15
  export type SerializedClosureValue = {
16
16
  kind: "fn";
@@ -42,6 +42,11 @@ export type SerializedReferenceValue = {
42
42
  id: number;
43
43
  };
44
44
  export type SerializedHeapValue = {
45
+ kind: "regex-object";
46
+ source: string;
47
+ flags: string;
48
+ lastIndex: SerializedSnapshotValue;
49
+ } | {
45
50
  kind: "collection-iterator";
46
51
  collectionKind: "map" | "set";
47
52
  method: CollectionIterationMethod;
@@ -94,6 +94,8 @@ function serializeValue(value, path, state) {
94
94
  };
95
95
  }
96
96
  if (typeof value === "number") {
97
+ if (Object.is(value, -0))
98
+ return { kind: "number", value: "-0" };
97
99
  if (Number.isFinite(value)) {
98
100
  return value;
99
101
  }
@@ -161,6 +163,11 @@ function serializeValue(value, path, state) {
161
163
  };
162
164
  }
163
165
  if (isSandboxRegex(value)) {
166
+ const reference = serializeHeapReference(value, path, state);
167
+ if (reference !== undefined)
168
+ return reference;
169
+ if (typeof value.lastIndex !== "number")
170
+ throw new TypeError(`Missing regex heap reference at ${path}.`);
164
171
  return { kind: "regex", source: value.source, flags: value.flags, lastIndex: value.lastIndex };
165
172
  }
166
173
  if (isSandboxDate(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxCollectionIterator(value) || isFloat32Array(value)) {
@@ -192,7 +199,11 @@ function serializeHeapReference(value, path, state) {
192
199
  }
193
200
  if (!state.serializedHeapIds.has(id)) {
194
201
  state.serializedHeapIds.add(id);
195
- if (isSandboxCollectionIterator(value)) {
202
+ if (isSandboxRegex(value)) {
203
+ state.heap[String(id)] = { kind: "regex-object", source: value.source, flags: value.flags,
204
+ lastIndex: serializeValue(value.lastIndex, `${path}.lastIndex`, state) };
205
+ }
206
+ else if (isSandboxCollectionIterator(value)) {
196
207
  const snapshot = snapshotCollectionIterator(value);
197
208
  const entries = Object.create(null);
198
209
  state.heap[String(id)] = {
@@ -313,6 +324,8 @@ function indexHeapContainers(input) {
313
324
  for (const [value, stat] of stats.entries()) {
314
325
  if (stat.count > 1 ||
315
326
  stat.cyclic ||
327
+ (isSandboxRegex(value) && (!Number.isSafeInteger(value.lastIndex) ||
328
+ (typeof value.lastIndex === "number" && value.lastIndex < 0) || Object.is(value.lastIndex, -0))) ||
316
329
  isSandboxDate(value) ||
317
330
  isFloat32Array(value) ||
318
331
  (Array.isArray(value) && requiresArrayEntries(value)) ||
@@ -337,7 +337,7 @@ function validateTaggedValue(record, path, state) {
337
337
  case "undefined":
338
338
  return;
339
339
  case "number":
340
- if (!["-Infinity", "Infinity", "NaN"].includes(String(record.value))) {
340
+ if (!["-Infinity", "Infinity", "NaN", "-0"].includes(String(record.value))) {
341
341
  fail("invalidValue", `${path}.value`, "unknown non-finite number value");
342
342
  }
343
343
  return;
@@ -359,6 +359,12 @@ function validateTaggedValue(record, path, state) {
359
359
  requireString(record.flags, `${path}.flags`, state.limits);
360
360
  requireSafeInteger(record.lastIndex, `${path}.lastIndex`, 0);
361
361
  return;
362
+ case "regex-object":
363
+ requireString(record.source, `${path}.source`, state.limits);
364
+ requireString(record.flags, `${path}.flags`, state.limits);
365
+ if (!Object.hasOwn(record, "lastIndex"))
366
+ fail("invalidValue", `${path}.lastIndex`, "missing regex cursor");
367
+ return;
362
368
  case "array":
363
369
  case "arguments":
364
370
  case "object":
@@ -408,7 +414,7 @@ function validateGeneratorShape(record, path, state) {
408
414
  function validateHeapValue(value, path, state) {
409
415
  const record = requireRecord(value, path);
410
416
  validateErrorType(record, path);
411
- if (!["arguments", "array", "object", "map", "set", "float32array", "date", "collection-iterator"].includes(String(record.kind)))
417
+ if (!["arguments", "array", "object", "map", "set", "float32array", "date", "collection-iterator", "regex-object"].includes(String(record.kind)))
412
418
  fail("unknownTag", `${path}.kind`, "unknown heap tag");
413
419
  validateValue(record, path, 1, state);
414
420
  if (record.kind === "arguments")