summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/ParserAtom.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/ParserAtom.h')
-rw-r--r--js/src/frontend/ParserAtom.h905
1 files changed, 905 insertions, 0 deletions
diff --git a/js/src/frontend/ParserAtom.h b/js/src/frontend/ParserAtom.h
new file mode 100644
index 0000000000..ef1aae07a7
--- /dev/null
+++ b/js/src/frontend/ParserAtom.h
@@ -0,0 +1,905 @@
+/* -*- 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/MemoryReporting.h" // mozilla::MallocSizeOf
+#include "mozilla/Range.h" // mozilla::Range
+#include "mozilla/Span.h" // mozilla::Span
+#include "mozilla/TextUtils.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "jstypes.h"
+#include "NamespaceImports.h"
+
+#include "frontend/TypedIndex.h" // TypedIndex
+#include "js/HashTable.h" // HashMap
+#include "js/ProtoKey.h" // JS_FOR_EACH_PROTOTYPE
+#include "js/Symbol.h" // JS_FOR_EACH_WELL_KNOWN_SYMBOL
+#include "js/TypeDecls.h" // Latin1Char
+#include "js/Utility.h" // UniqueChars
+#include "js/Vector.h" // Vector
+#include "threading/Mutex.h" // Mutex
+#include "util/Text.h" // InflatedChar16Sequence
+#include "vm/CommonPropertyNames.h"
+#include "vm/StaticStrings.h"
+#include "vm/WellKnownAtom.h" // WellKnownAtomId, WellKnownAtomInfo
+
+struct JS_PUBLIC_API JSContext;
+
+class JSAtom;
+class JSString;
+
+namespace mozilla {
+union Utf8Unit;
+}
+
+namespace js {
+
+class AtomSet;
+class JS_PUBLIC_API GenericPrinter;
+class LifoAlloc;
+class StringBuffer;
+
+namespace frontend {
+
+struct CompilationAtomCache;
+struct CompilationStencil;
+
+template <typename CharT>
+class SpecificParserAtomLookup;
+
+// These types correspond into indices in the StaticStrings arrays.
+enum class Length1StaticParserString : uint8_t;
+enum class Length2StaticParserString : uint16_t;
+enum class Length3StaticParserString : uint8_t;
+
+class ParserAtom;
+using ParserAtomIndex = TypedIndex<ParserAtom>;
+
+// ParserAtomIndex, WellKnownAtomId, Length1StaticParserString,
+// Length2StaticParserString, Length3StaticParserString, or null.
+//
+// 0x0000_0000 Null atom
+//
+// 0x1YYY_YYYY 28-bit ParserAtom
+//
+// 0x2000_YYYY Well-known atom ID
+// 0x2001_YYYY Static length-1 atom : whole Latin1 range
+// 0x2002_YYYY Static length-2 atom : `[A-Za-z0-9$_]{2}`
+// 0x2003_YYYY Static length-3 atom : decimal "100" to "255"
+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 Length1StaticSubTag = 1 << SubTagShift;
+ static constexpr uint32_t Length2StaticSubTag = 2 << SubTagShift;
+ static constexpr uint32_t Length3StaticSubTag = 3 << SubTagShift;
+
+ public:
+ static constexpr uint32_t IndexLimit = Bit(IndexBit);
+ static constexpr uint32_t SmallIndexLimit = Bit(SmallIndexBit);
+
+ static constexpr size_t Length1StaticLimit = 256U;
+ static constexpr size_t Length2StaticLimit =
+ StaticStrings::NUM_LENGTH2_ENTRIES;
+ static constexpr size_t Length3StaticLimit = 256U;
+
+ 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);
+
+ // Length1Static/Length2Static string shouldn't use WellKnownAtomId.
+#define CHECK_(NAME, _) MOZ_ASSERT(index != WellKnownAtomId::NAME);
+ FOR_EACH_NON_EMPTY_TINY_PROPERTYNAME(CHECK_)
+#undef CHECK_
+ }
+ explicit constexpr TaggedParserAtomIndex(Length1StaticParserString index)
+ : data_(uint32_t(index) | WellKnownTag | Length1StaticSubTag) {}
+ explicit constexpr TaggedParserAtomIndex(Length2StaticParserString index)
+ : data_(uint32_t(index) | WellKnownTag | Length2StaticSubTag) {}
+ explicit constexpr TaggedParserAtomIndex(Length3StaticParserString index)
+ : data_(uint32_t(index) | WellKnownTag | Length3StaticSubTag) {}
+
+ class WellKnown {
+ public:
+#define METHOD_(NAME, _) \
+ static constexpr TaggedParserAtomIndex NAME() { \
+ return TaggedParserAtomIndex(WellKnownAtomId::NAME); \
+ }
+ FOR_EACH_NONTINY_COMMON_PROPERTYNAME(METHOD_)
+#undef METHOD_
+
+#define METHOD_(NAME, _) \
+ static constexpr TaggedParserAtomIndex NAME() { \
+ return TaggedParserAtomIndex(WellKnownAtomId::NAME); \
+ }
+ JS_FOR_EACH_PROTOTYPE(METHOD_)
+#undef METHOD_
+
+#define METHOD_(NAME) \
+ static constexpr TaggedParserAtomIndex NAME() { \
+ return TaggedParserAtomIndex(WellKnownAtomId::NAME); \
+ }
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(METHOD_)
+#undef METHOD_
+
+#define METHOD_(NAME, STR) \
+ static constexpr TaggedParserAtomIndex NAME() { \
+ return TaggedParserAtomIndex(Length1StaticParserString((STR)[0])); \
+ }
+ FOR_EACH_LENGTH1_PROPERTYNAME(METHOD_)
+#undef METHOD_
+
+#define METHOD_(NAME, STR) \
+ static constexpr TaggedParserAtomIndex NAME() { \
+ return TaggedParserAtomIndex(Length2StaticParserString( \
+ (StaticStrings::getLength2IndexStatic((STR)[0], (STR)[1])))); \
+ }
+ FOR_EACH_LENGTH2_PROPERTYNAME(METHOD_)
+#undef METHOD_
+
+ static constexpr TaggedParserAtomIndex empty() {
+ return TaggedParserAtomIndex(WellKnownAtomId::empty_);
+ }
+ };
+
+ // The value of rawData() for WellKnown TaggedParserAtomIndex.
+ // For using in switch-case.
+ class WellKnownRawData {
+ public:
+#define METHOD_(NAME, _) \
+ static constexpr uint32_t NAME() { \
+ return uint32_t(WellKnownAtomId::NAME) | WellKnownTag | WellKnownSubTag; \
+ }
+ FOR_EACH_NONTINY_COMMON_PROPERTYNAME(METHOD_)
+#undef METHOD_
+
+#define METHOD_(NAME, _) \
+ static constexpr uint32_t NAME() { \
+ return uint32_t(WellKnownAtomId::NAME) | WellKnownTag | WellKnownSubTag; \
+ }
+ JS_FOR_EACH_PROTOTYPE(METHOD_)
+#undef METHOD_
+
+#define METHOD_(NAME) \
+ static constexpr uint32_t NAME() { \
+ return uint32_t(WellKnownAtomId::NAME) | WellKnownTag | WellKnownSubTag; \
+ }
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(METHOD_)
+#undef METHOD_
+
+#define METHOD_(NAME, STR) \
+ static constexpr uint32_t NAME() { \
+ return uint32_t((STR)[0]) | WellKnownTag | Length1StaticSubTag; \
+ }
+ FOR_EACH_LENGTH1_PROPERTYNAME(METHOD_)
+#undef METHOD_
+
+#define METHOD_(NAME, STR) \
+ static constexpr uint32_t NAME() { \
+ return uint32_t( \
+ StaticStrings::getLength2IndexStatic((STR)[0], (STR)[1])) | \
+ WellKnownTag | Length2StaticSubTag; \
+ }
+ FOR_EACH_LENGTH2_PROPERTYNAME(METHOD_)
+#undef METHOD_
+
+ static constexpr uint32_t empty() {
+ return uint32_t(WellKnownAtomId::empty_) | WellKnownTag | WellKnownSubTag;
+ }
+ };
+
+ // NOTE: this is not well-known "null".
+ 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 isLength1StaticParserString() const {
+ return (data_ & (TagMask | SubTagMask)) ==
+ (WellKnownTag | Length1StaticSubTag);
+ }
+ bool isLength2StaticParserString() const {
+ return (data_ & (TagMask | SubTagMask)) ==
+ (WellKnownTag | Length2StaticSubTag);
+ }
+ bool isLength3StaticParserString() const {
+ return (data_ & (TagMask | SubTagMask)) ==
+ (WellKnownTag | Length3StaticSubTag);
+ }
+ bool isNull() const {
+ bool result = !data_;
+ MOZ_ASSERT_IF(result, (data_ & TagMask) == NullTag);
+ return result;
+ }
+ HashNumber staticOrWellKnownHash() const;
+
+ ParserAtomIndex toParserAtomIndex() const {
+ MOZ_ASSERT(isParserAtomIndex());
+ return ParserAtomIndex(data_ & IndexMask);
+ }
+ WellKnownAtomId toWellKnownAtomId() const {
+ MOZ_ASSERT(isWellKnownAtomId());
+ return WellKnownAtomId(data_ & SmallIndexMask);
+ }
+ Length1StaticParserString toLength1StaticParserString() const {
+ MOZ_ASSERT(isLength1StaticParserString());
+ return Length1StaticParserString(data_ & SmallIndexMask);
+ }
+ Length2StaticParserString toLength2StaticParserString() const {
+ MOZ_ASSERT(isLength2StaticParserString());
+ return Length2StaticParserString(data_ & SmallIndexMask);
+ }
+ Length3StaticParserString toLength3StaticParserString() const {
+ MOZ_ASSERT(isLength3StaticParserString());
+ return Length3StaticParserString(data_ & SmallIndexMask);
+ }
+
+ uint32_t* rawDataRef() { return &data_; }
+ uint32_t rawData() const { return data_; }
+
+ bool operator==(const TaggedParserAtomIndex& rhs) const {
+ return data_ == rhs.data_;
+ }
+ bool operator!=(const TaggedParserAtomIndex& rhs) const {
+ return data_ != rhs.data_;
+ }
+
+ explicit operator bool() const { return !isNull(); }
+};
+
+// Trivial variant of TaggedParserAtomIndex, to use in collection that requires
+// trivial type.
+// Provides minimal set of methods to use in collection.
+class TrivialTaggedParserAtomIndex {
+ uint32_t data_;
+
+ public:
+ static TrivialTaggedParserAtomIndex from(TaggedParserAtomIndex index) {
+ TrivialTaggedParserAtomIndex result;
+ result.data_ = index.rawData();
+ return result;
+ }
+
+ operator TaggedParserAtomIndex() const {
+ return TaggedParserAtomIndex::fromRaw(data_);
+ }
+
+ static TrivialTaggedParserAtomIndex null() {
+ TrivialTaggedParserAtomIndex result;
+ result.data_ = 0;
+ return result;
+ }
+
+ bool isNull() const {
+ static_assert(TaggedParserAtomIndex::NullTag == 0);
+ return data_ == 0;
+ }
+
+ uint32_t rawData() const { return data_; }
+
+ bool operator==(const TrivialTaggedParserAtomIndex& rhs) const {
+ return data_ == rhs.data_;
+ }
+ bool operator!=(const TrivialTaggedParserAtomIndex& rhs) const {
+ return data_ != rhs.data_;
+ }
+
+ explicit operator bool() const { return !isNull(); }
+};
+
+/**
+ * A ParserAtom 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 ParserAtom memory).
+ * 2. Inline char16_t storage (immediately after the ParserAtom memory).
+ */
+class alignas(alignof(uint32_t)) ParserAtom {
+ friend class ParserAtomsTable;
+ friend class WellKnownParserAtoms;
+
+ 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;
+ static constexpr uint32_t AtomizeFlag = 1 << 2;
+
+ public:
+ // Whether to atomize the ParserAtom during instantiation.
+ //
+ // If this ParserAtom is used by opcode with JOF_ATOM, or used as a binding
+ // in scope, it needs to be instantiated as JSAtom.
+ // Otherwise, it needs to be instantiated as LinearString, to reduce the
+ // cost of atomization.
+ enum class Atomize : uint32_t {
+ No = 0,
+ Yes = AtomizeFlag,
+ };
+
+ private:
+ // 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 <typename CharT, typename SeqCharT>
+ static void drainChar16Seq(CharT* buf, InflatedChar16Sequence<SeqCharT> seq,
+ uint32_t length) {
+ static_assert(
+ std::is_same_v<CharT, char16_t> || std::is_same_v<CharT, Latin1Char>,
+ "Invalid target buffer type.");
+ CharT* cur = buf;
+ while (seq.hasMore()) {
+ char16_t ch = seq.next();
+ if constexpr (std::is_same_v<CharT, Latin1Char>) {
+ 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;
+
+ uint32_t flags_ = 0;
+
+ // End of fields.
+
+ ParserAtom(uint32_t length, HashNumber hash, bool hasTwoByteChars)
+ : hash_(hash),
+ length_(length),
+ flags_(hasTwoByteChars ? HasTwoByteCharsFlag : 0) {}
+
+ public:
+ // The constexpr constructor is used by XDR
+ constexpr ParserAtom() = default;
+
+ // ParserAtoms may own their content buffers in variant_, and thus
+ // cannot be copy-constructed - as a new chars would need to be allocated.
+ ParserAtom(const ParserAtom&) = delete;
+ ParserAtom(ParserAtom&& other) = delete;
+
+ template <typename CharT, typename SeqCharT>
+ static ParserAtom* allocate(FrontendContext* fc, LifoAlloc& alloc,
+ InflatedChar16Sequence<SeqCharT> seq,
+ uint32_t length, HashNumber hash);
+
+ bool hasLatin1Chars() const { return !(flags_ & HasTwoByteCharsFlag); }
+ bool hasTwoByteChars() const { return flags_ & HasTwoByteCharsFlag; }
+
+ bool isAscii() const {
+ if (hasTwoByteChars()) {
+ return false;
+ }
+ for (Latin1Char ch : latin1Range()) {
+ if (!mozilla::IsAscii(ch)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool isPrivateName() const {
+ if (length() < 2) {
+ return false;
+ }
+
+ return charAt(0) == '#';
+ }
+
+ HashNumber hash() const { return hash_; }
+ uint32_t length() const { return length_; }
+
+ bool isUsedByStencil() const { return flags_ & UsedByStencilFlag; }
+
+ private:
+ bool isMarkedAtomize() const { return flags_ & AtomizeFlag; }
+
+ static constexpr uint32_t MinimumLengthForNonAtom = 8;
+
+ public:
+ bool isInstantiatedAsJSAtom() const;
+
+ template <typename CharT>
+ bool equalsSeq(HashNumber hash, InflatedChar16Sequence<CharT> seq) const;
+
+ // Convert NotInstantiated and usedByStencil entry to a js-atom.
+ JSString* instantiateString(JSContext* cx, FrontendContext* fc,
+ ParserAtomIndex index,
+ CompilationAtomCache& atomCache) const;
+ JSAtom* instantiateAtom(JSContext* cx, FrontendContext* fc,
+ ParserAtomIndex index,
+ CompilationAtomCache& atomCache) const;
+ JSAtom* instantiatePermanentAtom(JSContext* cx, FrontendContext* fc,
+ AtomSet& atomSet, ParserAtomIndex index,
+ CompilationAtomCache& atomCache) const;
+
+ private:
+ void markUsedByStencil(Atomize atomize) {
+ flags_ |= UsedByStencilFlag | uint32_t(atomize);
+ }
+ void markAtomize(Atomize atomize) { flags_ |= uint32_t(atomize); }
+
+ template <typename CharT>
+ const CharT* chars() const {
+ MOZ_ASSERT(sizeof(CharT) == (hasTwoByteChars() ? 2 : 1));
+ return reinterpret_cast<const CharT*>(this + 1);
+ }
+
+ template <typename CharT>
+ CharT* chars() {
+ MOZ_ASSERT(sizeof(CharT) == (hasTwoByteChars() ? 2 : 1));
+ return reinterpret_cast<CharT*>(this + 1);
+ }
+
+ const Latin1Char* latin1Chars() const { return chars<Latin1Char>(); }
+ const char16_t* twoByteChars() const { return chars<char16_t>(); }
+ mozilla::Range<const Latin1Char> latin1Range() const {
+ return mozilla::Range(latin1Chars(), length_);
+ }
+ mozilla::Range<const char16_t> twoByteRange() const {
+ return mozilla::Range(twoByteChars(), length_);
+ }
+
+ // Returns index-th char.
+ // Boundary check isn't performed.
+ char16_t charAt(size_t index) const {
+ MOZ_ASSERT(index < length());
+ if (hasLatin1Chars()) {
+ return latin1Chars()[index];
+ }
+ return twoByteChars()[index];
+ }
+
+ public:
+#if defined(DEBUG) || defined(JS_JITSPEW)
+ void dump() const;
+ void dumpCharsNoQuote(js::GenericPrinter& out) const;
+#endif
+};
+
+/**
+ * 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 ParserAtom* entry) const = 0;
+ virtual bool equalsEntry(const WellKnownAtomInfo* info) const = 0;
+};
+
+struct ParserAtomLookupHasher {
+ using Lookup = ParserAtomLookup;
+
+ static inline HashNumber hash(const Lookup& l) { return l.hash(); }
+ static inline bool match(const ParserAtom* entry, const Lookup& l) {
+ return l.equalsEntry(entry);
+ }
+};
+
+struct WellKnownAtomInfoHasher {
+ using Lookup = ParserAtomLookup;
+
+ static inline HashNumber hash(const Lookup& l) { return l.hash(); }
+ static inline bool match(const WellKnownAtomInfo* info, const Lookup& l) {
+ return l.equalsEntry(info);
+ }
+};
+
+using ParserAtomVector = Vector<ParserAtom*, 0, js::SystemAllocPolicy>;
+using ParserAtomSpan = mozilla::Span<ParserAtom*>;
+
+/**
+ * WellKnownParserAtoms allows the parser to lookup up specific atoms in
+ * constant time.
+ */
+class WellKnownParserAtoms {
+ static WellKnownParserAtoms singleton_;
+
+ // 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 EntryMap = HashMap<const WellKnownAtomInfo*, TaggedParserAtomIndex,
+ WellKnownAtomInfoHasher, js::SystemAllocPolicy>;
+ EntryMap wellKnownMap_;
+
+ bool initSingle(const WellKnownAtomInfo& info, TaggedParserAtomIndex index);
+
+ bool init();
+ void free();
+
+ public:
+ static bool initSingleton();
+ static void freeSingleton();
+
+ static WellKnownParserAtoms& getSingleton() {
+ MOZ_ASSERT(!singleton_.wellKnownMap_.empty());
+ return singleton_;
+ }
+
+ // Maximum length of any well known atoms. This can be increased if needed.
+ static constexpr size_t MaxWellKnownLength = 32;
+
+ template <typename CharT>
+ TaggedParserAtomIndex lookupChar16Seq(
+ const SpecificParserAtomLookup<CharT>& lookup) const;
+
+ template <typename CharsT>
+ TaggedParserAtomIndex lookupTinyIndex(CharsT chars, size_t length) const {
+ static_assert(std::is_same_v<CharsT, const Latin1Char*> ||
+ std::is_same_v<CharsT, const char16_t*> ||
+ std::is_same_v<CharsT, const char*> ||
+ std::is_same_v<CharsT, char16_t*>,
+ "This assert mostly explicitly documents the calling types, "
+ "and forces that to be updated if new types show up.");
+ switch (length) {
+ case 0:
+ return TaggedParserAtomIndex::WellKnown::empty();
+
+ case 1: {
+ if (char16_t(chars[0]) < TaggedParserAtomIndex::Length1StaticLimit) {
+ return TaggedParserAtomIndex(Length1StaticParserString(chars[0]));
+ }
+ break;
+ }
+
+ case 2:
+ if (StaticStrings::fitsInSmallChar(chars[0]) &&
+ StaticStrings::fitsInSmallChar(chars[1])) {
+ return TaggedParserAtomIndex(Length2StaticParserString(
+ StaticStrings::getLength2Index(chars[0], chars[1])));
+ }
+ break;
+
+ case 3: {
+ int i;
+ if (StaticStrings::fitsInLength3Static(chars[0], chars[1], chars[2],
+ &i)) {
+ return TaggedParserAtomIndex(Length3StaticParserString(i));
+ }
+ break;
+ }
+ }
+
+ // No match on tiny Atoms
+ return TaggedParserAtomIndex::null();
+ }
+
+ TaggedParserAtomIndex lookupTinyIndexUTF8(const mozilla::Utf8Unit* utf8Ptr,
+ size_t nbyte) const;
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return wellKnownMap_.shallowSizeOfExcludingThis(mallocSizeOf);
+ }
+};
+
+bool InstantiateMarkedAtoms(JSContext* cx, FrontendContext* fc,
+ const ParserAtomSpan& entries,
+ CompilationAtomCache& atomCache);
+
+bool InstantiateMarkedAtomsAsPermanent(JSContext* cx, FrontendContext* fc,
+ AtomSet& atomSet,
+ const ParserAtomSpan& entries,
+ CompilationAtomCache& atomCache);
+
+/**
+ * A ParserAtomsTable owns and manages the vector of ParserAtom entries
+ * associated with a given compile session.
+ */
+class ParserAtomsTable {
+ friend struct CompilationStencil;
+
+ private:
+ LifoAlloc* alloc_;
+
+ // The ParserAtom are owned by the LifoAlloc.
+ using EntryMap = HashMap<const ParserAtom*, TaggedParserAtomIndex,
+ ParserAtomLookupHasher, js::SystemAllocPolicy>;
+ EntryMap entryMap_;
+ ParserAtomVector entries_;
+
+ public:
+ explicit ParserAtomsTable(LifoAlloc& alloc);
+ ParserAtomsTable(ParserAtomsTable&&) = default;
+ ParserAtomsTable& operator=(ParserAtomsTable&& other) noexcept {
+ entryMap_ = std::move(other.entryMap_);
+ entries_ = std::move(other.entries_);
+ return *this;
+ }
+
+ void fixupAlloc(LifoAlloc& alloc) { alloc_ = &alloc; }
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return entryMap_.shallowSizeOfExcludingThis(mallocSizeOf) +
+ entries_.sizeOfExcludingThis(mallocSizeOf);
+ }
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+ }
+
+ private:
+ // Internal APIs for interning to the table after well-known atoms cases have
+ // been tested.
+ TaggedParserAtomIndex addEntry(FrontendContext* fc, EntryMap::AddPtr& addPtr,
+ ParserAtom* entry);
+ template <typename AtomCharT, typename SeqCharT>
+ TaggedParserAtomIndex internChar16Seq(FrontendContext* fc,
+ EntryMap::AddPtr& addPtr,
+ HashNumber hash,
+ InflatedChar16Sequence<SeqCharT> seq,
+ uint32_t length);
+
+ template <typename AtomCharT>
+ TaggedParserAtomIndex internExternalParserAtomImpl(FrontendContext* fc,
+ const ParserAtom* atom);
+
+ public:
+ TaggedParserAtomIndex internAscii(FrontendContext* fc, const char* asciiPtr,
+ uint32_t length);
+
+ TaggedParserAtomIndex internLatin1(FrontendContext* fc,
+ const JS::Latin1Char* latin1Ptr,
+ uint32_t length);
+
+ TaggedParserAtomIndex internUtf8(FrontendContext* fc,
+ const mozilla::Utf8Unit* utf8Ptr,
+ uint32_t nbyte);
+
+ TaggedParserAtomIndex internChar16(FrontendContext* fc,
+ const char16_t* char16Ptr,
+ uint32_t length);
+
+ TaggedParserAtomIndex internJSAtom(FrontendContext* fc,
+ CompilationAtomCache& atomCache,
+ JSAtom* atom);
+
+ // Intern ParserAtom data from other ParserAtomTable.
+ // This copies flags as well.
+ TaggedParserAtomIndex internExternalParserAtom(FrontendContext* fc,
+ const ParserAtom* atom);
+
+ // The atomIndex given as argument is in relation with the context Stencil.
+ // The atomIndex might be a well-known or static, in which case this function
+ // is a no-op.
+ TaggedParserAtomIndex internExternalParserAtomIndex(
+ FrontendContext* fc, const CompilationStencil& context,
+ TaggedParserAtomIndex atomIndex);
+
+ // Compare an internal atom index with an external atom index coming from the
+ // stencil given as argument.
+ bool isEqualToExternalParserAtomIndex(TaggedParserAtomIndex internal,
+ const CompilationStencil& context,
+ TaggedParserAtomIndex external) const;
+
+ bool addPlaceholder(FrontendContext* fc);
+
+ private:
+ const ParserAtom* getWellKnown(WellKnownAtomId atomId) const;
+ ParserAtom* getParserAtom(ParserAtomIndex index) const;
+
+ public:
+ const ParserAtomVector& entries() const { return entries_; }
+
+ // Accessors for querying atom properties.
+ bool isIdentifier(TaggedParserAtomIndex index) const;
+ bool isPrivateName(TaggedParserAtomIndex index) const;
+ bool isExtendedUnclonedSelfHostedFunctionName(
+ TaggedParserAtomIndex index) const;
+ bool isModuleExportName(TaggedParserAtomIndex index) const;
+ bool isIndex(TaggedParserAtomIndex index, uint32_t* indexp) const;
+ bool isInstantiatedAsJSAtom(TaggedParserAtomIndex index) const;
+ uint32_t length(TaggedParserAtomIndex index) const;
+ HashNumber hash(TaggedParserAtomIndex index) const;
+
+ // Methods for atom.
+ void markUsedByStencil(TaggedParserAtomIndex index,
+ ParserAtom::Atomize atomize) const;
+ void markAtomize(TaggedParserAtomIndex index,
+ ParserAtom::Atomize atomize) const;
+ double toNumber(TaggedParserAtomIndex index) const;
+ UniqueChars toNewUTF8CharsZ(FrontendContext* fc,
+ TaggedParserAtomIndex index) const;
+ UniqueChars toPrintableString(TaggedParserAtomIndex index) const;
+ UniqueChars toQuotedString(TaggedParserAtomIndex index) const;
+ JSAtom* toJSAtom(JSContext* cx, FrontendContext* fc,
+ TaggedParserAtomIndex index,
+ CompilationAtomCache& atomCache) const;
+
+ private:
+ JSAtom* toWellKnownJSAtom(JSContext* cx, TaggedParserAtomIndex index) const;
+
+ public:
+ bool appendTo(StringBuffer& buffer, TaggedParserAtomIndex index) const;
+
+ public:
+#if defined(DEBUG) || defined(JS_JITSPEW)
+ void dump(TaggedParserAtomIndex index) const;
+ void dumpCharsNoQuote(js::GenericPrinter& out,
+ TaggedParserAtomIndex index) const;
+
+ static void dumpCharsNoQuote(js::GenericPrinter& out, WellKnownAtomId id);
+ static void dumpCharsNoQuote(js::GenericPrinter& out,
+ Length1StaticParserString index);
+ static void dumpCharsNoQuote(js::GenericPrinter& out,
+ Length2StaticParserString index);
+ static void dumpCharsNoQuote(js::GenericPrinter& out,
+ Length3StaticParserString index);
+#endif
+
+ static void getLength1Content(Length1StaticParserString s,
+ Latin1Char contents[1]) {
+ contents[0] = Latin1Char(s);
+ }
+
+ static void getLength2Content(Length2StaticParserString s, char contents[2]) {
+ contents[0] = StaticStrings::firstCharOfLength2(size_t(s));
+ contents[1] = StaticStrings::secondCharOfLength2(size_t(s));
+ }
+
+ static void getLength3Content(Length3StaticParserString s, char contents[3]) {
+ contents[0] = StaticStrings::firstCharOfLength3(int32_t(s));
+ contents[1] = StaticStrings::secondCharOfLength3(int32_t(s));
+ contents[2] = StaticStrings::thirdCharOfLength3(int32_t(s));
+ }
+};
+
+// Lightweight version of ParserAtomsTable.
+// This doesn't support deduplication.
+// Used while decoding XDR.
+class ParserAtomSpanBuilder {
+ ParserAtomSpan& entries_;
+
+ public:
+ explicit ParserAtomSpanBuilder(ParserAtomSpan& entries) : entries_(entries) {}
+
+ bool allocate(FrontendContext* fc, LifoAlloc& alloc, size_t count);
+
+ void set(ParserAtomIndex index, const ParserAtom* atom) {
+ entries_[index] = const_cast<ParserAtom*>(atom);
+ }
+};
+
+template <typename CharT>
+class SpecificParserAtomLookup : public ParserAtomLookup {
+ // The sequence of characters to look up.
+ InflatedChar16Sequence<CharT> seq_;
+
+ public:
+ explicit SpecificParserAtomLookup(const InflatedChar16Sequence<CharT>& seq)
+ : SpecificParserAtomLookup(seq, seq.computeHash()) {}
+
+ SpecificParserAtomLookup(const InflatedChar16Sequence<CharT>& seq,
+ HashNumber hash)
+ : ParserAtomLookup(hash), seq_(seq) {
+ MOZ_ASSERT(seq_.computeHash() == hash);
+ }
+
+ virtual bool equalsEntry(const ParserAtom* entry) const override {
+ return entry->equalsSeq<CharT>(hash_, seq_);
+ }
+
+ virtual bool equalsEntry(const WellKnownAtomInfo* info) const override {
+ // Compare hashes first.
+ if (info->hash != hash_) {
+ return false;
+ }
+
+ InflatedChar16Sequence<CharT> seq = seq_;
+ for (uint32_t i = 0; i < info->length; i++) {
+ if (!seq.hasMore() || char16_t(info->content[i]) != seq.next()) {
+ return false;
+ }
+ }
+ return !seq.hasMore();
+ }
+};
+
+template <typename CharT>
+inline bool ParserAtom::equalsSeq(HashNumber hash,
+ InflatedChar16Sequence<CharT> 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