poe-code 14.0.51 → 14.0.53
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 +451 -181
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safe-bash/dist/browser.js +35 -15
- package/packages/safe-bash/dist/browser.js.map +3 -3
- package/packages/safe-bash/dist/shell/arrays/state.d.ts +2 -1
- package/packages/safe-bash/dist/shell/arrays/state.d.ts.map +1 -1
- package/packages/safe-bash/dist/shell/arrays/state.js +23 -5
- package/packages/safe-bash/dist/shell/arrays/state.js.map +1 -1
- package/packages/safe-bash/dist/shell/cleanup.d.ts.map +1 -1
- package/packages/safe-bash/dist/shell/cleanup.js +11 -5
- package/packages/safe-bash/dist/shell/cleanup.js.map +1 -1
- package/packages/safe-bash/dist/shell/runtime.js +5 -5
- package/packages/safe-bash/dist/shell/runtime.js.map +1 -1
- package/packages/safe-js/dist/chunks/{chunk-MQU3AETK.js → chunk-4XYGVCOQ.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-7AIY5C3F.js → chunk-VDFW3EKS.js} +368 -138
- package/packages/safe-js/dist/chunks/chunk-VDFW3EKS.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/graph-depth.js +10 -4
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/cancel.js +3 -0
- package/packages/safe-js/dist/interp/collection-brands.d.ts +5 -0
- package/packages/safe-js/dist/interp/collection-brands.js +8 -0
- package/packages/safe-js/dist/interp/collection-iterator.d.ts +27 -0
- package/packages/safe-js/dist/interp/collection-iterator.js +61 -0
- package/packages/safe-js/dist/interp/globals/collections.js +1 -1
- package/packages/safe-js/dist/interp/globals/misc.js +3 -2
- package/packages/safe-js/dist/interp/globals/object-array.js +2 -2
- package/packages/safe-js/dist/interp/globals/object.js +3 -0
- package/packages/safe-js/dist/interp/interpreter.js +20 -11
- package/packages/safe-js/dist/interp/iteration.d.ts +2 -1
- package/packages/safe-js/dist/interp/iteration.js +4 -1
- package/packages/safe-js/dist/interp/methods/array.d.ts +12 -3
- package/packages/safe-js/dist/interp/methods/array.js +164 -46
- package/packages/safe-js/dist/interp/methods/collection-iterator.d.ts +4 -0
- package/packages/safe-js/dist/interp/methods/collection-iterator.js +19 -0
- package/packages/safe-js/dist/interp/methods/map.js +3 -4
- package/packages/safe-js/dist/interp/methods/number.d.ts +2 -2
- package/packages/safe-js/dist/interp/methods/number.js +27 -46
- package/packages/safe-js/dist/interp/methods/set.js +3 -3
- package/packages/safe-js/dist/interp/patterns.d.ts +2 -0
- package/packages/safe-js/dist/interp/patterns.js +20 -2
- package/packages/safe-js/dist/interp/promise.js +1 -1
- package/packages/safe-js/dist/interp/string-coercion.js +4 -0
- package/packages/safe-js/dist/interp/values.d.ts +4 -5
- package/packages/safe-js/dist/interp/values.js +42 -10
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +10 -0
- package/packages/safe-js/dist/snapshot/replay-data.js +32 -0
- package/packages/safe-js/dist/snapshot/replay-inputs.js +15 -0
- package/packages/safe-js/dist/snapshot/restore.js +16 -0
- package/packages/safe-js/dist/snapshot/serialize.d.ts +10 -1
- package/packages/safe-js/dist/snapshot/serialize.js +25 -9
- package/packages/safe-js/dist/snapshot/validation.js +14 -1
- package/packages/safe-js/dist/chunks/chunk-7AIY5C3F.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-MQU3AETK.js.map → chunk-4XYGVCOQ.js.map} +0 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet } from "../values.js";
|
|
2
2
|
import { assertCollectionMutable, enterRunningState } from "../running-state.js";
|
|
3
|
+
import { getSandboxDataProperty, getSandboxPrototype } from "../object-model.js";
|
|
4
|
+
import { sandboxNumber } from "../string-coercion.js";
|
|
5
|
+
const arrayLikeSources = new WeakMap();
|
|
3
6
|
const activeArrayCallbacks = new WeakMap();
|
|
4
7
|
const arrayMethodNames = new Set([
|
|
5
8
|
"map",
|
|
@@ -51,7 +54,7 @@ export function getArrayMember(value, property, options) {
|
|
|
51
54
|
return createSandboxClosure({
|
|
52
55
|
sandbox: true,
|
|
53
56
|
name: `Array#${property}`,
|
|
54
|
-
call: (args, context) => callArrayMethod(
|
|
57
|
+
call: (args, context) => callArrayMethod(context?.thisValue, property, args, { ...options, context }, context?.stack ?? [])
|
|
55
58
|
});
|
|
56
59
|
}
|
|
57
60
|
return undefined;
|
|
@@ -69,29 +72,94 @@ function getArrayIndex(property) {
|
|
|
69
72
|
export function isArrayMethodName(property) {
|
|
70
73
|
return typeof property === "string" && arrayMethodNames.has(property);
|
|
71
74
|
}
|
|
72
|
-
export async function callArrayMethod(
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
callbackState
|
|
88
|
-
if (callbackState
|
|
89
|
-
|
|
90
|
-
|
|
75
|
+
export async function callArrayMethod(receiver, methodName, args, options, stack = []) {
|
|
76
|
+
if (receiver === null || receiver === undefined)
|
|
77
|
+
throw new TypeError("Array method requires a receiver.");
|
|
78
|
+
if (typeof receiver !== "object")
|
|
79
|
+
throw new TypeError("Object primitive boxing is not supported.");
|
|
80
|
+
const retainedReceiver = {};
|
|
81
|
+
options.budget.setRetainedValues(retainedReceiver, () => [receiver]);
|
|
82
|
+
try {
|
|
83
|
+
const value = Array.isArray(receiver) || methodName === "concat"
|
|
84
|
+
? receiver
|
|
85
|
+
: await arrayLikeView(receiver, options);
|
|
86
|
+
if (isMutatingArrayMethod(methodName)) {
|
|
87
|
+
assertCollectionMutable(receiver);
|
|
88
|
+
}
|
|
89
|
+
if (isCallbackArrayMethod(methodName)) {
|
|
90
|
+
let callbackState = activeArrayCallbacks.get(receiver);
|
|
91
|
+
if (callbackState === undefined) {
|
|
92
|
+
callbackState = { depth: 0, leave: enterRunningState(receiver) };
|
|
93
|
+
activeArrayCallbacks.set(receiver, callbackState);
|
|
94
|
+
}
|
|
95
|
+
callbackState.depth += 1;
|
|
96
|
+
try {
|
|
97
|
+
const result = await callArrayMethodUnlocked(value, methodName, args, options, stack);
|
|
98
|
+
return result === value ? receiver : result;
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
callbackState.depth -= 1;
|
|
102
|
+
if (callbackState.depth === 0) {
|
|
103
|
+
activeArrayCallbacks.delete(receiver);
|
|
104
|
+
callbackState.leave();
|
|
105
|
+
}
|
|
91
106
|
}
|
|
92
107
|
}
|
|
108
|
+
const result = await callArrayMethodUnlocked(value, methodName, args, options, stack);
|
|
109
|
+
return result === value ? receiver : result;
|
|
93
110
|
}
|
|
94
|
-
|
|
111
|
+
finally {
|
|
112
|
+
options.budget.setRetainedValues(retainedReceiver, undefined);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function arrayLikeView(receiver, options) {
|
|
116
|
+
const rawLength = options.context?.getProperty !== undefined
|
|
117
|
+
? options.context.getProperty(receiver, "length")
|
|
118
|
+
: getSandboxDataProperty(receiver, "length", options.budget);
|
|
119
|
+
const number = await sandboxNumber(rawLength, options.budget, options.context);
|
|
120
|
+
const length = Number.isNaN(number) || number <= 0 ? 0 : Math.min(Math.trunc(number), Number.MAX_SAFE_INTEGER);
|
|
121
|
+
// The view is internal only: callbacks, results, and accounting keep the guest receiver.
|
|
122
|
+
const view = new Proxy(Object.create(null), {
|
|
123
|
+
get: (_target, key) => {
|
|
124
|
+
options.budget.visitNode();
|
|
125
|
+
if (key === "length")
|
|
126
|
+
return length;
|
|
127
|
+
if (typeof key !== "string")
|
|
128
|
+
return undefined;
|
|
129
|
+
return options.context?.getProperty !== undefined
|
|
130
|
+
? options.context.getProperty(receiver, key)
|
|
131
|
+
: getSandboxDataProperty(receiver, key, options.budget);
|
|
132
|
+
},
|
|
133
|
+
has: (_target, key) => {
|
|
134
|
+
options.budget.visitNode();
|
|
135
|
+
if (typeof key === "string" && options.hasProperty !== undefined)
|
|
136
|
+
return options.hasProperty(receiver, key);
|
|
137
|
+
for (let current = receiver; current !== null; current = getSandboxPrototype(current, options.budget)) {
|
|
138
|
+
options.budget.visitNode();
|
|
139
|
+
if (Object.hasOwn(current, key))
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
},
|
|
144
|
+
set: (_target, key, entry) => {
|
|
145
|
+
options.budget.visitNode();
|
|
146
|
+
if (typeof key !== "string")
|
|
147
|
+
throw new TypeError("Array indices must be string keys.");
|
|
148
|
+
if (options.setProperty !== undefined)
|
|
149
|
+
options.setProperty(receiver, key, entry);
|
|
150
|
+
else if (!Reflect.set(receiver, key, entry))
|
|
151
|
+
throw new TypeError(`Cannot assign property '${key}'.`);
|
|
152
|
+
return true;
|
|
153
|
+
},
|
|
154
|
+
deleteProperty: (_target, key) => {
|
|
155
|
+
options.budget.visitNode();
|
|
156
|
+
if (typeof key === "string" && options.deleteProperty !== undefined)
|
|
157
|
+
return options.deleteProperty(receiver, key);
|
|
158
|
+
return Reflect.deleteProperty(receiver, key);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
arrayLikeSources.set(view, receiver);
|
|
162
|
+
return view;
|
|
95
163
|
}
|
|
96
164
|
async function callArrayMethodUnlocked(value, methodName, args, options, stack) {
|
|
97
165
|
switch (methodName) {
|
|
@@ -130,8 +198,22 @@ async function callArrayMethodUnlocked(value, methodName, args, options, stack)
|
|
|
130
198
|
return Reflect.apply(Array.prototype.lastIndexOf, value, [...args]);
|
|
131
199
|
case "join":
|
|
132
200
|
return options.budget.allocateString(Reflect.apply(Array.prototype.join, value, [...args]));
|
|
133
|
-
case "slice":
|
|
134
|
-
|
|
201
|
+
case "slice": {
|
|
202
|
+
const length = value.length;
|
|
203
|
+
const start = toIntegerOrInfinity(await sandboxNumber(args[0], options.budget, options.context));
|
|
204
|
+
const end = args[1] === undefined ? length : toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context));
|
|
205
|
+
const first = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
206
|
+
const final = end < 0 ? Math.max(length + end, 0) : Math.min(end, length);
|
|
207
|
+
const count = Math.max(final - first, 0);
|
|
208
|
+
options.budget.allocateArrayLength(count);
|
|
209
|
+
const result = new Array(count);
|
|
210
|
+
for (let index = 0; index < count; index += 1) {
|
|
211
|
+
options.budget.visitNode();
|
|
212
|
+
if (first + index in value)
|
|
213
|
+
result[index] = value[first + index];
|
|
214
|
+
}
|
|
215
|
+
return budgetProducedValue(result, options.budget);
|
|
216
|
+
}
|
|
135
217
|
case "concat":
|
|
136
218
|
return budgetProducedValue(Reflect.apply(Array.prototype.concat, value, [...args]), options.budget);
|
|
137
219
|
case "splice": {
|
|
@@ -152,7 +234,7 @@ async function callArrayMethodUnlocked(value, methodName, args, options, stack)
|
|
|
152
234
|
return budgetProducedValue(Reflect.apply(Array.prototype.at, value, [...args]), options.budget);
|
|
153
235
|
case "sort":
|
|
154
236
|
if (args[0] === undefined) {
|
|
155
|
-
|
|
237
|
+
Reflect.apply(Array.prototype.sort, value, []);
|
|
156
238
|
budgetProducedValue(value, options.budget);
|
|
157
239
|
return value;
|
|
158
240
|
}
|
|
@@ -160,11 +242,17 @@ async function callArrayMethodUnlocked(value, methodName, args, options, stack)
|
|
|
160
242
|
budgetProducedValue(value, options.budget);
|
|
161
243
|
return value;
|
|
162
244
|
case "reverse":
|
|
163
|
-
|
|
245
|
+
Reflect.apply(Array.prototype.reverse, value, []);
|
|
164
246
|
budgetProducedValue(value, options.budget);
|
|
165
247
|
return value;
|
|
166
248
|
case "toSorted": {
|
|
167
|
-
const
|
|
249
|
+
const length = value.length;
|
|
250
|
+
options.budget.allocateArrayLength(length);
|
|
251
|
+
const result = new Array(length);
|
|
252
|
+
for (let index = 0; index < length; index += 1) {
|
|
253
|
+
options.budget.visitNode();
|
|
254
|
+
result[index] = value[index];
|
|
255
|
+
}
|
|
168
256
|
if (args[0] === undefined) {
|
|
169
257
|
result.sort();
|
|
170
258
|
}
|
|
@@ -174,13 +262,33 @@ async function callArrayMethodUnlocked(value, methodName, args, options, stack)
|
|
|
174
262
|
return budgetProducedValue(result, options.budget);
|
|
175
263
|
}
|
|
176
264
|
case "toReversed": {
|
|
177
|
-
const
|
|
178
|
-
|
|
265
|
+
const length = value.length;
|
|
266
|
+
options.budget.allocateArrayLength(length);
|
|
267
|
+
const result = new Array(length);
|
|
268
|
+
for (let index = 0; index < length; index += 1) {
|
|
269
|
+
options.budget.visitNode();
|
|
270
|
+
result[index] = value[length - index - 1];
|
|
271
|
+
}
|
|
179
272
|
return budgetProducedValue(result, options.budget);
|
|
180
273
|
}
|
|
181
274
|
case "toSpliced": {
|
|
182
|
-
const
|
|
183
|
-
|
|
275
|
+
const length = value.length;
|
|
276
|
+
const start = toIntegerOrInfinity(await sandboxNumber(args[0], options.budget, options.context));
|
|
277
|
+
const first = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
278
|
+
const inserted = Math.max(args.length - 2, 0);
|
|
279
|
+
const deleted = args.length === 0 ? 0 : args.length === 1 ? length - first
|
|
280
|
+
: Math.min(Math.max(toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context)), 0), length - first);
|
|
281
|
+
const resultLength = length + inserted - deleted;
|
|
282
|
+
if (resultLength > Number.MAX_SAFE_INTEGER)
|
|
283
|
+
throw new TypeError("Array-like length exceeds the safe integer limit.");
|
|
284
|
+
options.budget.allocateArrayLength(resultLength);
|
|
285
|
+
const result = new Array(resultLength);
|
|
286
|
+
for (let index = 0; index < resultLength; index += 1) {
|
|
287
|
+
options.budget.visitNode();
|
|
288
|
+
result[index] = index < first ? value[index]
|
|
289
|
+
: index < first + inserted ? args[index - first + 2]
|
|
290
|
+
: value[index - inserted + deleted];
|
|
291
|
+
}
|
|
184
292
|
return budgetProducedValue(result, options.budget);
|
|
185
293
|
}
|
|
186
294
|
case "with": {
|
|
@@ -197,41 +305,46 @@ async function callArrayMethodUnlocked(value, methodName, args, options, stack)
|
|
|
197
305
|
result[position] =
|
|
198
306
|
position === actualIndex
|
|
199
307
|
? args[1]
|
|
200
|
-
: Object.hasOwn(value, position)
|
|
201
|
-
?
|
|
202
|
-
:
|
|
308
|
+
: Array.isArray(value) && !Object.hasOwn(value, position)
|
|
309
|
+
? undefined
|
|
310
|
+
: value[position];
|
|
203
311
|
}
|
|
204
312
|
return budgetProducedValue(result, options.budget);
|
|
205
313
|
}
|
|
206
314
|
case "push": {
|
|
207
|
-
appendArrayValues(value, args);
|
|
208
|
-
const nextLength = value.length;
|
|
315
|
+
const nextLength = appendArrayValues(value, args);
|
|
209
316
|
budgetProducedValue(value, options.budget);
|
|
210
317
|
return nextLength;
|
|
211
318
|
}
|
|
212
319
|
case "pop":
|
|
213
|
-
return budgetProducedValue(
|
|
320
|
+
return budgetProducedValue(Reflect.apply(Array.prototype.pop, value, []), options.budget);
|
|
214
321
|
case "shift":
|
|
215
|
-
return budgetProducedValue(
|
|
322
|
+
return budgetProducedValue(Reflect.apply(Array.prototype.shift, value, []), options.budget);
|
|
216
323
|
case "unshift": {
|
|
217
|
-
prependArrayValues(value, args);
|
|
218
|
-
const nextLength = value.length;
|
|
324
|
+
const nextLength = prependArrayValues(value, args);
|
|
219
325
|
budgetProducedValue(value, options.budget);
|
|
220
326
|
return nextLength;
|
|
221
327
|
}
|
|
222
328
|
}
|
|
223
329
|
}
|
|
224
330
|
function appendArrayValues(target, values) {
|
|
331
|
+
let length = target.length;
|
|
332
|
+
if (length + values.length > Number.MAX_SAFE_INTEGER)
|
|
333
|
+
throw new TypeError("Array-like length exceeds the safe integer limit.");
|
|
225
334
|
for (const value of values) {
|
|
226
|
-
target
|
|
335
|
+
target[length++] = value;
|
|
227
336
|
}
|
|
337
|
+
target.length = length;
|
|
338
|
+
return length;
|
|
228
339
|
}
|
|
229
340
|
function prependArrayValues(target, values) {
|
|
230
341
|
const originalLength = target.length;
|
|
231
|
-
|
|
232
|
-
|
|
342
|
+
const length = originalLength + values.length;
|
|
343
|
+
if (length > Number.MAX_SAFE_INTEGER)
|
|
344
|
+
throw new TypeError("Array-like length exceeds the safe integer limit.");
|
|
345
|
+
for (let index = values.length === 0 ? -1 : originalLength - 1; index >= 0; index -= 1) {
|
|
233
346
|
const targetIndex = index + values.length;
|
|
234
|
-
if (
|
|
347
|
+
if (index in target) {
|
|
235
348
|
target[targetIndex] = target[index];
|
|
236
349
|
}
|
|
237
350
|
else {
|
|
@@ -241,6 +354,8 @@ function prependArrayValues(target, values) {
|
|
|
241
354
|
for (let index = 0; index < values.length; index += 1) {
|
|
242
355
|
target[index] = values[index];
|
|
243
356
|
}
|
|
357
|
+
target.length = length;
|
|
358
|
+
return length;
|
|
244
359
|
}
|
|
245
360
|
function isCallbackArrayMethod(methodName) {
|
|
246
361
|
return (methodName === "map" ||
|
|
@@ -276,6 +391,7 @@ function getRequiredCallback(methodName, value) {
|
|
|
276
391
|
}
|
|
277
392
|
async function mapArray(value, callback, options, stack, thisValue) {
|
|
278
393
|
const length = value.length;
|
|
394
|
+
options.budget.allocateArrayLength(length);
|
|
279
395
|
const result = new Array(length);
|
|
280
396
|
options.budget.setRetainedValues(result, () => [result]);
|
|
281
397
|
try {
|
|
@@ -415,7 +531,7 @@ async function reduceFromLeft(value, callback, accumulator, startIndex, length,
|
|
|
415
531
|
if (!(index in value)) {
|
|
416
532
|
continue;
|
|
417
533
|
}
|
|
418
|
-
current = await options.callClosure(callback, [current, value[index], index, value], stack);
|
|
534
|
+
current = await options.callClosure(callback, [current, value[index], index, arrayLikeSources.get(value) ?? value], stack);
|
|
419
535
|
}
|
|
420
536
|
return current;
|
|
421
537
|
}
|
|
@@ -433,7 +549,7 @@ async function reduceFromRight(value, callback, accumulator, startIndex, length,
|
|
|
433
549
|
if (!(index in value)) {
|
|
434
550
|
continue;
|
|
435
551
|
}
|
|
436
|
-
current = await options.callClosure(callback, [current, value[index], index, value], stack);
|
|
552
|
+
current = await options.callClosure(callback, [current, value[index], index, arrayLikeSources.get(value) ?? value], stack);
|
|
437
553
|
}
|
|
438
554
|
return current;
|
|
439
555
|
}
|
|
@@ -552,7 +668,7 @@ async function compareEntries(left, right, comparator, options, stack) {
|
|
|
552
668
|
return Number.isNaN(result) ? 0 : result;
|
|
553
669
|
}
|
|
554
670
|
async function callArrayCallback(callback, value, index, array, options, stack, thisValue) {
|
|
555
|
-
return options.callClosure(callback, [value, index, array], stack, thisValue);
|
|
671
|
+
return options.callClosure(callback, [value, index, arrayLikeSources.get(array) ?? array], stack, thisValue);
|
|
556
672
|
}
|
|
557
673
|
function findNextDefinedIndex(value, startIndex, direction, length, budget) {
|
|
558
674
|
for (let index = startIndex; direction > 0 ? index < length : index >= 0; index += direction) {
|
|
@@ -564,6 +680,8 @@ function findNextDefinedIndex(value, startIndex, direction, length, budget) {
|
|
|
564
680
|
return -1;
|
|
565
681
|
}
|
|
566
682
|
function budgetProducedValue(value, budget) {
|
|
683
|
+
if (typeof value === "object" && value !== null)
|
|
684
|
+
value = arrayLikeSources.get(value) ?? value;
|
|
567
685
|
allocateProducedValue(value, budget, new WeakSet());
|
|
568
686
|
return value;
|
|
569
687
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Budget } from "../budget.js";
|
|
2
|
+
import { type SandboxCollectionIterator } from "../collection-iterator.js";
|
|
3
|
+
import { type SandboxValue } from "../values.js";
|
|
4
|
+
export declare function getCollectionIteratorMember(target: SandboxCollectionIterator, property: string | number, budget: Budget): SandboxValue;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { collectionIteratorState, isSandboxCollectionIterator, nextCollectionIterator } from "../collection-iterator.js";
|
|
2
|
+
import { createSandboxClosure } from "../values.js";
|
|
3
|
+
export function getCollectionIteratorMember(target, property, budget) {
|
|
4
|
+
if (Object.hasOwn(target, property))
|
|
5
|
+
return target[property];
|
|
6
|
+
if (property !== "next")
|
|
7
|
+
return undefined;
|
|
8
|
+
const { collectionKind } = collectionIteratorState(target);
|
|
9
|
+
return createSandboxClosure({
|
|
10
|
+
sandbox: true,
|
|
11
|
+
name: "next",
|
|
12
|
+
call: (_args, context) => {
|
|
13
|
+
const receiver = context?.thisValue;
|
|
14
|
+
if (!isSandboxCollectionIterator(receiver) || collectionIteratorState(receiver).collectionKind !== collectionKind)
|
|
15
|
+
throw new TypeError("Iterator next requires a matching collection iterator receiver.");
|
|
16
|
+
return nextCollectionIterator(receiver, budget);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createSandboxClosure, isSandboxClosure, isSandboxMap } from "../values.js";
|
|
2
2
|
import { assertCollectionMutable } from "../running-state.js";
|
|
3
3
|
import { enterKeyedCollectionCallback, updateKeyedCollectionCallbacks } from "./collection-callback.js";
|
|
4
|
+
import { createSandboxCollectionIterator } from "../collection-iterator.js";
|
|
4
5
|
const mapMethodNames = new Set([
|
|
5
6
|
"get",
|
|
6
7
|
"set",
|
|
@@ -80,10 +81,8 @@ export async function callMapMethod(target, methodName, args, options, stack = [
|
|
|
80
81
|
return undefined;
|
|
81
82
|
}
|
|
82
83
|
case "keys":
|
|
83
|
-
return allocateProducedSandboxValue([...target.entries.keys()], options.budget);
|
|
84
84
|
case "values":
|
|
85
|
-
return allocateProducedSandboxValue([...target.entries.values()], options.budget);
|
|
86
85
|
case "entries":
|
|
87
|
-
return
|
|
86
|
+
return createSandboxCollectionIterator(target, methodName);
|
|
88
87
|
}
|
|
89
88
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Budget } from "../budget.js";
|
|
2
|
-
import { type SandboxValue } from "../values.js";
|
|
2
|
+
import { type SandboxCallContext, type SandboxValue } from "../values.js";
|
|
3
3
|
export type NumberMethodName = "toExponential" | "toFixed" | "toPrecision" | "toString";
|
|
4
4
|
export declare function getNumberMember(property: string | number, budget: Budget): SandboxValue | undefined;
|
|
5
5
|
export declare function isNumberMethodName(property: string | number): property is NumberMethodName;
|
|
6
|
-
export declare function callNumberMethod(value: SandboxValue, methodName: NumberMethodName, args: readonly SandboxValue[], budget: Budget): string
|
|
6
|
+
export declare function callNumberMethod(value: SandboxValue, methodName: NumberMethodName, args: readonly SandboxValue[], budget: Budget, context?: SandboxCallContext): string | Promise<string>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sandboxNumber } from "../string-coercion.js";
|
|
1
2
|
import { createSandboxClosure } from "../values.js";
|
|
2
3
|
const numberMethodNames = new Set([
|
|
3
4
|
"toExponential",
|
|
@@ -12,65 +13,45 @@ export function getNumberMember(property, budget) {
|
|
|
12
13
|
return createSandboxClosure({
|
|
13
14
|
sandbox: true,
|
|
14
15
|
name: `Number#${property}`,
|
|
15
|
-
call: (args, context) => callNumberMethod(context?.thisValue, property, args, budget)
|
|
16
|
+
call: (args, context) => callNumberMethod(context?.thisValue, property, args, budget, context)
|
|
16
17
|
});
|
|
17
18
|
}
|
|
18
19
|
export function isNumberMethodName(property) {
|
|
19
20
|
return typeof property === "string" && numberMethodNames.has(property);
|
|
20
21
|
}
|
|
21
|
-
export function callNumberMethod(value, methodName, args, budget) {
|
|
22
|
+
export function callNumberMethod(value, methodName, args, budget, context) {
|
|
22
23
|
if (typeof value !== "number") {
|
|
23
24
|
throw new TypeError(`Number#${methodName} requires a number receiver.`);
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
switch (methodName) {
|
|
29
|
-
case "toString":
|
|
30
|
-
return value.toString(asValidatedRadix(args[0]));
|
|
31
|
-
case "toExponential":
|
|
32
|
-
return args[0] === undefined
|
|
33
|
-
? value.toExponential()
|
|
34
|
-
: value.toExponential(asValidatedFractionDigits(args[0], methodName));
|
|
35
|
-
case "toFixed":
|
|
36
|
-
return value.toFixed(asValidatedFractionDigits(args[0], methodName));
|
|
37
|
-
case "toPrecision":
|
|
38
|
-
return args[0] === undefined
|
|
39
|
-
? value.toPrecision()
|
|
40
|
-
: value.toPrecision(asValidatedPrecision(args[0]));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function asValidatedRadix(value) {
|
|
44
|
-
if (value === undefined) {
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
const radix = toIntegerOrInfinity(value);
|
|
48
|
-
if (radix < 2 || radix > 36) {
|
|
49
|
-
throw new RangeError("Number#toString radix must be between 2 and 36.");
|
|
26
|
+
const argument = args[0];
|
|
27
|
+
if (argument !== null && typeof argument === "object") {
|
|
28
|
+
return formatObjectArgument(value, methodName, argument, budget, context);
|
|
50
29
|
}
|
|
51
|
-
return
|
|
30
|
+
return formatNumber(value, methodName, argument === undefined ? undefined : Number(argument), budget);
|
|
52
31
|
}
|
|
53
|
-
function
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
32
|
+
async function formatObjectArgument(value, methodName, argument, budget, context) {
|
|
33
|
+
const retainedArgument = {};
|
|
34
|
+
budget.setRetainedValues(retainedArgument, () => [argument]);
|
|
35
|
+
try {
|
|
36
|
+
const number = await sandboxNumber(argument, budget, context);
|
|
37
|
+
return formatNumber(value, methodName, number, budget);
|
|
57
38
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
function asValidatedPrecision(value) {
|
|
61
|
-
const precision = toIntegerOrInfinity(value);
|
|
62
|
-
if (precision < 1 || precision > 100) {
|
|
63
|
-
throw new RangeError("Number#toPrecision precision must be between 1 and 100.");
|
|
39
|
+
finally {
|
|
40
|
+
budget.setRetainedValues(retainedArgument, undefined);
|
|
64
41
|
}
|
|
65
|
-
return precision;
|
|
66
42
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
43
|
+
function formatNumber(value, methodName, argument, budget) {
|
|
44
|
+
let result;
|
|
45
|
+
try {
|
|
46
|
+
result = value[methodName](argument);
|
|
71
47
|
}
|
|
72
|
-
|
|
73
|
-
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (!(error instanceof RangeError))
|
|
50
|
+
throw error;
|
|
51
|
+
const detail = methodName === "toString" ? "radix must be between 2 and 36."
|
|
52
|
+
: methodName === "toPrecision" ? "precision must be between 1 and 100."
|
|
53
|
+
: "digits must be between 0 and 100.";
|
|
54
|
+
throw new RangeError(`Number#${methodName} ${detail}`);
|
|
74
55
|
}
|
|
75
|
-
return
|
|
56
|
+
return budget.allocateString(result);
|
|
76
57
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createSandboxClosure, isSandboxClosure, isSandboxSet } from "../values.js";
|
|
2
2
|
import { assertCollectionMutable } from "../running-state.js";
|
|
3
3
|
import { enterKeyedCollectionCallback, updateKeyedCollectionCallbacks } from "./collection-callback.js";
|
|
4
|
+
import { createSandboxCollectionIterator } from "../collection-iterator.js";
|
|
4
5
|
const setMethodNames = new Set([
|
|
5
6
|
"add",
|
|
6
7
|
"has",
|
|
@@ -77,8 +78,7 @@ export async function callSetMethod(target, methodName, args, options, stack = [
|
|
|
77
78
|
}
|
|
78
79
|
case "keys":
|
|
79
80
|
case "values":
|
|
80
|
-
return allocateProducedSandboxValue([...target.values], options.budget);
|
|
81
81
|
case "entries":
|
|
82
|
-
return
|
|
82
|
+
return createSandboxCollectionIterator(target, methodName);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AssignmentPattern, MemberExpression, ParseResult, RestElement, VariableDeclarationKind, VariableDeclarator } from "../parse.js";
|
|
2
2
|
import type { AsyncEvaluationResult } from "./async.js";
|
|
3
3
|
import type { Scope } from "./scope.js";
|
|
4
|
+
import type { Budget } from "./budget.js";
|
|
4
5
|
import { type SandboxValue } from "./values.js";
|
|
5
6
|
type Pattern = VariableDeclarator["id"] | AssignmentPattern | MemberExpression | RestElement;
|
|
6
7
|
export type PatternTarget = {
|
|
@@ -10,6 +11,7 @@ export type PatternTarget = {
|
|
|
10
11
|
assign: true;
|
|
11
12
|
};
|
|
12
13
|
export type PatternContext = {
|
|
14
|
+
budget?: Budget;
|
|
13
15
|
evaluate(node: ParseResult): Promise<AsyncEvaluationResult>;
|
|
14
16
|
toPropertyKey(value: SandboxValue): Promise<string>;
|
|
15
17
|
getProperty(value: SandboxValue, key: string | number): SandboxValue;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isSandboxCollectionIterator, nextCollectionIterator } from "./collection-iterator.js";
|
|
1
2
|
import { isSandboxMap, isSandboxSet, ownEnumerableSandboxEntries } from "./values.js";
|
|
2
3
|
export async function bindPattern(pattern, value, target, scope, context) {
|
|
3
4
|
switch (pattern.type) {
|
|
@@ -46,13 +47,30 @@ async function bindAssignmentPattern(pattern, value, target, scope, context) {
|
|
|
46
47
|
return bindPattern(pattern.left, defaultValue.value, target, scope, context);
|
|
47
48
|
}
|
|
48
49
|
async function bindArrayPattern(pattern, value, target, scope, context) {
|
|
49
|
-
const
|
|
50
|
+
const iterator = isSandboxCollectionIterator(value) ? value : undefined;
|
|
51
|
+
const values = iterator === undefined ? getArrayPatternValues(value) : undefined;
|
|
50
52
|
for (let index = 0; index < pattern.elements.length; index += 1) {
|
|
51
53
|
const element = pattern.elements[index];
|
|
52
54
|
if (element === null) {
|
|
55
|
+
if (iterator !== undefined)
|
|
56
|
+
nextCollectionIterator(iterator, context.budget);
|
|
53
57
|
continue;
|
|
54
58
|
}
|
|
55
|
-
|
|
59
|
+
let elementValue;
|
|
60
|
+
if (iterator === undefined) {
|
|
61
|
+
elementValue = element.type === "RestElement" ? values.slice(index) : values[index];
|
|
62
|
+
}
|
|
63
|
+
else if (element.type === "RestElement") {
|
|
64
|
+
const rest = [];
|
|
65
|
+
for (let entry = nextCollectionIterator(iterator, context.budget); !entry.done; entry = nextCollectionIterator(iterator, context.budget)) {
|
|
66
|
+
context.budget?.allocateArrayLength(rest.length + 1);
|
|
67
|
+
rest.push(entry.value);
|
|
68
|
+
}
|
|
69
|
+
elementValue = rest;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
elementValue = nextCollectionIterator(iterator, context.budget).value;
|
|
73
|
+
}
|
|
56
74
|
const binding = await bindPattern(element, elementValue, target, scope, context);
|
|
57
75
|
if (!binding.ok) {
|
|
58
76
|
return binding;
|
|
@@ -337,7 +337,7 @@ async function settleIterable(iterable, method, budget, constructor, context) {
|
|
|
337
337
|
const promiseResolve = readPromiseReceiverProperty(constructor, "resolve", prototype);
|
|
338
338
|
if (!isSandboxClosure(promiseResolve))
|
|
339
339
|
throw new TypeError("Promise constructor requires a callable resolve.");
|
|
340
|
-
const iterator = getSandboxIterator(iterable);
|
|
340
|
+
const iterator = getSandboxIterator(iterable, budget);
|
|
341
341
|
if (iterator === undefined)
|
|
342
342
|
throw new TypeError("Promise helpers require an iterable.");
|
|
343
343
|
let index = 0;
|
|
@@ -4,6 +4,7 @@ import { invokeBuiltinClosure } from "./builtin-call.js";
|
|
|
4
4
|
import { float32Storage, isFloat32Array } from "./float32.js";
|
|
5
5
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
6
6
|
import { isGuestHostObject } from "./host-capabilities.js";
|
|
7
|
+
import { collectionIteratorState, isSandboxCollectionIterator } from "./collection-iterator.js";
|
|
7
8
|
import { getGuestFunctionProperties, getSandboxPrototype, hasExplicitSandboxPrototype, isGuestClosure } from "./object-model.js";
|
|
8
9
|
import { getRegexMember } from "./methods/regex.js";
|
|
9
10
|
import { isSandboxClosure, isSandboxPromise, isSandboxMap, isSandboxSet, isSandboxRegex, isSandboxGenerator } from "./values.js";
|
|
@@ -65,6 +66,7 @@ function conversionHook(value, name, budget) {
|
|
|
65
66
|
isSandboxClosure(value) ||
|
|
66
67
|
isSandboxMap(value) ||
|
|
67
68
|
isSandboxSet(value) ||
|
|
69
|
+
isSandboxCollectionIterator(value) ||
|
|
68
70
|
isSandboxPromise(value) ||
|
|
69
71
|
isSandboxRegex(value) ||
|
|
70
72
|
isSandboxGenerator(value) ||
|
|
@@ -97,6 +99,8 @@ async function defaultToString(value, budget, context, joining) {
|
|
|
97
99
|
return "[object Map]";
|
|
98
100
|
if (isSandboxSet(value))
|
|
99
101
|
return "[object Set]";
|
|
102
|
+
if (isSandboxCollectionIterator(value))
|
|
103
|
+
return collectionIteratorState(value).collectionKind === "map" ? "[object Map Iterator]" : "[object Set Iterator]";
|
|
100
104
|
if (isSandboxGenerator(value))
|
|
101
105
|
return "[object Generator]";
|
|
102
106
|
if (isSandboxRegex(value)) {
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
+
import { sandboxMapBrand, sandboxSetBrand } from "./collection-brands.js";
|
|
2
|
+
import { type SandboxCollectionIterator } from "./collection-iterator.js";
|
|
1
3
|
import type { Budget, CompileTicket } from "./budget.js";
|
|
2
4
|
import { CompileScope } from "./regex/compile-guard.js";
|
|
3
5
|
import type { GeneratorChannel } from "./generator.js";
|
|
4
6
|
import { type RegexPattern } from "./regex/parse.js";
|
|
5
7
|
export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
|
|
8
|
+
export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
|
|
6
9
|
declare const sandboxClosureBrand: unique symbol;
|
|
7
10
|
declare const sandboxGeneratorBrand: unique symbol;
|
|
8
|
-
declare const sandboxMapBrand: unique symbol;
|
|
9
11
|
declare const sandboxPromiseBrand: unique symbol;
|
|
10
12
|
declare const sandboxRegexBrand: unique symbol;
|
|
11
13
|
declare const sandboxRegexPattern: unique symbol;
|
|
12
|
-
declare const sandboxSetBrand: unique symbol;
|
|
13
14
|
declare const sandboxRetainedValues: unique symbol;
|
|
14
15
|
export type SandboxPrimitive = string | number | boolean | null | undefined;
|
|
15
|
-
export type SandboxValue = SandboxPrimitive | Date | Float32Array | SandboxObject | SandboxArray | SandboxClosure | SandboxGenerator | SandboxMap | SandboxSet | SandboxPromise | SandboxRegex;
|
|
16
|
+
export type SandboxValue = SandboxPrimitive | Date | Float32Array | SandboxObject | SandboxArray | SandboxClosure | SandboxGenerator | SandboxCollectionIterator | SandboxMap | SandboxSet | SandboxPromise | SandboxRegex;
|
|
16
17
|
export type SandboxObject = {
|
|
17
18
|
[key: string]: SandboxValue;
|
|
18
19
|
};
|
|
@@ -123,9 +124,7 @@ export declare function createSandboxSet(values?: Iterable<SandboxValue>): Sandb
|
|
|
123
124
|
export declare function createSandboxRegex(source: string, flags?: string, lastIndex?: number, compilation?: CompileScope): SandboxRegex;
|
|
124
125
|
export declare function getSandboxRegexPattern(regex: SandboxRegex): RegexPattern;
|
|
125
126
|
export declare function isSandboxClosure(value: unknown): value is SandboxClosure;
|
|
126
|
-
export declare function isSandboxMap(value: unknown): value is SandboxMap;
|
|
127
127
|
export declare function isSandboxPromise(value: unknown): value is SandboxPromise;
|
|
128
|
-
export declare function isSandboxSet(value: unknown): value is SandboxSet;
|
|
129
128
|
export declare function isSandboxGenerator(value: unknown): value is SandboxGenerator;
|
|
130
129
|
export declare function isSandboxRegex(value: unknown): value is SandboxRegex;
|
|
131
130
|
export declare function deepCopyToSandbox(value: unknown): SandboxValue;
|