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.
- package/dist/index.js +1723 -797
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safe-js/dist/chunks/{chunk-4VT2M7WL.js → chunk-MKTUVVEB.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-WBWD7ZCM.js → chunk-YKISHTWT.js} +1675 -772
- package/packages/safe-js/dist/chunks/chunk-YKISHTWT.js.map +7 -0
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.js +1 -1
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/accessors.d.ts +9 -0
- package/packages/safe-js/dist/interp/accessors.js +68 -0
- package/packages/safe-js/dist/interp/async.js +3 -3
- package/packages/safe-js/dist/interp/classes.js +10 -4
- package/packages/safe-js/dist/interp/exceptions.d.ts +1 -1
- package/packages/safe-js/dist/interp/exceptions.js +47 -8
- package/packages/safe-js/dist/interp/globals/collections.js +53 -16
- package/packages/safe-js/dist/interp/globals/console-json.js +41 -26
- package/packages/safe-js/dist/interp/globals/object-array.js +229 -68
- package/packages/safe-js/dist/interp/globals/object.js +6 -3
- package/packages/safe-js/dist/interp/globals/primitives.js +11 -5
- package/packages/safe-js/dist/interp/interpreter.d.ts +2 -2
- package/packages/safe-js/dist/interp/interpreter.js +83 -44
- package/packages/safe-js/dist/interp/iteration.js +13 -0
- package/packages/safe-js/dist/interp/methods/array.d.ts +1 -1
- package/packages/safe-js/dist/interp/methods/array.js +368 -109
- package/packages/safe-js/dist/interp/methods/function.js +56 -20
- package/packages/safe-js/dist/interp/methods/regex.js +1 -1
- package/packages/safe-js/dist/interp/object-model.d.ts +1 -0
- package/packages/safe-js/dist/interp/object-model.js +48 -10
- package/packages/safe-js/dist/interp/patterns.d.ts +2 -2
- package/packages/safe-js/dist/interp/patterns.js +35 -16
- package/packages/safe-js/dist/interp/promise.d.ts +1 -1
- package/packages/safe-js/dist/interp/promise.js +103 -63
- package/packages/safe-js/dist/interp/string-coercion.d.ts +1 -0
- package/packages/safe-js/dist/interp/string-coercion.js +47 -37
- package/packages/safe-js/dist/interp/values.d.ts +2 -1
- package/packages/safe-js/dist/interp/values.js +26 -1
- package/packages/safe-js/dist/parse/parser.d.ts +1 -1
- package/packages/safe-js/dist/parse/parser.js +37 -8
- package/packages/safe-js/dist/chunks/chunk-WBWD7ZCM.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-4VT2M7WL.js.map → chunk-MKTUVVEB.js.map} +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createSandboxBox } from "../boxed.js";
|
|
2
2
|
import { createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet, measureSandboxData } from "../values.js";
|
|
3
3
|
import { assertCollectionMutable, enterRunningState } from "../running-state.js";
|
|
4
|
+
import { retainValues } from "../resources.js";
|
|
4
5
|
import { getSandboxDataProperty, getSandboxPrototype } from "../object-model.js";
|
|
5
|
-
import { sandboxNumber } from "../string-coercion.js";
|
|
6
|
+
import { joinSandboxArray, sandboxNumber, sandboxString } from "../string-coercion.js";
|
|
6
7
|
const arrayLikeSources = new WeakMap();
|
|
7
8
|
const activeArrayCallbacks = new WeakMap();
|
|
8
9
|
const arrayMethodNames = new Set([
|
|
@@ -117,7 +118,7 @@ export async function callArrayMethod(receiver, methodName, args, options, stack
|
|
|
117
118
|
}
|
|
118
119
|
async function arrayLikeView(receiver, options) {
|
|
119
120
|
const rawLength = options.context?.getProperty !== undefined
|
|
120
|
-
? options.context.getProperty(receiver, "length")
|
|
121
|
+
? await options.context.getProperty(receiver, "length")
|
|
121
122
|
: getSandboxDataProperty(receiver, "length", options.budget);
|
|
122
123
|
const number = await sandboxNumber(rawLength, options.budget, options.context);
|
|
123
124
|
const length = Number.isNaN(number) || number <= 0 ? 0 : Math.min(Math.trunc(number), Number.MAX_SAFE_INTEGER);
|
|
@@ -192,172 +193,364 @@ async function callArrayMethodUnlocked(value, methodName, args, options, stack)
|
|
|
192
193
|
case "flatMap":
|
|
193
194
|
return budgetProducedValue(await flatMapArray(value, getRequiredCallback(methodName, args[0]), options, stack, args[1]), options.budget);
|
|
194
195
|
case "flat":
|
|
195
|
-
return budgetProducedValue(flattenArray(value,
|
|
196
|
+
return budgetProducedValue(await flattenArray(value, args[0] === undefined
|
|
197
|
+
? 1
|
|
198
|
+
: toIntegerOrInfinity(await sandboxNumber(args[0], options.budget, options.context)), options), options.budget);
|
|
196
199
|
case "includes":
|
|
197
|
-
return Reflect.apply(Array.prototype.includes, value, [...args]);
|
|
198
200
|
case "indexOf":
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
case "lastIndexOf": {
|
|
202
|
+
const length = value.length;
|
|
203
|
+
if (length === 0)
|
|
204
|
+
return methodName === "includes" ? false : -1;
|
|
205
|
+
const reverse = methodName === "lastIndexOf";
|
|
206
|
+
const start = reverse && args.length < 2
|
|
207
|
+
? length - 1
|
|
208
|
+
: toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context));
|
|
209
|
+
let index = reverse
|
|
210
|
+
? Math.min(start < 0 ? length + start : start, length - 1)
|
|
211
|
+
: start < 0
|
|
212
|
+
? Math.max(length + start, 0)
|
|
213
|
+
: start;
|
|
214
|
+
for (; index >= 0 && index < length; index += reverse ? -1 : 1) {
|
|
215
|
+
options.budget.visitNode();
|
|
216
|
+
if (methodName !== "includes" && !(index in value))
|
|
217
|
+
continue;
|
|
218
|
+
const entry = await readArrayElement(value, index, options);
|
|
219
|
+
if (entry === args[0] ||
|
|
220
|
+
(methodName === "includes" && Number.isNaN(entry) && Number.isNaN(args[0])))
|
|
221
|
+
return methodName === "includes" ? true : index;
|
|
222
|
+
}
|
|
223
|
+
return methodName === "includes" ? false : -1;
|
|
224
|
+
}
|
|
225
|
+
case "join": {
|
|
226
|
+
const length = value.length;
|
|
227
|
+
const separator = args[0] === undefined ? "," : await sandboxString(args[0], options.budget, options.context);
|
|
228
|
+
return joinSandboxArray(arrayLikeSources.get(value) ?? value, length, separator, options.budget, options.context);
|
|
229
|
+
}
|
|
204
230
|
case "slice": {
|
|
205
231
|
const length = value.length;
|
|
206
232
|
const start = toIntegerOrInfinity(await sandboxNumber(args[0], options.budget, options.context));
|
|
207
|
-
const end = args[1] === undefined
|
|
233
|
+
const end = args[1] === undefined
|
|
234
|
+
? length
|
|
235
|
+
: toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context));
|
|
208
236
|
const first = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
209
237
|
const final = end < 0 ? Math.max(length + end, 0) : Math.min(end, length);
|
|
210
238
|
const count = Math.max(final - first, 0);
|
|
211
239
|
options.budget.allocateArrayLength(count);
|
|
212
240
|
const result = new Array(count);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
241
|
+
const release = retainValues(options.budget, () => [result]);
|
|
242
|
+
try {
|
|
243
|
+
for (let index = 0; index < count; index += 1) {
|
|
244
|
+
options.budget.visitNode();
|
|
245
|
+
if (first + index in value)
|
|
246
|
+
result[index] = await readArrayElement(value, first + index, options);
|
|
247
|
+
}
|
|
248
|
+
return budgetProducedValue(result, options.budget);
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
release();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
case "concat": {
|
|
255
|
+
const result = [];
|
|
256
|
+
const retained = {};
|
|
257
|
+
options.budget.setRetainedValues(retained, () => [result]);
|
|
258
|
+
try {
|
|
259
|
+
for (const entry of [value, ...args]) {
|
|
260
|
+
if (!Array.isArray(entry)) {
|
|
261
|
+
options.budget.allocateArrayLength(result.length + 1);
|
|
262
|
+
result.push(entry);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const start = result.length;
|
|
266
|
+
const length = entry.length;
|
|
267
|
+
options.budget.allocateArrayLength(start + length);
|
|
268
|
+
result.length += length;
|
|
269
|
+
for (let index = 0; index < length; index++) {
|
|
270
|
+
options.budget.visitNode();
|
|
271
|
+
if (index in entry)
|
|
272
|
+
result[start + index] = await readArrayElement(entry, index, options);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return budgetProducedValue(result, options.budget);
|
|
276
|
+
}
|
|
277
|
+
finally {
|
|
278
|
+
options.budget.setRetainedValues(retained, undefined);
|
|
217
279
|
}
|
|
218
|
-
return budgetProducedValue(result, options.budget);
|
|
219
280
|
}
|
|
220
|
-
case "concat":
|
|
221
|
-
return budgetProducedValue(Reflect.apply(Array.prototype.concat, value, [...args]), options.budget);
|
|
222
281
|
case "splice": {
|
|
223
|
-
const
|
|
282
|
+
const length = value.length;
|
|
283
|
+
const start = toIntegerOrInfinity(await sandboxNumber(args[0], options.budget, options.context));
|
|
284
|
+
const first = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
285
|
+
const inserted = Math.max(args.length - 2, 0);
|
|
286
|
+
const deleted = args.length === 0
|
|
287
|
+
? 0
|
|
288
|
+
: args.length === 1
|
|
289
|
+
? length - first
|
|
290
|
+
: Math.min(Math.max(toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context)), 0), length - first);
|
|
291
|
+
const nextLength = length + inserted - deleted;
|
|
292
|
+
if (nextLength > Number.MAX_SAFE_INTEGER)
|
|
293
|
+
throw new TypeError("Array-like length exceeds the safe integer limit.");
|
|
294
|
+
options.budget.allocateArrayLength(deleted);
|
|
295
|
+
const removed = new Array(deleted);
|
|
296
|
+
const retained = {};
|
|
297
|
+
options.budget.setRetainedValues(retained, () => [removed]);
|
|
298
|
+
try {
|
|
299
|
+
for (let index = 0; index < deleted; index++) {
|
|
300
|
+
options.budget.visitNode();
|
|
301
|
+
if (first + index in value)
|
|
302
|
+
removed[index] = await readArrayElement(value, first + index, options);
|
|
303
|
+
}
|
|
304
|
+
if (inserted < deleted) {
|
|
305
|
+
for (let index = first; index < length - deleted; index++)
|
|
306
|
+
await moveArrayElement(value, index + deleted, index + inserted, options);
|
|
307
|
+
for (let index = length; index > nextLength; index--)
|
|
308
|
+
deleteArrayElement(value, index - 1, options);
|
|
309
|
+
}
|
|
310
|
+
else if (inserted > deleted) {
|
|
311
|
+
for (let index = length - deleted; index > first; index--)
|
|
312
|
+
await moveArrayElement(value, index + deleted - 1, index + inserted - 1, options);
|
|
313
|
+
}
|
|
314
|
+
for (let index = 0; index < inserted; index++)
|
|
315
|
+
await writeArrayProperty(value, first + index, args[index + 2], options);
|
|
316
|
+
await writeArrayProperty(value, "length", nextLength, options);
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
options.budget.setRetainedValues(retained, undefined);
|
|
320
|
+
}
|
|
224
321
|
budgetProducedValue(removed, options.budget);
|
|
225
322
|
budgetProducedValue(value, options.budget);
|
|
226
323
|
return removed;
|
|
227
324
|
}
|
|
228
|
-
case "fill":
|
|
229
|
-
|
|
325
|
+
case "fill": {
|
|
326
|
+
const length = value.length;
|
|
327
|
+
const start = toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context));
|
|
328
|
+
const end = args[2] === undefined
|
|
329
|
+
? length
|
|
330
|
+
: toIntegerOrInfinity(await sandboxNumber(args[2], options.budget, options.context));
|
|
331
|
+
const first = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
332
|
+
const final = end < 0 ? Math.max(length + end, 0) : Math.min(end, length);
|
|
333
|
+
for (let index = first; index < final; index++)
|
|
334
|
+
await writeArrayProperty(value, index, args[0], options);
|
|
230
335
|
budgetProducedValue(value, options.budget);
|
|
231
336
|
return value;
|
|
232
|
-
|
|
233
|
-
|
|
337
|
+
}
|
|
338
|
+
case "copyWithin": {
|
|
339
|
+
const length = value.length;
|
|
340
|
+
const target = toIntegerOrInfinity(await sandboxNumber(args[0], options.budget, options.context));
|
|
341
|
+
const start = toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context));
|
|
342
|
+
const end = args[2] === undefined
|
|
343
|
+
? length
|
|
344
|
+
: toIntegerOrInfinity(await sandboxNumber(args[2], options.budget, options.context));
|
|
345
|
+
let to = target < 0 ? Math.max(length + target, 0) : Math.min(target, length);
|
|
346
|
+
let from = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
347
|
+
const final = end < 0 ? Math.max(length + end, 0) : Math.min(end, length);
|
|
348
|
+
let count = Math.min(final - from, length - to);
|
|
349
|
+
const direction = from < to && to < from + count ? -1 : 1;
|
|
350
|
+
if (direction < 0) {
|
|
351
|
+
from += count - 1;
|
|
352
|
+
to += count - 1;
|
|
353
|
+
}
|
|
354
|
+
while (count-- > 0) {
|
|
355
|
+
await moveArrayElement(value, from, to, options);
|
|
356
|
+
from += direction;
|
|
357
|
+
to += direction;
|
|
358
|
+
}
|
|
234
359
|
budgetProducedValue(value, options.budget);
|
|
235
360
|
return value;
|
|
236
|
-
|
|
237
|
-
|
|
361
|
+
}
|
|
362
|
+
case "at": {
|
|
363
|
+
const length = value.length;
|
|
364
|
+
const index = toIntegerOrInfinity(await sandboxNumber(args[0], options.budget, options.context));
|
|
365
|
+
const actual = index < 0 ? length + index : index;
|
|
366
|
+
return actual < 0 || actual >= length
|
|
367
|
+
? undefined
|
|
368
|
+
: budgetProducedValue(await readArrayElement(value, actual, options), options.budget);
|
|
369
|
+
}
|
|
238
370
|
case "sort":
|
|
239
|
-
|
|
240
|
-
|
|
371
|
+
await sortArray(value, args[0] === undefined ? undefined : getRequiredCallback(methodName, args[0]), options, stack);
|
|
372
|
+
budgetProducedValue(value, options.budget);
|
|
373
|
+
return value;
|
|
374
|
+
case "reverse": {
|
|
375
|
+
const length = value.length;
|
|
376
|
+
let lowerValue;
|
|
377
|
+
let upperValue;
|
|
378
|
+
const release = retainValues(options.budget, () => [lowerValue, upperValue]);
|
|
379
|
+
try {
|
|
380
|
+
for (let lower = 0; lower < Math.floor(length / 2); lower++) {
|
|
381
|
+
options.budget.visitNode();
|
|
382
|
+
lowerValue = upperValue = undefined;
|
|
383
|
+
const upper = length - lower - 1;
|
|
384
|
+
const lowerExists = lower in value;
|
|
385
|
+
lowerValue = lowerExists ? await readArrayElement(value, lower, options) : undefined;
|
|
386
|
+
const upperExists = upper in value;
|
|
387
|
+
upperValue = upperExists ? await readArrayElement(value, upper, options) : undefined;
|
|
388
|
+
if (lowerExists && upperExists) {
|
|
389
|
+
await writeArrayProperty(value, lower, upperValue, options);
|
|
390
|
+
await writeArrayProperty(value, upper, lowerValue, options);
|
|
391
|
+
}
|
|
392
|
+
else if (!lowerExists && upperExists) {
|
|
393
|
+
await writeArrayProperty(value, lower, upperValue, options);
|
|
394
|
+
deleteArrayElement(value, upper, options);
|
|
395
|
+
}
|
|
396
|
+
else if (lowerExists) {
|
|
397
|
+
deleteArrayElement(value, lower, options);
|
|
398
|
+
await writeArrayProperty(value, upper, lowerValue, options);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
241
401
|
budgetProducedValue(value, options.budget);
|
|
242
402
|
return value;
|
|
243
403
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
Reflect.apply(Array.prototype.reverse, value, []);
|
|
249
|
-
budgetProducedValue(value, options.budget);
|
|
250
|
-
return value;
|
|
404
|
+
finally {
|
|
405
|
+
release();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
251
408
|
case "toSorted": {
|
|
252
409
|
const length = value.length;
|
|
253
410
|
options.budget.allocateArrayLength(length);
|
|
254
411
|
const result = new Array(length);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
412
|
+
const release = retainValues(options.budget, () => [result]);
|
|
413
|
+
try {
|
|
414
|
+
for (let index = 0; index < length; index += 1) {
|
|
415
|
+
options.budget.visitNode();
|
|
416
|
+
result[index] = await readArrayElement(value, index, options);
|
|
417
|
+
}
|
|
418
|
+
await sortArray(result, args[0] === undefined ? undefined : getRequiredCallback(methodName, args[0]), options, stack);
|
|
419
|
+
return budgetProducedValue(result, options.budget);
|
|
261
420
|
}
|
|
262
|
-
|
|
263
|
-
|
|
421
|
+
finally {
|
|
422
|
+
release();
|
|
264
423
|
}
|
|
265
|
-
return budgetProducedValue(result, options.budget);
|
|
266
424
|
}
|
|
267
425
|
case "toReversed": {
|
|
268
426
|
const length = value.length;
|
|
269
427
|
options.budget.allocateArrayLength(length);
|
|
270
428
|
const result = new Array(length);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
429
|
+
const release = retainValues(options.budget, () => [result]);
|
|
430
|
+
try {
|
|
431
|
+
for (let index = 0; index < length; index += 1) {
|
|
432
|
+
options.budget.visitNode();
|
|
433
|
+
result[index] = await readArrayElement(value, length - index - 1, options);
|
|
434
|
+
}
|
|
435
|
+
return budgetProducedValue(result, options.budget);
|
|
436
|
+
}
|
|
437
|
+
finally {
|
|
438
|
+
release();
|
|
274
439
|
}
|
|
275
|
-
return budgetProducedValue(result, options.budget);
|
|
276
440
|
}
|
|
277
441
|
case "toSpliced": {
|
|
278
442
|
const length = value.length;
|
|
279
443
|
const start = toIntegerOrInfinity(await sandboxNumber(args[0], options.budget, options.context));
|
|
280
444
|
const first = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
281
445
|
const inserted = Math.max(args.length - 2, 0);
|
|
282
|
-
const deleted = args.length === 0
|
|
283
|
-
|
|
446
|
+
const deleted = args.length === 0
|
|
447
|
+
? 0
|
|
448
|
+
: args.length === 1
|
|
449
|
+
? length - first
|
|
450
|
+
: Math.min(Math.max(toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context)), 0), length - first);
|
|
284
451
|
const resultLength = length + inserted - deleted;
|
|
285
452
|
if (resultLength > Number.MAX_SAFE_INTEGER)
|
|
286
453
|
throw new TypeError("Array-like length exceeds the safe integer limit.");
|
|
287
454
|
options.budget.allocateArrayLength(resultLength);
|
|
288
455
|
const result = new Array(resultLength);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
456
|
+
const release = retainValues(options.budget, () => [result]);
|
|
457
|
+
try {
|
|
458
|
+
for (let index = 0; index < resultLength; index += 1) {
|
|
459
|
+
options.budget.visitNode();
|
|
460
|
+
result[index] =
|
|
461
|
+
index < first
|
|
462
|
+
? await readArrayElement(value, index, options)
|
|
463
|
+
: index < first + inserted
|
|
464
|
+
? args[index - first + 2]
|
|
465
|
+
: await readArrayElement(value, index - inserted + deleted, options);
|
|
466
|
+
}
|
|
467
|
+
return budgetProducedValue(result, options.budget);
|
|
468
|
+
}
|
|
469
|
+
finally {
|
|
470
|
+
release();
|
|
294
471
|
}
|
|
295
|
-
return budgetProducedValue(result, options.budget);
|
|
296
472
|
}
|
|
297
473
|
case "with": {
|
|
298
474
|
const length = value.length;
|
|
299
|
-
const index = toIntegerOrInfinity(
|
|
475
|
+
const index = toIntegerOrInfinity(options.context === undefined
|
|
476
|
+
? args[0]
|
|
477
|
+
: await sandboxNumber(args[0], options.budget, options.context));
|
|
300
478
|
const actualIndex = index < 0 ? length + index : index;
|
|
301
479
|
if (actualIndex < 0 || actualIndex >= length) {
|
|
302
480
|
throw new RangeError("Invalid index");
|
|
303
481
|
}
|
|
304
482
|
options.budget.allocateArrayLength(length);
|
|
305
483
|
const result = [];
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
:
|
|
312
|
-
|
|
313
|
-
|
|
484
|
+
const release = retainValues(options.budget, () => [result]);
|
|
485
|
+
try {
|
|
486
|
+
for (let position = 0; position < length; position += 1) {
|
|
487
|
+
options.budget.visitNode();
|
|
488
|
+
result[position] =
|
|
489
|
+
position === actualIndex ? args[1] : await readArrayElement(value, position, options);
|
|
490
|
+
}
|
|
491
|
+
return budgetProducedValue(result, options.budget);
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
release();
|
|
314
495
|
}
|
|
315
|
-
return budgetProducedValue(result, options.budget);
|
|
316
496
|
}
|
|
317
497
|
case "push": {
|
|
318
|
-
const nextLength = appendArrayValues(value, args);
|
|
498
|
+
const nextLength = await appendArrayValues(value, args, options);
|
|
319
499
|
budgetProducedValue(value, options.budget);
|
|
320
500
|
return nextLength;
|
|
321
501
|
}
|
|
322
502
|
case "pop":
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
503
|
+
case "shift": {
|
|
504
|
+
const length = value.length;
|
|
505
|
+
if (length === 0) {
|
|
506
|
+
await writeArrayProperty(value, "length", 0, options);
|
|
507
|
+
return undefined;
|
|
508
|
+
}
|
|
509
|
+
const result = await readArrayElement(value, methodName === "pop" ? length - 1 : 0, options);
|
|
510
|
+
const retained = {};
|
|
511
|
+
options.budget.setRetainedValues(retained, () => [result]);
|
|
512
|
+
try {
|
|
513
|
+
if (methodName === "shift")
|
|
514
|
+
for (let index = 1; index < length; index++)
|
|
515
|
+
await moveArrayElement(value, index, index - 1, options);
|
|
516
|
+
deleteArrayElement(value, length - 1, options);
|
|
517
|
+
await writeArrayProperty(value, "length", length - 1, options);
|
|
518
|
+
return budgetProducedValue(result, options.budget);
|
|
519
|
+
}
|
|
520
|
+
finally {
|
|
521
|
+
options.budget.setRetainedValues(retained, undefined);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
326
524
|
case "unshift": {
|
|
327
|
-
const nextLength = prependArrayValues(value, args);
|
|
525
|
+
const nextLength = await prependArrayValues(value, args, options);
|
|
328
526
|
budgetProducedValue(value, options.budget);
|
|
329
527
|
return nextLength;
|
|
330
528
|
}
|
|
331
529
|
}
|
|
332
530
|
}
|
|
333
|
-
function appendArrayValues(target, values) {
|
|
531
|
+
async function appendArrayValues(target, values, options) {
|
|
334
532
|
let length = target.length;
|
|
335
533
|
if (length + values.length > Number.MAX_SAFE_INTEGER)
|
|
336
534
|
throw new TypeError("Array-like length exceeds the safe integer limit.");
|
|
337
535
|
for (const value of values) {
|
|
338
|
-
target
|
|
536
|
+
await writeArrayProperty(target, length++, value, options);
|
|
339
537
|
}
|
|
340
|
-
target
|
|
538
|
+
await writeArrayProperty(target, "length", length, options);
|
|
341
539
|
return length;
|
|
342
540
|
}
|
|
343
|
-
function prependArrayValues(target, values) {
|
|
541
|
+
async function prependArrayValues(target, values, options) {
|
|
344
542
|
const originalLength = target.length;
|
|
345
543
|
const length = originalLength + values.length;
|
|
346
544
|
if (length > Number.MAX_SAFE_INTEGER)
|
|
347
545
|
throw new TypeError("Array-like length exceeds the safe integer limit.");
|
|
348
546
|
for (let index = values.length === 0 ? -1 : originalLength - 1; index >= 0; index -= 1) {
|
|
349
547
|
const targetIndex = index + values.length;
|
|
350
|
-
|
|
351
|
-
target[targetIndex] = target[index];
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
delete target[targetIndex];
|
|
355
|
-
}
|
|
548
|
+
await moveArrayElement(target, index, targetIndex, options);
|
|
356
549
|
}
|
|
357
550
|
for (let index = 0; index < values.length; index += 1) {
|
|
358
|
-
target
|
|
551
|
+
await writeArrayProperty(target, index, values[index], options);
|
|
359
552
|
}
|
|
360
|
-
target
|
|
553
|
+
await writeArrayProperty(target, "length", length, options);
|
|
361
554
|
return length;
|
|
362
555
|
}
|
|
363
556
|
function isCallbackArrayMethod(methodName) {
|
|
@@ -392,6 +585,42 @@ function getRequiredCallback(methodName, value) {
|
|
|
392
585
|
}
|
|
393
586
|
return value;
|
|
394
587
|
}
|
|
588
|
+
function readArrayElement(value, index, options) {
|
|
589
|
+
const receiver = arrayLikeSources.get(value) ?? value;
|
|
590
|
+
return options.context?.getProperty !== undefined
|
|
591
|
+
? options.context.getProperty(receiver, index)
|
|
592
|
+
: getSandboxDataProperty(receiver, index, options.budget);
|
|
593
|
+
}
|
|
594
|
+
function writeArrayProperty(value, key, entry, options) {
|
|
595
|
+
options.budget.visitNode();
|
|
596
|
+
const receiver = arrayLikeSources.get(value) ?? value;
|
|
597
|
+
if (Array.isArray(receiver)) {
|
|
598
|
+
if (key === "length")
|
|
599
|
+
options.budget.allocateArrayLength(Number(entry));
|
|
600
|
+
else if (typeof key === "number")
|
|
601
|
+
options.budget.allocateArrayLength(key + 1);
|
|
602
|
+
}
|
|
603
|
+
if (options.setProperty !== undefined)
|
|
604
|
+
return options.setProperty(receiver, String(key), entry);
|
|
605
|
+
if (!Reflect.set(receiver, key, entry))
|
|
606
|
+
throw new TypeError(`Cannot assign to read only property '${key}'.`);
|
|
607
|
+
}
|
|
608
|
+
function deleteArrayElement(value, index, options) {
|
|
609
|
+
options.budget.visitNode();
|
|
610
|
+
const receiver = arrayLikeSources.get(value) ?? value;
|
|
611
|
+
const deleted = options.deleteProperty === undefined
|
|
612
|
+
? Reflect.deleteProperty(receiver, String(index))
|
|
613
|
+
: options.deleteProperty(receiver, String(index));
|
|
614
|
+
if (!deleted)
|
|
615
|
+
throw new TypeError(`Cannot delete property '${index}'.`);
|
|
616
|
+
}
|
|
617
|
+
async function moveArrayElement(value, from, to, options) {
|
|
618
|
+
options.budget.visitNode();
|
|
619
|
+
if (from in value)
|
|
620
|
+
await writeArrayProperty(value, to, await readArrayElement(value, from, options), options);
|
|
621
|
+
else
|
|
622
|
+
deleteArrayElement(value, to, options);
|
|
623
|
+
}
|
|
395
624
|
async function mapArray(value, callback, options, stack, thisValue) {
|
|
396
625
|
const length = value.length;
|
|
397
626
|
options.budget.allocateArrayLength(length);
|
|
@@ -403,7 +632,7 @@ async function mapArray(value, callback, options, stack, thisValue) {
|
|
|
403
632
|
if (!(index in value)) {
|
|
404
633
|
continue;
|
|
405
634
|
}
|
|
406
|
-
result[index] = await callArrayCallback(callback, value
|
|
635
|
+
result[index] = await callArrayCallback(callback, await readArrayElement(value, index, options), index, value, options, stack, thisValue);
|
|
407
636
|
}
|
|
408
637
|
return result;
|
|
409
638
|
}
|
|
@@ -421,7 +650,7 @@ async function filterArray(value, callback, options, stack, thisValue) {
|
|
|
421
650
|
if (!(index in value)) {
|
|
422
651
|
continue;
|
|
423
652
|
}
|
|
424
|
-
const entry = value
|
|
653
|
+
const entry = await readArrayElement(value, index, options);
|
|
425
654
|
if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
|
|
426
655
|
result.push(entry);
|
|
427
656
|
}
|
|
@@ -436,7 +665,7 @@ async function findInArray(value, callback, options, stack, thisValue) {
|
|
|
436
665
|
const length = value.length;
|
|
437
666
|
for (let index = 0; index < length; index += 1) {
|
|
438
667
|
options.budget.visitNode();
|
|
439
|
-
const entry =
|
|
668
|
+
const entry = await readArrayElement(value, index, options);
|
|
440
669
|
if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
|
|
441
670
|
return entry;
|
|
442
671
|
}
|
|
@@ -447,7 +676,7 @@ async function findIndexInArray(value, callback, options, stack, thisValue) {
|
|
|
447
676
|
const length = value.length;
|
|
448
677
|
for (let index = 0; index < length; index += 1) {
|
|
449
678
|
options.budget.visitNode();
|
|
450
|
-
const entry =
|
|
679
|
+
const entry = await readArrayElement(value, index, options);
|
|
451
680
|
if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
|
|
452
681
|
return index;
|
|
453
682
|
}
|
|
@@ -458,7 +687,7 @@ async function findLastInArray(value, callback, options, stack, thisValue) {
|
|
|
458
687
|
const length = value.length;
|
|
459
688
|
for (let index = length - 1; index >= 0; index -= 1) {
|
|
460
689
|
options.budget.visitNode();
|
|
461
|
-
const entry =
|
|
690
|
+
const entry = await readArrayElement(value, index, options);
|
|
462
691
|
if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
|
|
463
692
|
return entry;
|
|
464
693
|
}
|
|
@@ -469,7 +698,7 @@ async function findLastIndexInArray(value, callback, options, stack, thisValue)
|
|
|
469
698
|
const length = value.length;
|
|
470
699
|
for (let index = length - 1; index >= 0; index -= 1) {
|
|
471
700
|
options.budget.visitNode();
|
|
472
|
-
const entry =
|
|
701
|
+
const entry = await readArrayElement(value, index, options);
|
|
473
702
|
if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
|
|
474
703
|
return index;
|
|
475
704
|
}
|
|
@@ -483,7 +712,7 @@ async function someInArray(value, callback, options, stack, thisValue) {
|
|
|
483
712
|
if (!(index in value)) {
|
|
484
713
|
continue;
|
|
485
714
|
}
|
|
486
|
-
if (await callArrayCallback(callback, value
|
|
715
|
+
if (await callArrayCallback(callback, await readArrayElement(value, index, options), index, value, options, stack, thisValue)) {
|
|
487
716
|
return true;
|
|
488
717
|
}
|
|
489
718
|
}
|
|
@@ -496,7 +725,7 @@ async function everyInArray(value, callback, options, stack, thisValue) {
|
|
|
496
725
|
if (!(index in value)) {
|
|
497
726
|
continue;
|
|
498
727
|
}
|
|
499
|
-
if (!(await callArrayCallback(callback, value
|
|
728
|
+
if (!(await callArrayCallback(callback, await readArrayElement(value, index, options), index, value, options, stack, thisValue))) {
|
|
500
729
|
return false;
|
|
501
730
|
}
|
|
502
731
|
}
|
|
@@ -511,7 +740,7 @@ async function reduceArray(value, callback, hasInitialValue, initialValue, optio
|
|
|
511
740
|
if (start < 0) {
|
|
512
741
|
throw new TypeError("Reduce of empty array with no initial value.");
|
|
513
742
|
}
|
|
514
|
-
return reduceFromLeft(value, callback, value
|
|
743
|
+
return reduceFromLeft(value, callback, await readArrayElement(value, start, options), start + 1, length, options, stack);
|
|
515
744
|
}
|
|
516
745
|
async function reduceRightArray(value, callback, hasInitialValue, initialValue, options, stack) {
|
|
517
746
|
const length = value.length;
|
|
@@ -522,7 +751,7 @@ async function reduceRightArray(value, callback, hasInitialValue, initialValue,
|
|
|
522
751
|
if (start < 0) {
|
|
523
752
|
throw new TypeError("Reduce of empty array with no initial value.");
|
|
524
753
|
}
|
|
525
|
-
return reduceFromRight(value, callback, value
|
|
754
|
+
return reduceFromRight(value, callback, await readArrayElement(value, start, options), start - 1, length, options, stack);
|
|
526
755
|
}
|
|
527
756
|
async function reduceFromLeft(value, callback, accumulator, startIndex, length, options, stack) {
|
|
528
757
|
let current = accumulator;
|
|
@@ -534,7 +763,12 @@ async function reduceFromLeft(value, callback, accumulator, startIndex, length,
|
|
|
534
763
|
if (!(index in value)) {
|
|
535
764
|
continue;
|
|
536
765
|
}
|
|
537
|
-
current = await options.callClosure(callback, [
|
|
766
|
+
current = await options.callClosure(callback, [
|
|
767
|
+
current,
|
|
768
|
+
await readArrayElement(value, index, options),
|
|
769
|
+
index,
|
|
770
|
+
arrayLikeSources.get(value) ?? value
|
|
771
|
+
], stack);
|
|
538
772
|
}
|
|
539
773
|
return current;
|
|
540
774
|
}
|
|
@@ -552,7 +786,12 @@ async function reduceFromRight(value, callback, accumulator, startIndex, length,
|
|
|
552
786
|
if (!(index in value)) {
|
|
553
787
|
continue;
|
|
554
788
|
}
|
|
555
|
-
current = await options.callClosure(callback, [
|
|
789
|
+
current = await options.callClosure(callback, [
|
|
790
|
+
current,
|
|
791
|
+
await readArrayElement(value, index, options),
|
|
792
|
+
index,
|
|
793
|
+
arrayLikeSources.get(value) ?? value
|
|
794
|
+
], stack);
|
|
556
795
|
}
|
|
557
796
|
return current;
|
|
558
797
|
}
|
|
@@ -567,7 +806,7 @@ async function forEachArray(value, callback, options, stack, thisValue) {
|
|
|
567
806
|
if (!(index in value)) {
|
|
568
807
|
continue;
|
|
569
808
|
}
|
|
570
|
-
await callArrayCallback(callback, value
|
|
809
|
+
await callArrayCallback(callback, await readArrayElement(value, index, options), index, value, options, stack, thisValue);
|
|
571
810
|
}
|
|
572
811
|
}
|
|
573
812
|
async function flatMapArray(value, callback, options, stack, thisValue) {
|
|
@@ -580,14 +819,14 @@ async function flatMapArray(value, callback, options, stack, thisValue) {
|
|
|
580
819
|
if (!(index in value)) {
|
|
581
820
|
continue;
|
|
582
821
|
}
|
|
583
|
-
const mapped = await callArrayCallback(callback, value
|
|
822
|
+
const mapped = await callArrayCallback(callback, await readArrayElement(value, index, options), index, value, options, stack, thisValue);
|
|
584
823
|
if (Array.isArray(mapped)) {
|
|
585
824
|
for (let mappedIndex = 0; mappedIndex < mapped.length; mappedIndex += 1) {
|
|
586
825
|
options.budget.visitNode();
|
|
587
826
|
if (!(mappedIndex in mapped)) {
|
|
588
827
|
continue;
|
|
589
828
|
}
|
|
590
|
-
result.push(mapped
|
|
829
|
+
result.push(await readArrayElement(mapped, mappedIndex, options));
|
|
591
830
|
options.budget.allocateArrayLength(result.length);
|
|
592
831
|
}
|
|
593
832
|
continue;
|
|
@@ -601,23 +840,32 @@ async function flatMapArray(value, callback, options, stack, thisValue) {
|
|
|
601
840
|
options.budget.setRetainedValues(result, undefined);
|
|
602
841
|
}
|
|
603
842
|
}
|
|
604
|
-
function flattenArray(value, depth,
|
|
843
|
+
async function flattenArray(value, depth, options) {
|
|
605
844
|
const result = [];
|
|
606
|
-
|
|
607
|
-
|
|
845
|
+
const retained = {};
|
|
846
|
+
options.budget.setRetainedValues(retained, () => [result]);
|
|
847
|
+
try {
|
|
848
|
+
await appendFlattenedEntries(value, depth, result, options);
|
|
849
|
+
return result;
|
|
850
|
+
}
|
|
851
|
+
finally {
|
|
852
|
+
options.budget.setRetainedValues(retained, undefined);
|
|
853
|
+
}
|
|
608
854
|
}
|
|
609
|
-
function appendFlattenedEntries(value, depth, result,
|
|
610
|
-
|
|
855
|
+
async function appendFlattenedEntries(value, depth, result, options) {
|
|
856
|
+
const length = value.length;
|
|
857
|
+
for (let index = 0; index < length; index += 1) {
|
|
858
|
+
options.budget.visitNode();
|
|
611
859
|
if (!(index in value)) {
|
|
612
860
|
continue;
|
|
613
861
|
}
|
|
614
|
-
const entry = value
|
|
862
|
+
const entry = await readArrayElement(value, index, options);
|
|
615
863
|
if (depth > 0 && Array.isArray(entry)) {
|
|
616
|
-
appendFlattenedEntries(entry, depth - 1, result,
|
|
864
|
+
await appendFlattenedEntries(entry, depth - 1, result, options);
|
|
617
865
|
continue;
|
|
618
866
|
}
|
|
619
867
|
result.push(entry);
|
|
620
|
-
budget.allocateArrayLength(result.length);
|
|
868
|
+
options.budget.allocateArrayLength(result.length);
|
|
621
869
|
}
|
|
622
870
|
}
|
|
623
871
|
async function sortArray(value, comparator, options, stack) {
|
|
@@ -632,7 +880,7 @@ async function sortArray(value, comparator, options, stack) {
|
|
|
632
880
|
if (!(index in value)) {
|
|
633
881
|
continue;
|
|
634
882
|
}
|
|
635
|
-
const entry = value
|
|
883
|
+
const entry = await readArrayElement(value, index, options);
|
|
636
884
|
if (entry === undefined) {
|
|
637
885
|
undefinedCount += 1;
|
|
638
886
|
continue;
|
|
@@ -651,14 +899,14 @@ async function sortArray(value, comparator, options, stack) {
|
|
|
651
899
|
}
|
|
652
900
|
currentEntry = undefined;
|
|
653
901
|
for (let index = 0; index < definedValues.length; index += 1) {
|
|
654
|
-
value
|
|
902
|
+
await writeArrayProperty(value, index, definedValues[index], options);
|
|
655
903
|
}
|
|
656
904
|
for (let index = 0; index < undefinedCount; index += 1) {
|
|
657
|
-
value
|
|
905
|
+
await writeArrayProperty(value, definedValues.length + index, undefined, options);
|
|
658
906
|
}
|
|
659
907
|
for (let index = definedValues.length + undefinedCount; index < length; index += 1) {
|
|
660
908
|
options.budget.visitNode();
|
|
661
|
-
|
|
909
|
+
deleteArrayElement(value, index, options);
|
|
662
910
|
}
|
|
663
911
|
}
|
|
664
912
|
finally {
|
|
@@ -667,7 +915,18 @@ async function sortArray(value, comparator, options, stack) {
|
|
|
667
915
|
}
|
|
668
916
|
async function compareEntries(left, right, comparator, options, stack) {
|
|
669
917
|
options.budget.visitNode();
|
|
670
|
-
|
|
918
|
+
if (comparator === undefined) {
|
|
919
|
+
const leftString = await sandboxString(left, options.budget, options.context);
|
|
920
|
+
const release = retainValues(options.budget, () => [leftString]);
|
|
921
|
+
try {
|
|
922
|
+
const rightString = await sandboxString(right, options.budget, options.context);
|
|
923
|
+
return leftString < rightString ? -1 : leftString > rightString ? 1 : 0;
|
|
924
|
+
}
|
|
925
|
+
finally {
|
|
926
|
+
release();
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
const result = await sandboxNumber(await options.callClosure(comparator, [left, right], stack), options.budget, options.context);
|
|
671
930
|
return Number.isNaN(result) ? 0 : result;
|
|
672
931
|
}
|
|
673
932
|
async function callArrayCallback(callback, value, index, array, options, stack, thisValue) {
|