tor-browser

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

RegExpObject.cpp (41662B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "vm/RegExpObject.h"
      8 
      9 #include "mozilla/MemoryReporting.h"
     10 #include "mozilla/PodOperations.h"
     11 
     12 #include <type_traits>
     13 
     14 #include "builtin/RegExp.h"
     15 #include "builtin/SelfHostingDefines.h"  // REGEXP_*_FLAG
     16 #include "frontend/FrontendContext.h"    // AutoReportFrontendContext
     17 #include "frontend/TokenStream.h"
     18 #include "gc/HashUtil.h"
     19 #include "irregexp/RegExpAPI.h"
     20 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     21 #include "js/friend/StackLimits.h"    // js::ReportOverRecursed
     22 #include "js/Object.h"                // JS::GetBuiltinClass
     23 #include "js/Printer.h"               // js::GenericPrinter
     24 #include "js/RegExp.h"
     25 #include "js/RegExpFlags.h"  // JS::RegExpFlags
     26 #include "util/StringBuilder.h"
     27 #include "util/Unicode.h"
     28 #include "vm/JSONPrinter.h"  // js::JSONPrinter
     29 #include "vm/MatchPairs.h"
     30 #include "vm/PlainObject.h"
     31 #include "vm/RegExpStatics.h"
     32 #include "vm/StringType.h"
     33 
     34 #include "vm/JSContext-inl.h"
     35 #include "vm/JSObject-inl.h"
     36 #include "vm/NativeObject-inl.h"
     37 #include "vm/Shape-inl.h"
     38 
     39 using namespace js;
     40 
     41 using JS::CompileOptions;
     42 using JS::RegExpFlag;
     43 using JS::RegExpFlags;
     44 using mozilla::DebugOnly;
     45 using mozilla::PodCopy;
     46 
     47 using JS::AutoCheckCannotGC;
     48 
     49 static_assert(RegExpFlag::HasIndices == REGEXP_HASINDICES_FLAG,
     50              "self-hosted JS and /d flag bits must agree");
     51 static_assert(RegExpFlag::Global == REGEXP_GLOBAL_FLAG,
     52              "self-hosted JS and /g flag bits must agree");
     53 static_assert(RegExpFlag::IgnoreCase == REGEXP_IGNORECASE_FLAG,
     54              "self-hosted JS and /i flag bits must agree");
     55 static_assert(RegExpFlag::Multiline == REGEXP_MULTILINE_FLAG,
     56              "self-hosted JS and /m flag bits must agree");
     57 static_assert(RegExpFlag::DotAll == REGEXP_DOTALL_FLAG,
     58              "self-hosted JS and /s flag bits must agree");
     59 static_assert(RegExpFlag::Unicode == REGEXP_UNICODE_FLAG,
     60              "self-hosted JS and /u flag bits must agree");
     61 static_assert(RegExpFlag::UnicodeSets == REGEXP_UNICODESETS_FLAG,
     62              "self-hosted JS and /v flag bits must agree");
     63 static_assert(RegExpFlag::Sticky == REGEXP_STICKY_FLAG,
     64              "self-hosted JS and /y flag bits must agree");
     65 /*
     66 * RegExpAlloc ( newTarget )
     67 * https://github.com/tc39/proposal-regexp-legacy-features?tab=readme-ov-file
     68 */
     69 RegExpObject* js::RegExpAlloc(JSContext* cx, NewObjectKind newKind,
     70                              HandleObject proto, HandleObject newTarget) {
     71  Rooted<RegExpObject*> regexp(
     72      cx, NewObjectWithClassProtoAndKind<RegExpObject>(cx, proto, newKind));
     73  if (!regexp) {
     74    return nullptr;
     75  }
     76 
     77  if (!SharedShape::ensureInitialCustomShape<RegExpObject>(cx, regexp)) {
     78    return nullptr;
     79  }
     80  // Step 1. Let obj be ? OrdinaryCreateFromConstructor(newTarget,
     81  // "%RegExpPrototype%", «[[RegExpMatcher]], [[OriginalSource]],
     82  // [[OriginalFlags]], [[Realm]], [[LegacyFeaturesEnabled]]»).
     83  // Set default newTarget if not provided
     84  bool legacyFeaturesEnabled = false;
     85  if (JS::Prefs::experimental_legacy_regexp()) {
     86    // Step 2. Let thisRealm be the current Realm Record.
     87    // Step 3. Set the value of obj’s [[Realm]] internal slot to thisRealm.
     88    JS::Realm* thisRealm = cx->realm();
     89 
     90    JSObject* thisRealmRegExp =
     91        &thisRealm->maybeGlobal()->getConstructor(JSProto_RegExp);
     92 
     93    // Step 4. If SameValue(newTarget, thisRealm.[[Intrinsics]].[[%RegExp%]]) is
     94    // true, Step 4.i then Set the value of obj’s [[LegacyFeaturesEnabled]]
     95    // internal slot to true. Step 5. Else, Step 5.i. Set the value of obj’s
     96    // [[LegacyFeaturesEnabled]] internal slot to false.
     97    legacyFeaturesEnabled = (!newTarget || newTarget == thisRealmRegExp);
     98  }
     99  regexp->setLegacyFeaturesEnabled(legacyFeaturesEnabled);
    100 
    101  // Step 6: Perform ! DefinePropertyOrThrow(obj, "lastIndex",
    102  // PropertyDescriptor {[[Writable]]: true, [Enumerable]]: false,
    103  // [[Configurable]]: false}).
    104  MOZ_ASSERT(regexp->lookupPure(cx->names().lastIndex)->slot() ==
    105             RegExpObject::lastIndexSlot());
    106 
    107  // Step 7: Return obj.
    108  return regexp;
    109 }
    110 
    111 /* MatchPairs */
    112 
    113 bool VectorMatchPairs::initArrayFrom(VectorMatchPairs& copyFrom) {
    114  MOZ_ASSERT(copyFrom.pairCount() > 0);
    115 
    116  if (!allocOrExpandArray(copyFrom.pairCount())) {
    117    return false;
    118  }
    119 
    120  PodCopy(pairs_, copyFrom.pairs_, pairCount_);
    121 
    122  return true;
    123 }
    124 
    125 bool VectorMatchPairs::allocOrExpandArray(size_t pairCount) {
    126  if (!vec_.resizeUninitialized(pairCount)) {
    127    return false;
    128  }
    129 
    130  pairs_ = &vec_[0];
    131  pairCount_ = pairCount;
    132  return true;
    133 }
    134 
    135 /* RegExpObject */
    136 
    137 /* static */
    138 RegExpShared* RegExpObject::getShared(JSContext* cx,
    139                                      Handle<RegExpObject*> regexp) {
    140  if (regexp->hasShared()) {
    141    return regexp->getShared();
    142  }
    143 
    144  return createShared(cx, regexp);
    145 }
    146 
    147 static const ClassSpec RegExpObjectClassSpec = {
    148    GenericCreateConstructor<js::regexp_construct, 2, gc::AllocKind::FUNCTION>,
    149    GenericCreatePrototype<RegExpObject>,
    150    js::regexp_static_methods,
    151    js::regexp_static_props,
    152    js::regexp_methods,
    153    js::regexp_properties,
    154    GenericFinishInit<WhichHasRealmFuseProperty::Proto>,
    155 };
    156 
    157 const JSClass RegExpObject::class_ = {
    158    "RegExp",
    159    JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
    160        JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
    161    JS_NULL_CLASS_OPS,
    162    &RegExpObjectClassSpec,
    163 };
    164 
    165 const JSClass RegExpObject::protoClass_ = {
    166    "RegExp.prototype",
    167    JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
    168    JS_NULL_CLASS_OPS,
    169    &RegExpObjectClassSpec,
    170 };
    171 
    172 template <typename CharT>
    173 RegExpObject* RegExpObject::create(JSContext* cx, const CharT* chars,
    174                                   size_t length, RegExpFlags flags,
    175                                   NewObjectKind newKind,
    176                                   HandleObject newTarget) {
    177  static_assert(std::is_same_v<CharT, char16_t>,
    178                "this code may need updating if/when CharT encodes UTF-8");
    179 
    180  Rooted<JSAtom*> source(cx, AtomizeChars(cx, chars, length));
    181  if (!source) {
    182    return nullptr;
    183  }
    184 
    185  return create(cx, source, flags, newKind, newTarget);
    186 }
    187 
    188 template RegExpObject* RegExpObject::create(JSContext* cx,
    189                                            const char16_t* chars,
    190                                            size_t length, RegExpFlags flags,
    191                                            NewObjectKind newKind,
    192                                            HandleObject newTarget);
    193 
    194 RegExpObject* RegExpObject::createSyntaxChecked(JSContext* cx,
    195                                                Handle<JSAtom*> source,
    196                                                RegExpFlags flags,
    197                                                NewObjectKind newKind,
    198                                                HandleObject newTarget) {
    199  RegExpObject* regexp = RegExpAlloc(cx, newKind, nullptr, newTarget);
    200  if (!regexp) {
    201    return nullptr;
    202  }
    203 
    204  regexp->initAndZeroLastIndex(source, flags, cx);
    205 
    206  return regexp;
    207 }
    208 
    209 RegExpObject* RegExpObject::create(JSContext* cx, Handle<JSAtom*> source,
    210                                   RegExpFlags flags, NewObjectKind newKind,
    211                                   HandleObject newTarget) {
    212  Rooted<RegExpObject*> regexp(cx);
    213  {
    214    AutoReportFrontendContext fc(cx);
    215    CompileOptions dummyOptions(cx);
    216    frontend::DummyTokenStream dummyTokenStream(&fc, dummyOptions);
    217 
    218    LifoAllocScope allocScope(&cx->tempLifoAlloc());
    219    if (!irregexp::CheckPatternSyntax(cx, cx->stackLimitForCurrentPrincipal(),
    220                                      dummyTokenStream, source, flags)) {
    221      return nullptr;
    222    }
    223 
    224    regexp = RegExpAlloc(cx, newKind, nullptr, newTarget);
    225    if (!regexp) {
    226      return nullptr;
    227    }
    228 
    229    regexp->initAndZeroLastIndex(source, flags, cx);
    230 
    231    MOZ_ASSERT(!regexp->hasShared());
    232  }
    233  return regexp;
    234 }
    235 
    236 /* static */
    237 RegExpShared* RegExpObject::createShared(JSContext* cx,
    238                                         Handle<RegExpObject*> regexp) {
    239  MOZ_ASSERT(!regexp->hasShared());
    240  Rooted<JSAtom*> source(cx, regexp->getSource());
    241  RegExpShared* shared =
    242      cx->zone()->regExps().get(cx, source, regexp->getFlags());
    243  if (!shared) {
    244    return nullptr;
    245  }
    246 
    247  regexp->setShared(shared);
    248 
    249  MOZ_ASSERT(regexp->hasShared());
    250 
    251  return shared;
    252 }
    253 
    254 SharedShape* RegExpObject::assignInitialShape(JSContext* cx,
    255                                              Handle<RegExpObject*> self) {
    256  MOZ_ASSERT(self->empty());
    257 
    258  static_assert(LAST_INDEX_SLOT == 0);
    259 
    260  /* The lastIndex property alone is writable but non-configurable. */
    261  if (!NativeObject::addPropertyInReservedSlot(cx, self, cx->names().lastIndex,
    262                                               LAST_INDEX_SLOT,
    263                                               {PropertyFlag::Writable})) {
    264    return nullptr;
    265  }
    266 
    267  // Cache the initial RegExpObject shape that has RegExp.prototype as proto in
    268  // the global object.
    269  SharedShape* shape = self->sharedShape();
    270  JSObject* proto = cx->global()->maybeGetPrototype(JSProto_RegExp);
    271  if (proto && shape->proto() == TaggedProto(proto)) {
    272    cx->global()->setRegExpShapeWithDefaultProto(shape);
    273  }
    274  return shape;
    275 }
    276 
    277 void RegExpObject::initIgnoringLastIndex(JSAtom* source, RegExpFlags flags) {
    278  // If this is a re-initialization with an existing RegExpShared, 'flags'
    279  // may not match getShared()->flags, so forget the RegExpShared.
    280  clearShared();
    281 
    282  setSource(source);
    283  setFlags(flags);
    284 }
    285 
    286 void RegExpObject::initAndZeroLastIndex(JSAtom* source, RegExpFlags flags,
    287                                        JSContext* cx) {
    288  initIgnoringLastIndex(source, flags);
    289  zeroLastIndex(cx);
    290 }
    291 
    292 template <typename KnownF, typename UnknownF>
    293 void ForEachRegExpFlag(JS::RegExpFlags flags, KnownF known, UnknownF unknown) {
    294  uint8_t raw = flags.value();
    295 
    296  for (uint8_t i = 1; i; i = i << 1) {
    297    if (!(raw & i)) {
    298      continue;
    299    }
    300    switch (raw & i) {
    301      case RegExpFlag::HasIndices:
    302        known("HasIndices", "d");
    303        break;
    304      case RegExpFlag::Global:
    305        known("Global", "g");
    306        break;
    307      case RegExpFlag::IgnoreCase:
    308        known("IgnoreCase", "i");
    309        break;
    310      case RegExpFlag::Multiline:
    311        known("Multiline", "m");
    312        break;
    313      case RegExpFlag::DotAll:
    314        known("DotAll", "s");
    315        break;
    316      case RegExpFlag::Unicode:
    317        known("Unicode", "u");
    318        break;
    319      case RegExpFlag::UnicodeSets:
    320        known("UnicodeSets", "v");
    321        break;
    322      case RegExpFlag::Sticky:
    323        known("Sticky", "y");
    324        break;
    325      default:
    326        unknown(i);
    327        break;
    328    }
    329  }
    330 }
    331 
    332 std::ostream& JS::operator<<(std::ostream& os, RegExpFlags flags) {
    333  ForEachRegExpFlag(
    334      flags, [&](const char* name, const char* c) { os << c; },
    335      [&](uint8_t value) { os << '?'; });
    336  return os;
    337 }
    338 
    339 #if defined(DEBUG) || defined(JS_JITSPEW)
    340 void RegExpObject::dumpOwnFields(js::JSONPrinter& json) const {
    341  {
    342    js::GenericPrinter& out = json.beginStringProperty("source");
    343    getSource()->dumpPropertyName(out);
    344    json.endStringProperty();
    345  }
    346 
    347  json.beginInlineListProperty("flags");
    348  ForEachRegExpFlag(
    349      getFlags(),
    350      [&](const char* name, const char* c) { json.value("%s", name); },
    351      [&](uint8_t value) { json.value("Unknown(%02x)", value); });
    352  json.endInlineList();
    353 
    354  {
    355    js::GenericPrinter& out = json.beginStringProperty("lastIndex");
    356    getLastIndex().dumpStringContent(out);
    357    json.endStringProperty();
    358  }
    359 }
    360 
    361 void RegExpObject::dumpOwnStringContent(js::GenericPrinter& out) const {
    362  out.put("/");
    363 
    364  getSource()->dumpCharsNoQuote(out);
    365 
    366  out.put("/");
    367 
    368  ForEachRegExpFlag(
    369      getFlags(), [&](const char* name, const char* c) { out.put(c); },
    370      [&](uint8_t value) {});
    371 }
    372 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */
    373 
    374 static MOZ_ALWAYS_INLINE bool IsRegExpLineTerminator(const JS::Latin1Char c) {
    375  return c == '\n' || c == '\r';
    376 }
    377 
    378 static MOZ_ALWAYS_INLINE bool IsRegExpLineTerminator(const char16_t c) {
    379  return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
    380 }
    381 
    382 static MOZ_ALWAYS_INLINE bool AppendEscapedLineTerminator(
    383    StringBuilder& sb, const JS::Latin1Char c) {
    384  switch (c) {
    385    case '\n':
    386      if (!sb.append('n')) {
    387        return false;
    388      }
    389      break;
    390    case '\r':
    391      if (!sb.append('r')) {
    392        return false;
    393      }
    394      break;
    395    default:
    396      MOZ_CRASH("Bad LineTerminator");
    397  }
    398  return true;
    399 }
    400 
    401 static MOZ_ALWAYS_INLINE bool AppendEscapedLineTerminator(StringBuilder& sb,
    402                                                          const char16_t c) {
    403  switch (c) {
    404    case '\n':
    405      if (!sb.append('n')) {
    406        return false;
    407      }
    408      break;
    409    case '\r':
    410      if (!sb.append('r')) {
    411        return false;
    412      }
    413      break;
    414    case 0x2028:
    415      if (!sb.append("u2028")) {
    416        return false;
    417      }
    418      break;
    419    case 0x2029:
    420      if (!sb.append("u2029")) {
    421        return false;
    422      }
    423      break;
    424    default:
    425      MOZ_CRASH("Bad LineTerminator");
    426  }
    427  return true;
    428 }
    429 
    430 template <typename CharT>
    431 static MOZ_ALWAYS_INLINE bool SetupBuilder(StringBuilder& sb,
    432                                           const CharT* oldChars, size_t oldLen,
    433                                           const CharT* it) {
    434  if constexpr (std::is_same_v<CharT, char16_t>) {
    435    if (!sb.ensureTwoByteChars()) {
    436      return false;
    437    }
    438  }
    439 
    440  if (!sb.reserve(oldLen + 1)) {
    441    return false;
    442  }
    443 
    444  sb.infallibleAppend(oldChars, size_t(it - oldChars));
    445  return true;
    446 }
    447 
    448 // Note: leaves the string builder empty if no escaping need be performed.
    449 template <typename CharT>
    450 static bool EscapeRegExpPattern(StringBuilder& sb, const CharT* oldChars,
    451                                size_t oldLen) {
    452  bool inBrackets = false;
    453  bool previousCharacterWasBackslash = false;
    454 
    455  for (const CharT* it = oldChars; it < oldChars + oldLen; ++it) {
    456    CharT ch = *it;
    457    if (!previousCharacterWasBackslash) {
    458      if (inBrackets) {
    459        if (ch == ']') {
    460          inBrackets = false;
    461        }
    462      } else if (ch == '/') {
    463        // There's a forward slash that needs escaping.
    464        if (sb.empty()) {
    465          // This is the first char we've seen that needs escaping,
    466          // copy everything up to this point.
    467          if (!SetupBuilder(sb, oldChars, oldLen, it)) {
    468            return false;
    469          }
    470        }
    471        if (!sb.append('\\')) {
    472          return false;
    473        }
    474      } else if (ch == '[') {
    475        inBrackets = true;
    476      }
    477    }
    478 
    479    if (IsRegExpLineTerminator(ch)) {
    480      // There's LineTerminator that needs escaping.
    481      if (sb.empty()) {
    482        // This is the first char we've seen that needs escaping,
    483        // copy everything up to this point.
    484        if (!SetupBuilder(sb, oldChars, oldLen, it)) {
    485          return false;
    486        }
    487      }
    488      if (!previousCharacterWasBackslash) {
    489        if (!sb.append('\\')) {
    490          return false;
    491        }
    492      }
    493      if (!AppendEscapedLineTerminator(sb, ch)) {
    494        return false;
    495      }
    496    } else if (!sb.empty()) {
    497      if (!sb.append(ch)) {
    498        return false;
    499      }
    500    }
    501 
    502    if (previousCharacterWasBackslash) {
    503      previousCharacterWasBackslash = false;
    504    } else if (ch == '\\') {
    505      previousCharacterWasBackslash = true;
    506    }
    507  }
    508 
    509  return true;
    510 }
    511 
    512 // ES6 draft rev32 21.2.3.2.4.
    513 JSLinearString* js::EscapeRegExpPattern(JSContext* cx, Handle<JSAtom*> src) {
    514  // Step 2.
    515  if (src->length() == 0) {
    516    return cx->names().emptyRegExp_;
    517  }
    518 
    519  // We may never need to use |sb|. Start using it lazily.
    520  JSStringBuilder sb(cx);
    521  bool escapeFailed = false;
    522  if (src->hasLatin1Chars()) {
    523    JS::AutoCheckCannotGC nogc;
    524    escapeFailed =
    525        !::EscapeRegExpPattern(sb, src->latin1Chars(nogc), src->length());
    526  } else {
    527    JS::AutoCheckCannotGC nogc;
    528    escapeFailed =
    529        !::EscapeRegExpPattern(sb, src->twoByteChars(nogc), src->length());
    530  }
    531  if (escapeFailed) {
    532    return nullptr;
    533  }
    534 
    535  // Step 3.
    536  if (sb.empty()) {
    537    return src;
    538  }
    539  return sb.finishString();
    540 }
    541 
    542 // ES6 draft rev32 21.2.5.14. Optimized for RegExpObject.
    543 JSLinearString* RegExpObject::toString(JSContext* cx,
    544                                       Handle<RegExpObject*> obj) {
    545  // Steps 3-4.
    546  Rooted<JSAtom*> src(cx, obj->getSource());
    547  if (!src) {
    548    return nullptr;
    549  }
    550  Rooted<JSLinearString*> escapedSrc(cx, EscapeRegExpPattern(cx, src));
    551 
    552  // Step 7.
    553  JSStringBuilder sb(cx);
    554  size_t len = escapedSrc->length();
    555  if (!sb.reserve(len + 2)) {
    556    return nullptr;
    557  }
    558  sb.infallibleAppend('/');
    559  if (!sb.append(escapedSrc)) {
    560    return nullptr;
    561  }
    562  sb.infallibleAppend('/');
    563 
    564  // Steps 5-7.
    565  if (obj->hasIndices() && !sb.append('d')) {
    566    return nullptr;
    567  }
    568  if (obj->global() && !sb.append('g')) {
    569    return nullptr;
    570  }
    571  if (obj->ignoreCase() && !sb.append('i')) {
    572    return nullptr;
    573  }
    574  if (obj->multiline() && !sb.append('m')) {
    575    return nullptr;
    576  }
    577  if (obj->dotAll() && !sb.append('s')) {
    578    return nullptr;
    579  }
    580  if (obj->unicode() && !sb.append('u')) {
    581    return nullptr;
    582  }
    583  if (obj->unicodeSets() && !sb.append('v')) {
    584    return nullptr;
    585  }
    586  if (obj->sticky() && !sb.append('y')) {
    587    return nullptr;
    588  }
    589 
    590  return sb.finishString();
    591 }
    592 
    593 template <typename CharT>
    594 static MOZ_ALWAYS_INLINE bool IsRegExpMetaChar(CharT ch) {
    595  switch (ch) {
    596    /* ES 2016 draft Mar 25, 2016 21.2.1 SyntaxCharacter. */
    597    case '^':
    598    case '$':
    599    case '\\':
    600    case '.':
    601    case '*':
    602    case '+':
    603    case '?':
    604    case '(':
    605    case ')':
    606    case '[':
    607    case ']':
    608    case '{':
    609    case '}':
    610    case '|':
    611      return true;
    612    default:
    613      return false;
    614  }
    615 }
    616 
    617 template <typename CharT>
    618 bool js::HasRegExpMetaChars(const CharT* chars, size_t length) {
    619  for (size_t i = 0; i < length; ++i) {
    620    if (IsRegExpMetaChar<CharT>(chars[i])) {
    621      return true;
    622    }
    623  }
    624  return false;
    625 }
    626 
    627 template bool js::HasRegExpMetaChars<Latin1Char>(const Latin1Char* chars,
    628                                                 size_t length);
    629 
    630 template bool js::HasRegExpMetaChars<char16_t>(const char16_t* chars,
    631                                               size_t length);
    632 
    633 bool js::StringHasRegExpMetaChars(const JSLinearString* str) {
    634  AutoCheckCannotGC nogc;
    635  if (str->hasLatin1Chars()) {
    636    return HasRegExpMetaChars(str->latin1Chars(nogc), str->length());
    637  }
    638 
    639  return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
    640 }
    641 
    642 /* RegExpShared */
    643 
    644 RegExpShared::RegExpShared(JSAtom* source, RegExpFlags flags)
    645    : CellWithTenuredGCPointer(source), pairCount_(0), flags(flags) {}
    646 
    647 void RegExpShared::traceChildren(JSTracer* trc) {
    648  TraceNullableCellHeaderEdge(trc, this, "RegExpShared source");
    649  if (kind() == RegExpShared::Kind::Atom) {
    650    TraceNullableEdge(trc, &patternAtom_, "RegExpShared pattern atom");
    651  } else {
    652    for (auto& comp : compilationArray) {
    653      TraceNullableEdge(trc, &comp.jitCode, "RegExpShared code");
    654    }
    655    TraceNullableEdge(trc, &groupsTemplate_, "RegExpShared groups template");
    656  }
    657 }
    658 
    659 void RegExpShared::discardJitCode() {
    660  for (auto& comp : compilationArray) {
    661    comp.jitCode = nullptr;
    662  }
    663 
    664  // We can also purge the tables used by JIT code.
    665  tables.clearAndFree();
    666 }
    667 
    668 void RegExpShared::finalize(JS::GCContext* gcx) {
    669  for (auto& comp : compilationArray) {
    670    if (comp.byteCode) {
    671      size_t length = comp.byteCodeLength();
    672      gcx->free_(this, comp.byteCode, length, MemoryUse::RegExpSharedBytecode);
    673    }
    674  }
    675  if (namedCaptureIndices_) {
    676    size_t length = numNamedCaptures() * sizeof(uint32_t);
    677    gcx->free_(this, namedCaptureIndices_, length,
    678               MemoryUse::RegExpSharedNamedCaptureData);
    679  }
    680  if (namedCaptureSliceIndices_) {
    681    size_t length = numDistinctNamedCaptures() * sizeof(uint32_t);
    682    gcx->free_(this, namedCaptureSliceIndices_, length,
    683               MemoryUse::RegExpSharedNamedCaptureSliceData);
    684  }
    685  tables.~JitCodeTables();
    686 }
    687 
    688 /* static */
    689 bool RegExpShared::compileIfNecessary(JSContext* cx,
    690                                      MutableHandleRegExpShared re,
    691                                      Handle<JSLinearString*> input,
    692                                      RegExpShared::CodeKind codeKind) {
    693  if (codeKind == RegExpShared::CodeKind::Any) {
    694    // We start by interpreting regexps, then compile them once they are
    695    // sufficiently hot. For very long input strings, we tier up eagerly.
    696    codeKind = RegExpShared::CodeKind::Bytecode;
    697    if (re->markedForTierUp() || input->length() > 1000) {
    698      codeKind = RegExpShared::CodeKind::Jitcode;
    699    }
    700  }
    701 
    702  // Fall back to bytecode if native codegen is not available.
    703  if (!IsNativeRegExpEnabled() && codeKind == RegExpShared::CodeKind::Jitcode) {
    704    codeKind = RegExpShared::CodeKind::Bytecode;
    705  }
    706 
    707  bool needsCompile = false;
    708  if (re->kind() == RegExpShared::Kind::Unparsed) {
    709    needsCompile = true;
    710  }
    711  if (re->kind() == RegExpShared::Kind::RegExp) {
    712    if (!re->isCompiled(input->hasLatin1Chars(), codeKind)) {
    713      needsCompile = true;
    714    }
    715  }
    716  if (needsCompile) {
    717    return irregexp::CompilePattern(cx, re, input, codeKind);
    718  }
    719  return true;
    720 }
    721 
    722 /* static */
    723 RegExpRunStatus RegExpShared::execute(JSContext* cx,
    724                                      MutableHandleRegExpShared re,
    725                                      Handle<JSLinearString*> input,
    726                                      size_t start, VectorMatchPairs* matches) {
    727  MOZ_ASSERT(matches);
    728 
    729  // TODO: Add tracelogger support
    730 
    731  /* Compile the code at point-of-use. */
    732  if (!compileIfNecessary(cx, re, input, RegExpShared::CodeKind::Any)) {
    733    return RegExpRunStatus::Error;
    734  }
    735 
    736  /*
    737   * Ensure sufficient memory for output vector.
    738   * No need to initialize it. The RegExp engine fills them in on a match.
    739   */
    740  if (!matches->allocOrExpandArray(re->pairCount())) {
    741    ReportOutOfMemory(cx);
    742    return RegExpRunStatus::Error;
    743  }
    744 
    745  if (re->kind() == RegExpShared::Kind::Atom) {
    746    return RegExpShared::executeAtom(re, input, start, matches);
    747  }
    748 
    749  /*
    750   * Ensure sufficient memory for output vector.
    751   * No need to initialize it. The RegExp engine fills them in on a match.
    752   */
    753  if (!matches->allocOrExpandArray(re->pairCount())) {
    754    ReportOutOfMemory(cx);
    755    return RegExpRunStatus::Error;
    756  }
    757 
    758  uint32_t interruptRetries = 0;
    759  const uint32_t maxInterruptRetries = 4;
    760  do {
    761    DebugOnly<bool> alreadyThrowing = cx->isExceptionPending();
    762    RegExpRunStatus result = irregexp::Execute(cx, re, input, start, matches);
    763 #ifdef DEBUG
    764    // Check if we must simulate the interruption
    765    if (js::irregexp::IsolateShouldSimulateInterrupt(cx->isolate)) {
    766      js::irregexp::IsolateClearShouldSimulateInterrupt(cx->isolate);
    767      cx->requestInterrupt(InterruptReason::CallbackUrgent);
    768    }
    769 #endif
    770    if (result == RegExpRunStatus::Error) {
    771      /* Execute can return RegExpRunStatus::Error:
    772       *
    773       *  1. If the native stack overflowed
    774       *  2. If the backtrack stack overflowed
    775       *  3. If an interrupt was requested during execution.
    776       *
    777       * In the first two cases, we want to throw an error. In the
    778       * third case, we want to handle the interrupt and try again.
    779       * We cap the number of times we will retry.
    780       */
    781      if (cx->isExceptionPending()) {
    782        // If this regexp is being executed by recovery instructions
    783        // while bailing out to handle an exception, there may already
    784        // be an exception pending. If so, just return that exception
    785        // instead of reporting a new one.
    786        MOZ_ASSERT(alreadyThrowing);
    787        return RegExpRunStatus::Error;
    788      }
    789      if (cx->hasAnyPendingInterrupt()) {
    790        if (!CheckForInterrupt(cx)) {
    791          return RegExpRunStatus::Error;
    792        }
    793        if (interruptRetries++ < maxInterruptRetries) {
    794          // The initial execution may have been interpreted, or the
    795          // interrupt may have triggered a GC that discarded jitcode.
    796          // To maximize the chance of succeeding before being
    797          // interrupted again, we want to ensure we are compiled.
    798          if (!compileIfNecessary(cx, re, input,
    799                                  RegExpShared::CodeKind::Jitcode)) {
    800            return RegExpRunStatus::Error;
    801          }
    802          continue;
    803        }
    804      }
    805      // If we have run out of retries, this regexp takes too long to execute.
    806      ReportOverRecursed(cx);
    807      return RegExpRunStatus::Error;
    808    }
    809 
    810    MOZ_ASSERT(result == RegExpRunStatus::Success ||
    811               result == RegExpRunStatus::Success_NotFound);
    812 
    813    return result;
    814  } while (true);
    815 
    816  MOZ_CRASH("Unreachable");
    817 }
    818 
    819 void RegExpShared::useAtomMatch(Handle<JSAtom*> pattern) {
    820  MOZ_ASSERT(kind() == RegExpShared::Kind::Unparsed);
    821  kind_ = RegExpShared::Kind::Atom;
    822  patternAtom_ = pattern;
    823  pairCount_ = 1;
    824 }
    825 
    826 void RegExpShared::useRegExpMatch(size_t pairCount) {
    827  MOZ_ASSERT(kind() == RegExpShared::Kind::Unparsed);
    828  kind_ = RegExpShared::Kind::RegExp;
    829  pairCount_ = pairCount;
    830  ticks_ = jit::JitOptions.regexpWarmUpThreshold;
    831 }
    832 
    833 /* static */
    834 void RegExpShared::InitializeNamedCaptures(JSContext* cx, HandleRegExpShared re,
    835                                           uint32_t numNamedCaptures,
    836                                           uint32_t numDistinctNamedCaptures,
    837                                           Handle<PlainObject*> templateObject,
    838                                           uint32_t* captureIndices,
    839                                           uint32_t* sliceIndices) {
    840  MOZ_ASSERT(!re->groupsTemplate_);
    841  MOZ_ASSERT(!re->namedCaptureIndices_);
    842  MOZ_ASSERT(!re->namedCaptureSliceIndices_);
    843 
    844  re->numNamedCaptures_ = numNamedCaptures;
    845  re->numDistinctNamedCaptures_ = numDistinctNamedCaptures;
    846  re->groupsTemplate_ = templateObject;
    847  re->namedCaptureIndices_ = captureIndices;
    848  re->namedCaptureSliceIndices_ = sliceIndices;
    849 
    850  uint32_t arraySize = numNamedCaptures * sizeof(uint32_t);
    851  js::AddCellMemory(re, arraySize, MemoryUse::RegExpSharedNamedCaptureData);
    852 
    853  if (sliceIndices) {
    854    arraySize = numDistinctNamedCaptures * sizeof(uint32_t);
    855    js::AddCellMemory(re, arraySize,
    856                      MemoryUse::RegExpSharedNamedCaptureSliceData);
    857  }
    858 }
    859 
    860 void RegExpShared::tierUpTick() {
    861  MOZ_ASSERT(kind() == RegExpShared::Kind::RegExp);
    862  if (ticks_ > 0) {
    863    ticks_--;
    864  }
    865 }
    866 
    867 bool RegExpShared::markedForTierUp() const {
    868  if (!IsNativeRegExpEnabled()) {
    869    return false;
    870  }
    871  if (kind() != RegExpShared::Kind::RegExp) {
    872    return false;
    873  }
    874  return ticks_ == 0;
    875 }
    876 
    877 // When either unicode flag is set and if |index| points to a trail surrogate,
    878 // step back to the corresponding lead surrogate.
    879 static size_t StepBackToLeadSurrogate(const JSLinearString* input,
    880                                      size_t index) {
    881  // |index| must be a position within a two-byte string, otherwise it can't
    882  // point to the trail surrogate of a surrogate pair.
    883  if (index == 0 || index >= input->length() || input->hasLatin1Chars()) {
    884    return index;
    885  }
    886 
    887  /*
    888   * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad
    889   * 21.2.2.2 step 2.
    890   *   Let listIndex be the index into Input of the character that was obtained
    891   *   from element index of str.
    892   *
    893   * In the spec, pattern match is performed with decoded Unicode code points,
    894   * but our implementation performs it with UTF-16 encoded strings. In step 2,
    895   * we should decrement lastIndex (index) if it points to a trail surrogate
    896   * that has a corresponding lead surrogate.
    897   *
    898   *   var r = /\uD83D\uDC38/ug;
    899   *   r.lastIndex = 1;
    900   *   var str = "\uD83D\uDC38";
    901   *   var result = r.exec(str); // pattern match starts from index 0
    902   *   print(result.index);      // prints 0
    903   *
    904   * Note: This doesn't match the current spec text and result in different
    905   * values for `result.index` under certain conditions. However, the spec will
    906   * change to match our implementation's behavior.
    907   * See https://github.com/tc39/ecma262/issues/128.
    908   */
    909  JS::AutoCheckCannotGC nogc;
    910  const auto* chars = input->twoByteChars(nogc);
    911  if (unicode::IsTrailSurrogate(chars[index]) &&
    912      unicode::IsLeadSurrogate(chars[index - 1])) {
    913    index--;
    914  }
    915  return index;
    916 }
    917 
    918 static RegExpRunStatus ExecuteAtomImpl(RegExpShared* re,
    919                                       const JSLinearString* input,
    920                                       size_t start, MatchPairs* matches) {
    921  MOZ_ASSERT(re->pairCount() == 1);
    922  size_t length = input->length();
    923  size_t searchLength = re->patternAtom()->length();
    924 
    925  if (re->unicode() || re->unicodeSets()) {
    926    start = StepBackToLeadSurrogate(input, start);
    927  }
    928 
    929  if (re->sticky()) {
    930    // First part checks size_t overflow.
    931    if (searchLength + start < searchLength || searchLength + start > length) {
    932      return RegExpRunStatus::Success_NotFound;
    933    }
    934    if (!HasSubstringAt(input, re->patternAtom(), start)) {
    935      return RegExpRunStatus::Success_NotFound;
    936    }
    937 
    938    (*matches)[0].start = start;
    939    (*matches)[0].limit = start + searchLength;
    940    matches->checkAgainst(input->length());
    941    return RegExpRunStatus::Success;
    942  }
    943 
    944  int res = StringFindPattern(input, re->patternAtom(), start);
    945  if (res == -1) {
    946    return RegExpRunStatus::Success_NotFound;
    947  }
    948 
    949  (*matches)[0].start = res;
    950  (*matches)[0].limit = res + searchLength;
    951  matches->checkAgainst(input->length());
    952  return RegExpRunStatus::Success;
    953 }
    954 
    955 RegExpRunStatus js::ExecuteRegExpAtomRaw(RegExpShared* re,
    956                                         const JSLinearString* input,
    957                                         size_t start, MatchPairs* matchPairs) {
    958  AutoUnsafeCallWithABI unsafe;
    959  return ExecuteAtomImpl(re, input, start, matchPairs);
    960 }
    961 
    962 /* static */
    963 RegExpRunStatus RegExpShared::executeAtom(MutableHandleRegExpShared re,
    964                                          Handle<JSLinearString*> input,
    965                                          size_t start,
    966                                          VectorMatchPairs* matches) {
    967  return ExecuteAtomImpl(re, input, start, matches);
    968 }
    969 
    970 size_t RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
    971  size_t n = 0;
    972 
    973  for (const auto& compilation : compilationArray) {
    974    if (compilation.byteCode) {
    975      n += mallocSizeOf(compilation.byteCode);
    976    }
    977  }
    978 
    979  n += tables.sizeOfExcludingThis(mallocSizeOf);
    980  for (size_t i = 0; i < tables.length(); i++) {
    981    n += mallocSizeOf(tables[i].get());
    982  }
    983 
    984  return n;
    985 }
    986 
    987 /* RegExpRealm */
    988 
    989 RegExpRealm::RegExpRealm() {
    990  for (auto& shape : matchResultShapes_) {
    991    shape = nullptr;
    992  }
    993 }
    994 
    995 SharedShape* RegExpRealm::createMatchResultShape(JSContext* cx,
    996                                                 ResultShapeKind kind) {
    997  MOZ_ASSERT(!matchResultShapes_[kind]);
    998 
    999  /* Create template array object */
   1000  Rooted<ArrayObject*> templateObject(cx, NewDenseEmptyArray(cx));
   1001  if (!templateObject) {
   1002    return nullptr;
   1003  }
   1004 
   1005  if (kind == ResultShapeKind::Indices) {
   1006    /* The |indices| array only has a |groups| property. */
   1007    if (!NativeDefineDataProperty(cx, templateObject, cx->names().groups,
   1008                                  UndefinedHandleValue, JSPROP_ENUMERATE)) {
   1009      return nullptr;
   1010    }
   1011    MOZ_ASSERT(templateObject->getLastProperty().slot() == IndicesGroupsSlot);
   1012 
   1013    matchResultShapes_[kind].set(templateObject->sharedShape());
   1014    return matchResultShapes_[kind];
   1015  }
   1016 
   1017  /* Set dummy index property */
   1018  if (!NativeDefineDataProperty(cx, templateObject, cx->names().index,
   1019                                UndefinedHandleValue, JSPROP_ENUMERATE)) {
   1020    return nullptr;
   1021  }
   1022  MOZ_ASSERT(templateObject->getLastProperty().slot() ==
   1023             MatchResultObjectIndexSlot);
   1024 
   1025  /* Set dummy input property */
   1026  if (!NativeDefineDataProperty(cx, templateObject, cx->names().input,
   1027                                UndefinedHandleValue, JSPROP_ENUMERATE)) {
   1028    return nullptr;
   1029  }
   1030  MOZ_ASSERT(templateObject->getLastProperty().slot() ==
   1031             MatchResultObjectInputSlot);
   1032 
   1033  /* Set dummy groups property */
   1034  if (!NativeDefineDataProperty(cx, templateObject, cx->names().groups,
   1035                                UndefinedHandleValue, JSPROP_ENUMERATE)) {
   1036    return nullptr;
   1037  }
   1038  MOZ_ASSERT(templateObject->getLastProperty().slot() ==
   1039             MatchResultObjectGroupsSlot);
   1040 
   1041  if (kind == ResultShapeKind::WithIndices) {
   1042    /* Set dummy indices property */
   1043    if (!NativeDefineDataProperty(cx, templateObject, cx->names().indices,
   1044                                  UndefinedHandleValue, JSPROP_ENUMERATE)) {
   1045      return nullptr;
   1046    }
   1047    MOZ_ASSERT(templateObject->getLastProperty().slot() ==
   1048               MatchResultObjectIndicesSlot);
   1049  }
   1050 
   1051 #ifdef DEBUG
   1052  if (kind == ResultShapeKind::Normal) {
   1053    MOZ_ASSERT(templateObject->numFixedSlots() == 0);
   1054    MOZ_ASSERT(templateObject->numDynamicSlots() ==
   1055               MatchResultObjectNumDynamicSlots);
   1056    MOZ_ASSERT(templateObject->slotSpan() == MatchResultObjectSlotSpan);
   1057  }
   1058 #endif
   1059 
   1060  matchResultShapes_[kind].set(templateObject->sharedShape());
   1061 
   1062  return matchResultShapes_[kind];
   1063 }
   1064 
   1065 void RegExpRealm::trace(JSTracer* trc) {
   1066  if (regExpStatics) {
   1067    regExpStatics->trace(trc);
   1068  }
   1069 
   1070  for (auto& shape : matchResultShapes_) {
   1071    TraceNullableEdge(trc, &shape, "RegExpRealm::matchResultShapes_");
   1072  }
   1073 }
   1074 
   1075 RegExpShared* RegExpZone::get(JSContext* cx, Handle<JSAtom*> source,
   1076                              RegExpFlags flags) {
   1077  DependentAddPtr<Set> p(cx, set_, Key(source, flags));
   1078  if (p) {
   1079    return *p;
   1080  }
   1081 
   1082  auto* shared = cx->newCell<RegExpShared>(source, flags);
   1083  if (!shared) {
   1084    return nullptr;
   1085  }
   1086 
   1087  if (!p.add(cx, set_, Key(source, flags), shared)) {
   1088    return nullptr;
   1089  }
   1090 
   1091  return shared;
   1092 }
   1093 
   1094 size_t RegExpZone::sizeOfIncludingThis(
   1095    mozilla::MallocSizeOf mallocSizeOf) const {
   1096  return mallocSizeOf(this) + set_.sizeOfExcludingThis(mallocSizeOf);
   1097 }
   1098 
   1099 RegExpZone::RegExpZone(Zone* zone) : set_(zone, zone) {}
   1100 
   1101 /* Functions */
   1102 
   1103 JSObject* js::CloneRegExpObject(JSContext* cx, Handle<RegExpObject*> regex) {
   1104  constexpr gc::AllocKind allocKind = RegExpObject::AllocKind;
   1105  static_assert(gc::GetGCKindSlots(allocKind) == RegExpObject::RESERVED_SLOTS);
   1106  MOZ_ASSERT(regex->asTenured().getAllocKind() == allocKind);
   1107 
   1108  Rooted<SharedShape*> shape(cx, regex->sharedShape());
   1109  Rooted<RegExpObject*> clone(cx, NativeObject::create<RegExpObject>(
   1110                                      cx, allocKind, gc::Heap::Default, shape));
   1111  if (!clone) {
   1112    return nullptr;
   1113  }
   1114 
   1115  RegExpShared* shared = RegExpObject::getShared(cx, regex);
   1116  if (!shared) {
   1117    return nullptr;
   1118  }
   1119 
   1120  clone->initAndZeroLastIndex(shared->getSource(), shared->getFlags(), cx);
   1121  clone->setShared(shared);
   1122  if (JS::Prefs::experimental_legacy_regexp()) {
   1123    clone->setLegacyFeaturesEnabled(regex->legacyFeaturesEnabled());
   1124  }
   1125  return clone;
   1126 }
   1127 
   1128 template <typename CharT>
   1129 static bool ParseRegExpFlags(const CharT* chars, size_t length,
   1130                             RegExpFlags* flagsOut, char16_t* invalidFlag) {
   1131  *flagsOut = RegExpFlag::NoFlags;
   1132 
   1133  for (size_t i = 0; i < length; i++) {
   1134    uint8_t flag;
   1135    if (!JS::MaybeParseRegExpFlag(chars[i], &flag) || *flagsOut & flag) {
   1136      *invalidFlag = chars[i];
   1137      return false;
   1138    }
   1139 
   1140    // /u and /v flags are mutually exclusive.
   1141    if (((*flagsOut & RegExpFlag::Unicode) &&
   1142         (flag & RegExpFlag::UnicodeSets)) ||
   1143        ((*flagsOut & RegExpFlag::UnicodeSets) &&
   1144         (flag & RegExpFlag::Unicode))) {
   1145      *invalidFlag = chars[i];
   1146      return false;
   1147    }
   1148 
   1149    *flagsOut |= flag;
   1150  }
   1151 
   1152  return true;
   1153 }
   1154 
   1155 bool js::ParseRegExpFlags(JSContext* cx, JSString* flagStr,
   1156                          RegExpFlags* flagsOut) {
   1157  JSLinearString* linear = flagStr->ensureLinear(cx);
   1158  if (!linear) {
   1159    return false;
   1160  }
   1161 
   1162  size_t len = linear->length();
   1163 
   1164  bool ok;
   1165  char16_t invalidFlag;
   1166  if (linear->hasLatin1Chars()) {
   1167    AutoCheckCannotGC nogc;
   1168    ok = ::ParseRegExpFlags(linear->latin1Chars(nogc), len, flagsOut,
   1169                            &invalidFlag);
   1170  } else {
   1171    AutoCheckCannotGC nogc;
   1172    ok = ::ParseRegExpFlags(linear->twoByteChars(nogc), len, flagsOut,
   1173                            &invalidFlag);
   1174  }
   1175 
   1176  if (!ok) {
   1177    JS::TwoByteChars range(&invalidFlag, 1);
   1178    UniqueChars utf8(JS::CharsToNewUTF8CharsZ(cx, range).c_str());
   1179    if (!utf8) {
   1180      return false;
   1181    }
   1182    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1183                             JSMSG_BAD_REGEXP_FLAG, utf8.get());
   1184    return false;
   1185  }
   1186 
   1187  return true;
   1188 }
   1189 
   1190 JS::ubi::Node::Size JS::ubi::Concrete<RegExpShared>::size(
   1191    mozilla::MallocSizeOf mallocSizeOf) const {
   1192  return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
   1193         get().sizeOfExcludingThis(mallocSizeOf);
   1194 }
   1195 
   1196 /*
   1197 * Regular Expressions.
   1198 */
   1199 JS_PUBLIC_API JSObject* JS::NewRegExpObject(JSContext* cx, const char* bytes,
   1200                                            size_t length, RegExpFlags flags) {
   1201  AssertHeapIsIdle();
   1202  CHECK_THREAD(cx);
   1203 
   1204  UniqueTwoByteChars chars(InflateString(cx, bytes, length));
   1205  if (!chars) {
   1206    return nullptr;
   1207  }
   1208 
   1209  return RegExpObject::create(cx, chars.get(), length, flags, GenericObject);
   1210 }
   1211 
   1212 JS_PUBLIC_API JSObject* JS::NewUCRegExpObject(JSContext* cx,
   1213                                              const char16_t* chars,
   1214                                              size_t length,
   1215                                              RegExpFlags flags) {
   1216  AssertHeapIsIdle();
   1217  CHECK_THREAD(cx);
   1218 
   1219  return RegExpObject::create(cx, chars, length, flags, GenericObject);
   1220 }
   1221 
   1222 JS_PUBLIC_API bool JS::SetRegExpInput(JSContext* cx, HandleObject obj,
   1223                                      HandleString input) {
   1224  AssertHeapIsIdle();
   1225  CHECK_THREAD(cx);
   1226  cx->check(input);
   1227 
   1228  Handle<GlobalObject*> global = obj.as<GlobalObject>();
   1229  RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
   1230  if (!res) {
   1231    return false;
   1232  }
   1233 
   1234  res->reset(input);
   1235  return true;
   1236 }
   1237 
   1238 JS_PUBLIC_API bool JS::ClearRegExpStatics(JSContext* cx, HandleObject obj) {
   1239  AssertHeapIsIdle();
   1240  CHECK_THREAD(cx);
   1241  MOZ_ASSERT(obj);
   1242 
   1243  Handle<GlobalObject*> global = obj.as<GlobalObject>();
   1244  RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
   1245  if (!res) {
   1246    return false;
   1247  }
   1248 
   1249  res->clear();
   1250  return true;
   1251 }
   1252 
   1253 JS_PUBLIC_API bool JS::ExecuteRegExp(JSContext* cx, HandleObject obj,
   1254                                     HandleObject reobj, const char16_t* chars,
   1255                                     size_t length, size_t* indexp, bool test,
   1256                                     MutableHandleValue rval) {
   1257  AssertHeapIsIdle();
   1258  CHECK_THREAD(cx);
   1259 
   1260  Handle<GlobalObject*> global = obj.as<GlobalObject>();
   1261  RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
   1262  if (!res) {
   1263    return false;
   1264  }
   1265 
   1266  Rooted<JSLinearString*> input(cx, NewStringCopyN<CanGC>(cx, chars, length));
   1267  if (!input) {
   1268    return false;
   1269  }
   1270 
   1271  return ExecuteRegExpLegacy(cx, res, reobj.as<RegExpObject>(), input, indexp,
   1272                             test, rval);
   1273 }
   1274 
   1275 JS_PUBLIC_API bool JS::ExecuteRegExpNoStatics(JSContext* cx, HandleObject obj,
   1276                                              const char16_t* chars,
   1277                                              size_t length, size_t* indexp,
   1278                                              bool test,
   1279                                              MutableHandleValue rval) {
   1280  AssertHeapIsIdle();
   1281  CHECK_THREAD(cx);
   1282 
   1283  Rooted<JSLinearString*> input(cx, NewStringCopyN<CanGC>(cx, chars, length));
   1284  if (!input) {
   1285    return false;
   1286  }
   1287 
   1288  return ExecuteRegExpLegacy(cx, nullptr, obj.as<RegExpObject>(), input, indexp,
   1289                             test, rval);
   1290 }
   1291 
   1292 JS_PUBLIC_API bool JS::ObjectIsRegExp(JSContext* cx, HandleObject obj,
   1293                                      bool* isRegExp) {
   1294  cx->check(obj);
   1295 
   1296  ESClass cls;
   1297  if (!GetBuiltinClass(cx, obj, &cls)) {
   1298    return false;
   1299  }
   1300 
   1301  *isRegExp = cls == ESClass::RegExp;
   1302  return true;
   1303 }
   1304 
   1305 JS_PUBLIC_API RegExpFlags JS::GetRegExpFlags(JSContext* cx, HandleObject obj) {
   1306  AssertHeapIsIdle();
   1307  CHECK_THREAD(cx);
   1308 
   1309  RegExpShared* shared = RegExpToShared(cx, obj);
   1310  if (!shared) {
   1311    return RegExpFlag::NoFlags;
   1312  }
   1313  return shared->getFlags();
   1314 }
   1315 
   1316 JS_PUBLIC_API JSString* JS::GetRegExpSource(JSContext* cx, HandleObject obj) {
   1317  AssertHeapIsIdle();
   1318  CHECK_THREAD(cx);
   1319 
   1320  RegExpShared* shared = RegExpToShared(cx, obj);
   1321  if (!shared) {
   1322    return nullptr;
   1323  }
   1324  return shared->getSource();
   1325 }
   1326 
   1327 JS_PUBLIC_API bool JS::CheckRegExpSyntax(JSContext* cx, const char16_t* chars,
   1328                                         size_t length, RegExpFlags flags,
   1329                                         MutableHandleValue error) {
   1330  AssertHeapIsIdle();
   1331  CHECK_THREAD(cx);
   1332 
   1333  AutoReportFrontendContext fc(cx);
   1334  CompileOptions dummyOptions(cx);
   1335  frontend::DummyTokenStream dummyTokenStream(&fc, dummyOptions);
   1336 
   1337  LifoAllocScope allocScope(&cx->tempLifoAlloc());
   1338 
   1339  mozilla::Range<const char16_t> source(chars, length);
   1340  bool success = irregexp::CheckPatternSyntax(
   1341      cx->tempLifoAlloc(), cx->stackLimitForCurrentPrincipal(),
   1342      dummyTokenStream, source, flags);
   1343  error.set(UndefinedValue());
   1344  if (!success) {
   1345    if (!fc.convertToRuntimeErrorAndClear()) {
   1346      return false;
   1347    }
   1348    // We can fail because of OOM or over-recursion even if the syntax is valid.
   1349    if (cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed()) {
   1350      return false;
   1351    }
   1352 
   1353    if (!cx->getPendingException(error)) {
   1354      return false;
   1355    }
   1356    cx->clearPendingException();
   1357  }
   1358  return true;
   1359 }