poe-code 14.0.60 → 14.0.62
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 +313 -135
- 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-C7UCTQDN.js → chunk-F7TJSPDZ.js} +7 -3
- package/packages/safe-js/dist/chunks/chunk-F7TJSPDZ.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-MKJBQPBH.js → chunk-VKGWGZBH.js} +268 -126
- package/packages/safe-js/dist/chunks/chunk-VKGWGZBH.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/async.js +3 -0
- package/packages/safe-js/dist/interp/function-string.d.ts +2 -0
- package/packages/safe-js/dist/interp/function-string.js +21 -0
- package/packages/safe-js/dist/interp/globals/misc.js +11 -12
- package/packages/safe-js/dist/interp/globals/numeric-parsers.d.ts +5 -0
- package/packages/safe-js/dist/interp/globals/numeric-parsers.js +47 -0
- package/packages/safe-js/dist/interp/globals/object-array.js +2 -10
- package/packages/safe-js/dist/interp/interpreter.js +9 -14
- package/packages/safe-js/dist/interp/methods/function.d.ts +2 -0
- package/packages/safe-js/dist/interp/methods/function.js +10 -1
- package/packages/safe-js/dist/interp/methods/string.js +18 -0
- package/packages/safe-js/dist/interp/resources.d.ts +3 -0
- package/packages/safe-js/dist/interp/resources.js +11 -0
- package/packages/safe-js/dist/interp/string-coercion.js +16 -4
- package/packages/safe-js/dist/interp/values.d.ts +3 -0
- package/packages/safe-js/dist/interp/values.js +3 -0
- package/packages/safe-js/dist/migrate.js +1 -1
- package/packages/safe-js/dist/parse/function-source.d.ts +7 -0
- package/packages/safe-js/dist/parse/function-source.js +1 -0
- package/packages/safe-js/dist/parse/hash.d.ts +2 -2
- package/packages/safe-js/dist/parse/hash.js +23 -9
- package/packages/safe-js/dist/parse/parser.js +36 -23
- package/packages/safe-js/dist/restore.js +2 -1
- package/packages/safe-js/dist/run.js +16 -5
- package/packages/safe-js/dist/snapshot/dump-format.d.ts +1 -1
- package/packages/safe-js/dist/snapshot/dump-format.js +1 -1
- package/packages/safe-js/dist/snapshot/migration.js +2 -2
- package/packages/safe-js/dist/snapshot/restore.js +2 -0
- package/packages/safe-js/dist/chunks/chunk-C7UCTQDN.js.map +0 -7
- package/packages/safe-js/dist/chunks/chunk-MKJBQPBH.js.map +0 -7
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveFsConfig,
|
|
23
23
|
splitFrontmatter,
|
|
24
24
|
withBrokenPipeGuard
|
|
25
|
-
} from "./chunks/chunk-
|
|
25
|
+
} from "./chunks/chunk-F7TJSPDZ.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-VKGWGZBH.js";
|
|
38
38
|
import "./chunks/chunk-UXMKEUH3.js";
|
|
39
39
|
import "./chunks/chunk-5DYEOCT5.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-F7TJSPDZ.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-VKGWGZBH.js";
|
|
54
54
|
import "./chunks/chunk-UXMKEUH3.js";
|
|
55
55
|
import "./chunks/chunk-5DYEOCT5.js";
|
|
56
56
|
import "./chunks/chunk-4YIDX44G.js";
|
|
@@ -8,6 +8,7 @@ import { awaitSandboxValue } from "./cancel.js";
|
|
|
8
8
|
import { hoistVarDeclarations } from "./var-hoist.js";
|
|
9
9
|
import { createPatternContext } from "./interpreter.js";
|
|
10
10
|
import { getGuestFunctionProperty, setSandboxPrototype } from "./object-model.js";
|
|
11
|
+
import { functionSources } from "../parse/function-source.js";
|
|
11
12
|
import { boundIdentifiers, containsParameterExpression, getFunctionLength, hoistedVarDeclarations } from "../parse/bindings.js";
|
|
12
13
|
import { createSandboxArguments, createSandboxClosure, createSandboxGenerator, createSandboxPromise, allocateProducedSandboxValue, reconcileCompiledValues, isSandboxPromise } from "./values.js";
|
|
13
14
|
export function emitResumeBreakpoint(context, breakpoint) {
|
|
@@ -62,6 +63,7 @@ export function createInterpretedClosure(node, context, evaluateNode) {
|
|
|
62
63
|
}
|
|
63
64
|
: undefined;
|
|
64
65
|
const closure = createSandboxClosure({
|
|
66
|
+
sourceRange: functionSources.get(node),
|
|
65
67
|
guest: true,
|
|
66
68
|
sandbox: true,
|
|
67
69
|
length: getFunctionLength(node.params),
|
|
@@ -116,6 +118,7 @@ export function executeAsyncFunction(execute, budget, signal) {
|
|
|
116
118
|
}
|
|
117
119
|
function createGeneratorClosure(node, context, evaluateNode) {
|
|
118
120
|
return createSandboxClosure({
|
|
121
|
+
sourceRange: functionSources.get(node),
|
|
119
122
|
guest: true,
|
|
120
123
|
generator: true,
|
|
121
124
|
sandbox: true,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { runResources } from "./resources.js";
|
|
2
|
+
export function functionString(value) {
|
|
3
|
+
if (runResources.getStore()?.functionSourceText === false)
|
|
4
|
+
return "[object Object]";
|
|
5
|
+
const source = value.sourceRange;
|
|
6
|
+
if (source !== undefined)
|
|
7
|
+
return source.text.slice(source.start, source.end);
|
|
8
|
+
let name = value.boundTarget === undefined ? (value.name?.split("#").at(-1) ?? "") : "";
|
|
9
|
+
for (let index = 0; index < name.length; index++) {
|
|
10
|
+
const code = name.charCodeAt(index);
|
|
11
|
+
if (!((code >= 65 && code <= 90) ||
|
|
12
|
+
(code >= 97 && code <= 122) ||
|
|
13
|
+
code === 36 ||
|
|
14
|
+
code === 95 ||
|
|
15
|
+
(index > 0 && code >= 48 && code <= 57))) {
|
|
16
|
+
name = "";
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return `function ${name}() { [native code] }`;
|
|
21
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isSandboxCollectionIterator } from "../collection-iterator.js";
|
|
2
2
|
import { assertSandboxGraphDepth } from "../../graph-depth.js";
|
|
3
3
|
import { CompileScope } from "../regex/compile-guard.js";
|
|
4
|
+
import { createNumericParsers } from "./numeric-parsers.js";
|
|
5
|
+
import { sandboxNumber } from "../string-coercion.js";
|
|
4
6
|
import { allocateProducedSandboxValue, cloneSandboxValue, createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet, reconcileCompiledValues } from "../values.js";
|
|
5
7
|
export function createMiscGlobals(options) {
|
|
6
8
|
return {
|
|
@@ -9,24 +11,21 @@ export function createMiscGlobals(options) {
|
|
|
9
11
|
call: ([value], context) => structuredCloneSandboxValue(value, options.budget, context?.compilation),
|
|
10
12
|
name: "structuredClone"
|
|
11
13
|
}),
|
|
12
|
-
|
|
13
|
-
sandbox: true,
|
|
14
|
-
call: (args) => Reflect.apply(globalThis.parseInt, globalThis, [...args]),
|
|
15
|
-
name: "parseInt"
|
|
16
|
-
}),
|
|
17
|
-
parseFloat: createSandboxClosure({
|
|
18
|
-
sandbox: true,
|
|
19
|
-
call: (args) => Reflect.apply(globalThis.parseFloat, globalThis, [...args]),
|
|
20
|
-
name: "parseFloat"
|
|
21
|
-
}),
|
|
14
|
+
...createNumericParsers(options.budget),
|
|
22
15
|
isNaN: createSandboxClosure({
|
|
23
16
|
sandbox: true,
|
|
24
|
-
call: ([value]) =>
|
|
17
|
+
call: ([value], context) => {
|
|
18
|
+
const number = sandboxNumber(value, options.budget, context);
|
|
19
|
+
return typeof number === "number" ? Number.isNaN(number) : number.then(Number.isNaN);
|
|
20
|
+
},
|
|
25
21
|
name: "isNaN"
|
|
26
22
|
}),
|
|
27
23
|
isFinite: createSandboxClosure({
|
|
28
24
|
sandbox: true,
|
|
29
|
-
call: ([value]) =>
|
|
25
|
+
call: ([value], context) => {
|
|
26
|
+
const number = sandboxNumber(value, options.budget, context);
|
|
27
|
+
return typeof number === "number" ? Number.isFinite(number) : number.then(Number.isFinite);
|
|
28
|
+
},
|
|
30
29
|
name: "isFinite"
|
|
31
30
|
})
|
|
32
31
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { retainValues } from "../resources.js";
|
|
2
|
+
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
3
|
+
import { createSandboxClosure } from "../values.js";
|
|
4
|
+
export function createNumericParsers(budget) {
|
|
5
|
+
return {
|
|
6
|
+
parseInt: createSandboxClosure({
|
|
7
|
+
sandbox: true,
|
|
8
|
+
name: "parseInt",
|
|
9
|
+
call: ([value, radix], context) => {
|
|
10
|
+
const parse = (text) => {
|
|
11
|
+
const release = retainValues(budget, () => [text]);
|
|
12
|
+
let convertedRadix;
|
|
13
|
+
try {
|
|
14
|
+
convertedRadix = sandboxNumber(radix, budget, context);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
release();
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
if (typeof convertedRadix === "number") {
|
|
21
|
+
try {
|
|
22
|
+
return globalThis.parseInt(text, convertedRadix);
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
release();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return convertedRadix
|
|
29
|
+
.then((number) => globalThis.parseInt(text, number))
|
|
30
|
+
.finally(release);
|
|
31
|
+
};
|
|
32
|
+
const text = sandboxString(value, budget, context);
|
|
33
|
+
return typeof text === "string" ? parse(text) : text.then(parse);
|
|
34
|
+
}
|
|
35
|
+
}),
|
|
36
|
+
parseFloat: createSandboxClosure({
|
|
37
|
+
sandbox: true,
|
|
38
|
+
name: "parseFloat",
|
|
39
|
+
call: ([value], context) => {
|
|
40
|
+
const text = sandboxString(value, budget, context);
|
|
41
|
+
return typeof text === "string"
|
|
42
|
+
? globalThis.parseFloat(text)
|
|
43
|
+
: text.then(globalThis.parseFloat);
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -10,6 +10,7 @@ import { isFloat32Array } from "../float32.js";
|
|
|
10
10
|
import { setSandboxProperty } from "../interpreter.js";
|
|
11
11
|
import { getSandboxIterator } from "../iteration.js";
|
|
12
12
|
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
13
|
+
import { createNumericParsers } from "./numeric-parsers.js";
|
|
13
14
|
import { getSandboxDataProperty, getSandboxPrototype, isGuestClosure, markDescriptorObject, materializeFunctionProperties, setSandboxPrototype } from "../object-model.js";
|
|
14
15
|
import { allocateProducedSandboxValue, createSandboxClosure, deepCopyToSandbox, defineOwnDataProperty, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet, measureSandboxData, ownEnumerableSandboxEntries as getOwnEnumerableEntries } from "../values.js";
|
|
15
16
|
export function createObjectArrayGlobals(options) {
|
|
@@ -222,16 +223,7 @@ export function createObjectArrayGlobals(options) {
|
|
|
222
223
|
call: ([value]) => typeof value === "number" && Number.isInteger(value),
|
|
223
224
|
name: "isInteger"
|
|
224
225
|
}),
|
|
225
|
-
|
|
226
|
-
sandbox: true,
|
|
227
|
-
call: (args) => Reflect.apply(Number.parseInt, Number, [...args]),
|
|
228
|
-
name: "parseInt"
|
|
229
|
-
}),
|
|
230
|
-
parseFloat: createSandboxClosure({
|
|
231
|
-
sandbox: true,
|
|
232
|
-
call: (args) => Reflect.apply(Number.parseFloat, Number, [...args]),
|
|
233
|
-
name: "parseFloat"
|
|
234
|
-
}),
|
|
226
|
+
...createNumericParsers(options.budget),
|
|
235
227
|
isSafeInteger: createSandboxClosure({
|
|
236
228
|
sandbox: true,
|
|
237
229
|
call: ([value]) => typeof value === "number" && Number.isSafeInteger(value),
|
|
@@ -4,7 +4,8 @@ import { toPropertyKey } from "./property-key.js";
|
|
|
4
4
|
import { assertPromiseExecutionAllowed } from "./promise-tracker.js";
|
|
5
5
|
import { SandboxJobQueue, runAsyncPrefix, suspendJob } from "./jobs.js";
|
|
6
6
|
import { withCancellationSignal } from "./cancel.js";
|
|
7
|
-
import {
|
|
7
|
+
import { retainValues } from "./resources.js";
|
|
8
|
+
import { sandboxString } from "./string-coercion.js";
|
|
8
9
|
import { attachErrorSpan, formatErrorStack, replaceErrorStack } from "../error/shape.js";
|
|
9
10
|
import { evaluateArrowFunctionExpression, evaluateFunctionExpression, evaluateAwaitExpression, emitResumeBreakpoint, createInterpretedClosure, normalizeClosureResult } from "./async.js";
|
|
10
11
|
import { HostCallResumabilityError } from "./host-call.js";
|
|
@@ -360,14 +361,18 @@ function isObjectPrototypeSetterProperty(property, key) {
|
|
|
360
361
|
}
|
|
361
362
|
async function evaluateTemplateLiteral(node, context) {
|
|
362
363
|
let value = context.budget.allocateString(node.quasis[0]?.value.cooked ?? "");
|
|
363
|
-
|
|
364
|
+
let input = undefined;
|
|
365
|
+
const release = retainValues(context.budget, () => [value, input]);
|
|
364
366
|
try {
|
|
365
367
|
for (let index = 0; index < node.expressions.length; index += 1) {
|
|
366
368
|
const expression = await evaluateNode(node.expressions[index], context);
|
|
367
369
|
if (expression.kind !== "normal") {
|
|
368
370
|
return expression;
|
|
369
371
|
}
|
|
370
|
-
|
|
372
|
+
input = expression.value;
|
|
373
|
+
const text = sandboxString(input, context.budget, createCoercionContext(context));
|
|
374
|
+
const expressionText = typeof text === "string" ? text : await text;
|
|
375
|
+
input = undefined;
|
|
371
376
|
value = context.budget.allocateString(value + expressionText);
|
|
372
377
|
const quasiText = context.budget.allocateString(node.quasis[index + 1]?.value.cooked ?? "");
|
|
373
378
|
value = context.budget.allocateString(value + quasiText);
|
|
@@ -1831,17 +1836,6 @@ async function evaluateMemberAccess(node, context, consume) {
|
|
|
1831
1836
|
release();
|
|
1832
1837
|
}
|
|
1833
1838
|
}
|
|
1834
|
-
function retainValues(budget, values) {
|
|
1835
|
-
const retained = {};
|
|
1836
|
-
budget.setRetainedValues(retained, values);
|
|
1837
|
-
const pending = runResources.getStore()?.referenceReleases;
|
|
1838
|
-
const release = () => {
|
|
1839
|
-
budget.setRetainedValues(retained, undefined);
|
|
1840
|
-
pending?.delete(release);
|
|
1841
|
-
};
|
|
1842
|
-
pending?.add(release);
|
|
1843
|
-
return release;
|
|
1844
|
-
}
|
|
1845
1839
|
async function evaluateMemberProperty(node, context) {
|
|
1846
1840
|
const property = await evaluateNode(node, context);
|
|
1847
1841
|
if (property.kind !== "normal") {
|
|
@@ -2552,6 +2546,7 @@ function createSetMethodOptions(context) {
|
|
|
2552
2546
|
}
|
|
2553
2547
|
function createFunctionMethodOptions(context) {
|
|
2554
2548
|
return {
|
|
2549
|
+
budget: context.budget,
|
|
2555
2550
|
callClosure: (closure, args, stack, thisValue, construct) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue, construct)
|
|
2556
2551
|
};
|
|
2557
2552
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type SandboxClosure, type SandboxValue } from "../values.js";
|
|
2
|
+
import type { Budget } from "../budget.js";
|
|
2
3
|
export type FunctionMethodOptions = {
|
|
4
|
+
budget?: Budget;
|
|
3
5
|
callClosure: (closure: SandboxClosure, args: readonly SandboxValue[], stack: readonly string[], thisValue: SandboxValue, construct?: boolean) => Promise<SandboxValue> | SandboxValue;
|
|
4
6
|
};
|
|
5
7
|
export declare function getFunctionMember(target: SandboxClosure, property: string | number, options: FunctionMethodOptions): SandboxValue | undefined;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createSandboxClosure, isSandboxClosure } from "../values.js";
|
|
2
2
|
import { getGuestFunctionProperty, isGuestClosure } from "../object-model.js";
|
|
3
|
-
|
|
3
|
+
import { functionString } from "../function-string.js";
|
|
4
|
+
import { runResources } from "../resources.js";
|
|
5
|
+
const functionMethodNames = new Set(["apply", "bind", "call", "toString"]);
|
|
4
6
|
export function getFunctionMember(target, property, options) {
|
|
5
7
|
if (isGuestClosure(target)) {
|
|
6
8
|
const value = getGuestFunctionProperty(target, String(property));
|
|
@@ -16,12 +18,15 @@ export function getFunctionMember(target, property, options) {
|
|
|
16
18
|
if (property === "length") {
|
|
17
19
|
return target.length;
|
|
18
20
|
}
|
|
21
|
+
if (property === "toString" && runResources.getStore()?.functionSourceText === false)
|
|
22
|
+
return undefined;
|
|
19
23
|
if (!isFunctionMethodName(property)) {
|
|
20
24
|
return undefined;
|
|
21
25
|
}
|
|
22
26
|
return createSandboxClosure({
|
|
23
27
|
sandbox: true,
|
|
24
28
|
name: `Function#${property}`,
|
|
29
|
+
...(property === "toString" ? { length: 0 } : {}),
|
|
25
30
|
call: (args, context) => callFunctionMethod(context?.thisValue, property, args, options, context?.stack ?? [])
|
|
26
31
|
});
|
|
27
32
|
}
|
|
@@ -32,6 +37,10 @@ function callFunctionMethod(target, methodName, args, options, stack) {
|
|
|
32
37
|
if (!isSandboxClosure(target)) {
|
|
33
38
|
throw new TypeError(`Function#${methodName} requires a callable receiver.`);
|
|
34
39
|
}
|
|
40
|
+
if (methodName === "toString") {
|
|
41
|
+
const text = functionString(target);
|
|
42
|
+
return options.budget?.allocateString(text) ?? text;
|
|
43
|
+
}
|
|
35
44
|
const thisValue = args[0];
|
|
36
45
|
if (methodName === "bind") {
|
|
37
46
|
const boundArgs = args.slice(1);
|
|
@@ -2,6 +2,7 @@ import { invokeBuiltinClosure } from "../builtin-call.js";
|
|
|
2
2
|
import { CompileScope } from "../regex/compile-guard.js";
|
|
3
3
|
import { normalizeLastIndex } from "../regex/engine.js";
|
|
4
4
|
import { sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
5
|
+
import { retainValues } from "../resources.js";
|
|
5
6
|
import { createSandboxClosure, createSandboxRegex, deepCopyFromSandbox, isSandboxClosure, isSandboxRegex } from "../values.js";
|
|
6
7
|
import { executeRegex, toMatchArray } from "./regex.js";
|
|
7
8
|
const SPLIT_STRING_MESSAGE = "String#split only supports string separator values.";
|
|
@@ -83,6 +84,9 @@ export function isStringMethodName(property) {
|
|
|
83
84
|
}
|
|
84
85
|
export function validateStringMethodArguments(_methodName, _args) { }
|
|
85
86
|
export function callStringMethod(value, methodName, args, budget, callClosure = async (closure, closureArgs) => await closure.call(closureArgs), parent, context) {
|
|
87
|
+
if (methodName === "concat" && args.some(argument => argument !== null && typeof argument === "object")) {
|
|
88
|
+
return callConcat(value, args, budget, context);
|
|
89
|
+
}
|
|
86
90
|
if (methodName === "isWellFormed") {
|
|
87
91
|
for (let index = 0; index < value.length; index++) {
|
|
88
92
|
const codeUnit = value.charCodeAt(index);
|
|
@@ -239,6 +243,20 @@ export function callStringMethod(value, methodName, args, budget, callClosure =
|
|
|
239
243
|
operation.release();
|
|
240
244
|
}
|
|
241
245
|
}
|
|
246
|
+
async function callConcat(value, args, budget, context) {
|
|
247
|
+
let result = budget.allocateString(value);
|
|
248
|
+
const release = retainValues(budget, () => [result]);
|
|
249
|
+
try {
|
|
250
|
+
for (const argument of args) {
|
|
251
|
+
const text = sandboxString(argument, budget, context);
|
|
252
|
+
result = budget.allocateString(result + (typeof text === "string" ? text : await text));
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
release();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
242
260
|
function callReplaceLikeMethod(value, methodName, args, budget, callClosure, context) {
|
|
243
261
|
const search = args[0];
|
|
244
262
|
const replacement = args[1];
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import type { Budget } from "./budget.js";
|
|
2
3
|
export type RunResources = {
|
|
3
4
|
signal: AbortSignal;
|
|
5
|
+
functionSourceText?: boolean;
|
|
4
6
|
referenceReleases: Set<() => void>;
|
|
5
7
|
add(close: () => Promise<void>): void;
|
|
6
8
|
};
|
|
7
9
|
export declare const runResources: AsyncLocalStorage<RunResources>;
|
|
10
|
+
export declare function retainValues(budget: Budget, values: () => Iterable<unknown>): () => void;
|
|
8
11
|
export declare function withRunResources<Result>(signal: AbortSignal | undefined, execute: () => Promise<Result>): Promise<Result>;
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
export const runResources = new AsyncLocalStorage();
|
|
3
|
+
export function retainValues(budget, values) {
|
|
4
|
+
const retained = {};
|
|
5
|
+
budget.setRetainedValues(retained, values);
|
|
6
|
+
const pending = runResources.getStore()?.referenceReleases;
|
|
7
|
+
const release = () => {
|
|
8
|
+
budget.setRetainedValues(retained, undefined);
|
|
9
|
+
pending?.delete(release);
|
|
10
|
+
};
|
|
11
|
+
pending?.add(release);
|
|
12
|
+
return release;
|
|
13
|
+
}
|
|
3
14
|
export async function withRunResources(signal, execute) {
|
|
4
15
|
const controller = new AbortController();
|
|
5
16
|
const cancel = () => controller.abort(signal?.reason);
|
|
@@ -7,6 +7,8 @@ import { isGuestHostObject } from "./host-capabilities.js";
|
|
|
7
7
|
import { collectionIteratorState, isSandboxCollectionIterator } from "./collection-iterator.js";
|
|
8
8
|
import { getGuestFunctionProperties, getSandboxPrototype, hasExplicitSandboxPrototype, isGuestClosure } from "./object-model.js";
|
|
9
9
|
import { getRegexMember } from "./methods/regex.js";
|
|
10
|
+
import { retainValues } from "./resources.js";
|
|
11
|
+
import { functionString } from "./function-string.js";
|
|
10
12
|
import { isSandboxClosure, isSandboxPromise, isSandboxMap, isSandboxSet, isSandboxRegex, isSandboxGenerator } from "./values.js";
|
|
11
13
|
const defaultStringHook = Symbol("defaultStringHook");
|
|
12
14
|
const defaultValueHook = Symbol("defaultValueHook");
|
|
@@ -95,6 +97,8 @@ function conversionHook(value, name, budget) {
|
|
|
95
97
|
return undefined;
|
|
96
98
|
}
|
|
97
99
|
async function defaultToString(value, budget, context, joining) {
|
|
100
|
+
if (isSandboxClosure(value))
|
|
101
|
+
return budget.allocateString(functionString(value));
|
|
98
102
|
if (isSandboxMap(value))
|
|
99
103
|
return "[object Map]";
|
|
100
104
|
if (isSandboxSet(value))
|
|
@@ -118,9 +122,10 @@ async function defaultToString(value, budget, context, joining) {
|
|
|
118
122
|
if (joining.has(value))
|
|
119
123
|
return "";
|
|
120
124
|
joining.add(value);
|
|
125
|
+
let text = "";
|
|
126
|
+
const release = retainValues(budget, () => [text]);
|
|
121
127
|
try {
|
|
122
128
|
const length = isFloat32Array(value) ? float32Storage(value).length : value.length;
|
|
123
|
-
let text = "";
|
|
124
129
|
for (let index = 0; index < length; index++) {
|
|
125
130
|
budget.visitNode();
|
|
126
131
|
const element = ownDataValue(value, String(index));
|
|
@@ -132,15 +137,22 @@ async function defaultToString(value, budget, context, joining) {
|
|
|
132
137
|
return text;
|
|
133
138
|
}
|
|
134
139
|
finally {
|
|
140
|
+
release();
|
|
135
141
|
joining.delete(value);
|
|
136
142
|
}
|
|
137
143
|
}
|
|
138
144
|
if (sandboxErrorTypes.has(value)) {
|
|
139
145
|
const nameValue = ownDataValue(value, "name");
|
|
140
146
|
const name = nameValue === undefined ? "Error" : await sandboxString(nameValue, budget, context, joining);
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
147
|
+
const release = retainValues(budget, () => [name]);
|
|
148
|
+
try {
|
|
149
|
+
const messageValue = ownDataValue(value, "message");
|
|
150
|
+
const message = messageValue === undefined ? "" : await sandboxString(messageValue, budget, context, joining);
|
|
151
|
+
return name === "" ? message : message === "" ? name : `${name}: ${message}`;
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
release();
|
|
155
|
+
}
|
|
144
156
|
}
|
|
145
157
|
return isSandboxPromise(value) ? "[object Promise]" : "[object Object]";
|
|
146
158
|
}
|
|
@@ -4,6 +4,7 @@ import type { Budget, CompileTicket } from "./budget.js";
|
|
|
4
4
|
import { CompileScope } from "./regex/compile-guard.js";
|
|
5
5
|
import type { GeneratorChannel } from "./generator.js";
|
|
6
6
|
import { type RegexPattern } from "./regex/parse.js";
|
|
7
|
+
import type { FunctionSource } from "../parse/function-source.js";
|
|
7
8
|
export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
|
|
8
9
|
export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
|
|
9
10
|
declare const sandboxClosureBrand: unique symbol;
|
|
@@ -57,6 +58,7 @@ export type SandboxCallContext = {
|
|
|
57
58
|
readonly invokeClosure?: (closure: SandboxClosure, args: readonly SandboxValue[], thisValue: SandboxValue, construct?: boolean) => Promise<SandboxValue>;
|
|
58
59
|
};
|
|
59
60
|
export type SandboxClosure = {
|
|
61
|
+
readonly sourceRange?: FunctionSource;
|
|
60
62
|
readonly async?: true;
|
|
61
63
|
readonly generator?: true;
|
|
62
64
|
readonly sandbox?: true;
|
|
@@ -94,6 +96,7 @@ type CopyFromSandboxOptions = {
|
|
|
94
96
|
compilation?: CompileScope;
|
|
95
97
|
};
|
|
96
98
|
export declare function createSandboxClosure(input: {
|
|
99
|
+
sourceRange?: FunctionSource;
|
|
97
100
|
guest?: boolean;
|
|
98
101
|
async?: boolean;
|
|
99
102
|
generator?: boolean;
|
|
@@ -39,6 +39,9 @@ export function createSandboxClosure(input) {
|
|
|
39
39
|
enumerable: false,
|
|
40
40
|
value: true
|
|
41
41
|
});
|
|
42
|
+
if (input.sourceRange !== undefined) {
|
|
43
|
+
Object.defineProperty(closure, "sourceRange", { value: input.sourceRange });
|
|
44
|
+
}
|
|
42
45
|
if (input.sandbox === true) {
|
|
43
46
|
Object.defineProperty(closure, "sandbox", { value: true });
|
|
44
47
|
}
|
|
@@ -12,7 +12,7 @@ export function inspectSnapshotMigration(snapshot, options) {
|
|
|
12
12
|
validateSnapshotData(snapshot);
|
|
13
13
|
validateDumpEnvelope(snapshot);
|
|
14
14
|
validateMigrationSemantics(snapshot.executionSemantics);
|
|
15
|
-
const sourceHash = hashSource(options.source);
|
|
15
|
+
const sourceHash = hashSource(options.source, undefined, snapshot.executionSemantics === EXECUTION_SEMANTICS);
|
|
16
16
|
if (snapshot.sourceHash !== sourceHash)
|
|
17
17
|
throw new TypeError(`Migration requires the original executable source (checkpoint hash ${snapshot.sourceHash}, supplied ${sourceHash}). Preserve the checkpoint; do not replace its hash.`);
|
|
18
18
|
validateSnapshotMigration(snapshot.migration, sourceHash);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const functionSources = new WeakMap();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type Module, type ParseResult } from "./parser.js";
|
|
2
2
|
import { type CompileOwner } from "../interp/budget.js";
|
|
3
|
-
export declare function hashSource(source: string, owner?: CompileOwner): string;
|
|
4
|
-
export declare function hashParsedAst(ast: Module | ParseResult): string;
|
|
3
|
+
export declare function hashSource(source: string, owner?: CompileOwner, includeFunctionSource?: boolean): string;
|
|
4
|
+
export declare function hashParsedAst(ast: Module | ParseResult, includeFunctionSource?: boolean): string;
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { parse, parseModule } from "./parser.js";
|
|
2
2
|
import { SandboxError } from "../interp/budget.js";
|
|
3
|
+
import { functionSources } from "./function-source.js";
|
|
3
4
|
const FNV_OFFSET_BASIS = 0x811c9dc5;
|
|
4
5
|
const FNV_PRIME = 0x01000193;
|
|
5
6
|
const IGNORED_KEYS = new Set(["nodeId", "raw", "span"]);
|
|
6
|
-
export function hashSource(source, owner) {
|
|
7
|
+
export function hashSource(source, owner, includeFunctionSource = true) {
|
|
7
8
|
try {
|
|
8
|
-
return hashParsedAst(parse(source, "<input>", owner));
|
|
9
|
+
return hashParsedAst(parse(source, "<input>", owner), includeFunctionSource);
|
|
9
10
|
}
|
|
10
11
|
catch (error) {
|
|
11
12
|
if (error instanceof SandboxError)
|
|
12
13
|
throw error;
|
|
13
|
-
return hashParsedAst(parseModule(source, "<input>", owner));
|
|
14
|
+
return hashParsedAst(parseModule(source, "<input>", owner), includeFunctionSource);
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
|
-
export function hashParsedAst(ast) {
|
|
17
|
+
export function hashParsedAst(ast, includeFunctionSource = true) {
|
|
17
18
|
let hash = FNV_OFFSET_BASIS;
|
|
18
19
|
visit(ast);
|
|
19
20
|
return hash.toString(16).padStart(8, "0");
|
|
20
|
-
function visit(value, includeTemplateRaw = false) {
|
|
21
|
+
function visit(value, includeTemplateRaw = false, enclosingSource) {
|
|
21
22
|
if (value === null) {
|
|
22
23
|
write("null");
|
|
23
24
|
return;
|
|
@@ -29,7 +30,7 @@ export function hashParsedAst(ast) {
|
|
|
29
30
|
if (Array.isArray(value)) {
|
|
30
31
|
write("[");
|
|
31
32
|
for (const entry of value) {
|
|
32
|
-
visit(entry, includeTemplateRaw);
|
|
33
|
+
visit(entry, includeTemplateRaw, enclosingSource);
|
|
33
34
|
write(",");
|
|
34
35
|
}
|
|
35
36
|
write("]");
|
|
@@ -47,6 +48,19 @@ export function hashParsedAst(ast) {
|
|
|
47
48
|
return;
|
|
48
49
|
case "object": {
|
|
49
50
|
const record = value;
|
|
51
|
+
const source = includeFunctionSource
|
|
52
|
+
? functionSources.get(value)
|
|
53
|
+
: undefined;
|
|
54
|
+
if (source !== undefined &&
|
|
55
|
+
!(enclosingSource !== undefined &&
|
|
56
|
+
source.text === enclosingSource.text &&
|
|
57
|
+
source.start >= enclosingSource.start &&
|
|
58
|
+
source.end <= enclosingSource.end)) {
|
|
59
|
+
write("function-source:");
|
|
60
|
+
write(String(source.end - source.start));
|
|
61
|
+
write(":");
|
|
62
|
+
write(source.text, source.start, source.end);
|
|
63
|
+
}
|
|
50
64
|
const keys = Object.keys(value)
|
|
51
65
|
.filter((key) => shouldHashKey(record, key, includeTemplateRaw))
|
|
52
66
|
.sort();
|
|
@@ -54,7 +68,7 @@ export function hashParsedAst(ast) {
|
|
|
54
68
|
for (const key of keys) {
|
|
55
69
|
write(JSON.stringify(key));
|
|
56
70
|
write(":");
|
|
57
|
-
visit(record[key], shouldIncludeTemplateRaw(record, key, includeTemplateRaw));
|
|
71
|
+
visit(record[key], shouldIncludeTemplateRaw(record, key, includeTemplateRaw), source ?? enclosingSource);
|
|
58
72
|
write(",");
|
|
59
73
|
}
|
|
60
74
|
write("}");
|
|
@@ -64,8 +78,8 @@ export function hashParsedAst(ast) {
|
|
|
64
78
|
throw new TypeError(`Unsupported AST value type: ${typeof value}`);
|
|
65
79
|
}
|
|
66
80
|
}
|
|
67
|
-
function write(chunk) {
|
|
68
|
-
for (let index =
|
|
81
|
+
function write(chunk, start = 0, end = chunk.length) {
|
|
82
|
+
for (let index = start; index < end; index += 1) {
|
|
69
83
|
hash ^= chunk.charCodeAt(index);
|
|
70
84
|
hash = Math.imul(hash, FNV_PRIME) >>> 0;
|
|
71
85
|
}
|