summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/ParserAtom.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/frontend/ParserAtom.cpp
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/frontend/ParserAtom.cpp')
-rw-r--r--js/src/frontend/ParserAtom.cpp864
1 files changed, 864 insertions, 0 deletions
diff --git a/js/src/frontend/ParserAtom.cpp b/js/src/frontend/ParserAtom.cpp
new file mode 100644
index 0000000000..e310549671
--- /dev/null
+++ b/js/src/frontend/ParserAtom.cpp
@@ -0,0 +1,864 @@
+/* -*- 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/. */
+
+#include "frontend/ParserAtom.h"
+
+#include <memory> // std::uninitialized_fill_n
+#include <type_traits>
+
+#include "jsnum.h"
+
+#include "frontend/CompilationInfo.h"
+#include "frontend/NameCollections.h"
+#include "frontend/StencilXdr.h" // CanCopyDataToDisk
+#include "vm/JSContext.h"
+#include "vm/Printer.h"
+#include "vm/Runtime.h"
+#include "vm/StringType.h"
+
+using namespace js;
+using namespace js::frontend;
+
+namespace js {
+
+// Iterates over a sequence of ParserAtoms and yield their sequence of
+// characters in order. This simulates concatenation of atoms. The underlying
+// ParserAtoms may be a mix of Latin1 and char16_t atoms.
+template <>
+class InflatedChar16Sequence<const ParserAtom*> {
+ private:
+ const ParserAtom** cur_ = nullptr;
+ const ParserAtom** lim_ = nullptr;
+ size_t index_ = 0;
+
+ void settle() {
+ // Check if we are out-of-bounds for current ParserAtom.
+ auto outOfBounds = [this]() { return index_ >= (*cur_)->length(); };
+
+ while (hasMore() && outOfBounds()) {
+ // Advance to start of next ParserAtom.
+ cur_++;
+ index_ = 0;
+ }
+ }
+
+ public:
+ explicit InflatedChar16Sequence(
+ const mozilla::Range<const ParserAtom*>& atoms)
+ : cur_(atoms.begin().get()), lim_(atoms.end().get()) {
+ settle();
+ }
+
+ bool hasMore() { return cur_ < lim_; }
+
+ char16_t next() {
+ MOZ_ASSERT(hasMore());
+ char16_t ch = (*cur_)->hasLatin1Chars() ? (*cur_)->latin1Chars()[index_]
+ : (*cur_)->twoByteChars()[index_];
+ index_++;
+ settle();
+ return ch;
+ }
+
+ HashNumber computeHash() const {
+ auto copy = *this;
+ HashNumber hash = 0;
+
+ while (copy.hasMore()) {
+ hash = mozilla::AddToHash(hash, copy.next());
+ }
+ return hash;
+ }
+};
+
+} // namespace js
+
+namespace js {
+
+template <>
+class InflatedChar16Sequence<LittleEndianChars> {
+ private:
+ LittleEndianChars chars_;
+ size_t idx_;
+ size_t len_;
+
+ public:
+ InflatedChar16Sequence(LittleEndianChars chars, size_t length)
+ : chars_(chars), idx_(0), len_(length) {}
+
+ bool hasMore() { return idx_ < len_; }
+
+ char16_t next() {
+ MOZ_ASSERT(hasMore());
+ return chars_[idx_++];
+ }
+
+ HashNumber computeHash() const {
+ auto copy = *this;
+ HashNumber hash = 0;
+ while (copy.hasMore()) {
+ hash = mozilla::AddToHash(hash, copy.next());
+ }
+ return hash;
+ }
+};
+
+} // namespace js
+
+namespace js {
+namespace frontend {
+
+JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId atomId) {
+#define ASSERT_OFFSET_(idpart, id, text) \
+ static_assert(offsetof(JSAtomState, id) == \
+ int32_t(WellKnownAtomId::id) * \
+ sizeof(js::ImmutablePropertyNamePtr));
+ FOR_EACH_COMMON_PROPERTYNAME(ASSERT_OFFSET_);
+#undef ASSERT_OFFSET_
+
+#define ASSERT_OFFSET_(name, clasp) \
+ static_assert(offsetof(JSAtomState, name) == \
+ int32_t(WellKnownAtomId::name) * \
+ sizeof(js::ImmutablePropertyNamePtr));
+ JS_FOR_EACH_PROTOTYPE(ASSERT_OFFSET_);
+#undef ASSERT_OFFSET_
+
+ static_assert(int32_t(WellKnownAtomId::abort) == 0,
+ "Unexpected order of WellKnownAtom");
+
+ return (&cx->names().abort)[int32_t(atomId)];
+}
+
+#ifdef DEBUG
+void TaggedParserAtomIndex::validateRaw() {
+ if (isParserAtomIndex()) {
+ MOZ_ASSERT(toParserAtomIndex().index < IndexLimit);
+ } else if (isWellKnownAtomId()) {
+ MOZ_ASSERT(uint32_t(toWellKnownAtomId()) <
+ uint32_t(WellKnownAtomId::Limit));
+ } else if (isStaticParserString1()) {
+ MOZ_ASSERT(size_t(toStaticParserString1()) <
+ WellKnownParserAtoms_ROM::ASCII_STATIC_LIMIT);
+ } else if (isStaticParserString2()) {
+ MOZ_ASSERT(size_t(toStaticParserString2()) <
+ WellKnownParserAtoms_ROM::NUM_LENGTH2_ENTRIES);
+ } else {
+ MOZ_ASSERT(isNull());
+ }
+}
+#endif
+
+template <typename CharT, typename SeqCharT>
+/* static */ ParserAtomEntry* ParserAtomEntry::allocate(
+ JSContext* cx, LifoAlloc& alloc, InflatedChar16Sequence<SeqCharT> seq,
+ uint32_t length, HashNumber hash) {
+ constexpr size_t HeaderSize = sizeof(ParserAtomEntry);
+ void* raw = alloc.alloc(HeaderSize + (sizeof(CharT) * length));
+ if (!raw) {
+ js::ReportOutOfMemory(cx);
+ return nullptr;
+ }
+
+ constexpr bool hasTwoByteChars = (sizeof(CharT) == 2);
+ static_assert(sizeof(CharT) == 1 || sizeof(CharT) == 2,
+ "CharT should be 1 or 2 byte type");
+ ParserAtomEntry* entry =
+ new (raw) ParserAtomEntry(length, hash, hasTwoByteChars);
+ CharT* entryBuf = entry->chars<CharT>();
+ drainChar16Seq(entryBuf, seq, length);
+ return entry;
+}
+
+/* static */ ParserAtomEntry* ParserAtomEntry::allocateRaw(
+ JSContext* cx, LifoAlloc& alloc, const uint8_t* srcRaw,
+ size_t totalLength) {
+ void* raw = alloc.alloc(totalLength);
+ if (!raw) {
+ js::ReportOutOfMemory(cx);
+ return nullptr;
+ }
+
+ memcpy(raw, srcRaw, totalLength);
+
+ return static_cast<ParserAtomEntry*>(raw);
+}
+
+bool ParserAtomEntry::equalsJSAtom(JSAtom* other) const {
+ // Compare hashes and lengths first.
+ if (hash_ != other->hash() || length_ != other->length()) {
+ return false;
+ }
+
+ JS::AutoCheckCannotGC nogc;
+
+ if (hasTwoByteChars()) {
+ // Compare heap-allocated 16-bit chars to atom.
+ return other->hasLatin1Chars()
+ ? EqualChars(twoByteChars(), other->latin1Chars(nogc), length_)
+ : EqualChars(twoByteChars(), other->twoByteChars(nogc), length_);
+ }
+
+ MOZ_ASSERT(hasLatin1Chars());
+ return other->hasLatin1Chars()
+ ? EqualChars(latin1Chars(), other->latin1Chars(nogc), length_)
+ : EqualChars(latin1Chars(), other->twoByteChars(nogc), length_);
+}
+
+template <typename CharT>
+UniqueChars ToPrintableStringImpl(JSContext* cx, mozilla::Range<CharT> str) {
+ Sprinter sprinter(cx);
+ if (!sprinter.init()) {
+ return nullptr;
+ }
+ if (!QuoteString<QuoteTarget::String>(&sprinter, str)) {
+ return nullptr;
+ }
+ return sprinter.release();
+}
+
+UniqueChars ParserAtomToPrintableString(JSContext* cx, const ParserAtom* atom) {
+ size_t length = atom->length();
+
+ return atom->hasLatin1Chars()
+ ? ToPrintableStringImpl(
+ cx, mozilla::Range(atom->latin1Chars(), length))
+ : ToPrintableStringImpl(
+ cx, mozilla::Range(atom->twoByteChars(), length));
+}
+
+bool ParserAtomEntry::isIndex(uint32_t* indexp) const {
+ size_t len = length();
+ if (len == 0 || len > UINT32_CHAR_BUFFER_LENGTH) {
+ return false;
+ }
+ if (hasLatin1Chars()) {
+ return mozilla::IsAsciiDigit(*latin1Chars()) &&
+ js::CheckStringIsIndex(latin1Chars(), len, indexp);
+ }
+ return mozilla::IsAsciiDigit(*twoByteChars()) &&
+ js::CheckStringIsIndex(twoByteChars(), len, indexp);
+}
+
+JSAtom* ParserAtomEntry::toJSAtom(JSContext* cx,
+ CompilationAtomCache& atomCache) const {
+ if (isParserAtomIndex()) {
+ JSAtom* atom = atomCache.getAtomAt(toParserAtomIndex());
+ if (atom) {
+ return atom;
+ }
+
+ return instantiate(cx, atomCache);
+ }
+
+ if (isWellKnownAtomId()) {
+ return GetWellKnownAtom(cx, toWellKnownAtomId());
+ }
+
+ if (isStaticParserString1()) {
+ char16_t ch = static_cast<char16_t>(toStaticParserString1());
+ return cx->staticStrings().getUnit(ch);
+ }
+
+ MOZ_ASSERT(isStaticParserString2());
+ size_t s = static_cast<size_t>(toStaticParserString2());
+ return cx->staticStrings().getLength2FromIndex(s);
+}
+
+JSAtom* ParserAtomEntry::toExistingJSAtom(
+ JSContext* cx, CompilationAtomCache& atomCache) const {
+ if (isParserAtomIndex()) {
+ JSAtom* atom = atomCache.getExistingAtomAt(toParserAtomIndex());
+ MOZ_ASSERT(atom);
+ return atom;
+ }
+
+ if (isWellKnownAtomId()) {
+ return GetWellKnownAtom(cx, toWellKnownAtomId());
+ }
+
+ if (isStaticParserString1()) {
+ char16_t ch = static_cast<char16_t>(toStaticParserString1());
+ return cx->staticStrings().getUnit(ch);
+ }
+
+ MOZ_ASSERT(isStaticParserString2());
+ size_t s = static_cast<size_t>(toStaticParserString2());
+ return cx->staticStrings().getLength2FromIndex(s);
+}
+
+JSAtom* ParserAtomEntry::instantiate(JSContext* cx,
+ CompilationAtomCache& atomCache) const {
+ JSAtom* atom;
+ if (hasLatin1Chars()) {
+ atom = AtomizeChars(cx, hash(), latin1Chars(), length());
+ } else {
+ atom = AtomizeChars(cx, hash(), twoByteChars(), length());
+ }
+ if (!atom) {
+ js::ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ if (!atomCache.setAtomAt(cx, toParserAtomIndex(), atom)) {
+ return nullptr;
+ }
+
+ return atom;
+}
+
+bool ParserAtomEntry::toNumber(JSContext* cx, double* result) const {
+ return hasLatin1Chars() ? CharsToNumber(cx, latin1Chars(), length(), result)
+ : CharsToNumber(cx, twoByteChars(), length(), result);
+}
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+void ParserAtomEntry::dump() const {
+ js::Fprinter out(stderr);
+ out.put("\"");
+ dumpCharsNoQuote(out);
+ out.put("\"\n");
+}
+
+void ParserAtomEntry::dumpCharsNoQuote(js::GenericPrinter& out) const {
+ if (hasLatin1Chars()) {
+ JSString::dumpCharsNoQuote<Latin1Char>(latin1Chars(), length(), out);
+ } else {
+ JSString::dumpCharsNoQuote<char16_t>(twoByteChars(), length(), out);
+ }
+}
+#endif
+
+ParserAtomsTable::ParserAtomsTable(JSRuntime* rt, LifoAlloc& alloc)
+ : wellKnownTable_(*rt->commonParserNames), alloc_(alloc) {}
+
+const ParserAtom* ParserAtomsTable::addEntry(JSContext* cx,
+ EntrySet::AddPtr& addPtr,
+ ParserAtomEntry* entry) {
+ MOZ_ASSERT(!addPtr);
+ ParserAtomIndex index = ParserAtomIndex(entries_.length());
+ if (size_t(index) >= TaggedParserAtomIndex::IndexLimit) {
+ ReportAllocationOverflow(cx);
+ return nullptr;
+ }
+ if (!entries_.append(entry)) {
+ js::ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ entry->setParserAtomIndex(index);
+ if (!entrySet_.add(addPtr, entry)) {
+ js::ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ return entry->asAtom();
+}
+
+template <typename AtomCharT, typename SeqCharT>
+const ParserAtom* ParserAtomsTable::internChar16Seq(
+ JSContext* cx, EntrySet::AddPtr& addPtr, HashNumber hash,
+ InflatedChar16Sequence<SeqCharT> seq, uint32_t length) {
+ MOZ_ASSERT(!addPtr);
+
+ ParserAtomEntry* entry =
+ ParserAtomEntry::allocate<AtomCharT>(cx, alloc_, seq, length, hash);
+ if (!entry) {
+ return nullptr;
+ }
+ return addEntry(cx, addPtr, entry);
+}
+
+static const uint16_t MAX_LATIN1_CHAR = 0xff;
+
+const ParserAtom* ParserAtomsTable::internAscii(JSContext* cx,
+ const char* asciiPtr,
+ uint32_t length) {
+ // ASCII strings are strict subsets of Latin1 strings.
+ const Latin1Char* latin1Ptr = reinterpret_cast<const Latin1Char*>(asciiPtr);
+ return internLatin1(cx, latin1Ptr, length);
+}
+
+const ParserAtom* ParserAtomsTable::internLatin1(JSContext* cx,
+ const Latin1Char* latin1Ptr,
+ uint32_t length) {
+ // Check for tiny strings which are abundant in minified code.
+ if (const ParserAtom* tiny = wellKnownTable_.lookupTiny(latin1Ptr, length)) {
+ return tiny;
+ }
+
+ // Check for well-known atom.
+ InflatedChar16Sequence<Latin1Char> seq(latin1Ptr, length);
+ SpecificParserAtomLookup<Latin1Char> lookup(seq);
+ if (const ParserAtom* wk = wellKnownTable_.lookupChar16Seq(lookup)) {
+ return wk;
+ }
+
+ // Check for existing atom.
+ auto addPtr = entrySet_.lookupForAdd(lookup);
+ if (addPtr) {
+ return (*addPtr)->asAtom();
+ }
+
+ return internChar16Seq<Latin1Char>(cx, addPtr, lookup.hash(), seq, length);
+}
+
+ParserAtomSpanBuilder::ParserAtomSpanBuilder(JSRuntime* rt,
+ ParserAtomSpan& entries)
+ : wellKnownTable_(*rt->commonParserNames), entries_(entries) {}
+
+bool ParserAtomSpanBuilder::allocate(JSContext* cx, LifoAlloc& alloc,
+ size_t count) {
+ if (count >= TaggedParserAtomIndex::IndexLimit) {
+ ReportAllocationOverflow(cx);
+ return false;
+ }
+
+ auto* p = alloc.newArrayUninitialized<ParserAtomEntry*>(count);
+ if (!p) {
+ js::ReportOutOfMemory(cx);
+ return false;
+ }
+ std::uninitialized_fill_n(p, count, nullptr);
+
+ entries_ = mozilla::Span(p, count);
+ return true;
+}
+
+const ParserAtom* ParserAtomsTable::internUtf8(JSContext* cx,
+ const mozilla::Utf8Unit* utf8Ptr,
+ uint32_t nbyte) {
+ // Check for tiny strings which are abundant in minified code.
+ // NOTE: The tiny atoms are all ASCII-only so we can directly look at the
+ // UTF-8 data without worrying about surrogates.
+ if (const ParserAtom* tiny = wellKnownTable_.lookupTiny(
+ reinterpret_cast<const Latin1Char*>(utf8Ptr), nbyte)) {
+ return tiny;
+ }
+
+ // If source text is ASCII, then the length of the target char buffer
+ // is the same as the length of the UTF8 input. Convert it to a Latin1
+ // encoded string on the heap.
+ JS::UTF8Chars utf8(utf8Ptr, nbyte);
+ JS::SmallestEncoding minEncoding = FindSmallestEncoding(utf8);
+ if (minEncoding == JS::SmallestEncoding::ASCII) {
+ // As ascii strings are a subset of Latin1 strings, and each encoding
+ // unit is the same size, we can reliably cast this `Utf8Unit*`
+ // to a `Latin1Char*`.
+ const Latin1Char* latin1Ptr = reinterpret_cast<const Latin1Char*>(utf8Ptr);
+ return internLatin1(cx, latin1Ptr, nbyte);
+ }
+
+ // Check for existing.
+ // NOTE: Well-known are all ASCII so have been handled above.
+ InflatedChar16Sequence<mozilla::Utf8Unit> seq(utf8Ptr, nbyte);
+ SpecificParserAtomLookup<mozilla::Utf8Unit> lookup(seq);
+ MOZ_ASSERT(wellKnownTable_.lookupChar16Seq(lookup) == nullptr);
+ EntrySet::AddPtr addPtr = entrySet_.lookupForAdd(lookup);
+ if (addPtr) {
+ return (*addPtr)->asAtom();
+ }
+
+ // Compute length in code-points.
+ uint32_t length = 0;
+ InflatedChar16Sequence<mozilla::Utf8Unit> seqCopy = seq;
+ while (seqCopy.hasMore()) {
+ mozilla::Unused << seqCopy.next();
+ length += 1;
+ }
+
+ // Otherwise, add new entry.
+ bool wide = (minEncoding == JS::SmallestEncoding::UTF16);
+ return wide
+ ? internChar16Seq<char16_t>(cx, addPtr, lookup.hash(), seq, length)
+ : internChar16Seq<Latin1Char>(cx, addPtr, lookup.hash(), seq,
+ length);
+}
+
+const ParserAtom* ParserAtomsTable::internChar16(JSContext* cx,
+ const char16_t* char16Ptr,
+ uint32_t length) {
+ // Check for tiny strings which are abundant in minified code.
+ if (const ParserAtom* tiny = wellKnownTable_.lookupTiny(char16Ptr, length)) {
+ return tiny;
+ }
+
+ // Check against well-known.
+ InflatedChar16Sequence<char16_t> seq(char16Ptr, length);
+ SpecificParserAtomLookup<char16_t> lookup(seq);
+ if (const ParserAtom* wk = wellKnownTable_.lookupChar16Seq(lookup)) {
+ return wk;
+ }
+
+ // Check for existing atom.
+ EntrySet::AddPtr addPtr = entrySet_.lookupForAdd(lookup);
+ if (addPtr) {
+ return (*addPtr)->asAtom();
+ }
+
+ // Compute the target encoding.
+ // NOTE: Length in code-points will be same, even if we deflate to Latin1.
+ bool wide = false;
+ InflatedChar16Sequence<char16_t> seqCopy = seq;
+ while (seqCopy.hasMore()) {
+ char16_t ch = seqCopy.next();
+ if (ch > MAX_LATIN1_CHAR) {
+ wide = true;
+ break;
+ }
+ }
+
+ // Otherwise, add new entry.
+ return wide
+ ? internChar16Seq<char16_t>(cx, addPtr, lookup.hash(), seq, length)
+ : internChar16Seq<Latin1Char>(cx, addPtr, lookup.hash(), seq,
+ length);
+}
+
+const ParserAtom* ParserAtomsTable::internJSAtom(JSContext* cx,
+ CompilationStencil& stencil,
+ JSAtom* atom) {
+ const ParserAtom* parserAtom;
+ {
+ JS::AutoCheckCannotGC nogc;
+
+ parserAtom =
+ atom->hasLatin1Chars()
+ ? internLatin1(cx, atom->latin1Chars(nogc), atom->length())
+ : internChar16(cx, atom->twoByteChars(nogc), atom->length());
+ if (!parserAtom) {
+ return nullptr;
+ }
+ }
+
+ if (parserAtom->isParserAtomIndex()) {
+ ParserAtomIndex index = parserAtom->toParserAtomIndex();
+ auto& atomCache = stencil.input.atomCache;
+
+ if (!atomCache.hasAtomAt(index)) {
+ if (!atomCache.setAtomAt(cx, index, atom)) {
+ return nullptr;
+ }
+ }
+ }
+
+ // We should (infallibly) map back to the same JSAtom.
+ MOZ_ASSERT(parserAtom->toJSAtom(cx, stencil.input.atomCache) == atom);
+
+ return parserAtom;
+}
+
+const ParserAtom* ParserAtomsTable::concatAtoms(
+ JSContext* cx, mozilla::Range<const ParserAtom*> atoms) {
+ MOZ_ASSERT(atoms.length() >= 2,
+ "concatAtoms should only be used for multiple inputs");
+
+ // Compute final length and encoding.
+ bool catLatin1 = true;
+ uint32_t catLen = 0;
+ for (const ParserAtom* atom : atoms) {
+ if (atom->hasTwoByteChars()) {
+ catLatin1 = false;
+ }
+ // Overflow check here, length
+ if (atom->length() >= (ParserAtomEntry::MAX_LENGTH - catLen)) {
+ js::ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ catLen += atom->length();
+ }
+
+ // Short Latin1 strings must check for both Tiny and WellKnown atoms so simple
+ // concatenate onto stack and use `internLatin1`.
+ if (catLatin1 && (catLen <= WellKnownParserAtoms::MaxWellKnownLength)) {
+ Latin1Char buf[WellKnownParserAtoms::MaxWellKnownLength];
+ size_t offset = 0;
+ for (const ParserAtom* atom : atoms) {
+ mozilla::PodCopy(buf + offset, atom->latin1Chars(), atom->length());
+ offset += atom->length();
+ }
+ return internLatin1(cx, buf, catLen);
+ }
+
+ // NOTE: We have ruled out Tiny and WellKnown atoms and can ignore below.
+
+ InflatedChar16Sequence<const ParserAtom*> seq(atoms);
+ SpecificParserAtomLookup<const ParserAtom*> lookup(seq);
+
+ // Check for existing atom.
+ auto addPtr = entrySet_.lookupForAdd(lookup);
+ if (addPtr) {
+ return (*addPtr)->asAtom();
+ }
+
+ // Otherwise, add new entry.
+ return catLatin1 ? internChar16Seq<Latin1Char>(cx, addPtr, lookup.hash(), seq,
+ catLen)
+ : internChar16Seq<char16_t>(cx, addPtr, lookup.hash(), seq,
+ catLen);
+}
+
+const ParserAtom* WellKnownParserAtoms::getWellKnown(
+ WellKnownAtomId atomId) const {
+#define ASSERT_OFFSET_(idpart, id, text) \
+ static_assert(offsetof(WellKnownParserAtoms, id) == \
+ int32_t(WellKnownAtomId::id) * sizeof(ParserAtom*));
+ FOR_EACH_COMMON_PROPERTYNAME(ASSERT_OFFSET_);
+#undef ASSERT_OFFSET_
+
+#define ASSERT_OFFSET_(name, clasp) \
+ static_assert(offsetof(WellKnownParserAtoms, name) == \
+ int32_t(WellKnownAtomId::name) * sizeof(ParserAtom*));
+ JS_FOR_EACH_PROTOTYPE(ASSERT_OFFSET_);
+#undef ASSERT_OFFSET_
+
+ static_assert(int32_t(WellKnownAtomId::abort) == 0,
+ "Unexpected order of WellKnownAtom");
+
+ return (&abort)[int32_t(atomId)];
+}
+
+/* static */
+const ParserAtom* WellKnownParserAtoms::getStatic1(StaticParserString1 s) {
+ return WellKnownParserAtoms::rom_.length1Table[size_t(s)].asAtom();
+}
+
+/* static */
+const ParserAtom* WellKnownParserAtoms::getStatic2(StaticParserString2 s) {
+ return WellKnownParserAtoms::rom_.length2Table[size_t(s)].asAtom();
+}
+
+const ParserAtom* ParserAtomSpanBuilder::getWellKnown(
+ WellKnownAtomId atomId) const {
+ return wellKnownTable_.getWellKnown(atomId);
+}
+
+const ParserAtom* ParserAtomSpanBuilder::getStatic1(
+ StaticParserString1 s) const {
+ return WellKnownParserAtoms::getStatic1(s);
+}
+
+const ParserAtom* ParserAtomSpanBuilder::getStatic2(
+ StaticParserString2 s) const {
+ return WellKnownParserAtoms::getStatic2(s);
+}
+
+const ParserAtom* ParserAtomSpanBuilder::getParserAtom(
+ ParserAtomIndex index) const {
+ return entries_[index]->asAtom();
+}
+
+template <class T>
+const ParserAtom* GetParserAtom(T self, TaggedParserAtomIndex index) {
+ if (index.isParserAtomIndex()) {
+ return self->getParserAtom(index.toParserAtomIndex());
+ }
+
+ if (index.isWellKnownAtomId()) {
+ return self->getWellKnown(index.toWellKnownAtomId());
+ }
+
+ if (index.isStaticParserString1()) {
+ return self->getStatic1(index.toStaticParserString1());
+ }
+
+ if (index.isStaticParserString2()) {
+ return self->getStatic2(index.toStaticParserString2());
+ }
+
+ MOZ_ASSERT(index.isNull());
+ return nullptr;
+}
+
+const ParserAtom* ParserAtomSpanBuilder::getParserAtom(
+ TaggedParserAtomIndex index) const {
+ return GetParserAtom(this, index);
+}
+
+const ParserAtom* ParserAtomsTable::getWellKnown(WellKnownAtomId atomId) const {
+ return wellKnownTable_.getWellKnown(atomId);
+}
+
+const ParserAtom* ParserAtomsTable::getStatic1(StaticParserString1 s) const {
+ return WellKnownParserAtoms::getStatic1(s);
+}
+
+const ParserAtom* ParserAtomsTable::getStatic2(StaticParserString2 s) const {
+ return WellKnownParserAtoms::getStatic2(s);
+}
+
+const ParserAtom* ParserAtomsTable::getParserAtom(ParserAtomIndex index) const {
+ return entries_[index]->asAtom();
+}
+
+const ParserAtom* ParserAtomsTable::getParserAtom(
+ TaggedParserAtomIndex index) const {
+ return GetParserAtom(this, index);
+}
+
+bool InstantiateMarkedAtoms(JSContext* cx, const ParserAtomSpan& entries,
+ CompilationAtomCache& atomCache) {
+ for (const auto& entry : entries) {
+ if (!entry) {
+ continue;
+ }
+ if (entry->isUsedByStencil() && entry->isParserAtomIndex() &&
+ !atomCache.hasAtomAt(entry->toParserAtomIndex())) {
+ if (!entry->instantiate(cx, atomCache)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+template <typename CharT>
+const ParserAtom* WellKnownParserAtoms::lookupChar16Seq(
+ const SpecificParserAtomLookup<CharT>& lookup) const {
+ EntrySet::Ptr get = wellKnownSet_.readonlyThreadsafeLookup(lookup);
+ if (get) {
+ return (*get)->asAtom();
+ }
+ return nullptr;
+}
+
+bool WellKnownParserAtoms::initSingle(JSContext* cx, const ParserName** name,
+ const ParserAtomEntry& romEntry) {
+ MOZ_ASSERT(name != nullptr);
+
+ unsigned int len = romEntry.length();
+ const Latin1Char* str = romEntry.latin1Chars();
+
+ // Well-known atoms are all currently ASCII with length <= MaxWellKnownLength.
+ MOZ_ASSERT(len <= MaxWellKnownLength);
+ MOZ_ASSERT(romEntry.isAscii());
+
+ // Strings matched by lookupTiny are stored in static table and aliases should
+ // only be added using initTinyStringAlias.
+ MOZ_ASSERT(lookupTiny(str, len) == nullptr,
+ "Well-known atom matches a tiny StaticString. Did you add it to "
+ "the wrong CommonPropertyNames.h list?");
+
+ InflatedChar16Sequence<Latin1Char> seq(str, len);
+ SpecificParserAtomLookup<Latin1Char> lookup(seq, romEntry.hash());
+
+ // Save name for returning after moving entry into set.
+ if (!wellKnownSet_.putNew(lookup, &romEntry)) {
+ js::ReportOutOfMemory(cx);
+ return false;
+ }
+
+ *name = romEntry.asName();
+ return true;
+}
+
+bool WellKnownParserAtoms::initTinyStringAlias(JSContext* cx,
+ const ParserName** name,
+ const char* str) {
+ MOZ_ASSERT(name != nullptr);
+
+ unsigned int len = strlen(str);
+
+ // Well-known atoms are all currently ASCII with length <= MaxWellKnownLength.
+ MOZ_ASSERT(len <= MaxWellKnownLength);
+ MOZ_ASSERT(FindSmallestEncoding(JS::UTF8Chars(str, len)) ==
+ JS::SmallestEncoding::ASCII);
+
+ // NOTE: If this assert fails, you may need to change which list is it belongs
+ // to in CommonPropertyNames.h.
+ const ParserAtom* tiny = lookupTiny(str, len);
+ MOZ_ASSERT(tiny, "Tiny common name was not found");
+
+ // Set alias to existing atom.
+ *name = tiny->asName();
+ return true;
+}
+
+bool WellKnownParserAtoms::init(JSContext* cx) {
+ // Tiny strings with a common name need a named alias to an entry in the
+ // WellKnownParserAtoms_ROM.
+#define COMMON_NAME_INIT_(_, name, text) \
+ if (!initTinyStringAlias(cx, &(name), text)) { \
+ return false; \
+ }
+ FOR_EACH_TINY_PROPERTYNAME(COMMON_NAME_INIT_)
+#undef COMMON_NAME_INIT_
+
+ // Initialize the named fields to point to entries in the ROM. This also adds
+ // the atom to the lookup HashSet. The HashSet is used for dynamic lookups
+ // later and does not change once this init method is complete.
+#define COMMON_NAME_INIT_(_, name, _2) \
+ if (!initSingle(cx, &(name), rom_.name)) { \
+ return false; \
+ }
+ FOR_EACH_NONTINY_COMMON_PROPERTYNAME(COMMON_NAME_INIT_)
+#undef COMMON_NAME_INIT_
+#define COMMON_NAME_INIT_(name, _) \
+ if (!initSingle(cx, &(name), rom_.name)) { \
+ return false; \
+ }
+ JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INIT_)
+#undef COMMON_NAME_INIT_
+
+ return true;
+}
+
+} /* namespace frontend */
+} /* namespace js */
+
+// XDR code.
+namespace js {
+
+template <XDRMode mode>
+XDRResult XDRParserAtomEntry(XDRState<mode>* xdr, ParserAtomEntry** entryp) {
+ static_assert(CanCopyDataToDisk<ParserAtomEntry>::value,
+ "ParserAtomEntry cannot be bulk-copied to disk.");
+
+ MOZ_TRY(xdr->align32());
+
+ const ParserAtomEntry* header;
+ if (mode == XDR_ENCODE) {
+ header = *entryp;
+ } else {
+ MOZ_TRY(xdr->peekData(&header));
+ }
+
+ const uint32_t CharSize =
+ header->hasLatin1Chars() ? sizeof(JS::Latin1Char) : sizeof(char16_t);
+ uint32_t totalLength =
+ sizeof(ParserAtomEntry) + (CharSize * header->length());
+
+ MOZ_TRY(xdr->borrowedData(entryp, totalLength));
+
+ return Ok();
+}
+
+template XDRResult XDRParserAtomEntry(XDRState<XDR_ENCODE>* xdr,
+ ParserAtomEntry** atomp);
+template XDRResult XDRParserAtomEntry(XDRState<XDR_DECODE>* xdr,
+ ParserAtomEntry** atomp);
+
+} /* namespace js */
+
+bool JSRuntime::initializeParserAtoms(JSContext* cx) {
+ MOZ_ASSERT(!commonParserNames);
+
+ if (parentRuntime) {
+ commonParserNames = parentRuntime->commonParserNames;
+ return true;
+ }
+
+ UniquePtr<js::frontend::WellKnownParserAtoms> names(
+ js_new<js::frontend::WellKnownParserAtoms>());
+ if (!names || !names->init(cx)) {
+ return false;
+ }
+
+ commonParserNames = names.release();
+ return true;
+}
+
+void JSRuntime::finishParserAtoms() {
+ if (!parentRuntime) {
+ js_delete(commonParserNames.ref());
+ }
+}