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.
Files changed (41) hide show
  1. package/dist/index.js +313 -135
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-C7UCTQDN.js → chunk-F7TJSPDZ.js} +7 -3
  6. package/packages/safe-js/dist/chunks/chunk-F7TJSPDZ.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-MKJBQPBH.js → chunk-VKGWGZBH.js} +268 -126
  8. package/packages/safe-js/dist/chunks/chunk-VKGWGZBH.js.map +7 -0
  9. package/packages/safe-js/dist/cli.js +2 -2
  10. package/packages/safe-js/dist/core.js +1 -1
  11. package/packages/safe-js/dist/index.js +2 -2
  12. package/packages/safe-js/dist/interp/async.js +3 -0
  13. package/packages/safe-js/dist/interp/function-string.d.ts +2 -0
  14. package/packages/safe-js/dist/interp/function-string.js +21 -0
  15. package/packages/safe-js/dist/interp/globals/misc.js +11 -12
  16. package/packages/safe-js/dist/interp/globals/numeric-parsers.d.ts +5 -0
  17. package/packages/safe-js/dist/interp/globals/numeric-parsers.js +47 -0
  18. package/packages/safe-js/dist/interp/globals/object-array.js +2 -10
  19. package/packages/safe-js/dist/interp/interpreter.js +9 -14
  20. package/packages/safe-js/dist/interp/methods/function.d.ts +2 -0
  21. package/packages/safe-js/dist/interp/methods/function.js +10 -1
  22. package/packages/safe-js/dist/interp/methods/string.js +18 -0
  23. package/packages/safe-js/dist/interp/resources.d.ts +3 -0
  24. package/packages/safe-js/dist/interp/resources.js +11 -0
  25. package/packages/safe-js/dist/interp/string-coercion.js +16 -4
  26. package/packages/safe-js/dist/interp/values.d.ts +3 -0
  27. package/packages/safe-js/dist/interp/values.js +3 -0
  28. package/packages/safe-js/dist/migrate.js +1 -1
  29. package/packages/safe-js/dist/parse/function-source.d.ts +7 -0
  30. package/packages/safe-js/dist/parse/function-source.js +1 -0
  31. package/packages/safe-js/dist/parse/hash.d.ts +2 -2
  32. package/packages/safe-js/dist/parse/hash.js +23 -9
  33. package/packages/safe-js/dist/parse/parser.js +36 -23
  34. package/packages/safe-js/dist/restore.js +2 -1
  35. package/packages/safe-js/dist/run.js +16 -5
  36. package/packages/safe-js/dist/snapshot/dump-format.d.ts +1 -1
  37. package/packages/safe-js/dist/snapshot/dump-format.js +1 -1
  38. package/packages/safe-js/dist/snapshot/migration.js +2 -2
  39. package/packages/safe-js/dist/snapshot/restore.js +2 -0
  40. package/packages/safe-js/dist/chunks/chunk-C7UCTQDN.js.map +0 -7
  41. 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-C7UCTQDN.js";
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-MKJBQPBH.js";
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";
@@ -5,7 +5,7 @@ import {
5
5
  defineExtension,
6
6
  lint,
7
7
  run
8
- } from "./chunks/chunk-MKJBQPBH.js";
8
+ } from "./chunks/chunk-VKGWGZBH.js";
9
9
  import "./chunks/chunk-4YIDX44G.js";
10
10
  export {
11
11
  Budget,
@@ -26,7 +26,7 @@ import {
26
26
  runWithSpawnUsageAccumulator,
27
27
  splitFrontmatter,
28
28
  supportsSpawnMode
29
- } from "./chunks/chunk-C7UCTQDN.js";
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-MKJBQPBH.js";
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,2 @@
1
+ import type { SandboxClosure } from "./values.js";
2
+ export declare function functionString(value: SandboxClosure): string;
@@ -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
- parseInt: createSandboxClosure({
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]) => globalThis.isNaN(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]) => globalThis.isFinite(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,5 @@
1
+ import type { Budget } from "../budget.js";
2
+ export declare function createNumericParsers(budget: Budget): {
3
+ parseInt: import("../values.js").SandboxClosure;
4
+ parseFloat: import("../values.js").SandboxClosure;
5
+ };
@@ -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
- parseInt: createSandboxClosure({
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 { runResources } from "./resources.js";
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
- const release = retainValues(context.budget, () => [value]);
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
- const expressionText = context.budget.allocateString(String(expression.value));
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
- const functionMethodNames = new Set(["apply", "bind", "call"]);
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 messageValue = ownDataValue(value, "message");
142
- const message = messageValue === undefined ? "" : await sandboxString(messageValue, budget, context, joining);
143
- return name === "" ? message : message === "" ? name : `${name}: ${message}`;
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,7 @@
1
+ import type { FunctionNode } from "./parser.js";
2
+ export type FunctionSource = {
3
+ readonly text: string;
4
+ readonly start: number;
5
+ readonly end: number;
6
+ };
7
+ export declare const functionSources: WeakMap<FunctionNode, FunctionSource>;
@@ -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 = 0; index < chunk.length; index += 1) {
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
  }