poe-code 14.0.63 → 14.0.65

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 (58) hide show
  1. package/dist/index.js +7473 -6816
  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-KMTTD3BL.js → chunk-4VT2M7WL.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-RU2BCN4G.js → chunk-WBWD7ZCM.js} +8215 -7599
  7. package/packages/safe-js/dist/chunks/chunk-WBWD7ZCM.js.map +7 -0
  8. package/packages/safe-js/dist/cli.js +2 -2
  9. package/packages/safe-js/dist/core.js +1 -1
  10. package/packages/safe-js/dist/index.js +2 -2
  11. package/packages/safe-js/dist/index.js.map +1 -1
  12. package/packages/safe-js/dist/interp/async.d.ts +13 -2
  13. package/packages/safe-js/dist/interp/async.js +16 -6
  14. package/packages/safe-js/dist/interp/classes.d.ts +4 -0
  15. package/packages/safe-js/dist/interp/classes.js +176 -0
  16. package/packages/safe-js/dist/interp/exceptions.js +4 -1
  17. package/packages/safe-js/dist/interp/globals/object-array.d.ts +2 -1
  18. package/packages/safe-js/dist/interp/globals/object-array.js +1 -1
  19. package/packages/safe-js/dist/interp/globals/object.js +10 -2
  20. package/packages/safe-js/dist/interp/globals/primitives.js +8 -3
  21. package/packages/safe-js/dist/interp/interpreter.d.ts +1 -1
  22. package/packages/safe-js/dist/interp/interpreter.js +99 -30
  23. package/packages/safe-js/dist/interp/methods/function.d.ts +1 -1
  24. package/packages/safe-js/dist/interp/methods/function.js +33 -16
  25. package/packages/safe-js/dist/interp/object-model.js +25 -11
  26. package/packages/safe-js/dist/interp/patterns.d.ts +1 -1
  27. package/packages/safe-js/dist/interp/patterns.js +1 -1
  28. package/packages/safe-js/dist/interp/values.d.ts +2 -1
  29. package/packages/safe-js/dist/lint/class-elements.d.ts +5 -0
  30. package/packages/safe-js/dist/lint/class-elements.js +20 -0
  31. package/packages/safe-js/dist/lint/rules/AS-async-not-needed.js +16 -0
  32. package/packages/safe-js/dist/lint/rules/AS-destructure-null-default.js +9 -0
  33. package/packages/safe-js/dist/lint/rules/AS-export-import-meta.js +9 -0
  34. package/packages/safe-js/dist/lint/rules/AS-floating-promise.js +9 -0
  35. package/packages/safe-js/dist/lint/rules/AS-frontmatter-field-unused.js +9 -0
  36. package/packages/safe-js/dist/lint/rules/AS-jsdoc-type.js +9 -0
  37. package/packages/safe-js/dist/lint/rules/AS-large-literal.js +9 -0
  38. package/packages/safe-js/dist/lint/rules/AS-missing-async.js +16 -0
  39. package/packages/safe-js/dist/lint/rules/AS-mutating-frozen.js +9 -0
  40. package/packages/safe-js/dist/lint/rules/AS-needless-template.js +9 -0
  41. package/packages/safe-js/dist/lint/rules/AS-shadow-global.js +12 -0
  42. package/packages/safe-js/dist/lint/rules/AS-unbounded-loop.js +11 -0
  43. package/packages/safe-js/dist/lint/rules/AS-unreachable.js +9 -0
  44. package/packages/safe-js/dist/lint/rules/AS-unused-import.js +16 -2
  45. package/packages/safe-js/dist/lint/rules/AS001.js +0 -1
  46. package/packages/safe-js/dist/lint/rules/AS003.js +16 -2
  47. package/packages/safe-js/dist/lint/rules/AS006-007.js +16 -2
  48. package/packages/safe-js/dist/lint/rules/AS009.js +20 -1
  49. package/packages/safe-js/dist/lint/rules/AS010.js +9 -0
  50. package/packages/safe-js/dist/lint/rules/AS011.js +9 -0
  51. package/packages/safe-js/dist/lint/rules/AS015.js +9 -0
  52. package/packages/safe-js/dist/parse/function-source.d.ts +2 -2
  53. package/packages/safe-js/dist/parse/parser.d.ts +42 -2
  54. package/packages/safe-js/dist/parse/parser.js +180 -36
  55. package/packages/safe-js/dist/parse/tokenizer.js +3 -0
  56. package/packages/safe-js/dist/parse.d.ts +1 -1
  57. package/packages/safe-js/dist/chunks/chunk-RU2BCN4G.js.map +0 -7
  58. /package/packages/safe-js/dist/chunks/{chunk-KMTTD3BL.js.map → chunk-4VT2M7WL.js.map} +0 -0
@@ -5,6 +5,7 @@ import { assertPromiseExecutionAllowed } from "./promise-tracker.js";
5
5
  import { SandboxJobQueue, runAsyncPrefix, suspendJob } from "./jobs.js";
6
6
  import { withCancellationSignal } from "./cancel.js";
7
7
  import { retainValues } from "./resources.js";
8
+ import { evaluateClass } from "./classes.js";
8
9
  import { sandboxString } from "./string-coercion.js";
9
10
  import { attachErrorSpan, formatErrorStack, replaceErrorStack } from "../error/shape.js";
