poe-code 14.0.5 → 14.0.7
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 +1604 -255
- 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-B7H4ZL65.js → chunk-BWVASPJX.js} +2099 -783
- package/packages/safe-js/dist/chunks/chunk-BWVASPJX.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-7B7BKWVF.js → chunk-ETKM3KPA.js} +2 -2
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.d.ts +3 -0
- package/packages/safe-js/dist/core.js +5 -1
- package/packages/safe-js/dist/extensions.d.ts +44 -0
- package/packages/safe-js/dist/extensions.js +83 -0
- package/packages/safe-js/dist/index.d.ts +4 -1
- package/packages/safe-js/dist/index.js +6 -2
- package/packages/safe-js/dist/index.js.map +2 -2
- package/packages/safe-js/dist/interp/async.d.ts +1 -0
- package/packages/safe-js/dist/interp/date.d.ts +19 -0
- package/packages/safe-js/dist/interp/date.js +129 -0
- package/packages/safe-js/dist/interp/globals/console-json.js +5 -1
- package/packages/safe-js/dist/interp/globals/date.d.ts +10 -0
- package/packages/safe-js/dist/interp/globals/date.js +95 -0
- package/packages/safe-js/dist/interp/globals/object-array.d.ts +2 -1
- package/packages/safe-js/dist/interp/globals/object-array.js +15 -1
- package/packages/safe-js/dist/interp/globals.d.ts +34 -0
- package/packages/safe-js/dist/interp/globals.js +24 -0
- package/packages/safe-js/dist/interp/host-bridge.d.ts +18 -1
- package/packages/safe-js/dist/interp/host-bridge.js +39 -5
- package/packages/safe-js/dist/interp/host-call.js +4 -0
- package/packages/safe-js/dist/interp/host-capabilities.d.ts +50 -0
- package/packages/safe-js/dist/interp/host-capabilities.js +151 -0
- package/packages/safe-js/dist/interp/interpreter.d.ts +4 -0
- package/packages/safe-js/dist/interp/interpreter.js +41 -6
- package/packages/safe-js/dist/interp/object-model.js +7 -0
- package/packages/safe-js/dist/interp/string-coercion.js +3 -0
- package/packages/safe-js/dist/interp/values.d.ts +2 -1
- package/packages/safe-js/dist/interp/values.js +36 -0
- package/packages/safe-js/dist/lint/rules/known-globals.d.ts +1 -1
- package/packages/safe-js/dist/lint/rules/known-globals.js +1 -0
- package/packages/safe-js/dist/modules/registry.d.ts +3 -1
- package/packages/safe-js/dist/modules/registry.js +2 -1
- package/packages/safe-js/dist/parse/parser.js +4 -2
- package/packages/safe-js/dist/realm.d.ts +46 -0
- package/packages/safe-js/dist/realm.js +779 -0
- package/packages/safe-js/dist/run.d.ts +7 -1
- package/packages/safe-js/dist/run.js +6 -35
- package/packages/safe-js/dist/snapshot/dump-format.js +8 -3
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +4 -1
- package/packages/safe-js/dist/snapshot/replay-data.js +11 -0
- package/packages/safe-js/dist/snapshot/restore.js +6 -0
- package/packages/safe-js/dist/snapshot/serialize.d.ts +5 -2
- package/packages/safe-js/dist/snapshot/serialize.js +8 -2
- package/packages/safe-js/dist/snapshot/validation.js +18 -1
- package/packages/safe-js/dist/chunks/chunk-B7H4ZL65.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-7B7BKWVF.js.map → chunk-ETKM3KPA.js.map} +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createSandboxDate, dateFromParts, dateMethods, dateNumber, dateString, dateTime, isSandboxDate, parseDate } from "../date.js";
|
|
2
|
+
import { declareHostOperation, wrapCallerInjectedBindings } from "../host-bridge.js";
|
|
3
|
+
import { createSandboxClosure } from "../values.js";
|
|
4
|
+
const intrinsics = new WeakMap();
|
|
5
|
+
const constructors = new WeakSet();
|
|
6
|
+
export function createDateGlobal(options) {
|
|
7
|
+
const validateClockTime = (value) => {
|
|
8
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || Math.abs(value) > 8.64e15)
|
|
9
|
+
throw new TypeError("Date clock must return a finite integer epoch within the Date range.");
|
|
10
|
+
return value;
|
|
11
|
+
};
|
|
12
|
+
const readNow = declareHostOperation(() => {
|
|
13
|
+
options.budget.visitNode();
|
|
14
|
+
const value = options.clock?.now === undefined ? Date.now() : options.clock.now();
|
|
15
|
+
return validateClockTime(value);
|
|
16
|
+
}, "re-issue", {
|
|
17
|
+
onReplay: (_args, outcome) => {
|
|
18
|
+
if (outcome.status === "fulfilled") {
|
|
19
|
+
const time = validateClockTime(outcome.value);
|
|
20
|
+
options.clock?.restore?.({ next: time + 1 });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const now = wrapCallerInjectedBindings({ now: readNow }, { ...options, moduleId: "<Date>" })
|
|
25
|
+
.now;
|
|
26
|
+
const prototype = createSandboxDate(NaN);
|
|
27
|
+
const constructor = createSandboxClosure({
|
|
28
|
+
sandbox: true,
|
|
29
|
+
name: "Date",
|
|
30
|
+
length: 7,
|
|
31
|
+
call: async (_args, context) => options.budget.allocateString(dateString(createSandboxDate(Number(await now.call([], context))))),
|
|
32
|
+
construct: async (args, context) => {
|
|
33
|
+
let time;
|
|
34
|
+
if (args.length === 0)
|
|
35
|
+
time = Number(await now.call([], context));
|
|
36
|
+
else if (args.length > 1)
|
|
37
|
+
time = dateFromParts(args, false);
|
|
38
|
+
else if (isSandboxDate(args[0]))
|
|
39
|
+
time = dateTime(args[0]);
|
|
40
|
+
else if (typeof args[0] === "string")
|
|
41
|
+
time = parseDate(args[0], options.budget);
|
|
42
|
+
else
|
|
43
|
+
time = dateNumber(args[0]);
|
|
44
|
+
options.budget.chargeDataUsage(9);
|
|
45
|
+
return createSandboxDate(time);
|
|
46
|
+
},
|
|
47
|
+
properties: {
|
|
48
|
+
now,
|
|
49
|
+
prototype,
|
|
50
|
+
parse: createSandboxClosure({
|
|
51
|
+
sandbox: true,
|
|
52
|
+
name: "parse",
|
|
53
|
+
length: 1,
|
|
54
|
+
call: ([value]) => parseDate(value, options.budget)
|
|
55
|
+
}),
|
|
56
|
+
UTC: createSandboxClosure({
|
|
57
|
+
sandbox: true,
|
|
58
|
+
name: "UTC",
|
|
59
|
+
length: 7,
|
|
60
|
+
call: (args) => dateFromParts(args, true)
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const methods = new Map();
|
|
65
|
+
for (const [name, method] of dateMethods)
|
|
66
|
+
methods.set(name, createSandboxClosure({
|
|
67
|
+
sandbox: true,
|
|
68
|
+
name,
|
|
69
|
+
length: method.length,
|
|
70
|
+
call: (args, context) => {
|
|
71
|
+
const receiver = context?.thisValue;
|
|
72
|
+
if (!isSandboxDate(receiver))
|
|
73
|
+
throw new TypeError(`Date#${name} requires a Date receiver.`);
|
|
74
|
+
options.budget.visitNode();
|
|
75
|
+
const value = method.invoke(receiver, args);
|
|
76
|
+
return typeof value === "string"
|
|
77
|
+
? options.budget.allocateString(value)
|
|
78
|
+
: value;
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
constructors.add(constructor);
|
|
82
|
+
intrinsics.set(options.compileOwner ?? options.budget, { constructor, prototype, methods });
|
|
83
|
+
return constructor;
|
|
84
|
+
}
|
|
85
|
+
export function isDateConstructor(value) {
|
|
86
|
+
return typeof value === "object" && value !== null && constructors.has(value);
|
|
87
|
+
}
|
|
88
|
+
export function getDateMember(property, budget, owner) {
|
|
89
|
+
const state = intrinsics.get(owner ?? budget);
|
|
90
|
+
return property === "constructor" ? state?.constructor : state?.methods.get(String(property));
|
|
91
|
+
}
|
|
92
|
+
export function getDatePrototype(value, budget, owner) {
|
|
93
|
+
const prototype = intrinsics.get(owner ?? budget)?.prototype;
|
|
94
|
+
return prototype === value ? null : (prototype ?? null);
|
|
95
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Budget } from "../budget.js";
|
|
1
|
+
import type { Budget, CompileOwner } from "../budget.js";
|
|
2
2
|
import { type SandboxClosure, type SandboxObject } from "../values.js";
|
|
3
3
|
export type ObjectArrayGlobals = {
|
|
4
4
|
Object: SandboxObject;
|
|
@@ -9,4 +9,5 @@ export type ObjectArrayGlobals = {
|
|
|
9
9
|
};
|
|
10
10
|
export declare function createObjectArrayGlobals(options: {
|
|
11
11
|
budget: Budget;
|
|
12
|
+
compileOwner?: CompileOwner;
|
|
12
13
|
}): ObjectArrayGlobals;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { dateTime, isSandboxDate } from "../date.js";
|
|
2
|
+
import { getDatePrototype } from "./date.js";
|
|
3
|
+
import { getHostObjectKeys, isGuestHostObject } from "../host-capabilities.js";
|
|
1
4
|
import { isFloat32Array } from "../float32.js";
|
|
2
5
|
import { setSandboxProperty } from "../interpreter.js";
|
|
3
6
|
import { getSandboxIterator } from "../iteration.js";
|
|
@@ -64,6 +67,8 @@ export function createObjectArrayGlobals(options) {
|
|
|
64
67
|
getPrototypeOf: createSandboxClosure({
|
|
65
68
|
sandbox: true,
|
|
66
69
|
call: ([value]) => {
|
|
70
|
+
if (isSandboxDate(value))
|
|
71
|
+
return getDatePrototype(value, options.budget, options.compileOwner);
|
|
67
72
|
objectProperties(value);
|
|
68
73
|
return getSandboxPrototype(value);
|
|
69
74
|
},
|
|
@@ -184,7 +189,7 @@ export function createObjectArrayGlobals(options) {
|
|
|
184
189
|
}),
|
|
185
190
|
Number: createSandboxClosure({
|
|
186
191
|
sandbox: true,
|
|
187
|
-
call: ([value]) => Number(value),
|
|
192
|
+
call: ([value]) => isSandboxDate(value) ? dateTime(value) : Number(value),
|
|
188
193
|
name: "Number",
|
|
189
194
|
properties: {
|
|
190
195
|
isFinite: createSandboxClosure({
|
|
@@ -287,6 +292,13 @@ function assignSandboxValues(target, sources, budget) {
|
|
|
287
292
|
return target;
|
|
288
293
|
}
|
|
289
294
|
function objectProperties(value, mutable = false) {
|
|
295
|
+
if (isSandboxDate(value)) {
|
|
296
|
+
if (mutable)
|
|
297
|
+
throw new TypeError("Date own properties and prototypes are not supported.");
|
|
298
|
+
return value;
|
|
299
|
+
}
|
|
300
|
+
if (isGuestHostObject(value))
|
|
301
|
+
throw new TypeError("Live host object descriptors are not supported.");
|
|
290
302
|
if (isGuestClosure(value))
|
|
291
303
|
return materializeFunctionProperties(value);
|
|
292
304
|
if (isSandboxClosure(value)) {
|
|
@@ -389,6 +401,8 @@ async function collectIteratorValues(iterator) {
|
|
|
389
401
|
}
|
|
390
402
|
}
|
|
391
403
|
function getOwnEnumerableKeys(value) {
|
|
404
|
+
if (isGuestHostObject(value))
|
|
405
|
+
return getHostObjectKeys(value);
|
|
392
406
|
return getOwnEnumerableEntries(value).map(([key]) => key);
|
|
393
407
|
}
|
|
394
408
|
function getOwnEnumerableValues(value) {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createConsoleJsonGlobals } from "./globals/console-json.js";
|
|
2
|
+
import type { RunClock } from "../run.js";
|
|
3
|
+
export declare function createBuiltinBindings(options: Parameters<typeof createConsoleJsonGlobals>[0] & {
|
|
4
|
+
random?: () => number;
|
|
5
|
+
clock?: RunClock;
|
|
6
|
+
}): {
|
|
7
|
+
RegExp: import("./values.js").SandboxClosure;
|
|
8
|
+
Promise: import("./values.js").SandboxClosure;
|
|
9
|
+
structuredClone: import("./values.js").SandboxClosure;
|
|
10
|
+
parseInt: import("./values.js").SandboxClosure;
|
|
11
|
+
parseFloat: import("./values.js").SandboxClosure;
|
|
12
|
+
isNaN: import("./values.js").SandboxClosure;
|
|
13
|
+
isFinite: import("./values.js").SandboxClosure;
|
|
14
|
+
Object: import("./values.js").SandboxObject;
|
|
15
|
+
Array: import("./values.js").SandboxClosure;
|
|
16
|
+
String: import("./values.js").SandboxClosure;
|
|
17
|
+
Number: import("./values.js").SandboxClosure;
|
|
18
|
+
Boolean: import("./values.js").SandboxClosure;
|
|
19
|
+
Infinity: number;
|
|
20
|
+
Math: import("./values.js").SandboxObject;
|
|
21
|
+
NaN: number;
|
|
22
|
+
Error: import("./values.js").SandboxClosure;
|
|
23
|
+
TypeError: import("./values.js").SandboxClosure;
|
|
24
|
+
RangeError: import("./values.js").SandboxClosure;
|
|
25
|
+
ReferenceError: import("./values.js").SandboxClosure;
|
|
26
|
+
SyntaxError: import("./values.js").SandboxClosure;
|
|
27
|
+
AggregateError: import("./values.js").SandboxClosure;
|
|
28
|
+
Float32Array: import("./values.js").SandboxClosure;
|
|
29
|
+
Date: import("./values.js").SandboxClosure;
|
|
30
|
+
Map: import("./values.js").SandboxClosure;
|
|
31
|
+
Set: import("./values.js").SandboxClosure;
|
|
32
|
+
JSON: import("./values.js").SandboxObject;
|
|
33
|
+
console: import("./values.js").SandboxObject;
|
|
34
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createConsoleJsonGlobals } from "./globals/console-json.js";
|
|
2
|
+
import { createCollectionGlobals } from "./globals/collections.js";
|
|
3
|
+
import { createFloat32ArrayGlobal } from "./globals/float32array.js";
|
|
4
|
+
import { createErrorGlobals } from "./globals/error.js";
|
|
5
|
+
import { createMathGlobals } from "./globals/math.js";
|
|
6
|
+
import { createRegexGlobals } from "./globals/regex.js";
|
|
7
|
+
import { createMiscGlobals } from "./globals/misc.js";
|
|
8
|
+
import { createObjectArrayGlobals } from "./globals/object-array.js";
|
|
9
|
+
import { createPromiseGlobals } from "./promise.js";
|
|
10
|
+
import { createDateGlobal } from "./globals/date.js";
|
|
11
|
+
export function createBuiltinBindings(options) {
|
|
12
|
+
return {
|
|
13
|
+
...createConsoleJsonGlobals(options),
|
|
14
|
+
...createCollectionGlobals(options),
|
|
15
|
+
Float32Array: createFloat32ArrayGlobal(options.budget),
|
|
16
|
+
Date: createDateGlobal(options),
|
|
17
|
+
...createErrorGlobals(options),
|
|
18
|
+
...createMathGlobals({ random: options.random }),
|
|
19
|
+
...createObjectArrayGlobals(options),
|
|
20
|
+
...createMiscGlobals(options),
|
|
21
|
+
...createPromiseGlobals(options),
|
|
22
|
+
...createRegexGlobals(options.compileOwner)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -6,7 +6,19 @@ import type { RunLifecycle } from "../snapshot/dump.js";
|
|
|
6
6
|
type CallerInjectedFunction = {
|
|
7
7
|
bivarianceHack(...args: readonly unknown[]): unknown;
|
|
8
8
|
}["bivarianceHack"];
|
|
9
|
-
type
|
|
9
|
+
export type RealmBridge = {
|
|
10
|
+
readonly owner: object;
|
|
11
|
+
assertActive(): void;
|
|
12
|
+
wrapCallback(closure: SandboxClosure): (...args: readonly unknown[]) => Promise<unknown>;
|
|
13
|
+
invoke(operation: CallerInjectedFunction, call: () => unknown): unknown;
|
|
14
|
+
awaitResult(operation: CallerInjectedFunction): boolean;
|
|
15
|
+
captureArguments(operation: CallerInjectedFunction, args: readonly SandboxValue[], copy: (values: readonly SandboxValue[]) => unknown[]): {
|
|
16
|
+
args: unknown[];
|
|
17
|
+
rollback(): void;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type HostBridgeOptions = {
|
|
21
|
+
realm?: RealmBridge;
|
|
10
22
|
registerCapabilities?: boolean;
|
|
11
23
|
capabilityPath?: readonly string[];
|
|
12
24
|
budget: Budget;
|
|
@@ -26,4 +38,9 @@ export declare function declareHostOperation<TFunction extends CallerInjectedFun
|
|
|
26
38
|
}): TFunction;
|
|
27
39
|
export declare function readHostOperationPolicy(operation: CallerInjectedFunction): PendingHostCallPolicyMode | undefined;
|
|
28
40
|
export declare function wrapCallerInjectedBindings(bindings: Record<string, CallerInjectedBinding>, options: HostBridgeOptions): Record<string, SandboxValue>;
|
|
41
|
+
export declare function copyHostValueToSandbox(value: unknown, stackFrames: readonly string[], options: HostBridgeOptions & {
|
|
42
|
+
errorData?: boolean;
|
|
43
|
+
}, state: {
|
|
44
|
+
seen: WeakMap<object, SandboxValue>;
|
|
45
|
+
}, path: string): SandboxValue;
|
|
29
46
|
export {};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { normalizeClosureResult } from "./async.js";
|
|
2
|
+
import { copyNativeDate } from "./date.js";
|
|
3
|
+
import { exportHostCapability, importHostCapability, isLiveCapability } from "./host-capabilities.js";
|
|
2
4
|
import { attachErrorSpan, replaceErrorStack } from "../error/shape.js";
|
|
3
5
|
import { SandboxError } from "./budget.js";
|
|
4
6
|
import { CompileScope } from "./regex/compile-guard.js";
|
|
@@ -38,7 +40,7 @@ export function wrapCallerInjectedBindings(bindings, options) {
|
|
|
38
40
|
const state = { seen: new WeakMap() };
|
|
39
41
|
const copied = Object.fromEntries(Object.entries(bindings).map(([name, value]) => [
|
|
40
42
|
name,
|
|
41
|
-
typeof value === "function"
|
|
43
|
+
typeof value === "function" && !isLiveCapability(value)
|
|
42
44
|
? wrapCallerInjectedFunction(name, value, { ...options, capabilityPath: [name] }, state)
|
|
43
45
|
: copyHostValueToSandbox(value, [], { ...options, operation: name, capabilityPath: [name] }, state, "<root>")
|
|
44
46
|
]));
|
|
@@ -56,9 +58,10 @@ function wrapCallerInjectedFunction(name, value, options, state) {
|
|
|
56
58
|
const bindingName = name === "default" && value.name.length > 0 ? value.name : name;
|
|
57
59
|
const callable = value;
|
|
58
60
|
return createSandboxClosure({
|
|
59
|
-
...(isAsyncFunction(callable) ? { async: true } : {}),
|
|
61
|
+
...(isAsyncFunction(callable) && !options.realm?.awaitResult(callable) ? { async: true } : {}),
|
|
60
62
|
cancellationSignal: options.signal,
|
|
61
63
|
call: (args, context) => {
|
|
64
|
+
options.realm?.assertActive();
|
|
62
65
|
const operationLease = options.budget.acquireCompileOwner(false, options.compileOwner);
|
|
63
66
|
const compilation = new CompileScope(operationLease.owner);
|
|
64
67
|
try {
|
|
@@ -75,10 +78,13 @@ function wrapCallerInjectedFunction(name, value, options, state) {
|
|
|
75
78
|
seen: new WeakMap(),
|
|
76
79
|
restored: []
|
|
77
80
|
};
|
|
78
|
-
const
|
|
81
|
+
const copyArguments = (values) => deepCopyFromSandbox([...values], {
|
|
79
82
|
compilation,
|
|
80
|
-
|
|
83
|
+
unwrapHostObject: options.realm === undefined ? undefined : object => exportHostCapability(object, options.realm.owner),
|
|
84
|
+
wrapClosure: (closure) => options.realm?.wrapCallback(closure) ?? wrapSandboxClosureForHost(closure, stackFrames, options.budget, operationLease.owner, callbacks)
|
|
81
85
|
});
|
|
86
|
+
const captured = options.realm?.captureArguments(callable, args, copyArguments);
|
|
87
|
+
const hostArgs = captured?.args ?? copyArguments(args);
|
|
82
88
|
const hostCalls = options.hostCalls;
|
|
83
89
|
const operation = options.operation ?? bindingName;
|
|
84
90
|
const moduleId = options.moduleId ?? "<bindings>";
|
|
@@ -86,6 +92,20 @@ function wrapCallerInjectedFunction(name, value, options, state) {
|
|
|
86
92
|
readRegisteredPendingHostCallPolicy(moduleId, operation) ??
|
|
87
93
|
"re-issue";
|
|
88
94
|
if (hostCalls === undefined) {
|
|
95
|
+
if (options.realm !== undefined) {
|
|
96
|
+
let result;
|
|
97
|
+
try {
|
|
98
|
+
result = options.realm.invoke(callable, () => Reflect.apply(callable, undefined, hostArgs));
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
captured.rollback();
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
if (options.realm.awaitResult(callable)) {
|
|
105
|
+
return Promise.resolve(result).then(value => copyHostResultToSandbox(value, stackFrames, options));
|
|
106
|
+
}
|
|
107
|
+
return copyHostResultToSandbox(result, stackFrames, options);
|
|
108
|
+
}
|
|
89
109
|
return copyHostResultToSandbox(invokeHostCallback(() => Reflect.apply(callable, undefined, hostArgs), options), stackFrames, options);
|
|
90
110
|
}
|
|
91
111
|
const issued = hostCalls.issue({
|
|
@@ -595,8 +615,13 @@ function wrapHostPromiseWithSignal(promise, signal) {
|
|
|
595
615
|
});
|
|
596
616
|
});
|
|
597
617
|
}
|
|
598
|
-
function copyHostValueToSandbox(value, stackFrames, options, state, path) {
|
|
618
|
+
export function copyHostValueToSandbox(value, stackFrames, options, state, path) {
|
|
599
619
|
const { budget } = options;
|
|
620
|
+
if (isLiveCapability(value)) {
|
|
621
|
+
if (options.realm === undefined || options.errorData || options.hostCalls !== undefined)
|
|
622
|
+
throw new TypeError("Live capabilities are not portable replay or error data.");
|
|
623
|
+
return importHostCapability(value, options.realm.owner);
|
|
624
|
+
}
|
|
600
625
|
if (value === null ||
|
|
601
626
|
value === undefined ||
|
|
602
627
|
typeof value === "number" ||
|
|
@@ -643,6 +668,15 @@ function copyHostValueToSandbox(value, stackFrames, options, state, path) {
|
|
|
643
668
|
: path
|
|
644
669
|
}, state);
|
|
645
670
|
}
|
|
671
|
+
const date = copyNativeDate(value);
|
|
672
|
+
if (date !== undefined) {
|
|
673
|
+
const existing = state.seen.get(value);
|
|
674
|
+
if (existing !== undefined)
|
|
675
|
+
return existing;
|
|
676
|
+
budget.chargeDataUsage(9);
|
|
677
|
+
state.seen.set(value, date);
|
|
678
|
+
return date;
|
|
679
|
+
}
|
|
646
680
|
if (isFloat32Array(value)) {
|
|
647
681
|
const existing = state.seen.get(value);
|
|
648
682
|
if (existing !== undefined)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { float32DataProperties, float32Storage, isFloat32Array } from "./float32.js";
|
|
3
|
+
import { copyNativeDate, serializedDateTime } from "./date.js";
|
|
3
4
|
import { cloneSandboxValue, createSandboxClosure, defineOwnDataProperty, isArrayIndexKey, measureSandboxData } from "./values.js";
|
|
4
5
|
import { CompileScope } from "./regex/compile-guard.js";
|
|
5
6
|
import { decodeReplayData, encodeReplayData } from "../snapshot/replay-data.js";
|
|
@@ -581,6 +582,9 @@ function normalize(value, seen) {
|
|
|
581
582
|
throw new TypeError("Host call arguments cannot contain cycles.");
|
|
582
583
|
seen.add(value);
|
|
583
584
|
try {
|
|
585
|
+
const date = copyNativeDate(value);
|
|
586
|
+
if (date !== undefined)
|
|
587
|
+
return Object.assign(Object.create(null), { $type: "date", time: serializedDateTime(date) });
|
|
584
588
|
if (isFloat32Array(value)) {
|
|
585
589
|
const storage = float32Storage(value);
|
|
586
590
|
const properties = Object.create(null);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type HostOperation } from "../extensions.js";
|
|
2
|
+
import type { SandboxClosure, SandboxObject, SandboxValue } from "./values.js";
|
|
3
|
+
declare const hostObjectBrand: unique symbol;
|
|
4
|
+
declare const guestReferenceBrand: unique symbol;
|
|
5
|
+
declare const sandboxHostObjectBrand: unique symbol;
|
|
6
|
+
type SandboxHostObject = SandboxObject & {
|
|
7
|
+
readonly [sandboxHostObjectBrand]: true;
|
|
8
|
+
};
|
|
9
|
+
export type HostObject = Readonly<Record<string, never>> & {
|
|
10
|
+
readonly [hostObjectBrand]: true;
|
|
11
|
+
};
|
|
12
|
+
export type GuestReference = Readonly<Record<string, never>> & {
|
|
13
|
+
readonly [guestReferenceBrand]: true;
|
|
14
|
+
};
|
|
15
|
+
export type HostObjectDefinition = {
|
|
16
|
+
properties?: Record<string, {
|
|
17
|
+
get?: () => unknown;
|
|
18
|
+
set?: (value: unknown) => void;
|
|
19
|
+
}>;
|
|
20
|
+
methods?: Record<string, HostOperation>;
|
|
21
|
+
};
|
|
22
|
+
export type HostObjectController = {
|
|
23
|
+
owner: object;
|
|
24
|
+
assertActive(): void;
|
|
25
|
+
chargeWork(units?: number): void;
|
|
26
|
+
read(operation: () => unknown): SandboxValue;
|
|
27
|
+
write(operation: (value: unknown) => void, value: SandboxValue): void;
|
|
28
|
+
method(operation: HostOperation): SandboxClosure;
|
|
29
|
+
};
|
|
30
|
+
type GuestCallbackState = {
|
|
31
|
+
owner: object;
|
|
32
|
+
closure?: SandboxClosure;
|
|
33
|
+
assertActive(): void;
|
|
34
|
+
};
|
|
35
|
+
export declare function createGuestReference(root: [SandboxValue], owner: object, assertActive: () => void): GuestReference;
|
|
36
|
+
export declare function readGuestReference(reference: unknown, owner: object): SandboxValue;
|
|
37
|
+
export declare function revokeGuestReference(reference: GuestReference, owner: object): void;
|
|
38
|
+
export declare function createLiveHostObject(definition: HostObjectDefinition, controller: HostObjectController): HostObject;
|
|
39
|
+
export declare function isGuestHostObject(value: unknown): value is SandboxHostObject;
|
|
40
|
+
export declare function isLiveCapability(value: unknown): boolean;
|
|
41
|
+
export declare function importHostCapability(value: object, owner: object): SandboxValue;
|
|
42
|
+
export declare function exportHostCapability(value: object, owner: object): HostObject;
|
|
43
|
+
export declare function registerGuestCallback(callback: HostOperation, state: GuestCallbackState): void;
|
|
44
|
+
export declare function readGuestCallback(callback: unknown, owner: object): SandboxClosure;
|
|
45
|
+
export declare function revokeGuestCallback(callback: object, owner: object): void;
|
|
46
|
+
export declare function revokeHostObject(value: HostObject, owner: object): void;
|
|
47
|
+
export declare function getHostObjectMember(value: SandboxObject, key: string): SandboxValue;
|
|
48
|
+
export declare function setHostObjectMember(value: SandboxObject, key: string, entry: SandboxValue): void;
|
|
49
|
+
export declare function getHostObjectKeys(value: SandboxObject): string[];
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { readDataRecord } from "../extensions.js";
|
|
2
|
+
const hostObjects = new WeakMap();
|
|
3
|
+
const guestObjects = new WeakMap();
|
|
4
|
+
const guestCallbacks = new WeakMap();
|
|
5
|
+
const guestReferences = new WeakMap();
|
|
6
|
+
export function createGuestReference(root, owner, assertActive) {
|
|
7
|
+
const reference = Object.freeze(Object.create(null));
|
|
8
|
+
guestReferences.set(reference, { root, owner, assertActive });
|
|
9
|
+
return reference;
|
|
10
|
+
}
|
|
11
|
+
export function readGuestReference(reference, owner) {
|
|
12
|
+
const state = typeof reference === "object" && reference !== null
|
|
13
|
+
? guestReferences.get(reference)
|
|
14
|
+
: undefined;
|
|
15
|
+
if (state === undefined || state.owner !== owner)
|
|
16
|
+
throw new TypeError("Foreign or invalid guest reference.");
|
|
17
|
+
state.assertActive();
|
|
18
|
+
if (state.root === undefined)
|
|
19
|
+
throw new TypeError("Guest reference is revoked.");
|
|
20
|
+
return state.root[0];
|
|
21
|
+
}
|
|
22
|
+
export function revokeGuestReference(reference, owner) {
|
|
23
|
+
const state = guestReferences.get(reference);
|
|
24
|
+
if (state === undefined || state.owner !== owner)
|
|
25
|
+
throw new TypeError("Foreign guest reference.");
|
|
26
|
+
state.root = undefined;
|
|
27
|
+
}
|
|
28
|
+
export function createLiveHostObject(definition, controller) {
|
|
29
|
+
const input = readDataRecord(definition, "Host object definition");
|
|
30
|
+
if (Object.keys(input).some((key) => key !== "properties" && key !== "methods"))
|
|
31
|
+
throw new TypeError("Unknown host object definition field.");
|
|
32
|
+
const properties = new Map();
|
|
33
|
+
for (const [name, inputProperty] of Object.entries(readDataRecord(input.properties ?? {}, "Host properties"))) {
|
|
34
|
+
const property = readDataRecord(inputProperty, `Host property '${name}'`);
|
|
35
|
+
if (Object.keys(property).some((key) => key !== "get" && key !== "set"))
|
|
36
|
+
throw new TypeError("Unknown host property field.");
|
|
37
|
+
if ((property.get !== undefined && typeof property.get !== "function") ||
|
|
38
|
+
(property.set !== undefined && typeof property.set !== "function"))
|
|
39
|
+
throw new TypeError("Host property operations must be functions.");
|
|
40
|
+
properties.set(name, property);
|
|
41
|
+
}
|
|
42
|
+
const operations = readDataRecord(input.methods ?? {}, "Host methods");
|
|
43
|
+
for (const [name, operation] of Object.entries(operations)) {
|
|
44
|
+
if (typeof operation !== "function")
|
|
45
|
+
throw new TypeError("Host methods must be functions.");
|
|
46
|
+
if (properties.has(name))
|
|
47
|
+
throw new TypeError(`Conflicting host member '${name}'.`);
|
|
48
|
+
}
|
|
49
|
+
for (const name of [...properties.keys(), ...Object.keys(operations)]) {
|
|
50
|
+
if (["constructor", "prototype", "__proto__"].includes(name))
|
|
51
|
+
throw new TypeError(`Reserved host member '${name}'.`);
|
|
52
|
+
}
|
|
53
|
+
controller.assertActive();
|
|
54
|
+
controller.chargeWork(properties.size + Object.keys(operations).length + 1);
|
|
55
|
+
const host = Object.freeze(Object.create(null));
|
|
56
|
+
const guest = Object.freeze(Object.create(null));
|
|
57
|
+
const methods = new Map(Object.entries(operations).map(([name, operation]) => [
|
|
58
|
+
name,
|
|
59
|
+
controller.method(operation)
|
|
60
|
+
]));
|
|
61
|
+
const state = { host, guest, controller, properties, methods };
|
|
62
|
+
hostObjects.set(host, state);
|
|
63
|
+
guestObjects.set(guest, state);
|
|
64
|
+
return host;
|
|
65
|
+
}
|
|
66
|
+
export function isGuestHostObject(value) {
|
|
67
|
+
return typeof value === "object" && value !== null && guestObjects.has(value);
|
|
68
|
+
}
|
|
69
|
+
export function isLiveCapability(value) {
|
|
70
|
+
return (((typeof value === "object" && value !== null) || typeof value === "function") &&
|
|
71
|
+
(hostObjects.has(value) ||
|
|
72
|
+
guestObjects.has(value) ||
|
|
73
|
+
guestCallbacks.has(value) ||
|
|
74
|
+
guestReferences.has(value)));
|
|
75
|
+
}
|
|
76
|
+
export function importHostCapability(value, owner) {
|
|
77
|
+
if (guestReferences.has(value))
|
|
78
|
+
return readGuestReference(value, owner);
|
|
79
|
+
const object = hostObjects.get(value);
|
|
80
|
+
if (object !== undefined) {
|
|
81
|
+
if (object.controller.owner !== owner)
|
|
82
|
+
throw new TypeError("Foreign realm host capability.");
|
|
83
|
+
object.controller.assertActive();
|
|
84
|
+
return object.guest;
|
|
85
|
+
}
|
|
86
|
+
const callback = guestCallbacks.get(value);
|
|
87
|
+
if (callback !== undefined) {
|
|
88
|
+
if (callback.owner !== owner)
|
|
89
|
+
throw new TypeError("Foreign realm guest callback.");
|
|
90
|
+
callback.assertActive();
|
|
91
|
+
if (callback.closure === undefined)
|
|
92
|
+
throw new TypeError("Guest callback is revoked.");
|
|
93
|
+
return callback.closure;
|
|
94
|
+
}
|
|
95
|
+
throw new TypeError("Unsupported live capability conversion.");
|
|
96
|
+
}
|
|
97
|
+
export function exportHostCapability(value, owner) {
|
|
98
|
+
const state = guestObjects.get(value);
|
|
99
|
+
if (state === undefined || state.controller.owner !== owner)
|
|
100
|
+
throw new TypeError("Foreign realm host capability.");
|
|
101
|
+
state.controller.assertActive();
|
|
102
|
+
return state.host;
|
|
103
|
+
}
|
|
104
|
+
export function registerGuestCallback(callback, state) {
|
|
105
|
+
guestCallbacks.set(callback, state);
|
|
106
|
+
}
|
|
107
|
+
export function readGuestCallback(callback, owner) {
|
|
108
|
+
const state = typeof callback === "function" ? guestCallbacks.get(callback) : undefined;
|
|
109
|
+
if (state === undefined || state.owner !== owner)
|
|
110
|
+
throw new TypeError("Foreign or invalid guest callback.");
|
|
111
|
+
state.assertActive();
|
|
112
|
+
if (state.closure === undefined)
|
|
113
|
+
throw new TypeError("Guest callback is revoked.");
|
|
114
|
+
return state.closure;
|
|
115
|
+
}
|
|
116
|
+
export function revokeGuestCallback(callback, owner) {
|
|
117
|
+
const state = guestCallbacks.get(callback);
|
|
118
|
+
if (state === undefined || state.owner !== owner)
|
|
119
|
+
throw new TypeError("Foreign guest callback.");
|
|
120
|
+
state.closure = undefined;
|
|
121
|
+
}
|
|
122
|
+
export function revokeHostObject(value, owner) {
|
|
123
|
+
const state = hostObjects.get(value);
|
|
124
|
+
if (state === undefined || state.controller.owner !== owner)
|
|
125
|
+
throw new TypeError("Foreign host object.");
|
|
126
|
+
state.properties.clear();
|
|
127
|
+
state.methods.clear();
|
|
128
|
+
}
|
|
129
|
+
export function getHostObjectMember(value, key) {
|
|
130
|
+
const state = guestObjects.get(value);
|
|
131
|
+
state.controller.assertActive();
|
|
132
|
+
state.controller.chargeWork();
|
|
133
|
+
const property = state.properties.get(key);
|
|
134
|
+
if (property !== undefined)
|
|
135
|
+
return property.get === undefined ? undefined : state.controller.read(property.get);
|
|
136
|
+
return state.methods.get(key);
|
|
137
|
+
}
|
|
138
|
+
export function setHostObjectMember(value, key, entry) {
|
|
139
|
+
const state = guestObjects.get(value);
|
|
140
|
+
state.controller.assertActive();
|
|
141
|
+
state.controller.chargeWork();
|
|
142
|
+
const property = state.properties.get(key);
|
|
143
|
+
if (property?.set === undefined)
|
|
144
|
+
throw new TypeError(`Host property '${key}' is not writable.`);
|
|
145
|
+
state.controller.write(property.set, entry);
|
|
146
|
+
}
|
|
147
|
+
export function getHostObjectKeys(value) {
|
|
148
|
+
const state = guestObjects.get(value);
|
|
149
|
+
state.controller.assertActive();
|
|
150
|
+
return [...state.properties.keys(), ...state.methods.keys()];
|
|
151
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SandboxJobQueue } from "./jobs.js";
|
|
1
2
|
import type { ParseResult, SourceSpan } from "../parse.js";
|
|
2
3
|
import { type AsyncEvaluationContext, type AsyncEvaluationResult, type InterpreterYieldPoint } from "./async.js";
|
|
3
4
|
import type { GeneratorCompletion } from "./generator.js";
|
|
@@ -43,6 +44,9 @@ export type InterpreterResult = {
|
|
|
43
44
|
stats: InterpreterStats;
|
|
44
45
|
};
|
|
45
46
|
export type InterpretOptions = {
|
|
47
|
+
assertActive?: () => void;
|
|
48
|
+
jobs?: SandboxJobQueue;
|
|
49
|
+
nested?: boolean;
|
|
46
50
|
compilation?: CompileScope;
|
|
47
51
|
compileOwner?: CompileOwner;
|
|
48
52
|
captureReplayState?: () => unknown;
|