poe-code 14.0.81 → 14.0.82
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 +360 -167
- 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-2LNBLFJ3.js → chunk-7LGBDF56.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-ZSYBRZU6.js → chunk-V6LQP64H.js} +308 -134
- package/packages/safe-js/dist/chunks/chunk-V6LQP64H.js.map +7 -0
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.js +1 -1
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/error-prototypes.d.ts +3 -0
- package/packages/safe-js/dist/interp/error-prototypes.js +1 -0
- package/packages/safe-js/dist/interp/exceptions.d.ts +1 -0
- package/packages/safe-js/dist/interp/exceptions.js +39 -15
- package/packages/safe-js/dist/interp/globals/error.d.ts +2 -0
- package/packages/safe-js/dist/interp/globals/error.js +104 -4
- package/packages/safe-js/dist/interp/globals.d.ts +1 -0
- package/packages/safe-js/dist/interp/globals.js +4 -2
- package/packages/safe-js/dist/interp/host-bridge.js +4 -2
- package/packages/safe-js/dist/interp/instanceof.js +3 -2
- package/packages/safe-js/dist/interp/object-model.js +11 -0
- package/packages/safe-js/dist/interp/values.js +47 -2
- package/packages/safe-js/dist/snapshot/guest-heap-validation.js +4 -1
- package/packages/safe-js/dist/snapshot/guest-heap.d.ts +2 -0
- package/packages/safe-js/dist/snapshot/guest-heap.js +6 -2
- package/packages/safe-js/dist/snapshot/restore.js +17 -2
- package/packages/safe-js/dist/snapshot/validation.js +1 -1
- package/packages/safe-js/dist/chunks/chunk-ZSYBRZU6.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-2LNBLFJ3.js.map → chunk-7LGBDF56.js.map} +0 -0
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveFsConfig,
|
|
23
23
|
splitFrontmatter,
|
|
24
24
|
withBrokenPipeGuard
|
|
25
|
-
} from "./chunks/chunk-
|
|
25
|
+
} from "./chunks/chunk-7LGBDF56.js";
|
|
26
26
|
import {
|
|
27
27
|
Budget,
|
|
28
28
|
SandboxError,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
replaceErrorStack,
|
|
35
35
|
restore,
|
|
36
36
|
run
|
|
37
|
-
} from "./chunks/chunk-
|
|
37
|
+
} from "./chunks/chunk-V6LQP64H.js";
|
|
38
38
|
import "./chunks/chunk-M2DLCGEF.js";
|
|
39
39
|
import "./chunks/chunk-Q7USUEL7.js";
|
|
40
40
|
import "./chunks/chunk-4YIDX44G.js";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
runWithSpawnUsageAccumulator,
|
|
27
27
|
splitFrontmatter,
|
|
28
28
|
supportsSpawnMode
|
|
29
|
-
} from "./chunks/chunk-
|
|
29
|
+
} from "./chunks/chunk-7LGBDF56.js";
|
|
30
30
|
import {
|
|
31
31
|
Budget,
|
|
32
32
|
FileSnapshotBackend,
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
registerPendingHostCallPolicy,
|
|
51
51
|
restore,
|
|
52
52
|
run
|
|
53
|
-
} from "./chunks/chunk-
|
|
53
|
+
} from "./chunks/chunk-V6LQP64H.js";
|
|
54
54
|
import "./chunks/chunk-M2DLCGEF.js";
|
|
55
55
|
import "./chunks/chunk-Q7USUEL7.js";
|
|
56
56
|
import "./chunks/chunk-4YIDX44G.js";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Budget } from "./budget.js";
|
|
2
|
+
import type { SandboxObject } from "./values.js";
|
|
3
|
+
export declare const errorPrototypes: WeakMap<Budget, Map<"Error" | "TypeError" | "RangeError" | "ReferenceError" | "SyntaxError" | "URIError" | "EvalError" | "AbortError" | "AggregateError" | "HarnessFailure", SandboxObject>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const errorPrototypes = new WeakMap();
|
|
@@ -55,5 +55,6 @@ export declare function createSubsetErrorValue(name: string, message: SandboxVal
|
|
|
55
55
|
cause?: unknown;
|
|
56
56
|
chargeBudget?: boolean;
|
|
57
57
|
span?: ErrorSourceSpan;
|
|
58
|
+
transport?: boolean;
|
|
58
59
|
}): SandboxObject;
|
|
59
60
|
export declare function isSandboxErrorConstructorInstance(value: SandboxValue, name: SandboxErrorName): boolean;
|
|
@@ -5,7 +5,8 @@ import { withFatalPromiseCleanup } from "./promise-tracker.js";
|
|
|
5
5
|
import { deepCopyToSandbox, ownEnumerableSandboxKeys } from "./values.js";
|
|
6
6
|
import { hasOwnSandboxProperty } from "./globals/object.js";
|
|
7
7
|
import { retainValues } from "./resources.js";
|
|
8
|
-
import { getSandboxDataProperty } from "./object-model.js";
|
|
8
|
+
import { getSandboxDataProperty, setSandboxPrototype } from "./object-model.js";
|
|
9
|
+
import { errorPrototypes } from "./error-prototypes.js";
|
|
9
10
|
import { toPropertyKey } from "./property-key.js";
|
|
10
11
|
import { internalSymbols } from "./internal-symbols.js";
|
|
11
12
|
import { containsResumeTarget } from "./resume-target.js";
|
|
@@ -162,8 +163,7 @@ export function surfaceThrownValue(reason, budget, stackFrames = [], span) {
|
|
|
162
163
|
throw reason;
|
|
163
164
|
}
|
|
164
165
|
if (isSubsetErrorValue(reason)) {
|
|
165
|
-
normalizeSurfacedSubsetError(reason, budget, stackFrames, span);
|
|
166
|
-
return reason;
|
|
166
|
+
return normalizeSurfacedSubsetError(reason, budget, stackFrames, span);
|
|
167
167
|
}
|
|
168
168
|
if (reason instanceof Error) {
|
|
169
169
|
const error = createSubsetErrorValue(reason.name || "Error", reason.message, stackFrames, budget, {
|
|
@@ -171,8 +171,7 @@ export function surfaceThrownValue(reason, budget, stackFrames = [], span) {
|
|
|
171
171
|
chargeBudget: false,
|
|
172
172
|
span
|
|
173
173
|
});
|
|
174
|
-
normalizeSurfacedSubsetError(error, budget, stackFrames, span);
|
|
175
|
-
return error;
|
|
174
|
+
return normalizeSurfacedSubsetError(error, budget, stackFrames, span);
|
|
176
175
|
}
|
|
177
176
|
if (isErrorLikeValue(reason)) {
|
|
178
177
|
const error = createSubsetErrorValue(reason.name || "Error", reason.message, stackFrames, budget, {
|
|
@@ -180,13 +179,13 @@ export function surfaceThrownValue(reason, budget, stackFrames = [], span) {
|
|
|
180
179
|
chargeBudget: false,
|
|
181
180
|
span
|
|
182
181
|
});
|
|
183
|
-
normalizeSurfacedSubsetError(error, budget, stackFrames, span);
|
|
184
|
-
return error;
|
|
182
|
+
return normalizeSurfacedSubsetError(error, budget, stackFrames, span);
|
|
185
183
|
}
|
|
186
|
-
|
|
184
|
+
const error = createSubsetErrorValue("Error", describeThrownValue(reason), stackFrames, budget, {
|
|
187
185
|
chargeBudget: false,
|
|
188
186
|
span
|
|
189
187
|
});
|
|
188
|
+
return normalizeSurfacedSubsetError(error, budget, stackFrames, span);
|
|
190
189
|
}
|
|
191
190
|
export function createSubsetErrorValue(name, message, stackFrames, budget, options = {}) {
|
|
192
191
|
const resumeChecks = options.chargeBudget === false ? budget.suspendChecks() : undefined;
|
|
@@ -195,11 +194,16 @@ export function createSubsetErrorValue(name, message, stackFrames, budget, optio
|
|
|
195
194
|
const errorMessage = budget.allocateString(coerceErrorMessage(message));
|
|
196
195
|
const header = errorMessage === "" ? errorName : `${errorName}: ${errorMessage}`;
|
|
197
196
|
const stack = budget.allocateString([header, ...[...stackFrames].reverse()].join("\n"));
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
197
|
+
const prototype = options.transport ? undefined : errorPrototypes.get(budget)?.get(toSandboxErrorName(errorName));
|
|
198
|
+
const error = prototype === undefined ? { name: errorName, message: errorMessage, stack } : {};
|
|
199
|
+
if (prototype !== undefined) {
|
|
200
|
+
if (message !== undefined)
|
|
201
|
+
Object.defineProperty(error, "message", { value: errorMessage, writable: true, configurable: true });
|
|
202
|
+
if (!errorPrototypes.get(budget).has(errorName))
|
|
203
|
+
Object.defineProperty(error, "name", { value: errorName, writable: true, configurable: true });
|
|
204
|
+
Object.defineProperty(error, "stack", { value: stack, writable: true, configurable: true });
|
|
205
|
+
setSandboxPrototype(error, prototype, budget);
|
|
206
|
+
}
|
|
203
207
|
sandboxErrorTypes.set(error, toSandboxErrorName(errorName));
|
|
204
208
|
attachErrorSpan(error, options.span);
|
|
205
209
|
attachWrappedErrorCause(error, options.cause);
|
|
@@ -224,6 +228,8 @@ function isSubsetErrorValue(value) {
|
|
|
224
228
|
if (typeof value !== "object" || value === null) {
|
|
225
229
|
return false;
|
|
226
230
|
}
|
|
231
|
+
if (sandboxErrorTypes.has(value))
|
|
232
|
+
return true;
|
|
227
233
|
const prototype = Object.getPrototypeOf(value);
|
|
228
234
|
return ((prototype === Object.prototype || prototype === null) &&
|
|
229
235
|
typeof value.name === "string" &&
|
|
@@ -236,20 +242,38 @@ function normalizeSurfacedSubsetError(error, budget, stackFrames, span) {
|
|
|
236
242
|
const name = budget.allocateString(toSandboxErrorName(readErrorName(error)));
|
|
237
243
|
const message = budget.allocateString(readSurfacedErrorMessage(error, name));
|
|
238
244
|
const frames = readSandboxStackFrames(error.stack);
|
|
245
|
+
if (!Object.isExtensible(error) || ["name", "message", "stack"].some(key => {
|
|
246
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, key);
|
|
247
|
+
return descriptor !== undefined && (!("value" in descriptor) || !descriptor.writable);
|
|
248
|
+
})) {
|
|
249
|
+
// Public diagnostics must not mutate frozen guest state or invoke setters.
|
|
250
|
+
const original = error;
|
|
251
|
+
const descriptors = Object.getOwnPropertyDescriptors(original);
|
|
252
|
+
delete descriptors.name;
|
|
253
|
+
delete descriptors.message;
|
|
254
|
+
delete descriptors.stack;
|
|
255
|
+
error = Object.defineProperties({}, descriptors);
|
|
256
|
+
const errorType = sandboxErrorTypes.get(original);
|
|
257
|
+
if (errorType !== undefined)
|
|
258
|
+
sandboxErrorTypes.set(error, errorType);
|
|
259
|
+
}
|
|
239
260
|
error.name = name;
|
|
240
261
|
error.message = message;
|
|
241
262
|
error.stack = budget.allocateString(formatErrorStack(name, message, frames.length > 0 ? frames : [...stackFrames].reverse()));
|
|
242
263
|
attachErrorSpan(error, readErrorSpan(error) ?? span);
|
|
264
|
+
return error;
|
|
243
265
|
}
|
|
244
266
|
finally {
|
|
245
267
|
resumeChecks();
|
|
246
268
|
}
|
|
247
269
|
}
|
|
248
270
|
function readErrorName(error) {
|
|
249
|
-
|
|
271
|
+
const name = getSandboxDataProperty(error, "name");
|
|
272
|
+
return typeof name === "string" && name.length > 0 ? name : sandboxErrorTypes.get(error) ?? "Error";
|
|
250
273
|
}
|
|
251
274
|
function readSurfacedErrorMessage(error, name) {
|
|
252
|
-
const
|
|
275
|
+
const value = getSandboxDataProperty(error, "message");
|
|
276
|
+
const message = typeof value === "string" ? value : "";
|
|
253
277
|
if (message === "") {
|
|
254
278
|
return `${name} thrown`;
|
|
255
279
|
}
|
|
@@ -5,7 +5,9 @@ export type ErrorName = (typeof errorNames)[number];
|
|
|
5
5
|
export type ErrorGlobals = Record<ErrorName, ReturnType<typeof createSandboxClosure>>;
|
|
6
6
|
export declare function createErrorGlobals(options: {
|
|
7
7
|
budget: Budget;
|
|
8
|
+
errorPrototypes?: boolean;
|
|
8
9
|
}): ErrorGlobals;
|
|
9
10
|
export declare function isSandboxErrorConstructorInstance(value: SandboxValue, constructor: SandboxValue): boolean;
|
|
10
11
|
export declare function isSandboxErrorConstructor(value: SandboxValue): value is SandboxClosure;
|
|
12
|
+
export declare function createErrorPrototypes(budget: Budget, constructors: ErrorGlobals): void;
|
|
11
13
|
export {};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import { errorPrototypes } from "../error-prototypes.js";
|
|
2
|
+
import { getSandboxPropertyDescriptor, getSandboxPrototype, materializeFunctionProperties, registerIntrinsicFunction, registerIntrinsicObject, setSandboxPrototype } from "../object-model.js";
|
|
3
|
+
import { registerBuiltinIdentities } from "../intrinsics.js";
|
|
4
|
+
import { sandboxString } from "../string-coercion.js";
|
|
5
|
+
import { retainValues } from "../resources.js";
|
|
6
|
+
import { readPropertyDescriptor } from "../accessors.js";
|
|
7
|
+
import { acquireSandboxIterator, getSandboxIterator, readIteratorResult } from "../iteration.js";
|
|
1
8
|
import { createSubsetErrorValue, isSandboxErrorConstructorInstance as isNamedSandboxErrorConstructorInstance } from "../exceptions.js";
|
|
2
9
|
import { createSandboxClosure, isSandboxClosure } from "../values.js";
|
|
3
10
|
const errorConstructorNames = new WeakMap();
|
|
@@ -12,7 +19,7 @@ const errorNames = [
|
|
|
12
19
|
"AggregateError"
|
|
13
20
|
];
|
|
14
21
|
export function createErrorGlobals(options) {
|
|
15
|
-
return Object.fromEntries(errorNames.map((name) => [name, createErrorConstructor(name, options.budget)]));
|
|
22
|
+
return Object.fromEntries(errorNames.map((name) => [name, createErrorConstructor(name, options.budget, options.errorPrototypes === true)]));
|
|
16
23
|
}
|
|
17
24
|
export function isSandboxErrorConstructorInstance(value, constructor) {
|
|
18
25
|
if (!isSandboxClosure(constructor)) {
|
|
@@ -27,12 +34,105 @@ export function isSandboxErrorConstructorInstance(value, constructor) {
|
|
|
27
34
|
export function isSandboxErrorConstructor(value) {
|
|
28
35
|
return isSandboxClosure(value) && errorConstructorNames.has(value);
|
|
29
36
|
}
|
|
30
|
-
function createErrorConstructor(name, budget) {
|
|
31
|
-
const call = (args, context) =>
|
|
32
|
-
|
|
37
|
+
function createErrorConstructor(name, budget, guest) {
|
|
38
|
+
const call = (args, context) => errorPrototypes.has(budget) ? createNativeError(name, args, budget, context, closure)
|
|
39
|
+
: createSubsetError(name, args, context?.stack ?? [], budget);
|
|
40
|
+
const closure = createSandboxClosure({ guest, sandbox: true, call, construct: call, name, length: name === "AggregateError" ? 2 : 1 });
|
|
33
41
|
errorConstructorNames.set(closure, name);
|
|
34
42
|
return closure;
|
|
35
43
|
}
|
|
44
|
+
export function createErrorPrototypes(budget, constructors) {
|
|
45
|
+
const prototypes = new Map();
|
|
46
|
+
for (const name of errorNames)
|
|
47
|
+
prototypes.set(name, Object.create(null));
|
|
48
|
+
errorPrototypes.set(budget, prototypes);
|
|
49
|
+
for (const name of errorNames) {
|
|
50
|
+
const constructor = constructors[name];
|
|
51
|
+
const prototype = prototypes.get(name);
|
|
52
|
+
Object.defineProperties(prototype, {
|
|
53
|
+
name: { value: name, writable: true, configurable: true },
|
|
54
|
+
message: { value: "", writable: true, configurable: true },
|
|
55
|
+
constructor: { value: constructor, writable: true, configurable: true }
|
|
56
|
+
});
|
|
57
|
+
Object.defineProperty(materializeFunctionProperties(constructor), "prototype", { value: prototype, writable: false });
|
|
58
|
+
setSandboxPrototype(prototype, name === "Error" ? getSandboxPrototype(Object.create(null), budget) : prototypes.get("Error"));
|
|
59
|
+
if (name !== "Error")
|
|
60
|
+
setSandboxPrototype(constructor, constructors.Error);
|
|
61
|
+
}
|
|
62
|
+
Object.defineProperty(prototypes.get("Error"), "toString", {
|
|
63
|
+
writable: true, configurable: true,
|
|
64
|
+
value: createSandboxClosure({ guest: true, sandbox: true, name: "toString", length: 0,
|
|
65
|
+
call: async (_args, context) => {
|
|
66
|
+
const receiver = context?.thisValue;
|
|
67
|
+
if (receiver === null || typeof receiver !== "object")
|
|
68
|
+
throw new TypeError("Error.prototype.toString requires an object.");
|
|
69
|
+
let name;
|
|
70
|
+
const release = retainValues(budget, () => [receiver, name]);
|
|
71
|
+
try {
|
|
72
|
+
const rawName = await readErrorProperty(receiver, "name", budget, context);
|
|
73
|
+
name = rawName === undefined ? "Error" : await sandboxString(rawName, budget, context);
|
|
74
|
+
const rawMessage = await readErrorProperty(receiver, "message", budget, context);
|
|
75
|
+
const message = rawMessage === undefined ? "" : await sandboxString(rawMessage, budget, context);
|
|
76
|
+
return budget.allocateString(name === "" ? message : message === "" ? name : `${name}: ${message}`);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
release();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
registerBuiltinIdentities(budget, Object.fromEntries(errorNames.map(name => [name, constructors[name]])));
|
|
85
|
+
for (const name of errorNames) {
|
|
86
|
+
registerIntrinsicFunction(budget, constructors[name]);
|
|
87
|
+
registerIntrinsicObject(budget, prototypes.get(name));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function readErrorProperty(value, key, budget, context) {
|
|
91
|
+
if (context?.getProperty !== undefined)
|
|
92
|
+
return context.getProperty(value, key);
|
|
93
|
+
const descriptor = getSandboxPropertyDescriptor(value, key, budget);
|
|
94
|
+
return descriptor === undefined ? undefined : readPropertyDescriptor(descriptor, value, context);
|
|
95
|
+
}
|
|
96
|
+
async function createNativeError(name, args, budget, context, constructor) {
|
|
97
|
+
const newTarget = context?.newTarget ?? constructor;
|
|
98
|
+
const candidate = await readErrorProperty(newTarget, "prototype", budget, context);
|
|
99
|
+
const prototype = candidate !== null && typeof candidate === "object" ? candidate : errorPrototypes.get(budget).get(name);
|
|
100
|
+
let error;
|
|
101
|
+
let iterator;
|
|
102
|
+
const errors = [];
|
|
103
|
+
const release = retainValues(budget, () => [prototype, error, iterator?.retainedValue, ...errors, ...args]);
|
|
104
|
+
try {
|
|
105
|
+
const message = name === "AggregateError" ? args[1] : args[0];
|
|
106
|
+
const options = name === "AggregateError" ? args[2] : args[1];
|
|
107
|
+
const text = message === undefined ? undefined : await sandboxString(message, budget, context);
|
|
108
|
+
error = createSubsetErrorValue(name, text, context?.stack ?? [], budget);
|
|
109
|
+
setSandboxPrototype(error, prototype, budget);
|
|
110
|
+
if (options !== null && typeof options === "object" && getSandboxPropertyDescriptor(options, "cause", budget) !== undefined) {
|
|
111
|
+
const cause = await readErrorProperty(options, "cause", budget, context);
|
|
112
|
+
Object.defineProperty(error, "cause", { value: cause, writable: true, configurable: true });
|
|
113
|
+
}
|
|
114
|
+
if (name === "AggregateError") {
|
|
115
|
+
iterator = context === undefined ? getSandboxIterator(args[0], budget) : await acquireSandboxIterator(args[0], budget, context);
|
|
116
|
+
if (iterator === undefined)
|
|
117
|
+
throw new TypeError("AggregateError errors must be iterable.");
|
|
118
|
+
while (true) {
|
|
119
|
+
budget.visitNode();
|
|
120
|
+
const result = await iterator.next();
|
|
121
|
+
if (result === null || typeof result !== "object")
|
|
122
|
+
throw new TypeError("Iterator result must be an object.");
|
|
123
|
+
if ((await readIteratorResult(iterator, result, "done")).value)
|
|
124
|
+
break;
|
|
125
|
+
budget.allocateArrayLength(errors.length + 1);
|
|
126
|
+
errors.push((await readIteratorResult(iterator, result, "value")).value);
|
|
127
|
+
}
|
|
128
|
+
Object.defineProperty(error, "errors", { value: errors, writable: true, configurable: true });
|
|
129
|
+
}
|
|
130
|
+
return error;
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
release();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
36
136
|
function createSubsetError(name, args, stackFrames, budget) {
|
|
37
137
|
const message = name === "AggregateError" ? args[1] : args[0];
|
|
38
138
|
const options = name === "AggregateError" ? args[2] : args[1];
|
|
@@ -4,6 +4,7 @@ export declare function createBuiltinBindings(options: Parameters<typeof createC
|
|
|
4
4
|
random?: () => number;
|
|
5
5
|
clock?: RunClock;
|
|
6
6
|
functionHasInstance?: boolean;
|
|
7
|
+
errorPrototypes?: boolean;
|
|
7
8
|
}): {
|
|
8
9
|
RegExp: import("./values.js").SandboxClosure;
|
|
9
10
|
Promise: import("./values.js").SandboxClosure;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createConsoleJsonGlobals } from "./globals/console-json.js";
|
|
2
2
|
import { createCollectionGlobals } from "./globals/collections.js";
|
|
3
3
|
import { createFloat32ArrayGlobal } from "./globals/float32array.js";
|
|
4
|
-
import { createErrorGlobals } from "./globals/error.js";
|
|
4
|
+
import { createErrorGlobals, createErrorPrototypes } from "./globals/error.js";
|
|
5
5
|
import { createMathGlobals } from "./globals/math.js";
|
|
6
6
|
import { createRegexGlobals } from "./globals/regex.js";
|
|
7
7
|
import { createMiscGlobals } from "./globals/misc.js";
|
|
@@ -22,7 +22,7 @@ export function createBuiltinBindings(options) {
|
|
|
22
22
|
Date: createDateGlobal(options),
|
|
23
23
|
Symbol: createSymbolGlobal(options.budget),
|
|
24
24
|
BigInt: createBigIntGlobal(options.budget),
|
|
25
|
-
...createErrorGlobals(options),
|
|
25
|
+
...createErrorGlobals({ ...options, errorPrototypes: options.errorPrototypes !== false }),
|
|
26
26
|
...createMathGlobals({ random: options.random, budget: options.budget }),
|
|
27
27
|
...createObjectArrayGlobals(options),
|
|
28
28
|
...createMiscGlobals(options),
|
|
@@ -31,6 +31,8 @@ export function createBuiltinBindings(options) {
|
|
|
31
31
|
...createRegexGlobals(options)
|
|
32
32
|
};
|
|
33
33
|
createFunctionPrototype(options.budget, options.functionHasInstance);
|
|
34
|
+
if (options.errorPrototypes !== false)
|
|
35
|
+
createErrorPrototypes(options.budget, bindings);
|
|
34
36
|
createGeneratorPrototypes(options.budget);
|
|
35
37
|
registerBuiltinIdentities(options.budget, bindings);
|
|
36
38
|
return bindings;
|
|
@@ -408,10 +408,12 @@ function createHostErrorValue(reason, stackFrames, budget, span, state = { seen:
|
|
|
408
408
|
const error = reason instanceof Error
|
|
409
409
|
? createSubsetErrorValue(reason.name, reason.message, stackFrames, budget, {
|
|
410
410
|
cause: reason,
|
|
411
|
-
chargeBudget
|
|
411
|
+
chargeBudget,
|
|
412
|
+
transport: true
|
|
412
413
|
})
|
|
413
414
|
: createSubsetErrorValue("Error", describeThrownReason(reason), stackFrames, budget, {
|
|
414
|
-
chargeBudget: false
|
|
415
|
+
chargeBudget: false,
|
|
416
|
+
transport: true
|
|
415
417
|
});
|
|
416
418
|
if (reason instanceof Error) {
|
|
417
419
|
state.seen.set(reason, error);
|
|
@@ -4,7 +4,7 @@ import { invokeBuiltinClosure } from "./builtin-call.js";
|
|
|
4
4
|
import { isFloat32Array } from "./float32.js";
|
|
5
5
|
import { isFloat32ArrayConstructor } from "./globals/float32array.js";
|
|
6
6
|
import { isSandboxErrorConstructorInstance } from "./globals/error.js";
|
|
7
|
-
import { getSandboxPropertyDescriptor, getSandboxPrototype, isGuestClosure } from "./object-model.js";
|
|
7
|
+
import { getSandboxPropertyDescriptor, getSandboxPrototype, hasExplicitSandboxPrototype, isGuestClosure } from "./object-model.js";
|
|
8
8
|
import { isSandboxClosure } from "./values.js";
|
|
9
9
|
export async function evaluateInstanceof(value, constructor, budget, context) {
|
|
10
10
|
if (constructor === null || typeof constructor !== "object")
|
|
@@ -29,7 +29,8 @@ export async function ordinaryHasInstance(value, constructor, budget, context) {
|
|
|
29
29
|
// These existing built-ins do not yet have ordinary prototype graphs.
|
|
30
30
|
if (isFloat32ArrayConstructor(constructor))
|
|
31
31
|
return isFloat32Array(value);
|
|
32
|
-
if (
|
|
32
|
+
if ((!isGuestClosure(constructor) || (value !== null && typeof value === "object" && !hasExplicitSandboxPrototype(value))) &&
|
|
33
|
+
isSandboxErrorConstructorInstance(value, constructor))
|
|
33
34
|
return true;
|
|
34
35
|
if (!isGuestClosure(constructor) || typeof value !== "object" || value === null)
|
|
35
36
|
return false;
|
|
@@ -7,6 +7,8 @@ import { releaseTemplateObjects } from "./template-objects.js";
|
|
|
7
7
|
import { isSandboxDate } from "./date.js";
|
|
8
8
|
import { retainedAccessorClosures } from "./accessors.js";
|
|
9
9
|
import { getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
|
|
10
|
+
import { errorPrototypes } from "./error-prototypes.js";
|
|
11
|
+
import { sandboxErrorTypes } from "../error/shape.js";
|
|
10
12
|
import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
11
13
|
import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, getPromiseProperties, isSandboxRegex, getRegexProperties, getCollectionProperties, isSandboxSet } from "./values.js";
|
|
12
14
|
const guestClosures = new WeakSet();
|
|
@@ -273,12 +275,21 @@ export function releaseObjectPrototype(budget) {
|
|
|
273
275
|
arrayPrototypes.delete(budget);
|
|
274
276
|
functionPrototypes.delete(budget);
|
|
275
277
|
generatorPrototypes.delete(budget);
|
|
278
|
+
errorPrototypes.delete(budget);
|
|
276
279
|
initialRegexDescriptors.delete(budget);
|
|
277
280
|
intrinsicPrototypes.delete(budget);
|
|
278
281
|
}
|
|
279
282
|
export function getSandboxPrototype(value, budget) {
|
|
280
283
|
if (prototypes.has(value))
|
|
281
284
|
return prototypes.get(value) ?? null;
|
|
285
|
+
// Host transport records are data-only. Resolve their default prototype in
|
|
286
|
+
// the receiving realm without persisting executable intrinsic graphs.
|
|
287
|
+
const errorType = sandboxErrorTypes.get(value);
|
|
288
|
+
if (budget !== undefined && errorType !== undefined) {
|
|
289
|
+
const prototype = errorPrototypes.get(budget)?.get(errorType);
|
|
290
|
+
if (prototype !== undefined)
|
|
291
|
+
return prototype;
|
|
292
|
+
}
|
|
282
293
|
if (budget !== undefined && isGuestClosure(value) && runResources.getStore()?.functionSourceText !== false)
|
|
283
294
|
return functionPrototypes.get(budget) ?? null;
|
|
284
295
|
if (budget !== undefined && Array.isArray(value))
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
|
|
2
2
|
import { internalSymbols } from "./internal-symbols.js";
|
|
3
|
+
import { getIntrinsicIdentity } from "./intrinsics.js";
|
|
3
4
|
import { getGeneratorProperties } from "./generator-properties.js";
|
|
4
5
|
import { getRegexProperties, regexGuestProperties } from "./regexp-properties.js";
|
|
5
6
|
import { getCollectionProperties, collectionGuestProperties, copyCollectionProperties } from "./collection-properties.js";
|
|
@@ -24,7 +25,7 @@ import { invokeCancelableClosure, readPromiseCancellation, registerPromiseCancel
|
|
|
24
25
|
import { parseRegex } from "./regex/parse.js";
|
|
25
26
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
26
27
|
import { sandboxErrorTypes } from "../error/shape.js";
|
|
27
|
-
import { getGuestFunctionProperties, getSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, hasNullObjectPrototype, isGuestClosure, isIntrinsicFunction, registerGuestClosure, setSandboxPrototype } from "./object-model.js";
|
|
28
|
+
import { getGuestFunctionProperties, getSandboxPrototype, hasExplicitSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, hasNullObjectPrototype, isGuestClosure, isIntrinsicFunction, registerGuestClosure, setSandboxPrototype } from "./object-model.js";
|
|
28
29
|
import { copySandboxArgumentProperties, createSandboxArguments, getSandboxArgumentEntries, isSandboxArguments } from "./arguments.js";
|
|
29
30
|
export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
|
|
30
31
|
export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
|
|
@@ -578,7 +579,7 @@ export function measureSandboxData(values, options = {}) {
|
|
|
578
579
|
if (descriptor !== undefined)
|
|
579
580
|
descriptors.push([key, descriptor]);
|
|
580
581
|
}
|
|
581
|
-
const includeNonEnumerable = isSandboxDate(value) || hasManagedDescriptors(value);
|
|
582
|
+
const includeNonEnumerable = isSandboxDate(value) || sandboxErrorTypes.has(value) || hasManagedDescriptors(value);
|
|
582
583
|
for (const [key, descriptor] of descriptors) {
|
|
583
584
|
if (!descriptor.enumerable && !includeNonEnumerable)
|
|
584
585
|
continue;
|
|
@@ -908,6 +909,50 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
|
|
|
908
909
|
}
|
|
909
910
|
if (nodeTypes.isProxy(value))
|
|
910
911
|
throw new TypeError("Unsupported proxy sandbox value.");
|
|
912
|
+
if (sandboxErrorTypes.has(value) && hasExplicitSandboxPrototype(value)) {
|
|
913
|
+
const prototype = getSandboxPrototype(value);
|
|
914
|
+
let nativePrototype = null;
|
|
915
|
+
if (prototype !== null) {
|
|
916
|
+
const identity = getIntrinsicIdentity(prototype);
|
|
917
|
+
const nativePrototypes = {
|
|
918
|
+
Error: Error.prototype, TypeError: TypeError.prototype, RangeError: RangeError.prototype,
|
|
919
|
+
ReferenceError: ReferenceError.prototype, SyntaxError: SyntaxError.prototype,
|
|
920
|
+
URIError: URIError.prototype, EvalError: EvalError.prototype, AggregateError: AggregateError.prototype
|
|
921
|
+
};
|
|
922
|
+
const path = identity === undefined ? [] : JSON.parse(identity);
|
|
923
|
+
if (path.length !== 2 || typeof path[0] !== "string" || path[1] !== "prototype" || !Object.hasOwn(nativePrototypes, path[0]))
|
|
924
|
+
throw new TypeError("Custom Error prototype links cannot be copied as data.");
|
|
925
|
+
nativePrototype = nativePrototypes[path[0]];
|
|
926
|
+
let current = prototype;
|
|
927
|
+
let prototypeDepth = 0;
|
|
928
|
+
while (current !== null) {
|
|
929
|
+
assertSandboxDataDepth(++prototypeDepth);
|
|
930
|
+
const objectPrototype = getIntrinsicIdentity(current) === '["Object","prototype"]';
|
|
931
|
+
const owner = objectPrototype ? Object.getOwnPropertyDescriptor(current, "constructor")?.value : current;
|
|
932
|
+
if (owner === null || typeof owner !== "object" ||
|
|
933
|
+
(objectPrototype && getIntrinsicIdentity(owner) !== '["Object"]') || hasGuestObjectState(owner))
|
|
934
|
+
throw new TypeError("Modified Error prototype links cannot be copied as data.");
|
|
935
|
+
current = getSandboxPrototype(current);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
const existing = state.seen.get(value);
|
|
939
|
+
if (existing !== undefined)
|
|
940
|
+
return existing;
|
|
941
|
+
const descriptors = Reflect.ownKeys(value).filter(key => typeof key !== "symbol" || !internalSymbols.has(key))
|
|
942
|
+
.map(key => [key, Object.getOwnPropertyDescriptor(value, key)]);
|
|
943
|
+
if (descriptors.some(([, descriptor]) => !("value" in descriptor)))
|
|
944
|
+
throw new TypeError("Error accessor properties cannot be copied as data.");
|
|
945
|
+
const copy = new Error();
|
|
946
|
+
delete copy.stack;
|
|
947
|
+
Object.setPrototypeOf(copy, nativePrototype);
|
|
948
|
+
state.seen.set(value, copy);
|
|
949
|
+
for (const [key, descriptor] of descriptors)
|
|
950
|
+
Object.defineProperty(copy, key, { ...descriptor,
|
|
951
|
+
value: copyFromSandbox(descriptor.value, state, joinPath(path, key), options, depth + 1) });
|
|
952
|
+
if (!Object.isExtensible(value))
|
|
953
|
+
Object.preventExtensions(copy);
|
|
954
|
+
return copy;
|
|
955
|
+
}
|
|
911
956
|
if (!isSandboxClosure(value) && hasGuestObjectState(value)) {
|
|
912
957
|
throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
|
|
913
958
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Budget } from "../interp/budget.js";
|
|
2
|
+
import { sandboxErrorNames } from "../error/shape.js";
|
|
2
3
|
import { createRawJson } from "../interp/raw-json.js";
|
|
3
4
|
import { createBuiltinBindings } from "../interp/globals.js";
|
|
4
5
|
import { getIntrinsicIdentity, listIntrinsicIdentities, resolveIntrinsicIdentity } from "../interp/intrinsics.js";
|
|
@@ -146,7 +147,9 @@ export function validateGuestHeapNode(raw, heap, maxArrayLength = 0xffffffff) {
|
|
|
146
147
|
state(node.state);
|
|
147
148
|
}
|
|
148
149
|
else if (node.kind === "guest-object" || node.kind === "guest-array") {
|
|
149
|
-
fields(node, ["kind", "state"], node.kind === "guest-array" ? ["templateNodeId", "templateOwner"] : []);
|
|
150
|
+
fields(node, ["kind", "state"], node.kind === "guest-array" ? ["templateNodeId", "templateOwner"] : ["errorType"]);
|
|
151
|
+
if (Object.hasOwn(node, "errorType") && !sandboxErrorNames.includes(node.errorType))
|
|
152
|
+
throw new TypeError("Invalid guest error type.");
|
|
150
153
|
if (Object.hasOwn(node, "templateOwner"))
|
|
151
154
|
reference(node.templateOwner, ["guest-array"]);
|
|
152
155
|
if (Object.hasOwn(node, "templateNodeId") && integer(node.templateNodeId) < 1)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SandboxErrorName } from "../error/shape.js";
|
|
1
2
|
import { type ScopeFrame } from "../interp/scope.js";
|
|
2
3
|
import { type PropertyDescriptorData } from "./property-descriptors.js";
|
|
3
4
|
import type { CompletionResult } from "../interp/exceptions.js";
|
|
@@ -73,6 +74,7 @@ export type GuestHeapNode<T> = {
|
|
|
73
74
|
} | {
|
|
74
75
|
kind: "guest-object";
|
|
75
76
|
state: GuestObjectState<T>;
|
|
77
|
+
errorType?: SandboxErrorName;
|
|
76
78
|
} | {
|
|
77
79
|
kind: "guest-array";
|
|
78
80
|
state: GuestObjectState<T>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getClosureOrigin, getGeneratorOrigin } from "../interp/closure-origin.js";
|
|
2
2
|
import { boundFunctionStates } from "../interp/bound-function-state.js";
|
|
3
|
+
import { sandboxErrorTypes } from "../error/shape.js";
|
|
3
4
|
import { getGeneratorProperties } from "../interp/generator-properties.js";
|
|
4
5
|
import { getIntrinsicIdentity } from "../interp/intrinsics.js";
|
|
5
6
|
import { getSandboxPrototype, hasExplicitSandboxPrototype, hasGuestObjectState, isGuestClosure, materializeFunctionProperties } from "../interp/object-model.js";
|
|
@@ -151,8 +152,11 @@ export function captureGuestHeapNode(value, encode) {
|
|
|
151
152
|
isSandboxRegExpIterator(value))
|
|
152
153
|
return undefined;
|
|
153
154
|
const prototype = Object.getPrototypeOf(value);
|
|
154
|
-
if ((prototype === null || prototype === Object.prototype) && hasGuestObjectState(value))
|
|
155
|
-
|
|
155
|
+
if ((prototype === null || prototype === Object.prototype) && hasGuestObjectState(value)) {
|
|
156
|
+
const errorType = sandboxErrorTypes.get(value);
|
|
157
|
+
return { kind: "guest-object", state: captureObjectState(value, encode),
|
|
158
|
+
...(errorType === undefined ? {} : { errorType }) };
|
|
159
|
+
}
|
|
156
160
|
return undefined;
|
|
157
161
|
}
|
|
158
162
|
if (origin.node.nodeId === undefined)
|
|
@@ -22,7 +22,7 @@ import { restoreDateTime } from "../interp/date.js";
|
|
|
22
22
|
import { createRawJson } from "../interp/raw-json.js";
|
|
23
23
|
import { createSandboxBox } from "../interp/boxed.js";
|
|
24
24
|
import { restoreBoxedProperties } from "./boxed.js";
|
|
25
|
-
import { sandboxErrorTypes } from "../error/shape.js";
|
|
25
|
+
import { sandboxErrorNames, sandboxErrorTypes } from "../error/shape.js";
|
|
26
26
|
import { SnapshotMismatchError } from "../restore.js";
|
|
27
27
|
import { evaluateNode, Scope, setSandboxProperty } from "../interp/interpreter.js";
|
|
28
28
|
import { getGuestFunctionProperties, getGuestFunctionProperty, getSandboxDataProperty, isGuestClosure, materializeFunctionProperties, registerGuestClosure, setSandboxPrototype } from "../interp/object-model.js";
|
|
@@ -398,7 +398,20 @@ function initializeIntrinsicRealm(state) {
|
|
|
398
398
|
const method = state.heap[String(value.id)];
|
|
399
399
|
return method?.kind === "intrinsic" && method.id === '["%FunctionPrototype%",{"symbol":"hasInstance"}]';
|
|
400
400
|
});
|
|
401
|
-
|
|
401
|
+
const errorConstructors = Object.values(state.heap).filter(node => node.kind === "intrinsic" && sandboxErrorNames.some(name => node.id === JSON.stringify([name])));
|
|
402
|
+
const errorPrototypes = errorConstructors.every(node => node.kind === "intrinsic" &&
|
|
403
|
+
node.state?.properties.properties.some(([key, descriptor]) => {
|
|
404
|
+
if (key !== "prototype" || descriptor.kind !== "data" || descriptor.writable ||
|
|
405
|
+
descriptor.enumerable || descriptor.configurable)
|
|
406
|
+
return false;
|
|
407
|
+
const value = descriptor.value;
|
|
408
|
+
if (value === null || typeof value !== "object" || !("kind" in value) || value.kind !== "ref")
|
|
409
|
+
return false;
|
|
410
|
+
const prototype = state.heap[String(value.id)];
|
|
411
|
+
return prototype?.kind === "intrinsic" &&
|
|
412
|
+
prototype.id === JSON.stringify([...JSON.parse(node.id), "prototype"]);
|
|
413
|
+
}));
|
|
414
|
+
createBuiltinBindings({ budget: state.budget, compileOwner: state.compilation.owner, functionHasInstance, errorPrototypes });
|
|
402
415
|
state.intrinsicsInitialized = true;
|
|
403
416
|
}
|
|
404
417
|
function restoreHeapValue(id, state) {
|
|
@@ -653,6 +666,8 @@ function restoreHeapValue(id, state) {
|
|
|
653
666
|
throw new TypeError(`Missing restored function properties for ${serialized.kind === "intrinsic" ? serialized.id : serialized.kind}.`);
|
|
654
667
|
if (objectState.prototype !== undefined)
|
|
655
668
|
setSandboxPrototype(value, deserializeValue(objectState.prototype, state), state.budget);
|
|
669
|
+
if (serialized.kind === "guest-object" && serialized.errorType !== undefined)
|
|
670
|
+
sandboxErrorTypes.set(value, serialized.errorType);
|
|
656
671
|
restorePropertyDescriptors(target, objectState.properties, entry => deserializeValue(entry, state));
|
|
657
672
|
if (serialized.kind === "guest-array" && serialized.templateNodeId !== undefined) {
|
|
658
673
|
const node = state.nodeById.get(serialized.templateNodeId);
|
|
@@ -706,7 +706,7 @@ function validateArrayHeap(record, path, state) {
|
|
|
706
706
|
function validateErrorType(record, path) {
|
|
707
707
|
if (!Object.hasOwn(record, "errorType"))
|
|
708
708
|
return;
|
|
709
|
-
if (record.kind !== "object" ||
|
|
709
|
+
if ((record.kind !== "object" && record.kind !== "guest-object") ||
|
|
710
710
|
!sandboxErrorNames.includes(record.errorType)) {
|
|
711
711
|
fail("invalidValue", `${path}.errorType`, "invalid error metadata");
|
|
712
712
|
}
|