tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RegExp.js (34977B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // https://github.com/tc39/ecma262/pull/2418 22.2.6.4 get RegExp.prototype.flags
      6 // https://arai-a.github.io/ecma262-compare/?pr=2418&id=sec-get-regexp.prototype.flags
      7 // Uncloned functions with `$` prefix are allocated as extended function
      8 // to store the original name in `SetCanonicalName`.
      9 function $RegExpFlagsGetter() {
     10  // Steps 1-2.
     11  var R = this;
     12  if (!IsObject(R)) {
     13    ThrowTypeError(JSMSG_OBJECT_REQUIRED, R === null ? "null" : typeof R);
     14  }
     15 
     16  // Step 3.
     17  var result = "";
     18 
     19  // Steps 4-5.
     20  if (R.hasIndices) {
     21    result += "d";
     22  }
     23 
     24  // Steps 6-7.
     25  if (R.global) {
     26    result += "g";
     27  }
     28 
     29  // Steps 8-9.
     30  if (R.ignoreCase) {
     31    result += "i";
     32  }
     33 
     34  // Steps 10-11.
     35  if (R.multiline) {
     36    result += "m";
     37  }
     38 
     39  // Steps 12-13.
     40  if (R.dotAll) {
     41    result += "s";
     42  }
     43 
     44  // Steps 14-15.
     45  if (R.unicode) {
     46    result += "u";
     47  }
     48 
     49  // Steps 16-17.
     50  if (R.unicodeSets) {
     51    result += "v";
     52  }
     53 
     54  // Steps 18-19
     55  if (R.sticky) {
     56    result += "y";
     57  }
     58 
     59  // Step 20.
     60  return result;
     61 }
     62 SetCanonicalName($RegExpFlagsGetter, "get flags");
     63 
     64 // ES 2017 draft 40edb3a95a475c1b251141ac681b8793129d9a6d 21.2.5.14.
     65 function $RegExpToString() {
     66  // Step 1.
     67  var R = this;
     68 
     69  // Step 2.
     70  if (!IsObject(R)) {
     71    ThrowTypeError(JSMSG_OBJECT_REQUIRED, R === null ? "null" : typeof R);
     72  }
     73 
     74  // Step 3.
     75  var pattern = ToString(R.source);
     76 
     77  // Step 4.
     78  var flags = ToString(R.flags);
     79 
     80  // Steps 5-6.
     81  return "/" + pattern + "/" + flags;
     82 }
     83 SetCanonicalName($RegExpToString, "toString");
     84 
     85 // ES 2016 draft Mar 25, 2016 21.2.5.2.3.
     86 function AdvanceStringIndex(S, index) {
     87  // Step 1.
     88  assert(typeof S === "string", "Expected string as 1st argument");
     89 
     90  // Step 2.
     91  assert(
     92    index >= 0 && index <= MAX_NUMERIC_INDEX,
     93    "Expected integer as 2nd argument"
     94  );
     95 
     96  // Step 3 (skipped).
     97 
     98  // Step 4 (skipped).
     99 
    100  // Steps 5-11.
    101  var supplementary = (
    102    index < S.length &&
    103    callFunction(std_String_codePointAt, S, index) > 0xffff
    104  );
    105  return index + 1 + supplementary;
    106 }
    107 
    108 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
    109 // 22.2.5.8 RegExp.prototype [ @@match ] ( string )
    110 function RegExpMatch(string) {
    111  // Step 1.
    112  var rx = this;
    113 
    114  // Step 2.
    115  if (!IsObject(rx)) {
    116    ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
    117  }
    118 
    119  // Step 3.
    120  var S = ToString(string);
    121 
    122  // Optimized paths for simple cases.
    123  if (IsOptimizableRegExpObject(rx)) {
    124    // Step 4.
    125    var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
    126    var global = !!(flags & REGEXP_GLOBAL_FLAG);
    127 
    128    if (global) {
    129      // Step 6.a.
    130      var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG) || !!(flags & REGEXP_UNICODESETS_FLAG);
    131 
    132      // Steps 6.b-e.
    133      return RegExpGlobalMatchOpt(rx, S, fullUnicode);
    134    }
    135 
    136    // Step 5.
    137    return RegExpBuiltinExec(rx, S);
    138  }
    139 
    140  // Stes 4-6
    141  return RegExpMatchSlowPath(rx, S);
    142 }
    143 
    144 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
    145 // 22.2.5.8 RegExp.prototype [ @@match ] ( string )
    146 // Steps 4-6
    147 function RegExpMatchSlowPath(rx, S) {
    148  // Step 4.
    149  var flags = ToString(rx.flags);
    150 
    151  // Step 5.
    152  if (!callFunction(std_String_includes, flags, "g")) {
    153    return RegExpExec(rx, S);
    154  }
    155 
    156  // Step 6.a.
    157  var fullUnicode = callFunction(std_String_includes, flags, "u") || callFunction(std_String_includes, flags, "v");
    158 
    159  // Step 6.b.
    160  rx.lastIndex = 0;
    161 
    162  // Step 6.c.
    163  var A = [];
    164 
    165  // Step 6.d.
    166  var n = 0;
    167 
    168  // Step 6.e.
    169  while (true) {
    170    // Step 6.e.i.
    171    var result = RegExpExec(rx, S);
    172 
    173    // Step 6.e.ii.
    174    if (result === null) {
    175      return n === 0 ? null : A;
    176    }
    177 
    178    // Step 6.e.iii.1.
    179    var matchStr = ToString(result[0]);
    180 
    181    // Step 6.e.iii.2.
    182    DefineDataProperty(A, n, matchStr);
    183 
    184    // Step 6.e.iii.3.
    185    if (matchStr === "") {
    186      var lastIndex = ToLength(rx.lastIndex);
    187      rx.lastIndex = fullUnicode
    188        ? AdvanceStringIndex(S, lastIndex)
    189        : lastIndex + 1;
    190    }
    191 
    192    // Step 6.e.iii.4.
    193    n++;
    194  }
    195 }
    196 
    197 // ES 2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.6.
    198 // Steps 6.b-e.
    199 // Optimized path for @@match with global flag.
    200 function RegExpGlobalMatchOpt(rx, S, fullUnicode) {
    201  // Step 6.b.
    202  var lastIndex = 0;
    203  rx.lastIndex = 0;
    204 
    205  // Step 6.c.
    206  var A = [];
    207 
    208  // Step 6.d.
    209  var n = 0;
    210 
    211  var lengthS = S.length;
    212 
    213  // Step 6.e.
    214  while (true) {
    215    // Step 6.e.i.
    216    var position = RegExpSearcher(rx, S, lastIndex);
    217 
    218    // Step 6.e.ii.
    219    if (position === -1) {
    220      return n === 0 ? null : A;
    221    }
    222 
    223    lastIndex = RegExpSearcherLastLimit(S);
    224 
    225    // Step 6.e.iii.1.
    226    var matchStr = Substring(S, position, lastIndex - position);
    227 
    228    // Step 6.e.iii.2.
    229    DefineDataProperty(A, n, matchStr);
    230 
    231    // Step 6.e.iii.4.
    232    if (matchStr === "") {
    233      lastIndex = fullUnicode
    234        ? AdvanceStringIndex(S, lastIndex)
    235        : lastIndex + 1;
    236      if (lastIndex > lengthS) {
    237        return A;
    238      }
    239    }
    240 
    241    // Step 6.e.iii.5.
    242    n++;
    243  }
    244 }
    245 
    246 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
    247 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
    248 function RegExpReplace(string, replaceValue) {
    249  // Step 1.
    250  var rx = this;
    251 
    252  // Step 2.
    253  if (!IsObject(rx)) {
    254    ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
    255  }
    256 
    257  // Step 3.
    258  var S = ToString(string);
    259 
    260  // Step 4.
    261  var lengthS = S.length;
    262 
    263  // Step 5.
    264  var functionalReplace = IsCallable(replaceValue);
    265 
    266  // Step 6.
    267  var firstDollarIndex = -1;
    268  if (!functionalReplace) {
    269    // Step 6.a.
    270    replaceValue = ToString(replaceValue);
    271 
    272    // Skip if replaceValue is an empty string or a single character.
    273    // A single character string may contain "$", but that cannot be a
    274    // substitution.
    275    if (replaceValue.length > 1) {
    276      firstDollarIndex = GetFirstDollarIndex(replaceValue);
    277    }
    278  }
    279 
    280  // Optimized paths.
    281  if (IsOptimizableRegExpObject(rx)) {
    282    // Step 7.
    283    var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
    284 
    285    // Step 9.
    286    var global = !!(flags & REGEXP_GLOBAL_FLAG);
    287 
    288    // Steps 9-17.
    289    if (global) {
    290      if (functionalReplace) {
    291        // For large strings check if the replacer function is
    292        // applicable for the elem-base optimization.
    293        if (lengthS > 5000) {
    294          var elemBase = GetElemBaseForLambda(replaceValue);
    295          if (IsObject(elemBase)) {
    296            return RegExpGlobalReplaceOptElemBase(
    297              rx,
    298              S,
    299              lengthS,
    300              replaceValue,
    301              flags,
    302              elemBase
    303            );
    304          }
    305        }
    306        return RegExpGlobalReplaceOptFunc(rx, S, lengthS, replaceValue, flags);
    307      }
    308      if (firstDollarIndex !== -1) {
    309        return RegExpGlobalReplaceOptSubst(
    310          rx,
    311          S,
    312          lengthS,
    313          replaceValue,
    314          flags,
    315          firstDollarIndex
    316        );
    317      }
    318      return RegExpGlobalReplaceOptSimple(rx, S, lengthS, replaceValue, flags);
    319    }
    320 
    321    if (functionalReplace) {
    322      return RegExpLocalReplaceOptFunc(rx, S, lengthS, replaceValue);
    323    }
    324    if (firstDollarIndex !== -1) {
    325      return RegExpLocalReplaceOptSubst(
    326        rx,
    327        S,
    328        lengthS,
    329        replaceValue,
    330        firstDollarIndex
    331      );
    332    }
    333    return RegExpLocalReplaceOptSimple(rx, S, lengthS, replaceValue);
    334  }
    335 
    336  // Steps 7-17.
    337  return RegExpReplaceSlowPath(
    338    rx,
    339    S,
    340    lengthS,
    341    replaceValue,
    342    functionalReplace,
    343    firstDollarIndex
    344  );
    345 }
    346 
    347 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
    348 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
    349 // Steps 7-17.
    350 // Slow path for @@replace.
    351 function RegExpReplaceSlowPath(
    352  rx,
    353  S,
    354  lengthS,
    355  replaceValue,
    356  functionalReplace,
    357  firstDollarIndex
    358 ) {
    359  // Step 7.
    360  var flags = ToString(rx.flags);
    361 
    362  // Step 8.
    363  var global = callFunction(std_String_includes, flags, "g");
    364 
    365  // Step 9.
    366  var fullUnicode = false;
    367  if (global) {
    368    // Step 9.a.
    369    fullUnicode = callFunction(std_String_includes, flags, "u") || callFunction(std_String_includes, flags, "v");
    370 
    371    // Step 9.b.
    372    rx.lastIndex = 0;
    373  }
    374 
    375  // Step 10.
    376  var results = new_List();
    377  var nResults = 0;
    378 
    379  // Steps 11-12.
    380  while (true) {
    381    // Step 12.a.
    382    var result = RegExpExec(rx, S);
    383 
    384    // Step 12.b.
    385    if (result === null) {
    386      break;
    387    }
    388 
    389    // Step 12.c.i.
    390    DefineDataProperty(results, nResults++, result);
    391 
    392    // Step 12.c.ii.
    393    if (!global) {
    394      break;
    395    }
    396 
    397    // Step 12.c.iii.1.
    398    var matchStr = ToString(result[0]);
    399 
    400    // Step 12.c.iii.2.
    401    if (matchStr === "") {
    402      var lastIndex = ToLength(rx.lastIndex);
    403      rx.lastIndex = fullUnicode
    404        ? AdvanceStringIndex(S, lastIndex)
    405        : lastIndex + 1;
    406    }
    407  }
    408 
    409  // Step 13.
    410  var accumulatedResult = "";
    411 
    412  // Step 14.
    413  var nextSourcePosition = 0;
    414 
    415  // Step 15.
    416  for (var i = 0; i < nResults; i++) {
    417    result = results[i];
    418 
    419    // Steps 15.a-b.
    420    var nCaptures = std_Math_max(ToLength(result.length) - 1, 0);
    421 
    422    // Step 15.c.
    423    var matched = ToString(result[0]);
    424 
    425    // Step 15.d.
    426    var matchLength = matched.length;
    427 
    428    // Steps 15.e-f.
    429    var position = std_Math_max(
    430      std_Math_min(ToInteger(result.index), lengthS),
    431      0
    432    );
    433 
    434    var replacement;
    435    if (functionalReplace || firstDollarIndex !== -1) {
    436      // Steps 15.g-l.
    437      replacement = RegExpGetComplexReplacement(
    438        result,
    439        matched,
    440        S,
    441        position,
    442        nCaptures,
    443        replaceValue,
    444        functionalReplace,
    445        firstDollarIndex
    446      );
    447    } else {
    448      // Steps 15.g, 15.i, 15.i.iv.
    449      // We don't need captures array, but ToString is visible to script.
    450      for (var n = 1; n <= nCaptures; n++) {
    451        // Steps 15.i.i-ii.
    452        var capN = result[n];
    453 
    454        // Step 15.i.ii.
    455        if (capN !== undefined) {
    456          ToString(capN);
    457        }
    458      }
    459 
    460      // Steps 15.j, 15.l.i.
    461      // We don't need namedCaptures, but ToObject is visible to script.
    462      var namedCaptures = result.groups;
    463      if (namedCaptures !== undefined) {
    464        ToObject(namedCaptures);
    465      }
    466 
    467      // Step 15.l.ii.
    468      replacement = replaceValue;
    469    }
    470 
    471    // Step 15.m.
    472    if (position >= nextSourcePosition) {
    473      // Step 15.m.ii.
    474      accumulatedResult +=
    475        Substring(S, nextSourcePosition, position - nextSourcePosition) +
    476        replacement;
    477 
    478      // Step 15.m.iii.
    479      nextSourcePosition = position + matchLength;
    480    }
    481  }
    482 
    483  // Step 16.
    484  if (nextSourcePosition >= lengthS) {
    485    return accumulatedResult;
    486  }
    487 
    488  // Step 17.
    489  return (
    490    accumulatedResult +
    491    Substring(S, nextSourcePosition, lengthS - nextSourcePosition)
    492  );
    493 }
    494 
    495 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
    496 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
    497 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
    498 // Steps 15.g-l.
    499 // Calculates functional/substitution replacement from match result.
    500 // Used in the following functions:
    501 //   * RegExpReplaceSlowPath
    502 function RegExpGetComplexReplacement(
    503  result,
    504  matched,
    505  S,
    506  position,
    507  nCaptures,
    508  replaceValue,
    509  functionalReplace,
    510  firstDollarIndex
    511 ) {
    512  // Step 15.g.
    513  var captures = new_List();
    514  var capturesLength = 0;
    515 
    516  // Step 15.k.i (reordered).
    517  DefineDataProperty(captures, capturesLength++, matched);
    518 
    519  // Steps 15.h, 15.i, 15.i.v.
    520  for (var n = 1; n <= nCaptures; n++) {
    521    // Step 15.i.i.
    522    var capN = result[n];
    523 
    524    // Step 15.i.ii.
    525    if (capN !== undefined) {
    526      capN = ToString(capN);
    527    }
    528 
    529    // Step 15.i.iii.
    530    DefineDataProperty(captures, capturesLength++, capN);
    531  }
    532 
    533  // Step 15.j.
    534  var namedCaptures = result.groups;
    535 
    536  // Step 15.k.
    537  if (functionalReplace) {
    538    // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise
    539    // use `std_Function_apply` with all arguments stored in `captures`.
    540    if (namedCaptures === undefined) {
    541      switch (nCaptures) {
    542        case 0:
    543          return ToString(
    544            callContentFunction(
    545              replaceValue,
    546              undefined,
    547              SPREAD(captures, 1),
    548              position,
    549              S
    550            )
    551          );
    552        case 1:
    553          return ToString(
    554            callContentFunction(
    555              replaceValue,
    556              undefined,
    557              SPREAD(captures, 2),
    558              position,
    559              S
    560            )
    561          );
    562        case 2:
    563          return ToString(
    564            callContentFunction(
    565              replaceValue,
    566              undefined,
    567              SPREAD(captures, 3),
    568              position,
    569              S
    570            )
    571          );
    572        case 3:
    573          return ToString(
    574            callContentFunction(
    575              replaceValue,
    576              undefined,
    577              SPREAD(captures, 4),
    578              position,
    579              S
    580            )
    581          );
    582        case 4:
    583          return ToString(
    584            callContentFunction(
    585              replaceValue,
    586              undefined,
    587              SPREAD(captures, 5),
    588              position,
    589              S
    590            )
    591          );
    592      }
    593    }
    594 
    595    // Steps 15.k.ii-vi.
    596    DefineDataProperty(captures, capturesLength++, position);
    597    DefineDataProperty(captures, capturesLength++, S);
    598    if (namedCaptures !== undefined) {
    599      DefineDataProperty(captures, capturesLength++, namedCaptures);
    600    }
    601    return ToString(
    602      callFunction(std_Function_apply, replaceValue, undefined, captures)
    603    );
    604  }
    605 
    606  // Step 15.l.
    607  if (namedCaptures !== undefined) {
    608    namedCaptures = ToObject(namedCaptures);
    609  }
    610  return RegExpGetSubstitution(
    611    captures,
    612    S,
    613    position,
    614    replaceValue,
    615    firstDollarIndex,
    616    namedCaptures
    617  );
    618 }
    619 
    620 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
    621 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
    622 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
    623 // Steps 15.g-k.
    624 // Calculates functional replacement from match result.
    625 // Used in the following functions:
    626 //   * RegExpGlobalReplaceOptFunc
    627 //   * RegExpGlobalReplaceOptElemBase
    628 //   * RegExpLocalReplaceOptFunc
    629 function RegExpGetFunctionalReplacement(result, S, position, replaceValue) {
    630  // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise
    631  // use `std_Function_apply` with all arguments stored in `captures`.
    632  assert(result.length >= 1, "RegExpMatcher doesn't return an empty array");
    633  var nCaptures = result.length - 1;
    634 
    635  // Step 15.j (reordered)
    636  var namedCaptures = result.groups;
    637 
    638  if (namedCaptures === undefined) {
    639    switch (nCaptures) {
    640      case 0:
    641        return ToString(
    642          callContentFunction(
    643            replaceValue,
    644            undefined,
    645            SPREAD(result, 1),
    646            position,
    647            S
    648          )
    649        );
    650      case 1:
    651        return ToString(
    652          callContentFunction(
    653            replaceValue,
    654            undefined,
    655            SPREAD(result, 2),
    656            position,
    657            S
    658          )
    659        );
    660      case 2:
    661        return ToString(
    662          callContentFunction(
    663            replaceValue,
    664            undefined,
    665            SPREAD(result, 3),
    666            position,
    667            S
    668          )
    669        );
    670      case 3:
    671        return ToString(
    672          callContentFunction(
    673            replaceValue,
    674            undefined,
    675            SPREAD(result, 4),
    676            position,
    677            S
    678          )
    679        );
    680      case 4:
    681        return ToString(
    682          callContentFunction(
    683            replaceValue,
    684            undefined,
    685            SPREAD(result, 5),
    686            position,
    687            S
    688          )
    689        );
    690    }
    691  }
    692 
    693  // Steps 15.g-i, 15.k.i-ii.
    694  var captures = new_List();
    695  for (var n = 0; n <= nCaptures; n++) {
    696    assert(
    697      typeof result[n] === "string" || result[n] === undefined,
    698      "RegExpMatcher returns only strings and undefined"
    699    );
    700    DefineDataProperty(captures, n, result[n]);
    701  }
    702 
    703  // Step 15.k.iii.
    704  DefineDataProperty(captures, nCaptures + 1, position);
    705  DefineDataProperty(captures, nCaptures + 2, S);
    706 
    707  // Step 15.k.iv.
    708  if (namedCaptures !== undefined) {
    709    DefineDataProperty(captures, nCaptures + 3, namedCaptures);
    710  }
    711 
    712  // Steps 15.k.v-vi.
    713  return ToString(
    714    callFunction(std_Function_apply, replaceValue, undefined, captures)
    715  );
    716 }
    717 
    718 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
    719 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
    720 // Steps 9.b-17.
    721 // Optimized path for @@replace with the following conditions:
    722 //   * global flag is true
    723 //   * replaceValue is a string without "$"
    724 function RegExpGlobalReplaceOptSimple(rx, S, lengthS, replaceValue, flags) {
    725  // Step 9.a.
    726  var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG) || !!(flags &  REGEXP_UNICODESETS_FLAG);
    727 
    728  // Step 9.b.
    729  var lastIndex = 0;
    730  rx.lastIndex = 0;
    731 
    732  // Step 13 (reordered).
    733  var accumulatedResult = "";
    734 
    735  // Step 14 (reordered).
    736  var nextSourcePosition = 0;
    737 
    738  // Step 12.
    739  while (true) {
    740    // Step 12.a.
    741    var position = RegExpSearcher(rx, S, lastIndex);
    742 
    743    // Step 12.b.
    744    if (position === -1) {
    745      break;
    746    }
    747 
    748    lastIndex = RegExpSearcherLastLimit(S);
    749 
    750    // Step 15.m.ii.
    751    accumulatedResult +=
    752      Substring(S, nextSourcePosition, position - nextSourcePosition) +
    753      replaceValue;
    754 
    755    // Step 15.m.iii.
    756    nextSourcePosition = lastIndex;
    757 
    758    // Step 12.c.iii.2.
    759    if (lastIndex === position) {
    760      lastIndex = fullUnicode
    761        ? AdvanceStringIndex(S, lastIndex)
    762        : lastIndex + 1;
    763      if (lastIndex > lengthS) {
    764        break;
    765      }
    766    }
    767  }
    768 
    769  // Step 16.
    770  if (nextSourcePosition >= lengthS) {
    771    return accumulatedResult;
    772  }
    773 
    774  // Step 17.
    775  return (
    776    accumulatedResult +
    777    Substring(S, nextSourcePosition, lengthS - nextSourcePosition)
    778  );
    779 }
    780 
    781 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
    782 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
    783 // Steps 7-17.
    784 // Optimized path for @@replace.
    785 
    786 // Conditions:
    787 //   * global flag is true
    788 //   * replaceValue is a function
    789 #define FUNC_NAME RegExpGlobalReplaceOptFunc
    790 #define FUNCTIONAL
    791 #include "RegExpGlobalReplaceOpt.h.js"
    792 #undef FUNCTIONAL
    793 #undef FUNC_NAME
    794 /* global RegExpGlobalReplaceOptFunc */
    795 
    796 // Conditions:
    797 //   * global flag is true
    798 //   * replaceValue is a function that returns element of an object
    799 #define FUNC_NAME RegExpGlobalReplaceOptElemBase
    800 #define ELEMBASE
    801 #include "RegExpGlobalReplaceOpt.h.js"
    802 #undef ELEMBASE
    803 #undef FUNC_NAME
    804 /* global RegExpGlobalReplaceOptElemBase */
    805 
    806 // Conditions:
    807 //   * global flag is true
    808 //   * replaceValue is a string with "$"
    809 #define FUNC_NAME RegExpGlobalReplaceOptSubst
    810 #define SUBSTITUTION
    811 #include "RegExpGlobalReplaceOpt.h.js"
    812 #undef SUBSTITUTION
    813 #undef FUNC_NAME
    814 /* global RegExpGlobalReplaceOptSubst */
    815 
    816 // Conditions:
    817 //   * global flag is false
    818 //   * replaceValue is a string without "$"
    819 #define FUNC_NAME RegExpLocalReplaceOptSimple
    820 #define SIMPLE
    821 #include "RegExpLocalReplaceOpt.h.js"
    822 #undef SIMPLE
    823 #undef FUNC_NAME
    824 /* global RegExpLocalReplaceOptSimple */
    825 
    826 // Conditions:
    827 //   * global flag is false
    828 //   * replaceValue is a function
    829 #define FUNC_NAME RegExpLocalReplaceOptFunc
    830 #define FUNCTIONAL
    831 #include "RegExpLocalReplaceOpt.h.js"
    832 #undef FUNCTIONAL
    833 #undef FUNC_NAME
    834 /* global RegExpLocalReplaceOptFunc */
    835 
    836 // Conditions:
    837 //   * global flag is false
    838 //   * replaceValue is a string with "$"
    839 #define FUNC_NAME RegExpLocalReplaceOptSubst
    840 #define SUBSTITUTION
    841 #include "RegExpLocalReplaceOpt.h.js"
    842 #undef SUBSTITUTION
    843 #undef FUNC_NAME
    844 /* global RegExpLocalReplaceOptSubst */
    845 
    846 // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
    847 // 21.2.5.9 RegExp.prototype [ @@search ] ( string )
    848 function RegExpSearch(string) {
    849  // Step 1.
    850  var rx = this;
    851 
    852  // Step 2.
    853  if (!IsObject(rx)) {
    854    ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
    855  }
    856 
    857  // Step 3.
    858  var S = ToString(string);
    859 
    860  // Step 4.
    861  var previousLastIndex = rx.lastIndex;
    862 
    863  // Step 5.
    864  var lastIndexIsZero = SameValue(previousLastIndex, 0);
    865  if (!lastIndexIsZero) {
    866    rx.lastIndex = 0;
    867  }
    868 
    869  if (IsOptimizableRegExpObject(rx) && S.length < 0x7fff) {
    870    // Step 6.
    871    var result = RegExpSearcher(rx, S, 0);
    872 
    873    // We need to consider two cases:
    874    //
    875    // 1. Neither global nor sticky is set:
    876    // RegExpBuiltinExec doesn't modify lastIndex for local RegExps, that
    877    // means |SameValue(rx.lastIndex, 0)| is true after calling exec. The
    878    // comparison in steps 7-8 |SameValue(rx.lastIndex, previousLastIndex)|
    879    // is therefore equal to the already computed |lastIndexIsZero| value.
    880    //
    881    // 2. Global or sticky flag is set.
    882    // RegExpBuiltinExec will always update lastIndex and we need to
    883    // restore the property to its original value.
    884 
    885    // Steps 7-8.
    886    if (!lastIndexIsZero) {
    887      rx.lastIndex = previousLastIndex;
    888    } else {
    889      var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
    890      if (flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)) {
    891        rx.lastIndex = previousLastIndex;
    892      }
    893    }
    894 
    895    // Steps 9-10.
    896    return result;
    897  }
    898 
    899  return RegExpSearchSlowPath(rx, S, previousLastIndex);
    900 }
    901 
    902 // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
    903 // 21.2.5.9 RegExp.prototype [ @@search ] ( string )
    904 // Steps 6-10.
    905 function RegExpSearchSlowPath(rx, S, previousLastIndex) {
    906  // Step 6.
    907  var result = RegExpExec(rx, S);
    908 
    909  // Step 7.
    910  var currentLastIndex = rx.lastIndex;
    911 
    912  // Step 8.
    913  if (!SameValue(currentLastIndex, previousLastIndex)) {
    914    rx.lastIndex = previousLastIndex;
    915  }
    916 
    917  // Step 9.
    918  if (result === null) {
    919    return -1;
    920  }
    921 
    922  // Step 10.
    923  return result.index;
    924 }
    925 
    926 // ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.11.
    927 function RegExpSplit(string, limit) {
    928  // Step 1.
    929  var rx = this;
    930 
    931  // Step 2.
    932  if (!IsObject(rx)) {
    933    ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
    934  }
    935 
    936  // Step 3.
    937  var S = ToString(string);
    938 
    939  // Step 4.
    940  var builtinCtor = GetBuiltinConstructor("RegExp");
    941  var C = SpeciesConstructor(rx, builtinCtor);
    942 
    943  var optimizable =
    944    IsOptimizableRegExpObject(rx) &&
    945    C === builtinCtor &&
    946    (limit === undefined || typeof limit === "number");
    947 
    948  var flags, unicodeMatching, splitter;
    949  if (optimizable) {
    950    // Step 5.
    951    flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
    952    #ifdef NIGHTLY_BUILD
    953    assert(!!(flags & REGEXP_LEGACY_FEATURES_ENABLED_FLAG),
    954           "Legacy features must be enabled in optimized path");
    955    #endif
    956    // Steps 6-7.
    957    unicodeMatching = !!(flags & REGEXP_UNICODE_FLAG);
    958 
    959    // Steps 8-10.
    960    // If split operation is optimizable, perform non-sticky match.
    961    if (flags & REGEXP_STICKY_FLAG) {
    962      var source = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT);
    963      var newFlags = flags & ~(REGEXP_STICKY_FLAG | REGEXP_LEGACY_FEATURES_ENABLED_FLAG);
    964      splitter = RegExpConstructRaw(source, newFlags, true);
    965    } else {
    966      splitter = rx;
    967    }
    968  } else {
    969    // Step 5.
    970    flags = ToString(rx.flags);
    971 
    972    // Steps 6-7.
    973    unicodeMatching = callFunction(std_String_includes, flags, "u");
    974 
    975    // Steps 8-9.
    976    var newFlags;
    977    if (callFunction(std_String_includes, flags, "y")) {
    978      newFlags = flags;
    979    } else {
    980      newFlags = flags + "y";
    981    }
    982 
    983    // Step 10.
    984    splitter = constructContentFunction(C, C, rx, newFlags);
    985  }
    986 
    987  // Step 11.
    988  var A = [];
    989 
    990  // Step 12.
    991  var lengthA = 0;
    992 
    993  // Step 13.
    994  var lim;
    995  if (limit === undefined) {
    996    lim = MAX_UINT32;
    997  } else {
    998    lim = limit >>> 0;
    999  }
   1000 
   1001  // Step 15.
   1002  var p = 0;
   1003 
   1004  // Step 16.
   1005  if (lim === 0) {
   1006    return A;
   1007  }
   1008 
   1009  // Step 14 (reordered).
   1010  var size = S.length;
   1011 
   1012  // Step 17.
   1013  if (size === 0) {
   1014    // Step 17.a-b.
   1015    if (optimizable) {
   1016      if (RegExpSearcher(splitter, S, 0) !== -1) {
   1017        return A;
   1018      }
   1019    } else {
   1020      if (RegExpExec(splitter, S) !== null) {
   1021        return A;
   1022      }
   1023    }
   1024 
   1025    // Step 17.d.
   1026    DefineDataProperty(A, 0, S);
   1027 
   1028    // Step 17.e.
   1029    return A;
   1030  }
   1031 
   1032  // Step 18.
   1033  var q = p;
   1034 
   1035  var optimizableNoCaptures = optimizable && !RegExpHasCaptureGroups(splitter, S);
   1036 
   1037  // Step 19.
   1038  while (q < size) {
   1039    var e, z;
   1040    if (optimizableNoCaptures) {
   1041      // If there are no capturing groups, avoid allocating the match result
   1042      // object |z| (we set it to null). This is the only difference between
   1043      // this branch and the |if (optimizable)| case below.
   1044 
   1045      // Step 19.a (skipped).
   1046      // splitter.lastIndex is not used.
   1047 
   1048      // Steps 19.b-c.
   1049      q = RegExpSearcher(splitter, S, q);
   1050      if (q === -1 || q >= size) {
   1051        break;
   1052      }
   1053 
   1054      // Step 19.d.i.
   1055      e = RegExpSearcherLastLimit(S);
   1056      z = null;
   1057    } else if (optimizable) {
   1058      // Step 19.a (skipped).
   1059      // splitter.lastIndex is not used.
   1060 
   1061      // Step 19.b.
   1062      z = RegExpMatcher(splitter, S, q);
   1063 
   1064      // Step 19.c.
   1065      if (z === null) {
   1066        break;
   1067      }
   1068 
   1069      // splitter.lastIndex is not updated.
   1070      q = z.index;
   1071      if (q >= size) {
   1072        break;
   1073      }
   1074 
   1075      // Step 19.d.i.
   1076      e = q + z[0].length;
   1077    } else {
   1078      // Step 19.a.
   1079      splitter.lastIndex = q;
   1080 
   1081      // Step 19.b.
   1082      z = RegExpExec(splitter, S);
   1083 
   1084      // Step 19.c.
   1085      if (z === null) {
   1086        q = unicodeMatching ? AdvanceStringIndex(S, q) : q + 1;
   1087        continue;
   1088      }
   1089 
   1090      // Step 19.d.i.
   1091      e = ToLength(splitter.lastIndex);
   1092    }
   1093 
   1094    // Step 19.d.iii.
   1095    if (e === p) {
   1096      q = unicodeMatching ? AdvanceStringIndex(S, q) : q + 1;
   1097      continue;
   1098    }
   1099 
   1100    // Steps 19.d.iv.1-3.
   1101    DefineDataProperty(A, lengthA, Substring(S, p, q - p));
   1102 
   1103    // Step 19.d.iv.4.
   1104    lengthA++;
   1105 
   1106    // Step 19.d.iv.5.
   1107    if (lengthA === lim) {
   1108      return A;
   1109    }
   1110 
   1111    // Step 19.d.iv.6.
   1112    p = e;
   1113 
   1114    if (z !== null) {
   1115      // Steps 19.d.iv.7-8.
   1116      var numberOfCaptures = std_Math_max(ToLength(z.length) - 1, 0);
   1117 
   1118      // Step 19.d.iv.9.
   1119      var i = 1;
   1120 
   1121      // Step 19.d.iv.10.
   1122      while (i <= numberOfCaptures) {
   1123        // Steps 19.d.iv.10.a-b.
   1124        DefineDataProperty(A, lengthA, z[i]);
   1125 
   1126        // Step 19.d.iv.10.c.
   1127        i++;
   1128 
   1129        // Step 19.d.iv.10.d.
   1130        lengthA++;
   1131 
   1132        // Step 19.d.iv.10.e.
   1133        if (lengthA === lim) {
   1134          return A;
   1135        }
   1136      }
   1137    }
   1138 
   1139    // Step 19.d.iv.11.
   1140    q = p;
   1141  }
   1142 
   1143  // Steps 20-22.
   1144  if (p >= size) {
   1145    DefineDataProperty(A, lengthA, "");
   1146  } else {
   1147    DefineDataProperty(A, lengthA, Substring(S, p, size - p));
   1148  }
   1149 
   1150  // Step 23.
   1151  return A;
   1152 }
   1153 
   1154 // ES6 21.2.5.2.
   1155 // NOTE: This is not RegExpExec (21.2.5.2.1).
   1156 function RegExp_prototype_Exec(string) {
   1157  // Steps 1-3.
   1158  var R = this;
   1159  if (!IsObject(R) || !IsRegExpObject(R)) {
   1160    return callFunction(
   1161      CallRegExpMethodIfWrapped,
   1162      R,
   1163      string,
   1164      "RegExp_prototype_Exec"
   1165    );
   1166  }
   1167 
   1168  // Steps 4-5.
   1169  var S = ToString(string);
   1170 
   1171  // Step 6.
   1172  return RegExpBuiltinExec(R, S);
   1173 }
   1174 
   1175 // ES6 21.2.5.13.
   1176 function RegExpTest(string) {
   1177  // Steps 1-2.
   1178  var R = this;
   1179  if (!IsObject(R)) {
   1180    ThrowTypeError(JSMSG_OBJECT_REQUIRED, R === null ? "null" : typeof R);
   1181  }
   1182 
   1183  // Steps 3-4.
   1184  var S = ToString(string);
   1185 
   1186  // Steps 5-6.
   1187  return RegExpExecForTest(R, S);
   1188 }
   1189 
   1190 // ES 2016 draft Mar 25, 2016 21.2.4.2.
   1191 function $RegExpSpecies() {
   1192  // Step 1.
   1193  return this;
   1194 }
   1195 SetCanonicalName($RegExpSpecies, "get [Symbol.species]");
   1196 
   1197 // String.prototype.matchAll proposal.
   1198 //
   1199 // RegExp.prototype [ @@matchAll ] ( string )
   1200 function RegExpMatchAll(string) {
   1201  // Step 1.
   1202  var rx = this;
   1203 
   1204  // Step 2.
   1205  if (!IsObject(rx)) {
   1206    ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
   1207  }
   1208 
   1209  // Step 3.
   1210  var str = ToString(string);
   1211 
   1212  // Step 4.
   1213  var builtinCtor = GetBuiltinConstructor("RegExp");
   1214  var C = SpeciesConstructor(rx, builtinCtor);
   1215 
   1216  var source, flags, matcher, lastIndex;
   1217  if (IsOptimizableRegExpObject(rx) && C === builtinCtor) {
   1218    // Step 5, 9-12.
   1219    source = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT);
   1220    flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
   1221    #ifdef NIGHTLY_BUILD
   1222    assert(!!(flags & REGEXP_LEGACY_FEATURES_ENABLED_FLAG),
   1223    "Legacy features must be enabled in optimized path");
   1224    #endif
   1225 
   1226    // Step 6.
   1227    matcher = rx;
   1228 
   1229    // Step 7.
   1230    lastIndex = ToLength(rx.lastIndex);
   1231 
   1232    // Step 8 (not applicable for the optimized path).
   1233  } else {
   1234    // Step 5.
   1235    source = "";
   1236    flags = ToString(rx.flags);
   1237 
   1238    // Step 6.
   1239    matcher = constructContentFunction(C, C, rx, flags);
   1240 
   1241    // Steps 7-8.
   1242    matcher.lastIndex = ToLength(rx.lastIndex);
   1243 
   1244    // Steps 9-12.
   1245    flags =
   1246      (callFunction(std_String_includes, flags, "g") ? REGEXP_GLOBAL_FLAG : 0) |
   1247      (callFunction(std_String_includes, flags, "u") ? REGEXP_UNICODE_FLAG : 0) |
   1248      (callFunction(std_String_includes, flags, "v") ? REGEXP_UNICODESETS_FLAG : 0);
   1249    
   1250    // Take the non-optimized path.
   1251    lastIndex = REGEXP_STRING_ITERATOR_LASTINDEX_SLOW;
   1252  }
   1253 
   1254  // Step 13.
   1255  return CreateRegExpStringIterator(matcher, str, source, flags, lastIndex);
   1256 }
   1257 
   1258 // String.prototype.matchAll proposal.
   1259 //
   1260 // CreateRegExpStringIterator ( R, S, global, fullUnicode )
   1261 function CreateRegExpStringIterator(regexp, string, source, flags, lastIndex) {
   1262  // Step 1.
   1263  assert(typeof string === "string", "|string| is a string value");
   1264 
   1265  // Steps 2-3.
   1266  assert(typeof flags === "number", "|flags| is a number value");
   1267 
   1268  assert(typeof source === "string", "|source| is a string value");
   1269  assert(typeof lastIndex === "number", "|lastIndex| is a number value");
   1270 
   1271  // Steps 4-9.
   1272  var iterator = NewRegExpStringIterator();
   1273  UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_REGEXP_SLOT, regexp);
   1274  UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_STRING_SLOT, string);
   1275  UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_SOURCE_SLOT, source);
   1276  UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_FLAGS_SLOT, flags | 0);
   1277  UnsafeSetReservedSlot(
   1278    iterator,
   1279    REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
   1280    lastIndex
   1281  );
   1282 
   1283  // Step 10.
   1284  return iterator;
   1285 }
   1286 
   1287 // String.prototype.matchAll proposal.
   1288 //
   1289 // %RegExpStringIteratorPrototype%.next ( )
   1290 function RegExpStringIteratorNext() {
   1291  // Steps 1-3.
   1292  var obj = this;
   1293  if (!IsObject(obj) || (obj = GuardToRegExpStringIterator(obj)) === null) {
   1294    return callFunction(
   1295      CallRegExpStringIteratorMethodIfWrapped,
   1296      this,
   1297      "RegExpStringIteratorNext"
   1298    );
   1299  }
   1300 
   1301  var result = { value: undefined, done: false };
   1302 
   1303  // Step 4.
   1304  var lastIndex = UnsafeGetReservedSlot(
   1305    obj,
   1306    REGEXP_STRING_ITERATOR_LASTINDEX_SLOT
   1307  );
   1308  if (lastIndex === REGEXP_STRING_ITERATOR_LASTINDEX_DONE) {
   1309    result.done = true;
   1310    return result;
   1311  }
   1312 
   1313  // Step 5.
   1314  var regexp = UnsafeGetObjectFromReservedSlot(
   1315    obj,
   1316    REGEXP_STRING_ITERATOR_REGEXP_SLOT
   1317  );
   1318 
   1319  // Step 6.
   1320  var string = UnsafeGetStringFromReservedSlot(
   1321    obj,
   1322    REGEXP_STRING_ITERATOR_STRING_SLOT
   1323  );
   1324 
   1325  // Steps 7-8.
   1326  var flags = UnsafeGetInt32FromReservedSlot(
   1327    obj,
   1328    REGEXP_STRING_ITERATOR_FLAGS_SLOT
   1329  );
   1330  var global = !!(flags & REGEXP_GLOBAL_FLAG);
   1331  var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG) || !!(flags & REGEXP_UNICODESETS_FLAG);
   1332 
   1333  if (lastIndex >= 0) {
   1334    assert(IsRegExpObject(regexp), "|regexp| is a RegExp object");
   1335 
   1336    var source = UnsafeGetStringFromReservedSlot(
   1337      obj,
   1338      REGEXP_STRING_ITERATOR_SOURCE_SLOT
   1339    );
   1340    if (
   1341      IsRegExpPrototypeOptimizable() &&
   1342      UnsafeGetStringFromReservedSlot(regexp, REGEXP_SOURCE_SLOT) === source &&
   1343      UnsafeGetInt32FromReservedSlot(regexp, REGEXP_FLAGS_SLOT) === flags
   1344    ) {
   1345      // Step 9 (Inlined RegExpBuiltinExec).
   1346      var globalOrSticky = !!(
   1347        flags &
   1348        (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)
   1349      );
   1350      if (!globalOrSticky) {
   1351        lastIndex = 0;
   1352      }
   1353 
   1354      var match =
   1355        lastIndex <= string.length
   1356          ? RegExpMatcher(regexp, string, lastIndex)
   1357          : null;
   1358 
   1359      // Step 10.
   1360      if (match === null) {
   1361        // Step 10.a.
   1362        UnsafeSetReservedSlot(
   1363          obj,
   1364          REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
   1365          REGEXP_STRING_ITERATOR_LASTINDEX_DONE
   1366        );
   1367 
   1368        // Step 10.b.
   1369        result.done = true;
   1370        return result;
   1371      }
   1372 
   1373      // Step 11.a.
   1374      if (global) {
   1375        // Step 11.a.i.
   1376        var matchLength = match[0].length;
   1377        lastIndex = match.index + matchLength;
   1378 
   1379        // Step 11.a.ii.
   1380        if (matchLength === 0) {
   1381          // Steps 11.a.ii.1-3.
   1382          lastIndex = fullUnicode
   1383            ? AdvanceStringIndex(string, lastIndex)
   1384            : lastIndex + 1;
   1385        }
   1386 
   1387        UnsafeSetReservedSlot(
   1388          obj,
   1389          REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
   1390          lastIndex
   1391        );
   1392      } else {
   1393        // Step 11.b.i.
   1394        UnsafeSetReservedSlot(
   1395          obj,
   1396          REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
   1397          REGEXP_STRING_ITERATOR_LASTINDEX_DONE
   1398        );
   1399      }
   1400 
   1401      // Steps 11.a.iii and 11.b.ii.
   1402      result.value = match;
   1403      return result;
   1404    }
   1405 
   1406    // Reify the RegExp object.
   1407    var newFlags = flags & ~REGEXP_LEGACY_FEATURES_ENABLED_FLAG;
   1408    regexp = RegExpConstructRaw(source, newFlags, true);
   1409    regexp.lastIndex = lastIndex;
   1410    UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_REGEXP_SLOT, regexp);
   1411 
   1412    // Mark the iterator as no longer optimizable.
   1413    UnsafeSetReservedSlot(
   1414      obj,
   1415      REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
   1416      REGEXP_STRING_ITERATOR_LASTINDEX_SLOW
   1417    );
   1418  }
   1419 
   1420  // Step 9.
   1421  var match = RegExpExec(regexp, string);
   1422 
   1423  // Step 10.
   1424  if (match === null) {
   1425    // Step 10.a.
   1426    UnsafeSetReservedSlot(
   1427      obj,
   1428      REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
   1429      REGEXP_STRING_ITERATOR_LASTINDEX_DONE
   1430    );
   1431 
   1432    // Step 10.b.
   1433    result.done = true;
   1434    return result;
   1435  }
   1436 
   1437  // Step 11.a.
   1438  if (global) {
   1439    // Step 11.a.i.
   1440    var matchStr = ToString(match[0]);
   1441 
   1442    // Step 11.a.ii.
   1443    if (matchStr.length === 0) {
   1444      // Step 11.a.ii.1.
   1445      var thisIndex = ToLength(regexp.lastIndex);
   1446 
   1447      // Step 11.a.ii.2.
   1448      var nextIndex = fullUnicode
   1449        ? AdvanceStringIndex(string, thisIndex)
   1450        : thisIndex + 1;
   1451 
   1452      // Step 11.a.ii.3.
   1453      regexp.lastIndex = nextIndex;
   1454    }
   1455  } else {
   1456    // Step 11.b.i.
   1457    UnsafeSetReservedSlot(
   1458      obj,
   1459      REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
   1460      REGEXP_STRING_ITERATOR_LASTINDEX_DONE
   1461    );
   1462  }
   1463 
   1464  // Steps 11.a.iii and 11.b.ii.
   1465  result.value = match;
   1466  return result;
   1467 }
   1468 
   1469 // ES2020 draft rev e97c95d064750fb949b6778584702dd658cf5624
   1470 // 7.2.8 IsRegExp ( argument )
   1471 function IsRegExp(argument) {
   1472  // Step 1.
   1473  if (!IsObject(argument)) {
   1474    return false;
   1475  }
   1476 
   1477  // Step 2.
   1478  var matcher = argument[GetBuiltinSymbol("match")];
   1479 
   1480  // Step 3.
   1481  if (matcher !== undefined) {
   1482    return !!matcher;
   1483  }
   1484 
   1485  // Steps 4-5.
   1486  return IsPossiblyWrappedRegExpObject(argument);
   1487 }