poe-code 14.0.64 → 14.0.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/index.js +8832 -7273
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-3EXDJDQM.js → chunk-MKTUVVEB.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-XO7B4P27.js → chunk-YKISHTWT.js} +4474 -2978
  7. package/packages/safe-js/dist/chunks/chunk-YKISHTWT.js.map +7 -0
  8. package/packages/safe-js/dist/cli.js +2 -2
  9. package/packages/safe-js/dist/core.js +1 -1
  10. package/packages/safe-js/dist/index.js +2 -2
  11. package/packages/safe-js/dist/index.js.map +1 -1
  12. package/packages/safe-js/dist/interp/accessors.d.ts +9 -0
  13. package/packages/safe-js/dist/interp/accessors.js +68 -0
  14. package/packages/safe-js/dist/interp/async.d.ts +13 -2
  15. package/packages/safe-js/dist/interp/async.js +19 -9
  16. package/packages/safe-js/dist/interp/classes.d.ts +4 -0
  17. package/packages/safe-js/dist/interp/classes.js +182 -0
  18. package/packages/safe-js/dist/interp/exceptions.d.ts +1 -1
  19. package/packages/safe-js/dist/interp/exceptions.js +51 -9
  20. package/packages/safe-js/dist/interp/globals/collections.js +53 -16
  21. package/packages/safe-js/dist/interp/globals/console-json.js +41 -26
  22. package/packages/safe-js/dist/interp/globals/object-array.d.ts +2 -1
  23. package/packages/safe-js/dist/interp/globals/object-array.js +230 -69
  24. package/packages/safe-js/dist/interp/globals/object.js +13 -2
  25. package/packages/safe-js/dist/interp/globals/primitives.js +16 -5
  26. package/packages/safe-js/dist/interp/interpreter.d.ts +2 -2
  27. package/packages/safe-js/dist/interp/interpreter.js +168 -61
  28. package/packages/safe-js/dist/interp/iteration.js +13 -0
  29. package/packages/safe-js/dist/interp/methods/array.d.ts +1 -1
  30. package/packages/safe-js/dist/interp/methods/array.js +368 -109
  31. package/packages/safe-js/dist/interp/methods/function.d.ts +1 -1
  32. package/packages/safe-js/dist/interp/methods/function.js +56 -20
  33. package/packages/safe-js/dist/interp/methods/regex.js +1 -1
  34. package/packages/safe-js/dist/interp/object-model.d.ts +1 -0
  35. package/packages/safe-js/dist/interp/object-model.js +48 -10
  36. package/packages/safe-js/dist/interp/patterns.d.ts +3 -3
  37. package/packages/safe-js/dist/interp/patterns.js +36 -17
  38. package/packages/safe-js/dist/interp/promise.d.ts +1 -1
  39. package/packages/safe-js/dist/interp/promise.js +103 -63
  40. package/packages/safe-js/dist/interp/string-coercion.d.ts +1 -0
  41. package/packages/safe-js/dist/interp/string-coercion.js +47 -37
  42. package/packages/safe-js/dist/interp/values.d.ts +4 -2
  43. package/packages/safe-js/dist/interp/values.js +26 -1
  44. package/packages/safe-js/dist/lint/class-elements.d.ts +5 -0
  45. package/packages/safe-js/dist/lint/class-elements.js +20 -0
  46. package/packages/safe-js/dist/lint/rules/AS-async-not-needed.js +16 -0
  47. package/packages/safe-js/dist/lint/rules/AS-destructure-null-default.js +9 -0
  48. package/packages/safe-js/dist/lint/rules/AS-export-import-meta.js +9 -0
  49. package/packages/safe-js/dist/lint/rules/AS-floating-promise.js +9 -0
  50. package/packages/safe-js/dist/lint/rules/AS-frontmatter-field-unused.js +9 -0
  51. package/packages/safe-js/dist/lint/rules/AS-jsdoc-type.js +9 -0
  52. package/packages/safe-js/dist/lint/rules/AS-large-literal.js +9 -0
  53. package/packages/safe-js/dist/lint/rules/AS-missing-async.js +16 -0
  54. package/packages/safe-js/dist/lint/rules/AS-mutating-frozen.js +9 -0
  55. package/packages/safe-js/dist/lint/rules/AS-needless-template.js +9 -0
  56. package/packages/safe-js/dist/lint/rules/AS-shadow-global.js +12 -0
  57. package/packages/safe-js/dist/lint/rules/AS-unbounded-loop.js +11 -0
  58. package/packages/safe-js/dist/lint/rules/AS-unreachable.js +9 -0
  59. package/packages/safe-js/dist/lint/rules/AS-unused-import.js +16 -2
  60. package/packages/safe-js/dist/lint/rules/AS001.js +0 -1
  61. package/packages/safe-js/dist/lint/rules/AS003.js +16 -2
  62. package/packages/safe-js/dist/lint/rules/AS006-007.js +16 -2
  63. package/packages/safe-js/dist/lint/rules/AS009.js +20 -1
  64. package/packages/safe-js/dist/lint/rules/AS010.js +9 -0
  65. package/packages/safe-js/dist/lint/rules/AS011.js +9 -0
  66. package/packages/safe-js/dist/lint/rules/AS015.js +9 -0
  67. package/packages/safe-js/dist/parse/function-source.d.ts +2 -2
  68. package/packages/safe-js/dist/parse/parser.d.ts +42 -2
  69. package/packages/safe-js/dist/parse/parser.js +208 -35
  70. package/packages/safe-js/dist/parse/tokenizer.js +3 -0
  71. package/packages/safe-js/dist/parse.d.ts +1 -1
  72. package/packages/safe-js/dist/chunks/chunk-XO7B4P27.js.map +0 -7
  73. /package/packages/safe-js/dist/chunks/{chunk-3EXDJDQM.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, toIntegerOrInfinity(args[0] ?? 1), options.budget), options.budget);
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
- return Reflect.apply(Array.prototype.indexOf, value, [...args]);
200
- case "lastIndexOf":
201
- return Reflect.apply(Array.prototype.lastIndexOf, value, [...args]);
202
- case "join":
203
- return options.budget.allocateString(Reflect.apply(Array.prototype.join, value, [...args]));
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 ? length : toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context));
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
- for (let index = 0; index < count; index += 1) {
214
- options.budget.visitNode();
215
- if (first + index in value)
216
- result[index] = value[first + index];
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 removed = Reflect.apply(Array.prototype.splice, value, [...args]);
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
- Reflect.apply(Array.prototype.fill, value, [...args]);
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
- case "copyWithin":
233
- Reflect.apply(Array.prototype.copyWithin, value, [...args]);
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
- case "at":
237
- return budgetProducedValue(Reflect.apply(Array.prototype.at, value, [...args]), options.budget);
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
- if (args[0] === undefined) {
240
- Reflect.apply(Array.prototype.sort, value, []);
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
- await sortArray(value, getRequiredCallback(methodName, args[0]), options, stack);
245
- budgetProducedValue(value, options.budget);
246
- return value;
247
- case "reverse":
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
- for (let index = 0; index < length; index += 1) {
256
- options.budget.visitNode();
257
- result[index] = value[index];
258
- }
259
- if (args[0] === undefined) {
260
- result.sort();
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
- else {
263
- await sortArray(result, getRequiredCallback(methodName, args[0]), options, stack);
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
- for (let index = 0; index < length; index += 1) {
272
- options.budget.visitNode();
273
- result[index] = value[length - index - 1];
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 ? 0 : args.length === 1 ? length - first
283
- : Math.min(Math.max(toIntegerOrInfinity(await sandboxNumber(args[1], options.budget, options.context)), 0), length - first);
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
- for (let index = 0; index < resultLength; index += 1) {
290
- options.budget.visitNode();
291
- result[index] = index < first ? value[index]
292
- : index < first + inserted ? args[index - first + 2]
293
- : value[index - inserted + deleted];
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(args[0]);
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
- for (let position = 0; position < length; position += 1) {
307
- options.budget.visitNode();
308
- result[position] =
309
- position === actualIndex
310
- ? args[1]
311
- : Array.isArray(value) && !Object.hasOwn(value, position)
312
- ? undefined
313
- : value[position];
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
- return budgetProducedValue(Reflect.apply(Array.prototype.pop, value, []), options.budget);
324
- case "shift":
325
- return budgetProducedValue(Reflect.apply(Array.prototype.shift, value, []), options.budget);
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[length++] = value;
536
+ await writeArrayProperty(target, length++, value, options);
339
537
  }
340
- target.length = length;
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
- if (index in target) {
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[index] = values[index];
551
+ await writeArrayProperty(target, index, values[index], options);
359
552
  }
360
- target.length = length;
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[index], index, value, options, stack, thisValue);
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[index];
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 = index in value ? value[index] : undefined;
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 = index in value ? value[index] : undefined;
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 = index in value ? value[index] : undefined;
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 = index in value ? value[index] : undefined;
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[index], index, value, options, stack, thisValue)) {
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[index], index, value, options, stack, thisValue))) {
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[start], start + 1, length, options, stack);
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[start], start - 1, length, options, stack);
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, [current, value[index], index, arrayLikeSources.get(value) ?? value], stack);
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, [current, value[index], index, arrayLikeSources.get(value) ?? value], stack);
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[index], index, value, options, stack, thisValue);
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[index], index, value, options, stack, thisValue);
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[mappedIndex]);
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, budget) {
843
+ async function flattenArray(value, depth, options) {
605
844
  const result = [];
606
- appendFlattenedEntries(value, depth, result, budget);
607
- return result;
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, budget) {
610
- for (let index = 0; index < value.length; index += 1) {
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[index];
862
+ const entry = await readArrayElement(value, index, options);
615
863
  if (depth > 0 && Array.isArray(entry)) {
616
- appendFlattenedEntries(entry, depth - 1, result, budget);
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[index];
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[index] = definedValues[index];
902
+ await writeArrayProperty(value, index, definedValues[index], options);
655
903
  }
656
904
  for (let index = 0; index < undefinedCount; index += 1) {
657
- value[definedValues.length + index] = undefined;
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
- delete value[index];
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
- const result = Number(await options.callClosure(comparator, [left, right], stack));
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) {