10
11
  import { evaluateArrowFunctionExpression, evaluateFunctionExpression, evaluateAwaitExpression, emitResumeBreakpoint, createInterpretedClosure, normalizeClosureResult } from "./async.js";
@@ -14,7 +15,7 @@ import { CompileScope, RegexCompileGuard } from "./regex/compile-guard.js";
14
15
  import { createCapturedException, createThrowCompletion, evaluateThrowStatement as evaluateThrowStatementResult, evaluateTryStatement as evaluateTryStatementResult, isCapturedException, isInterpreterError, surfaceThrownValue } from "./exceptions.js";
15
16
  import { callArrayMethod, getArrayMember, isArrayMethodName } from "./methods/array.js";
16
17
  import { getFunctionMember } from "./methods/function.js";
17
- import { getBoxedPrototype, getGuestFunctionProperty, getSandboxPrototype, isDefaultBoxedMethod, isGuestClosure, materializeFunctionProperties, setSandboxPrototype } from "./object-model.js";
18
+ import { getBoxedPrototype, getGuestFunctionProperty, getSandboxPrototype, hasExplicitSandboxPrototype, isDefaultBoxedMethod, isGuestClosure, materializeFunctionProperties, setSandboxPrototype } from "./object-model.js";
18
19
  import { getStringIndex } from "./methods/string.js";
19
20
  import { assertSandboxDataDepth } from "../graph-depth.js";
20
21
  import { callMapMethod, getMapMember, isMapMethodName } from "./methods/map.js";
