poe-code 14.0.56 → 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.
- package/dist/index.js +124 -51
- package/dist/index.js.map +3 -3
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safe-js/dist/chunks/{chunk-SRYQTOX4.js → chunk-H4O5OPM2.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-XI33TU4X.js → chunk-N3CN2ACZ.js} +122 -51
- package/packages/safe-js/dist/chunks/chunk-N3CN2ACZ.js.map +7 -0
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.js +1 -1
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/globals/misc.js +20 -7
- package/packages/safe-js/dist/interp/methods/regex.d.ts +1 -1
- package/packages/safe-js/dist/interp/methods/regex.js +18 -6
- package/packages/safe-js/dist/interp/methods/string.js +59 -26
- package/packages/safe-js/dist/interp/regex/engine.d.ts +1 -0
- package/packages/safe-js/dist/interp/regex/engine.js +3 -3
- package/packages/safe-js/dist/interp/values.d.ts +6 -3
- package/packages/safe-js/dist/interp/values.js +19 -6
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +1 -1
- package/packages/safe-js/dist/snapshot/replay-data.js +4 -4
- package/packages/safe-js/dist/snapshot/restore.js +8 -0
- package/packages/safe-js/dist/snapshot/serialize.d.ts +6 -1
- package/packages/safe-js/dist/snapshot/serialize.js +14 -1
- package/packages/safe-js/dist/snapshot/validation.js +8 -2
- package/packages/safe-js/dist/chunks/chunk-XI33TU4X.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-SRYQTOX4.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-
|
|
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-
|
|
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";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
runWithSpawnUsageAccumulator,
|
|
27
27
|
splitFrontmatter,
|
|
28
28
|
supportsSpawnMode
|
|
29
|
-
} from "./chunks/chunk-
|
|
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-
|
|
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 {
|
|
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
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
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)) {
|
|
@@ -6,5 +6,5 @@ export declare function isRegexMethodName(property: string | number): property i
|
|
|
6
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
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): RegexMatch | null;
|
|
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;
|
|
@@ -2,8 +2,12 @@ import { createSandboxClosure, getSandboxRegexPattern, isSandboxClosure, isSandb
|
|
|
2
2
|
import { matchRegex } from "../regex/engine.js";
|
|
3
3
|
import { invokeBuiltinClosure } from "../builtin-call.js";
|
|
4
4
|
import { getSandboxDataProperty } from "../object-model.js";
|
|
5
|
-
import { sandboxString } from "../string-coercion.js";
|
|
5
|
+
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
6
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
|
+
};
|
|
7
11
|
export function isRegexMethodName(property) {
|
|
8
12
|
return typeof property === "string" && regexMethodNames.has(property);
|
|
9
13
|
}
|
|
@@ -16,6 +20,8 @@ export function getRegexMember(target, property, budget) {
|
|
|
16
20
|
}
|
|
17
21
|
if (property === "lastIndex")
|
|
18
22
|
return target.lastIndex;
|
|
23
|
+
if (Object.hasOwn(regexFlagProperties, property))
|
|
24
|
+
return target.flags.includes(regexFlagProperties[property]);
|
|
19
25
|
if (!isRegexMethodName(property)) {
|
|
20
26
|
return undefined;
|
|
21
27
|
}
|
|
@@ -64,14 +70,16 @@ export function setRegexMember(target, property, value) {
|
|
|
64
70
|
if (property !== "lastIndex") {
|
|
65
71
|
throw new TypeError(`RegExp#${String(property)} is not writable.`);
|
|
66
72
|
}
|
|
67
|
-
target.lastIndex =
|
|
73
|
+
target.lastIndex = value;
|
|
68
74
|
}
|
|
69
75
|
export async function callRegexMethod(target, methodName, args, budget, context) {
|
|
70
76
|
if (target === null || typeof target !== "object" || (methodName === "exec" && !isSandboxRegex(target))) {
|
|
71
77
|
throw new TypeError(`RegExp#${methodName} requires ${methodName === "exec" ? "a regex" : "an object"} receiver.`);
|
|
72
78
|
}
|
|
73
79
|
const retained = {};
|
|
74
|
-
|
|
80
|
+
let cursor;
|
|
81
|
+
let convertedInput;
|
|
82
|
+
budget.setRetainedValues(retained, () => [target, ...args, cursor, convertedInput]);
|
|
75
83
|
try {
|
|
76
84
|
const input = await sandboxString(args[0], budget, context);
|
|
77
85
|
if (methodName === "test") {
|
|
@@ -87,16 +95,20 @@ export async function callRegexMethod(target, methodName, args, budget, context)
|
|
|
87
95
|
}
|
|
88
96
|
if (!isSandboxRegex(target))
|
|
89
97
|
throw new TypeError("RegExp execution requires a regex receiver.");
|
|
90
|
-
|
|
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);
|
|
91
103
|
return methodName === "test" ? match !== null : toMatchArray(match, input);
|
|
92
104
|
}
|
|
93
105
|
finally {
|
|
94
106
|
budget.setRetainedValues(retained, undefined);
|
|
95
107
|
}
|
|
96
108
|
}
|
|
97
|
-
export function executeRegex(target, input) {
|
|
109
|
+
export function executeRegex(target, input, lastIndex) {
|
|
98
110
|
const pattern = getSandboxRegexPattern(target);
|
|
99
|
-
const match = matchRegex(pattern, input,
|
|
111
|
+
const match = matchRegex(pattern, input, lastIndex);
|
|
100
112
|
if (pattern.flags.global) {
|
|
101
113
|
target.lastIndex = match === null ? 0 : match.index + match.text.length;
|
|
102
114
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { invokeBuiltinClosure } from "../builtin-call.js";
|
|
2
2
|
import { CompileScope } from "../regex/compile-guard.js";
|
|
3
|
-
import {
|
|
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
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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:
|
|
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?:
|
|
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
|
|
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 {
|
|
@@ -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
|
-
|
|
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,
|
|
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 (
|
|
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")
|