poe-code 13.0.2 → 13.0.4

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 (58) hide show
  1. package/dist/index.js +14764 -13923
  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-RIQRHKCB.js → chunk-WGS5B4OT.js} +14518 -13706
  6. package/packages/safe-js/dist/chunks/chunk-WGS5B4OT.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-JP474LP7.js → chunk-ZGWWEL43.js} +2 -2
  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.d.ts +11 -3
  11. package/packages/safe-js/dist/index.js +14 -6
  12. package/packages/safe-js/dist/index.js.map +4 -4
  13. package/packages/safe-js/dist/interp/async.d.ts +2 -0
  14. package/packages/safe-js/dist/interp/async.js +31 -15
  15. package/packages/safe-js/dist/interp/budget.d.ts +29 -0
  16. package/packages/safe-js/dist/interp/budget.js +143 -0
  17. package/packages/safe-js/dist/interp/globals/console-json.d.ts +2 -1
  18. package/packages/safe-js/dist/interp/globals/console-json.js +24 -6
  19. package/packages/safe-js/dist/interp/globals/regex.d.ts +2 -1
  20. package/packages/safe-js/dist/interp/globals/regex.js +24 -3
  21. package/packages/safe-js/dist/interp/host-bridge.d.ts +2 -1
  22. package/packages/safe-js/dist/interp/host-bridge.js +98 -55
  23. package/packages/safe-js/dist/interp/host-call.d.ts +3 -2
  24. package/packages/safe-js/dist/interp/host-call.js +91 -52
  25. package/packages/safe-js/dist/interp/interpreter.d.ts +4 -1
  26. package/packages/safe-js/dist/interp/interpreter.js +115 -79
  27. package/packages/safe-js/dist/interp/methods/array.js +136 -71
  28. package/packages/safe-js/dist/interp/methods/string.d.ts +2 -1
  29. package/packages/safe-js/dist/interp/methods/string.js +111 -100
  30. package/packages/safe-js/dist/interp/regex/compile-guard.d.ts +33 -0
  31. package/packages/safe-js/dist/interp/regex/compile-guard.js +179 -0
  32. package/packages/safe-js/dist/interp/regex/parse.d.ts +2 -1
  33. package/packages/safe-js/dist/interp/regex/parse.js +87 -16
  34. package/packages/safe-js/dist/interp/values.d.ts +7 -2
  35. package/packages/safe-js/dist/interp/values.js +91 -9
  36. package/packages/safe-js/dist/modules/registry.d.ts +2 -1
  37. package/packages/safe-js/dist/modules/registry.js +1 -0
  38. package/packages/safe-js/dist/parse/hash.d.ts +2 -1
  39. package/packages/safe-js/dist/parse/hash.js +7 -4
  40. package/packages/safe-js/dist/parse/parser.d.ts +5 -3
  41. package/packages/safe-js/dist/parse/parser.js +53 -27
  42. package/packages/safe-js/dist/parse/tokenizer.d.ts +2 -0
  43. package/packages/safe-js/dist/parse/tokenizer.js +48 -26
  44. package/packages/safe-js/dist/parse.d.ts +3 -1
  45. package/packages/safe-js/dist/parse.js +3 -1
  46. package/packages/safe-js/dist/restore.d.ts +2 -1
  47. package/packages/safe-js/dist/restore.js +3 -3
  48. package/packages/safe-js/dist/run.js +289 -263
  49. package/packages/safe-js/dist/snapshot/migration.d.ts +3 -2
  50. package/packages/safe-js/dist/snapshot/migration.js +12 -5
  51. package/packages/safe-js/dist/snapshot/replay-data.d.ts +2 -1
  52. package/packages/safe-js/dist/snapshot/replay-data.js +172 -162
  53. package/packages/safe-js/dist/snapshot/replay-inputs.d.ts +2 -1
  54. package/packages/safe-js/dist/snapshot/replay-inputs.js +18 -11
  55. package/packages/safe-js/dist/snapshot/restore.d.ts +2 -2
  56. package/packages/safe-js/dist/snapshot/restore.js +142 -113
  57. package/packages/safe-js/dist/chunks/chunk-RIQRHKCB.js.map +0 -7
  58. /package/packages/safe-js/dist/chunks/{chunk-JP474LP7.js.map → chunk-ZGWWEL43.js.map} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet } from "../values.js";
