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.
Files changed (57) hide show
  1. package/dist/index.js +14418 -13628
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-73LWNNUG.js → chunk-WGS5B4OT.js} +14505 -13744
  6. package/packages/safe-js/dist/chunks/chunk-WGS5B4OT.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-QCJ7UAMC.js → chunk-ZGWWEL43.js} +2 -2
  8. package/packages/safe-js/dist/cli.js +2 -2
  9. package/packages/safe-js/dist/core.js +1 -1
  10. package/packages/safe-js/dist/index.d.ts +11 -3
  11. package/packages/safe-js/dist/index.js +14 -6
  12. package/packages/safe-js/dist/index.js.map +4 -4
  13. package/packages/safe-js/dist/interp/async.d.ts +2 -0
  14. package/packages/safe-js/dist/interp/async.js +31 -15
  15. package/packages/safe-js/dist/interp/budget.d.ts +29 -0
  16. package/packages/safe-js/dist/interp/budget.js +143 -0
  17. package/packages/safe-js/dist/interp/globals/console-json.d.ts +2 -1
  18. package/packages/safe-js/dist/interp/globals/console-json.js +24 -6
  19. package/packages/safe-js/dist/interp/globals/regex.d.ts +2 -1
  20. package/packages/safe-js/dist/interp/globals/regex.js +24 -3
  21. package/packages/safe-js/dist/interp/host-bridge.d.ts +2 -1
  22. package/packages/safe-js/dist/interp/host-bridge.js +98 -55
  23. package/packages/safe-js/dist/interp/host-call.d.ts +3 -2
  24. package/packages/safe-js/dist/interp/host-call.js +91 -52
  25. package/packages/safe-js/dist/interp/interpreter.d.ts +4 -1
  26. package/packages/safe-js/dist/interp/interpreter.js +115 -79
  27. package/packages/safe-js/dist/interp/methods/string.d.ts +2 -1
  28. package/packages/safe-js/dist/interp/methods/string.js +111 -100
  29. package/packages/safe-js/dist/interp/regex/compile-guard.d.ts +33 -0
  30. package/packages/safe-js/dist/interp/regex/compile-guard.js +179 -0
  31. package/packages/safe-js/dist/interp/regex/parse.d.ts +2 -1
  32. package/packages/safe-js/dist/interp/regex/parse.js +87 -16
  33. package/packages/safe-js/dist/interp/values.d.ts +7 -2
  34. package/packages/safe-js/dist/interp/values.js +91 -9
  35. package/packages/safe-js/dist/modules/registry.d.ts +2 -1
  36. package/packages/safe-js/dist/modules/registry.js +1 -0
  37. package/packages/safe-js/dist/parse/hash.d.ts +2 -1
  38. package/packages/safe-js/dist/parse/hash.js +7 -4
  39. package/packages/safe-js/dist/parse/parser.d.ts +5 -3
  40. package/packages/safe-js/dist/parse/parser.js +53 -27
  41. package/packages/safe-js/dist/parse/tokenizer.d.ts +2 -0
  42. package/packages/safe-js/dist/parse/tokenizer.js +48 -26
  43. package/packages/safe-js/dist/parse.d.ts +3 -1
  44. package/packages/safe-js/dist/parse.js +3 -1
  45. package/packages/safe-js/dist/restore.d.ts +2 -1
  46. package/packages/safe-js/dist/restore.js +3 -3
  47. package/packages/safe-js/dist/run.js +289 -263
  48. package/packages/safe-js/dist/snapshot/migration.d.ts +3 -2
  49. package/packages/safe-js/dist/snapshot/migration.js +12 -5
  50. package/packages/safe-js/dist/snapshot/replay-data.d.ts +2 -1
  51. package/packages/safe-js/dist/snapshot/replay-data.js +172 -162
  52. package/packages/safe-js/dist/snapshot/replay-inputs.d.ts +2 -1
  53. package/packages/safe-js/dist/snapshot/replay-inputs.js +18 -11
  54. package/packages/safe-js/dist/snapshot/restore.d.ts +2 -2
  55. package/packages/safe-js/dist/snapshot/restore.js +142 -113
  56. package/packages/safe-js/dist/chunks/chunk-73LWNNUG.js.map +0 -7
  57. /package/packages/safe-js/dist/chunks/{chunk-QCJ7UAMC.js.map → chunk-ZGWWEL43.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.recordedReplay = replay !== undefined;
67
- if (replay !== undefined) {
68
- const replayRecords = restoreReplayCalls(replay, this.encodedOutcomes, this.callbackSizes, this.requiredHostCapabilities);
69
- const restoredRunId = replayRecords[0]?.runId ?? records[0]?.runId ?? randomUUID();
70
- validateRestoredRecords(records, restoredRunId, sourceHash);
71
- for (const record of records) {
72
- const replayRecord = replayRecords[readCallOrdinal(record) - 1];
73
- if (replayRecord === undefined ||
74
- !callIdentityMatches(record, replayRecord) ||
75
- record.lifecycle !== replayRecord.lifecycle) {
76
- throw new HostCallResumabilityError(record, "reset", `Host call ${record.id} conflicts with the replay journal; reset is required.`);
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
- records = replayRecords;
80
- }
81
- this.runId = records[0]?.runId ?? randomUUID();
82
- this.records = records.map((record) => ({
83
- ...record,
84
- ...(record.functions === undefined ? {} : { functions: [...record.functions] }),
85
- ...(record.callbacks === undefined ? {} : { callbacks: structuredClone(record.callbacks) }),
86
- ...(record.outcome === undefined ? {} : { outcome: copyOutcome(record.outcome) })
87
- }));
88
- validateRestoredRecords(this.records, this.runId, sourceHash);
89
- this.retainedSize = this.records.length;
90
- try {
91
- this.budget?.setRetainedDataUsage(this, this.retainedSize);
92
- for (const record of this.records) {
93
- if (record.outcome !== undefined)
94
- this.retainOutcome(record, record.outcome);
95
- for (const [index, callback] of (record.callbacks ?? []).entries()) {
96
- this.retainedSize +=
97
- 1 +
98
- (this.callbackSizes.get(`${record.id}/callback/${index + 1}`) ??
99
- measureSandboxData([decodeReplayData(callback.arguments)]));
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
- this.budget?.setRetainedDataUsage(this, this.retainedSize);
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
- catch (error) {
105
- this.dispose();
106
- throw error;
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 size = measureSandboxData([outcome.status === "fulfilled" ? outcome.value : outcome.reason], { ignoreClosures: true });
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
- this.budget?.setRetainedDataUsage(this, retainedSize);
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
- this.budget?.setRetainedDataUsage(this, this.retainedSize);
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 value = decodeReplayData(encoded.data, { resolveCapability: this.resolveCapability });
344
- return encoded.status === "fulfilled"
345
- ? { status: "fulfilled", value }
346
- : { status: "rejected", reason: value };
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
- callbackSizes.set(`${entry.id}/callback/${index + 1}`, measureSandboxData([decodeReplayData(callback.arguments, { resolveCapability })]));
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, measureSandboxData } from "./values.js";
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 scope = options.scope === undefined
81
- ? new Scope(options.bindings, undefined, undefined, {
82
- chargeData: false
83
- }, options.snapshot?.bindings).child({}, { functionBoundary: true })
84
- : options.useScopeDirectly === true && options.bindings === undefined
85
- ? options.scope
86
- : options.scope.child(options.bindings ?? {}, {
87
- functionBoundary: true
88
- });
89
- const stats = { nodeVisits: 0 };
90
- Object.defineProperties(stats, {
91
- currentDataSize: { enumerable: false, value: 0, writable: true },
92
- peakDataSize: { enumerable: false, value: 0, writable: true }
93
- });
94
- const activeLoopIterations = new Map();
95
- const jobs = new SandboxJobQueue();
96
- hoistVarDeclarations(node, scope);
97
- const context = {
98
- budget,
99
- callStack: [],
100
- onYield: options.onYield,
101
- captureReplayState: options.captureReplayState,
102
- rootNode: node,
103
- scope,
104
- signal: options.signal,
105
- stats,
106
- activeLoopIterations,
107
- restoredLoopIterations: new Map(Object.entries(options.snapshot?.loopIterations ?? {}).map(([nodeId, iteration]) => [
108
- Number(nodeId),
109
- iteration
110
- ])),
111
- generatorResume: options.generatorResume,
112
- generatorYield: options.generatorYield,
113
- resumeTarget: { nodeId: options.snapshot?.resumeNodeId }
114
- };
115
- const evaluation = await withCancellationSignal(options.signal, () => jobs.run(() => evaluateNode(node, context)));
116
- await jobs.drain();
117
- const snapshot = scope.snapshot();
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
- if (evaluation.kind === "throw") {
128
- if (options.surfaceUnhandledThrows === true) {
129
- throw surfaceThrownValue(evaluation.value, budget, evaluation.stackFrames, evaluation.span);
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
- return {
151
- ok: true,
152
- snapshot,
153
- stats
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, context);
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.reconcileDataUsage(measureSandboxData([...scope.retainedValues(), ...budget.retainedValues(), transient]));
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, _context) {
243
+ async function evaluateRegexLiteral(node, context) {
219
244
  const lastSlash = node.raw.lastIndexOf("/");
220
- return {
221
- kind: "normal",
222
- hasValue: true,
223
- value: createSandboxRegex(node.raw.slice(1, lastSlash), node.raw.slice(lastSlash + 1))
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>): 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 {};