poe-code 14.0.74 → 14.0.75
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 +926 -84
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safe-js/dist/chunks/{chunk-2J6W3YGB.js → chunk-BAJI3K4B.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-YEZOXLY4.js → chunk-EOTY4PCT.js} +901 -84
- package/packages/safe-js/dist/chunks/chunk-EOTY4PCT.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/regex.js +108 -2
- package/packages/safe-js/dist/interp/iteration.js +5 -2
- package/packages/safe-js/dist/interp/methods/regex-split.d.ts +3 -0
- package/packages/safe-js/dist/interp/methods/regex-split.js +102 -0
- package/packages/safe-js/dist/interp/methods/regex.d.ts +1 -1
- package/packages/safe-js/dist/interp/methods/regex.js +16 -5
- package/packages/safe-js/dist/interp/methods/regexp-iterator.d.ts +5 -1
- package/packages/safe-js/dist/interp/methods/regexp-iterator.js +59 -7
- package/packages/safe-js/dist/interp/methods/string.js +7 -7
- package/packages/safe-js/dist/interp/object-model.d.ts +1 -0
- package/packages/safe-js/dist/interp/object-model.js +4 -1
- package/packages/safe-js/dist/interp/regex/compile-guard.js +26 -4
- package/packages/safe-js/dist/interp/regex/engine.d.ts +4 -0
- package/packages/safe-js/dist/interp/regex/engine.js +230 -30
- package/packages/safe-js/dist/interp/regex/parse.d.ts +42 -4
- package/packages/safe-js/dist/interp/regex/parse.js +429 -25
- package/packages/safe-js/dist/interp/regexp-iterator.d.ts +4 -2
- package/packages/safe-js/dist/interp/regexp-iterator.js +6 -1
- package/packages/safe-js/dist/interp/values.js +5 -3
- package/packages/safe-js/dist/snapshot/dump-format.js +10 -2
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +3 -0
- package/packages/safe-js/dist/snapshot/replay-data.js +14 -4
- package/packages/safe-js/dist/snapshot/restore.js +8 -2
- package/packages/safe-js/dist/snapshot/serialize.d.ts +3 -0
- package/packages/safe-js/dist/snapshot/serialize.js +4 -1
- package/packages/safe-js/dist/snapshot/validation.js +8 -0
- package/packages/safe-js/dist/chunks/chunk-YEZOXLY4.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-2J6W3YGB.js.map → chunk-BAJI3K4B.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-BAJI3K4B.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-EOTY4PCT.js";
|
|
38
38
|
import "./chunks/chunk-ZWJ5BJUI.js";
|
|
39
39
|
import "./chunks/chunk-3YTQEPZI.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-BAJI3K4B.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-EOTY4PCT.js";
|
|
54
54
|
import "./chunks/chunk-ZWJ5BJUI.js";
|
|
55
55
|
import "./chunks/chunk-3YTQEPZI.js";
|
|
56
56
|
import "./chunks/chunk-4YIDX44G.js";
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { createSandboxClosure, createSandboxRegex, isSandboxRegex, reconcileCompiledValues } from "../values.js";
|
|
1
|
+
import { createSandboxClosure, createSandboxRegex, isSandboxClosure, isSandboxRegex, reconcileCompiledValues } from "../values.js";
|
|
2
2
|
import { CompileScope } from "../regex/compile-guard.js";
|
|
3
3
|
import { SandboxError } from "../budget.js";
|
|
4
|
-
import { sandboxString } from "../string-coercion.js";
|
|
4
|
+
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
5
5
|
import { getSandboxPropertyDescriptor, installRegexPrototype, materializeFunctionProperties, setSandboxPrototype } from "../object-model.js";
|
|
6
6
|
import { accessorAdapter, readPropertyDescriptor } from "../accessors.js";
|
|
7
7
|
import { callRegexMethod, getRegexMember, regexFlagProperties, regexSearch } from "../methods/regex.js";
|
|
8
8
|
import { regexMatch, regexReplace } from "../methods/string.js";
|
|
9
9
|
import { invokeBuiltinClosure } from "../builtin-call.js";
|
|
10
|
+
import { restoreSandboxRegExpIterator } from "../regexp-iterator.js";
|
|
11
|
+
import { normalizeLastIndex } from "../regex/engine.js";
|
|
12
|
+
import { retainValues } from "../resources.js";
|
|
13
|
+
import { setSandboxProperty } from "../interpreter.js";
|
|
14
|
+
import { regexSplit } from "../methods/regex-split.js";
|
|
10
15
|
export function createRegexGlobals(options) {
|
|
11
16
|
const invoke = (construct) => async (args, context) => {
|
|
12
17
|
const selected = options.compileOwner ?? context?.compilation?.owner;
|
|
@@ -75,7 +80,52 @@ export function createRegexGlobals(options) {
|
|
|
75
80
|
}
|
|
76
81
|
};
|
|
77
82
|
const constructor = createSandboxClosure({ guest: true, sandbox: true, name: "RegExp", length: 2, call: invoke(false), construct: invoke(true) });
|
|
83
|
+
Object.defineProperty(materializeFunctionProperties(constructor), "escape", {
|
|
84
|
+
value: createSandboxClosure({ guest: true, sandbox: true, name: "escape", length: 1,
|
|
85
|
+
call: (args) => {
|
|
86
|
+
const input = args[0];
|
|
87
|
+
if (typeof input !== "string")
|
|
88
|
+
throw new TypeError("RegExp.escape requires a string.");
|
|
89
|
+
const allocation = {};
|
|
90
|
+
let escaped = "";
|
|
91
|
+
try {
|
|
92
|
+
for (const character of input) {
|
|
93
|
+
options.budget.visitNode();
|
|
94
|
+
const point = character.codePointAt(0);
|
|
95
|
+
let part = character;
|
|
96
|
+
if (escaped.length === 0 && ((point >= 48 && point <= 57) ||
|
|
97
|
+
(point >= 65 && point <= 90) || (point >= 97 && point <= 122))) {
|
|
98
|
+
part = `\\x${point.toString(16)}`;
|
|
99
|
+
}
|
|
100
|
+
else if ("^$\\.*+?()[]{}|/".includes(character)) {
|
|
101
|
+
part = "\\" + character;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const control = "\f\n\r\t\v".indexOf(character);
|
|
105
|
+
if (control !== -1)
|
|
106
|
+
part = "\\" + "fnrtv"[control];
|
|
107
|
+
else if (",-=<>#&!%:;@~'`\"".includes(character) || character.trim() === "" ||
|
|
108
|
+
(point >= 0xd800 && point <= 0xdfff)) {
|
|
109
|
+
part = point <= 0xff ? `\\x${point.toString(16).padStart(2, "0")}`
|
|
110
|
+
: `\\u${point.toString(16).padStart(4, "0")}`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
options.budget.setRetainedDataUsage(allocation, escaped.length + part.length);
|
|
114
|
+
escaped = options.budget.allocateString(escaped + part);
|
|
115
|
+
}
|
|
116
|
+
return escaped;
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
options.budget.setRetainedDataUsage(allocation, 0);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}), writable: true, configurable: true
|
|
123
|
+
});
|
|
78
124
|
const prototype = Object.create(null);
|
|
125
|
+
Object.defineProperty(materializeFunctionProperties(constructor), Symbol.species, {
|
|
126
|
+
get: accessorAdapter(createSandboxClosure({ sandbox: true, name: "get [Symbol.species]", length: 0,
|
|
127
|
+
call: (_args, context) => context?.thisValue }), "get"), configurable: true
|
|
128
|
+
});
|
|
79
129
|
Object.defineProperty(materializeFunctionProperties(constructor), "prototype", { value: prototype, writable: false });
|
|
80
130
|
Object.defineProperty(prototype, "constructor", { value: constructor, writable: true, configurable: true });
|
|
81
131
|
Object.defineProperty(prototype, Symbol.search, {
|
|
@@ -93,6 +143,62 @@ export function createRegexGlobals(options) {
|
|
|
93
143
|
call: (args, context) => regexReplace(args[0], context?.thisValue, args[1], options.budget, (closure, values) => invokeBuiltinClosure(closure, values, options.budget, context, undefined), context) }),
|
|
94
144
|
writable: true, configurable: true
|
|
95
145
|
});
|
|
146
|
+
Object.defineProperty(prototype, Symbol.matchAll, {
|
|
147
|
+
value: createSandboxClosure({ guest: true, sandbox: true, name: "[Symbol.matchAll]", length: 1,
|
|
148
|
+
call: async (args, context) => {
|
|
149
|
+
const target = context?.thisValue;
|
|
150
|
+
if (target === null || typeof target !== "object")
|
|
151
|
+
throw new TypeError("RegExp matchAll requires an object receiver.");
|
|
152
|
+
let input = args[0];
|
|
153
|
+
let string;
|
|
154
|
+
let species;
|
|
155
|
+
let field;
|
|
156
|
+
let flags;
|
|
157
|
+
let matcher;
|
|
158
|
+
const release = retainValues(options.budget, () => [target, input, string, species, field, flags, matcher]);
|
|
159
|
+
const read = (value, key) => {
|
|
160
|
+
if (context?.getProperty !== undefined)
|
|
161
|
+
return context.getProperty(value, key);
|
|
162
|
+
const descriptor = getSandboxPropertyDescriptor(value, key, options.budget);
|
|
163
|
+
return descriptor === undefined ? undefined : readPropertyDescriptor(descriptor, value, context);
|
|
164
|
+
};
|
|
165
|
+
try {
|
|
166
|
+
string = await sandboxString(input, options.budget, context);
|
|
167
|
+
input = undefined;
|
|
168
|
+
field = await read(target, "constructor");
|
|
169
|
+
species = constructor;
|
|
170
|
+
if (field !== undefined) {
|
|
171
|
+
if (field === null || typeof field !== "object")
|
|
172
|
+
throw new TypeError("Invalid RegExp species constructor.");
|
|
173
|
+
species = await read(field, Symbol.species);
|
|
174
|
+
if (species === null || species === undefined)
|
|
175
|
+
species = constructor;
|
|
176
|
+
}
|
|
177
|
+
field = undefined;
|
|
178
|
+
if (!isSandboxClosure(species) || species.construct === undefined)
|
|
179
|
+
throw new TypeError("RegExp species must be a constructor.");
|
|
180
|
+
field = await read(target, "flags");
|
|
181
|
+
flags = await sandboxString(field, options.budget, context);
|
|
182
|
+
field = undefined;
|
|
183
|
+
matcher = await invokeBuiltinClosure(species, [target, flags], options.budget, context, undefined, true);
|
|
184
|
+
field = await read(target, "lastIndex");
|
|
185
|
+
const cursor = normalizeLastIndex(await sandboxNumber(field, options.budget, context));
|
|
186
|
+
field = undefined;
|
|
187
|
+
await setSandboxProperty(matcher, "lastIndex", cursor, options.budget, true, context);
|
|
188
|
+
return restoreSandboxRegExpIterator({ matcher, input: string, exhausted: false,
|
|
189
|
+
global: flags.includes("g"), unicode: flags.includes("u") || flags.includes("v") });
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
release();
|
|
193
|
+
}
|
|
194
|
+
} }),
|
|
195
|
+
writable: true, configurable: true
|
|
196
|
+
});
|
|
197
|
+
Object.defineProperty(prototype, Symbol.split, {
|
|
198
|
+
value: createSandboxClosure({ guest: true, sandbox: true, name: "[Symbol.split]", length: 2,
|
|
199
|
+
call: (args, context) => regexSplit(context?.thisValue, args[0], args[1], constructor, options.budget, context) }),
|
|
200
|
+
writable: true, configurable: true
|
|
201
|
+
});
|
|
96
202
|
for (const name of ["exec", "test", "toString"]) {
|
|
97
203
|
Object.defineProperty(prototype, name, {
|
|
98
204
|
value: createSandboxClosure({ sandbox: true, name, length: name === "toString" ? 0 : 1,
|
|
@@ -5,7 +5,7 @@ import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
|
5
5
|
import { getHostObjectIterator, isGuestHostObject } from "./host-capabilities.js";
|
|
6
6
|
import { isSandboxCollectionIterator, nextCollectionIterator } from "./collection-iterator.js";
|
|
7
7
|
import { isSandboxRegExpIterator } from "./regexp-iterator.js";
|
|
8
|
-
import { nextRegExpIterator } from "./methods/regexp-iterator.js";
|
|
8
|
+
import { nextObservableRegExpIterator, nextRegExpIterator } from "./methods/regexp-iterator.js";
|
|
9
9
|
import { Budget, isFatalSandboxError } from "./budget.js";
|
|
10
10
|
import { sandboxString } from "./string-coercion.js";
|
|
11
11
|
import { awaitSandboxValue, awaitWithSignal } from "./cancel.js";
|
|
@@ -225,8 +225,11 @@ export function getSandboxIterator(value, budget, context) {
|
|
|
225
225
|
}
|
|
226
226
|
if (isSandboxCollectionIterator(value))
|
|
227
227
|
return { next: () => nextCollectionIterator(value, budget), snapshotIndex: () => 0 };
|
|
228
|
-
if (isSandboxRegExpIterator(value))
|
|
228
|
+
if (isSandboxRegExpIterator(value)) {
|
|
229
|
+
if (context !== undefined && budget !== undefined)
|
|
230
|
+
return { asynchronous: true, next: () => nextObservableRegExpIterator(value, budget, context), snapshotIndex: () => 0 };
|
|
229
231
|
return { next: () => nextRegExpIterator(value, budget), snapshotIndex: () => 0 };
|
|
232
|
+
}
|
|
230
233
|
if (isGuestHostObject(value))
|
|
231
234
|
return getHostObjectIterator(value);
|
|
232
235
|
if (isFloat32Array(value)) {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type Budget } from "../budget.js";
|
|
2
|
+
import { type SandboxCallContext, type SandboxClosure, type SandboxValue } from "../values.js";
|
|
3
|
+
export declare function regexSplit(target: SandboxValue, input: SandboxValue, limit: SandboxValue, constructor: SandboxClosure, budget: Budget, context?: SandboxCallContext): Promise<SandboxValue[]>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { isSandboxClosure } from "../values.js";
|
|
2
|
+
import { getSandboxPropertyDescriptor } from "../object-model.js";
|
|
3
|
+
import { readPropertyDescriptor } from "../accessors.js";
|
|
4
|
+
import { invokeBuiltinClosure } from "../builtin-call.js";
|
|
5
|
+
import { setSandboxProperty } from "../interpreter.js";
|
|
6
|
+
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
7
|
+
import { normalizeLastIndex } from "../regex/engine.js";
|
|
8
|
+
import { retainValues } from "../resources.js";
|
|
9
|
+
import { regexExec } from "./regex.js";
|
|
10
|
+
export async function regexSplit(target, input, limit, constructor, budget, context) {
|
|
11
|
+
if (target === null || typeof target !== "object")
|
|
12
|
+
throw new TypeError("RegExp split requires an object receiver.");
|
|
13
|
+
let string;
|
|
14
|
+
let species;
|
|
15
|
+
let field;
|
|
16
|
+
let flags;
|
|
17
|
+
let matcher;
|
|
18
|
+
let match;
|
|
19
|
+
const result = [];
|
|
20
|
+
const release = retainValues(budget, () => [target, input, limit, string, species, field, flags, matcher, match, result]);
|
|
21
|
+
const read = (value, key) => {
|
|
22
|
+
if (context?.getProperty !== undefined)
|
|
23
|
+
return context.getProperty(value, key);
|
|
24
|
+
const descriptor = getSandboxPropertyDescriptor(value, key, budget);
|
|
25
|
+
return descriptor === undefined ? undefined : readPropertyDescriptor(descriptor, value, context);
|
|
26
|
+
};
|
|
27
|
+
const append = (value) => {
|
|
28
|
+
budget.allocateArrayLength(result.length + 1);
|
|
29
|
+
result.push(value);
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
string = await sandboxString(input, budget, context);
|
|
33
|
+
input = undefined;
|
|
34
|
+
field = await read(target, "constructor");
|
|
35
|
+
species = constructor;
|
|
36
|
+
if (field !== undefined) {
|
|
37
|
+
if (field === null || typeof field !== "object")
|
|
38
|
+
throw new TypeError("Invalid RegExp species constructor.");
|
|
39
|
+
species = await read(field, Symbol.species);
|
|
40
|
+
if (species === null || species === undefined)
|
|
41
|
+
species = constructor;
|
|
42
|
+
}
|
|
43
|
+
field = undefined;
|
|
44
|
+
if (!isSandboxClosure(species) || species.construct === undefined)
|
|
45
|
+
throw new TypeError("RegExp species must be a constructor.");
|
|
46
|
+
field = await read(target, "flags");
|
|
47
|
+
flags = await sandboxString(field, budget, context);
|
|
48
|
+
field = undefined;
|
|
49
|
+
const unicode = flags.includes("u") || flags.includes("v");
|
|
50
|
+
if (!flags.includes("y"))
|
|
51
|
+
flags = budget.allocateString(flags + "y");
|
|
52
|
+
matcher = await invokeBuiltinClosure(species, [target, flags], budget, context, undefined, true);
|
|
53
|
+
const maximum = limit === undefined ? 2 ** 32 - 1 : (await sandboxNumber(limit, budget, context)) >>> 0;
|
|
54
|
+
limit = undefined;
|
|
55
|
+
if (maximum === 0)
|
|
56
|
+
return result;
|
|
57
|
+
if (string.length === 0) {
|
|
58
|
+
match = await regexExec(matcher, string, budget, context);
|
|
59
|
+
if (match === null)
|
|
60
|
+
append(string);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
let end = 0;
|
|
64
|
+
let index = 0;
|
|
65
|
+
while (index < string.length) {
|
|
66
|
+
budget.visitNode();
|
|
67
|
+
match = undefined;
|
|
68
|
+
await setSandboxProperty(matcher, "lastIndex", index, budget, true, context);
|
|
69
|
+
match = await regexExec(matcher, string, budget, context);
|
|
70
|
+
if (match !== null) {
|
|
71
|
+
field = await read(matcher, "lastIndex");
|
|
72
|
+
const next = Math.min(normalizeLastIndex(await sandboxNumber(field, budget, context)), string.length);
|
|
73
|
+
field = undefined;
|
|
74
|
+
if (next !== end) {
|
|
75
|
+
append(budget.allocateString(string.slice(end, index)));
|
|
76
|
+
if (result.length === maximum)
|
|
77
|
+
return result;
|
|
78
|
+
end = next;
|
|
79
|
+
field = await read(match, "length");
|
|
80
|
+
const length = normalizeLastIndex(await sandboxNumber(field, budget, context));
|
|
81
|
+
field = undefined;
|
|
82
|
+
for (let capture = 1; capture < length; capture++) {
|
|
83
|
+
budget.visitNode();
|
|
84
|
+
field = await read(match, String(capture));
|
|
85
|
+
append(field);
|
|
86
|
+
field = undefined;
|
|
87
|
+
if (result.length === maximum)
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
index = end;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
index += unicode && (string.codePointAt(index) ?? 0) > 0xffff ? 2 : 1;
|
|
95
|
+
}
|
|
96
|
+
append(budget.allocateString(string.slice(end)));
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
release();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -11,4 +11,4 @@ export declare function callRegexMethod(target: SandboxValue, methodName: RegexM
|
|
|
11
11
|
export declare function regexExec(target: SandboxValue, input: string, budget: Budget, context?: SandboxCallContext): Promise<SandboxValue>;
|
|
12
12
|
export declare function regexToString(target: SandboxValue, budget: Budget, context?: SandboxCallContext): Promise<string>;
|
|
13
13
|
export declare function executeRegex(target: SandboxRegex, input: string, lastIndex: number): RegexMatch | null;
|
|
14
|
-
export declare function toMatchArray(match: RegexMatch | null, input: string): SandboxValue;
|
|
14
|
+
export declare function toMatchArray(match: RegexMatch | null, input: string, budget?: Budget): SandboxValue;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSandboxClosure, getSandboxRegexPattern, getRegexProperties, isSandboxClosure, isSandboxRegex } from "../values.js";
|
|
2
2
|
import { matchRegex } from "../regex/engine.js";
|
|
3
3
|
import { invokeBuiltinClosure } from "../builtin-call.js";
|
|
4
|
-
import { getSandboxPropertyDescriptor, getSandboxPrototype, hasExplicitSandboxPrototype } from "../object-model.js";
|
|
4
|
+
import { getSandboxPropertyDescriptor, getSandboxPrototype, hasExplicitSandboxPrototype, setSandboxPrototype } from "../object-model.js";
|
|
5
5
|
import { readPropertyDescriptor } from "../accessors.js";
|
|
6
6
|
import { retainValues } from "../resources.js";
|
|
7
7
|
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
@@ -160,7 +160,7 @@ export async function callRegexMethod(target, methodName, args, budget, context)
|
|
|
160
160
|
cursor = target.lastIndex;
|
|
161
161
|
const lastIndex = await sandboxNumber(cursor, budget, context);
|
|
162
162
|
const match = executeRegex(target, input, lastIndex);
|
|
163
|
-
return toMatchArray(match, input);
|
|
163
|
+
return toMatchArray(match, input, budget);
|
|
164
164
|
}
|
|
165
165
|
finally {
|
|
166
166
|
budget.setRetainedValues(retained, undefined);
|
|
@@ -218,16 +218,27 @@ export async function regexToString(target, budget, context) {
|
|
|
218
218
|
export function executeRegex(target, input, lastIndex) {
|
|
219
219
|
const pattern = getSandboxRegexPattern(target);
|
|
220
220
|
const match = matchRegex(pattern, input, lastIndex);
|
|
221
|
-
if (pattern.flags.global) {
|
|
221
|
+
if (pattern.flags.global || pattern.flags.sticky) {
|
|
222
222
|
target.lastIndex = match === null ? 0 : match.index + match.text.length;
|
|
223
223
|
}
|
|
224
224
|
return match;
|
|
225
225
|
}
|
|
226
|
-
export function toMatchArray(match, input) {
|
|
226
|
+
export function toMatchArray(match, input, budget) {
|
|
227
227
|
if (match === null) {
|
|
228
228
|
return null;
|
|
229
229
|
}
|
|
230
|
+
budget?.allocateArrayLength(match.captures.length + 1);
|
|
230
231
|
const result = [match.text, ...match.captures];
|
|
231
|
-
|
|
232
|
+
if (match.groups !== undefined)
|
|
233
|
+
setSandboxPrototype(match.groups, null, budget);
|
|
234
|
+
if (match.indicesGroups !== undefined)
|
|
235
|
+
setSandboxPrototype(match.indicesGroups, null, budget);
|
|
236
|
+
Object.assign(result, { index: match.index, input, groups: match.groups });
|
|
237
|
+
if (match.indices !== undefined) {
|
|
238
|
+
budget?.allocateArrayLength(match.indices.length);
|
|
239
|
+
budget?.allocateArrayLength(2);
|
|
240
|
+
Object.assign(match.indices, { groups: match.indicesGroups });
|
|
241
|
+
Object.assign(result, { indices: match.indices });
|
|
242
|
+
}
|
|
232
243
|
return result;
|
|
233
244
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { Budget } from "../budget.js";
|
|
2
2
|
import { type SandboxRegExpIterator } from "../regexp-iterator.js";
|
|
3
|
-
import { type SandboxValue } from "../values.js";
|
|
3
|
+
import { type SandboxCallContext, type SandboxValue } from "../values.js";
|
|
4
4
|
export declare function nextRegExpIterator(iterator: SandboxRegExpIterator, budget?: Budget): {
|
|
5
5
|
value: SandboxValue;
|
|
6
6
|
done: boolean;
|
|
7
7
|
};
|
|
8
|
+
export declare function nextObservableRegExpIterator(iterator: SandboxRegExpIterator, budget: Budget, context: SandboxCallContext): Promise<{
|
|
9
|
+
value: SandboxValue;
|
|
10
|
+
done: boolean;
|
|
11
|
+
}>;
|
|
8
12
|
export declare function getRegExpIteratorMember(property: PropertyKey, budget: Budget): SandboxValue;
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { isSandboxRegExpIterator, regexpIteratorState } from "../regexp-iterator.js";
|
|
2
|
-
import { createSandboxClosure } from "../values.js";
|
|
3
|
-
import { executeRegex, toMatchArray } from "./regex.js";
|
|
2
|
+
import { createSandboxClosure, isSandboxRegex } from "../values.js";
|
|
3
|
+
import { executeRegex, regexExec, toMatchArray } from "./regex.js";
|
|
4
|
+
import { getSandboxPropertyDescriptor, hasRegexPropertyOverride } from "../object-model.js";
|
|
5
|
+
import { readPropertyDescriptor } from "../accessors.js";
|
|
6
|
+
import { setSandboxProperty } from "../interpreter.js";
|
|
7
|
+
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
8
|
+
import { normalizeLastIndex } from "../regex/engine.js";
|
|
9
|
+
import { retainValues } from "../resources.js";
|
|
4
10
|
export function nextRegExpIterator(iterator, budget) {
|
|
5
11
|
const state = regexpIteratorState(iterator);
|
|
6
12
|
budget?.visitNode();
|
|
7
13
|
if (state.exhausted)
|
|
8
14
|
return { value: undefined, done: true };
|
|
9
15
|
const matcher = state.matcher;
|
|
16
|
+
if (!isSandboxRegex(matcher))
|
|
17
|
+
throw new TypeError("Custom RegExp iterator execution requires a sandbox context.");
|
|
10
18
|
const input = state.input;
|
|
11
19
|
const match = executeRegex(matcher, input, Number(matcher.lastIndex));
|
|
12
|
-
if (match === null || !matcher.flags.includes("g")) {
|
|
20
|
+
if (match === null || !(state.global ?? matcher.flags.includes("g"))) {
|
|
13
21
|
state.exhausted = true;
|
|
14
22
|
state.matcher = undefined;
|
|
15
23
|
state.input = undefined;
|
|
@@ -18,12 +26,56 @@ export function nextRegExpIterator(iterator, budget) {
|
|
|
18
26
|
return { value: undefined, done: true };
|
|
19
27
|
if (!state.exhausted && match.text.length === 0) {
|
|
20
28
|
const index = Number(matcher.lastIndex);
|
|
21
|
-
const unicode = matcher.flags.includes("u") || matcher.flags.includes("v");
|
|
29
|
+
const unicode = state.unicode ?? (matcher.flags.includes("u") || matcher.flags.includes("v"));
|
|
22
30
|
const codePoint = input.codePointAt(index);
|
|
23
31
|
matcher.lastIndex = index + (unicode && codePoint !== undefined && codePoint > 0xffff ? 2 : 1);
|
|
24
32
|
}
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
return { value: toMatchArray(match, input, budget), done: false };
|
|
34
|
+
}
|
|
35
|
+
export async function nextObservableRegExpIterator(iterator, budget, context) {
|
|
36
|
+
const state = regexpIteratorState(iterator);
|
|
37
|
+
if (state.exhausted)
|
|
38
|
+
return { value: undefined, done: true };
|
|
39
|
+
const matcher = state.matcher;
|
|
40
|
+
if (isSandboxRegex(matcher) && !hasRegexPropertyOverride(matcher, ["exec"], budget) &&
|
|
41
|
+
(matcher.lastIndex === null || typeof matcher.lastIndex !== "object"))
|
|
42
|
+
return nextRegExpIterator(iterator, budget);
|
|
43
|
+
const input = state.input;
|
|
44
|
+
let result;
|
|
45
|
+
let field;
|
|
46
|
+
const release = retainValues(budget, () => [iterator, matcher, input, result, field]);
|
|
47
|
+
const read = (value, key) => {
|
|
48
|
+
if (context.getProperty !== undefined)
|
|
49
|
+
return context.getProperty(value, key);
|
|
50
|
+
const descriptor = getSandboxPropertyDescriptor(value, key, budget);
|
|
51
|
+
return descriptor === undefined ? undefined : readPropertyDescriptor(descriptor, value, context);
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
budget.visitNode();
|
|
55
|
+
result = await regexExec(matcher, input, budget, context);
|
|
56
|
+
const global = state.global ?? (isSandboxRegex(matcher) && matcher.flags.includes("g"));
|
|
57
|
+
if (result === null || !global) {
|
|
58
|
+
state.exhausted = true;
|
|
59
|
+
state.matcher = undefined;
|
|
60
|
+
state.input = undefined;
|
|
61
|
+
return result === null ? { value: undefined, done: true } : { value: result, done: false };
|
|
62
|
+
}
|
|
63
|
+
field = await read(result, "0");
|
|
64
|
+
const text = await sandboxString(field, budget, context);
|
|
65
|
+
field = undefined;
|
|
66
|
+
if (text.length === 0) {
|
|
67
|
+
field = await read(matcher, "lastIndex");
|
|
68
|
+
const index = normalizeLastIndex(await sandboxNumber(field, budget, context));
|
|
69
|
+
field = undefined;
|
|
70
|
+
const unicode = state.unicode ?? (isSandboxRegex(matcher) && (matcher.flags.includes("u") || matcher.flags.includes("v")));
|
|
71
|
+
const point = input.codePointAt(index);
|
|
72
|
+
await setSandboxProperty(matcher, "lastIndex", index + (unicode && point !== undefined && point > 0xffff ? 2 : 1), budget, true, context);
|
|
73
|
+
}
|
|
74
|
+
return { value: result, done: false };
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
release();
|
|
78
|
+
}
|
|
27
79
|
}
|
|
28
80
|
export function getRegExpIteratorMember(property, budget) {
|
|
29
81
|
if (property === Symbol.toStringTag)
|
|
@@ -43,7 +95,7 @@ export function getRegExpIteratorMember(property, budget) {
|
|
|
43
95
|
const receiver = context?.thisValue;
|
|
44
96
|
if (!isSandboxRegExpIterator(receiver))
|
|
45
97
|
throw new TypeError("RegExp string iterator next requires a matching receiver.");
|
|
46
|
-
return nextRegExpIterator(receiver, budget);
|
|
98
|
+
return context === undefined ? nextRegExpIterator(receiver, budget) : nextObservableRegExpIterator(receiver, budget, context);
|
|
47
99
|
}
|
|
48
100
|
});
|
|
49
101
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { types } from "node:util";
|
|
2
2
|
import { invokeBuiltinClosure } from "../builtin-call.js";
|
|
3
3
|
import { CompileScope } from "../regex/compile-guard.js";
|
|
4
|
-
import { normalizeLastIndex } from "../regex/engine.js";
|
|
4
|
+
import { advanceStringIndex, normalizeLastIndex } from "../regex/engine.js";
|
|
5
5
|
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
6
6
|
import { retainValues } from "../resources.js";
|
|
7
7
|
import { getSandboxDataProperty, getSandboxPropertyDescriptor, hasRegexPropertyOverride } from "../object-model.js";
|
|
8
8
|
import { readPropertyDescriptor } from "../accessors.js";
|
|
9
9
|
import { createSandboxBox } from "../boxed.js";
|
|
10
10
|
import { setSandboxProperty } from "../interpreter.js";
|
|
11
|
-
import { createSandboxClosure, createSandboxRegex, deepCopyFromSandbox, isSandboxClosure, isSandboxRegex, measureSandboxData } from "../values.js";
|
|
11
|
+
import { createSandboxClosure, createSandboxRegex, deepCopyFromSandbox, getSandboxRegexPattern, isSandboxClosure, isSandboxRegex, measureSandboxData } from "../values.js";
|
|
12
12
|
import { executeRegex, getRegexMember, regexExec, regexFlagProperties, regexSearch, toMatchArray } from "./regex.js";
|
|
13
13
|
import { restoreSandboxRegExpIterator } from "../regexp-iterator.js";
|
|
14
14
|
export const stringMethodNames = new Set([
|
|
@@ -356,7 +356,7 @@ function callReplaceLikeMethod(value, methodName, args, budget, callClosure, use
|
|
|
356
356
|
return replaceWithClosure(value, search, replacement, methodName === "replaceAll", budget, callClosure, context);
|
|
357
357
|
}
|
|
358
358
|
async function replaceRegex(value, regex, replacement, budget, callClosure, context) {
|
|
359
|
-
if (hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget))
|
|
359
|
+
if (getSandboxRegexPattern(regex).groups !== undefined || hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget))
|
|
360
360
|
return regexReplace(value, regex, replacement, budget, callClosure, context);
|
|
361
361
|
if (regex.flags.includes("g"))
|
|
362
362
|
regex.lastIndex = 0;
|
|
@@ -410,7 +410,7 @@ export async function regexReplace(input, regex, replacementInput, budget, callC
|
|
|
410
410
|
input = undefined;
|
|
411
411
|
replacement = isSandboxClosure(replacementInput) ? replacementInput : await sandboxString(replacementInput, budget, context);
|
|
412
412
|
replacementInput = undefined;
|
|
413
|
-
if (isSandboxRegex(regex) && !hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget)) {
|
|
413
|
+
if (isSandboxRegex(regex) && getSandboxRegexPattern(regex).groups === undefined && !hasRegexPropertyOverride(regex, ["exec", "flags", ...Object.keys(regexFlagProperties)], budget)) {
|
|
414
414
|
release();
|
|
415
415
|
return await replaceRegex(value, regex, replacement, budget, callClosure, context);
|
|
416
416
|
}
|
|
@@ -641,7 +641,7 @@ function splitNormalized(value, separator, limit, budget, parent) {
|
|
|
641
641
|
if (match === null)
|
|
642
642
|
break;
|
|
643
643
|
if (match.text.length === 0)
|
|
644
|
-
splitter.lastIndex = match.index
|
|
644
|
+
splitter.lastIndex = advanceStringIndex(value, match.index, splitter.flags.includes("u") || splitter.flags.includes("v"));
|
|
645
645
|
endedWithZeroWidthMatch = match.text.length === 0 && match.index === value.length;
|
|
646
646
|
if (match.text.length === 0 &&
|
|
647
647
|
(match.index === copiedThrough || match.index === value.length))
|
|
@@ -822,7 +822,7 @@ function callMatchLikeMethod(value, methodName, args, compilation, lastIndex) {
|
|
|
822
822
|
return match?.index ?? -1;
|
|
823
823
|
}
|
|
824
824
|
if (methodName === "match" && !regex.flags.includes("g"))
|
|
825
|
-
return toMatchArray(executeRegex(regex, value, lastIndex ?? Number(regex.lastIndex)), value);
|
|
825
|
+
return toMatchArray(executeRegex(regex, value, lastIndex ?? Number(regex.lastIndex)), value, compilation.owner?.budget);
|
|
826
826
|
if (methodName === "match")
|
|
827
827
|
regex.lastIndex = 0;
|
|
828
828
|
const matcher = methodName === "matchAll"
|
|
@@ -843,7 +843,7 @@ function collectRegexMatches(regex, value, all, budget, lastIndex = 0) {
|
|
|
843
843
|
matches.push(match);
|
|
844
844
|
lastIndex = match.index + match.text.length;
|
|
845
845
|
if (all && match.text.length === 0)
|
|
846
|
-
regex.lastIndex =
|
|
846
|
+
regex.lastIndex = lastIndex = advanceStringIndex(value, lastIndex, regex.flags.includes("u") || regex.flags.includes("v"));
|
|
847
847
|
} while (all);
|
|
848
848
|
return matches;
|
|
849
849
|
}
|
|
@@ -16,6 +16,7 @@ export declare function isIntrinsicConstructor(value: object): boolean;
|
|
|
16
16
|
export declare function releaseObjectPrototype(budget: Budget): void;
|
|
17
17
|
export declare function getSandboxPrototype(value: object, budget?: Budget): object | null;
|
|
18
18
|
export declare function hasExplicitSandboxPrototype(value: object): boolean;
|
|
19
|
+
export declare function hasNullObjectPrototype(value: object): boolean;
|
|
19
20
|
export declare function getSandboxPropertyDescriptor(value: SandboxValue, key: PropertyKey, budget?: Budget): PropertyDescriptor | undefined;
|
|
20
21
|
export declare function getSandboxDataProperty(value: SandboxValue, key: PropertyKey, budget?: Budget): SandboxValue;
|
|
21
22
|
export declare function setSandboxPrototype(value: object, prototype: object | null, budget?: Budget): void;
|
|
@@ -180,6 +180,9 @@ export function getSandboxPrototype(value, budget) {
|
|
|
180
180
|
export function hasExplicitSandboxPrototype(value) {
|
|
181
181
|
return prototypes.has(value);
|
|
182
182
|
}
|
|
183
|
+
export function hasNullObjectPrototype(value) {
|
|
184
|
+
return prototypes.get(value) === null && !Array.isArray(value) && !isSandboxClosure(value) && !isSandboxRegex(value);
|
|
185
|
+
}
|
|
183
186
|
export function getSandboxPropertyDescriptor(value, key, budget) {
|
|
184
187
|
const hostProperties = isSandboxClosure(value) ? value.properties : undefined;
|
|
185
188
|
if (isSandboxClosure(value) && !isGuestClosure(value))
|
|
@@ -288,7 +291,7 @@ export function hasGuestObjectState(value) {
|
|
|
288
291
|
return !intrinsicUnchanged();
|
|
289
292
|
if (isLiveCapability(value))
|
|
290
293
|
return true;
|
|
291
|
-
if (functionProperties.has(value) || prototypes.has(value))
|
|
294
|
+
if (functionProperties.has(value) || (prototypes.has(value) && !hasNullObjectPrototype(value)))
|
|
292
295
|
return true;
|
|
293
296
|
if (isSandboxBox(value) || isSandboxDate(value))
|
|
294
297
|
return false;
|
|
@@ -121,7 +121,7 @@ export class RegexCompileGuard {
|
|
|
121
121
|
this.work(flags.length);
|
|
122
122
|
}
|
|
123
123
|
retain(pattern, valueUnits = 0) {
|
|
124
|
-
const units =
|
|
124
|
+
const units = measurePattern(pattern);
|
|
125
125
|
if (this.ticket !== undefined) {
|
|
126
126
|
this.ticket.owner.budget.resizeCompileTicket(this.ticket, units + valueUnits);
|
|
127
127
|
}
|
|
@@ -139,7 +139,16 @@ export class RegexCompileGuard {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
export function regexCompiledData(pattern) {
|
|
142
|
-
return (compiledData.get(pattern) ?? { units:
|
|
142
|
+
return (compiledData.get(pattern) ?? { units: measurePattern(pattern) });
|
|
143
|
+
}
|
|
144
|
+
function measurePattern(pattern) {
|
|
145
|
+
let units = 10 + pattern.source.length + measureNode(pattern.body);
|
|
146
|
+
if (pattern.groups !== undefined) {
|
|
147
|
+
units += 1;
|
|
148
|
+
for (const [name, indices] of Object.entries(pattern.groups))
|
|
149
|
+
units += name.length + 2 + indices.length;
|
|
150
|
+
}
|
|
151
|
+
return units;
|
|
143
152
|
}
|
|
144
153
|
function measureNode(node) {
|
|
145
154
|
switch (node.type) {
|
|
@@ -148,18 +157,27 @@ function measureNode(node) {
|
|
|
148
157
|
return 2;
|
|
149
158
|
case "literal":
|
|
150
159
|
return 3 + node.value.length;
|
|
160
|
+
case "namedBackreference":
|
|
161
|
+
return 3 + node.name.length;
|
|
151
162
|
case "anchor":
|
|
152
163
|
case "wordBoundary":
|
|
164
|
+
case "backreference":
|
|
153
165
|
return 3;
|
|
154
166
|
case "characterClass": {
|
|
155
167
|
let usage = 5 + node.items.length;
|
|
168
|
+
if (node.operation !== undefined)
|
|
169
|
+
usage += 1 + node.operation.length;
|
|
170
|
+
if (node.strings !== undefined)
|
|
171
|
+
usage += 1;
|
|
156
172
|
for (const item of node.items) {
|
|
157
173
|
usage +=
|
|
158
174
|
item.type === "character"
|
|
159
175
|
? 3 + item.value.length
|
|
160
176
|
: item.type === "range"
|
|
161
177
|
? 4 + item.from.length + item.to.length
|
|
162
|
-
: 4
|
|
178
|
+
: item.type === "property" ? 4 + item.value.length + (item.strings === undefined ? 0 : 1)
|
|
179
|
+
: item.type === "set" ? 3 + measureNode({ type: "characterClass", ...item.value })
|
|
180
|
+
: item.type === "strings" ? 4 + item.values.reduce((total, value) => total + 1 + value.length, 0) : 4;
|
|
163
181
|
}
|
|
164
182
|
return usage;
|
|
165
183
|
}
|
|
@@ -172,7 +190,11 @@ function measureNode(node) {
|
|
|
172
190
|
return usage;
|
|
173
191
|
}
|
|
174
192
|
case "group":
|
|
175
|
-
return 5 + measureNode(node.body)
|
|
193
|
+
return 5 + measureNode(node.body) + (node.name === undefined ? 0 : 1 + node.name.length) +
|
|
194
|
+
(node.modifiers === undefined ? 0 : 2 + Object.keys(node.modifiers).length);
|
|
195
|
+
case "lookahead":
|
|
196
|
+
case "lookbehind":
|
|
197
|
+
return 4 + measureNode(node.body);
|
|
176
198
|
case "quantifier":
|
|
177
199
|
return (Object.hasOwn(node, "max") ? 6 : 5) + measureNode(node.body);
|
|
178
200
|
}
|