@@ -50,6 +51,15 @@ const dispatchTable = {
50
51
  BlockStatement: evaluateBlockStatement,
51
52
  BooleanLiteral: evaluatePrimitiveLiteral,
52
53
  CallExpression: evaluateCallExpression,
54
+ ClassDeclaration: (node, context) => evaluateClass(node, context, evaluateNode, createCoercionContext(context)),
55
+ ClassExpression: (node, context) => evaluateClass(node, context, evaluateNode, createCoercionContext(context)),
56
+ NewTargetExpression: async (_node, context) => ({ kind: "normal", hasValue: true, value: context.functionEnvironment?.newTarget }),
57
+ Super: async (_node, context) => {
58
+ const home = context.functionEnvironment?.homeObject;
59
+ if (home === undefined)
60
+ throw new ReferenceError("Super binding is unavailable.");
61
+ return { kind: "normal", hasValue: true, value: getSandboxPrototype(home, context.budget) };
62
+ },
53
63
  ConditionalExpression: evaluateConditionalExpression,
54
64
  ContinueStatement: evaluateContinueStatement,
55
65
  DoWhileStatement: evaluateDoWhileStatement,
@@ -182,6 +192,8 @@ export async function interpret(node, options = {}) {
182
192
  export { Scope } from "./scope.js";
183
193
  async function evaluateNode(node, context) {
184
194
  context.assertActive?.();
195
+ if (context.inferredName !== undefined && node.type !== "ClassExpression")
196
+ context = { ...context, inferredName: undefined };
185
197
  const replayWait = promiseReplayContext.getStore()?.beforeNode(node.nodeId);
186
198
  if (replayWait !== undefined)
187
199
  await suspendJob(replayWait);
@@ -332,7 +344,7 @@ async function evaluateObjectExpression(node, context) {
332
344
  const releaseKey = retainValues(context.budget, () => [key.value]);
333
345
  let value;
334
346
  try {
335
- value = await evaluateNode(property.value, context);
347
+ value = await evaluateNode(property.value, { ...context, inferredName: String(key.value) });
336
348
  }
337
349
  finally {
338
350
  releaseKey();
@@ -390,22 +402,32 @@ async function evaluateTemplateLiteral(node, context) {
390
402
  }
391
403
  }
392
404
  async function evaluateTaggedTemplateExpression(node, context) {
393
- const tag = await evaluateNode(node.tag, context);
394
- if (tag.kind !== "normal") {
395
- return tag;
396
- }
397
- if (!isSandboxClosure(tag.value)) {
398
- throw new TypeError("Tagged template tag must be a function.");
399
- }
400
- const values = await evaluateTemplateExpressionValues(node.quasi, context);
401
- if (!values.ok) {
402
- return values.result;
403
- }
404
- return {
405
- kind: "normal",
406
- hasValue: true,
407
- value: await invokeSandboxClosure(tag.value, [createTaggedTemplateStrings(node.quasi, context), ...values.value], context, [...context.callStack, formatStackFrame(node, tag.value.name)])
405
+ const invokeTag = async (tag, receiver) => {
406
+ if (!isSandboxClosure(tag))
407
+ throw new TypeError("Tagged template tag must be a function.");
408
+ const release = retainValues(context.budget, () => [tag, receiver]);
409
+ try {
410
+ const values = await evaluateTemplateExpressionValues(node.quasi, context);
411
+ if (!values.ok)
412
+ return values.result;
413
+ return {
414
+ kind: "normal", hasValue: true,
415
+ value: await invokeSandboxClosure(tag, [createTaggedTemplateStrings(node.quasi, context), ...values.value], context, [...context.callStack, formatStackFrame(node, tag.name)], node.span, receiver)
416
+ };
417
+ }
418
+ finally {
419
+ release();
420
+ }
408
421
  };
422
+ if (node.tag.type === "MemberExpression")
423
+ return evaluateMemberAccess(node.tag, context, async (member) => {
424
+ if (member.kind === "nullish")
425
+ throw new TypeError("Tagged template tag must be a function.");
426
+ const key = await toPropertyKey(member.property, context.budget, createCoercionContext(context));
427
+ return invokeTag(getPropertyValue(member.object, key, context), member.superReceiver === undefined ? member.object : member.superReceiver.value);
428
+ });
429
+ const tag = await evaluateNode(node.tag, context);
430
+ return tag.kind === "normal" ? invokeTag(tag.value, undefined) : tag;
409
431
  }
410
432
  async function evaluateTemplateExpressionValues(node, context) {
411
433
  const values = [];
@@ -552,7 +574,7 @@ async function evaluateAssignmentExpression(node, context) {
552
574
  value: current
553
575
  };
554
576
  }
555
- const right = await evaluateNode(node.right, context);
577
+ const right = await evaluateNode(node.right, { ...context, inferredName: node.left.type === "Identifier" ? node.left.name : undefined });
556
578
  if (right.kind !== "normal") {
557
579
  return right;
558
580
  }
@@ -626,7 +648,10 @@ async function evaluateMemberAssignmentExpression(node, context) {
626
648
  }
627
649
  operands = [value];
628
650
  property ??= await toPropertyKey(member.property, context.budget, createCoercionContext(context));
629
- setSandboxProperty(member.object, property, value, context.budget);
651
+ if (member.superReceiver === undefined)
652
+ setSandboxProperty(member.object, property, value, context.budget);
653
+ else
654
+ setSuperProperty(member.object, member.superReceiver.value, property, value, context.budget);
630
655
  return {
631
656
  kind: "normal",
632
657
  hasValue: true,
@@ -772,7 +797,7 @@ async function evaluateVariableDeclaration(node, context) {
772
797
  hasValue: true,
773
798
  value: undefined
774
799
  }
775
- : await evaluateNode(declarator.init, context);
800
+ : await evaluateNode(declarator.init, { ...context, inferredName: declarator.id.type === "Identifier" ? declarator.id.name : undefined });
776
801
  if (value.kind !== "normal") {
777
802
  return value;
778
803
  }
@@ -946,6 +971,11 @@ function predeclareStatementListBindings(statements, context, functionBody = fal
946
971
  const { scope } = context;
947
972
  const names = new Set();
948
973
  for (const statement of statements) {
974
+ if (statement.type === "ClassDeclaration") {
975
+ scope.predeclare(statement.id.name, "let");
976
+ names.add(statement.id.name);
977
+ continue;
978
+ }
949
979
  if (statement.type === "FunctionDeclaration") {
950
980
  const name = statement.id.name;
951
981
  if (names.has(name) && !(functionBody && scope.getOwnBindingKind(name) === "var")) {
@@ -1646,6 +1676,8 @@ async function evaluateDeleteExpression(node, context) {
1646
1676
  throw createError("UNSUPPORTED_NODE", node, "Unary operator 'delete' requires a member target.");
1647
1677
  }
1648
1678
  return evaluateMemberAccess(node.argument, context, async (member) => {
1679
+ if (member.kind === "resolved" && member.superReceiver !== undefined)
1680
+ throw new ReferenceError("Cannot delete a super property.");
1649
1681
  if (member.kind === "nullish" || member.object === null || member.object === undefined) {
1650
1682
  if (member.kind === "nullish") {
1651
1683
  return {
@@ -1705,7 +1737,10 @@ async function evaluateMemberUpdateExpression(node, context) {
1705
1737
  const property = await toPropertyKey(member.property, context.budget, createCoercionContext(context));
1706
1738
  const current = toNumber(await toNumericPrimitive(getPropertyValue(member.object, property, context), context));
1707
1739
  const next = node.operator === "++" ? current + 1 : current - 1;
1708
- setSandboxProperty(member.object, property, next, context.budget);
1740
+ if (member.superReceiver === undefined)
1741
+ setSandboxProperty(member.object, property, next, context.budget);
1742
+ else
1743
+ setSuperProperty(member.object, member.superReceiver.value, property, next, context.budget);
1709
1744
  return {
1710
1745
  kind: "normal",
1711
1746
  hasValue: true,
@@ -1771,13 +1806,22 @@ export function createPatternContext(context, scope = context.scope, evaluate =
1771
1806
  const evaluationContext = { ...context, scope };
1772
1807
  return {
1773
1808
  budget: context.budget,
1774
- evaluate: node => evaluate(node, evaluationContext),
1809
+ evaluate: (node, inferredName) => evaluate(node, { ...evaluationContext, inferredName }),
1775
1810
  toPropertyKey: value => toPropertyKey(value, context.budget, createCoercionContext(evaluationContext)),
1776
1811
  getProperty: (value, key) => getPropertyValue(value, key, evaluationContext),
1777
1812
  setProperty: (target, key, value) => setSandboxProperty(target, key, value, context.budget)
1778
1813
  };
1779
1814
  }
1780
1815
  async function evaluateCallExpression(node, context) {
1816
+ if (node.callee.type === "Super") {
1817
+ const construction = context.functionEnvironment?.construction;
1818
+ if (construction === undefined)
1819
+ throw new ReferenceError("Super constructor binding is unavailable.");
1820
+ const args = await evaluateCallArguments(node.arguments, context);
1821
+ if (!args.ok)
1822
+ return args.result;
1823
+ return { kind: "normal", hasValue: true, value: await construction.superCall(args.value) };
1824
+ }
1781
1825
  if (node.callee.type === "MemberExpression") {
1782
1826
  context.budget.visitNode();
1783
1827
  context.stats.nodeVisits += 1;
@@ -1847,6 +1891,7 @@ function attachFatalSandboxErrorContext(error, node, stackFrames) {
1847
1891
  replaceErrorStack(error, stackFrames);
1848
1892
  }
1849
1893
  async function evaluateMemberAccess(node, context, consume) {
1894
+ const superReceiver = node.object.type === "Super" ? { value: context.scope.lookup("this") } : undefined;
1850
1895
  const object = await evaluateNode(node.object, context);
1851
1896
  if (object.kind !== "normal")
1852
1897
  return object;
@@ -1862,7 +1907,8 @@ async function evaluateMemberAccess(node, context, consume) {
1862
1907
  if (result.kind !== "normal")
1863
1908
  return result;
1864
1909
  property = result.value;
1865
- return await consume({ kind: "resolved", object: object.value, property });
1910
+ return await consume({ kind: "resolved", object: object.value, property,
1911
+ ...(superReceiver === undefined ? {} : { superReceiver: { value: superReceiver.value.found ? superReceiver.value.value : undefined } }) });
1866
1912
  }
1867
1913
  finally {
1868
1914
  release();
@@ -1918,6 +1964,8 @@ async function evaluateMemberCallExpression(node, context) {
1918
1964
  ...reference,
1919
1965
  property: await toPropertyKey(reference.property, context.budget, createCoercionContext(context))
1920
1966
  };
1967
+ if (member.superReceiver !== undefined)
1968
+ return evaluateResolvedCallExpression(node, getPropertyValue(member.object, member.property, context), context, member.superReceiver.value);
1921
1969
  if ((typeof member.object === "string" || typeof member.object === "number" || typeof member.object === "boolean") &&
1922
1970
  getBoxedPrototype(member.object, context.budget) !== undefined) {
1923
1971
  if (isDefaultBoxedMethod(member.object, member.property, context.budget)) {
@@ -2219,8 +2267,9 @@ function hasSandboxProperty(value, key, context) {
2219
2267
  return hasHostObjectMember(current, key);
2220
2268
  if (hasOwnSandboxProperty(current, key, false))
2221
2269
  return true;
2222
- if (Array.isArray(current) || !isPlainSandboxObject(current) ||
2223
- isSandboxDate(current) || isFloat32Array(current) || isSandboxGenerator(current) || isSandboxCollectionIterator(current)) {
2270
+ if (!(isGuestClosure(current) && hasExplicitSandboxPrototype(current)) &&
2271
+ (Array.isArray(current) || !isPlainSandboxObject(current) ||
2272
+ isSandboxDate(current) || isFloat32Array(current) || isSandboxGenerator(current) || isSandboxCollectionIterator(current))) {
2224
2273
  return getPropertyValue(current, key, context) !== undefined;
2225
2274
  }
2226
2275
  current = getSandboxPrototype(current, context.budget);
@@ -2467,7 +2516,7 @@ function getArrayMemberValue(target, property, context) {
2467
2516
  }
2468
2517
  return getArrayMember(target, property, createArrayMethodOptions(context));
2469
2518
  }
2470
- export function setSandboxProperty(target, property, value, budget) {
2519
+ export function setSandboxProperty(target, property, value, budget, checkInherited = true) {
2471
2520
  if (isSandboxDate(target))
2472
2521
  throw new TypeError("Date own properties are not supported.");
2473
2522
  if (isGuestHostObject(target)) {
@@ -2504,7 +2553,7 @@ export function setSandboxProperty(target, property, value, budget) {
2504
2553
  Object.defineProperty(target, key, { value });
2505
2554
  }
2506
2555
  else {
2507
- if (typeof prototypeOwner === "object" && prototypeOwner !== null) {
2556
+ if (checkInherited && typeof prototypeOwner === "object" && prototypeOwner !== null) {
2508
2557
  let depth = 0;
2509
2558
  for (let prototype = getSandboxPrototype(prototypeOwner, budget); prototype !== null; prototype = getSandboxPrototype(prototype, budget)) {
2510
2559
  budget.visitNode();
@@ -2521,6 +2570,25 @@ export function setSandboxProperty(target, property, value, budget) {
2521
2570
  defineSandboxProperty(target, key, value);
2522
2571
  }
2523
2572
  }
2573
+ function setSuperProperty(base, receiver, key, value, budget) {
2574
+ if (typeof base !== "object" || base === null)
2575
+ throw new TypeError("Cannot assign a property of null.");
2576
+ let depth = 0;
2577
+ for (let current = base; current !== null; current = getSandboxPrototype(current, budget)) {
2578
+ budget.visitNode();
2579
+ assertSandboxDataDepth(depth++);
2580
+ const properties = isSandboxClosure(current) ? current.properties : current;
2581
+ const descriptor = properties === undefined ? undefined : Object.getOwnPropertyDescriptor(properties, key);
2582
+ if (descriptor === undefined)
2583
+ continue;
2584
+ if (descriptor.writable !== true)
2585
+ throw new TypeError(`Cannot assign to read only property '${key}'.`);
2586
+ break;
2587
+ }
2588
+ if (typeof receiver !== "object" || receiver === null)
2589
+ throw new TypeError("Super assignment requires an object receiver.");
2590
+ setSandboxProperty(receiver, key, value, budget, false);
2591
+ }
2524
2592
  function deleteSandboxProperty(target, property) {
2525
2593
  if (isGuestHostObject(target))
2526
2594
  return deleteHostObjectMember(target, String(property));
@@ -2599,10 +2667,10 @@ function createSetMethodOptions(context) {
2599
2667
  function createFunctionMethodOptions(context) {
2600
2668
  return {
2601
2669
  budget: context.budget,
2602
- callClosure: (closure, args, stack, thisValue, construct) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue, construct)
2670
+ callClosure: (closure, args, stack, thisValue, construct, newTarget) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue, construct, newTarget)
2603
2671
  };
2604
2672
  }
2605
- async function invokeSandboxClosure(callee, args, context, stack, span, thisValue = undefined, construct = false) {
2673
+ async function invokeSandboxClosure(callee, args, context, stack, span, thisValue = undefined, construct = false, newTarget) {
2606
2674
  const leaveCall = context.budget.enterCall();
2607
2675
  try {
2608
2676
  const invoke = construct ? callee.construct : callee.call;
@@ -2613,10 +2681,11 @@ async function invokeSandboxClosure(callee, args, context, stack, span, thisValu
2613
2681
  {
2614
2682
  stack,
2615
2683
  thisValue,
2684
+ newTarget: construct ? newTarget ?? callee : undefined,
2616
2685
  compilation: context.compilation,
2617
2686
  getProperty: (value, property) => getPropertyValue(value, property, context),
2618
2687
  reconcileData: (value) => reconcileDataBudget(context.budget, context.stats, context.scope, value, context.compilation, context.compilation?.parent),
2619
- invokeClosure: (closure, argumentsList, receiver, asConstructor) => invokeSandboxClosure(closure, argumentsList, context, stack, span, receiver, asConstructor),
2688
+ invokeClosure: (closure, argumentsList, receiver, asConstructor, target) => invokeSandboxClosure(closure, argumentsList, context, stack, span, receiver, asConstructor, target),
2620
2689
  ...(span === undefined ? {} : { span })
2621
2690
  }
2622
2691
  ]);
@@ -2,6 +2,6 @@ import { type SandboxClosure, type SandboxValue } from "../values.js";
2
2
  import type { Budget } from "../budget.js";
3
3
  export type FunctionMethodOptions = {
4
4
  budget?: Budget;
5
- callClosure: (closure: SandboxClosure, args: readonly SandboxValue[], stack: readonly string[], thisValue: SandboxValue, construct?: boolean) => Promise<SandboxValue> | SandboxValue;
5
+ callClosure: (closure: SandboxClosure, args: readonly SandboxValue[], stack: readonly string[], thisValue: SandboxValue, construct?: boolean, newTarget?: SandboxClosure) => Promise<SandboxValue> | SandboxValue;
6
6
  };
7
7
  export declare function getFunctionMember(target: SandboxClosure, property: string | number, options: FunctionMethodOptions): SandboxValue | undefined;
@@ -1,23 +1,37 @@
1
1
  import { createSandboxClosure, isSandboxClosure } from "../values.js";
2
- import { getGuestFunctionProperty, isGuestClosure } from "../object-model.js";
2
+ import { getGuestFunctionProperty, getSandboxPrototype, hasExplicitSandboxPrototype, isGuestClosure, setSandboxPrototype } from "../object-model.js";
3
+ import { assertSandboxDataDepth } from "../../graph-depth.js";
3
4
  import { functionString } from "../function-string.js";
4
5
  import { runResources } from "../resources.js";
5
6
  const functionMethodNames = new Set(["apply", "bind", "call", "toString"]);
6
7
  export function getFunctionMember(target, property, options) {
7
- if (isGuestClosure(target)) {
8
- const value = getGuestFunctionProperty(target, String(property));
9
- if (value !== undefined || Object.hasOwn(target.properties ?? {}, String(property)))
10
- return value;
11
- if (!isFunctionMethodName(property))
12
- return undefined;
13
- }
14
- const properties = target.properties;
15
- if (properties !== undefined && Object.hasOwn(properties, String(property))) {
16
- return properties[String(property)];
17
- }
18
- if (property === "length") {
19
- return target.length;
8
+ let current = target;
9
+ let depth = 0;
10
+ while (current !== null) {
11
+ if (isGuestClosure(current)) {
12
+ const value = getGuestFunctionProperty(current, String(property));
13
+ if (value !== undefined || Object.hasOwn(current.properties ?? {}, String(property)))
14
+ return value;
15
+ }
16
+ else if (isSandboxClosure(current)) {
17
+ if (current.properties !== undefined && Object.hasOwn(current.properties, String(property)))
18
+ return current.properties[String(property)];
19
+ if (property === "length")
20
+ return current.length;
21
+ }
22
+ else if (Object.hasOwn(current, String(property))) {
23
+ return current[String(property)];
24
+ }
25
+ if (isSandboxClosure(current) && !hasExplicitSandboxPrototype(current))
26
+ break;
27
+ current = getSandboxPrototype(current, options.budget);
28
+ if (current !== null) {
29
+ options.budget?.visitNode();
30
+ assertSandboxDataDepth(++depth);
31
+ }
20
32
  }
33
+ if (current === null)
34
+ return undefined;
21
35
  if (property === "toString" && runResources.getStore()?.functionSourceText === false)
22
36
  return undefined;
23
37
  if (!isFunctionMethodName(property)) {
@@ -44,7 +58,7 @@ function callFunctionMethod(target, methodName, args, options, stack) {
44
58
  const thisValue = args[0];
45
59
  if (methodName === "bind") {
46
60
  const boundArgs = args.slice(1);
47
- return createSandboxClosure({
61
+ const bound = createSandboxClosure({
48
62
  guest: true,
49
63
  sandbox: true,
50
64
  name: `bound ${target.name ?? ""}`,
@@ -55,9 +69,12 @@ function callFunctionMethod(target, methodName, args, options, stack) {
55
69
  ...(target.construct === undefined
56
70
  ? {}
57
71
  : {
58
- construct: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], undefined, true)
72
+ construct: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], undefined, true, context?.newTarget === bound ? target : context?.newTarget)
59
73
  })
60
74
  });
75
+ if (hasExplicitSandboxPrototype(target))
76
+ setSandboxPrototype(bound, getSandboxPrototype(target, options.budget), options.budget);
77
+ return bound;
61
78
  }
62
79
  if (methodName === "call") {
63
80
  return options.callClosure(target, args.slice(1), stack, thisValue);
@@ -91,17 +91,26 @@ function registerIntrinsicPrototype(budget, prototype, constructor) {
91
91
  if (roots === undefined)
92
92
  intrinsicPrototypeRoots.set(budget, roots = new Set());
93
93
  roots.add(prototype);
94
- const records = [prototype, materializeFunctionProperties(constructor)].map(value => ({
95
- value, descriptors: new Map(Object.entries(Object.getOwnPropertyDescriptors(value)))
94
+ const records = [prototype, constructor].map(target => ({
95
+ target,
96
+ value: isGuestClosure(target) ? materializeFunctionProperties(target) : target,
97
+ prototype: getSandboxPrototype(target),
98
+ explicit: hasExplicitSandboxPrototype(target)
99
+ })).map(record => ({
100
+ ...record, descriptors: new Map(Object.entries(Object.getOwnPropertyDescriptors(record.value)))
96
101
  }));
97
102
  const unchanged = (before, after) => before !== undefined && after !== undefined && Object.is(before.value, after.value) &&
98
103
  before.writable === after.writable && before.configurable === after.configurable && before.enumerable === after.enumerable;
99
- intrinsicConstructors.set(constructor, () => records.every(({ value, descriptors }) => {
104
+ intrinsicConstructors.set(constructor, () => records.every(({ target, value, descriptors, prototype: parent, explicit }) => {
100
105
  const current = Object.getOwnPropertyDescriptors(value);
101
- return Object.keys(current).length === descriptors.size &&
106
+ return getSandboxPrototype(target) === parent && hasExplicitSandboxPrototype(target) === explicit &&
107
+ Object.keys(current).length === descriptors.size &&
102
108
  Object.keys(current).every(key => unchanged(descriptors.get(key), current[key]));
103
109
  }));
104
- budget.setRetainedValues(prototype, () => records.flatMap(({ value, descriptors }) => Object.entries(Object.getOwnPropertyDescriptors(value)).flatMap(([key, descriptor]) => unchanged(descriptors.get(key), descriptor) ? [] : [key, descriptor.value])));
110
+ budget.setRetainedValues(prototype, () => records.flatMap(({ target, value, descriptors, prototype: parent }) => [
111
+ ...(getSandboxPrototype(target) === parent ? [] : [getSandboxPrototype(target)]),
112
+ ...Object.entries(Object.getOwnPropertyDescriptors(value)).flatMap(([key, descriptor]) => unchanged(descriptors.get(key), descriptor) ? [] : [key, descriptor.value])
113
+ ]));
105
114
  }
106
115
  export function releaseObjectPrototype(budget) {
107
116
  for (const prototype of intrinsicPrototypeRoots.get(budget) ?? [])
@@ -118,7 +127,7 @@ export function getSandboxPrototype(value, budget) {
118
127
  if (prototype !== undefined && prototype !== value)
119
128
  return prototype;
120
129
  }
121
- return budget !== undefined && isPrototypeRecord(value)
130
+ return budget !== undefined && !isSandboxClosure(value) && isPrototypeRecord(value)
122
131
  ? intrinsicPrototypes.get(budget) ?? null
123
132
  : null;
124
133
  }
@@ -131,9 +140,12 @@ export function getSandboxDataProperty(value, key, budget) {
131
140
  while (typeof current === "object" && current !== null) {
132
141
  if (isGuestHostObject(current))
133
142
  return getHostObjectMember(current, String(key));
134
- if (isGuestClosure(current))
135
- return getGuestFunctionProperty(current, String(key));
136
- if (isSandboxClosure(current)) {
143
+ if (isGuestClosure(current)) {
144
+ const entry = getGuestFunctionProperty(current, String(key));
145
+ if (entry !== undefined || Object.hasOwn(current.properties ?? {}, String(key)))
146
+ return entry;
147
+ }
148
+ else if (isSandboxClosure(current)) {
137
149
  const properties = current.properties;
138
150
  return properties !== undefined && Object.hasOwn(properties, String(key))
139
151
  ? properties[String(key)]
@@ -145,7 +157,7 @@ export function getSandboxDataProperty(value, key, budget) {
145
157
  isSandboxRegex(current) ||
146
158
  isSandboxGenerator(current))
147
159
  return undefined;
148
- if (Object.hasOwn(current, String(key)))
160
+ if (!isSandboxClosure(current) && Object.hasOwn(current, String(key)))
149
161
  return current[String(key)];
150
162
  current = getSandboxPrototype(current, budget);
151
163
  if (current !== null) {
@@ -160,7 +172,7 @@ export function setSandboxPrototype(value, prototype, budget) {
160
172
  throw new TypeError("Object.prototype has an immutable null prototype.");
161
173
  }
162
174
  if (!isPrototypeRecord(value) || (prototype !== null && !isPrototypeRecord(prototype))) {
163
- throw new TypeError("Prototype links require ordinary sandbox objects; callable and exotic prototype chains are not supported.");
175
+ throw new TypeError("Prototype links require ordinary sandbox objects or guest functions.");
164
176
  }
165
177
  if (prototypes.has(value) && getSandboxPrototype(value, budget) === prototype)
166
178
  return;
@@ -177,6 +189,8 @@ export function setSandboxPrototype(value, prototype, budget) {
177
189
  prototypes.set(value, prototype);
178
190
  }
179
191
  function isPrototypeRecord(value) {
192
+ if (isGuestClosure(value))
193
+ return true;
180
194
  if (isGuestHostObject(value))
181
195
  return false;
182
196
  if (isSandboxClosure(value) ||
@@ -12,7 +12,7 @@ export type PatternTarget = {
12
12
  };
13
13
  export type PatternContext = {
14
14
  budget?: Budget;
15
- evaluate(node: ParseResult): Promise<AsyncEvaluationResult>;
15
+ evaluate(node: ParseResult, inferredName?: string): Promise<AsyncEvaluationResult>;
16
16
  toPropertyKey(value: SandboxValue): Promise<string>;
17
17
  getProperty(value: SandboxValue, key: string | number): SandboxValue;
18
18
  setProperty(target: SandboxValue, key: string | number, value: SandboxValue): void;
@@ -40,7 +40,7 @@ async function bindAssignmentPattern(pattern, value, target, scope, context) {
40
40
  if (value !== undefined) {
41
41
  return bindPattern(pattern.left, value, target, scope, context);
42
42
  }
43
- const defaultValue = await context.evaluate(pattern.right);
43
+ const defaultValue = await context.evaluate(pattern.right, pattern.left.type === "Identifier" ? pattern.left.name : undefined);
44
44
  if (defaultValue.kind !== "normal") {
45
45
  return { ok: false, result: defaultValue };
46
46
  }
@@ -38,6 +38,7 @@ export type SandboxRegex = {
38
38
  readonly [sandboxRegexPattern]: RegexPattern;
39
39
  };
40
40
  export type SandboxCallContext = {
41
+ readonly newTarget?: SandboxClosure;
41
42
  readonly compilation?: CompileScope;
42
43
  readonly span?: {
43
44
  readonly end: {
@@ -55,7 +56,7 @@ export type SandboxCallContext = {
55
56
  readonly thisValue: SandboxValue;
56
57
  readonly getProperty?: (value: SandboxValue, property: string | number) => SandboxValue;
57
58
  readonly reconcileData?: (value: SandboxValue) => void;
58
- readonly invokeClosure?: (closure: SandboxClosure, args: readonly SandboxValue[], thisValue: SandboxValue, construct?: boolean) => Promise<SandboxValue>;
59
+ readonly invokeClosure?: (closure: SandboxClosure, args: readonly SandboxValue[], thisValue: SandboxValue, construct?: boolean, newTarget?: SandboxClosure) => Promise<SandboxValue>;
59
60
  };
60
61
  export type SandboxClosure = {
61
62
  readonly sourceRange?: FunctionSource;
@@ -0,0 +1,5 @@
1
+ import type { BlockStatement, ClassNode, Expression } from "../parse.js";
2
+ /** Visit both definition-time expressions and deferred element bodies. */
3
+ export declare function visitClassElements(node: ClassNode, expression: (node: Expression) => void, statement: (node: BlockStatement) => void): void;
4
+ /** Await in a computed key belongs to the defining function, unlike methods. */
5
+ export declare function classDefinitionContains(node: ClassNode, expression: (node: Expression) => boolean): boolean;
@@ -0,0 +1,20 @@
1
+ /** Visit both definition-time expressions and deferred element bodies. */
2
+ export function visitClassElements(node, expression, statement) {
3
+ if (node.superClass !== undefined)
4
+ expression(node.superClass);
5
+ for (const element of node.body.body) {
6
+ if (element.type === "StaticBlock") {
7
+ statement(element.body);
8
+ continue;
9
+ }
10
+ if (element.computed)
11
+ expression(element.key);
12
+ if (element.value !== undefined)
13
+ expression(element.value);
14
+ }
15
+ }
16
+ /** Await in a computed key belongs to the defining function, unlike methods. */
17
+ export function classDefinitionContains(node, expression) {
18
+ return (node.superClass !== undefined && expression(node.superClass)) ||
19
+ node.body.body.some(element => element.type !== "StaticBlock" && element.computed && expression(element.key));
20
+ }
@@ -1,3 +1,4 @@
1
+ import { classDefinitionContains, visitClassElements } from "../class-elements.js";
1
2
  import { parseModule } from "../../parse/parser.js";
2
3
  export const AS_ASYNC_NOT_NEEDED_MESSAGE = "Async functions without await should remove the async keyword.";
3
4
  export function AS_ASYNC_NOT_NEEDED(source, options = {}) {
@@ -32,6 +33,10 @@ class ASAsyncNotNeededScanner {
32
33
  }
33
34
  }
34
35
  visitStatement(node) {
36
+ if (node.type === "ClassDeclaration") {
37
+ visitClassElements(node, expression => this.visitExpression(expression), statement => this.visitStatement(statement));
38
+ return;
39
+ }
35
40
  switch (node.type) {
36
41
  case "FunctionDeclaration":
37
42
  this.visitFunctionExpression(node);
@@ -166,6 +171,10 @@ class ASAsyncNotNeededScanner {
166
171
  this.visitExpression(node.argument);
167
172
  }
168
173
  visitExpression(node) {
174
+ if (node.type === "ClassExpression") {
175
+ visitClassElements(node, expression => this.visitExpression(expression), statement => this.visitStatement(statement));
176
+ return;
177
+ }
169
178
  switch (node.type) {
170
179
  case "YieldExpression":
171
180
  if (node.argument !== undefined) {
@@ -406,6 +415,8 @@ function statementListContainsAwait(statements) {
406
415
  }
407
416
  function statementContainsAwait(node) {
408
417
  switch (node.type) {
418
+ case "ClassDeclaration":
419
+ return classDefinitionContains(node, expressionContainsAwait);
409
420
  case "BlockStatement":
410
421
  return statementListContainsAwait(node.body);
411
422
  case "ExpressionStatement":
@@ -468,6 +479,11 @@ function variableDeclarationContainsAwait(node) {
468
479
  }
469
480
  function expressionContainsAwait(node) {
470
481
  switch (node.type) {
482
+ case "ClassExpression":
483
+ return classDefinitionContains(node, expressionContainsAwait);
484
+ case "NewTargetExpression":
485
+ case "Super":
486
+ return false;
471
487
  case "AwaitExpression":
472
488
  return true;
473
489
  case "YieldExpression":
@@ -1,3 +1,4 @@
1
+ import { visitClassElements } from "../class-elements.js";
1
2
  import { parseModule } from "../../parse/parser.js";
2
3
  const MESSAGE = "Destructuring default values only apply to undefined, not null.";
3
4
  const HINT = "Handle null explicitly before destructuring or use ?? after binding.";
@@ -20,6 +21,10 @@ class ASDestructureNullDefaultScanner {
20
21
  }
21
22
  }
22
23
  visitStatement(node) {
24
+ if (node.type === "ClassDeclaration") {
25
+ visitClassElements(node, expression => this.visitExpression(expression), statement => this.visitStatement(statement));
26
+ return;
27
+ }
23
28
  switch (node.type) {
24
29
  case "FunctionDeclaration":
25
30
  this.visitArrowFunction(node);
@@ -147,6 +152,10 @@ class ASDestructureNullDefaultScanner {
147
152
  this.visitExpression(node.argument);
148
153
  }
149
154
  visitExpression(node) {
155
+ if (node.type === "ClassExpression") {
156
+ visitClassElements(node, expression => this.visitExpression(expression), statement => this.visitStatement(statement));
157
+ return;
158
+ }
150
159
  switch (node.type) {
151
160
  case "YieldExpression":
152
161
  if (node.argument !== undefined) {
@@ -1,3 +1,4 @@
1
+ import { visitClassElements } from "../class-elements.js";
1
2
  import { parseModule } from "../../parse/parser.js";
2
3
  export function AS_EXPORT_IMPORT_META(source, options = {}) {
3
4
  return new Scanner(options.filename ?? "<input>", new Set(options.allowedExportNames ?? []), options.defaultExport).scan(source);
@@ -35,6 +36,10 @@ class Scanner {
35
36
  }
36
37
  }
37
38
  visitStatement(node) {
39
+ if (node.type === "ClassDeclaration") {
40
+ visitClassElements(node, expression => this.visitExpression(expression), statement => this.visitStatement(statement));
41
+ return;
42
+ }
38
43
  switch (node.type) {
39
44
  case "FunctionDeclaration":
40
45
  for (const param of node.params) {
@@ -176,6 +181,10 @@ class Scanner {
176
181
  this.visitExpression(node.argument);
177
182
  }
178
183
  visitExpression(node) {
184
+ if (node.type === "ClassExpression") {
185
+ visitClassElements(node, expression => this.visitExpression(expression), statement => this.visitStatement(statement));
186
+ return;
187
+ }
179
188
  switch (node.type) {
180
189
  case "YieldExpression":
181
190
  if (node.argument !== undefined) {