poe-code 5.0.3 → 6.0.0
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 +1859 -1313
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/dist/providers/poe-agent.js +14 -2
- package/dist/providers/poe-agent.js.map +4 -4
- package/package.json +1 -1
- package/packages/safejs/dist/interp/async.js +31 -9
- package/packages/safejs/dist/interp/cancel.d.ts +7 -2
- package/packages/safejs/dist/interp/cancel.js +128 -92
- package/packages/safejs/dist/interp/exceptions.js +7 -3
- package/packages/safejs/dist/interp/globals/collections.js +2 -0
- package/packages/safejs/dist/interp/globals/console-json.js +4 -0
- package/packages/safejs/dist/interp/globals/error.js +1 -5
- package/packages/safejs/dist/interp/globals/math.js +2 -4
- package/packages/safejs/dist/interp/globals/misc.js +5 -0
- package/packages/safejs/dist/interp/globals/object-array.js +25 -0
- package/packages/safejs/dist/interp/globals/regex.js +1 -1
- package/packages/safejs/dist/interp/host-bridge.js +8 -5
- package/packages/safejs/dist/interp/interpreter.js +26 -32
- package/packages/safejs/dist/interp/iteration.d.ts +3 -3
- package/packages/safejs/dist/interp/iteration.js +20 -9
- package/packages/safejs/dist/interp/methods/array.js +1 -0
- package/packages/safejs/dist/interp/methods/function.d.ts +1 -1
- package/packages/safejs/dist/interp/methods/function.js +17 -1
- package/packages/safejs/dist/interp/methods/generator.js +1 -0
- package/packages/safejs/dist/interp/methods/map.js +1 -0
- package/packages/safejs/dist/interp/methods/number.js +1 -0
- package/packages/safejs/dist/interp/methods/regex.js +1 -0
- package/packages/safejs/dist/interp/methods/set.js +1 -0
- package/packages/safejs/dist/interp/methods/string.js +1 -0
- package/packages/safejs/dist/interp/promise-tracker.d.ts +12 -2
- package/packages/safejs/dist/interp/promise-tracker.js +65 -7
- package/packages/safejs/dist/interp/promise.d.ts +7 -5
- package/packages/safejs/dist/interp/promise.js +531 -148
- package/packages/safejs/dist/interp/values.d.ts +6 -0
- package/packages/safejs/dist/interp/values.js +25 -5
- package/packages/safejs/dist/lint/rules/AS001.js +0 -5
- package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -1
- package/packages/safejs/dist/snapshot/dump-format.js +1 -1
- package/packages/safejs/dist/snapshot/replay-data.js +3 -0
- package/packages/toolcraft-design/dist/explorer/detail-content.d.ts +6 -0
- package/packages/toolcraft-design/dist/explorer/detail-content.js +32 -0
- package/packages/toolcraft-design/dist/explorer/layout.d.ts +1 -0
- package/packages/toolcraft-design/dist/explorer/layout.js +8 -0
- package/packages/toolcraft-design/dist/explorer/reducer.js +8 -19
- package/packages/toolcraft-design/dist/explorer/render/detail.d.ts +1 -1
- package/packages/toolcraft-design/dist/explorer/render/detail.js +8 -36
- package/packages/toolcraft-design/dist/explorer/render/pane.d.ts +1 -1
- package/packages/toolcraft-design/dist/explorer/render/pane.js +1 -8
|
@@ -1,139 +1,423 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SandboxError } from "./budget.js";
|
|
2
|
+
import { coerceThrownValue, createSubsetErrorValue } from "./exceptions.js";
|
|
3
|
+
import { getSandboxIterator } from "./iteration.js";
|
|
2
4
|
import { runPromiseJob } from "./jobs.js";
|
|
3
5
|
import { observeSandboxPromise } from "./promise-tracker.js";
|
|
4
6
|
import { createSandboxClosure, createSandboxPromise, isSandboxClosure, isSandboxPromise } from "./values.js";
|
|
7
|
+
const promiseConstructors = new WeakSet();
|
|
8
|
+
const intrinsicPromiseConstructors = new WeakMap();
|
|
9
|
+
const promisePrototypes = new WeakMap();
|
|
10
|
+
export function isSandboxPromiseConstructor(value) {
|
|
11
|
+
return isSandboxClosure(value) && promiseConstructors.has(value);
|
|
12
|
+
}
|
|
5
13
|
export function createPromiseGlobals(options) {
|
|
6
|
-
|
|
7
|
-
|
|
14
|
+
promisePrototypes.delete(options.budget);
|
|
15
|
+
const prototype = getPromisePrototype(options.budget);
|
|
16
|
+
const construct = async ([executor], context) => {
|
|
17
|
+
if (!isSandboxClosure(executor))
|
|
18
|
+
throw new TypeError("Promise executor must be a function.");
|
|
19
|
+
let fulfill;
|
|
20
|
+
let reject;
|
|
21
|
+
const pending = createSandboxPromise(new Promise((resolve, rejectPromise) => {
|
|
22
|
+
fulfill = resolve;
|
|
23
|
+
reject = rejectPromise;
|
|
24
|
+
}), { span: context?.span });
|
|
25
|
+
let settled = false;
|
|
26
|
+
const settle = (state, value) => {
|
|
27
|
+
if (settled)
|
|
28
|
+
return;
|
|
29
|
+
settled = true;
|
|
30
|
+
try {
|
|
31
|
+
if (state === "rejected") {
|
|
32
|
+
reject(budgetSandboxValue(value, options.budget));
|
|
33
|
+
}
|
|
34
|
+
else if (value === pending) {
|
|
35
|
+
reject(createSubsetErrorValue("TypeError", "Promise cannot resolve to itself.", context?.stack ?? [], options.budget));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
fulfill(resolveSandboxValue(value, { budget: options.budget, self: pending }));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
reject(error);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
try {
|
|
46
|
+
const result = executor.call([
|
|
47
|
+
createSandboxClosure({
|
|
48
|
+
sandbox: true,
|
|
49
|
+
name: "resolve",
|
|
50
|
+
retainedValues: () => [pending],
|
|
51
|
+
call: ([value]) => {
|
|
52
|
+
settle("fulfilled", value);
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
createSandboxClosure({
|
|
57
|
+
sandbox: true,
|
|
58
|
+
name: "reject",
|
|
59
|
+
retainedValues: () => [pending],
|
|
60
|
+
call: ([reason]) => {
|
|
61
|
+
settle("rejected", reason);
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
], { stack: context?.stack ?? [], thisValue: undefined });
|
|
66
|
+
if (executor.async !== true)
|
|
67
|
+
await result;
|
|
68
|
+
else if (isSandboxPromise(result) && result.synchronousPrefix !== undefined)
|
|
69
|
+
await result.synchronousPrefix;
|
|
70
|
+
else if (isPromiseLike(result))
|
|
71
|
+
createSandboxPromise(Promise.resolve(result));
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error instanceof SandboxError &&
|
|
75
|
+
(error.code === "budgetExceeded" || error.code === "reentry")) {
|
|
76
|
+
observeSandboxPromise(pending);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
settle("rejected", error);
|
|
80
|
+
}
|
|
81
|
+
return pending;
|
|
82
|
+
};
|
|
83
|
+
const promiseConstructor = createSandboxClosure({
|
|
84
|
+
sandbox: true,
|
|
85
|
+
name: "Promise",
|
|
86
|
+
construct,
|
|
87
|
+
call: () => {
|
|
88
|
+
throw new TypeError("Constructor Promise requires 'new'.");
|
|
89
|
+
},
|
|
90
|
+
properties: {
|
|
91
|
+
prototype,
|
|
8
92
|
all: createSandboxClosure({
|
|
9
|
-
|
|
10
|
-
call: ([values]) =>
|
|
93
|
+
sandbox: true,
|
|
94
|
+
call: ([values], context) => settleIterable(values, "all", options.budget, context === undefined ? promiseConstructor : context.thisValue, context),
|
|
11
95
|
name: "all"
|
|
12
96
|
}),
|
|
13
97
|
race: createSandboxClosure({
|
|
14
|
-
|
|
15
|
-
call: ([values]) =>
|
|
98
|
+
sandbox: true,
|
|
99
|
+
call: ([values], context) => settleIterable(values, "race", options.budget, context === undefined ? promiseConstructor : context.thisValue, context),
|
|
16
100
|
name: "race"
|
|
17
101
|
}),
|
|
18
102
|
allSettled: createSandboxClosure({
|
|
19
|
-
|
|
20
|
-
call: ([values]) =>
|
|
21
|
-
? {
|
|
22
|
-
status: "fulfilled",
|
|
23
|
-
value: result.value
|
|
24
|
-
}
|
|
25
|
-
: {
|
|
26
|
-
reason: result.reason,
|
|
27
|
-
status: "rejected"
|
|
28
|
-
}), options.budget)), options.budget)),
|
|
103
|
+
sandbox: true,
|
|
104
|
+
call: ([values], context) => settleIterable(values, "allSettled", options.budget, context === undefined ? promiseConstructor : context.thisValue, context),
|
|
29
105
|
name: "allSettled"
|
|
30
106
|
}),
|
|
31
107
|
any: createSandboxClosure({
|
|
32
|
-
|
|
33
|
-
call: ([values]) =>
|
|
34
|
-
const aggregate = createSubsetErrorValue("AggregateError", "All promises were rejected", [], options.budget);
|
|
35
|
-
aggregate.errors = Array.isArray(error.errors)
|
|
36
|
-
? error.errors
|
|
37
|
-
: [];
|
|
38
|
-
throw budgetSandboxValue(aggregate, options.budget);
|
|
39
|
-
}), options.budget)),
|
|
108
|
+
sandbox: true,
|
|
109
|
+
call: ([values], context) => settleIterable(values, "any", options.budget, context === undefined ? promiseConstructor : context.thisValue, context),
|
|
40
110
|
name: "any"
|
|
41
111
|
}),
|
|
42
112
|
resolve: createSandboxClosure({
|
|
43
|
-
|
|
44
|
-
call: ([value]) =>
|
|
45
|
-
?
|
|
46
|
-
|
|
113
|
+
sandbox: true,
|
|
114
|
+
call: ([value], context) => {
|
|
115
|
+
const constructor = context === undefined ? promiseConstructor : context.thisValue;
|
|
116
|
+
if (typeof constructor !== "object" || constructor === null) {
|
|
117
|
+
throw new TypeError("Promise.resolve requires an object receiver.");
|
|
118
|
+
}
|
|
119
|
+
if (isSandboxPromise(value) &&
|
|
120
|
+
getPromiseMember("constructor", options.budget) === constructor) {
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
return isSandboxPromiseConstructor(constructor)
|
|
124
|
+
? createSandboxPromise(resolveSandboxValue(value, { budget: options.budget }))
|
|
125
|
+
: settleConstructedPromise(constructor, value, "fulfilled", options.budget, context);
|
|
126
|
+
},
|
|
47
127
|
name: "resolve"
|
|
48
128
|
}),
|
|
49
129
|
reject: createSandboxClosure({
|
|
50
|
-
|
|
51
|
-
call: ([reason], context) =>
|
|
130
|
+
sandbox: true,
|
|
131
|
+
call: ([reason], context) => {
|
|
132
|
+
const constructor = context === undefined ? promiseConstructor : context.thisValue;
|
|
133
|
+
return isSandboxPromiseConstructor(constructor)
|
|
134
|
+
? createRejectedSandboxPromise(reason, options.budget, context?.span)
|
|
135
|
+
: settleConstructedPromise(constructor, reason, "rejected", options.budget, context);
|
|
136
|
+
},
|
|
52
137
|
name: "reject"
|
|
53
138
|
})
|
|
54
139
|
}
|
|
55
|
-
};
|
|
140
|
+
});
|
|
141
|
+
Object.defineProperty(prototype, "constructor", {
|
|
142
|
+
value: promiseConstructor,
|
|
143
|
+
writable: true,
|
|
144
|
+
configurable: true
|
|
145
|
+
});
|
|
146
|
+
promiseConstructors.add(promiseConstructor);
|
|
147
|
+
intrinsicPromiseConstructors.set(options.budget, promiseConstructor);
|
|
148
|
+
return { Promise: promiseConstructor };
|
|
56
149
|
}
|
|
57
|
-
export function getPromiseMember(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
150
|
+
export function getPromiseMember(property, budget) {
|
|
151
|
+
const prototype = getPromisePrototype(budget);
|
|
152
|
+
return Object.hasOwn(prototype, property) ? prototype[property] : undefined;
|
|
153
|
+
}
|
|
154
|
+
async function settleConstructedPromise(constructor, value, state, budget, context) {
|
|
155
|
+
const capability = await createPromiseCapability(constructor, budget, context);
|
|
156
|
+
await callPromiseClosure(state === "fulfilled" ? capability.resolve : capability.reject, [value], undefined, budget, context);
|
|
157
|
+
return capability.promise;
|
|
158
|
+
}
|
|
159
|
+
async function createPromiseCapability(constructor, budget, context) {
|
|
160
|
+
if (!isSandboxClosure(constructor) || constructor.construct === undefined) {
|
|
161
|
+
throw new TypeError("Promise method requires a constructor receiver.");
|
|
162
|
+
}
|
|
163
|
+
let resolve;
|
|
164
|
+
let reject;
|
|
165
|
+
const executor = createSandboxClosure({
|
|
166
|
+
sandbox: true,
|
|
167
|
+
retainedValues: () => [resolve, reject],
|
|
168
|
+
call: (args) => {
|
|
169
|
+
if (resolve !== undefined || reject !== undefined) {
|
|
170
|
+
throw new TypeError("Promise capability is already initialized.");
|
|
171
|
+
}
|
|
172
|
+
[resolve, reject] = args;
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
const leaveCall = budget.enterCall();
|
|
177
|
+
try {
|
|
178
|
+
const promise = await constructor.construct([executor], {
|
|
179
|
+
stack: context?.stack ?? [],
|
|
180
|
+
thisValue: undefined,
|
|
181
|
+
...(context?.span === undefined ? {} : { span: context.span })
|
|
67
182
|
});
|
|
183
|
+
if (!isSandboxClosure(resolve) || !isSandboxClosure(reject)) {
|
|
184
|
+
throw new TypeError("Promise capability requires callable resolve and reject functions.");
|
|
185
|
+
}
|
|
186
|
+
return { promise, resolve, reject };
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
leaveCall();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async function callPromiseClosure(callback, args, thisValue, budget, context) {
|
|
193
|
+
const leaveCall = budget.enterCall();
|
|
194
|
+
try {
|
|
195
|
+
const stack = context?.stack ?? [];
|
|
196
|
+
const values = args.map((value) => value instanceof Error && !(value instanceof SandboxError)
|
|
197
|
+
? coerceThrownValue(value, budget, stack)
|
|
198
|
+
: value);
|
|
199
|
+
let result = callback.call(values, { stack, thisValue });
|
|
200
|
+
if (callback.async !== true)
|
|
201
|
+
result = await result;
|
|
202
|
+
else if (isPromiseLike(result))
|
|
203
|
+
result = createSandboxPromise(Promise.resolve(result));
|
|
204
|
+
if (isSandboxPromise(result) && result.synchronousPrefix !== undefined) {
|
|
205
|
+
await result.synchronousPrefix;
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
leaveCall();
|
|
68
211
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
212
|
+
}
|
|
213
|
+
function getPromisePrototype(budget) {
|
|
214
|
+
const existing = promisePrototypes.get(budget);
|
|
215
|
+
if (existing !== undefined)
|
|
216
|
+
return existing;
|
|
217
|
+
const prototype = {
|
|
218
|
+
then: createSandboxClosure({
|
|
219
|
+
sandbox: true,
|
|
220
|
+
call: ([onFulfilled, onRejected], context) => {
|
|
221
|
+
const target = context?.thisValue;
|
|
222
|
+
if (!isSandboxPromise(target))
|
|
223
|
+
throw new TypeError("Promise.then requires a promise receiver.");
|
|
224
|
+
validatePromiseConstructorProperty(target, prototype);
|
|
73
225
|
observeSandboxPromise(target);
|
|
74
|
-
const chained = createSandboxPromise(target.promise.then((value) =>
|
|
226
|
+
const chained = createSandboxPromise(target.promise.then((value) => {
|
|
227
|
+
consumeSettledHostCall(target);
|
|
228
|
+
return runPromiseReaction(onFulfilled, value, "fulfilled", budget, chained);
|
|
229
|
+
}, (reason) => {
|
|
230
|
+
consumeSettledHostCall(target);
|
|
231
|
+
return runPromiseReaction(onRejected, reason, "rejected", budget, chained);
|
|
232
|
+
}));
|
|
75
233
|
return chained;
|
|
76
234
|
},
|
|
235
|
+
name: "then"
|
|
236
|
+
}),
|
|
237
|
+
catch: createSandboxClosure({
|
|
238
|
+
sandbox: true,
|
|
239
|
+
call: ([onRejected], context) => {
|
|
240
|
+
const target = context?.thisValue;
|
|
241
|
+
const then = readPromiseReceiverProperty(target, "then", prototype);
|
|
242
|
+
if (!isSandboxClosure(then))
|
|
243
|
+
throw new TypeError("Promise.catch requires a callable then.");
|
|
244
|
+
return then.call([undefined, onRejected], {
|
|
245
|
+
stack: context?.stack ?? [],
|
|
246
|
+
thisValue: target
|
|
247
|
+
});
|
|
248
|
+
},
|
|
77
249
|
name: "catch"
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
250
|
+
}),
|
|
251
|
+
finally: createSandboxClosure({
|
|
252
|
+
sandbox: true,
|
|
253
|
+
call: ([onFinally], context) => {
|
|
254
|
+
const target = context?.thisValue;
|
|
255
|
+
if (typeof target !== "object" || target === null) {
|
|
256
|
+
throw new TypeError("Promise.finally requires an object receiver.");
|
|
257
|
+
}
|
|
258
|
+
validatePromiseConstructorProperty(target, prototype);
|
|
259
|
+
const then = readPromiseReceiverProperty(target, "then", prototype);
|
|
260
|
+
if (!isSandboxClosure(then))
|
|
261
|
+
throw new TypeError("Promise.finally requires a callable then.");
|
|
262
|
+
const handlers = isSandboxClosure(onFinally)
|
|
263
|
+
? ["fulfilled", "rejected"].map((state) => createSandboxClosure({
|
|
264
|
+
sandbox: true,
|
|
265
|
+
retainedValues: () => [onFinally],
|
|
266
|
+
call: async ([value]) => {
|
|
267
|
+
const result = await callPromiseClosure(onFinally, [], undefined, budget, context);
|
|
268
|
+
const pending = isSandboxPromise(result) &&
|
|
269
|
+
getPromiseMember("constructor", budget) ===
|
|
270
|
+
intrinsicPromiseConstructors.get(budget)
|
|
271
|
+
? result
|
|
272
|
+
: createSandboxPromise(resolveSandboxValue(result, { budget }));
|
|
273
|
+
const cleanupThen = getPromiseMember("then", budget);
|
|
274
|
+
if (!isSandboxClosure(cleanupThen))
|
|
275
|
+
throw new TypeError("Promise cleanup requires a callable then.");
|
|
276
|
+
return callPromiseClosure(cleanupThen, [
|
|
277
|
+
createSandboxClosure({
|
|
278
|
+
sandbox: true,
|
|
279
|
+
retainedValues: () => [value],
|
|
280
|
+
call: () => {
|
|
281
|
+
if (state === "rejected")
|
|
282
|
+
throw value;
|
|
283
|
+
return value;
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
], pending, budget, context);
|
|
287
|
+
}
|
|
288
|
+
}))
|
|
289
|
+
: [onFinally, onFinally];
|
|
290
|
+
return then.call(handlers, { stack: context?.stack ?? [], thisValue: target });
|
|
87
291
|
},
|
|
88
292
|
name: "finally"
|
|
89
|
-
})
|
|
293
|
+
})
|
|
294
|
+
};
|
|
295
|
+
for (const name of Object.keys(prototype)) {
|
|
296
|
+
Object.defineProperty(prototype, name, { enumerable: false });
|
|
90
297
|
}
|
|
91
|
-
|
|
298
|
+
promisePrototypes.set(budget, prototype);
|
|
299
|
+
return prototype;
|
|
92
300
|
}
|
|
93
|
-
function
|
|
94
|
-
|
|
301
|
+
function readPromiseReceiverProperty(receiver, property, prototype) {
|
|
302
|
+
const properties = isSandboxPromise(receiver)
|
|
303
|
+
? prototype
|
|
304
|
+
: isSandboxClosure(receiver)
|
|
305
|
+
? receiver.properties
|
|
306
|
+
: receiver;
|
|
307
|
+
return typeof properties === "object" &&
|
|
308
|
+
properties !== null &&
|
|
309
|
+
Object.hasOwn(properties, property)
|
|
310
|
+
? properties[property]
|
|
311
|
+
: undefined;
|
|
95
312
|
}
|
|
96
|
-
function
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
throw new TypeError("Promise
|
|
100
|
-
}
|
|
101
|
-
const entries = [];
|
|
102
|
-
while (true) {
|
|
103
|
-
const next = readIteratorNext(iterator);
|
|
104
|
-
if (next.done === true) {
|
|
105
|
-
return entries;
|
|
106
|
-
}
|
|
107
|
-
entries.push(resolveSandboxValue(next.value, { budget }));
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function getPromiseIterator(iterable) {
|
|
111
|
-
if (typeof iterable === "string") {
|
|
112
|
-
return iterable[Symbol.iterator]();
|
|
113
|
-
}
|
|
114
|
-
if ((typeof iterable !== "object" && typeof iterable !== "function") || iterable === null) {
|
|
115
|
-
return undefined;
|
|
116
|
-
}
|
|
117
|
-
const iteratorMethod = iterable[Symbol.iterator];
|
|
118
|
-
if (typeof iteratorMethod !== "function") {
|
|
119
|
-
return undefined;
|
|
120
|
-
}
|
|
121
|
-
const iterator = Reflect.apply(iteratorMethod, iterable, []);
|
|
122
|
-
if ((typeof iterator !== "object" && typeof iterator !== "function") || iterator === null) {
|
|
123
|
-
throw new TypeError("Promise helper iterable returned a non-object iterator.");
|
|
313
|
+
function validatePromiseConstructorProperty(receiver, prototype) {
|
|
314
|
+
const constructor = readPromiseReceiverProperty(receiver, "constructor", prototype);
|
|
315
|
+
if (constructor !== undefined && (typeof constructor !== "object" || constructor === null)) {
|
|
316
|
+
throw new TypeError("Promise constructor property must be an object.");
|
|
124
317
|
}
|
|
125
|
-
return iterator;
|
|
126
318
|
}
|
|
127
|
-
function
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
319
|
+
async function settleIterable(iterable, method, budget, constructor, context) {
|
|
320
|
+
const capability = await createPromiseCapability(constructor, budget, context);
|
|
321
|
+
const prototype = getPromisePrototype(budget);
|
|
322
|
+
const values = [];
|
|
323
|
+
let remaining = 1;
|
|
324
|
+
const complete = async () => {
|
|
325
|
+
if (remaining !== 0 || method === "race")
|
|
326
|
+
return;
|
|
327
|
+
if (method === "any") {
|
|
328
|
+
const error = createSubsetErrorValue("AggregateError", "All promises were rejected", [], budget);
|
|
329
|
+
error.errors = values;
|
|
330
|
+
await callPromiseClosure(capability.reject, [budgetSandboxValue(error, budget)], undefined, budget, context);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
await callPromiseClosure(capability.resolve, [budgetSandboxValue(values, budget)], undefined, budget, context);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
try {
|
|
337
|
+
const promiseResolve = readPromiseReceiverProperty(constructor, "resolve", prototype);
|
|
338
|
+
if (!isSandboxClosure(promiseResolve))
|
|
339
|
+
throw new TypeError("Promise constructor requires a callable resolve.");
|
|
340
|
+
const iterator = getSandboxIterator(iterable);
|
|
341
|
+
if (iterator === undefined)
|
|
342
|
+
throw new TypeError("Promise helpers require an iterable.");
|
|
343
|
+
let index = 0;
|
|
344
|
+
while (true) {
|
|
345
|
+
budget.visitNode();
|
|
346
|
+
const next = iterator.generator
|
|
347
|
+
? await iterator.next()
|
|
348
|
+
: iterator.next();
|
|
349
|
+
if (typeof next !== "object" || next === null)
|
|
350
|
+
throw new TypeError("Iterator result must be an object.");
|
|
351
|
+
if (next.done)
|
|
352
|
+
break;
|
|
353
|
+
const value = next.value;
|
|
354
|
+
try {
|
|
355
|
+
budget.allocateArrayLength(index + 1);
|
|
356
|
+
const entryIndex = index++;
|
|
357
|
+
if (method !== "race")
|
|
358
|
+
values.push(undefined);
|
|
359
|
+
const entry = await callPromiseClosure(promiseResolve, [value], constructor, budget, context);
|
|
360
|
+
let called = false;
|
|
361
|
+
const handlers = ["fulfilled", "rejected"].map((state) => {
|
|
362
|
+
if (method === "race" || (method === "any" && state === "fulfilled"))
|
|
363
|
+
return state === "fulfilled" ? capability.resolve : capability.reject;
|
|
364
|
+
if (method === "all" && state === "rejected")
|
|
365
|
+
return capability.reject;
|
|
366
|
+
return createSandboxClosure({
|
|
367
|
+
sandbox: true,
|
|
368
|
+
retainedValues: () => [
|
|
369
|
+
capability.promise,
|
|
370
|
+
capability.resolve,
|
|
371
|
+
capability.reject,
|
|
372
|
+
values
|
|
373
|
+
],
|
|
374
|
+
call: async ([settlement]) => {
|
|
375
|
+
if (called)
|
|
376
|
+
return undefined;
|
|
377
|
+
called = true;
|
|
378
|
+
values[entryIndex] =
|
|
379
|
+
method === "allSettled"
|
|
380
|
+
? state === "fulfilled"
|
|
381
|
+
? { status: state, value: settlement }
|
|
382
|
+
: { status: state, reason: settlement }
|
|
383
|
+
: settlement;
|
|
384
|
+
remaining--;
|
|
385
|
+
await complete();
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
remaining++;
|
|
391
|
+
const then = readPromiseReceiverProperty(entry, "then", prototype);
|
|
392
|
+
if (!isSandboxClosure(then))
|
|
393
|
+
throw new TypeError("Promise resolver result requires a callable then.");
|
|
394
|
+
await callPromiseClosure(then, handlers, entry, budget, context);
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
try {
|
|
398
|
+
const closed = iterator.return?.();
|
|
399
|
+
if (iterator.generator)
|
|
400
|
+
await closed;
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
remaining--;
|
|
409
|
+
await complete();
|
|
131
410
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
411
|
+
catch (error) {
|
|
412
|
+
if (error instanceof SandboxError &&
|
|
413
|
+
(error.code === "budgetExceeded" || error.code === "reentry")) {
|
|
414
|
+
if (isSandboxPromise(capability.promise))
|
|
415
|
+
observeSandboxPromise(capability.promise);
|
|
416
|
+
throw error;
|
|
417
|
+
}
|
|
418
|
+
await callPromiseClosure(capability.reject, [error], undefined, budget, context);
|
|
135
419
|
}
|
|
136
|
-
return
|
|
420
|
+
return capability.promise;
|
|
137
421
|
}
|
|
138
422
|
function schedulePromise(promise, budget) {
|
|
139
423
|
return Promise.resolve().then(() => promise.then((value) => resolveSandboxValue(value, { budget }), (reason) => Promise.reject(budgetSandboxValue(reason, budget))));
|
|
@@ -149,7 +433,12 @@ function budgetSandboxValue(value, budget) {
|
|
|
149
433
|
return value;
|
|
150
434
|
}
|
|
151
435
|
export function resolveSandboxValue(value, options = {}) {
|
|
152
|
-
|
|
436
|
+
try {
|
|
437
|
+
return resolveSandboxValueNow(value, options);
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
return Promise.reject(error);
|
|
441
|
+
}
|
|
153
442
|
}
|
|
154
443
|
// A host call result is consumed once, when it settles. Observing the same promise
|
|
155
444
|
// again is ordinary JavaScript and must not be reported as a double consumption.
|
|
@@ -159,15 +448,18 @@ export function consumeSettledHostCall(value) {
|
|
|
159
448
|
}
|
|
160
449
|
value.hostCallJournal?.consume(value.hostCall);
|
|
161
450
|
}
|
|
162
|
-
function resolveSandboxValueNow(value, options
|
|
451
|
+
function resolveSandboxValueNow(value, options) {
|
|
163
452
|
if (isPromiseLike(value)) {
|
|
164
|
-
return Promise.resolve(value).then((resolved) => resolveSandboxValueNow(resolved, options
|
|
453
|
+
return Promise.resolve(value).then((resolved) => resolveSandboxValueNow(resolved, options), (reason) => Promise.reject(budgetIfNeeded(reason, options.budget)));
|
|
165
454
|
}
|
|
166
455
|
if (isSandboxPromise(value)) {
|
|
456
|
+
if (options.budget !== undefined) {
|
|
457
|
+
return resolvePromiseResult(value, options.budget, options.self);
|
|
458
|
+
}
|
|
167
459
|
observeSandboxPromise(value);
|
|
168
460
|
return value.promise.then((resolved) => {
|
|
169
461
|
consumeSettledHostCall(value);
|
|
170
|
-
return resolveSandboxValueNow(resolved, options
|
|
462
|
+
return resolveSandboxValueNow(resolved, options);
|
|
171
463
|
}, (reason) => {
|
|
172
464
|
consumeSettledHostCall(value);
|
|
173
465
|
return Promise.reject(budgetIfNeeded(reason, options.budget));
|
|
@@ -175,21 +467,14 @@ function resolveSandboxValueNow(value, options, seenThenables) {
|
|
|
175
467
|
}
|
|
176
468
|
const then = getThenable(value);
|
|
177
469
|
if (then !== undefined) {
|
|
178
|
-
return resolveThenable(value, then, options
|
|
470
|
+
return resolveThenable(value, then, options);
|
|
179
471
|
}
|
|
180
472
|
return Promise.resolve(budgetIfNeeded(value, options.budget));
|
|
181
473
|
}
|
|
182
|
-
function resolveThenable(value, then, options
|
|
474
|
+
function resolveThenable(value, then, options) {
|
|
183
475
|
if (typeof value !== "object" || value === null) {
|
|
184
476
|
return Promise.resolve(budgetIfNeeded(value, options.budget));
|
|
185
477
|
}
|
|
186
|
-
if (seenThenables.has(value)) {
|
|
187
|
-
return Promise.reject({
|
|
188
|
-
message: "Promise cannot resolve to itself.",
|
|
189
|
-
name: "TypeError"
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
seenThenables.add(value);
|
|
193
478
|
return new Promise((resolve, reject) => {
|
|
194
479
|
let settlement;
|
|
195
480
|
let completed = false;
|
|
@@ -200,7 +485,9 @@ function resolveThenable(value, then, options, seenThenables) {
|
|
|
200
485
|
completed = true;
|
|
201
486
|
try {
|
|
202
487
|
if (settlement.state === "fulfilled") {
|
|
203
|
-
resolve(
|
|
488
|
+
resolve(isSandboxPromise(settlement.value) || getThenable(settlement.value) !== undefined
|
|
489
|
+
? resolveSandboxValueNow(settlement.value, options)
|
|
490
|
+
: budgetIfNeeded(settlement.value, options.budget));
|
|
204
491
|
}
|
|
205
492
|
else {
|
|
206
493
|
reject(budgetIfNeeded(settlement.value, options.budget));
|
|
@@ -219,6 +506,7 @@ function resolveThenable(value, then, options, seenThenables) {
|
|
|
219
506
|
};
|
|
220
507
|
callInPromiseJob(then, [
|
|
221
508
|
createSandboxClosure({
|
|
509
|
+
sandbox: true,
|
|
222
510
|
call: ([resolved]) => {
|
|
223
511
|
recordSettlement("fulfilled", resolved);
|
|
224
512
|
return undefined;
|
|
@@ -226,56 +514,151 @@ function resolveThenable(value, then, options, seenThenables) {
|
|
|
226
514
|
name: "resolve"
|
|
227
515
|
}),
|
|
228
516
|
createSandboxClosure({
|
|
517
|
+
sandbox: true,
|
|
229
518
|
call: ([reason]) => {
|
|
230
519
|
recordSettlement("rejected", reason);
|
|
231
520
|
return undefined;
|
|
232
521
|
},
|
|
233
522
|
name: "reject"
|
|
234
523
|
})
|
|
235
|
-
], value
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
524
|
+
], value, {
|
|
525
|
+
fulfilled: () => {
|
|
526
|
+
invocationPending = false;
|
|
527
|
+
complete();
|
|
528
|
+
},
|
|
529
|
+
rejected: (error) => {
|
|
530
|
+
invocationPending = false;
|
|
531
|
+
if (error instanceof SandboxError &&
|
|
532
|
+
(error.code === "budgetExceeded" || error.code === "reentry")) {
|
|
533
|
+
completed = true;
|
|
534
|
+
reject(error);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
recordSettlement("rejected", error);
|
|
538
|
+
complete();
|
|
539
|
+
}
|
|
540
|
+
}).catch(reject);
|
|
541
|
+
});
|
|
244
542
|
}
|
|
245
543
|
function runPromiseReaction(handler, value, state, budget, self) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
544
|
+
return new Promise((resolve, reject) => {
|
|
545
|
+
if (state === "rejected" &&
|
|
546
|
+
value instanceof SandboxError &&
|
|
547
|
+
(value.code === "budgetExceeded" || value.code === "reentry")) {
|
|
548
|
+
reject(value);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const argument = state === "rejected" && value instanceof Error ? coerceThrownValue(value, budget, []) : value;
|
|
552
|
+
const fulfilled = (result) => {
|
|
553
|
+
if (isPromiseLike(result)) {
|
|
554
|
+
resolve(resolvePromiseResult(result, budget, self));
|
|
555
|
+
}
|
|
556
|
+
else if (isSelfResolution(result, self)) {
|
|
557
|
+
reject(createSubsetErrorValue("TypeError", "Promise cannot resolve to itself.", [], budget));
|
|
558
|
+
}
|
|
559
|
+
else if (isSandboxPromise(result) || getThenable(result) !== undefined) {
|
|
560
|
+
resolve(resolvePromiseResult(result, budget, self));
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
resolve(budgetSandboxValue(result, budget));
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
if (isSandboxClosure(handler)) {
|
|
567
|
+
callInPromiseJob(handler, [argument], undefined, { fulfilled, rejected: reject }).catch(reject);
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
runPromiseJob(() => {
|
|
571
|
+
if (state === "fulfilled")
|
|
572
|
+
fulfilled(value);
|
|
573
|
+
else
|
|
574
|
+
reject(budgetSandboxValue(value, budget));
|
|
575
|
+
}).catch(reject);
|
|
576
|
+
}
|
|
577
|
+
});
|
|
258
578
|
}
|
|
259
|
-
function callInPromiseJob(handler, args, thisValue = undefined) {
|
|
579
|
+
function callInPromiseJob(handler, args, thisValue = undefined, completion) {
|
|
260
580
|
return runPromiseJob(async () => {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
581
|
+
try {
|
|
582
|
+
let result = handler.call(args, { stack: [], thisValue });
|
|
583
|
+
if (handler.async !== true)
|
|
584
|
+
result = await result;
|
|
585
|
+
if (isSandboxPromise(result) && result.synchronousPrefix !== undefined) {
|
|
586
|
+
await result.synchronousPrefix;
|
|
587
|
+
}
|
|
588
|
+
completion?.fulfilled(result);
|
|
589
|
+
return { value: result };
|
|
590
|
+
}
|
|
591
|
+
catch (error) {
|
|
592
|
+
if (completion === undefined)
|
|
593
|
+
throw error;
|
|
594
|
+
completion.rejected(error);
|
|
595
|
+
return { value: undefined };
|
|
266
596
|
}
|
|
267
|
-
return { value: result };
|
|
268
597
|
});
|
|
269
598
|
}
|
|
270
|
-
function
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return resolveSandboxValue(
|
|
599
|
+
function resolvePromiseResult(result, budget, self) {
|
|
600
|
+
if (isPromiseLike(result)) {
|
|
601
|
+
return Promise.resolve(result).then((resolved) => resolvePromiseResult(resolved, budget, self));
|
|
602
|
+
}
|
|
603
|
+
if (isSelfResolution(result, self)) {
|
|
604
|
+
return Promise.reject(createSubsetErrorValue("TypeError", "Promise cannot resolve to itself.", [], budget));
|
|
605
|
+
}
|
|
606
|
+
if (!isSandboxPromise(result))
|
|
607
|
+
return resolveSandboxValue(result, { budget, self });
|
|
608
|
+
const then = getPromiseMember("then", budget);
|
|
609
|
+
if (!isSandboxClosure(then))
|
|
610
|
+
return Promise.resolve(result);
|
|
611
|
+
return new Promise((resolve, reject) => {
|
|
612
|
+
let settled = false;
|
|
613
|
+
const settle = (state, value) => {
|
|
614
|
+
if (settled)
|
|
615
|
+
return;
|
|
616
|
+
settled = true;
|
|
617
|
+
try {
|
|
618
|
+
if (state === "rejected")
|
|
619
|
+
reject(budgetSandboxValue(value, budget));
|
|
620
|
+
else if (isSandboxPromise(value) || getThenable(value) !== undefined) {
|
|
621
|
+
resolve(resolvePromiseResult(value, budget, self));
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
resolve(budgetSandboxValue(value, budget));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
reject(error);
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
runPromiseJob(async () => {
|
|
632
|
+
try {
|
|
633
|
+
const completion = await callPromiseClosure(then, [
|
|
634
|
+
createSandboxClosure({
|
|
635
|
+
sandbox: true,
|
|
636
|
+
call: ([value]) => {
|
|
637
|
+
settle("fulfilled", value);
|
|
638
|
+
return undefined;
|
|
639
|
+
}
|
|
640
|
+
}),
|
|
641
|
+
createSandboxClosure({
|
|
642
|
+
sandbox: true,
|
|
643
|
+
call: ([reason]) => {
|
|
644
|
+
settle("rejected", reason);
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
})
|
|
648
|
+
], result, budget);
|
|
649
|
+
if (isSandboxPromise(completion)) {
|
|
650
|
+
completion.promise.catch((reason) => {
|
|
651
|
+
if (reason instanceof SandboxError &&
|
|
652
|
+
(reason.code === "budgetExceeded" || reason.code === "reentry")) {
|
|
653
|
+
reject(reason);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
settle("rejected", error);
|
|
660
|
+
}
|
|
661
|
+
}).catch(reject);
|
|
279
662
|
});
|
|
280
663
|
}
|
|
281
664
|
function isSelfResolution(result, self) {
|