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,5 +1,11 @@
|
|
|
1
1
|
import { replaceErrorStack } from "../error/shape.js";
|
|
2
2
|
export const REGEX_STEP_LIMIT = 2_000;
|
|
3
|
+
export const REGEX_COMPILE_LIMITS = Object.freeze({
|
|
4
|
+
sourceLength: 4_096,
|
|
5
|
+
flagsLength: 8,
|
|
6
|
+
depth: 64,
|
|
7
|
+
allocations: 16_384
|
|
8
|
+
});
|
|
3
9
|
const DEADLINE_CHECK_INTERVAL = 1_024;
|
|
4
10
|
export class SandboxError extends Error {
|
|
5
11
|
code;
|
|
@@ -42,6 +48,12 @@ export class Budget {
|
|
|
42
48
|
retainedDataSize = 0;
|
|
43
49
|
retainedData = new Map();
|
|
44
50
|
retainedValueSources = new Map();
|
|
51
|
+
compileGeneration = 0;
|
|
52
|
+
activeCompileOwner;
|
|
53
|
+
compileUses = 0;
|
|
54
|
+
provisionalScopes = 0;
|
|
55
|
+
compileTickets = new Map();
|
|
56
|
+
completedCompileTickets = new Set();
|
|
45
57
|
constructor(options = {}) {
|
|
46
58
|
this.deadline = normalizeDeadline(options.deadline);
|
|
47
59
|
this.limits = Object.freeze({
|
|
@@ -122,6 +134,126 @@ export class Budget {
|
|
|
122
134
|
for (const values of this.retainedValueSources.values())
|
|
123
135
|
yield* values();
|
|
124
136
|
}
|
|
137
|
+
acquireCompileOwner(reset = false, owner) {
|
|
138
|
+
if ((owner !== undefined &&
|
|
139
|
+
(owner.budget !== this || owner.generation !== this.compileGeneration)) ||
|
|
140
|
+
(this.activeCompileOwner !== undefined && this.activeCompileOwner !== owner) ||
|
|
141
|
+
(reset && (this.compileUses !== 0 || owner !== undefined))) {
|
|
142
|
+
throw new SandboxError("reentry");
|
|
143
|
+
}
|
|
144
|
+
if (reset)
|
|
145
|
+
this.reset();
|
|
146
|
+
const selected = owner ?? Object.freeze({ budget: this, generation: this.compileGeneration });
|
|
147
|
+
this.activeCompileOwner = selected;
|
|
148
|
+
this.compileUses += 1;
|
|
149
|
+
let released = false;
|
|
150
|
+
return {
|
|
151
|
+
owner: selected,
|
|
152
|
+
release: () => {
|
|
153
|
+
if (released)
|
|
154
|
+
return;
|
|
155
|
+
released = true;
|
|
156
|
+
this.compileUses -= 1;
|
|
157
|
+
if (this.compileUses === 0)
|
|
158
|
+
this.activeCompileOwner = undefined;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
createCompileTicket(owner) {
|
|
163
|
+
if (owner.budget !== this ||
|
|
164
|
+
owner.generation !== this.compileGeneration ||
|
|
165
|
+
this.activeCompileOwner !== owner) {
|
|
166
|
+
throw new SandboxError("reentry");
|
|
167
|
+
}
|
|
168
|
+
const ticket = Object.freeze({ owner });
|
|
169
|
+
this.compileTickets.set(ticket, 0);
|
|
170
|
+
return ticket;
|
|
171
|
+
}
|
|
172
|
+
compileTicketUsage(ticket) {
|
|
173
|
+
return ticket.owner.generation === this.compileGeneration
|
|
174
|
+
? (this.compileTickets.get(ticket) ?? 0)
|
|
175
|
+
: 0;
|
|
176
|
+
}
|
|
177
|
+
resizeCompileTicket(ticket, usage) {
|
|
178
|
+
if (ticket.owner.generation !== this.compileGeneration || !this.compileTickets.has(ticket)) {
|
|
179
|
+
throw new SandboxError("reentry");
|
|
180
|
+
}
|
|
181
|
+
this.setRetainedDataUsage(ticket, usage);
|
|
182
|
+
this.compileTickets.set(ticket, usage);
|
|
183
|
+
}
|
|
184
|
+
discardCompileTicket(ticket) {
|
|
185
|
+
if (ticket.owner.generation !== this.compileGeneration)
|
|
186
|
+
return;
|
|
187
|
+
const usage = this.compileTickets.get(ticket);
|
|
188
|
+
if (usage === undefined)
|
|
189
|
+
return;
|
|
190
|
+
this.compileTickets.delete(ticket);
|
|
191
|
+
this.completedCompileTickets.delete(ticket);
|
|
192
|
+
this.retainedData.delete(ticket);
|
|
193
|
+
this.retainedDataSize -= usage;
|
|
194
|
+
this.currentDataSize -= usage;
|
|
195
|
+
}
|
|
196
|
+
reconcileCompileData(usage, included, transferred = included, retainedOwner, complete = false) {
|
|
197
|
+
let includedUsage = 0;
|
|
198
|
+
let transferredUsage = 0;
|
|
199
|
+
let discardedUsage = 0;
|
|
200
|
+
const releasing = [];
|
|
201
|
+
const retained = new Set();
|
|
202
|
+
for (const ticket of included) {
|
|
203
|
+
const charge = this.compileTicketUsage(ticket);
|
|
204
|
+
includedUsage += charge;
|
|
205
|
+
if (charge > 0 &&
|
|
206
|
+
transferred.has(ticket) &&
|
|
207
|
+
(this.provisionalScopes === 0 || retainedOwner !== undefined)) {
|
|
208
|
+
releasing.push(ticket);
|
|
209
|
+
transferredUsage += charge;
|
|
210
|
+
}
|
|
211
|
+
else if (charge > 0 && complete && transferred.has(ticket)) {
|
|
212
|
+
retained.add(ticket);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (retainedOwner === undefined) {
|
|
216
|
+
for (const ticket of this.completedCompileTickets) {
|
|
217
|
+
if (included.has(ticket))
|
|
218
|
+
continue;
|
|
219
|
+
releasing.push(ticket);
|
|
220
|
+
discardedUsage += this.compileTicketUsage(ticket);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const oldOwnerUsage = retainedOwner === undefined ? 0 : (this.retainedData.get(retainedOwner) ?? 0);
|
|
224
|
+
const nextRetained = this.retainedDataSize -
|
|
225
|
+
transferredUsage -
|
|
226
|
+
discardedUsage +
|
|
227
|
+
(retainedOwner === undefined ? 0 : usage - oldOwnerUsage);
|
|
228
|
+
const measured = retainedOwner === undefined
|
|
229
|
+
? usage - includedUsage + transferredUsage
|
|
230
|
+
: this.currentDataSize - this.retainedDataSize;
|
|
231
|
+
const total = measured + nextRetained;
|
|
232
|
+
this.checkDataUsage(total);
|
|
233
|
+
for (const ticket of releasing) {
|
|
234
|
+
this.compileTickets.delete(ticket);
|
|
235
|
+
this.completedCompileTickets.delete(ticket);
|
|
236
|
+
this.retainedData.delete(ticket);
|
|
237
|
+
}
|
|
238
|
+
if (retainedOwner !== undefined) {
|
|
239
|
+
if (usage === 0)
|
|
240
|
+
this.retainedData.delete(retainedOwner);
|
|
241
|
+
else
|
|
242
|
+
this.retainedData.set(retainedOwner, usage);
|
|
243
|
+
}
|
|
244
|
+
this.retainedDataSize = nextRetained;
|
|
245
|
+
this.currentDataSize = total;
|
|
246
|
+
this.peakDataSize = Math.max(this.peakDataSize, total);
|
|
247
|
+
for (const ticket of retained)
|
|
248
|
+
this.completedCompileTickets.add(ticket);
|
|
249
|
+
return retained;
|
|
250
|
+
}
|
|
251
|
+
chargeDataUsage(usage) {
|
|
252
|
+
const total = this.currentDataSize + usage;
|
|
253
|
+
this.checkDataUsage(total);
|
|
254
|
+
this.currentDataSize = total;
|
|
255
|
+
this.peakDataSize = Math.max(this.peakDataSize, total);
|
|
256
|
+
}
|
|
125
257
|
provisionDataUsage(usage) {
|
|
126
258
|
const previous = this.currentDataSize;
|
|
127
259
|
const previousRetained = this.retainedDataSize;
|
|
@@ -129,11 +261,16 @@ export class Budget {
|
|
|
129
261
|
this.checkDataUsage(next);
|
|
130
262
|
this.currentDataSize = next;
|
|
131
263
|
this.peakDataSize = Math.max(this.peakDataSize, next);
|
|
264
|
+
const generation = this.compileGeneration;
|
|
265
|
+
this.provisionalScopes += 1;
|
|
132
266
|
let released = false;
|
|
133
267
|
return () => {
|
|
134
268
|
if (released)
|
|
135
269
|
return;
|
|
136
270
|
released = true;
|
|
271
|
+
if (generation !== this.compileGeneration)
|
|
272
|
+
return;
|
|
273
|
+
this.provisionalScopes -= 1;
|
|
137
274
|
this.currentDataSize = previous + this.retainedDataSize - previousRetained;
|
|
138
275
|
};
|
|
139
276
|
}
|
|
@@ -144,6 +281,12 @@ export class Budget {
|
|
|
144
281
|
return this.enterDepth();
|
|
145
282
|
}
|
|
146
283
|
reset() {
|
|
284
|
+
if (this.compileUses !== 0)
|
|
285
|
+
throw new SandboxError("reentry");
|
|
286
|
+
this.compileGeneration += 1;
|
|
287
|
+
this.provisionalScopes = 0;
|
|
288
|
+
this.compileTickets.clear();
|
|
289
|
+
this.completedCompileTickets.clear();
|
|
147
290
|
this.stepsUsed = 0;
|
|
148
291
|
this.peakCallDepth = 0;
|
|
149
292
|
this.currentCallDepth = 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Budget } from "../budget.js";
|
|
1
|
+
import type { Budget, CompileOwner } from "../budget.js";
|
|
2
2
|
import type { HostCallJournal } from "../host-call.js";
|
|
3
3
|
import { type SandboxObject } from "../values.js";
|
|
4
4
|
export type ConsoleSink = {
|
|
@@ -7,6 +7,7 @@ export type ConsoleSink = {
|
|
|
7
7
|
};
|
|
8
8
|
export type ConsoleJsonGlobalsOptions = {
|
|
9
9
|
budget: Budget;
|
|
10
|
+
compileOwner?: CompileOwner;
|
|
10
11
|
sink?: ConsoleSink;
|
|
11
12
|
hostCalls?: HostCallJournal;
|
|
12
13
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CompileScope } from "../regex/compile-guard.js";
|
|
1
2
|
import { wrapCallerInjectedBindings } from "../host-bridge.js";
|
|
2
3
|
import { allocateProducedSandboxValue, createSandboxClosure, deepCopyFromSandbox, isSandboxClosure, isSandboxPromise } from "../values.js";
|
|
3
4
|
export function createConsoleJsonGlobals(options) {
|
|
@@ -19,17 +20,33 @@ export function createConsoleJsonGlobals(options) {
|
|
|
19
20
|
? {
|
|
20
21
|
error: createSandboxClosure({
|
|
21
22
|
sandbox: true,
|
|
22
|
-
call: async (args) => {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
call: async (args, context) => {
|
|
24
|
+
const operation = options.budget.acquireCompileOwner(false, options.compileOwner ?? context?.compilation?.owner);
|
|
25
|
+
const compilation = new CompileScope(operation.owner);
|
|
26
|
+
try {
|
|
27
|
+
sink.error(...args.map((value) => deepCopyFromSandbox(value, { compilation })));
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
compilation.dispose();
|
|
32
|
+
operation.release();
|
|
33
|
+
}
|
|
25
34
|
},
|
|
26
35
|
name: "error"
|
|
27
36
|
}),
|
|
28
37
|
log: createSandboxClosure({
|
|
29
38
|
sandbox: true,
|
|
30
|
-
call: async (args) => {
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
call: async (args, context) => {
|
|
40
|
+
const operation = options.budget.acquireCompileOwner(false, options.compileOwner ?? context?.compilation?.owner);
|
|
41
|
+
const compilation = new CompileScope(operation.owner);
|
|
42
|
+
try {
|
|
43
|
+
sink.log(...args.map((value) => deepCopyFromSandbox(value, { compilation })));
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
compilation.dispose();
|
|
48
|
+
operation.release();
|
|
49
|
+
}
|
|
33
50
|
},
|
|
34
51
|
name: "log"
|
|
35
52
|
})
|
|
@@ -45,6 +62,7 @@ export function createConsoleJsonGlobals(options) {
|
|
|
45
62
|
}
|
|
46
63
|
}, {
|
|
47
64
|
budget: options.budget,
|
|
65
|
+
compileOwner: options.compileOwner,
|
|
48
66
|
hostCalls: options.hostCalls,
|
|
49
67
|
moduleId: "<console>"
|
|
50
68
|
})
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
-
import { createSandboxClosure, createSandboxRegex } from "../values.js";
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { createSandboxClosure, createSandboxRegex, reconcileCompiledValues } from "../values.js";
|
|
2
|
+
import { CompileScope } from "../regex/compile-guard.js";
|
|
3
|
+
import { SandboxError } from "../budget.js";
|
|
4
|
+
export function createRegexGlobals(owner) {
|
|
5
|
+
const construct = (args, context) => {
|
|
6
|
+
const selected = owner ?? context?.compilation?.owner;
|
|
7
|
+
if (context?.compilation?.owner !== undefined && selected !== context.compilation.owner) {
|
|
8
|
+
throw new SandboxError("reentry");
|
|
9
|
+
}
|
|
10
|
+
const operation = selected?.budget.acquireCompileOwner(false, selected);
|
|
11
|
+
const compilation = context?.compilation ?? new CompileScope(selected);
|
|
12
|
+
try {
|
|
13
|
+
const regex = createSandboxRegex(String(args[0] ?? ""), String(args[1] ?? ""), 0, compilation);
|
|
14
|
+
if (context?.compilation === undefined && selected !== undefined) {
|
|
15
|
+
reconcileCompiledValues(selected.budget, [regex], compilation);
|
|
16
|
+
}
|
|
17
|
+
return regex;
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
if (context?.compilation === undefined)
|
|
21
|
+
compilation.dispose();
|
|
22
|
+
operation?.release();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
4
25
|
return {
|
|
5
26
|
RegExp: createSandboxClosure({ sandbox: true, name: "RegExp", call: construct, construct })
|
|
6
27
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Budget } from "./budget.js";
|
|
1
|
+
import { type Budget, type CompileOwner } from "./budget.js";
|
|
2
2
|
import { type PendingHostCallPolicyMode } from "../snapshot/policy.js";
|
|
3
3
|
import { type HostCallJournal, type HostCallOutcome } from "./host-call.js";
|
|
4
4
|
import { type SandboxClosure, type SandboxValue } from "./values.js";
|
|
@@ -10,6 +10,7 @@ type HostBridgeOptions = {
|
|
|
10
10
|
registerCapabilities?: boolean;
|
|
11
11
|
capabilityPath?: readonly string[];
|
|
12
12
|
budget: Budget;
|
|
13
|
+
compileOwner?: CompileOwner;
|
|
13
14
|
hostCalls?: HostCallJournal;
|
|
14
15
|
moduleId?: string;
|
|
15
16
|
operation?: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { normalizeClosureResult } from "./async.js";
|
|
2
2
|
import { attachErrorSpan, replaceErrorStack } from "../error/shape.js";
|
|
3
3
|
import { SandboxError } from "./budget.js";
|
|
4
|
+
import { CompileScope } from "./regex/compile-guard.js";
|
|
4
5
|
import { checkFloat32Allocation, copyFloat32Storage, float32DataProperties, float32Storage, isFloat32Array } from "./float32.js";
|
|
5
6
|
import { createSubsetErrorValue } from "./exceptions.js";
|
|
6
7
|
import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
|
|
@@ -31,16 +32,22 @@ export function readHostOperationPolicy(operation) {
|
|
|
31
32
|
return hostOperationPolicies.get(operation);
|
|
32
33
|
}
|
|
33
34
|
export function wrapCallerInjectedBindings(bindings, options) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
const operation = options.budget.acquireCompileOwner(false, options.compileOwner);
|
|
36
|
+
options = { ...options, registerCapabilities: true, compileOwner: operation.owner };
|
|
37
|
+
try {
|
|
38
|
+
const state = { seen: new WeakMap() };
|
|
39
|
+
const copied = Object.fromEntries(Object.entries(bindings).map(([name, value]) => [
|
|
40
|
+
name,
|
|
41
|
+
typeof value === "function"
|
|
42
|
+
? wrapCallerInjectedFunction(name, value, { ...options, capabilityPath: [name] }, state)
|
|
43
|
+
: copyHostValueToSandbox(value, [], { ...options, operation: name, capabilityPath: [name] }, state, "<root>")
|
|
44
|
+
]));
|
|
45
|
+
options.budget.chargeDataUsage(measureSandboxData(Object.values(copied)));
|
|
46
|
+
return copied;
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
operation.release();
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
52
|
function wrapCallerInjectedFunction(name, value, options, state) {
|
|
46
53
|
const existing = state.seen.get(value) ?? options.hostCalls?.nativeClosures.get(value);
|
|
@@ -52,6 +59,8 @@ function wrapCallerInjectedFunction(name, value, options, state) {
|
|
|
52
59
|
...(isAsyncFunction(callable) ? { async: true } : {}),
|
|
53
60
|
cancellationSignal: options.signal,
|
|
54
61
|
call: (args, context) => {
|
|
62
|
+
const operationLease = options.budget.acquireCompileOwner(false, options.compileOwner);
|
|
63
|
+
const compilation = new CompileScope(operationLease.owner);
|
|
55
64
|
try {
|
|
56
65
|
if (options.signal?.aborted)
|
|
57
66
|
throw readAbortReason(options.signal);
|
|
@@ -67,7 +76,8 @@ function wrapCallerInjectedFunction(name, value, options, state) {
|
|
|
67
76
|
restored: []
|
|
68
77
|
};
|
|
69
78
|
const hostArgs = deepCopyFromSandbox([...args], {
|
|
70
|
-
|
|
79
|
+
compilation,
|
|
80
|
+
wrapClosure: (closure) => wrapSandboxClosureForHost(closure, stackFrames, options.budget, operationLease.owner, callbacks)
|
|
71
81
|
});
|
|
72
82
|
const hostCalls = options.hostCalls;
|
|
73
83
|
const operation = options.operation ?? bindingName;
|
|
@@ -112,13 +122,27 @@ function wrapCallerInjectedFunction(name, value, options, state) {
|
|
|
112
122
|
return {
|
|
113
123
|
token,
|
|
114
124
|
start: () => {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
const callbackLease = options.budget.acquireCompileOwner(false, options.compileOwner);
|
|
126
|
+
const callbackCompilation = new CompileScope(callbackLease.owner);
|
|
127
|
+
try {
|
|
128
|
+
const callback = callbacks.entries.get(invocation.id);
|
|
129
|
+
if (callback === undefined)
|
|
130
|
+
throw new TypeError("Missing restored host callback.");
|
|
131
|
+
const args = decodeReplayData(invocation.arguments, {
|
|
132
|
+
resolveCapability: hostCalls.resolveCapability
|
|
133
|
+
}, callbackCompilation);
|
|
134
|
+
void callback(args, token)
|
|
135
|
+
.then(resolve, reject)
|
|
136
|
+
.finally(() => {
|
|
137
|
+
callbackCompilation.dispose();
|
|
138
|
+
callbackLease.release();
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
callbackCompilation.dispose();
|
|
143
|
+
callbackLease.release();
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
122
146
|
}
|
|
123
147
|
};
|
|
124
148
|
}));
|
|
@@ -128,12 +152,17 @@ function wrapCallerInjectedFunction(name, value, options, state) {
|
|
|
128
152
|
return executeHostCall(issued.record, issued.restored, () => invokeHostCallback(() => Reflect.apply(callable, undefined, hostArgs), options), stackFrames, options, context?.span, callbacks, hostOperationReplayHandlers.get(value)?.bind(undefined, hostArgs));
|
|
129
153
|
}
|
|
130
154
|
catch (error) {
|
|
155
|
+
compilation.dispose();
|
|
131
156
|
if (isFatalBridgeError(error) ||
|
|
132
157
|
(typeof error === "object" && error !== null && sandboxErrorTypes.has(error))) {
|
|
133
158
|
throw error;
|
|
134
159
|
}
|
|
135
160
|
throw createHostErrorValue(error, context?.stack ?? [], options.budget, context?.span);
|
|
136
161
|
}
|
|
162
|
+
finally {
|
|
163
|
+
compilation.dispose();
|
|
164
|
+
operationLease.release();
|
|
165
|
+
}
|
|
137
166
|
},
|
|
138
167
|
name: bindingName,
|
|
139
168
|
properties: (closure) => {
|
|
@@ -345,7 +374,7 @@ function copyHostResultToSandbox(result, stackFrames, options) {
|
|
|
345
374
|
const value = copyHostValueToSandbox(result, stackFrames, { ...options, registerCapabilities: false }, {
|
|
346
375
|
seen: new WeakMap()
|
|
347
376
|
}, "<root>");
|
|
348
|
-
options.budget.
|
|
377
|
+
options.budget.chargeDataUsage(measureSandboxData([value]));
|
|
349
378
|
return value;
|
|
350
379
|
}
|
|
351
380
|
function createHostErrorValue(reason, stackFrames, budget, span, state = { seen: new WeakMap() }, chargeBudget = false) {
|
|
@@ -375,7 +404,7 @@ function createHostErrorValue(reason, stackFrames, budget, span, state = { seen:
|
|
|
375
404
|
if (data !== undefined) {
|
|
376
405
|
const copied = copyHostValueToSandbox(data, stackFrames, { budget, errorData: true }, state, "<error>");
|
|
377
406
|
Object.defineProperties(error, Object.getOwnPropertyDescriptors(copied));
|
|
378
|
-
budget.
|
|
407
|
+
budget.chargeDataUsage(measureSandboxData([error]));
|
|
379
408
|
}
|
|
380
409
|
}
|
|
381
410
|
attachErrorSpan(error, span);
|
|
@@ -400,21 +429,24 @@ function copyHostErrorMetadata(error, reason, budget, chargeBudget) {
|
|
|
400
429
|
resumeChecks?.();
|
|
401
430
|
}
|
|
402
431
|
}
|
|
403
|
-
function wrapSandboxClosureForHost(closure, stackFrames, budget, callbacks) {
|
|
432
|
+
function wrapSandboxClosureForHost(closure, stackFrames, budget, compileOwner, callbacks) {
|
|
404
433
|
const existing = callbacks?.seen.get(closure);
|
|
405
434
|
if (existing !== undefined)
|
|
406
435
|
return existing;
|
|
407
436
|
const id = (callbacks?.entries.size ?? 0) + 1;
|
|
408
437
|
const invoke = async (sandboxArgs, token) => {
|
|
438
|
+
const operation = budget.acquireCompileOwner(false, compileOwner);
|
|
439
|
+
const compilation = new CompileScope(operation.owner);
|
|
409
440
|
let leaveRunning;
|
|
410
441
|
let leaveCall;
|
|
411
|
-
const wrapClosure = (nestedClosure) => wrapSandboxClosureForHost(nestedClosure, stackFrames, budget, callbacks);
|
|
442
|
+
const wrapClosure = (nestedClosure) => wrapSandboxClosureForHost(nestedClosure, stackFrames, budget, compileOwner, callbacks);
|
|
412
443
|
try {
|
|
413
444
|
leaveRunning = enterRunningState(closure);
|
|
414
445
|
leaveCall = budget.enterCall();
|
|
415
446
|
let result;
|
|
416
447
|
try {
|
|
417
448
|
result = closure.call(sandboxArgs, {
|
|
449
|
+
compilation,
|
|
418
450
|
stack: stackFrames,
|
|
419
451
|
thisValue: undefined
|
|
420
452
|
});
|
|
@@ -422,12 +454,14 @@ function wrapSandboxClosureForHost(closure, stackFrames, budget, callbacks) {
|
|
|
422
454
|
catch (error) {
|
|
423
455
|
if (isSandboxLikeValue(error)) {
|
|
424
456
|
throw deepCopyFromSandbox(error, {
|
|
457
|
+
compilation,
|
|
425
458
|
wrapClosure
|
|
426
459
|
});
|
|
427
460
|
}
|
|
428
461
|
throw error;
|
|
429
462
|
}
|
|
430
463
|
return await deepCopyFromSandbox(normalizeClosureResult(result, budget), {
|
|
464
|
+
compilation,
|
|
431
465
|
wrapClosure
|
|
432
466
|
});
|
|
433
467
|
}
|
|
@@ -437,6 +471,8 @@ function wrapSandboxClosureForHost(closure, stackFrames, budget, callbacks) {
|
|
|
437
471
|
throw error;
|
|
438
472
|
}
|
|
439
473
|
finally {
|
|
474
|
+
compilation.dispose();
|
|
475
|
+
operation.release();
|
|
440
476
|
leaveCall?.();
|
|
441
477
|
leaveRunning?.();
|
|
442
478
|
if (token !== undefined)
|
|
@@ -444,41 +480,48 @@ function wrapSandboxClosureForHost(closure, stackFrames, budget, callbacks) {
|
|
|
444
480
|
}
|
|
445
481
|
};
|
|
446
482
|
const wrapped = async (...args) => {
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
:
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
483
|
+
const operation = budget.acquireCompileOwner(false, compileOwner);
|
|
484
|
+
try {
|
|
485
|
+
const sandboxArgs = copyHostValueToSandbox([...args], stackFrames, {
|
|
486
|
+
budget,
|
|
487
|
+
compileOwner,
|
|
488
|
+
hostCalls: callbacks?.journal
|
|
489
|
+
}, { seen: new WeakMap() }, "<callback>");
|
|
490
|
+
const restored = callbacks?.nextReissuedInvocation === undefined
|
|
491
|
+
? undefined
|
|
492
|
+
: callbacks.restored[callbacks.nextReissuedInvocation++];
|
|
493
|
+
if (restored !== undefined) {
|
|
494
|
+
if (restored.id !== id ||
|
|
495
|
+
JSON.stringify(encodeReplayData(sandboxArgs, {
|
|
496
|
+
identifyCapability: callbacks?.journal?.identifyCapability
|
|
497
|
+
})) !== JSON.stringify(restored.arguments)) {
|
|
498
|
+
const error = new HostCallResumabilityError(callbacks.record, "external-reconciliation", "Host callback arguments changed while re-issuing the operation; external reconciliation is required.");
|
|
499
|
+
promiseReplayContext.getStore()?.fail(error);
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
502
|
+
return await restored.result;
|
|
462
503
|
}
|
|
463
|
-
|
|
504
|
+
const replay = promiseReplayContext.getStore();
|
|
505
|
+
const catchUp = replay?.waitForLiveExecution();
|
|
506
|
+
if (catchUp !== undefined)
|
|
507
|
+
await catchUp;
|
|
508
|
+
const token = callbacks?.record === undefined
|
|
509
|
+
? undefined
|
|
510
|
+
: callbacks.journal?.recordCallback(callbacks.record, id, sandboxArgs, replay?.currentStep ?? 0);
|
|
511
|
+
if (token !== undefined)
|
|
512
|
+
replay?.beginCallback(token);
|
|
513
|
+
const pending = invoke(sandboxArgs, token);
|
|
514
|
+
callbacks?.active.add(pending);
|
|
515
|
+
void pending.then(() => {
|
|
516
|
+
callbacks?.active.delete(pending);
|
|
517
|
+
}, () => {
|
|
518
|
+
callbacks?.active.delete(pending);
|
|
519
|
+
});
|
|
520
|
+
return await pending;
|
|
521
|
+
}
|
|
522
|
+
finally {
|
|
523
|
+
operation.release();
|
|
464
524
|
}
|
|
465
|
-
const replay = promiseReplayContext.getStore();
|
|
466
|
-
const catchUp = replay?.waitForLiveExecution();
|
|
467
|
-
if (catchUp !== undefined)
|
|
468
|
-
await catchUp;
|
|
469
|
-
const token = callbacks?.record === undefined
|
|
470
|
-
? undefined
|
|
471
|
-
: callbacks.journal?.recordCallback(callbacks.record, id, sandboxArgs, replay?.currentStep ?? 0);
|
|
472
|
-
if (token !== undefined)
|
|
473
|
-
replay?.beginCallback(token);
|
|
474
|
-
const pending = invoke(sandboxArgs, token);
|
|
475
|
-
callbacks?.active.add(pending);
|
|
476
|
-
void pending.then(() => {
|
|
477
|
-
callbacks?.active.delete(pending);
|
|
478
|
-
}, () => {
|
|
479
|
-
callbacks?.active.delete(pending);
|
|
480
|
-
});
|
|
481
|
-
return pending;
|
|
482
525
|
};
|
|
483
526
|
if (closure.length !== undefined)
|
|
484
527
|
Object.defineProperty(wrapped, "length", { value: closure.length });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SandboxClosure, type SandboxValue } from "./values.js";
|
|
2
|
-
import type { Budget } from "./budget.js";
|
|
2
|
+
import type { Budget, CompileOwner } from "./budget.js";
|
|
3
3
|
import { type ReplayData } from "../snapshot/replay-data.js";
|
|
4
4
|
import { type PendingHostCallPolicyMode } from "../snapshot/policy.js";
|
|
5
5
|
export type HostCallLifecycle = "created" | "running" | "settled" | "consumed" | "cancelled";
|
|
@@ -77,6 +77,7 @@ export declare class HostCallJournal {
|
|
|
77
77
|
private readonly sourceHash;
|
|
78
78
|
private readonly resumeProvider?;
|
|
79
79
|
private budget?;
|
|
80
|
+
private compileOwner?;
|
|
80
81
|
readonly runId: string;
|
|
81
82
|
private nextCall;
|
|
82
83
|
private readonly records;
|
|
@@ -96,7 +97,7 @@ export declare class HostCallJournal {
|
|
|
96
97
|
private readonly capabilityWaiters;
|
|
97
98
|
readonly identifyCapability: (key: SandboxClosure) => string | undefined;
|
|
98
99
|
readonly resolveCapability: (id: string) => SandboxClosure;
|
|
99
|
-
constructor(sourceHash: string, records?: readonly HostCallRecord[], resumeProvider?: HostCallResumeProvider | undefined, replay?: unknown, budget?: Budget | undefined);
|
|
100
|
+
constructor(sourceHash: string, records?: readonly HostCallRecord[], resumeProvider?: HostCallResumeProvider | undefined, replay?: unknown, budget?: Budget | undefined, compileOwner?: CompileOwner | undefined);
|
|
100
101
|
issue(input: {
|
|
101
102
|
moduleId: string;
|
|
102
103
|
operation: string;
|