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.
- package/dist/index.js +7473 -6816
- 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-KMTTD3BL.js → chunk-4VT2M7WL.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-RU2BCN4G.js → chunk-WBWD7ZCM.js} +8215 -7599
- package/packages/safe-js/dist/chunks/chunk-WBWD7ZCM.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/index.js.map +1 -1
- package/packages/safe-js/dist/interp/async.d.ts +13 -2
- package/packages/safe-js/dist/interp/async.js +16 -6
- package/packages/safe-js/dist/interp/classes.d.ts +4 -0
- package/packages/safe-js/dist/interp/classes.js +176 -0
- package/packages/safe-js/dist/interp/exceptions.js +4 -1
- package/packages/safe-js/dist/interp/globals/object-array.d.ts +2 -1
- package/packages/safe-js/dist/interp/globals/object-array.js +1 -1
- package/packages/safe-js/dist/interp/globals/object.js +10 -2
- package/packages/safe-js/dist/interp/globals/primitives.js +8 -3
- package/packages/safe-js/dist/interp/interpreter.d.ts +1 -1
- package/packages/safe-js/dist/interp/interpreter.js +99 -30
- package/packages/safe-js/dist/interp/methods/function.d.ts +1 -1
- package/packages/safe-js/dist/interp/methods/function.js +33 -16
- package/packages/safe-js/dist/interp/object-model.js +25 -11
- package/packages/safe-js/dist/interp/patterns.d.ts +1 -1
- package/packages/safe-js/dist/interp/patterns.js +1 -1
- package/packages/safe-js/dist/interp/values.d.ts +2 -1
- package/packages/safe-js/dist/lint/class-elements.d.ts +5 -0
- package/packages/safe-js/dist/lint/class-elements.js +20 -0
- package/packages/safe-js/dist/lint/rules/AS-async-not-needed.js +16 -0
- package/packages/safe-js/dist/lint/rules/AS-destructure-null-default.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-export-import-meta.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-floating-promise.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-frontmatter-field-unused.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-jsdoc-type.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-large-literal.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-missing-async.js +16 -0
- package/packages/safe-js/dist/lint/rules/AS-mutating-frozen.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-needless-template.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-shadow-global.js +12 -0
- package/packages/safe-js/dist/lint/rules/AS-unbounded-loop.js +11 -0
- package/packages/safe-js/dist/lint/rules/AS-unreachable.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS-unused-import.js +16 -2
- package/packages/safe-js/dist/lint/rules/AS001.js +0 -1
- package/packages/safe-js/dist/lint/rules/AS003.js +16 -2
- package/packages/safe-js/dist/lint/rules/AS006-007.js +16 -2
- package/packages/safe-js/dist/lint/rules/AS009.js +20 -1
- package/packages/safe-js/dist/lint/rules/AS010.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS011.js +9 -0
- package/packages/safe-js/dist/lint/rules/AS015.js +9 -0
- package/packages/safe-js/dist/parse/function-source.d.ts +2 -2
- package/packages/safe-js/dist/parse/parser.d.ts +42 -2
- package/packages/safe-js/dist/parse/parser.js +180 -36
- package/packages/safe-js/dist/parse/tokenizer.js +3 -0
- package/packages/safe-js/dist/parse.d.ts +1 -1
- package/packages/safe-js/dist/chunks/chunk-RU2BCN4G.js.map +0 -7
- /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
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
2223
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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,
|
|
95
|
-
|
|
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
|
|
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(({
|
|
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
|
-
|
|
136
|
-
|
|
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
|
|
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) {
|