poe-code 14.0.65 → 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.
Files changed (42) hide show
  1. package/dist/index.js +1723 -797
  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-4VT2M7WL.js → chunk-MKTUVVEB.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-WBWD7ZCM.js → chunk-YKISHTWT.js} +1675 -772
  7. package/packages/safe-js/dist/chunks/chunk-YKISHTWT.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/interp/accessors.d.ts +9 -0
  12. package/packages/safe-js/dist/interp/accessors.js +68 -0
  13. package/packages/safe-js/dist/interp/async.js +3 -3
  14. package/packages/safe-js/dist/interp/classes.js +10 -4
  15. package/packages/safe-js/dist/interp/exceptions.d.ts +1 -1
  16. package/packages/safe-js/dist/interp/exceptions.js +47 -8
  17. package/packages/safe-js/dist/interp/globals/collections.js +53 -16
  18. package/packages/safe-js/dist/interp/globals/console-json.js +41 -26
  19. package/packages/safe-js/dist/interp/globals/object-array.js +229 -68
  20. package/packages/safe-js/dist/interp/globals/object.js +6 -3
  21. package/packages/safe-js/dist/interp/globals/primitives.js +11 -5
  22. package/packages/safe-js/dist/interp/interpreter.d.ts +2 -2
  23. package/packages/safe-js/dist/interp/interpreter.js +83 -44
  24. package/packages/safe-js/dist/interp/iteration.js +13 -0
  25. package/packages/safe-js/dist/interp/methods/array.d.ts +1 -1
  26. package/packages/safe-js/dist/interp/methods/array.js +368 -109
  27. package/packages/safe-js/dist/interp/methods/function.js +56 -20
  28. package/packages/safe-js/dist/interp/methods/regex.js +1 -1
  29. package/packages/safe-js/dist/interp/object-model.d.ts +1 -0
  30. package/packages/safe-js/dist/interp/object-model.js +48 -10
  31. package/packages/safe-js/dist/interp/patterns.d.ts +2 -2
  32. package/packages/safe-js/dist/interp/patterns.js +35 -16
  33. package/packages/safe-js/dist/interp/promise.d.ts +1 -1
  34. package/packages/safe-js/dist/interp/promise.js +103 -63
  35. package/packages/safe-js/dist/interp/string-coercion.d.ts +1 -0
  36. package/packages/safe-js/dist/interp/string-coercion.js +47 -37
  37. package/packages/safe-js/dist/interp/values.d.ts +2 -1
  38. package/packages/safe-js/dist/interp/values.js +26 -1
  39. package/packages/safe-js/dist/parse/parser.d.ts +1 -1
  40. package/packages/safe-js/dist/parse/parser.js +37 -8
  41. package/packages/safe-js/dist/chunks/chunk-WBWD7ZCM.js.map +0 -7
  42. /package/packages/safe-js/dist/chunks/{chunk-4VT2M7WL.js.map → chunk-MKTUVVEB.js.map} +0 -0
