/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef frontend_ParserAtom_h #define frontend_ParserAtom_h #include "mozilla/DebugOnly.h" // mozilla::DebugOnly #include "mozilla/HashFunctions.h" // HashString #include "mozilla/Range.h" // mozilla::Range #include "mozilla/Span.h" // mozilla::Span #include "mozilla/Variant.h" // mozilla::Variant #include "ds/LifoAlloc.h" // LifoAlloc #include "frontend/TypedIndex.h" // TypedIndex #include "js/HashTable.h" // HashSet #include "js/UniquePtr.h" // js::UniquePtr #include "js/Vector.h" // Vector #include "vm/CommonPropertyNames.h" #include "vm/StringType.h" // CompareChars, StringEqualsAscii namespace js { namespace frontend { struct CompilationAtomCache; struct CompilationStencil; class ParserAtom; class ParserName; template class SpecificParserAtomLookup; class ParserAtomsTable; // An index to map WellKnownParserAtoms to cx->names(). // This is consistent across multiple compilation. // // GetWellKnownAtom in ParserAtom.cpp relies on the fact that // JSAtomState fields and this enum variants use the same order. enum class WellKnownAtomId : uint32_t { #define ENUM_ENTRY_(_, name, _2) name, FOR_EACH_COMMON_PROPERTYNAME(ENUM_ENTRY_) #undef ENUM_ENTRY_ #define ENUM_ENTRY_(name, _) name, JS_FOR_EACH_PROTOTYPE(ENUM_ENTRY_) #undef ENUM_ENTRY_ Limit, }; // These types correspond into indices in the StaticStrings arrays. enum class StaticParserString1 : uint8_t; enum class StaticParserString2 : uint16_t; class ParserAtom; using ParserAtomIndex = TypedIndex; // ParserAtomIndex, WellKnownAtomId, StaticParserString1, StaticParserString2, // or null. // // 0x0000_0000 Null atom // // 0x1YYY_YYYY 28-bit ParserAtom // // 0x2000_YYYY Well-known atom ID // 0x2001_YYYY Static length-1 atom // 0x2002_YYYY Static length-2 atom class TaggedParserAtomIndex { uint32_t data_; public: static constexpr size_t IndexBit = 28; static constexpr size_t IndexMask = BitMask(IndexBit); static constexpr size_t TagShift = IndexBit; static constexpr size_t TagBit = 4; static constexpr size_t TagMask = BitMask(TagBit) << TagShift; enum class Kind : uint32_t { Null = 0, ParserAtomIndex, WellKnown, }; private: static constexpr size_t SmallIndexBit = 16; static constexpr size_t SmallIndexMask = BitMask(SmallIndexBit); static constexpr size_t SubTagShift = SmallIndexBit; static constexpr size_t SubTagBit = 2; static constexpr size_t SubTagMask = BitMask(SubTagBit) << SubTagShift; public: static constexpr uint32_t NullTag = uint32_t(Kind::Null) << TagShift; static constexpr uint32_t ParserAtomIndexTag = uint32_t(Kind::ParserAtomIndex) << TagShift; static constexpr uint32_t WellKnownTag = uint32_t(Kind::WellKnown) << TagShift; private: static constexpr uint32_t WellKnownSubTag = 0 << SubTagShift; static constexpr uint32_t Static1SubTag = 1 << SubTagShift; static constexpr uint32_t Static2SubTag = 2 << SubTagShift; public: static constexpr uint32_t IndexLimit = Bit(IndexBit); static constexpr uint32_t SmallIndexLimit = Bit(SmallIndexBit); private: explicit TaggedParserAtomIndex(uint32_t data) : data_(data) {} public: constexpr TaggedParserAtomIndex() : data_(NullTag) {} explicit constexpr TaggedParserAtomIndex(ParserAtomIndex index) : data_(index.index | ParserAtomIndexTag) { MOZ_ASSERT(index.index < IndexLimit); } explicit constexpr TaggedParserAtomIndex(WellKnownAtomId index) : data_(uint32_t(index) | WellKnownTag | WellKnownSubTag) { MOZ_ASSERT(uint32_t(index) < SmallIndexLimit); // Static1/Static2 string shouldn't use WellKnownAtomId. #define CHECK_(_, name, _2) MOZ_ASSERT(index != WellKnownAtomId::name); FOR_EACH_NON_EMPTY_TINY_PROPERTYNAME(CHECK_) #undef CHECK_ } explicit constexpr TaggedParserAtomIndex(StaticParserString1 index) : data_(uint32_t(index) | WellKnownTag | Static1SubTag) {} explicit constexpr TaggedParserAtomIndex(StaticParserString2 index) : data_(uint32_t(index) | WellKnownTag | Static2SubTag) {} static TaggedParserAtomIndex star() { return TaggedParserAtomIndex(StaticParserString1('*')); } static TaggedParserAtomIndex arguments() { return TaggedParserAtomIndex(WellKnownAtomId::arguments); } static TaggedParserAtomIndex dotThis() { return TaggedParserAtomIndex(WellKnownAtomId::dotThis); } static TaggedParserAtomIndex dotGenerator() { return TaggedParserAtomIndex(WellKnownAtomId::dotGenerator); } static TaggedParserAtomIndex null() { return TaggedParserAtomIndex(); } #ifdef DEBUG void validateRaw(); #endif static TaggedParserAtomIndex fromRaw(uint32_t data) { auto result = TaggedParserAtomIndex(data); #ifdef DEBUG result.validateRaw(); #endif return result; } bool isParserAtomIndex() const { return (data_ & TagMask) == ParserAtomIndexTag; } bool isWellKnownAtomId() const { return (data_ & (TagMask | SubTagMask)) == (WellKnownTag | WellKnownSubTag); } bool isStaticParserString1() const { return (data_ & (TagMask | SubTagMask)) == (WellKnownTag | Static1SubTag); } bool isStaticParserString2() const { return (data_ & (TagMask | SubTagMask)) == (WellKnownTag | Static2SubTag); } bool isNull() const { bool result = !data_; MOZ_ASSERT_IF(result, (data_ & TagMask) == NullTag); return result; } ParserAtomIndex toParserAtomIndex() const { MOZ_ASSERT(isParserAtomIndex()); return ParserAtomIndex(data_ & IndexMask); } WellKnownAtomId toWellKnownAtomId() const { MOZ_ASSERT(isWellKnownAtomId()); return WellKnownAtomId(data_ & SmallIndexMask); } StaticParserString1 toStaticParserString1() const { MOZ_ASSERT(isStaticParserString1()); return StaticParserString1(data_ & SmallIndexMask); } StaticParserString2 toStaticParserString2() const { MOZ_ASSERT(isStaticParserString2()); return StaticParserString2(data_ & SmallIndexMask); } uint32_t* rawData() { return &data_; } bool operator==(const TaggedParserAtomIndex& rhs) const { return data_ == rhs.data_; } explicit operator bool() const { return !isNull(); } }; /** * A ParserAtomEntry is an in-parser representation of an interned atomic * string. It mostly mirrors the information carried by a JSAtom*. * * The atom contents are stored in one of two locations: * 1. Inline Latin1Char storage (immediately after the ParserAtomEntry memory). * 2. Inline char16_t storage (immediately after the ParserAtomEntry memory). */ class alignas(alignof(uint32_t)) ParserAtomEntry { friend class ParserAtomsTable; friend class WellKnownParserAtoms; friend class WellKnownParserAtoms_ROM; static const uint16_t MAX_LATIN1_CHAR = 0xff; // Bit flags inside flags_. static constexpr uint32_t HasTwoByteCharsFlag = 1 << 0; static constexpr uint32_t UsedByStencilFlag = 1 << 1; // Helper routine to read some sequence of two-byte chars, and write them // into a target buffer of a particular character width. // // The characters in the sequence must have been verified prior template static void drainChar16Seq(CharT* buf, InflatedChar16Sequence seq, uint32_t length) { static_assert( std::is_same_v || std::is_same_v, "Invalid target buffer type."); CharT* cur = buf; while (seq.hasMore()) { char16_t ch = seq.next(); if constexpr (std::is_same_v) { MOZ_ASSERT(ch <= MAX_LATIN1_CHAR); } MOZ_ASSERT(cur < (buf + length)); *cur = ch; cur++; } } private: // The JSAtom-compatible hash of the string. HashNumber hash_ = 0; // The length of the buffer in chars_. uint32_t length_ = 0; TaggedParserAtomIndex index_; uint32_t flags_ = 0; // End of fields. static const uint32_t MAX_LENGTH = JSString::MAX_LENGTH; ParserAtomEntry(uint32_t length, HashNumber hash, bool hasTwoByteChars) : hash_(hash), length_(length), flags_(hasTwoByteChars ? HasTwoByteCharsFlag : 0) {} public: // The constexpr constructor is used by StaticParserAtomEntry and XDR constexpr ParserAtomEntry() = default; // ParserAtomEntries may own their content buffers in variant_, and thus // cannot be copy-constructed - as a new chars would need to be allocated. ParserAtomEntry(const ParserAtomEntry&) = delete; ParserAtomEntry(ParserAtomEntry&& other) = delete; template static ParserAtomEntry* allocate(JSContext* cx, LifoAlloc& alloc, InflatedChar16Sequence seq, uint32_t length, HashNumber hash); static ParserAtomEntry* allocateRaw(JSContext* cx, LifoAlloc& alloc, const uint8_t* srcRaw, size_t totalLength); ParserAtom* asAtom() { return reinterpret_cast(this); } const ParserAtom* asAtom() const { return reinterpret_cast(this); } inline ParserName* asName(); inline const ParserName* asName() const; bool hasLatin1Chars() const { return !(flags_ & HasTwoByteCharsFlag); } bool hasTwoByteChars() const { return flags_ & HasTwoByteCharsFlag; } template const CharT* chars() const { MOZ_ASSERT(sizeof(CharT) == (hasTwoByteChars() ? 2 : 1)); return reinterpret_cast(this + 1); } template CharT* chars() { MOZ_ASSERT(sizeof(CharT) == (hasTwoByteChars() ? 2 : 1)); return reinterpret_cast(this + 1); } const Latin1Char* latin1Chars() const { return chars(); } const char16_t* twoByteChars() const { return chars(); } mozilla::Range latin1Range() const { return mozilla::Range(latin1Chars(), length_); } mozilla::Range twoByteRange() const { return mozilla::Range(twoByteChars(), length_); } bool isIndex(uint32_t* indexp) const; bool isIndex() const { uint32_t index; return isIndex(&index); } bool isAscii() const { if (hasTwoByteChars()) { return false; } for (Latin1Char ch : latin1Range()) { if (!mozilla::IsAscii(ch)) { return false; } } return true; } HashNumber hash() const { return hash_; } uint32_t length() const { return length_; } bool isUsedByStencil() const { return flags_ & UsedByStencilFlag; } void markUsedByStencil() const { if (isParserAtomIndex()) { // Use const method + const_cast here to avoid marking static strings' // field mutable. const_cast(this)->flags_ |= UsedByStencilFlag; } } bool equalsJSAtom(JSAtom* other) const; template bool equalsSeq(HashNumber hash, InflatedChar16Sequence seq) const; TaggedParserAtomIndex toIndex() const { return index_; } ParserAtomIndex toParserAtomIndex() const { return index_.toParserAtomIndex(); } WellKnownAtomId toWellKnownAtomId() const { return index_.toWellKnownAtomId(); } StaticParserString1 toStaticParserString1() const { return index_.toStaticParserString1(); } StaticParserString2 toStaticParserString2() const { return index_.toStaticParserString2(); } bool isParserAtomIndex() const { return index_.isParserAtomIndex(); } bool isWellKnownAtomId() const { return index_.isWellKnownAtomId(); } bool isStaticParserString1() const { return index_.isStaticParserString1(); } bool isStaticParserString2() const { return index_.isStaticParserString2(); } void setParserAtomIndex(ParserAtomIndex index) { index_ = TaggedParserAtomIndex(index); } private: constexpr void setWellKnownAtomId(WellKnownAtomId atomId) { index_ = TaggedParserAtomIndex(atomId); } constexpr void setStaticParserString1(StaticParserString1 s) { index_ = TaggedParserAtomIndex(s); } constexpr void setStaticParserString2(StaticParserString2 s) { index_ = TaggedParserAtomIndex(s); } constexpr void setHashAndLength(HashNumber hash, uint32_t length) { hash_ = hash; length_ = length; } public: // Convert this entry to a js-atom. The first time this method is called // the entry will cache the JSAtom pointer to return later. JSAtom* toJSAtom(JSContext* cx, CompilationAtomCache& atomCache) const; // Same as toJSAtom, but this is guaranteed to be instantiated. JSAtom* toExistingJSAtom(JSContext* cx, CompilationAtomCache& atomCache) const; // Convert NotInstantiated and usedByStencil entry to a js-atom. JSAtom* instantiate(JSContext* cx, CompilationAtomCache& atomCache) const; // Convert this entry to a number. bool toNumber(JSContext* cx, double* result) const; #if defined(DEBUG) || defined(JS_JITSPEW) void dump() const; void dumpCharsNoQuote(js::GenericPrinter& out) const; #endif }; class ParserAtom : public ParserAtomEntry { ParserAtom() = delete; ParserAtom(const ParserAtom&) = delete; }; class ParserName : public ParserAtom { ParserName() = delete; ParserName(const ParserName&) = delete; }; UniqueChars ParserAtomToPrintableString(JSContext* cx, const ParserAtom* atom); inline ParserName* ParserAtomEntry::asName() { MOZ_ASSERT(!isIndex()); return static_cast(this); } inline const ParserName* ParserAtomEntry::asName() const { MOZ_ASSERT(!isIndex()); return static_cast(this); } // A ParserAtomEntry with explicit inline storage. This is compatible with // constexpr to have builtin atoms. Care must be taken to ensure these atoms are // unique. template class StaticParserAtomEntry : public ParserAtomEntry { alignas(alignof(ParserAtomEntry)) char storage_[Length] = {}; public: constexpr StaticParserAtomEntry() = default; constexpr char* storage() { static_assert( offsetof(StaticParserAtomEntry, storage_) == sizeof(ParserAtomEntry), "StaticParserAtomEntry storage should follow ParserAtomEntry"); return storage_; } }; template <> class StaticParserAtomEntry<0> : public ParserAtomEntry { public: constexpr StaticParserAtomEntry() = default; }; /** * A lookup structure that allows for querying ParserAtoms in * a hashtable using a flexible input type that supports string * representations of various forms. */ class ParserAtomLookup { protected: HashNumber hash_; ParserAtomLookup(HashNumber hash) : hash_(hash) {} public: HashNumber hash() const { return hash_; } virtual bool equalsEntry(const ParserAtomEntry* entry) const = 0; }; struct ParserAtomLookupHasher { using Lookup = ParserAtomLookup; static inline HashNumber hash(const Lookup& l) { return l.hash(); } static inline bool match(const ParserAtomEntry* entry, const Lookup& l) { return l.equalsEntry(entry); } }; // We use this class to build a read-only constexpr table of ParserAtoms for the // well-known atoms set. This should be resolved at compile-time (including hash // computation) thanks to C++ constexpr. class WellKnownParserAtoms_ROM { // NOTE: While the well-known strings are all Latin1, we must use char16_t in // some places in order to have constexpr mozilla::HashString. using CharTraits = std::char_traits; using Char16Traits = std::char_traits; public: static const size_t ASCII_STATIC_LIMIT = 128U; static const size_t NUM_SMALL_CHARS = StaticStrings::NUM_SMALL_CHARS; static const size_t NUM_LENGTH2_ENTRIES = NUM_SMALL_CHARS * NUM_SMALL_CHARS; StaticParserAtomEntry<0> emptyAtom; StaticParserAtomEntry<1> length1Table[ASCII_STATIC_LIMIT]; StaticParserAtomEntry<2> length2Table[NUM_LENGTH2_ENTRIES]; #define PROPERTYNAME_FIELD_(_, name, text) \ StaticParserAtomEntry name; FOR_EACH_NONTINY_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD_) #undef PROPERTYNAME_FIELD_ #define PROPERTYNAME_FIELD_(name, _) \ StaticParserAtomEntry name; JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD_) #undef PROPERTYNAME_FIELD_ public: constexpr WellKnownParserAtoms_ROM() { // Empty atom emptyAtom.setHashAndLength(mozilla::HashString(u""), 0); emptyAtom.setWellKnownAtomId(WellKnownAtomId::empty); // Length-1 static atoms for (size_t i = 0; i < ASCII_STATIC_LIMIT; ++i) { init(length1Table[i], i); } // Length-2 static atoms for (size_t i = 0; i < NUM_LENGTH2_ENTRIES; ++i) { init(length2Table[i], i); } // Initialize each well-known property atoms #define PROPERTYNAME_FIELD_(_, name, text) \ init(name, name.storage(), u"" text, WellKnownAtomId::name); FOR_EACH_NONTINY_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD_) #undef PROPERTYNAME_FIELD_ // Initialize each well-known prototype atoms #define PROPERTYNAME_FIELD_(name, _) \ init(name, name.storage(), u"" #name, WellKnownAtomId::name); JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD_) #undef PROPERTYNAME_FIELD_ } private: // Initialization moved out of the constructor to workaround bug 1668238. static constexpr void init(StaticParserAtomEntry<1>& entry, size_t i) { size_t len = 1; char16_t buf[] = {static_cast(i), /* null-terminator */ 0}; entry.setHashAndLength(mozilla::HashString(buf), len); entry.setStaticParserString1(StaticParserString1(i)); entry.storage()[0] = buf[0]; } static constexpr void init(StaticParserAtomEntry<2>& entry, size_t i) { size_t len = 2; char16_t buf[] = {StaticStrings::fromSmallChar(i >> 6), StaticStrings::fromSmallChar(i & 0x003F), /* null-terminator */ 0}; entry.setHashAndLength(mozilla::HashString(buf), len); entry.setStaticParserString2(StaticParserString2(i)); entry.storage()[0] = buf[0]; entry.storage()[1] = buf[1]; } static constexpr void init(ParserAtomEntry& entry, char* storage, const char16_t* text, WellKnownAtomId id) { size_t len = Char16Traits::length(text); entry.setHashAndLength(mozilla::HashString(text), len); entry.setWellKnownAtomId(id); for (size_t i = 0; i < len; ++i) { storage[i] = text[i]; } } public: // Fast-path tiny strings since they are abundant in minified code. template const ParserAtom* lookupTiny(CharsT chars, size_t length) const { static_assert(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v, "This assert mostly explicitly documents the calling types, " "and forces that to be updated if new types show up."); switch (length) { case 0: return emptyAtom.asAtom(); case 1: { if (char16_t(chars[0]) < ASCII_STATIC_LIMIT) { size_t index = static_cast(chars[0]); return length1Table[index].asAtom(); } break; } case 2: if (StaticStrings::fitsInSmallChar(chars[0]) && StaticStrings::fitsInSmallChar(chars[1])) { size_t index = StaticStrings::getLength2Index(chars[0], chars[1]); return length2Table[index].asAtom(); } break; } // No match on tiny Atoms return nullptr; } }; using ParserAtomVector = Vector; using ParserAtomSpan = mozilla::Span; /** * WellKnownParserAtoms reserves a set of common ParserAtoms on the JSRuntime * in a read-only format to be used by parser. These reserved atoms can be * translated to equivalent JSAtoms in constant time. * * The common-names set allows the parser to lookup up specific atoms in * constant time. * * We also reserve tiny (length 1/2) parser-atoms for fast lookup similar to * the js::StaticStrings mechanism. This speeds up parsing minified code. */ class WellKnownParserAtoms { public: // Named fields allow quickly finding an atom if it is known at compile time. // This is particularly useful for the Parser. #define PROPERTYNAME_FIELD_(_, name, _2) const ParserName* name{}; FOR_EACH_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD_) #undef PROPERTYNAME_FIELD_ #define PROPERTYNAME_FIELD_(name, _) const ParserName* name{}; JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD_) #undef PROPERTYNAME_FIELD_ // The ParserAtomEntry of all well-known and tiny ParserAtoms are generated at // compile-time into a ROM that is computed using constexpr. This results in // the data being in the .rodata section of binary and easily shared by // multiple JS processes. static constexpr WellKnownParserAtoms_ROM rom_ = {}; // Common property and prototype names are tracked in a hash table. This table // does not key for any items already in a direct-indexing tiny atom table. using EntrySet = HashSet; EntrySet wellKnownSet_; bool initTinyStringAlias(JSContext* cx, const ParserName** name, const char* str); bool initSingle(JSContext* cx, const ParserName** name, const ParserAtomEntry& romEntry); public: bool init(JSContext* cx); // Maximum length of any well known atoms. This can be increased if needed. static constexpr size_t MaxWellKnownLength = 32; template const ParserAtom* lookupChar16Seq( const SpecificParserAtomLookup& lookup) const; template const ParserAtom* lookupTiny(CharsT chars, size_t length) const { return rom_.lookupTiny(chars, length); } const ParserAtom* getWellKnown(WellKnownAtomId atomId) const; static const ParserAtom* getStatic1(StaticParserString1 s); static const ParserAtom* getStatic2(StaticParserString2 s); }; bool InstantiateMarkedAtoms(JSContext* cx, const ParserAtomSpan& entries, CompilationAtomCache& atomCache); /** * A ParserAtomsTable owns and manages the vector of ParserAtom entries * associated with a given compile session. */ class ParserAtomsTable { private: const WellKnownParserAtoms& wellKnownTable_; LifoAlloc& alloc_; // The ParserAtomEntry are owned by the LifoAlloc. using EntrySet = HashSet; EntrySet entrySet_; ParserAtomVector entries_; public: ParserAtomsTable(JSRuntime* rt, LifoAlloc& alloc); ParserAtomsTable(ParserAtomsTable&&) = default; private: // Internal APIs for interning to the table after well-known atoms cases have // been tested. const ParserAtom* addEntry(JSContext* cx, EntrySet::AddPtr& addPtr, ParserAtomEntry* entry); template const ParserAtom* internChar16Seq(JSContext* cx, EntrySet::AddPtr& addPtr, HashNumber hash, InflatedChar16Sequence seq, uint32_t length); public: const ParserAtom* internAscii(JSContext* cx, const char* asciiPtr, uint32_t length); const ParserAtom* internLatin1(JSContext* cx, const JS::Latin1Char* latin1Ptr, uint32_t length); const ParserAtom* internUtf8(JSContext* cx, const mozilla::Utf8Unit* utf8Ptr, uint32_t nbyte); const ParserAtom* internChar16(JSContext* cx, const char16_t* char16Ptr, uint32_t length); const ParserAtom* internJSAtom(JSContext* cx, CompilationStencil& stencil, JSAtom* atom); const ParserAtom* concatAtoms(JSContext* cx, mozilla::Range atoms); const ParserAtom* getWellKnown(WellKnownAtomId atomId) const; const ParserAtom* getStatic1(StaticParserString1 s) const; const ParserAtom* getStatic2(StaticParserString2 s) const; const ParserAtom* getParserAtom(ParserAtomIndex index) const; const ParserAtom* getParserAtom(TaggedParserAtomIndex index) const; const ParserAtomVector& entries() const { return entries_; } }; // Lightweight version of ParserAtomsTable. // This doesn't support deduplication. // Used while decoding XDR. class ParserAtomSpanBuilder { private: const WellKnownParserAtoms& wellKnownTable_; ParserAtomSpan& entries_; public: ParserAtomSpanBuilder(JSRuntime* rt, ParserAtomSpan& entries); bool allocate(JSContext* cx, LifoAlloc& alloc, size_t count); size_t size() const { return entries_.size(); } void set(ParserAtomIndex index, const ParserAtomEntry* atom) { entries_[index] = const_cast(atom); } public: const ParserAtom* getWellKnown(WellKnownAtomId atomId) const; const ParserAtom* getStatic1(StaticParserString1 s) const; const ParserAtom* getStatic2(StaticParserString2 s) const; const ParserAtom* getParserAtom(ParserAtomIndex index) const; const ParserAtom* getParserAtom(TaggedParserAtomIndex index) const; }; template class SpecificParserAtomLookup : public ParserAtomLookup { // The sequence of characters to look up. InflatedChar16Sequence seq_; public: explicit SpecificParserAtomLookup(const InflatedChar16Sequence& seq) : SpecificParserAtomLookup(seq, seq.computeHash()) {} SpecificParserAtomLookup(const InflatedChar16Sequence& seq, HashNumber hash) : ParserAtomLookup(hash), seq_(seq) { MOZ_ASSERT(seq_.computeHash() == hash); } virtual bool equalsEntry(const ParserAtomEntry* entry) const override { return entry->equalsSeq(hash_, seq_); } }; template inline bool ParserAtomEntry::equalsSeq( HashNumber hash, InflatedChar16Sequence seq) const { // Compare hashes first. if (hash_ != hash) { return false; } if (hasTwoByteChars()) { const char16_t* chars = twoByteChars(); for (uint32_t i = 0; i < length_; i++) { if (!seq.hasMore() || chars[i] != seq.next()) { return false; } } } else { const Latin1Char* chars = latin1Chars(); for (uint32_t i = 0; i < length_; i++) { if (!seq.hasMore() || char16_t(chars[i]) != seq.next()) { return false; } } } return !seq.hasMore(); } JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId atomId); } /* namespace frontend */ } /* namespace js */ #endif // frontend_ParserAtom_h