poe-code 13.0.3 → 13.0.5
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 +14421 -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-BI5UBZ7A.js} +14508 -13744
- package/packages/safe-js/dist/chunks/chunk-BI5UBZ7A.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-QCJ7UAMC.js → chunk-FH5X7CUS.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 +95 -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-FH5X7CUS.js.map} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { float32DataProperties, float32Storage, isFloat32Array } from "./float32.js";
|
|
3
3
|
import { cloneSandboxValue, createSandboxClosure, defineOwnDataProperty, isArrayIndexKey, measureSandboxData } from "./values.js";
|
|
4
|
+
import { CompileScope } from "./regex/compile-guard.js";
|
|
4
5
|
import { decodeReplayData, encodeReplayData } from "../snapshot/replay-data.js";
|
|
5
6
|
import { validateSnapshotData } from "../snapshot/validation.js";
|
|
6
7
|
import { pendingHostCallResumeIdentityMatches } from "../snapshot/policy.js";
|
|
@@ -35,6 +36,7 @@ export class HostCallJournal {
|
|
|
35
36
|
sourceHash;
|
|
36
37
|
resumeProvider;
|
|
37
38
|
budget;
|
|
39
|
+
compileOwner;
|
|
38
40
|
runId;
|
|
39
41
|
nextCall = 1;
|
|
40
42
|
records;
|
|
@@ -59,54 +61,71 @@ export class HostCallJournal {
|
|
|
59
61
|
throw new UnresolvedReplayCapabilityError(id);
|
|
60
62
|
return capability;
|
|
61
63
|
};
|
|
62
|
-
constructor(sourceHash, records = [], resumeProvider, replay, budget) {
|
|
64
|
+
constructor(sourceHash, records = [], resumeProvider, replay, budget, compileOwner) {
|
|
63
65
|
this.sourceHash = sourceHash;
|
|
64
66
|
this.resumeProvider = resumeProvider;
|
|
65
67
|
this.budget = budget;
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
this.compileOwner = compileOwner;
|
|
69
|
+
const operation = budget?.acquireCompileOwner(false, compileOwner);
|
|
70
|
+
this.compileOwner = operation?.owner;
|
|
71
|
+
const compilation = new CompileScope(operation?.owner);
|
|
72
|
+
try {
|
|
73
|
+
this.recordedReplay = replay !== undefined;
|
|
74
|
+
if (replay !== undefined) {
|
|
75
|
+
const replayRecords = restoreReplayCalls(replay, this.encodedOutcomes, this.callbackSizes, this.requiredHostCapabilities, compilation);
|
|
76
|
+
const restoredRunId = replayRecords[0]?.runId ?? records[0]?.runId ?? randomUUID();
|
|
77
|
+
validateRestoredRecords(records, restoredRunId, sourceHash);
|
|
78
|
+
for (const record of records) {
|
|
79
|
+
const replayRecord = replayRecords[readCallOrdinal(record) - 1];
|
|
80
|
+
if (replayRecord === undefined ||
|
|
81
|
+
!callIdentityMatches(record, replayRecord) ||
|
|
82
|
+
record.lifecycle !== replayRecord.lifecycle) {
|
|
83
|
+
throw new HostCallResumabilityError(record, "reset", `Host call ${record.id} conflicts with the replay journal; reset is required.`);
|
|
84
|
+
}
|
|
77
85
|
}
|
|
86
|
+
records = replayRecords;
|
|
78
87
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
this.runId = records[0]?.runId ?? randomUUID();
|
|
89
|
+
this.records = records.map((record) => ({
|
|
90
|
+
...record,
|
|
91
|
+
...(record.functions === undefined ? {} : { functions: [...record.functions] }),
|
|
92
|
+
...(record.callbacks === undefined ? {} : { callbacks: structuredClone(record.callbacks) }),
|
|
93
|
+
...(record.outcome === undefined ? {} : { outcome: copyOutcome(record.outcome) })
|
|
94
|
+
}));
|
|
95
|
+
validateRestoredRecords(this.records, this.runId, sourceHash);
|
|
96
|
+
this.retainedSize = this.records.length;
|
|
97
|
+
try {
|
|
98
|
+
this.budget?.setRetainedDataUsage(this, this.retainedSize);
|
|
99
|
+
for (const record of this.records) {
|
|
100
|
+
if (record.outcome !== undefined)
|
|
101
|
+
this.retainOutcome(record, record.outcome);
|
|
102
|
+
for (const [index, callback] of (record.callbacks ?? []).entries()) {
|
|
103
|
+
let size = this.callbackSizes.get(`${record.id}/callback/${index + 1}`);
|
|
104
|
+
if (size === undefined) {
|
|
105
|
+
const validation = new CompileScope(operation?.owner);
|
|
106
|
+
try {
|
|
107
|
+
size = measureSandboxData([decodeReplayData(callback.arguments, {}, validation)]);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
validation.dispose();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this.retainedSize += 1 + size;
|
|
114
|
+
}
|
|
100
115
|
}
|
|
116
|
+
this.budget?.setRetainedDataUsage(this, this.retainedSize);
|
|
101
117
|
}
|
|
102
|
-
|
|
118
|
+
catch (error) {
|
|
119
|
+
this.dispose();
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
this.restored = [...this.records];
|
|
123
|
+
this.budget?.setRetainedValues(this, () => this.capabilities.values());
|
|
103
124
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
125
|
+
finally {
|
|
126
|
+
compilation.dispose();
|
|
127
|
+
operation?.release();
|
|
107
128
|
}
|
|
108
|
-
this.restored = [...this.records];
|
|
109
|
-
this.budget?.setRetainedValues(this, () => this.capabilities.values());
|
|
110
129
|
}
|
|
111
130
|
issue(input) {
|
|
112
131
|
const restored = this.restored[0];
|
|
@@ -318,16 +337,17 @@ export class HostCallJournal {
|
|
|
318
337
|
}
|
|
319
338
|
return positions;
|
|
320
339
|
}
|
|
321
|
-
retainOutcome(record, outcome) {
|
|
322
|
-
const
|
|
340
|
+
retainOutcome(record, outcome, budget = this.budget) {
|
|
341
|
+
const included = new Set();
|
|
342
|
+
const size = measureSandboxData([outcome.status === "fulfilled" ? outcome.value : outcome.reason], { ignoreClosures: true, compileTickets: included });
|
|
323
343
|
const retainedSize = this.retainedSize + size - (this.outcomeSizes.get(record.id) ?? 0);
|
|
324
|
-
|
|
344
|
+
budget?.reconcileCompileData(retainedSize, included, included, this);
|
|
325
345
|
let copied;
|
|
326
346
|
try {
|
|
327
347
|
copied = copyOutcome(outcome);
|
|
328
348
|
}
|
|
329
349
|
catch (error) {
|
|
330
|
-
|
|
350
|
+
budget?.setRetainedDataUsage(this, this.retainedSize);
|
|
331
351
|
throw error;
|
|
332
352
|
}
|
|
333
353
|
this.retainedSize = retainedSize;
|
|
@@ -340,10 +360,21 @@ export class HostCallJournal {
|
|
|
340
360
|
return undefined;
|
|
341
361
|
const encoded = this.encodedOutcomes.get(record.id);
|
|
342
362
|
if (encoded !== undefined) {
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
363
|
+
const budget = this.compileOwner?.budget ?? this.budget;
|
|
364
|
+
const operation = budget?.acquireCompileOwner(false, this.compileOwner);
|
|
365
|
+
const compilation = new CompileScope(operation?.owner);
|
|
366
|
+
try {
|
|
367
|
+
const value = decodeReplayData(encoded.data, { resolveCapability: this.resolveCapability }, compilation);
|
|
368
|
+
const outcome = encoded.status === "fulfilled"
|
|
369
|
+
? { status: "fulfilled", value }
|
|
370
|
+
: { status: "rejected", reason: value };
|
|
371
|
+
this.retainOutcome(record, outcome, budget);
|
|
372
|
+
return outcome;
|
|
373
|
+
}
|
|
374
|
+
finally {
|
|
375
|
+
compilation.dispose();
|
|
376
|
+
operation?.release();
|
|
377
|
+
}
|
|
347
378
|
}
|
|
348
379
|
const outcome = this.outcomes.get(record.id);
|
|
349
380
|
return outcome === undefined ? undefined : copyOutcome(outcome);
|
|
@@ -376,7 +407,7 @@ function copyOutcome(outcome) {
|
|
|
376
407
|
? { status: "fulfilled", value: cloneSandboxValue(outcome.value) }
|
|
377
408
|
: { status: "rejected", reason: cloneSandboxValue(outcome.reason) };
|
|
378
409
|
}
|
|
379
|
-
function restoreReplayCalls(input, encodedOutcomes, callbackSizes, requiredHostCapabilities = new Set()) {
|
|
410
|
+
function restoreReplayCalls(input, encodedOutcomes, callbackSizes, requiredHostCapabilities = new Set(), compilation) {
|
|
380
411
|
validateSnapshotData(input);
|
|
381
412
|
if (input === null ||
|
|
382
413
|
typeof input !== "object" ||
|
|
@@ -444,11 +475,19 @@ function restoreReplayCalls(input, encodedOutcomes, callbackSizes, requiredHostC
|
|
|
444
475
|
!Number.isSafeInteger(callback.id) ||
|
|
445
476
|
callback.id < 1 ||
|
|
446
477
|
!Number.isSafeInteger(callback.step) ||
|
|
447
|
-
callback.step < previousStep
|
|
448
|
-
!Array.isArray(decodeReplayData(callback.arguments, { resolveCapability })))
|
|
478
|
+
callback.step < previousStep)
|
|
449
479
|
throw new TypeError("Invalid replay callback.");
|
|
450
480
|
previousStep = callback.step;
|
|
451
|
-
|
|
481
|
+
const validation = new CompileScope(compilation?.owner);
|
|
482
|
+
try {
|
|
483
|
+
const args = decodeReplayData(callback.arguments, { resolveCapability }, validation);
|
|
484
|
+
if (!Array.isArray(args))
|
|
485
|
+
throw new TypeError("Invalid replay callback.");
|
|
486
|
+
callbackSizes.set(`${entry.id}/callback/${index + 1}`, measureSandboxData([args]));
|
|
487
|
+
}
|
|
488
|
+
finally {
|
|
489
|
+
validation.dispose();
|
|
490
|
+
}
|
|
452
491
|
}
|
|
453
492
|
}
|
|
454
493
|
let outcome;
|
|
@@ -457,7 +496,7 @@ function restoreReplayCalls(input, encodedOutcomes, callbackSizes, requiredHostC
|
|
|
457
496
|
typeof entry.outcome !== "object" ||
|
|
458
497
|
!["fulfilled", "rejected"].includes(entry.outcome.status))
|
|
459
498
|
throw new TypeError("Invalid replay call outcome.");
|
|
460
|
-
const value = decodeReplayData(entry.outcome.data, { resolveCapability });
|
|
499
|
+
const value = decodeReplayData(entry.outcome.data, { resolveCapability }, compilation);
|
|
461
500
|
encodedOutcomes.set(entry.id, structuredClone(entry.outcome));
|
|
462
501
|
outcome =
|
|
463
502
|
entry.outcome.status === "fulfilled"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ParseResult, SourceSpan } from "../parse.js";
|
|
2
2
|
import { type InterpreterYieldPoint } from "./async.js";
|
|
3
3
|
import type { GeneratorCompletion } from "./generator.js";
|
|
4
|
-
import { Budget } from "./budget.js";
|
|
4
|
+
import { Budget, type CompileOwner } from "./budget.js";
|
|
5
|
+
import { CompileScope } from "./regex/compile-guard.js";
|
|
5
6
|
import { type SandboxValue } from "./values.js";
|
|
6
7
|
import { Scope } from "./scope.js";
|
|
7
8
|
export type InterpreterValue = SandboxValue;
|
|
@@ -41,6 +42,8 @@ export type InterpreterResult = {
|
|
|
41
42
|
stats: InterpreterStats;
|
|
42
43
|
};
|
|
43
44
|
export type InterpretOptions = {
|
|
45
|
+
compilation?: CompileScope;
|
|
46
|
+
compileOwner?: CompileOwner;
|
|
44
47
|
captureReplayState?: () => unknown;
|
|
45
48
|
bindings?: Record<string, InterpreterValue>;
|
|
46
49
|
budget?: Budget;
|
|
@@ -6,6 +6,7 @@ import { attachErrorSpan, formatErrorStack, readErrorSpan, replaceErrorStack } f
|
|
|
6
6
|
import { evaluateArrowFunctionExpression, evaluateFunctionExpression, evaluateAwaitExpression, emitResumeBreakpoint, createInterpretedClosure, normalizeClosureResult } from "./async.js";
|
|
7
7
|
import { HostCallResumabilityError } from "./host-call.js";
|
|
8
8
|
import { Budget, SandboxError } from "./budget.js";
|
|
9
|
+
import { CompileScope, RegexCompileGuard } from "./regex/compile-guard.js";
|
|
9
10
|
import { coerceThrownValue, createCapturedException, evaluateThrowStatement as evaluateThrowStatementResult, evaluateTryStatement as evaluateTryStatementResult, isCapturedException, surfaceThrownValue } from "./exceptions.js";
|
|
10
11
|
import { callArrayMethod, getArrayMember, isArrayMethodName } from "./methods/array.js";
|
|
11
12
|
import { getFunctionMember } from "./methods/function.js";
|
|
@@ -23,7 +24,7 @@ import { isSandboxErrorConstructor, isSandboxErrorConstructorInstance } from "./
|
|
|
23
24
|
import { isSandboxMapConstructor, isSandboxSetConstructor } from "./globals/collections.js";
|
|
24
25
|
import { getFloat32Member, isFloat32ArrayConstructor, setFloat32Member } from "./globals/float32array.js";
|
|
25
26
|
import { isFloat32Array } from "./float32.js";
|
|
26
|
-
import { createSandboxRegex, allocateProducedSandboxValue, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet,
|
|
27
|
+
import { createSandboxRegex, allocateProducedSandboxValue, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet, reconcileCompiledValues } from "./values.js";
|
|
27
28
|
import { Scope } from "./scope.js";
|
|
28
29
|
import { hoistVarDeclarations } from "./var-hoist.js";
|
|
29
30
|
const taggedTemplateRawArrays = new WeakMap();
|
|
@@ -77,81 +78,90 @@ const dispatchTable = {
|
|
|
77
78
|
};
|
|
78
79
|
export async function interpret(node, options = {}) {
|
|
79
80
|
const budget = options.budget ?? new Budget();
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
reconcileDataBudget(budget, stats, scope, "hasValue" in evaluation && evaluation.hasValue ? evaluation.value : undefined);
|
|
119
|
-
if (evaluation.kind === "error") {
|
|
120
|
-
return {
|
|
121
|
-
ok: false,
|
|
122
|
-
error: evaluation.error,
|
|
123
|
-
snapshot,
|
|
124
|
-
stats
|
|
81
|
+
const operation = budget.acquireCompileOwner(false, options.compileOwner ?? options.compilation?.owner);
|
|
82
|
+
const compilation = new CompileScope(operation.owner, options.compilation);
|
|
83
|
+
try {
|
|
84
|
+
const scope = options.scope === undefined
|
|
85
|
+
? new Scope(options.bindings, undefined, undefined, {
|
|
86
|
+
chargeData: false
|
|
87
|
+
}, options.snapshot?.bindings).child({}, { functionBoundary: true })
|
|
88
|
+
: options.useScopeDirectly === true && options.bindings === undefined
|
|
89
|
+
? options.scope
|
|
90
|
+
: options.scope.child(options.bindings ?? {}, {
|
|
91
|
+
functionBoundary: true
|
|
92
|
+
});
|
|
93
|
+
const stats = { nodeVisits: 0 };
|
|
94
|
+
Object.defineProperties(stats, {
|
|
95
|
+
currentDataSize: { enumerable: false, value: 0, writable: true },
|
|
96
|
+
peakDataSize: { enumerable: false, value: 0, writable: true }
|
|
97
|
+
});
|
|
98
|
+
const activeLoopIterations = new Map();
|
|
99
|
+
const jobs = new SandboxJobQueue();
|
|
100
|
+
hoistVarDeclarations(node, scope);
|
|
101
|
+
const context = {
|
|
102
|
+
compilation,
|
|
103
|
+
budget,
|
|
104
|
+
callStack: [],
|
|
105
|
+
onYield: options.onYield,
|
|
106
|
+
captureReplayState: options.captureReplayState,
|
|
107
|
+
rootNode: node,
|
|
108
|
+
scope,
|
|
109
|
+
signal: options.signal,
|
|
110
|
+
stats,
|
|
111
|
+
activeLoopIterations,
|
|
112
|
+
restoredLoopIterations: new Map(Object.entries(options.snapshot?.loopIterations ?? {}).map(([nodeId, iteration]) => [
|
|
113
|
+
Number(nodeId),
|
|
114
|
+
iteration
|
|
115
|
+
])),
|
|
116
|
+
generatorResume: options.generatorResume,
|
|
117
|
+
generatorYield: options.generatorYield,
|
|
118
|
+
resumeTarget: { nodeId: options.snapshot?.resumeNodeId }
|
|
125
119
|
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
const evaluation = await withCancellationSignal(options.signal, () => jobs.run(() => evaluateNode(node, context)));
|
|
121
|
+
await jobs.drain();
|
|
122
|
+
const snapshot = scope.snapshot();
|
|
123
|
+
reconcileDataBudget(budget, stats, scope, "hasValue" in evaluation && evaluation.hasValue ? evaluation.value : undefined, compilation, options.compilation);
|
|
124
|
+
if (evaluation.kind === "error") {
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
error: evaluation.error,
|
|
128
|
+
snapshot,
|
|
129
|
+
stats
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (evaluation.kind === "throw") {
|
|
133
|
+
if (options.surfaceUnhandledThrows === true) {
|
|
134
|
+
throw surfaceThrownValue(evaluation.value, budget, evaluation.stackFrames, evaluation.span);
|
|
135
|
+
}
|
|
136
|
+
throw evaluation.value;
|
|
137
|
+
}
|
|
138
|
+
if ((evaluation.kind === "break" || evaluation.kind === "continue") &&
|
|
139
|
+
evaluation.label !== undefined) {
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
error: createError("LABEL_NOT_FOUND", evaluation.node ?? node, `Label '${evaluation.label}' not found`),
|
|
143
|
+
snapshot,
|
|
144
|
+
stats
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (evaluation.hasValue) {
|
|
148
|
+
return {
|
|
149
|
+
ok: true,
|
|
150
|
+
returnValue: evaluation.value,
|
|
151
|
+
snapshot,
|
|
152
|
+
stats
|
|
153
|
+
};
|
|
130
154
|
}
|
|
131
|
-
throw evaluation.value;
|
|
132
|
-
}
|
|
133
|
-
if ((evaluation.kind === "break" || evaluation.kind === "continue") &&
|
|
134
|
-
evaluation.label !== undefined) {
|
|
135
|
-
return {
|
|
136
|
-
ok: false,
|
|
137
|
-
error: createError("LABEL_NOT_FOUND", evaluation.node ?? node, `Label '${evaluation.label}' not found`),
|
|
138
|
-
snapshot,
|
|
139
|
-
stats
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
if (evaluation.hasValue) {
|
|
143
155
|
return {
|
|
144
156
|
ok: true,
|
|
145
|
-
returnValue: evaluation.value,
|
|
146
157
|
snapshot,
|
|
147
158
|
stats
|
|
148
159
|
};
|
|
149
160
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
161
|
+
finally {
|
|
162
|
+
compilation.dispose();
|
|
163
|
+
operation.release();
|
|
164
|
+
}
|
|
155
165
|
}
|
|
156
166
|
export { Scope } from "./scope.js";
|
|
157
167
|
async function evaluateNode(node, context) {
|
|
@@ -171,9 +181,20 @@ async function evaluateNode(node, context) {
|
|
|
171
181
|
error: createError("UNSUPPORTED_NODE", node, `Unsupported AST node type '${node.type}'.`)
|
|
172
182
|
};
|
|
173
183
|
}
|
|
184
|
+
const compilation = new CompileScope(context.compilation?.owner, context.compilation);
|
|
185
|
+
const evaluationContext = {
|
|
186
|
+
...context,
|
|
187
|
+
compilation,
|
|
188
|
+
get generatorResume() {
|
|
189
|
+
return context.generatorResume;
|
|
190
|
+
},
|
|
191
|
+
set generatorResume(value) {
|
|
192
|
+
context.generatorResume = value;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
174
195
|
try {
|
|
175
|
-
const result = await handler(node,
|
|
176
|
-
reconcileDataBudget(context.budget, context.stats, context.scope, "hasValue" in result && result.hasValue ? result.value : undefined);
|
|
196
|
+
const result = await handler(node, evaluationContext);
|
|
197
|
+
reconcileDataBudget(context.budget, context.stats, context.scope, "hasValue" in result && result.hasValue ? result.value : undefined, compilation, context.compilation);
|
|
177
198
|
return result;
|
|
178
199
|
}
|
|
179
200
|
catch (error) {
|
|
@@ -193,6 +214,7 @@ async function evaluateNode(node, context) {
|
|
|
193
214
|
const exception = isCapturedException(error)
|
|
194
215
|
? coerceThrownValue(error.reason, context.budget, error.stackFrames, node.span, error.sandbox)
|
|
195
216
|
: coerceThrownValue(error, context.budget, context.callStack, node.span, true);
|
|
217
|
+
reconcileDataBudget(context.budget, context.stats, context.scope, exception, compilation, context.compilation);
|
|
196
218
|
return {
|
|
197
219
|
kind: "throw",
|
|
198
220
|
hasValue: true,
|
|
@@ -201,9 +223,12 @@ async function evaluateNode(node, context) {
|
|
|
201
223
|
value: exception
|
|
202
224
|
};
|
|
203
225
|
}
|
|
226
|
+
finally {
|
|
227
|
+
compilation.dispose();
|
|
228
|
+
}
|
|
204
229
|
}
|
|
205
|
-
function reconcileDataBudget(budget, stats, scope, transient) {
|
|
206
|
-
budget
|
|
230
|
+
function reconcileDataBudget(budget, stats, scope, transient, compilation, parent) {
|
|
231
|
+
reconcileCompiledValues(budget, [...scope.retainedValues(), ...budget.retainedValues(), transient], compilation, parent, [transient]);
|
|
207
232
|
stats.currentDataSize = budget.currentDataSize;
|
|
208
233
|
stats.peakDataSize = budget.peakDataSize;
|
|
209
234
|
}
|
|
@@ -215,13 +240,23 @@ async function evaluatePrimitiveLiteral(node, context) {
|
|
|
215
240
|
value
|
|
216
241
|
};
|
|
217
242
|
}
|
|
218
|
-
async function evaluateRegexLiteral(node,
|
|
243
|
+
async function evaluateRegexLiteral(node, context) {
|
|
219
244
|
const lastSlash = node.raw.lastIndexOf("/");
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
245
|
+
const guard = new RegexCompileGuard(context.compilation);
|
|
246
|
+
try {
|
|
247
|
+
guard.checkLength(Math.max(0, lastSlash - 1));
|
|
248
|
+
guard.checkLength(node.raw.length - lastSlash - 1, true);
|
|
249
|
+
guard.allocate(Math.max(0, node.raw.length - 2));
|
|
250
|
+
guard.work(Math.max(0, node.raw.length - 2));
|
|
251
|
+
return {
|
|
252
|
+
kind: "normal",
|
|
253
|
+
hasValue: true,
|
|
254
|
+
value: createSandboxRegex(node.raw.slice(1, lastSlash), node.raw.slice(lastSlash + 1), 0, context.compilation)
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
guard.close();
|
|
259
|
+
}
|
|
225
260
|
}
|
|
226
261
|
async function evaluateEmptyStatement(_node, _context) {
|
|
227
262
|
return {
|
|
@@ -1836,7 +1871,7 @@ async function evaluateStringMethodCall(node, target, methodName, context) {
|
|
|
1836
1871
|
return {
|
|
1837
1872
|
kind: "normal",
|
|
1838
1873
|
hasValue: true,
|
|
1839
|
-
value: await callStringMethod(target, methodName, args.value, context.budget, (closure, closureArgs) => invokeSandboxClosure(closure, closureArgs, context, context.callStack))
|
|
1874
|
+
value: await callStringMethod(target, methodName, args.value, context.budget, (closure, closureArgs) => invokeSandboxClosure(closure, closureArgs, context, context.callStack), context.compilation)
|
|
1840
1875
|
};
|
|
1841
1876
|
}
|
|
1842
1877
|
catch (error) {
|
|
@@ -2333,6 +2368,7 @@ async function invokeSandboxClosure(callee, args, context, stack, span, thisValu
|
|
|
2333
2368
|
{
|
|
2334
2369
|
stack,
|
|
2335
2370
|
thisValue,
|
|
2371
|
+
compilation: context.compilation,
|
|
2336
2372
|
invokeClosure: (closure, argumentsList, receiver) => invokeSandboxClosure(closure, argumentsList, context, stack, span, receiver),
|
|
2337
2373
|
...(span === undefined ? {} : { span })
|
|
2338
2374
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Expression } from "../../parse.js";
|
|
2
2
|
import { Budget } from "../budget.js";
|
|
3
|
+
import { CompileScope } from "../regex/compile-guard.js";
|
|
3
4
|
import { type SandboxClosure, type SandboxValue } from "../values.js";
|
|
4
5
|
type StringMethodName = "at" | "charAt" | "charCodeAt" | "codePointAt" | "concat" | "endsWith" | "includes" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "matchAll" | "normalize" | "padEnd" | "padStart" | "repeat" | "replace" | "replaceAll" | "slice" | "search" | "split" | "startsWith" | "substr" | "substring" | "toLowerCase" | "toUpperCase" | "trim" | "trimEnd" | "trimStart";
|
|
5
6
|
export declare function getStringMember(value: string, property: string | number, budget: Budget): SandboxValue | undefined;
|
|
6
7
|
export declare function isStringMethodName(property: string | number): property is StringMethodName;
|
|
7
8
|
export declare function validateStringMethodArguments(_methodName: StringMethodName, _args: readonly Expression[]): void;
|
|
8
|
-
export declare function callStringMethod(value: string, methodName: StringMethodName, args: readonly SandboxValue[], budget: Budget, callClosure?: (closure: SandboxClosure, args: readonly SandboxValue[]) => Promise<SandboxValue
|
|
9
|
+
export declare function callStringMethod(value: string, methodName: StringMethodName, args: readonly SandboxValue[], budget: Budget, callClosure?: (closure: SandboxClosure, args: readonly SandboxValue[]) => Promise<SandboxValue>, parent?: CompileScope): SandboxValue | Promise<SandboxValue>;
|
|
9
10
|
export {};
|