@@ -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 bound = createSandboxClosure({
62
- guest: true,
63
- sandbox: true,
64
- name: `bound ${target.name ?? ""}`,
65
- length: target.length === undefined ? undefined : Math.max(0, target.length - boundArgs.length),
66
- boundTarget: target,
67
- retainedValues: () => [target, thisValue, ...boundArgs],
68
- call: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], thisValue),
69
- ...(target.construct === undefined
70
- ? {}
71
- : {
72
- construct: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], undefined, true, context?.newTarget === bound ? target : context?.newTarget)
73
- })
74
- });
75
- if (hasExplicitSandboxPrototype(target))
76
- setSandboxPrototype(bound, getSandboxPrototype(target, options.budget), options.budget);
77
- return bound;
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
- return options.callClosure(target, applyArgs, stack, thisValue);
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].map(target => ({
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
- })).map(record => ({
100
- ...record, descriptors: new Map(Object.entries(Object.getOwnPropertyDescriptors(record.value)))
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 && after !== undefined && Object.is(before.value, after.value) &&
103
- before.writable === after.writable && before.configurable === after.configurable && before.enumerable === after.enumerable;
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 && hasExplicitSandboxPrototype(target) === explicit &&
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 ? [] : [getSandboxPrototype(target)]),
112
- ...Object.entries(Object.getOwnPropertyDescriptors(value)).flatMap(([key, descriptor]) => unchanged(descriptors.get(key), descriptor) ? [] : [key, descriptor.value])
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;
@@ -14,8 +14,8 @@ export type PatternContext = {
14
14
  budget?: Budget;
15
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, ownEnumerableSandboxEntries } from "./values.js";
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":
@@ -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
- if (iterator !== undefined)
56
- nextCollectionIterator(iterator, context.budget);
68
+ await next();
57
69
  continue;
58
70
  }
59
71
  let elementValue;
60
- if (iterator === undefined) {
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 = nextCollectionIterator(iterator, context.budget); !entry.done; entry = nextCollectionIterator(iterator, context.budget)) {
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 = nextCollectionIterator(iterator, context.budget).value;
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
- for (const [key, entryValue] of ownEnumerableSandboxEntries(value, excludedKeys)) {
183
- defineProperty(rest, key, entryValue);
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 getThenable(value: SandboxValue): SandboxClosure | undefined;
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 then = readPromiseReceiverProperty(target, "then", prototype);
243
- if (!isSandboxClosure(then))
244
- throw new TypeError("Promise.catch requires a callable then.");
245
- return then.call([undefined, onRejected], {
246
- stack: context?.stack ?? [],
247
- thisValue: target
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
- validatePromiseConstructorProperty(target, prototype);
260
- const then = readPromiseReceiverProperty(target, "then", prototype);
261
- if (!isSandboxClosure(then))
262
- throw new TypeError("Promise.finally requires a callable then.");
263
- const handlers = isSandboxClosure(onFinally)
264
- ? ["fulfilled", "rejected"].map((state) => createSandboxClosure({
265
- sandbox: true,
266
- retainedValues: () => [onFinally],
267
- call: async ([value]) => {
268
- const result = await callPromiseClosure(onFinally, [], undefined, budget, context);
269
- const pending = isSandboxPromise(result) &&
270
- getPromiseMember("constructor", budget) ===
271
- intrinsicPromiseConstructors.get(budget)
272
- ? result
273
- : createSandboxPromise(resolveSandboxValue(result, { budget }));
274
- const cleanupThen = getPromiseMember("then", budget);
275
- if (!isSandboxClosure(cleanupThen))
276
- throw new TypeError("Promise cleanup requires a callable then.");
277
- return callPromiseClosure(cleanupThen, [
278
- createSandboxClosure({
279
- sandbox: true,
280
- retainedValues: () => [value],
281
- call: () => {
282
- if (state === "rejected")
283
- throw value;
284
- return value;
285
- }
286
- })
287
- ], pending, budget, context);
288
- }
289
- }))
290
- : [onFinally, onFinally];
291
- return then.call(handlers, { stack: context?.stack ?? [], thisValue: target });
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 constructor = readPromiseReceiverProperty(receiver, "constructor", prototype);
316
- if (constructor !== undefined && (typeof constructor !== "object" || constructor === null)) {
317
- throw new TypeError("Promise constructor property must be an object.");
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(isSandboxPromise(settlement.value) || getThenable(settlement.value) !== undefined
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 (isSandboxPromise(result) || getThenable(result) !== undefined) {
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 (isSandboxPromise(value) || getThenable(value) !== undefined) {
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 getThenable(value) {
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 then = isSandboxClosure(value)
680
- ? value.properties?.then
681
- : value.then;
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) {
@@ -2,3 +2,4 @@ import type { Budget } from "./budget.js";
2
2
  import { type SandboxCallContext, type SandboxValue } from "./values.js";
3
3
  export declare function sandboxNumber(value: SandboxValue, budget: Budget, context?: SandboxCallContext): number | Promise<number>;
4
4
  export declare function sandboxString(value: SandboxValue, budget: Budget, context?: SandboxCallContext, joining?: Set<object>): string | Promise<string>;
5
+ export declare function joinSandboxArray(value: SandboxValue & object, length: number, separator: string, budget: Budget, context?: SandboxCallContext, joining?: Set<object>): Promise<string>;