poe-code 14.0.64 → 14.0.66
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 +8832 -7273
- 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-3EXDJDQM.js → chunk-MKTUVVEB.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-XO7B4P27.js → chunk-YKISHTWT.js} +4474 -2978
- package/packages/safe-js/dist/chunks/chunk-YKISHTWT.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/accessors.d.ts +9 -0
- package/packages/safe-js/dist/interp/accessors.js +68 -0
- package/packages/safe-js/dist/interp/async.d.ts +13 -2
- package/packages/safe-js/dist/interp/async.js +19 -9
- package/packages/safe-js/dist/interp/classes.d.ts +4 -0
- package/packages/safe-js/dist/interp/classes.js +182 -0
- package/packages/safe-js/dist/interp/exceptions.d.ts +1 -1
- package/packages/safe-js/dist/interp/exceptions.js +51 -9
- package/packages/safe-js/dist/interp/globals/collections.js +53 -16
- package/packages/safe-js/dist/interp/globals/console-json.js +41 -26
- package/packages/safe-js/dist/interp/globals/object-array.d.ts +2 -1
- package/packages/safe-js/dist/interp/globals/object-array.js +230 -69
- package/packages/safe-js/dist/interp/globals/object.js +13 -2
- package/packages/safe-js/dist/interp/globals/primitives.js +16 -5
- package/packages/safe-js/dist/interp/interpreter.d.ts +2 -2
- package/packages/safe-js/dist/interp/interpreter.js +168 -61
- package/packages/safe-js/dist/interp/iteration.js +13 -0
- package/packages/safe-js/dist/interp/methods/array.d.ts +1 -1
- package/packages/safe-js/dist/interp/methods/array.js +368 -109
- package/packages/safe-js/dist/interp/methods/function.d.ts +1 -1
- package/packages/safe-js/dist/interp/methods/function.js +56 -20
- package/packages/safe-js/dist/interp/methods/regex.js +1 -1
- package/packages/safe-js/dist/interp/object-model.d.ts +1 -0
- package/packages/safe-js/dist/interp/object-model.js +48 -10
- package/packages/safe-js/dist/interp/patterns.d.ts +3 -3
- package/packages/safe-js/dist/interp/patterns.js +36 -17
- package/packages/safe-js/dist/interp/promise.d.ts +1 -1
- package/packages/safe-js/dist/interp/promise.js +103 -63
- package/packages/safe-js/dist/interp/string-coercion.d.ts +1 -0
- package/packages/safe-js/dist/interp/string-coercion.js +47 -37
- package/packages/safe-js/dist/interp/values.d.ts +4 -2
- package/packages/safe-js/dist/interp/values.js +26 -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 +208 -35
- 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-XO7B4P27.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-3EXDJDQM.js.map → chunk-MKTUVVEB.js.map} +0 -0
|
@@ -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;
|
|
@@ -41,13 +41,13 @@ export function getFunctionMember(target, property, options) {
|
|
|
41
41
|
sandbox: true,
|
|
42
42
|
name: `Function#${property}`,
|
|
43
43
|
...(property === "toString" ? { length: 0 } : {}),
|
|
44
|
-
call: (args, context) => callFunctionMethod(context?.thisValue, property, args, options, context?.stack ?? [])
|
|
44
|
+
call: (args, context) => callFunctionMethod(context?.thisValue, property, args, options, context?.stack ?? [], context)
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
function isFunctionMethodName(property) {
|
|
48
48
|
return typeof property === "string" && functionMethodNames.has(property);
|
|
49
49
|
}
|
|
50
|
-
function callFunctionMethod(target, methodName, args, options, stack) {
|
|
50
|
+
function callFunctionMethod(target, methodName, args, options, stack, context) {
|
|
51
51
|
if (!isSandboxClosure(target)) {
|
|
52
52
|
throw new TypeError(`Function#${methodName} requires a callable receiver.`);
|
|
53
53
|
}
|
|
@@ -58,23 +58,41 @@ function callFunctionMethod(target, methodName, args, options, stack) {
|
|
|
58
58
|
const thisValue = args[0];
|
|
59
59
|
if (methodName === "bind") {
|
|
60
60
|
const boundArgs = args.slice(1);
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
61
|
+
const bind = (length, name) => {
|
|
62
|
+
const bound = createSandboxClosure({
|
|
63
|
+
guest: true,
|
|
64
|
+
sandbox: true,
|
|
65
|
+
name: `bound ${name}`,
|
|
66
|
+
length,
|
|
67
|
+
boundTarget: target,
|
|
68
|
+
retainedValues: () => [target, thisValue, ...boundArgs, bound.name],
|
|
69
|
+
call: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], thisValue),
|
|
70
|
+
...(target.construct === undefined
|
|
71
|
+
? {}
|
|
72
|
+
: {
|
|
73
|
+
construct: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], undefined, true, context?.newTarget === bound ? target : context?.newTarget)
|
|
74
|
+
})
|
|
75
|
+
});
|
|
76
|
+
if (hasExplicitSandboxPrototype(target))
|
|
77
|
+
setSandboxPrototype(bound, getSandboxPrototype(target, options.budget), options.budget);
|
|
78
|
+
return bound;
|
|
79
|
+
};
|
|
80
|
+
if (context?.getProperty === undefined)
|
|
81
|
+
return bind(target.length === undefined ? undefined : Math.max(0, target.length - boundArgs.length), target.name ?? "");
|
|
82
|
+
return (async () => {
|
|
83
|
+
const properties = target.properties;
|
|
84
|
+
const defaultName = target.name;
|
|
85
|
+
const hasLength = !isGuestClosure(target) ||
|
|
86
|
+
target.properties === undefined ||
|
|
87
|
+
Object.hasOwn(target.properties, "length");
|
|
88
|
+
const length = hasLength ? await context.getProperty(target, "length") : undefined;
|
|
89
|
+
const name = !isGuestClosure(target) && !Object.hasOwn(properties ?? {}, "name")
|
|
90
|
+
? defaultName
|
|
91
|
+
: await context.getProperty(target, "name");
|
|
92
|
+
return bind(typeof length === "number" && !Number.isNaN(length)
|
|
93
|
+
? Math.max(0, Math.trunc(length) - boundArgs.length)
|
|
94
|
+
: 0, typeof name === "string" ? name : "");
|
|
95
|
+
})();
|
|
78
96
|
}
|
|
79
97
|
if (methodName === "call") {
|
|
80
98
|
return options.callClosure(target, args.slice(1), stack, thisValue);
|
|
@@ -86,5 +104,23 @@ function callFunctionMethod(target, methodName, args, options, stack) {
|
|
|
86
104
|
if (!Array.isArray(applyArgs)) {
|
|
87
105
|
throw new TypeError("Function#apply requires an array or nullish arguments value.");
|
|
88
106
|
}
|
|
89
|
-
|
|
107
|
+
if (context?.getProperty === undefined)
|
|
108
|
+
return options.callClosure(target, applyArgs, stack, thisValue);
|
|
109
|
+
return (async () => {
|
|
110
|
+
const values = [];
|
|
111
|
+
const length = applyArgs.length;
|
|
112
|
+
options.budget?.allocateArrayLength(length);
|
|
113
|
+
const retained = {};
|
|
114
|
+
options.budget?.setRetainedValues(retained, () => values);
|
|
115
|
+
try {
|
|
116
|
+
for (let index = 0; index < length; index++) {
|
|
117
|
+
options.budget?.visitNode();
|
|
118
|
+
values.push(await context.getProperty(applyArgs, index));
|
|
119
|
+
}
|
|
120
|
+
return await options.callClosure(target, values, stack, thisValue);
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
options.budget?.setRetainedValues(retained, undefined);
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
90
126
|
}
|
|
@@ -84,7 +84,7 @@ export async function callRegexMethod(target, methodName, args, budget, context)
|
|
|
84
84
|
const input = await sandboxString(args[0], budget, context);
|
|
85
85
|
if (methodName === "test") {
|
|
86
86
|
const exec = context?.getProperty === undefined
|
|
87
|
-
? getSandboxDataProperty(target, "exec", budget) : context.getProperty(target, "exec");
|
|
87
|
+
? getSandboxDataProperty(target, "exec", budget) : await context.getProperty(target, "exec");
|
|
88
88
|
if (isSandboxClosure(exec)) {
|
|
89
89
|
const result = await invokeBuiltinClosure(exec, [input], budget, context, target);
|
|
90
90
|
if (result !== null && typeof result !== "object") {
|
|
@@ -14,6 +14,7 @@ export declare function isIntrinsicConstructor(value: object): boolean;
|
|
|
14
14
|
export declare function releaseObjectPrototype(budget: Budget): void;
|
|
15
15
|
export declare function getSandboxPrototype(value: object, budget?: Budget): object | null;
|
|
16
16
|
export declare function hasExplicitSandboxPrototype(value: object): boolean;
|
|
17
|
+
export declare function getSandboxPropertyDescriptor(value: SandboxValue, key: string | number, budget?: Budget): PropertyDescriptor | undefined;
|
|
17
18
|
export declare function getSandboxDataProperty(value: SandboxValue, key: string | number, budget?: Budget): SandboxValue;
|
|
18
19
|
export declare function setSandboxPrototype(value: object, prototype: object | null, budget?: Budget): void;
|
|
19
20
|
export declare function markDescriptorObject(value: object): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
2
|
+
import { retainedAccessorClosures } from "./accessors.js";
|
|
2
3
|
import { getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
|
|
3
4
|
import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
4
5
|
import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "./values.js";
|
|
@@ -89,27 +90,41 @@ export function isIntrinsicConstructor(value) {
|
|
|
89
90
|
function registerIntrinsicPrototype(budget, prototype, constructor) {
|
|
90
91
|
let roots = intrinsicPrototypeRoots.get(budget);
|
|
91
92
|
if (roots === undefined)
|
|
92
|
-
intrinsicPrototypeRoots.set(budget, roots = new Set());
|
|
93
|
+
intrinsicPrototypeRoots.set(budget, (roots = new Set()));
|
|
93
94
|
roots.add(prototype);
|
|
94
|
-
const records = [prototype, constructor]
|
|
95
|
+
const records = [prototype, constructor]
|
|
96
|
+
.map((target) => ({
|
|
95
97
|
target,
|
|
96
98
|
value: isGuestClosure(target) ? materializeFunctionProperties(target) : target,
|
|
97
99
|
prototype: getSandboxPrototype(target),
|
|
98
100
|
explicit: hasExplicitSandboxPrototype(target)
|
|
99
|
-
}))
|
|
100
|
-
|
|
101
|
+
}))
|
|
102
|
+
.map((record) => ({
|
|
103
|
+
...record,
|
|
104
|
+
descriptors: new Map(Object.entries(Object.getOwnPropertyDescriptors(record.value)))
|
|
101
105
|
}));
|
|
102
|
-
const unchanged = (before, after) => before !== undefined &&
|
|
103
|
-
|
|
106
|
+
const unchanged = (before, after) => before !== undefined &&
|
|
107
|
+
after !== undefined &&
|
|
108
|
+
Object.is(before.value, after.value) &&
|
|
109
|
+
before.get === after.get &&
|
|
110
|
+
before.set === after.set &&
|
|
111
|
+
before.writable === after.writable &&
|
|
112
|
+
before.configurable === after.configurable &&
|
|
113
|
+
before.enumerable === after.enumerable;
|
|
104
114
|
intrinsicConstructors.set(constructor, () => records.every(({ target, value, descriptors, prototype: parent, explicit }) => {
|
|
105
115
|
const current = Object.getOwnPropertyDescriptors(value);
|
|
106
|
-
return getSandboxPrototype(target) === parent &&
|
|
116
|
+
return (getSandboxPrototype(target) === parent &&
|
|
117
|
+
hasExplicitSandboxPrototype(target) === explicit &&
|
|
107
118
|
Object.keys(current).length === descriptors.size &&
|
|
108
|
-
Object.keys(current).every(key => unchanged(descriptors.get(key), current[key]));
|
|
119
|
+
Object.keys(current).every((key) => unchanged(descriptors.get(key), current[key])));
|
|
109
120
|
}));
|
|
110
121
|
budget.setRetainedValues(prototype, () => records.flatMap(({ target, value, descriptors, prototype: parent }) => [
|
|
111
|
-
...(getSandboxPrototype(target) === parent
|
|
112
|
-
|
|
122
|
+
...(getSandboxPrototype(target) === parent
|
|
123
|
+
? []
|
|
124
|
+
: [getSandboxPrototype(target)]),
|
|
125
|
+
...Object.entries(Object.getOwnPropertyDescriptors(value)).flatMap(([key, descriptor]) => unchanged(descriptors.get(key), descriptor)
|
|
126
|
+
? []
|
|
127
|
+
: [key, descriptor.value, ...retainedAccessorClosures(descriptor)])
|
|
113
128
|
]));
|
|
114
129
|
}
|
|
115
130
|
export function releaseObjectPrototype(budget) {
|
|
@@ -134,6 +149,29 @@ export function getSandboxPrototype(value, budget) {
|
|
|
134
149
|
export function hasExplicitSandboxPrototype(value) {
|
|
135
150
|
return prototypes.has(value);
|
|
136
151
|
}
|
|
152
|
+
export function getSandboxPropertyDescriptor(value, key, budget) {
|
|
153
|
+
const hostProperties = isSandboxClosure(value) ? value.properties : undefined;
|
|
154
|
+
if (isSandboxClosure(value) && !isGuestClosure(value))
|
|
155
|
+
return hostProperties === undefined
|
|
156
|
+
? undefined
|
|
157
|
+
: Object.getOwnPropertyDescriptor(hostProperties, key);
|
|
158
|
+
let current = value;
|
|
159
|
+
let depth = 0;
|
|
160
|
+
while (typeof current === "object" &&
|
|
161
|
+
current !== null &&
|
|
162
|
+
(Array.isArray(current) || isPrototypeRecord(current))) {
|
|
163
|
+
const properties = isGuestClosure(current) ? getGuestFunctionProperties(current) : current;
|
|
164
|
+
const descriptor = properties === undefined ? undefined : Object.getOwnPropertyDescriptor(properties, key);
|
|
165
|
+
if (descriptor !== undefined)
|
|
166
|
+
return descriptor;
|
|
167
|
+
current = getSandboxPrototype(current, budget);
|
|
168
|
+
if (current !== null) {
|
|
169
|
+
budget?.visitNode();
|
|
170
|
+
assertSandboxDataDepth(++depth);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
137
175
|
export function getSandboxDataProperty(value, key, budget) {
|
|
138
176
|
let current = value;
|
|
139
177
|
let depth = 0;
|
|
@@ -12,10 +12,10 @@ 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
|
-
getProperty(value: SandboxValue, key: string | number): SandboxValue
|
|
18
|
-
setProperty(target: SandboxValue, key: string | number, value: SandboxValue): void
|
|
17
|
+
getProperty(value: SandboxValue, key: string | number): SandboxValue | Promise<SandboxValue>;
|
|
18
|
+
setProperty(target: SandboxValue, key: string | number, value: SandboxValue): void | Promise<void>;
|
|
19
19
|
};
|
|
20
20
|
export type BindPatternResult = {
|
|
21
21
|
ok: true;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { retainValues } from "./resources.js";
|
|
2
|
+
import { hasOwnSandboxProperty } from "./globals/object.js";
|
|
1
3
|
import { isSandboxCollectionIterator, nextCollectionIterator } from "./collection-iterator.js";
|
|
2
|
-
import { isSandboxMap, isSandboxSet,
|
|
4
|
+
import { isSandboxMap, isSandboxSet, ownEnumerableSandboxKeys } from "./values.js";
|
|
3
5
|
export async function bindPattern(pattern, value, target, scope, context) {
|
|
4
6
|
switch (pattern.type) {
|
|
5
7
|
case "Identifier":
|
|
@@ -40,7 +42,7 @@ async function bindAssignmentPattern(pattern, value, target, scope, context) {
|
|
|
40
42
|
if (value !== undefined) {
|
|
41
43
|
return bindPattern(pattern.left, value, target, scope, context);
|
|
42
44
|
}
|
|
43
|
-
const defaultValue = await context.evaluate(pattern.right);
|
|
45
|
+
const defaultValue = await context.evaluate(pattern.right, pattern.left.type === "Identifier" ? pattern.left.name : undefined);
|
|
44
46
|
if (defaultValue.kind !== "normal") {
|
|
45
47
|
return { ok: false, result: defaultValue };
|
|
46
48
|
}
|
|
@@ -49,27 +51,34 @@ async function bindAssignmentPattern(pattern, value, target, scope, context) {
|
|
|
49
51
|
async function bindArrayPattern(pattern, value, target, scope, context) {
|
|
50
52
|
const iterator = isSandboxCollectionIterator(value) ? value : undefined;
|
|
51
53
|
const values = iterator === undefined ? getArrayPatternValues(value) : undefined;
|
|
54
|
+
let cursor = 0;
|
|
55
|
+
let done = false;
|
|
56
|
+
const next = async () => {
|
|
57
|
+
if (iterator !== undefined)
|
|
58
|
+
return nextCollectionIterator(iterator, context.budget);
|
|
59
|
+
if (done || cursor >= values.length) {
|
|
60
|
+
done = true;
|
|
61
|
+
return { done: true, value: undefined };
|
|
62
|
+
}
|
|
63
|
+
return { done: false, value: await context.getProperty(values, cursor++) };
|
|
64
|
+
};
|
|
52
65
|
for (let index = 0; index < pattern.elements.length; index += 1) {
|
|
53
66
|
const element = pattern.elements[index];
|
|
54
67
|
if (element === null) {
|
|
55
|
-
|
|
56
|
-
nextCollectionIterator(iterator, context.budget);
|
|
68
|
+
await next();
|
|
57
69
|
continue;
|
|
58
70
|
}
|
|
59
71
|
let elementValue;
|
|
60
|
-
if (
|
|
61
|
-
elementValue = element.type === "RestElement" ? values.slice(index) : values[index];
|
|
62
|
-
}
|
|
63
|
-
else if (element.type === "RestElement") {
|
|
72
|
+
if (element.type === "RestElement") {
|
|
64
73
|
const rest = [];
|
|
65
|
-
for (let entry =
|
|
74
|
+
for (let entry = await next(); !entry.done; entry = await next()) {
|
|
66
75
|
context.budget?.allocateArrayLength(rest.length + 1);
|
|
67
76
|
rest.push(entry.value);
|
|
68
77
|
}
|
|
69
78
|
elementValue = rest;
|
|
70
79
|
}
|
|
71
80
|
else {
|
|
72
|
-
elementValue =
|
|
81
|
+
elementValue = (await next()).value;
|
|
73
82
|
}
|
|
74
83
|
const binding = await bindPattern(element, elementValue, target, scope, context);
|
|
75
84
|
if (!binding.ok) {
|
|
@@ -85,7 +94,7 @@ async function bindObjectPattern(pattern, value, target, scope, context) {
|
|
|
85
94
|
const excludedKeys = new Set();
|
|
86
95
|
for (const property of pattern.properties) {
|
|
87
96
|
if (property.type === "RestElement") {
|
|
88
|
-
const binding = await bindPattern(property, copyObjectRestValue(value, excludedKeys), target, scope, context);
|
|
97
|
+
const binding = await bindPattern(property, await copyObjectRestValue(value, excludedKeys, context), target, scope, context);
|
|
89
98
|
if (!binding.ok) {
|
|
90
99
|
return binding;
|
|
91
100
|
}
|
|
@@ -96,7 +105,7 @@ async function bindObjectPattern(pattern, value, target, scope, context) {
|
|
|
96
105
|
return key;
|
|
97
106
|
}
|
|
98
107
|
excludedKeys.add(String(key.value));
|
|
99
|
-
const binding = await bindPattern(property.value, context.getProperty(value, key.value), target, scope, context);
|
|
108
|
+
const binding = await bindPattern(property.value, await context.getProperty(value, key.value), target, scope, context);
|
|
100
109
|
if (!binding.ok) {
|
|
101
110
|
return binding;
|
|
102
111
|
}
|
|
@@ -120,7 +129,7 @@ async function bindMemberExpression(pattern, value, scope, context) {
|
|
|
120
129
|
if (!isIndexableValue(object.value)) {
|
|
121
130
|
throw new TypeError("Assignment expressions require a sandbox object property.");
|
|
122
131
|
}
|
|
123
|
-
context.setProperty(object.value, await context.toPropertyKey(property.value), value);
|
|
132
|
+
await context.setProperty(object.value, await context.toPropertyKey(property.value), value);
|
|
124
133
|
return { ok: true };
|
|
125
134
|
}
|
|
126
135
|
async function evaluatePatternKey(property, context) {
|
|
@@ -177,12 +186,22 @@ function describeRuntimeValue(value) {
|
|
|
177
186
|
return value.constructor?.name ?? "Object";
|
|
178
187
|
return typeof value;
|
|
179
188
|
}
|
|
180
|
-
function copyObjectRestValue(value, excludedKeys) {
|
|
189
|
+
async function copyObjectRestValue(value, excludedKeys, context) {
|
|
181
190
|
const rest = Object.create(null);
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
const release = context.budget === undefined
|
|
192
|
+
? () => undefined
|
|
193
|
+
: retainValues(context.budget, () => [value, rest]);
|
|
194
|
+
try {
|
|
195
|
+
for (const key of ownEnumerableSandboxKeys(value)) {
|
|
196
|
+
if (excludedKeys.has(key) || !hasOwnSandboxProperty(value, key, true))
|
|
197
|
+
continue;
|
|
198
|
+
defineProperty(rest, key, await context.getProperty(value, key));
|
|
199
|
+
}
|
|
200
|
+
return rest;
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
release();
|
|
184
204
|
}
|
|
185
|
-
return rest;
|
|
186
205
|
}
|
|
187
206
|
function isIndexableValue(value) {
|
|
188
207
|
return typeof value === "object" && value !== null;
|
|
@@ -13,4 +13,4 @@ export declare function resolveSandboxValue(value: SandboxValue | Promise<Sandbo
|
|
|
13
13
|
self?: SandboxPromise;
|
|
14
14
|
}): Promise<SandboxValue>;
|
|
15
15
|
export declare function consumeSettledHostCall(value: SandboxPromise): undefined;
|
|
16
|
-
export declare function
|
|
16
|
+
export declare function requiresPromiseResolution(value: SandboxValue, budget?: Budget): boolean;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { SandboxError } from "./budget.js";
|
|
2
|
+
import { accessorClosure } from "./accessors.js";
|
|
3
|
+
import { getSandboxPropertyDescriptor } from "./object-model.js";
|
|
2
4
|
import { coerceThrownValue, createSubsetErrorValue } from "./exceptions.js";
|
|
3
5
|
import { getSandboxIterator } from "./iteration.js";
|
|
4
6
|
import { retainValues } from "./resources.js";
|
|
@@ -197,7 +199,7 @@ async function callPromiseClosure(callback, args, thisValue, budget, context) {
|
|
|
197
199
|
const values = args.map((value) => value instanceof Error && !(value instanceof SandboxError)
|
|
198
200
|
? coerceThrownValue(value, budget, stack)
|
|
199
201
|
: value);
|
|
200
|
-
let result = callback.call(values, { stack, thisValue });
|
|
202
|
+
let result = callback.call(values, { ...context, stack, thisValue, newTarget: undefined });
|
|
201
203
|
if (callback.async !== true)
|
|
202
204
|
result = await result;
|
|
203
205
|
else if (isPromiseLike(result))
|
|
@@ -226,10 +228,10 @@ function getPromisePrototype(budget) {
|
|
|
226
228
|
observeSandboxPromise(target);
|
|
227
229
|
const chained = createSandboxPromise(target.promise.then((value) => {
|
|
228
230
|
consumeSettledHostCall(target);
|
|
229
|
-
return runPromiseReaction(onFulfilled, value, "fulfilled", budget, chained);
|
|
231
|
+
return runPromiseReaction(onFulfilled, value, "fulfilled", budget, chained, context);
|
|
230
232
|
}, (reason) => {
|
|
231
233
|
consumeSettledHostCall(target);
|
|
232
|
-
return runPromiseReaction(onRejected, reason, "rejected", budget, chained);
|
|
234
|
+
return runPromiseReaction(onRejected, reason, "rejected", budget, chained, context);
|
|
233
235
|
}));
|
|
234
236
|
return chained;
|
|
235
237
|
},
|
|
@@ -239,13 +241,18 @@ function getPromisePrototype(budget) {
|
|
|
239
241
|
sandbox: true,
|
|
240
242
|
call: ([onRejected], context) => {
|
|
241
243
|
const target = context?.thisValue;
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
244
|
+
const invoke = (then) => {
|
|
245
|
+
if (!isSandboxClosure(then))
|
|
246
|
+
throw new TypeError("Promise.catch requires a callable then.");
|
|
247
|
+
return then.call([undefined, onRejected], {
|
|
248
|
+
...context,
|
|
249
|
+
stack: context?.stack ?? [],
|
|
250
|
+
thisValue: target,
|
|
251
|
+
newTarget: undefined
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
const then = readPromiseReceiverProperty(target, "then", prototype, context);
|
|
255
|
+
return then instanceof Promise ? then.then(invoke) : invoke(then);
|
|
249
256
|
},
|
|
250
257
|
name: "catch"
|
|
251
258
|
}),
|
|
@@ -256,39 +263,50 @@ function getPromisePrototype(budget) {
|
|
|
256
263
|
if (typeof target !== "object" || target === null) {
|
|
257
264
|
throw new TypeError("Promise.finally requires an object receiver.");
|
|
258
265
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
266
|
+
const invoke = (then) => {
|
|
267
|
+
if (!isSandboxClosure(then))
|
|
268
|
+
throw new TypeError("Promise.finally requires a callable then.");
|
|
269
|
+
const handlers = isSandboxClosure(onFinally)
|
|
270
|
+
? ["fulfilled", "rejected"].map((state) => createSandboxClosure({
|
|
271
|
+
sandbox: true,
|
|
272
|
+
retainedValues: () => [onFinally],
|
|
273
|
+
call: async ([value]) => {
|
|
274
|
+
const result = await callPromiseClosure(onFinally, [], undefined, budget, context);
|
|
275
|
+
const pending = isSandboxPromise(result) &&
|
|
276
|
+
getPromiseMember("constructor", budget) ===
|
|
277
|
+
intrinsicPromiseConstructors.get(budget)
|
|
278
|
+
? result
|
|
279
|
+
: createSandboxPromise(resolveSandboxValue(result, { budget }));
|
|
280
|
+
const cleanupThen = getPromiseMember("then", budget);
|
|
281
|
+
if (!isSandboxClosure(cleanupThen))
|
|
282
|
+
throw new TypeError("Promise cleanup requires a callable then.");
|
|
283
|
+
return callPromiseClosure(cleanupThen, [
|
|
284
|
+
createSandboxClosure({
|
|
285
|
+
sandbox: true,
|
|
286
|
+
retainedValues: () => [value],
|
|
287
|
+
call: () => {
|
|
288
|
+
if (state === "rejected")
|
|
289
|
+
throw value;
|
|
290
|
+
return value;
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
], pending, budget, context);
|
|
294
|
+
}
|
|
295
|
+
}))
|
|
296
|
+
: [onFinally, onFinally];
|
|
297
|
+
return then.call(handlers, {
|
|
298
|
+
...context,
|
|
299
|
+
stack: context?.stack ?? [],
|
|
300
|
+
thisValue: target,
|
|
301
|
+
newTarget: undefined
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
const finish = () => {
|
|
305
|
+
const then = readPromiseReceiverProperty(target, "then", prototype, context);
|
|
306
|
+
return then instanceof Promise ? then.then(invoke) : invoke(then);
|
|
307
|
+
};
|
|
308
|
+
const validation = validatePromiseConstructorProperty(target, prototype, context);
|
|
309
|
+
return validation instanceof Promise ? validation.then(finish) : finish();
|
|
292
310
|
},
|
|
293
311
|
name: "finally"
|
|
294
312
|
})
|
|
@@ -299,7 +317,9 @@ function getPromisePrototype(budget) {
|
|
|
299
317
|
promisePrototypes.set(budget, prototype);
|
|
300
318
|
return prototype;
|
|
301
319
|
}
|
|
302
|
-
function readPromiseReceiverProperty(receiver, property, prototype) {
|
|
320
|
+
function readPromiseReceiverProperty(receiver, property, prototype, context) {
|
|
321
|
+
if (!isSandboxPromise(receiver) && context?.getProperty !== undefined)
|
|
322
|
+
return context.getProperty(receiver, property);
|
|
303
323
|
const properties = isSandboxPromise(receiver)
|
|
304
324
|
? prototype
|
|
305
325
|
: isSandboxClosure(receiver)
|
|
@@ -311,11 +331,13 @@ function readPromiseReceiverProperty(receiver, property, prototype) {
|
|
|
311
331
|
? properties[property]
|
|
312
332
|
: undefined;
|
|
313
333
|
}
|
|
314
|
-
function validatePromiseConstructorProperty(receiver, prototype) {
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
334
|
+
function validatePromiseConstructorProperty(receiver, prototype, context) {
|
|
335
|
+
const validate = (constructor) => {
|
|
336
|
+
if (constructor !== undefined && (typeof constructor !== "object" || constructor === null))
|
|
337
|
+
throw new TypeError("Promise constructor property must be an object.");
|
|
338
|
+
};
|
|
339
|
+
const constructor = readPromiseReceiverProperty(receiver, "constructor", prototype, context);
|
|
340
|
+
return constructor instanceof Promise ? constructor.then(validate) : validate(constructor);
|
|
319
341
|
}
|
|
320
342
|
async function settleIterable(iterable, method, budget, constructor, context) {
|
|
321
343
|
const capability = await createPromiseCapability(constructor, budget, context);
|
|
@@ -335,7 +357,7 @@ async function settleIterable(iterable, method, budget, constructor, context) {
|
|
|
335
357
|
}
|
|
336
358
|
};
|
|
337
359
|
try {
|
|
338
|
-
const promiseResolve = readPromiseReceiverProperty(constructor, "resolve", prototype);
|
|
360
|
+
const promiseResolve = await readPromiseReceiverProperty(constructor, "resolve", prototype, context);
|
|
339
361
|
if (!isSandboxClosure(promiseResolve))
|
|
340
362
|
throw new TypeError("Promise constructor requires a callable resolve.");
|
|
341
363
|
const iterator = getSandboxIterator(iterable, budget, context);
|
|
@@ -391,7 +413,7 @@ async function settleIterable(iterable, method, budget, constructor, context) {
|
|
|
391
413
|
});
|
|
392
414
|
});
|
|
393
415
|
remaining++;
|
|
394
|
-
const then = readPromiseReceiverProperty(entry, "then", prototype);
|
|
416
|
+
const then = await readPromiseReceiverProperty(entry, "then", prototype, context);
|
|
395
417
|
if (!isSandboxClosure(then))
|
|
396
418
|
throw new TypeError("Promise resolver result requires a callable then.");
|
|
397
419
|
await callPromiseClosure(then, handlers, entry, budget, context);
|
|
@@ -472,7 +494,11 @@ function resolveSandboxValueNow(value, options) {
|
|
|
472
494
|
return Promise.reject(budgetIfNeeded(reason, options.budget));
|
|
473
495
|
});
|
|
474
496
|
}
|
|
475
|
-
const then = getThenable(value);
|
|
497
|
+
const then = getThenable(value, options.budget);
|
|
498
|
+
if (then instanceof Promise)
|
|
499
|
+
return then.then((method) => method === undefined
|
|
500
|
+
? budgetIfNeeded(value, options.budget)
|
|
501
|
+
: resolveThenable(value, method, options));
|
|
476
502
|
if (then !== undefined) {
|
|
477
503
|
return resolveThenable(value, then, options);
|
|
478
504
|
}
|
|
@@ -492,7 +518,7 @@ function resolveThenable(value, then, options) {
|
|
|
492
518
|
completed = true;
|
|
493
519
|
try {
|
|
494
520
|
if (settlement.state === "fulfilled") {
|
|
495
|
-
resolve(
|
|
521
|
+
resolve(requiresPromiseResolution(settlement.value, options.budget)
|
|
496
522
|
? resolveSandboxValueNow(settlement.value, options)
|
|
497
523
|
: budgetIfNeeded(settlement.value, options.budget));
|
|
498
524
|
}
|
|
@@ -547,7 +573,7 @@ function resolveThenable(value, then, options) {
|
|
|
547
573
|
}).catch(reject);
|
|
548
574
|
});
|
|
549
575
|
}
|
|
550
|
-
function runPromiseReaction(handler, value, state, budget, self) {
|
|
576
|
+
function runPromiseReaction(handler, value, state, budget, self, context) {
|
|
551
577
|
return new Promise((resolve, reject) => {
|
|
552
578
|
if (state === "rejected" &&
|
|
553
579
|
value instanceof SandboxError &&
|
|
@@ -563,7 +589,7 @@ function runPromiseReaction(handler, value, state, budget, self) {
|
|
|
563
589
|
else if (isSelfResolution(result, self)) {
|
|
564
590
|
reject(createSubsetErrorValue("TypeError", "Promise cannot resolve to itself.", [], budget));
|
|
565
591
|
}
|
|
566
|
-
else if (
|
|
592
|
+
else if (requiresPromiseResolution(result, budget)) {
|
|
567
593
|
resolve(resolvePromiseResult(result, budget, self));
|
|
568
594
|
}
|
|
569
595
|
else {
|
|
@@ -571,7 +597,7 @@ function runPromiseReaction(handler, value, state, budget, self) {
|
|
|
571
597
|
}
|
|
572
598
|
};
|
|
573
599
|
if (isSandboxClosure(handler)) {
|
|
574
|
-
callInPromiseJob(handler, [argument], undefined, { fulfilled, rejected: reject }).catch(reject);
|
|
600
|
+
callInPromiseJob(handler, [argument], undefined, { fulfilled, rejected: reject }, context).catch(reject);
|
|
575
601
|
}
|
|
576
602
|
else {
|
|
577
603
|
runPromiseJob(() => {
|
|
@@ -583,10 +609,10 @@ function runPromiseReaction(handler, value, state, budget, self) {
|
|
|
583
609
|
}
|
|
584
610
|
});
|
|
585
611
|
}
|
|
586
|
-
function callInPromiseJob(handler, args, thisValue = undefined, completion) {
|
|
612
|
+
function callInPromiseJob(handler, args, thisValue = undefined, completion, context) {
|
|
587
613
|
return runPromiseJob(async () => {
|
|
588
614
|
try {
|
|
589
|
-
let result = handler.call(args, { stack: [], thisValue });
|
|
615
|
+
let result = handler.call(args, { ...context, stack: [], thisValue, newTarget: undefined });
|
|
590
616
|
if (handler.async !== true)
|
|
591
617
|
result = await result;
|
|
592
618
|
if (isSandboxPromise(result) && result.synchronousPrefix !== undefined) {
|
|
@@ -624,7 +650,7 @@ function resolvePromiseResult(result, budget, self) {
|
|
|
624
650
|
try {
|
|
625
651
|
if (state === "rejected")
|
|
626
652
|
reject(budgetSandboxValue(value, budget));
|
|
627
|
-
else if (
|
|
653
|
+
else if (requiresPromiseResolution(value, budget)) {
|
|
628
654
|
resolve(resolvePromiseResult(value, budget, self));
|
|
629
655
|
}
|
|
630
656
|
else {
|
|
@@ -672,13 +698,27 @@ function isSelfResolution(result, self) {
|
|
|
672
698
|
return (self !== undefined &&
|
|
673
699
|
(result === self || (isSandboxPromise(result) && result.promise === self.promise)));
|
|
674
700
|
}
|
|
675
|
-
export function
|
|
701
|
+
export function requiresPromiseResolution(value, budget) {
|
|
702
|
+
if (isSandboxPromise(value))
|
|
703
|
+
return true;
|
|
704
|
+
const descriptor = getSandboxPropertyDescriptor(value, "then", budget);
|
|
705
|
+
return (descriptor !== undefined && (!("value" in descriptor) || isSandboxClosure(descriptor.value)));
|
|
706
|
+
}
|
|
707
|
+
function getThenable(value, budget) {
|
|
676
708
|
if (typeof value !== "object" || value === null || isSandboxPromise(value)) {
|
|
677
709
|
return undefined;
|
|
678
710
|
}
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
|
|
711
|
+
const descriptor = getSandboxPropertyDescriptor(value, "then", budget);
|
|
712
|
+
if (descriptor !== undefined && !("value" in descriptor)) {
|
|
713
|
+
const getter = accessorClosure(descriptor.get);
|
|
714
|
+
if (getter === undefined)
|
|
715
|
+
return undefined;
|
|
716
|
+
const result = budget === undefined
|
|
717
|
+
? getter.call([], { stack: [], thisValue: value })
|
|
718
|
+
: callPromiseClosure(getter, [], value, budget);
|
|
719
|
+
return Promise.resolve(result).then((then) => (isSandboxClosure(then) ? then : undefined));
|
|
720
|
+
}
|
|
721
|
+
const then = descriptor?.value;
|
|
682
722
|
return isSandboxClosure(then) ? then : undefined;
|
|
683
723
|
}
|
|
684
724
|
function budgetIfNeeded(value, budget) {
|