tor-browser

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

ParserAtom.cpp (42898B)


      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 "frontend/ParserAtom.h"
      8 
      9 #include "mozilla/TextUtils.h"  // mozilla::IsAscii
     10 
     11 #include <memory>  // std::uninitialized_fill_n
     12 
     13 #include "jsnum.h"  // CharsToNumber
     14 
     15 #include "frontend/CompilationStencil.h"
     16 #include "js/GCAPI.h"            // JS::AutoSuppressGCAnalysis
     17 #include "js/Printer.h"          // Sprinter, QuoteString
     18 #include "util/Identifier.h"     // IsIdentifier
     19 #include "util/StringBuilder.h"  // StringBuilder
     20 #include "util/Text.h"           // AsciiDigitToNumber
     21 #include "util/Unicode.h"
     22 #include "vm/JSContext.h"
     23 #include "vm/Runtime.h"
     24 #include "vm/SelfHosting.h"  // ExtendedUnclonedSelfHostedFunctionNamePrefix
     25 #include "vm/StaticStrings.h"
     26 #include "vm/StringType.h"
     27 
     28 using namespace js;
     29 using namespace js::frontend;
     30 
     31 namespace js {
     32 namespace frontend {
     33 
     34 JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId atomId) {
     35 #define ASSERT_OFFSET_(NAME, _)                  \
     36  static_assert(offsetof(JSAtomState, NAME) ==   \
     37                int32_t(WellKnownAtomId::NAME) * \
     38                    sizeof(js::ImmutableTenuredPtr<PropertyName*>));
     39  FOR_EACH_COMMON_PROPERTYNAME(ASSERT_OFFSET_);
     40 #undef ASSERT_OFFSET_
     41 
     42 #define ASSERT_OFFSET_(NAME, _)                  \
     43  static_assert(offsetof(JSAtomState, NAME) ==   \
     44                int32_t(WellKnownAtomId::NAME) * \
     45                    sizeof(js::ImmutableTenuredPtr<PropertyName*>));
     46  JS_FOR_EACH_PROTOTYPE(ASSERT_OFFSET_);
     47 #undef ASSERT_OFFSET_
     48 
     49 #define ASSERT_OFFSET_(NAME)                     \
     50  static_assert(offsetof(JSAtomState, NAME) ==   \
     51                int32_t(WellKnownAtomId::NAME) * \
     52                    sizeof(js::ImmutableTenuredPtr<PropertyName*>));
     53  JS_FOR_EACH_WELL_KNOWN_SYMBOL(ASSERT_OFFSET_);
     54 #undef ASSERT_OFFSET_
     55 
     56  static_assert(int32_t(WellKnownAtomId::abort) == 0,
     57                "Unexpected order of WellKnownAtom");
     58 
     59  return (&cx->names().abort)[int32_t(atomId)];
     60 }
     61 
     62 #ifdef DEBUG
     63 void TaggedParserAtomIndex::validateRaw() {
     64  if (isParserAtomIndex()) {
     65    MOZ_ASSERT(toParserAtomIndex().index < IndexLimit);
     66  } else if (isWellKnownAtomId()) {
     67    MOZ_ASSERT(uint32_t(toWellKnownAtomId()) <
     68               uint32_t(WellKnownAtomId::Limit));
     69  } else if (isLength1StaticParserString()) {
     70    // always valid
     71  } else if (isLength2StaticParserString()) {
     72    MOZ_ASSERT(size_t(toLength2StaticParserString()) < Length2StaticLimit);
     73  } else if (isLength3StaticParserString()) {
     74    // always valid
     75  } else {
     76    MOZ_ASSERT(isNull());
     77  }
     78 }
     79 #endif
     80 
     81 HashNumber TaggedParserAtomIndex::staticOrWellKnownHash() const {
     82  MOZ_ASSERT(!isParserAtomIndex());
     83 
     84  if (isWellKnownAtomId()) {
     85    const auto& info = GetWellKnownAtomInfo(toWellKnownAtomId());
     86    return info.hash;
     87  }
     88 
     89  if (isLength1StaticParserString()) {
     90    Latin1Char content[1];
     91    ParserAtomsTable::getLength1Content(toLength1StaticParserString(), content);
     92    return mozilla::HashString(content, 1);
     93  }
     94 
     95  if (isLength2StaticParserString()) {
     96    char content[2];
     97    ParserAtomsTable::getLength2Content(toLength2StaticParserString(), content);
     98    return mozilla::HashString(reinterpret_cast<const Latin1Char*>(content), 2);
     99  }
    100 
    101  MOZ_ASSERT(isLength3StaticParserString());
    102  char content[3];
    103  ParserAtomsTable::getLength3Content(toLength3StaticParserString(), content);
    104  return mozilla::HashString(reinterpret_cast<const Latin1Char*>(content), 3);
    105 }
    106 
    107 template <typename CharT, typename SeqCharT>
    108 /* static */ ParserAtom* ParserAtom::allocate(
    109    FrontendContext* fc, LifoAlloc& alloc, InflatedChar16Sequence<SeqCharT> seq,
    110    uint32_t length, HashNumber hash) {
    111  constexpr size_t HeaderSize = sizeof(ParserAtom);
    112  void* raw = alloc.alloc(HeaderSize + (sizeof(CharT) * length));
    113  if (!raw) {
    114    js::ReportOutOfMemory(fc);
    115    return nullptr;
    116  }
    117 
    118  constexpr bool hasTwoByteChars = (sizeof(CharT) == 2);
    119  static_assert(sizeof(CharT) == 1 || sizeof(CharT) == 2,
    120                "CharT should be 1 or 2 byte type");
    121  ParserAtom* entry = new (raw) ParserAtom(length, hash, hasTwoByteChars);
    122  CharT* entryBuf = entry->chars<CharT>();
    123  drainChar16Seq(entryBuf, seq, length);
    124  return entry;
    125 }
    126 
    127 bool ParserAtom::isInstantiatedAsJSAtom() const {
    128  if (isMarkedAtomize()) {
    129    return true;
    130  }
    131 
    132  // Always use JSAtom for short strings.
    133  if (length() < MinimumLengthForNonAtom) {
    134    return true;
    135  }
    136 
    137  return false;
    138 }
    139 
    140 JSString* ParserAtom::instantiateString(JSContext* cx, FrontendContext* fc,
    141                                        ParserAtomIndex index,
    142                                        CompilationAtomCache& atomCache) const {
    143  MOZ_ASSERT(!isInstantiatedAsJSAtom());
    144 
    145  JSString* str;
    146  if (hasLatin1Chars()) {
    147    str = NewStringCopyNDontDeflateNonStaticValidLength<CanGC>(
    148        cx, latin1Chars(), length(), gc::Heap::Tenured);
    149  } else {
    150    str = NewStringCopyNDontDeflateNonStaticValidLength<CanGC>(
    151        cx, twoByteChars(), length(), gc::Heap::Tenured);
    152  }
    153  if (!str) {
    154    return nullptr;
    155  }
    156  if (!atomCache.setAtomAt(fc, index, str)) {
    157    return nullptr;
    158  }
    159 
    160  return str;
    161 }
    162 
    163 JSAtom* ParserAtom::instantiateAtom(JSContext* cx, FrontendContext* fc,
    164                                    ParserAtomIndex index,
    165                                    CompilationAtomCache& atomCache) const {
    166  MOZ_ASSERT(isInstantiatedAsJSAtom());
    167 
    168  JSAtom* atom;
    169  if (hasLatin1Chars()) {
    170    atom =
    171        AtomizeCharsNonStaticValidLength(cx, hash(), latin1Chars(), length());
    172  } else {
    173    atom =
    174        AtomizeCharsNonStaticValidLength(cx, hash(), twoByteChars(), length());
    175  }
    176  if (!atom) {
    177    return nullptr;
    178  }
    179  if (!atomCache.setAtomAt(fc, index, atom)) {
    180    return nullptr;
    181  }
    182  return atom;
    183 }
    184 
    185 JSAtom* ParserAtom::instantiatePermanentAtom(
    186    JSContext* cx, FrontendContext* fc, AtomSet& atomSet, ParserAtomIndex index,
    187    CompilationAtomCache& atomCache) const {
    188  MOZ_ASSERT(!cx->zone());
    189 
    190  MOZ_ASSERT(hasLatin1Chars());
    191  MOZ_ASSERT(length() <= JSString::MAX_LENGTH);
    192  JSAtom* atom = PermanentlyAtomizeCharsNonStaticValidLength(
    193      cx, atomSet, hash(), latin1Chars(), length());
    194  if (!atom) {
    195    return nullptr;
    196  }
    197  if (!atomCache.setAtomAt(fc, index, atom)) {
    198    return nullptr;
    199  }
    200  return atom;
    201 }
    202 
    203 #if defined(DEBUG) || defined(JS_JITSPEW)
    204 void ParserAtom::dump() const {
    205  js::Fprinter out(stderr);
    206  out.put("\"");
    207  dumpCharsNoQuote(out);
    208  out.put("\"\n");
    209 }
    210 
    211 void ParserAtom::dumpCharsNoQuote(js::GenericPrinter& out) const {
    212  if (hasLatin1Chars()) {
    213    JSString::dumpCharsNoQuote<Latin1Char>(latin1Chars(), length(), out);
    214  } else {
    215    JSString::dumpCharsNoQuote<char16_t>(twoByteChars(), length(), out);
    216  }
    217 }
    218 
    219 void ParserAtomsTable::dump(TaggedParserAtomIndex index) const {
    220  if (index.isParserAtomIndex()) {
    221    getParserAtom(index.toParserAtomIndex())->dump();
    222    return;
    223  }
    224 
    225  if (index.isWellKnownAtomId()) {
    226    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    227    js::Fprinter out(stderr);
    228    out.put("\"");
    229    out.put(info.content, info.length);
    230    out.put("\"");
    231    return;
    232  }
    233 
    234  if (index.isLength1StaticParserString()) {
    235    js::Fprinter out(stderr);
    236    out.put("\"");
    237    dumpCharsNoQuote(out, index.toLength1StaticParserString());
    238    out.put("\"\n");
    239    return;
    240  }
    241 
    242  if (index.isLength2StaticParserString()) {
    243    js::Fprinter out(stderr);
    244    out.put("\"");
    245    dumpCharsNoQuote(out, index.toLength2StaticParserString());
    246    out.put("\"\n");
    247    return;
    248  }
    249 
    250  if (index.isLength3StaticParserString()) {
    251    js::Fprinter out(stderr);
    252    out.put("\"");
    253    dumpCharsNoQuote(out, index.toLength3StaticParserString());
    254    out.put("\"\n");
    255    return;
    256  }
    257 
    258  MOZ_ASSERT(index.isNull());
    259  js::Fprinter out(stderr);
    260  out.put("#<null>");
    261 }
    262 
    263 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
    264                                        TaggedParserAtomIndex index) const {
    265  if (index.isParserAtomIndex()) {
    266    getParserAtom(index.toParserAtomIndex())->dumpCharsNoQuote(out);
    267    return;
    268  }
    269 
    270  if (index.isWellKnownAtomId()) {
    271    dumpCharsNoQuote(out, index.toWellKnownAtomId());
    272    return;
    273  }
    274 
    275  if (index.isLength1StaticParserString()) {
    276    dumpCharsNoQuote(out, index.toLength1StaticParserString());
    277    return;
    278  }
    279 
    280  if (index.isLength2StaticParserString()) {
    281    dumpCharsNoQuote(out, index.toLength2StaticParserString());
    282    return;
    283  }
    284 
    285  if (index.isLength3StaticParserString()) {
    286    dumpCharsNoQuote(out, index.toLength3StaticParserString());
    287    return;
    288  }
    289 
    290  MOZ_ASSERT(index.isNull());
    291  out.put("#<null>");
    292 }
    293 
    294 /* static */
    295 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
    296                                        WellKnownAtomId id) {
    297  const auto& info = GetWellKnownAtomInfo(id);
    298  out.put(info.content, info.length);
    299 }
    300 
    301 /* static */
    302 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
    303                                        Length1StaticParserString index) {
    304  Latin1Char content[1];
    305  getLength1Content(index, content);
    306  out.putChar(content[0]);
    307 }
    308 
    309 /* static */
    310 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
    311                                        Length2StaticParserString index) {
    312  char content[2];
    313  getLength2Content(index, content);
    314  out.putChar(content[0]);
    315  out.putChar(content[1]);
    316 }
    317 
    318 /* static */
    319 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
    320                                        Length3StaticParserString index) {
    321  char content[3];
    322  getLength3Content(index, content);
    323  out.putChar(content[0]);
    324  out.putChar(content[1]);
    325  out.putChar(content[2]);
    326 }
    327 #endif
    328 
    329 ParserAtomsTable::ParserAtomsTable(LifoAlloc& alloc) : alloc_(&alloc) {}
    330 
    331 TaggedParserAtomIndex ParserAtomsTable::addEntry(FrontendContext* fc,
    332                                                 EntryMap::AddPtr& addPtr,
    333                                                 ParserAtom* entry) {
    334  MOZ_ASSERT(!addPtr);
    335  ParserAtomIndex index = ParserAtomIndex(entries_.length());
    336  if (size_t(index) >= TaggedParserAtomIndex::IndexLimit) {
    337    ReportAllocationOverflow(fc);
    338    return TaggedParserAtomIndex::null();
    339  }
    340  if (!entries_.append(entry)) {
    341    js::ReportOutOfMemory(fc);
    342    return TaggedParserAtomIndex::null();
    343  }
    344  auto taggedIndex = TaggedParserAtomIndex(index);
    345  if (!entryMap_.add(addPtr, entry, taggedIndex)) {
    346    js::ReportOutOfMemory(fc);
    347    return TaggedParserAtomIndex::null();
    348  }
    349  return taggedIndex;
    350 }
    351 
    352 template <typename AtomCharT, typename SeqCharT>
    353 TaggedParserAtomIndex ParserAtomsTable::internChar16Seq(
    354    FrontendContext* fc, EntryMap::AddPtr& addPtr, HashNumber hash,
    355    InflatedChar16Sequence<SeqCharT> seq, uint32_t length) {
    356  MOZ_ASSERT(!addPtr);
    357 
    358  ParserAtom* entry =
    359      ParserAtom::allocate<AtomCharT>(fc, *alloc_, seq, length, hash);
    360  if (!entry) {
    361    return TaggedParserAtomIndex::null();
    362  }
    363  return addEntry(fc, addPtr, entry);
    364 }
    365 
    366 static const uint16_t MAX_LATIN1_CHAR = 0xff;
    367 
    368 TaggedParserAtomIndex ParserAtomsTable::internAscii(FrontendContext* fc,
    369                                                    const char* asciiPtr,
    370                                                    uint32_t length) {
    371  // ASCII strings are strict subsets of Latin1 strings.
    372  const Latin1Char* latin1Ptr = reinterpret_cast<const Latin1Char*>(asciiPtr);
    373  return internLatin1(fc, latin1Ptr, length);
    374 }
    375 
    376 TaggedParserAtomIndex ParserAtomsTable::internLatin1(
    377    FrontendContext* fc, const Latin1Char* latin1Ptr, uint32_t length) {
    378  // Check for tiny strings which are abundant in minified code.
    379  if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndex(
    380          latin1Ptr, length)) {
    381    return tiny;
    382  }
    383 
    384  // Check for well-known atom.
    385  InflatedChar16Sequence<Latin1Char> seq(latin1Ptr, length);
    386  SpecificParserAtomLookup<Latin1Char> lookup(seq);
    387  if (auto wk = WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup)) {
    388    return wk;
    389  }
    390 
    391  // Check for existing atom.
    392  auto addPtr = entryMap_.lookupForAdd(lookup);
    393  if (addPtr) {
    394    return addPtr->value();
    395  }
    396 
    397  return internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq, length);
    398 }
    399 
    400 bool IsWide(const InflatedChar16Sequence<char16_t>& seq) {
    401  InflatedChar16Sequence<char16_t> seqCopy = seq;
    402  while (seqCopy.hasMore()) {
    403    char16_t ch = seqCopy.next();
    404    if (ch > MAX_LATIN1_CHAR) {
    405      return true;
    406    }
    407  }
    408 
    409  return false;
    410 }
    411 
    412 template <typename AtomCharT>
    413 TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtomImpl(
    414    FrontendContext* fc, const ParserAtom* atom) {
    415  InflatedChar16Sequence<AtomCharT> seq(atom->chars<AtomCharT>(),
    416                                        atom->length());
    417  SpecificParserAtomLookup<AtomCharT> lookup(seq, atom->hash());
    418 
    419  // Check for existing atom.
    420  auto addPtr = entryMap_.lookupForAdd(lookup);
    421  if (addPtr) {
    422    auto index = addPtr->value();
    423 
    424    // Copy UsedByStencilFlag and AtomizeFlag.
    425    MOZ_ASSERT(entries_[index.toParserAtomIndex()]->hasTwoByteChars() ==
    426               atom->hasTwoByteChars());
    427    entries_[index.toParserAtomIndex()]->flags_ |= atom->flags_;
    428    return index;
    429  }
    430 
    431  auto index =
    432      internChar16Seq<AtomCharT>(fc, addPtr, atom->hash(), seq, atom->length());
    433  if (!index) {
    434    return TaggedParserAtomIndex::null();
    435  }
    436 
    437  // Copy UsedByStencilFlag and AtomizeFlag.
    438  MOZ_ASSERT(entries_[index.toParserAtomIndex()]->hasTwoByteChars() ==
    439             atom->hasTwoByteChars());
    440  entries_[index.toParserAtomIndex()]->flags_ |= atom->flags_;
    441  return index;
    442 }
    443 
    444 TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtom(
    445    FrontendContext* fc, const ParserAtom* atom) {
    446  if (atom->hasLatin1Chars()) {
    447    return internExternalParserAtomImpl<JS::Latin1Char>(fc, atom);
    448  }
    449  return internExternalParserAtomImpl<char16_t>(fc, atom);
    450 }
    451 
    452 bool ParserAtomsTable::addPlaceholder(FrontendContext* fc) {
    453  ParserAtomIndex index = ParserAtomIndex(entries_.length());
    454  if (size_t(index) >= TaggedParserAtomIndex::IndexLimit) {
    455    ReportAllocationOverflow(fc);
    456    return false;
    457  }
    458  if (!entries_.append(nullptr)) {
    459    js::ReportOutOfMemory(fc);
    460    return false;
    461  }
    462  return true;
    463 }
    464 
    465 TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtomIndex(
    466    FrontendContext* fc, const CompilationStencil& context,
    467    TaggedParserAtomIndex atom) {
    468  // When the atom is not a parser atom index, the value represent the atom
    469  // without the need for a ParserAtom, and thus we can skip interning it.
    470  if (!atom.isParserAtomIndex()) {
    471    return atom;
    472  }
    473  auto index = atom.toParserAtomIndex();
    474  return internExternalParserAtom(fc, context.parserAtomData[index]);
    475 }
    476 
    477 bool ParserAtomsTable::isEqualToExternalParserAtomIndex(
    478    TaggedParserAtomIndex internal, const CompilationStencil& context,
    479    TaggedParserAtomIndex external) const {
    480  // If one is null, well-known or static, then testing the equality of the bits
    481  // of the TaggedParserAtomIndex is sufficient.
    482  if (!internal.isParserAtomIndex() || !external.isParserAtomIndex()) {
    483    return internal == external;
    484  }
    485 
    486  // Otherwise we have to compare 2 atom-indexes from different ParserAtomTable.
    487  ParserAtom* internalAtom = getParserAtom(internal.toParserAtomIndex());
    488  ParserAtom* externalAtom =
    489      context.parserAtomData[external.toParserAtomIndex()];
    490 
    491  if (internalAtom->hash() != externalAtom->hash()) {
    492    return false;
    493  }
    494 
    495  HashNumber hash = internalAtom->hash();
    496  size_t length = internalAtom->length();
    497  if (internalAtom->hasLatin1Chars()) {
    498    const Latin1Char* chars = internalAtom->latin1Chars();
    499    InflatedChar16Sequence<Latin1Char> seq(chars, length);
    500    return externalAtom->equalsSeq(hash, seq);
    501  }
    502 
    503  const char16_t* chars = internalAtom->twoByteChars();
    504  InflatedChar16Sequence<char16_t> seq(chars, length);
    505  return externalAtom->equalsSeq(hash, seq);
    506 }
    507 
    508 bool ParserAtomSpanBuilder::allocate(FrontendContext* fc, LifoAlloc& alloc,
    509                                     size_t count) {
    510  if (count >= TaggedParserAtomIndex::IndexLimit) {
    511    ReportAllocationOverflow(fc);
    512    return false;
    513  }
    514 
    515  auto* p = alloc.newArrayUninitialized<ParserAtom*>(count);
    516  if (!p) {
    517    js::ReportOutOfMemory(fc);
    518    return false;
    519  }
    520  std::uninitialized_fill_n(p, count, nullptr);
    521 
    522  entries_ = mozilla::Span(p, count);
    523  return true;
    524 }
    525 
    526 static inline bool IsLatin1(mozilla::Utf8Unit c1, mozilla::Utf8Unit c2) {
    527  auto u1 = c1.toUint8();
    528  auto u2 = c2.toUint8();
    529 
    530  // 0x80-0xBF
    531  if (u1 == 0xC2 && 0x80 <= u2 && u2 <= 0xBF) {
    532    return true;
    533  }
    534 
    535  // 0xC0-0xFF
    536  if (u1 == 0xC3 && 0x80 <= u2 && u2 <= 0xBF) {
    537    return true;
    538  }
    539 
    540  return false;
    541 }
    542 
    543 TaggedParserAtomIndex ParserAtomsTable::internUtf8(
    544    FrontendContext* fc, const mozilla::Utf8Unit* utf8Ptr, uint32_t nbyte) {
    545  if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndexUTF8(
    546          utf8Ptr, nbyte)) {
    547    return tiny;
    548  }
    549 
    550  // If source text is ASCII, then the length of the target char buffer
    551  // is the same as the length of the UTF8 input.  Convert it to a Latin1
    552  // encoded string on the heap.
    553  JS::UTF8Chars utf8(utf8Ptr, nbyte);
    554  JS::SmallestEncoding minEncoding = FindSmallestEncoding(utf8);
    555  if (minEncoding == JS::SmallestEncoding::ASCII) {
    556    // As ascii strings are a subset of Latin1 strings, and each encoding
    557    // unit is the same size, we can reliably cast this `Utf8Unit*`
    558    // to a `Latin1Char*`.
    559    const Latin1Char* latin1Ptr = reinterpret_cast<const Latin1Char*>(utf8Ptr);
    560    return internLatin1(fc, latin1Ptr, nbyte);
    561  }
    562 
    563  // Check for existing.
    564  // NOTE: Well-known are all ASCII so have been handled above.
    565  InflatedChar16Sequence<mozilla::Utf8Unit> seq(utf8Ptr, nbyte);
    566  SpecificParserAtomLookup<mozilla::Utf8Unit> lookup(seq);
    567  MOZ_ASSERT(!WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup));
    568  EntryMap::AddPtr addPtr = entryMap_.lookupForAdd(lookup);
    569  if (addPtr) {
    570    return addPtr->value();
    571  }
    572 
    573  // Compute length in code-points.
    574  uint32_t length = 0;
    575  InflatedChar16Sequence<mozilla::Utf8Unit> seqCopy = seq;
    576  while (seqCopy.hasMore()) {
    577    (void)seqCopy.next();
    578    length += 1;
    579  }
    580 
    581  // Otherwise, add new entry.
    582  bool wide = (minEncoding == JS::SmallestEncoding::UTF16);
    583  return wide
    584             ? internChar16Seq<char16_t>(fc, addPtr, lookup.hash(), seq, length)
    585             : internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq,
    586                                           length);
    587 }
    588 
    589 TaggedParserAtomIndex ParserAtomsTable::internChar16(FrontendContext* fc,
    590                                                     const char16_t* char16Ptr,
    591                                                     uint32_t length) {
    592  // Check for tiny strings which are abundant in minified code.
    593  if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndex(
    594          char16Ptr, length)) {
    595    return tiny;
    596  }
    597 
    598  // Check against well-known.
    599  InflatedChar16Sequence<char16_t> seq(char16Ptr, length);
    600  SpecificParserAtomLookup<char16_t> lookup(seq);
    601  if (auto wk = WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup)) {
    602    return wk;
    603  }
    604 
    605  // Check for existing atom.
    606  EntryMap::AddPtr addPtr = entryMap_.lookupForAdd(lookup);
    607  if (addPtr) {
    608    return addPtr->value();
    609  }
    610 
    611  // Otherwise, add new entry.
    612  return IsWide(seq)
    613             ? internChar16Seq<char16_t>(fc, addPtr, lookup.hash(), seq, length)
    614             : internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq,
    615                                           length);
    616 }
    617 
    618 TaggedParserAtomIndex ParserAtomsTable::internJSAtom(
    619    FrontendContext* fc, CompilationAtomCache& atomCache, JSAtom* atom) {
    620  TaggedParserAtomIndex parserAtom;
    621  {
    622    JS::AutoCheckCannotGC nogc;
    623 
    624    parserAtom =
    625        atom->hasLatin1Chars()
    626            ? internLatin1(fc, atom->latin1Chars(nogc), atom->length())
    627            : internChar16(fc, atom->twoByteChars(nogc), atom->length());
    628    if (!parserAtom) {
    629      return TaggedParserAtomIndex::null();
    630    }
    631  }
    632 
    633  if (parserAtom.isParserAtomIndex()) {
    634    ParserAtomIndex index = parserAtom.toParserAtomIndex();
    635    if (!atomCache.hasAtomAt(index)) {
    636      if (!atomCache.setAtomAt(fc, index, atom)) {
    637        return TaggedParserAtomIndex::null();
    638      }
    639    }
    640  }
    641 
    642  // We should (infallibly) map back to the same JSAtom.
    643 #ifdef DEBUG
    644  if (JSContext* cx = fc->maybeCurrentJSContext()) {
    645    JS::AutoSuppressGCAnalysis suppress(cx);
    646    MOZ_ASSERT(toJSAtom(cx, fc, parserAtom, atomCache) == atom);
    647  }
    648 #endif
    649 
    650  return parserAtom;
    651 }
    652 
    653 ParserAtom* ParserAtomsTable::getParserAtom(ParserAtomIndex index) const {
    654  return entries_[index];
    655 }
    656 
    657 void ParserAtomsTable::markUsedByStencil(TaggedParserAtomIndex index,
    658                                         ParserAtom::Atomize atomize) const {
    659  if (!index.isParserAtomIndex()) {
    660    return;
    661  }
    662 
    663  getParserAtom(index.toParserAtomIndex())->markUsedByStencil(atomize);
    664 }
    665 
    666 void ParserAtomsTable::markAtomize(TaggedParserAtomIndex index,
    667                                   ParserAtom::Atomize atomize) const {
    668  if (!index.isParserAtomIndex()) {
    669    return;
    670  }
    671 
    672  getParserAtom(index.toParserAtomIndex())->markAtomize(atomize);
    673 }
    674 
    675 bool ParserAtomsTable::isIdentifier(TaggedParserAtomIndex index) const {
    676  if (index.isParserAtomIndex()) {
    677    const auto* atom = getParserAtom(index.toParserAtomIndex());
    678    return atom->hasLatin1Chars()
    679               ? IsIdentifier(atom->latin1Chars(), atom->length())
    680               : IsIdentifier(atom->twoByteChars(), atom->length());
    681  }
    682 
    683  if (index.isWellKnownAtomId()) {
    684    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    685    return IsIdentifier(reinterpret_cast<const Latin1Char*>(info.content),
    686                        info.length);
    687  }
    688 
    689  if (index.isLength1StaticParserString()) {
    690    Latin1Char content[1];
    691    getLength1Content(index.toLength1StaticParserString(), content);
    692    if (MOZ_UNLIKELY(content[0] > 127)) {
    693      return IsIdentifier(content, 1);
    694    }
    695    return IsIdentifierASCII(char(content[0]));
    696  }
    697 
    698  if (index.isLength2StaticParserString()) {
    699    char content[2];
    700    getLength2Content(index.toLength2StaticParserString(), content);
    701    return IsIdentifierASCII(content[0], content[1]);
    702  }
    703 
    704  MOZ_ASSERT(index.isLength3StaticParserString());
    705 #ifdef DEBUG
    706  char content[3];
    707  getLength3Content(index.toLength3StaticParserString(), content);
    708  MOZ_ASSERT(!reinterpret_cast<const Latin1Char*>(
    709      IsIdentifier(reinterpret_cast<const Latin1Char*>(content), 3)));
    710 #endif
    711  return false;
    712 }
    713 
    714 bool ParserAtomsTable::isPrivateName(TaggedParserAtomIndex index) const {
    715  if (!index.isParserAtomIndex()) {
    716    return false;
    717  }
    718 
    719  const auto* atom = getParserAtom(index.toParserAtomIndex());
    720  return atom->isPrivateName();
    721 }
    722 
    723 bool ParserAtomsTable::isExtendedUnclonedSelfHostedFunctionName(
    724    TaggedParserAtomIndex index) const {
    725  if (index.isParserAtomIndex()) {
    726    const auto* atom = getParserAtom(index.toParserAtomIndex());
    727    if (atom->length() < 2) {
    728      return false;
    729    }
    730 
    731    return atom->charAt(0) == ExtendedUnclonedSelfHostedFunctionNamePrefix;
    732  }
    733 
    734  if (index.isWellKnownAtomId()) {
    735    switch (index.toWellKnownAtomId()) {
    736      case WellKnownAtomId::dollar_ArrayBufferSpecies_:
    737      case WellKnownAtomId::dollar_ArraySpecies_:
    738      case WellKnownAtomId::dollar_ArrayValues_:
    739      case WellKnownAtomId::dollar_RegExpFlagsGetter_:
    740      case WellKnownAtomId::dollar_RegExpToString_:
    741      case WellKnownAtomId::dollar_SharedArrayBufferSpecies_:
    742      case WellKnownAtomId::dollar_TypedArraySpecies_: {
    743 #ifdef DEBUG
    744        const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    745        MOZ_ASSERT(info.content[0] ==
    746                   ExtendedUnclonedSelfHostedFunctionNamePrefix);
    747 #endif
    748        return true;
    749      }
    750      default: {
    751 #ifdef DEBUG
    752        const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    753        MOZ_ASSERT(info.length == 0 ||
    754                   info.content[0] !=
    755                       ExtendedUnclonedSelfHostedFunctionNamePrefix);
    756 #endif
    757        break;
    758      }
    759    }
    760    return false;
    761  }
    762 
    763  // Length-1/2/3 shouldn't be used for extented uncloned self-hosted
    764  // function name, and this query shouldn't be used for them.
    765 #ifdef DEBUG
    766  if (index.isLength1StaticParserString()) {
    767    Latin1Char content[1];
    768    getLength1Content(index.toLength1StaticParserString(), content);
    769    MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix);
    770  } else if (index.isLength2StaticParserString()) {
    771    char content[2];
    772    getLength2Content(index.toLength2StaticParserString(), content);
    773    MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix);
    774  } else {
    775    MOZ_ASSERT(index.isLength3StaticParserString());
    776    char content[3];
    777    getLength3Content(index.toLength3StaticParserString(), content);
    778    MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix);
    779  }
    780 #endif
    781  return false;
    782 }
    783 
    784 static bool HasUnpairedSurrogate(mozilla::Range<const char16_t> chars) {
    785  for (auto ptr = chars.begin(); ptr < chars.end();) {
    786    char16_t ch = *ptr++;
    787    if (unicode::IsLeadSurrogate(ch)) {
    788      if (ptr == chars.end() || !unicode::IsTrailSurrogate(*ptr++)) {
    789        return true;
    790      }
    791    } else if (unicode::IsTrailSurrogate(ch)) {
    792      return true;
    793    }
    794  }
    795  return false;
    796 }
    797 
    798 bool ParserAtomsTable::isModuleExportName(TaggedParserAtomIndex index) const {
    799  if (index.isParserAtomIndex()) {
    800    const ParserAtom* name = getParserAtom(index.toParserAtomIndex());
    801    return name->hasLatin1Chars() ||
    802           !HasUnpairedSurrogate(name->twoByteRange());
    803  }
    804 
    805  // Well-known/length-2 are ASCII.
    806  // length-1 are Latin1.
    807  return true;
    808 }
    809 
    810 bool ParserAtomsTable::isIndex(TaggedParserAtomIndex index,
    811                               uint32_t* indexp) const {
    812  if (index.isParserAtomIndex()) {
    813    const auto* atom = getParserAtom(index.toParserAtomIndex());
    814    size_t len = atom->length();
    815    if (len == 0 || len > UINT32_CHAR_BUFFER_LENGTH) {
    816      return false;
    817    }
    818    if (atom->hasLatin1Chars()) {
    819      return mozilla::IsAsciiDigit(*atom->latin1Chars()) &&
    820             js::CheckStringIsIndex(atom->latin1Chars(), len, indexp);
    821    }
    822    return mozilla::IsAsciiDigit(*atom->twoByteChars()) &&
    823           js::CheckStringIsIndex(atom->twoByteChars(), len, indexp);
    824  }
    825 
    826  if (index.isWellKnownAtomId()) {
    827 #ifdef DEBUG
    828    // Well-known atom shouldn't start with digit.
    829    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    830    MOZ_ASSERT(info.length == 0 || !mozilla::IsAsciiDigit(info.content[0]));
    831 #endif
    832    return false;
    833  }
    834 
    835  if (index.isLength1StaticParserString()) {
    836    Latin1Char content[1];
    837    getLength1Content(index.toLength1StaticParserString(), content);
    838    if (mozilla::IsAsciiDigit(content[0])) {
    839      *indexp = AsciiDigitToNumber(content[0]);
    840      return true;
    841    }
    842    return false;
    843  }
    844 
    845  if (index.isLength2StaticParserString()) {
    846    char content[2];
    847    getLength2Content(index.toLength2StaticParserString(), content);
    848    // Leading '0' isn't allowed.
    849    // See CheckStringIsIndex comment.
    850    if (content[0] != '0' && mozilla::IsAsciiDigit(content[0]) &&
    851        mozilla::IsAsciiDigit(content[1])) {
    852      *indexp =
    853          AsciiDigitToNumber(content[0]) * 10 + AsciiDigitToNumber(content[1]);
    854      return true;
    855    }
    856    return false;
    857  }
    858 
    859  MOZ_ASSERT(index.isLength3StaticParserString());
    860  *indexp = uint32_t(index.toLength3StaticParserString());
    861 #ifdef DEBUG
    862  char content[3];
    863  getLength3Content(index.toLength3StaticParserString(), content);
    864  MOZ_ASSERT(uint32_t(AsciiDigitToNumber(content[0])) * 100 +
    865                 uint32_t(AsciiDigitToNumber(content[1])) * 10 +
    866                 uint32_t(AsciiDigitToNumber(content[2])) ==
    867             *indexp);
    868  MOZ_ASSERT(100 <= *indexp);
    869 #endif
    870  return true;
    871 }
    872 
    873 bool ParserAtomsTable::isInstantiatedAsJSAtom(
    874    TaggedParserAtomIndex index) const {
    875  if (index.isParserAtomIndex()) {
    876    const auto* atom = getParserAtom(index.toParserAtomIndex());
    877    return atom->isInstantiatedAsJSAtom();
    878  }
    879 
    880  // Everything else are always JSAtom.
    881  return true;
    882 }
    883 
    884 uint32_t ParserAtomsTable::length(TaggedParserAtomIndex index) const {
    885  if (index.isParserAtomIndex()) {
    886    return getParserAtom(index.toParserAtomIndex())->length();
    887  }
    888 
    889  if (index.isWellKnownAtomId()) {
    890    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    891    return info.length;
    892  }
    893 
    894  if (index.isLength1StaticParserString()) {
    895    return 1;
    896  }
    897 
    898  if (index.isLength2StaticParserString()) {
    899    return 2;
    900  }
    901 
    902  MOZ_ASSERT(index.isLength3StaticParserString());
    903  return 3;
    904 }
    905 
    906 HashNumber ParserAtomsTable::hash(TaggedParserAtomIndex index) const {
    907  if (index.isParserAtomIndex()) {
    908    return getParserAtom(index.toParserAtomIndex())->hash();
    909  }
    910 
    911  return index.staticOrWellKnownHash();
    912 }
    913 
    914 double ParserAtomsTable::toNumber(TaggedParserAtomIndex index) const {
    915  if (index.isParserAtomIndex()) {
    916    const auto* atom = getParserAtom(index.toParserAtomIndex());
    917    size_t len = atom->length();
    918    return atom->hasLatin1Chars() ? CharsToNumber(atom->latin1Chars(), len)
    919                                  : CharsToNumber(atom->twoByteChars(), len);
    920  }
    921 
    922  if (index.isWellKnownAtomId()) {
    923    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    924    return CharsToNumber(reinterpret_cast<const Latin1Char*>(info.content),
    925                         info.length);
    926  }
    927 
    928  if (index.isLength1StaticParserString()) {
    929    Latin1Char content[1];
    930    getLength1Content(index.toLength1StaticParserString(), content);
    931    return CharsToNumber(content, 1);
    932  }
    933 
    934  if (index.isLength2StaticParserString()) {
    935    char content[2];
    936    getLength2Content(index.toLength2StaticParserString(), content);
    937    return CharsToNumber(reinterpret_cast<const Latin1Char*>(content), 2);
    938  }
    939 
    940  MOZ_ASSERT(index.isLength3StaticParserString());
    941  double result = double(index.toLength3StaticParserString());
    942 #ifdef DEBUG
    943  char content[3];
    944  getLength3Content(index.toLength3StaticParserString(), content);
    945  double tmp = CharsToNumber(reinterpret_cast<const Latin1Char*>(content), 3);
    946  MOZ_ASSERT(tmp == result);
    947 #endif
    948  return result;
    949 }
    950 
    951 UniqueChars ParserAtomsTable::toNewUTF8CharsZ(
    952    FrontendContext* fc, TaggedParserAtomIndex index) const {
    953  auto* alloc = fc->getAllocator();
    954 
    955  if (index.isParserAtomIndex()) {
    956    const auto* atom = getParserAtom(index.toParserAtomIndex());
    957    return UniqueChars(
    958        atom->hasLatin1Chars()
    959            ? JS::CharsToNewUTF8CharsZ(alloc, atom->latin1Range()).c_str()
    960            : JS::CharsToNewUTF8CharsZ(alloc, atom->twoByteRange()).c_str());
    961  }
    962 
    963  if (index.isWellKnownAtomId()) {
    964    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    965    return UniqueChars(
    966        JS::CharsToNewUTF8CharsZ(
    967            alloc,
    968            mozilla::Range(reinterpret_cast<const Latin1Char*>(info.content),
    969                           info.length))
    970            .c_str());
    971  }
    972 
    973  if (index.isLength1StaticParserString()) {
    974    Latin1Char content[1];
    975    getLength1Content(index.toLength1StaticParserString(), content);
    976    return UniqueChars(
    977        JS::CharsToNewUTF8CharsZ(alloc, mozilla::Range(content, 1)).c_str());
    978  }
    979 
    980  if (index.isLength2StaticParserString()) {
    981    char content[2];
    982    getLength2Content(index.toLength2StaticParserString(), content);
    983    return UniqueChars(
    984        JS::CharsToNewUTF8CharsZ(
    985            alloc,
    986            mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2))
    987            .c_str());
    988  }
    989 
    990  MOZ_ASSERT(index.isLength3StaticParserString());
    991  char content[3];
    992  getLength3Content(index.toLength3StaticParserString(), content);
    993  return UniqueChars(
    994      JS::CharsToNewUTF8CharsZ(
    995          alloc,
    996          mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3))
    997          .c_str());
    998 }
    999 
   1000 template <typename CharT>
   1001 UniqueChars ToPrintableStringImpl(mozilla::Range<CharT> str,
   1002                                  char quote = '\0') {
   1003  // Pass nullptr as JSContext, given we don't use JSString.
   1004  // OOM should be handled by caller.
   1005  Sprinter sprinter(nullptr);
   1006  if (!sprinter.init()) {
   1007    return nullptr;
   1008  }
   1009  QuoteString<QuoteTarget::String>(&sprinter, str, quote);
   1010  return sprinter.release();
   1011 }
   1012 
   1013 UniqueChars ParserAtomsTable::toPrintableString(
   1014    TaggedParserAtomIndex index) const {
   1015  if (index.isParserAtomIndex()) {
   1016    const auto* atom = getParserAtom(index.toParserAtomIndex());
   1017    return atom->hasLatin1Chars() ? ToPrintableStringImpl(atom->latin1Range())
   1018                                  : ToPrintableStringImpl(atom->twoByteRange());
   1019  }
   1020 
   1021  if (index.isWellKnownAtomId()) {
   1022    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
   1023    return ToPrintableStringImpl(mozilla::Range(
   1024        reinterpret_cast<const Latin1Char*>(info.content), info.length));
   1025  }
   1026 
   1027  if (index.isLength1StaticParserString()) {
   1028    Latin1Char content[1];
   1029    getLength1Content(index.toLength1StaticParserString(), content);
   1030    return ToPrintableStringImpl(mozilla::Range<const Latin1Char>(content, 1));
   1031  }
   1032 
   1033  if (index.isLength2StaticParserString()) {
   1034    char content[2];
   1035    getLength2Content(index.toLength2StaticParserString(), content);
   1036    return ToPrintableStringImpl(
   1037        mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2));
   1038  }
   1039 
   1040  MOZ_ASSERT(index.isLength3StaticParserString());
   1041  char content[3];
   1042  getLength3Content(index.toLength3StaticParserString(), content);
   1043  return ToPrintableStringImpl(
   1044      mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3));
   1045 }
   1046 
   1047 UniqueChars ParserAtomsTable::toQuotedString(
   1048    TaggedParserAtomIndex index) const {
   1049  if (index.isParserAtomIndex()) {
   1050    const auto* atom = getParserAtom(index.toParserAtomIndex());
   1051    return atom->hasLatin1Chars()
   1052               ? ToPrintableStringImpl(atom->latin1Range(), '\"')
   1053               : ToPrintableStringImpl(atom->twoByteRange(), '\"');
   1054  }
   1055 
   1056  if (index.isWellKnownAtomId()) {
   1057    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
   1058    return ToPrintableStringImpl(
   1059        mozilla::Range(reinterpret_cast<const Latin1Char*>(info.content),
   1060                       info.length),
   1061        '\"');
   1062  }
   1063 
   1064  if (index.isLength1StaticParserString()) {
   1065    Latin1Char content[1];
   1066    getLength1Content(index.toLength1StaticParserString(), content);
   1067    return ToPrintableStringImpl(mozilla::Range<const Latin1Char>(content, 1),
   1068                                 '\"');
   1069  }
   1070 
   1071  if (index.isLength2StaticParserString()) {
   1072    char content[2];
   1073    getLength2Content(index.toLength2StaticParserString(), content);
   1074    return ToPrintableStringImpl(
   1075        mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2), '\"');
   1076  }
   1077 
   1078  MOZ_ASSERT(index.isLength3StaticParserString());
   1079  char content[3];
   1080  getLength3Content(index.toLength3StaticParserString(), content);
   1081  return ToPrintableStringImpl(
   1082      mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3), '\"');
   1083 }
   1084 
   1085 JSAtom* ParserAtomsTable::toJSAtom(JSContext* cx, FrontendContext* fc,
   1086                                   TaggedParserAtomIndex index,
   1087                                   CompilationAtomCache& atomCache) const {
   1088  // This function can be called before we instantiate atoms based on
   1089  // AtomizeFlag.
   1090 
   1091  if (index.isParserAtomIndex()) {
   1092    auto atomIndex = index.toParserAtomIndex();
   1093 
   1094    // If we already instantiated this parser atom, it should always be JSAtom.
   1095    // `asAtom()` called in getAtomAt asserts that.
   1096    JSAtom* atom = atomCache.getAtomAt(atomIndex);
   1097    if (atom) {
   1098      return atom;
   1099    }
   1100 
   1101    // For consistency, mark atomize.
   1102    ParserAtom* parserAtom = getParserAtom(atomIndex);
   1103    parserAtom->markAtomize(ParserAtom::Atomize::Yes);
   1104    return parserAtom->instantiateAtom(cx, fc, atomIndex, atomCache);
   1105  }
   1106 
   1107  if (index.isWellKnownAtomId()) {
   1108    return GetWellKnownAtom(cx, index.toWellKnownAtomId());
   1109  }
   1110 
   1111  if (index.isLength1StaticParserString()) {
   1112    char16_t ch = static_cast<char16_t>(index.toLength1StaticParserString());
   1113    return cx->staticStrings().getUnit(ch);
   1114  }
   1115 
   1116  if (index.isLength2StaticParserString()) {
   1117    size_t s = static_cast<size_t>(index.toLength2StaticParserString());
   1118    return cx->staticStrings().getLength2FromIndex(s);
   1119  }
   1120 
   1121  MOZ_ASSERT(index.isLength3StaticParserString());
   1122  uint32_t s = uint32_t(index.toLength3StaticParserString());
   1123  return cx->staticStrings().getUint(s);
   1124 }
   1125 
   1126 bool ParserAtomsTable::appendTo(StringBuilder& sb,
   1127                                TaggedParserAtomIndex index) const {
   1128  if (index.isParserAtomIndex()) {
   1129    const auto* atom = getParserAtom(index.toParserAtomIndex());
   1130    size_t length = atom->length();
   1131    return atom->hasLatin1Chars() ? sb.append(atom->latin1Chars(), length)
   1132                                  : sb.append(atom->twoByteChars(), length);
   1133  }
   1134 
   1135  if (index.isWellKnownAtomId()) {
   1136    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
   1137    return sb.append(info.content, info.length);
   1138  }
   1139 
   1140  if (index.isLength1StaticParserString()) {
   1141    Latin1Char content[1];
   1142    getLength1Content(index.toLength1StaticParserString(), content);
   1143    return sb.append(content[0]);
   1144  }
   1145 
   1146  if (index.isLength2StaticParserString()) {
   1147    char content[2];
   1148    getLength2Content(index.toLength2StaticParserString(), content);
   1149    return sb.append(content, 2);
   1150  }
   1151 
   1152  MOZ_ASSERT(index.isLength3StaticParserString());
   1153  char content[3];
   1154  getLength3Content(index.toLength3StaticParserString(), content);
   1155  return sb.append(content, 3);
   1156 }
   1157 
   1158 bool InstantiateMarkedAtoms(JSContext* cx, FrontendContext* fc,
   1159                            const ParserAtomSpan& entries,
   1160                            CompilationAtomCache& atomCache) {
   1161  MOZ_ASSERT(cx->zone());
   1162 
   1163  for (size_t i = 0; i < entries.size(); i++) {
   1164    const auto& entry = entries[i];
   1165    if (!entry) {
   1166      continue;
   1167    }
   1168    if (!entry->isUsedByStencil()) {
   1169      continue;
   1170    }
   1171 
   1172    auto index = ParserAtomIndex(i);
   1173    if (atomCache.hasAtomAt(index)) {
   1174      continue;
   1175    }
   1176 
   1177    if (!entry->isInstantiatedAsJSAtom()) {
   1178      if (!entry->instantiateString(cx, fc, index, atomCache)) {
   1179        return false;
   1180      }
   1181    } else {
   1182      if (!entry->instantiateAtom(cx, fc, index, atomCache)) {
   1183        return false;
   1184      }
   1185    }
   1186  }
   1187  return true;
   1188 }
   1189 
   1190 bool InstantiateMarkedAtomsAsPermanent(JSContext* cx, FrontendContext* fc,
   1191                                       AtomSet& atomSet,
   1192                                       const ParserAtomSpan& entries,
   1193                                       CompilationAtomCache& atomCache) {
   1194  MOZ_ASSERT(!cx->zone());
   1195 
   1196  for (size_t i = 0; i < entries.size(); i++) {
   1197    const auto& entry = entries[i];
   1198    if (!entry) {
   1199      continue;
   1200    }
   1201    if (!entry->isUsedByStencil()) {
   1202      continue;
   1203    }
   1204 
   1205    auto index = ParserAtomIndex(i);
   1206    if (atomCache.hasAtomAt(index)) {
   1207      MOZ_ASSERT(atomCache.getAtomAt(index)->isPermanentAtom());
   1208      continue;
   1209    }
   1210 
   1211    if (!entry->instantiatePermanentAtom(cx, fc, atomSet, index, atomCache)) {
   1212      return false;
   1213    }
   1214  }
   1215  return true;
   1216 }
   1217 
   1218 /* static */
   1219 MOZ_RUNINIT WellKnownParserAtoms WellKnownParserAtoms::singleton_;
   1220 
   1221 template <typename CharT>
   1222 TaggedParserAtomIndex WellKnownParserAtoms::lookupChar16Seq(
   1223    const SpecificParserAtomLookup<CharT>& lookup) const {
   1224  EntryMap::Ptr ptr = wellKnownMap_.readonlyThreadsafeLookup(lookup);
   1225  if (ptr) {
   1226    return ptr->value();
   1227  }
   1228  return TaggedParserAtomIndex::null();
   1229 }
   1230 
   1231 TaggedParserAtomIndex WellKnownParserAtoms::lookupTinyIndexUTF8(
   1232    const mozilla::Utf8Unit* utf8Ptr, size_t nbyte) const {
   1233  // Check for tiny strings which are abundant in minified code.
   1234  if (nbyte == 2 && IsLatin1(utf8Ptr[0], utf8Ptr[1])) {
   1235    // Special case the length-1 non-ASCII range.
   1236    InflatedChar16Sequence<mozilla::Utf8Unit> seq(utf8Ptr, 2);
   1237    char16_t u = seq.next();
   1238    const Latin1Char c = u;
   1239    MOZ_ASSERT(!seq.hasMore());
   1240    auto tiny = lookupTinyIndex(&c, 1);
   1241    MOZ_ASSERT(tiny);
   1242    return tiny;
   1243  }
   1244 
   1245  // NOTE: Other than length-1 non-ASCII range, the tiny atoms are all
   1246  //       ASCII-only so we can directly look at the UTF-8 data without
   1247  //       worrying about surrogates.
   1248  return lookupTinyIndex(reinterpret_cast<const Latin1Char*>(utf8Ptr), nbyte);
   1249 }
   1250 
   1251 bool WellKnownParserAtoms::initSingle(const WellKnownAtomInfo& info,
   1252                                      TaggedParserAtomIndex index) {
   1253  unsigned int len = info.length;
   1254  const Latin1Char* str = reinterpret_cast<const Latin1Char*>(info.content);
   1255 
   1256  // Well-known atoms are all currently ASCII with length <= MaxWellKnownLength.
   1257  MOZ_ASSERT(len <= MaxWellKnownLength);
   1258  MOZ_ASSERT(mozilla::IsAscii(mozilla::Span(info.content, len)));
   1259 
   1260  // Strings matched by lookupTinyIndex are stored in static table and aliases
   1261  // should be initialized directly in WellKnownParserAtoms::init.
   1262  MOZ_ASSERT(lookupTinyIndex(str, len) == TaggedParserAtomIndex::null(),
   1263             "Well-known atom matches a tiny StaticString. Did you add it to "
   1264             "the wrong CommonPropertyNames.h list?");
   1265 
   1266  InflatedChar16Sequence<Latin1Char> seq(str, len);
   1267  SpecificParserAtomLookup<Latin1Char> lookup(seq, info.hash);
   1268 
   1269  // Save name for returning after moving entry into set.
   1270  if (!wellKnownMap_.putNew(lookup, &info, index)) {
   1271    return false;
   1272  }
   1273 
   1274  return true;
   1275 }
   1276 
   1277 bool WellKnownParserAtoms::init() {
   1278  MOZ_ASSERT(wellKnownMap_.empty());
   1279 
   1280  // Add well-known strings to the HashMap. The HashMap is used for dynamic
   1281  // lookups later and does not change once this init method is complete.
   1282 #define COMMON_NAME_INIT_(NAME, _)                             \
   1283  if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \
   1284                  TaggedParserAtomIndex::WellKnown::NAME())) { \
   1285    return false;                                              \
   1286  }
   1287  FOR_EACH_NONTINY_COMMON_PROPERTYNAME(COMMON_NAME_INIT_)
   1288 #undef COMMON_NAME_INIT_
   1289 #define COMMON_NAME_INIT_(NAME, _)                             \
   1290  if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \
   1291                  TaggedParserAtomIndex::WellKnown::NAME())) { \
   1292    return false;                                              \
   1293  }
   1294  JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INIT_)
   1295 #undef COMMON_NAME_INIT_
   1296 #define COMMON_NAME_INIT_(NAME)                                \
   1297  if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \
   1298                  TaggedParserAtomIndex::WellKnown::NAME())) { \
   1299    return false;                                              \
   1300  }
   1301  JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INIT_)
   1302 #undef COMMON_NAME_INIT_
   1303 
   1304  return true;
   1305 }
   1306 
   1307 void WellKnownParserAtoms::free() { wellKnownMap_.clear(); }
   1308 
   1309 /* static */ bool WellKnownParserAtoms::initSingleton() {
   1310  return singleton_.init();
   1311 }
   1312 
   1313 /* static */ void WellKnownParserAtoms::freeSingleton() { singleton_.free(); }
   1314 
   1315 } /* namespace frontend */
   1316 } /* namespace js */