poe-code 13.0.3 → 13.0.4
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 +14418 -13628
- 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-73LWNNUG.js → chunk-WGS5B4OT.js} +14505 -13744
- package/packages/safe-js/dist/chunks/chunk-WGS5B4OT.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-QCJ7UAMC.js → chunk-ZGWWEL43.js} +2 -2
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.js +1 -1
- package/packages/safe-js/dist/index.d.ts +11 -3
- package/packages/safe-js/dist/index.js +14 -6
- package/packages/safe-js/dist/index.js.map +4 -4
- package/packages/safe-js/dist/interp/async.d.ts +2 -0
- package/packages/safe-js/dist/interp/async.js +31 -15
- package/packages/safe-js/dist/interp/budget.d.ts +29 -0
- package/packages/safe-js/dist/interp/budget.js +143 -0
- package/packages/safe-js/dist/interp/globals/console-json.d.ts +2 -1
- package/packages/safe-js/dist/interp/globals/console-json.js +24 -6
- package/packages/safe-js/dist/interp/globals/regex.d.ts +2 -1
- package/packages/safe-js/dist/interp/globals/regex.js +24 -3
- package/packages/safe-js/dist/interp/host-bridge.d.ts +2 -1
- package/packages/safe-js/dist/interp/host-bridge.js +98 -55
- package/packages/safe-js/dist/interp/host-call.d.ts +3 -2
- package/packages/safe-js/dist/interp/host-call.js +91 -52
- package/packages/safe-js/dist/interp/interpreter.d.ts +4 -1
- package/packages/safe-js/dist/interp/interpreter.js +115 -79
- package/packages/safe-js/dist/interp/methods/string.d.ts +2 -1
- package/packages/safe-js/dist/interp/methods/string.js +111 -100
- package/packages/safe-js/dist/interp/regex/compile-guard.d.ts +33 -0
- package/packages/safe-js/dist/interp/regex/compile-guard.js +179 -0
- package/packages/safe-js/dist/interp/regex/parse.d.ts +2 -1
- package/packages/safe-js/dist/interp/regex/parse.js +87 -16
- package/packages/safe-js/dist/interp/values.d.ts +7 -2
- package/packages/safe-js/dist/interp/values.js +91 -9
- package/packages/safe-js/dist/modules/registry.d.ts +2 -1
- package/packages/safe-js/dist/modules/registry.js +1 -0
- package/packages/safe-js/dist/parse/hash.d.ts +2 -1
- package/packages/safe-js/dist/parse/hash.js +7 -4
- package/packages/safe-js/dist/parse/parser.d.ts +5 -3
- package/packages/safe-js/dist/parse/parser.js +53 -27
- package/packages/safe-js/dist/parse/tokenizer.d.ts +2 -0
- package/packages/safe-js/dist/parse/tokenizer.js +48 -26
- package/packages/safe-js/dist/parse.d.ts +3 -1
- package/packages/safe-js/dist/parse.js +3 -1
- package/packages/safe-js/dist/restore.d.ts +2 -1
- package/packages/safe-js/dist/restore.js +3 -3
- package/packages/safe-js/dist/run.js +289 -263
- package/packages/safe-js/dist/snapshot/migration.d.ts +3 -2
- package/packages/safe-js/dist/snapshot/migration.js +12 -5
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +2 -1
- package/packages/safe-js/dist/snapshot/replay-data.js +172 -162
- package/packages/safe-js/dist/snapshot/replay-inputs.d.ts +2 -1
- package/packages/safe-js/dist/snapshot/replay-inputs.js +18 -11
- package/packages/safe-js/dist/snapshot/restore.d.ts +2 -2
- package/packages/safe-js/dist/snapshot/restore.js +142 -113
- package/packages/safe-js/dist/chunks/chunk-73LWNNUG.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-QCJ7UAMC.js.map → chunk-ZGWWEL43.js.map} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CompileScope } from "../regex/compile-guard.js";
|
|
1
2
|
import { createSandboxClosure, createSandboxRegex, deepCopyFromSandbox, isSandboxClosure, isSandboxRegex } from "../values.js";
|
|
2
3
|
import { executeRegex, toMatchArray } from "./regex.js";
|
|
3
4
|
const SPLIT_STRING_MESSAGE = "String#split only supports string separator values.";
|
|
@@ -47,7 +48,9 @@ export function getStringMember(value, property, budget) {
|
|
|
47
48
|
sandbox: true,
|
|
48
49
|
name: `String#${property}`,
|
|
49
50
|
...(property === "localeCompare" ? { length: 1 } : {}),
|
|
50
|
-
call: (args) => callStringMethod(value, property, args, budget
|
|
51
|
+
call: (args, context) => callStringMethod(value, property, args, budget, context === undefined
|
|
52
|
+
? undefined
|
|
53
|
+
: async (closure, closureArgs) => await closure.call(closureArgs, { ...context, thisValue: undefined }), context?.compilation)
|
|
51
54
|
});
|
|
52
55
|
}
|
|
53
56
|
function getStringIndex(property) {
|
|
@@ -61,106 +64,114 @@ export function isStringMethodName(property) {
|
|
|
61
64
|
return typeof property === "string" && stringMethodNames.has(property);
|
|
62
65
|
}
|
|
63
66
|
export function validateStringMethodArguments(_methodName, _args) { }
|
|
64
|
-
export function callStringMethod(value, methodName, args, budget, callClosure = async (closure, closureArgs) => await closure.call(closureArgs)) {
|
|
67
|
+
export function callStringMethod(value, methodName, args, budget, callClosure = async (closure, closureArgs) => await closure.call(closureArgs), parent) {
|
|
65
68
|
if (methodName === "replace" || methodName === "replaceAll") {
|
|
66
69
|
return callReplaceLikeMethod(value, methodName, args, budget, callClosure);
|
|
67
70
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
if (methodName === "localeCompare") {
|
|
75
|
-
if (isSandboxClosure(args[0])) {
|
|
76
|
-
throw new TypeError("String#localeCompare does not support function comparison values.");
|
|
71
|
+
const operation = budget.acquireCompileOwner(false, parent?.owner);
|
|
72
|
+
const compilation = new CompileScope(operation.owner);
|
|
73
|
+
try {
|
|
74
|
+
if (methodName === "split") {
|
|
75
|
+
return callSplit(value, args, budget, compilation);
|
|
77
76
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
deepCopyFromSandbox(args[1])
|
|
81
|
-
]);
|
|
82
|
-
const options = args[2];
|
|
83
|
-
const nativeOptions = options === undefined || options === null
|
|
84
|
-
? options
|
|
85
|
-
: Object.fromEntries([
|
|
86
|
-
"usage",
|
|
87
|
-
"localeMatcher",
|
|
88
|
-
"collation",
|
|
89
|
-
"numeric",
|
|
90
|
-
"caseFirst",
|
|
91
|
-
"sensitivity",
|
|
92
|
-
"ignorePunctuation"
|
|
93
|
-
].map((property) => {
|
|
94
|
-
const descriptor = Object.getOwnPropertyDescriptor(options, property);
|
|
95
|
-
if (descriptor !== undefined && !("value" in descriptor)) {
|
|
96
|
-
throw new TypeError("String#localeCompare only supports data option properties.");
|
|
97
|
-
}
|
|
98
|
-
const option = descriptor?.value;
|
|
99
|
-
return [
|
|
100
|
-
property,
|
|
101
|
-
option === undefined
|
|
102
|
-
? undefined
|
|
103
|
-
: property === "numeric" || property === "ignorePunctuation"
|
|
104
|
-
? Boolean(option)
|
|
105
|
-
: deepCopyFromSandbox(option)
|
|
106
|
-
];
|
|
107
|
-
}));
|
|
108
|
-
return Reflect.apply(String.prototype.localeCompare, value, [
|
|
109
|
-
comparison,
|
|
110
|
-
locales,
|
|
111
|
-
nativeOptions
|
|
112
|
-
]);
|
|
113
|
-
}
|
|
114
|
-
if (args.some(isSandboxClosure)) {
|
|
115
|
-
throw new TypeError(`String#${methodName} does not support function arguments.`);
|
|
116
|
-
}
|
|
117
|
-
switch (methodName) {
|
|
118
|
-
case "at": {
|
|
119
|
-
const result = value.at(asNumber(args[0]));
|
|
120
|
-
return result === undefined ? undefined : budget.allocateString(result);
|
|
77
|
+
if (methodName === "match" || methodName === "matchAll" || methodName === "search") {
|
|
78
|
+
return callMatchLikeMethod(value, methodName, args, compilation);
|
|
121
79
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
80
|
+
if (methodName === "localeCompare") {
|
|
81
|
+
if (isSandboxClosure(args[0])) {
|
|
82
|
+
throw new TypeError("String#localeCompare does not support function comparison values.");
|
|
83
|
+
}
|
|
84
|
+
const comparison = budget.allocateString(String(deepCopyFromSandbox(args[0], { compilation })));
|
|
85
|
+
const locales = Reflect.apply(Intl.getCanonicalLocales, Intl, [
|
|
86
|
+
deepCopyFromSandbox(args[1], { compilation })
|
|
87
|
+
]);
|
|
88
|
+
const options = args[2];
|
|
89
|
+
const nativeOptions = options === undefined || options === null
|
|
90
|
+
? options
|
|
91
|
+
: Object.fromEntries([
|
|
92
|
+
"usage",
|
|
93
|
+
"localeMatcher",
|
|
94
|
+
"collation",
|
|
95
|
+
"numeric",
|
|
96
|
+
"caseFirst",
|
|
97
|
+
"sensitivity",
|
|
98
|
+
"ignorePunctuation"
|
|
99
|
+
].map((property) => {
|
|
100
|
+
const descriptor = Object.getOwnPropertyDescriptor(options, property);
|
|
101
|
+
if (descriptor !== undefined && !("value" in descriptor)) {
|
|
102
|
+
throw new TypeError("String#localeCompare only supports data option properties.");
|
|
103
|
+
}
|
|
104
|
+
const option = descriptor?.value;
|
|
105
|
+
return [
|
|
106
|
+
property,
|
|
107
|
+
option === undefined
|
|
108
|
+
? undefined
|
|
109
|
+
: property === "numeric" || property === "ignorePunctuation"
|
|
110
|
+
? Boolean(option)
|
|
111
|
+
: deepCopyFromSandbox(option, { compilation })
|
|
112
|
+
];
|
|
113
|
+
}));
|
|
114
|
+
return Reflect.apply(String.prototype.localeCompare, value, [
|
|
115
|
+
comparison,
|
|
116
|
+
locales,
|
|
117
|
+
nativeOptions
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
if (args.some(isSandboxClosure)) {
|
|
121
|
+
throw new TypeError(`String#${methodName} does not support function arguments.`);
|
|
122
|
+
}
|
|
123
|
+
switch (methodName) {
|
|
124
|
+
case "at": {
|
|
125
|
+
const result = value.at(asNumber(args[0]));
|
|
126
|
+
return result === undefined ? undefined : budget.allocateString(result);
|
|
127
|
+
}
|
|
128
|
+
case "charAt":
|
|
129
|
+
return budget.allocateString(value.charAt(asNumber(args[0])));
|
|
130
|
+
case "charCodeAt":
|
|
131
|
+
return value.charCodeAt(asNumber(args[0]));
|
|
132
|
+
case "codePointAt":
|
|
133
|
+
return value.codePointAt(asNumber(args[0]));
|
|
134
|
+
case "concat":
|
|
135
|
+
return budget.allocateString(value.concat(...args.map(String)));
|
|
136
|
+
case "endsWith":
|
|
137
|
+
return value.endsWith(String(args[0]), asNumberOrUndefined(args[1]));
|
|
138
|
+
case "includes":
|
|
139
|
+
return value.includes(String(args[0]), asNumberOrUndefined(args[1]));
|
|
140
|
+
case "indexOf":
|
|
141
|
+
return value.indexOf(String(args[0]), asNumberOrUndefined(args[1]));
|
|
142
|
+
case "lastIndexOf":
|
|
143
|
+
return value.lastIndexOf(String(args[0]), asNumberOrUndefined(args[1]));
|
|
144
|
+
case "normalize":
|
|
145
|
+
return budget.allocateString(value.normalize(asStringOrUndefined(args[0])));
|
|
146
|
+
case "padEnd":
|
|
147
|
+
return budget.allocateString(value.padEnd(asNumber(args[0]), asStringOrUndefined(args[1])));
|
|
148
|
+
case "padStart":
|
|
149
|
+
return budget.allocateString(value.padStart(asNumber(args[0]), asStringOrUndefined(args[1])));
|
|
150
|
+
case "repeat":
|
|
151
|
+
return budget.allocateString(value.repeat(asNumber(args[0])));
|
|
152
|
+
case "slice":
|
|
153
|
+
return budget.allocateString(value.slice(asNumberOrUndefined(args[0]), asNumberOrUndefined(args[1])));
|
|
154
|
+
case "startsWith":
|
|
155
|
+
return value.startsWith(String(args[0]), asNumberOrUndefined(args[1]));
|
|
156
|
+
case "substr":
|
|
157
|
+
return budget.allocateString(value.substr(asNumber(args[0]), asNumberOrUndefined(args[1])));
|
|
158
|
+
case "substring":
|
|
159
|
+
return budget.allocateString(value.substring(asNumber(args[0]), asNumberOrUndefined(args[1])));
|
|
160
|
+
case "toLowerCase":
|
|
161
|
+
return budget.allocateString(value.toLowerCase());
|
|
162
|
+
case "toUpperCase":
|
|
163
|
+
return budget.allocateString(value.toUpperCase());
|
|
164
|
+
case "trim":
|
|
165
|
+
return budget.allocateString(value.trim());
|
|
166
|
+
case "trimEnd":
|
|
167
|
+
return budget.allocateString(value.trimEnd());
|
|
168
|
+
case "trimStart":
|
|
169
|
+
return budget.allocateString(value.trimStart());
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
compilation.dispose();
|
|
174
|
+
operation.release();
|
|
164
175
|
}
|
|
165
176
|
}
|
|
166
177
|
function callReplaceLikeMethod(value, methodName, args, budget, callClosure) {
|
|
@@ -276,12 +287,12 @@ function findReplacementOffsets(value, searchValue, replaceAll) {
|
|
|
276
287
|
}
|
|
277
288
|
return offsets;
|
|
278
289
|
}
|
|
279
|
-
function callSplit(value, args, budget) {
|
|
290
|
+
function callSplit(value, args, budget, compilation) {
|
|
280
291
|
if (args.some(isSandboxClosure))
|
|
281
292
|
throw new TypeError("String#split does not support function arguments.");
|
|
282
293
|
if (isSandboxRegex(args[0])) {
|
|
283
294
|
const regex = args[0];
|
|
284
|
-
const splitter = createSandboxRegex(regex.source, regex.flags.includes("g") ? regex.flags : `${regex.flags}g
|
|
295
|
+
const splitter = createSandboxRegex(regex.source, regex.flags.includes("g") ? regex.flags : `${regex.flags}g`, 0, compilation);
|
|
285
296
|
const limit = asNumberOrUndefined(args[1]) ?? 2 ** 32 - 1;
|
|
286
297
|
const result = [];
|
|
287
298
|
let copiedThrough = 0;
|
|
@@ -313,7 +324,7 @@ function callSplit(value, args, budget) {
|
|
|
313
324
|
budget.allocateArrayLength(result.length);
|
|
314
325
|
return result;
|
|
315
326
|
}
|
|
316
|
-
function callMatchLikeMethod(value, methodName, args) {
|
|
327
|
+
function callMatchLikeMethod(value, methodName, args, compilation) {
|
|
317
328
|
const regex = args[0];
|
|
318
329
|
if (!isSandboxRegex(regex))
|
|
319
330
|
throw new TypeError(`String#${methodName} requires a regex argument.`);
|
|
@@ -331,7 +342,7 @@ function callMatchLikeMethod(value, methodName, args) {
|
|
|
331
342
|
if (methodName === "match")
|
|
332
343
|
regex.lastIndex = 0;
|
|
333
344
|
const matcher = methodName === "matchAll"
|
|
334
|
-
? createSandboxRegex(regex.source, regex.flags, regex.lastIndex)
|
|
345
|
+
? createSandboxRegex(regex.source, regex.flags, regex.lastIndex, compilation)
|
|
335
346
|
: regex;
|
|
336
347
|
const matches = collectRegexMatches(matcher, value, true);
|
|
337
348
|
if (methodName === "match" && matches.length === 0)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type CompileOwner, type CompileTicket } from "../budget.js";
|
|
2
|
+
import type { RegexPattern } from "./parse.js";
|
|
3
|
+
export declare class CompileScope {
|
|
4
|
+
readonly owner?: CompileOwner | undefined;
|
|
5
|
+
readonly parent?: CompileScope | undefined;
|
|
6
|
+
readonly tickets: Set<CompileTicket>;
|
|
7
|
+
closed: boolean;
|
|
8
|
+
constructor(owner?: CompileOwner | undefined, parent?: CompileScope | undefined);
|
|
9
|
+
forward(tickets: Iterable<CompileTicket>, parent: CompileScope): void;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
12
|
+
export declare class RegexCompileGuard {
|
|
13
|
+
private readonly scope?;
|
|
14
|
+
private allocations;
|
|
15
|
+
private depth;
|
|
16
|
+
private readonly ticket?;
|
|
17
|
+
private retained;
|
|
18
|
+
constructor(scope?: CompileScope | undefined);
|
|
19
|
+
checkLength(length: number, flags?: boolean): void;
|
|
20
|
+
work(units: number): void;
|
|
21
|
+
allocate(units: number): void;
|
|
22
|
+
array(length: number): void;
|
|
23
|
+
enterGroup(): void;
|
|
24
|
+
leaveGroup(): void;
|
|
25
|
+
preflight(source: string, flags: string): void;
|
|
26
|
+
retain(pattern: RegexPattern, valueUnits?: number): void;
|
|
27
|
+
retainScratch(): void;
|
|
28
|
+
close(): void;
|
|
29
|
+
}
|
|
30
|
+
export declare function regexCompiledData(pattern: RegexPattern): {
|
|
31
|
+
units: number;
|
|
32
|
+
ticket?: CompileTicket;
|
|
33
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { REGEX_COMPILE_LIMITS, SandboxError } from "../budget.js";
|
|
2
|
+
const compiledData = new WeakMap();
|
|
3
|
+
export class CompileScope {
|
|
4
|
+
owner;
|
|
5
|
+
parent;
|
|
6
|
+
tickets = new Set();
|
|
7
|
+
closed = false;
|
|
8
|
+
constructor(owner, parent) {
|
|
9
|
+
this.owner = owner;
|
|
10
|
+
this.parent = parent;
|
|
11
|
+
}
|
|
12
|
+
forward(tickets, parent) {
|
|
13
|
+
if (parent === this)
|
|
14
|
+
return;
|
|
15
|
+
if (this.owner !== parent.owner)
|
|
16
|
+
throw new SandboxError("reentry");
|
|
17
|
+
for (const ticket of tickets) {
|
|
18
|
+
if (!this.tickets.delete(ticket))
|
|
19
|
+
continue;
|
|
20
|
+
if (ticket.owner.budget.compileTicketUsage(ticket) > 0)
|
|
21
|
+
parent.tickets.add(ticket);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
dispose() {
|
|
25
|
+
for (const ticket of this.tickets)
|
|
26
|
+
ticket.owner.budget.discardCompileTicket(ticket);
|
|
27
|
+
this.tickets.clear();
|
|
28
|
+
this.closed = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class RegexCompileGuard {
|
|
32
|
+
scope;
|
|
33
|
+
allocations = 0;
|
|
34
|
+
depth = 0;
|
|
35
|
+
ticket;
|
|
36
|
+
retained = false;
|
|
37
|
+
constructor(scope) {
|
|
38
|
+
this.scope = scope;
|
|
39
|
+
if (scope?.owner !== undefined) {
|
|
40
|
+
this.ticket = scope.owner.budget.createCompileTicket(scope.owner);
|
|
41
|
+
scope.tickets.add(this.ticket);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
checkLength(length, flags = false) {
|
|
45
|
+
const hard = flags ? REGEX_COMPILE_LIMITS.flagsLength : REGEX_COMPILE_LIMITS.sourceLength;
|
|
46
|
+
const limit = Math.min(hard, this.scope?.owner?.budget.limits.stringLength ?? hard);
|
|
47
|
+
if (length > limit)
|
|
48
|
+
throw new SandboxError({ budget: "stringLength", current: length, limit });
|
|
49
|
+
}
|
|
50
|
+
work(units) {
|
|
51
|
+
const budget = this.scope?.owner?.budget;
|
|
52
|
+
if (budget === undefined)
|
|
53
|
+
return;
|
|
54
|
+
for (let index = 0; index < units; index += 1)
|
|
55
|
+
budget.visitNode();
|
|
56
|
+
}
|
|
57
|
+
allocate(units) {
|
|
58
|
+
const next = this.allocations + units;
|
|
59
|
+
if (next > REGEX_COMPILE_LIMITS.allocations) {
|
|
60
|
+
throw new SandboxError({
|
|
61
|
+
budget: "dataSize",
|
|
62
|
+
current: next,
|
|
63
|
+
limit: REGEX_COMPILE_LIMITS.allocations
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
this.work(units);
|
|
67
|
+
if (this.ticket !== undefined) {
|
|
68
|
+
this.ticket.owner.budget.resizeCompileTicket(this.ticket, next);
|
|
69
|
+
}
|
|
70
|
+
this.allocations = next;
|
|
71
|
+
}
|
|
72
|
+
array(length) {
|
|
73
|
+
this.scope?.owner?.budget.allocateArrayLength(length);
|
|
74
|
+
this.allocate(1);
|
|
75
|
+
}
|
|
76
|
+
enterGroup() {
|
|
77
|
+
const next = this.depth + 1;
|
|
78
|
+
if (next > REGEX_COMPILE_LIMITS.depth) {
|
|
79
|
+
throw new SandboxError({
|
|
80
|
+
budget: "dataDepth",
|
|
81
|
+
current: next,
|
|
82
|
+
limit: REGEX_COMPILE_LIMITS.depth
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
this.depth = next;
|
|
86
|
+
}
|
|
87
|
+
leaveGroup() {
|
|
88
|
+
this.depth -= 1;
|
|
89
|
+
}
|
|
90
|
+
preflight(source, flags) {
|
|
91
|
+
this.checkLength(source.length);
|
|
92
|
+
this.checkLength(flags.length, true);
|
|
93
|
+
let escaped = false;
|
|
94
|
+
let characterClass = false;
|
|
95
|
+
let nesting = 0;
|
|
96
|
+
for (let position = 0; position < source.length; position += 1) {
|
|
97
|
+
this.work(1);
|
|
98
|
+
const character = source[position];
|
|
99
|
+
if (escaped) {
|
|
100
|
+
escaped = false;
|
|
101
|
+
}
|
|
102
|
+
else if (character === "\\") {
|
|
103
|
+
escaped = true;
|
|
104
|
+
}
|
|
105
|
+
else if (character === "[" && !characterClass) {
|
|
106
|
+
characterClass = true;
|
|
107
|
+
}
|
|
108
|
+
else if (character === "]" && characterClass) {
|
|
109
|
+
characterClass = false;
|
|
110
|
+
}
|
|
111
|
+
else if (!characterClass && character === "(") {
|
|
112
|
+
nesting += 1;
|
|
113
|
+
this.enterGroup();
|
|
114
|
+
}
|
|
115
|
+
else if (!characterClass && character === ")" && nesting > 0) {
|
|
116
|
+
nesting -= 1;
|
|
117
|
+
this.leaveGroup();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
this.depth = 0;
|
|
121
|
+
this.work(flags.length);
|
|
122
|
+
}
|
|
123
|
+
retain(pattern, valueUnits = 0) {
|
|
124
|
+
const units = 10 + pattern.source.length + measureNode(pattern.body);
|
|
125
|
+
if (this.ticket !== undefined) {
|
|
126
|
+
this.ticket.owner.budget.resizeCompileTicket(this.ticket, units + valueUnits);
|
|
127
|
+
}
|
|
128
|
+
compiledData.set(pattern, { units, ticket: this.ticket });
|
|
129
|
+
this.retained = true;
|
|
130
|
+
}
|
|
131
|
+
retainScratch() {
|
|
132
|
+
this.retained = true;
|
|
133
|
+
}
|
|
134
|
+
close() {
|
|
135
|
+
if (this.retained || this.ticket === undefined)
|
|
136
|
+
return;
|
|
137
|
+
this.ticket.owner.budget.discardCompileTicket(this.ticket);
|
|
138
|
+
this.scope?.tickets.delete(this.ticket);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export function regexCompiledData(pattern) {
|
|
142
|
+
return (compiledData.get(pattern) ?? { units: 10 + pattern.source.length + measureNode(pattern.body) });
|
|
143
|
+
}
|
|
144
|
+
function measureNode(node) {
|
|
145
|
+
switch (node.type) {
|
|
146
|
+
case "empty":
|
|
147
|
+
case "dot":
|
|
148
|
+
return 2;
|
|
149
|
+
case "literal":
|
|
150
|
+
return 3 + node.value.length;
|
|
151
|
+
case "anchor":
|
|
152
|
+
case "wordBoundary":
|
|
153
|
+
return 3;
|
|
154
|
+
case "characterClass": {
|
|
155
|
+
let usage = 5 + node.items.length;
|
|
156
|
+
for (const item of node.items) {
|
|
157
|
+
usage +=
|
|
158
|
+
item.type === "character"
|
|
159
|
+
? 3 + item.value.length
|
|
160
|
+
: item.type === "range"
|
|
161
|
+
? 4 + item.from.length + item.to.length
|
|
162
|
+
: 4;
|
|
163
|
+
}
|
|
164
|
+
return usage;
|
|
165
|
+
}
|
|
166
|
+
case "sequence":
|
|
167
|
+
case "alternation": {
|
|
168
|
+
const children = node.type === "sequence" ? node.elements : node.alternatives;
|
|
169
|
+
let usage = 4 + children.length;
|
|
170
|
+
for (const child of children)
|
|
171
|
+
usage += measureNode(child);
|
|
172
|
+
return usage;
|
|
173
|
+
}
|
|
174
|
+
case "group":
|
|
175
|
+
return 5 + measureNode(node.body);
|
|
176
|
+
case "quantifier":
|
|
177
|
+
return (Object.hasOwn(node, "max") ? 6 : 5) + measureNode(node.body);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CompileScope } from "./compile-guard.js";
|
|
1
2
|
export type RegexFlags = {
|
|
2
3
|
global: boolean;
|
|
3
4
|
ignoreCase: boolean;
|
|
@@ -58,4 +59,4 @@ export type RegexPattern = {
|
|
|
58
59
|
captureCount: number;
|
|
59
60
|
body: RegexNode;
|
|
60
61
|
};
|
|
61
|
-
export declare function parseRegex(source: string, flags?: string): RegexPattern;
|
|
62
|
+
export declare function parseRegex(source: string, flags?: string, compilation?: CompileScope, valueUnits?: number): RegexPattern;
|