diff --git a/js/src/frontend/ParserAtom.cpp b/js/src/frontend/ParserAtom.cpp index d1cabb4a3526..0e362d616728 100644 --- a/js/src/frontend/ParserAtom.cpp +++ b/js/src/frontend/ParserAtom.cpp @@ -111,7 +111,7 @@ namespace frontend { static JS::OOM PARSER_ATOMS_OOM; -static JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId kind) { +static JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId atomId) { #define ASSERT_OFFSET_(idpart, id, text) \ static_assert(offsetof(JSAtomState, id) == \ int32_t(WellKnownAtomId::id) * \ @@ -129,7 +129,7 @@ static JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId kind) { static_assert(int32_t(WellKnownAtomId::abort) == 0, "Unexpected order of WellKnownAtom"); - return (&cx->names().abort)[int32_t(kind)]; + return (&cx->names().abort)[int32_t(atomId)]; } mozilla::GenericErrorResult RaiseParserAtomsOOMError(JSContext* cx) { @@ -555,6 +555,33 @@ JS::Result ParserAtomsTable::concatAtoms( catLen); } +const ParserAtom* ParserAtomsTable::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 (&wellKnownTable_.abort)[int32_t(atomId)]; +} + +const ParserAtom* ParserAtomsTable::getStatic1(StaticParserString1 s) const { + return WellKnownParserAtoms::rom_.length1Table[size_t(s)].asAtom(); +} + +const ParserAtom* ParserAtomsTable::getStatic2(StaticParserString2 s) const { + return WellKnownParserAtoms::rom_.length2Table[size_t(s)].asAtom(); +} + template const ParserAtom* WellKnownParserAtoms::lookupChar16Seq( const SpecificParserAtomLookup& lookup) const { @@ -566,7 +593,7 @@ const ParserAtom* WellKnownParserAtoms::lookupChar16Seq( } bool WellKnownParserAtoms::initSingle(JSContext* cx, const ParserName** name, - const char* str, WellKnownAtomId kind) { + const char* str, WellKnownAtomId atomId) { MOZ_ASSERT(name != nullptr); unsigned int len = strlen(str); @@ -592,7 +619,7 @@ bool WellKnownParserAtoms::initSingle(JSContext* cx, const ParserName** name, return false; } UniquePtr entry = maybeEntry.unwrap(); - entry->setWellKnownAtomId(kind); + entry->setWellKnownAtomId(atomId); // Save name for returning after moving entry into set. const ParserName* nm = entry.get()->asName(); @@ -665,9 +692,59 @@ bool WellKnownParserAtoms::init(JSContext* cx) { // XDR code. namespace js { +enum class ParserAtomTag : uint32_t { + Normal = 0, + WellKnown, + Static1, + Static2, +}; + template -static XDRResult XDRParserAtomIndex(XDRState* xdr, uint32_t* index) { - return xdr->codeUint32(index); +static XDRResult XDRParserAtomTaggedIndex(XDRState* xdr, + ParserAtomTag* tag, uint32_t* index) { + // We encode 2 bit (tag) + 32 bit (index) data in the following format: + // + // index = 0bAABB'CCDD'EEFF'GGHH'IIJJ'KKLL'MMNN'OOPP + // tag = 0bTT + // + // if index < 0b0011'1111'1111'1111'1111'1111'1111'1111: + // single uint32_t = 0bBBCC'DDEE'FFGG'HHII'JJKK'LLMM'NNOO'PPTT + // else: + // two uint32_t = 0b1111'1111'1111'1111'1111'1111'1111'11TT, + // 0bAABB'CCDD'EEFF'GGHH'IIJJ'KKLL'MMNN'OOPP + + constexpr uint32_t TagShift = 2; + constexpr uint32_t TagMask = 0b0011; + constexpr uint32_t TwoUnitPattern = UINT32_MAX ^ TagMask; + constexpr uint32_t CodeLimit = TwoUnitPattern >> TagShift; + + MOZ_ASSERT((uint32_t(*tag) & TagMask) == uint32_t(*tag)); + + if (mode == XDR_ENCODE) { + if (*index < CodeLimit) { + uint32_t data = (*index) << TagShift | uint32_t(*tag); + return xdr->codeUint32(&data); + } + + uint32_t data = TwoUnitPattern | uint32_t(*tag); + MOZ_TRY(xdr->codeUint32(&data)); + return xdr->codeUint32(index); + } + + MOZ_ASSERT(mode == XDR_DECODE); + + uint32_t data; + MOZ_TRY(xdr->codeUint32(&data)); + + *tag = ParserAtomTag(data & TagMask); + + if ((data & TwoUnitPattern) == TwoUnitPattern) { + MOZ_TRY(xdr->codeUint32(index)); + } else { + *index = data >> TagShift; + } + + return Ok(); } template @@ -733,49 +810,86 @@ XDRResult XDRParserAtomData(XDRState* xdr, const ParserAtom** atomp) { return Ok(); } +template XDRResult XDRParserAtomData(XDRState* xdr, + const ParserAtom** atomp); +template XDRResult XDRParserAtomData(XDRState* xdr, + const ParserAtom** atomp); + template XDRResult XDRParserAtom(XDRState* xdr, const ParserAtom** atomp) { - // If dedup tables aren't enabled for this XDR encoding, encode/decode - // the parser atoms inline. - if (!xdr->hasAtomMap() && !xdr->hasAtomTable()) { - return XDRParserAtomData(xdr, atomp); - } - if (mode == XDR_ENCODE) { MOZ_ASSERT(xdr->hasAtomMap()); - // Atom contents are encoded in a separate buffer, which is joined to the - // final result in XDRIncrementalEncoder::linearize. References to atoms - // are encoded as indices into the atom stream. uint32_t atomIndex; - XDRParserAtomMap::AddPtr p = xdr->parserAtomMap().lookupForAdd(*atomp); - if (p) { - atomIndex = p->value(); + ParserAtomTag tag = ParserAtomTag::Normal; + if ((*atomp)->isWellKnownAtomId()) { + atomIndex = uint32_t((*atomp)->toWellKnownAtomId()); + tag = ParserAtomTag::WellKnown; + } else if ((*atomp)->isStaticParserString1()) { + atomIndex = uint32_t((*atomp)->toStaticParserString1()); + tag = ParserAtomTag::Static1; + } else if ((*atomp)->isStaticParserString2()) { + atomIndex = uint32_t((*atomp)->toStaticParserString2()); + tag = ParserAtomTag::Static2; } else { - xdr->switchToAtomBuf(); - MOZ_TRY(XDRParserAtomData(xdr, atomp)); - xdr->switchToMainBuf(); + // Either AtomIndexKind::Unresolved or AtomIndexKind::AtomIndex. - atomIndex = xdr->natoms(); - xdr->natoms() += 1; - if (!xdr->parserAtomMap().add(p, *atomp, atomIndex)) { - return xdr->fail(JS::TranscodeResult_Throw); + // Atom contents are encoded in a separate buffer, which is joined to the + // final result in XDRIncrementalEncoder::linearize. References to atoms + // are encoded as indices into the atom stream. + XDRParserAtomMap::AddPtr p = xdr->parserAtomMap().lookupForAdd(*atomp); + if (p) { + atomIndex = p->value(); + } else { + xdr->switchToAtomBuf(); + MOZ_TRY(XDRParserAtomData(xdr, atomp)); + xdr->switchToMainBuf(); + + atomIndex = xdr->natoms(); + xdr->natoms() += 1; + if (!xdr->parserAtomMap().add(p, *atomp, atomIndex)) { + return xdr->fail(JS::TranscodeResult_Throw); + } } + tag = ParserAtomTag::Normal; } - MOZ_TRY(XDRParserAtomIndex(xdr, &atomIndex)); + MOZ_TRY(XDRParserAtomTaggedIndex(xdr, &tag, &atomIndex)); return Ok(); } MOZ_ASSERT(mode == XDR_DECODE && xdr->hasAtomTable()); - uint32_t atomIndex; - MOZ_TRY(XDRParserAtomIndex(xdr, &atomIndex)); - if (atomIndex >= xdr->parserAtomTable().length()) { - return xdr->fail(JS::TranscodeResult_Failure_BadDecode); - } - const ParserAtom* atom = xdr->parserAtomTable()[atomIndex]; + uint32_t atomIndex = 0; + ParserAtomTag tag = ParserAtomTag::Normal; + MOZ_TRY(XDRParserAtomTaggedIndex(xdr, &tag, &atomIndex)); + + switch (tag) { + case ParserAtomTag::Normal: + if (atomIndex >= xdr->parserAtomTable().length()) { + return xdr->fail(JS::TranscodeResult_Failure_BadDecode); + } + *atomp = xdr->parserAtomTable()[atomIndex]; + break; + case ParserAtomTag::WellKnown: + if (atomIndex >= uint32_t(WellKnownAtomId::Limit)) { + return xdr->fail(JS::TranscodeResult_Failure_BadDecode); + } + *atomp = xdr->frontendAtoms().getWellKnown(WellKnownAtomId(atomIndex)); + break; + case ParserAtomTag::Static1: + if (atomIndex >= WellKnownParserAtoms_ROM::ASCII_STATIC_LIMIT) { + return xdr->fail(JS::TranscodeResult_Failure_BadDecode); + } + *atomp = xdr->frontendAtoms().getStatic1(StaticParserString1(atomIndex)); + break; + case ParserAtomTag::Static2: + if (atomIndex >= WellKnownParserAtoms_ROM::NUM_LENGTH2_ENTRIES) { + return xdr->fail(JS::TranscodeResult_Failure_BadDecode); + } + *atomp = xdr->frontendAtoms().getStatic2(StaticParserString2(atomIndex)); + break; + } - *atomp = atom; return Ok(); } diff --git a/js/src/frontend/ParserAtom.h b/js/src/frontend/ParserAtom.h index 8f04880fde6f..1f33e6c14592 100644 --- a/js/src/frontend/ParserAtom.h +++ b/js/src/frontend/ParserAtom.h @@ -51,6 +51,7 @@ enum class WellKnownAtomId : uint32_t { #define ENUM_ENTRY_(name, clasp) name, JS_FOR_EACH_PROTOTYPE(ENUM_ENTRY_) #undef ENUM_ENTRY_ + Limit, }; // These types correspond into indices in the StaticStrings arrays. @@ -183,13 +184,36 @@ class alignas(alignof(uint32_t)) ParserAtomEntry { template bool equalsSeq(HashNumber hash, InflatedChar16Sequence seq) const; + WellKnownAtomId toWellKnownAtomId() const { + MOZ_ASSERT(isWellKnownAtomId()); + return WellKnownAtomId(atomIndex_); + } + StaticParserString1 toStaticParserString1() const { + MOZ_ASSERT(isStaticParserString1()); + return StaticParserString1(atomIndex_); + } + StaticParserString2 toStaticParserString2() const { + MOZ_ASSERT(isStaticParserString2()); + return StaticParserString2(atomIndex_); + } + + bool isWellKnownAtomId() const { + return atomIndexKind_ == AtomIndexKind::WellKnown; + } + bool isStaticParserString1() const { + return atomIndexKind_ == AtomIndexKind::Static1; + } + bool isStaticParserString2() const { + return atomIndexKind_ == AtomIndexKind::Static2; + } + private: void setAtomIndex(AtomIndex index) { atomIndex_ = index; atomIndexKind_ = AtomIndexKind::AtomIndex; } - constexpr void setWellKnownAtomId(WellKnownAtomId kind) { - atomIndex_ = static_cast(kind); + constexpr void setWellKnownAtomId(WellKnownAtomId atomId) { + atomIndex_ = static_cast(atomId); atomIndexKind_ = AtomIndexKind::WellKnown; } constexpr void setStaticParserString1(StaticParserString1 s) { @@ -414,7 +438,7 @@ class WellKnownParserAtoms { bool initTinyStringAlias(JSContext* cx, const ParserName** name, const char* str); bool initSingle(JSContext* cx, const ParserName** name, const char* str, - WellKnownAtomId kind); + WellKnownAtomId atomId); public: WellKnownParserAtoms() = default; @@ -489,6 +513,10 @@ class ParserAtomsTable { JS::Result concatAtoms( JSContext* cx, mozilla::Range atoms); + + const ParserAtom* getWellKnown(WellKnownAtomId atomId) const; + const ParserAtom* getStatic1(StaticParserString1 s) const; + const ParserAtom* getStatic2(StaticParserString2 s) const; }; template diff --git a/js/src/vm/Xdr.cpp b/js/src/vm/Xdr.cpp index b06bdd28b7fb..7bb52522af2b 100644 --- a/js/src/vm/Xdr.cpp +++ b/js/src/vm/Xdr.cpp @@ -20,7 +20,7 @@ #include "builtin/ModuleObject.h" #include "debugger/DebugAPI.h" #include "frontend/CompilationInfo.h" // frontend::CompilationStencil, frontend::CompilationInfo, frontend::CompilationInfoVector -#include "frontend/ParserAtom.h" // XDRParserAtom +#include "frontend/ParserAtom.h" // XDRParserAtomData #include "js/BuildId.h" // JS::BuildIdCharVector #include "vm/JSContext.h" #include "vm/JSScript.h" @@ -318,7 +318,7 @@ static XDRResult ParserAtomTable(XDRState* xdr) { for (uint32_t i = 0; i < atomCount; i++) { const frontend::ParserAtom* atom = nullptr; - MOZ_TRY(XDRParserAtom(xdr, &atom)); + MOZ_TRY(XDRParserAtomData(xdr, &atom)); if (!xdr->parserAtomTable().append(atom)) { ReportOutOfMemory(xdr->cx()); return xdr->fail(JS::TranscodeResult_Throw); diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index b28e831a4a41..4c91642ca159 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -781,6 +781,10 @@ template XDRResult XDRParserAtom(XDRState* xdr, const frontend::ParserAtom** atomp); +template +XDRResult XDRParserAtomData(XDRState* xdr, + const frontend::ParserAtom** atomp); + template XDRResult XDRParserAtomOrNull(XDRState* xdr, const frontend::ParserAtom** atomp);