2
- import { assertCollectionMutable, enterCollectionCallback } from "../running-state.js";
2
+ import { assertCollectionMutable, enterRunningState } from "../running-state.js";
3
3
  const activeArrayCallbacks = new WeakMap();
4
4
  const arrayMethodNames = new Set([
5
5
  "map",
@@ -76,7 +76,7 @@ export async function callArrayMethod(value, methodName, args, options, stack =
76
76
  if (isCallbackArrayMethod(methodName)) {
77
77
  let callbackState = activeArrayCallbacks.get(value);
78
78
  if (callbackState === undefined) {
79
- callbackState = { depth: 0, leave: enterCollectionCallback(value) };
79
+ callbackState = { depth: 0, leave: enterRunningState(value) };
80
80
  activeArrayCallbacks.set(value, callbackState);
81
81
  }
82
82
  callbackState.depth += 1;
@@ -267,34 +267,57 @@ function getRequiredCallback(methodName, value) {
267
267
  async function mapArray(value, callback, options, stack, thisValue) {
268
268
  const length = value.length;
269
269
  const result = new Array(length);
270
- for (let index = 0; index < length; index += 1) {
271
- if (!(index in value)) {
272
- continue;
270
+ options.budget.setRetainedValues(result, () => [result]);
271
+ try {
272
+ for (let index = 0; index < length; index += 1) {
273
+ options.budget.visitNode();
274
+ if (!(index in value)) {
275
+ continue;
276
+ }
277
+ result[index] = await callArrayCallback(callback, value[index], index, value, options, stack, thisValue);
273
278
  }
274
- result[index] = await callArrayCallback(callback, value[index], index, value, options, stack, thisValue);
279
+ return result;
280
+ }
281
+ finally {
282
+ options.budget.setRetainedValues(result, undefined);
275
283
  }
276
- return result;
277
284
  }
278
285
  async function filterArray(value, callback, options, stack, thisValue) {
279
286
  const length = value.length;
280
287
  const result = [];
281
- for (let index = 0; index < length; index += 1) {
282
- if (!(index in value)) {
283
- continue;
284
- }
285
- if (await callArrayCallback(callback, value[index], index, value, options, stack, thisValue)) {
286
- result.push(value[index]);
288
+ options.budget.setRetainedValues(result, () => [result]);
289
+ try {
290
+ for (let index = 0; index < length; index += 1) {
291
+ options.budget.visitNode();
292
+ if (!(index in value)) {
293
+ continue;
294
+ }
295
+ const entry = value[index];
296
+ if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
297
+ result.push(entry);
298
+ }
287
299
  }
300
+ return result;
301
+ }
302
+ finally {
303
+ options.budget.setRetainedValues(result, undefined);
288
304
  }
289
- return result;
290
305
  }
291
306
  async function findInArray(value, callback, options, stack, thisValue) {
292
- const index = await findIndexInArray(value, callback, options, stack, thisValue);
293
- return index < 0 ? undefined : value[index];
307
+ const length = value.length;
308
+ for (let index = 0; index < length; index += 1) {
309
+ options.budget.visitNode();
310
+ const entry = index in value ? value[index] : undefined;
311
+ if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
312
+ return entry;
313
+ }
314
+ }
315
+ return undefined;
294
316
  }
295
317
  async function findIndexInArray(value, callback, options, stack, thisValue) {
296
318
  const length = value.length;
297
319
  for (let index = 0; index < length; index += 1) {
320
+ options.budget.visitNode();
298
321
  const entry = index in value ? value[index] : undefined;
299
322
  if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
300
323
  return index;
@@ -305,6 +328,7 @@ async function findIndexInArray(value, callback, options, stack, thisValue) {
305
328
  async function findLastInArray(value, callback, options, stack, thisValue) {
306
329
  const length = value.length;
307
330
  for (let index = length - 1; index >= 0; index -= 1) {
331
+ options.budget.visitNode();
308
332
  const entry = index in value ? value[index] : undefined;
309
333
  if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
310
334
  return entry;
@@ -315,6 +339,7 @@ async function findLastInArray(value, callback, options, stack, thisValue) {
315
339
  async function findLastIndexInArray(value, callback, options, stack, thisValue) {
316
340
  const length = value.length;
317
341
  for (let index = length - 1; index >= 0; index -= 1) {
342
+ options.budget.visitNode();
318
343
  const entry = index in value ? value[index] : undefined;
319
344
  if (await callArrayCallback(callback, entry, index, value, options, stack, thisValue)) {
320
345
  return index;
@@ -325,6 +350,7 @@ async function findLastIndexInArray(value, callback, options, stack, thisValue)
325
350
  async function someInArray(value, callback, options, stack, thisValue) {
326
351
  const length = value.length;
327
352
  for (let index = 0; index < length; index += 1) {
353
+ options.budget.visitNode();
328
354
  if (!(index in value)) {
329
355
  continue;
330
356
  }
@@ -337,6 +363,7 @@ async function someInArray(value, callback, options, stack, thisValue) {
337
363
  async function everyInArray(value, callback, options, stack, thisValue) {
338
364
  const length = value.length;
339
365
  for (let index = 0; index < length; index += 1) {
366
+ options.budget.visitNode();
340
367
  if (!(index in value)) {
341
368
  continue;
342
369
  }
@@ -348,10 +375,10 @@ async function everyInArray(value, callback, options, stack, thisValue) {
348
375
  }
349
376
  async function reduceArray(value, callback, hasInitialValue, initialValue, options, stack) {
350
377
  const length = value.length;
351
- const start = findNextDefinedIndex(value, 0, 1, length);
352
378
  if (hasInitialValue) {
353
- return reduceFromLeft(value, callback, initialValue, start, length, options, stack);
379
+ return reduceFromLeft(value, callback, initialValue, 0, length, options, stack);
354
380
  }
381
+ const start = findNextDefinedIndex(value, 0, 1, length, options.budget);
355
382
  if (start < 0) {
356
383
  throw new TypeError("Reduce of empty array with no initial value.");
357
384
  }
@@ -359,10 +386,10 @@ async function reduceArray(value, callback, hasInitialValue, initialValue, optio
359
386
  }
360
387
  async function reduceRightArray(value, callback, hasInitialValue, initialValue, options, stack) {
361
388
  const length = value.length;
362
- const start = findNextDefinedIndex(value, length - 1, -1, length);
363
389
  if (hasInitialValue) {
364
- return reduceFromRight(value, callback, initialValue, start, length, options, stack);
390
+ return reduceFromRight(value, callback, initialValue, length - 1, length, options, stack);
365
391
  }
392
+ const start = findNextDefinedIndex(value, length - 1, -1, length, options.budget);
366
393
  if (start < 0) {
367
394
  throw new TypeError("Reduce of empty array with no initial value.");
368
395
  }
@@ -370,27 +397,44 @@ async function reduceRightArray(value, callback, hasInitialValue, initialValue,
370
397
  }
371
398
  async function reduceFromLeft(value, callback, accumulator, startIndex, length, options, stack) {
372
399
  let current = accumulator;
373
- for (let index = startIndex; index < length; index += 1) {
374
- if (!(index in value)) {
375
- continue;
400
+ const retainedAccumulator = {};
401
+ options.budget.setRetainedValues(retainedAccumulator, () => [current]);
402
+ try {
403
+ for (let index = startIndex; index < length; index += 1) {
404
+ options.budget.visitNode();
405
+ if (!(index in value)) {
406
+ continue;
407
+ }
408
+ current = await options.callClosure(callback, [current, value[index], index, value], stack);
376
409
  }
377
- current = await options.callClosure(callback, [current, value[index], index, value], stack);
410
+ return current;
411
+ }
412
+ finally {
413
+ options.budget.setRetainedValues(retainedAccumulator, undefined);
378
414
  }
379
- return current;
380
415
  }
381
416
  async function reduceFromRight(value, callback, accumulator, startIndex, length, options, stack) {
382
417
  let current = accumulator;
383
- for (let index = Math.min(startIndex, length - 1); index >= 0; index -= 1) {
384
- if (!(index in value)) {
385
- continue;
418
+ const retainedAccumulator = {};
419
+ options.budget.setRetainedValues(retainedAccumulator, () => [current]);
420
+ try {
421
+ for (let index = Math.min(startIndex, length - 1); index >= 0; index -= 1) {
422
+ options.budget.visitNode();
423
+ if (!(index in value)) {
424
+ continue;
425
+ }
426
+ current = await options.callClosure(callback, [current, value[index], index, value], stack);
386
427
  }
387
- current = await options.callClosure(callback, [current, value[index], index, value], stack);
428
+ return current;
429
+ }
430
+ finally {
431
+ options.budget.setRetainedValues(retainedAccumulator, undefined);
388
432
  }
389
- return current;
390
433
  }
391
434
  async function forEachArray(value, callback, options, stack, thisValue) {
392
435
  const length = value.length;
393
436
  for (let index = 0; index < length; index += 1) {
437
+ options.budget.visitNode();
394
438
  if (!(index in value)) {
395
439
  continue;
396
440
  }
@@ -400,25 +444,33 @@ async function forEachArray(value, callback, options, stack, thisValue) {
400
444
  async function flatMapArray(value, callback, options, stack, thisValue) {
401
445
  const length = value.length;
402
446
  const result = [];
403
- for (let index = 0; index < length; index += 1) {
404
- if (!(index in value)) {
405
- continue;
406
- }
407
- const mapped = await callArrayCallback(callback, value[index], index, value, options, stack, thisValue);
408
- if (Array.isArray(mapped)) {
409
- for (let mappedIndex = 0; mappedIndex < mapped.length; mappedIndex += 1) {
410
- if (!(mappedIndex in mapped)) {
411
- continue;
447
+ options.budget.setRetainedValues(result, () => [result]);
448
+ try {
449
+ for (let index = 0; index < length; index += 1) {
450
+ options.budget.visitNode();
451
+ if (!(index in value)) {
452
+ continue;
453
+ }
454
+ const mapped = await callArrayCallback(callback, value[index], index, value, options, stack, thisValue);
455
+ if (Array.isArray(mapped)) {
456
+ for (let mappedIndex = 0; mappedIndex < mapped.length; mappedIndex += 1) {
457
+ options.budget.visitNode();
458
+ if (!(mappedIndex in mapped)) {
459
+ continue;
460
+ }
461
+ result.push(mapped[mappedIndex]);
462
+ options.budget.allocateArrayLength(result.length);
412
463
  }
413
- result.push(mapped[mappedIndex]);
414
- options.budget.allocateArrayLength(result.length);
464
+ continue;
415
465
  }
416
- continue;
466
+ result.push(mapped);
467
+ options.budget.allocateArrayLength(result.length);
417
468
  }
418
- result.push(mapped);
419
- options.budget.allocateArrayLength(result.length);
469
+ return result;
470
+ }
471
+ finally {
472
+ options.budget.setRetainedValues(result, undefined);
420
473
  }
421
- return result;
422
474
  }
423
475
  function flattenArray(value, depth, budget) {
424
476
  const result = [];
@@ -440,48 +492,61 @@ function appendFlattenedEntries(value, depth, result, budget) {
440
492
  }
441
493
  }
442
494
  async function sortArray(value, comparator, options, stack) {
495
+ const length = value.length;
443
496
  const definedValues = [];
444
497
  let undefinedCount = 0;
445
- for (let index = 0; index < value.length; index += 1) {
446
- if (!(index in value)) {
447
- continue;
498
+ let currentEntry;
499
+ options.budget.setRetainedValues(definedValues, () => [definedValues, currentEntry]);
500
+ try {
501
+ for (let index = 0; index < length; index += 1) {
502
+ options.budget.visitNode();
503
+ if (!(index in value)) {
504
+ continue;
505
+ }
506
+ const entry = value[index];
507
+ if (entry === undefined) {
508
+ undefinedCount += 1;
509
+ continue;
510
+ }
511
+ definedValues.push(entry);
512
+ }
513
+ for (let index = 1; index < definedValues.length; index += 1) {
514
+ currentEntry = definedValues[index];
515
+ let cursor = index - 1;
516
+ while (cursor >= 0 &&
517
+ (await compareEntries(definedValues[cursor], currentEntry, comparator, options, stack)) > 0) {
518
+ definedValues[cursor + 1] = definedValues[cursor];
519
+ cursor -= 1;
520
+ }
521
+ definedValues[cursor + 1] = currentEntry;
448
522
  }
449
- const entry = value[index];
450
- if (entry === undefined) {
451
- undefinedCount += 1;
452
- continue;
523
+ currentEntry = undefined;
524
+ for (let index = 0; index < definedValues.length; index += 1) {
525
+ value[index] = definedValues[index];
453
526
  }
454
- definedValues.push(entry);
455
- }
456
- for (let index = 1; index < definedValues.length; index += 1) {
457
- const entry = definedValues[index];
458
- let cursor = index - 1;
459
- while (cursor >= 0 &&
460
- (await compareEntries(definedValues[cursor], entry, comparator, options, stack)) > 0) {
461
- definedValues[cursor + 1] = definedValues[cursor];
462
- cursor -= 1;
527
+ for (let index = 0; index < undefinedCount; index += 1) {
528
+ value[definedValues.length + index] = undefined;
529
+ }
530
+ for (let index = definedValues.length + undefinedCount; index < length; index += 1) {
531
+ options.budget.visitNode();
532
+ delete value[index];
463
533
  }
464
- definedValues[cursor + 1] = entry;
465
- }
466
- for (let index = 0; index < definedValues.length; index += 1) {
467
- value[index] = definedValues[index];
468
- }
469
- for (let index = 0; index < undefinedCount; index += 1) {
470
- value[definedValues.length + index] = undefined;
471
534
  }
472
- for (let index = definedValues.length + undefinedCount; index < value.length; index += 1) {
473
- delete value[index];
535
+ finally {
536
+ options.budget.setRetainedValues(definedValues, undefined);
474
537
  }
475
538
  }
476
539
  async function compareEntries(left, right, comparator, options, stack) {
540
+ options.budget.visitNode();
477
541
  const result = Number(await options.callClosure(comparator, [left, right], stack));
478
542
  return Number.isNaN(result) ? 0 : result;
479
543
  }
480
544
  async function callArrayCallback(callback, value, index, array, options, stack, thisValue) {
481
545
  return options.callClosure(callback, [value, index, array], stack, thisValue);
482
546
  }
483
- function findNextDefinedIndex(value, startIndex, direction, length) {
547
+ function findNextDefinedIndex(value, startIndex, direction, length, budget) {
484
548
  for (let index = startIndex; direction > 0 ? index < length : index >= 0; index += direction) {
549
+ budget.visitNode();
485
550
  if (index in value) {
486
551
  return index;
487
552
  }
@@ -1,9 +1,10 @@
1
1
  import type { Expression } from "../../parse.js";
2
2
  import { Budget } from "../budget.js";
3
+ import { CompileScope } from "../regex/compile-guard.js";
3
4
  import { type SandboxClosure, type SandboxValue } from "../values.js";
4
5
  type StringMethodName = "at" | "charAt" | "charCodeAt" | "codePointAt" | "concat" | "endsWith" | "includes" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "matchAll" | "normalize" | "padEnd" | "padStart" | "repeat" | "replace" | "replaceAll" | "slice" | "search" | "split" | "startsWith" | "substr" | "substring" | "toLowerCase" | "toUpperCase" | "trim" | "trimEnd" | "trimStart";
5
6
  export declare function getStringMember(value: string, property: string | number, budget: Budget): SandboxValue | undefined;
6
7
  export declare function isStringMethodName(property: string | number): property is StringMethodName;
7
8
  export declare function validateStringMethodArguments(_methodName: StringMethodName, _args: readonly Expression[]): void;
8
- export declare function callStringMethod(value: string, methodName: StringMethodName, args: readonly SandboxValue[], budget: Budget, callClosure?: (closure: SandboxClosure, args: readonly SandboxValue[]) => Promise<SandboxValue>): SandboxValue | Promise<SandboxValue>;
9
+ export declare function callStringMethod(value: string, methodName: StringMethodName, args: readonly SandboxValue[], budget: Budget, callClosure?: (closure: SandboxClosure, args: readonly SandboxValue[]) => Promise<SandboxValue>, parent?: CompileScope): SandboxValue | Promise<SandboxValue>;
9
10
  export {};
@@ -1,3 +1,4 @@
1
+ import { CompileScope } from "../regex/compile-guard.js";
1
2
  import { createSandboxClosure, createSandboxRegex, deepCopyFromSandbox, isSandboxClosure, isSandboxRegex } from "../values.js";
2
3
  import { executeRegex, toMatchArray } from "./regex.js";
3
4
  const SPLIT_STRING_MESSAGE = "String#split only supports string separator values.";
@@ -47,7 +48,9 @@ export function getStringMember(value, property, budget) {
47
48
  sandbox: true,
48
49
  name: `String#${property}`,
49
50
  ...(property === "localeCompare" ? { length: 1 } : {}),
50
- call: (args) => callStringMethod(value, property, args, budget)
51
+ call: (args, context) => callStringMethod(value, property, args, budget, context === undefined
52
+ ? undefined
53
+ : async (closure, closureArgs) => await closure.call(closureArgs, { ...context, thisValue: undefined }), context?.compilation)
51
54
  });
52
55
  }
53
56
  function getStringIndex(property) {
@@ -61,106 +64,114 @@ export function isStringMethodName(property) {
61
64
  return typeof property === "string" && stringMethodNames.has(property);
62
65
  }
63
66
  export function validateStringMethodArguments(_methodName, _args) { }
64
- export function callStringMethod(value, methodName, args, budget, callClosure = async (closure, closureArgs) => await closure.call(closureArgs)) {
67
+ export function callStringMethod(value, methodName, args, budget, callClosure = async (closure, closureArgs) => await closure.call(closureArgs), parent) {
65
68
  if (methodName === "replace" || methodName === "replaceAll") {
66
69
  return callReplaceLikeMethod(value, methodName, args, budget, callClosure);
67
70
  }
68
- if (methodName === "split") {
69
- return callSplit(value, args, budget);
70
- }
71
- if (methodName === "match" || methodName === "matchAll" || methodName === "search") {
72
- return callMatchLikeMethod(value, methodName, args);
73
- }
74
- if (methodName === "localeCompare") {
75
- if (isSandboxClosure(args[0])) {
76
- throw new TypeError("String#localeCompare does not support function comparison values.");
71
+ const operation = budget.acquireCompileOwner(false, parent?.owner);
72
+ const compilation = new CompileScope(operation.owner);
73
+ try {
74
+ if (methodName === "split") {
75
+ return callSplit(value, args, budget, compilation);
77
76
  }
78
- const comparison = budget.allocateString(String(deepCopyFromSandbox(args[0])));
79
- const locales = Reflect.apply(Intl.getCanonicalLocales, Intl, [
80
- deepCopyFromSandbox(args[1])
81
- ]);
82
- const options = args[2];
83
- const nativeOptions = options === undefined || options === null
84
- ? options
85
- : Object.fromEntries([
86
- "usage",
87
- "localeMatcher",
88
- "collation",
89
- "numeric",
90
- "caseFirst",
91
- "sensitivity",
92
- "ignorePunctuation"
93
- ].map((property) => {
94
- const descriptor = Object.getOwnPropertyDescriptor(options, property);
95
- if (descriptor !== undefined && !("value" in descriptor)) {
96
- throw new TypeError("String#localeCompare only supports data option properties.");
97
- }
98
- const option = descriptor?.value;
99
- return [
100
- property,
101
- option === undefined
102
- ? undefined
103
- : property === "numeric" || property === "ignorePunctuation"
104
- ? Boolean(option)
105
- : deepCopyFromSandbox(option)
106
- ];
107
- }));
108
- return Reflect.apply(String.prototype.localeCompare, value, [
109
- comparison,
110
- locales,
111
- nativeOptions
112
- ]);
113
- }
114
- if (args.some(isSandboxClosure)) {
115
- throw new TypeError(`String#${methodName} does not support function arguments.`);
116
- }
117
- switch (methodName) {
118
- case "at": {
119
- const result = value.at(asNumber(args[0]));
120
- return result === undefined ? undefined : budget.allocateString(result);
77
+ if (methodName === "match" || methodName === "matchAll" || methodName === "search") {
78
+ return callMatchLikeMethod(value, methodName, args, compilation);
121
79
  }
122
- case "charAt":
123
- return budget.allocateString(value.charAt(asNumber(args[0])));
124
- case "charCodeAt":
125
- return value.charCodeAt(asNumber(args[0]));
126
- case "codePointAt":
127
- return value.codePointAt(asNumber(args[0]));
128
- case "concat":
129
- return budget.allocateString(value.concat(...args.map(String)));
130
- case "endsWith":
131
- return value.endsWith(String(args[0]), asNumberOrUndefined(args[1]));
132
- case "includes":
133
- return value.includes(String(args[0]), asNumberOrUndefined(args[1]));
134
- case "indexOf":
135
- return value.indexOf(String(args[0]), asNumberOrUndefined(args[1]));
136
- case "lastIndexOf":
137
- return value.lastIndexOf(String(args[0]), asNumberOrUndefined(args[1]));
138
- case "normalize":
139
- return budget.allocateString(value.normalize(asStringOrUndefined(args[0])));
140
- case "padEnd":
141
- return budget.allocateString(value.padEnd(asNumber(args[0]), asStringOrUndefined(args[1])));
142
- case "padStart":
143
- return budget.allocateString(value.padStart(asNumber(args[0]), asStringOrUndefined(args[1])));
144
- case "repeat":
145
- return budget.allocateString(value.repeat(asNumber(args[0])));
146
- case "slice":
147
- return budget.allocateString(value.slice(asNumberOrUndefined(args[0]), asNumberOrUndefined(args[1])));
148
- case "startsWith":
149
- return value.startsWith(String(args[0]), asNumberOrUndefined(args[1]));
150
- case "substr":
151
- return budget.allocateString(value.substr(asNumber(args[0]), asNumberOrUndefined(args[1])));
152
- case "substring":
153
- return budget.allocateString(value.substring(asNumber(args[0]), asNumberOrUndefined(args[1])));
154
- case "toLowerCase":
155
- return budget.allocateString(value.toLowerCase());
156
- case "toUpperCase":
157
- return budget.allocateString(value.toUpperCase());
158
- case "trim":
159
- return budget.allocateString(value.trim());
160
- case "trimEnd":
161
- return budget.allocateString(value.trimEnd());
162
- case "trimStart":
163
- return budget.allocateString(value.trimStart());
80
+ if (methodName === "localeCompare") {
81
+ if (isSandboxClosure(args[0])) {
82
+ throw new TypeError("String#localeCompare does not support function comparison values.");
83
+ }
84
+ const comparison = budget.allocateString(String(deepCopyFromSandbox(args[0], { compilation })));
85
+ const locales = Reflect.apply(Intl.getCanonicalLocales, Intl, [
86
+ deepCopyFromSandbox(args[1], { compilation })
87
+ ]);
88
+ const options = args[2];
89
+ const nativeOptions = options === undefined || options === null
90
+ ? options
91
+ : Object.fromEntries([
92
+ "usage",
93
+ "localeMatcher",
94
+ "collation",
95
+ "numeric",
96
+ "caseFirst",
97
+ "sensitivity",
98
+ "ignorePunctuation"
99
+ ].map((property) => {
100
+ const descriptor = Object.getOwnPropertyDescriptor(options, property);
101
+ if (descriptor !== undefined && !("value" in descriptor)) {
102
+ throw new TypeError("String#localeCompare only supports data option properties.");
103
+ }
104
+ const option = descriptor?.value;
105
+ return [
106
+ property,
107
+ option === undefined
108
+ ? undefined
109
+ : property === "numeric" || property === "ignorePunctuation"
110
+ ? Boolean(option)
111
+ : deepCopyFromSandbox(option, { compilation })
112
+ ];
113
+ }));
114
+ return Reflect.apply(String.prototype.localeCompare, value, [
115
+ comparison,
116
+ locales,
117
+ nativeOptions
118
+ ]);
119
+ }
120
+ if (args.some(isSandboxClosure)) {
121
+ throw new TypeError(`String#${methodName} does not support function arguments.`);
122
+ }
123
+ switch (methodName) {
124
+ case "at": {
125
+ const result = value.at(asNumber(args[0]));
126
+ return result === undefined ? undefined : budget.allocateString(result);
127
+ }
128
+ case "charAt":
129
+ return budget.allocateString(value.charAt(asNumber(args[0])));
130
+ case "charCodeAt":
131
+ return value.charCodeAt(asNumber(args[0]));
132
+ case "codePointAt":
133
+ return value.codePointAt(asNumber(args[0]));
134
+ case "concat":
135
+ return budget.allocateString(value.concat(...args.map(String)));
136
+ case "endsWith":
137
+ return value.endsWith(String(args[0]), asNumberOrUndefined(args[1]));
138
+ case "includes":
139
+ return value.includes(String(args[0]), asNumberOrUndefined(args[1]));
140
+ case "indexOf":
141
+ return value.indexOf(String(args[0]), asNumberOrUndefined(args[1]));
142
+ case "lastIndexOf":
143
+ return value.lastIndexOf(String(args[0]), asNumberOrUndefined(args[1]));
144
+ case "normalize":
145
+ return budget.allocateString(value.normalize(asStringOrUndefined(args[0])));
146
+ case "padEnd":
147
+ return budget.allocateString(value.padEnd(asNumber(args[0]), asStringOrUndefined(args[1])));
148
+ case "padStart":
149
+ return budget.allocateString(value.padStart(asNumber(args[0]), asStringOrUndefined(args[1])));
150
+ case "repeat":
151
+ return budget.allocateString(value.repeat(asNumber(args[0])));
152
+ case "slice":
153
+ return budget.allocateString(value.slice(asNumberOrUndefined(args[0]), asNumberOrUndefined(args[1])));
154
+ case "startsWith":
155
+ return value.startsWith(String(args[0]), asNumberOrUndefined(args[1]));
156
+ case "substr":
157
+ return budget.allocateString(value.substr(asNumber(args[0]), asNumberOrUndefined(args[1])));
158
+ case "substring":
159
+ return budget.allocateString(value.substring(asNumber(args[0]), asNumberOrUndefined(args[1])));
160
+ case "toLowerCase":
161
+ return budget.allocateString(value.toLowerCase());
162
+ case "toUpperCase":
163
+ return budget.allocateString(value.toUpperCase());
164
+ case "trim":
165
+ return budget.allocateString(value.trim());
166
+ case "trimEnd":
167
+ return budget.allocateString(value.trimEnd());
168
+ case "trimStart":
169
+ return budget.allocateString(value.trimStart());
170
+ }
171
+ }
172
+ finally {
173
+ compilation.dispose();
174
+ operation.release();
164
175
  }
165
176
  }
166
177
  function callReplaceLikeMethod(value, methodName, args, budget, callClosure) {
@@ -276,12 +287,12 @@ function findReplacementOffsets(value, searchValue, replaceAll) {
276
287
  }
277
288
  return offsets;
278
289
  }
279
- function callSplit(value, args, budget) {
290
+ function callSplit(value, args, budget, compilation) {
280
291
  if (args.some(isSandboxClosure))
281
292
  throw new TypeError("String#split does not support function arguments.");
282
293
  if (isSandboxRegex(args[0])) {
283
294
  const regex = args[0];
284
- const splitter = createSandboxRegex(regex.source, regex.flags.includes("g") ? regex.flags : `${regex.flags}g`);
295
+ const splitter = createSandboxRegex(regex.source, regex.flags.includes("g") ? regex.flags : `${regex.flags}g`, 0, compilation);
285
296
  const limit = asNumberOrUndefined(args[1]) ?? 2 ** 32 - 1;
286
297
  const result = [];
287
298
  let copiedThrough = 0;
@@ -313,7 +324,7 @@ function callSplit(value, args, budget) {
313
324
  budget.allocateArrayLength(result.length);
314
325
  return result;
315
326
  }
316
- function callMatchLikeMethod(value, methodName, args) {
327
+ function callMatchLikeMethod(value, methodName, args, compilation) {
317
328
  const regex = args[0];
318
329
  if (!isSandboxRegex(regex))
319
330
  throw new TypeError(`String#${methodName} requires a regex argument.`);
@@ -331,7 +342,7 @@ function callMatchLikeMethod(value, methodName, args) {
331
342
  if (methodName === "match")
332
343
  regex.lastIndex = 0;
333
344
  const matcher = methodName === "matchAll"
334
- ? createSandboxRegex(regex.source, regex.flags, regex.lastIndex)
345
+ ? createSandboxRegex(regex.source, regex.flags, regex.lastIndex, compilation)
335
346
  : regex;
336
347
  const matches = collectRegexMatches(matcher, value, true);
337
348
  if (methodName === "match" && matches.length === 0)
@@ -0,0 +1,33 @@
1
+ import { type CompileOwner, type CompileTicket } from "../budget.js";
2
+ import type { RegexPattern } from "./parse.js";
3
+ export declare class CompileScope {
4
+ readonly owner?: CompileOwner | undefined;
5
+ readonly parent?: CompileScope | undefined;
6
+ readonly tickets: Set<CompileTicket>;
7
+ closed: boolean;
8
+ constructor(owner?: CompileOwner | undefined, parent?: CompileScope | undefined);
9
+ forward(tickets: Iterable<CompileTicket>, parent: CompileScope): void;
10
+ dispose(): void;
11
+ }
12
+ export declare class RegexCompileGuard {
13
+ private readonly scope?;
14
+ private allocations;
15
+ private depth;
16
+ private readonly ticket?;
17
+ private retained;
18
+ constructor(scope?: CompileScope | undefined);
19
+ checkLength(length: number, flags?: boolean): void;
20
+ work(units: number): void;
21
+ allocate(units: number): void;
22
+ array(length: number): void;
23
+ enterGroup(): void;
24
+ leaveGroup(): void;
25
+ preflight(source: string, flags: string): void;
26
+ retain(pattern: RegexPattern, valueUnits?: number): void;
27
+ retainScratch(): void;
28
+ close(): void;
29
+ }
30
+ export declare function regexCompiledData(pattern: RegexPattern): {
31
+ units: number;
32
+ ticket?: CompileTicket;
33
+ };