summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/StencilXdr.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/frontend/StencilXdr.cpp1536
1 files changed, 1536 insertions, 0 deletions
diff --git a/js/src/frontend/StencilXdr.cpp b/js/src/frontend/StencilXdr.cpp
new file mode 100644
index 0000000000..2c6d7ec49c
--- /dev/null
+++ b/js/src/frontend/StencilXdr.cpp
@@ -0,0 +1,1536 @@
+/* -*- 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/StencilXdr.h" // StencilXDR
+
+#include "mozilla/ArrayUtils.h" // mozilla::ArrayEqual
+#include "mozilla/OperatorNewExtensions.h" // mozilla::KnownNotNull
+#include "mozilla/ScopeExit.h" // mozilla::MakeScopeExit
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint8_t, uint16_t, uint32_t
+#include <type_traits> // std::has_unique_object_representations
+#include <utility> // std::forward
+
+#include "ds/LifoAlloc.h" // LifoAlloc
+#include "frontend/CompilationStencil.h" // CompilationStencil, ExtensibleCompilationStencil
+#include "frontend/ScriptIndex.h" // ScriptIndex
+#include "vm/Scope.h" // SizeOfParserScopeData
+#include "vm/StencilEnums.h" // js::ImmutableScriptFlagsEnum
+
+using namespace js;
+using namespace js::frontend;
+
+using mozilla::Utf8Unit;
+
+template <typename NameType>
+struct CanEncodeNameType {
+ static constexpr bool value = false;
+};
+
+template <>
+struct CanEncodeNameType<TaggedParserAtomIndex> {
+ static constexpr bool value = true;
+};
+
+template <XDRMode mode, typename T, size_t N, class AP>
+static XDRResult XDRVectorUninitialized(XDRState<mode>* xdr,
+ Vector<T, N, AP>& vec,
+ uint32_t& length) {
+ if (mode == XDR_ENCODE) {
+ MOZ_ASSERT(vec.length() <= UINT32_MAX);
+ length = vec.length();
+ }
+
+ MOZ_TRY(xdr->codeUint32(&length));
+
+ if (mode == XDR_DECODE) {
+ MOZ_ASSERT(vec.empty());
+ if (!vec.resizeUninitialized(length)) {
+ js::ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode, typename T, size_t N, class AP>
+static XDRResult XDRVectorInitialized(XDRState<mode>* xdr,
+ Vector<T, N, AP>& vec, uint32_t length) {
+ MOZ_ASSERT_IF(mode == XDR_ENCODE, length == vec.length());
+
+ if (mode == XDR_DECODE) {
+ MOZ_ASSERT(vec.empty());
+ if (!vec.resize(length)) {
+ js::ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode, typename T, size_t N, class AP>
+static XDRResult XDRVectorInitialized(XDRState<mode>* xdr,
+ Vector<T, N, AP>& vec) {
+ uint32_t length;
+ if (mode == XDR_ENCODE) {
+ MOZ_ASSERT(vec.length() <= UINT32_MAX);
+ length = vec.length();
+ }
+
+ MOZ_TRY(xdr->codeUint32(&length));
+
+ return XDRVectorInitialized(xdr, vec, length);
+}
+
+template <XDRMode mode, typename T, size_t N, class AP>
+static XDRResult XDRVectorContent(XDRState<mode>* xdr, Vector<T, N, AP>& vec) {
+ static_assert(CanCopyDataToDisk<T>::value,
+ "Vector content cannot be bulk-copied to disk.");
+
+ uint32_t length;
+ MOZ_TRY(XDRVectorUninitialized(xdr, vec, length));
+ MOZ_TRY(xdr->codeBytes(vec.begin(), sizeof(T) * length));
+
+ return Ok();
+}
+
+template <XDRMode mode, typename T>
+static XDRResult XDRSpanInitialized(XDRState<mode>* xdr, LifoAlloc& alloc,
+ mozilla::Span<T>& span, uint32_t size) {
+ MOZ_ASSERT_IF(mode == XDR_ENCODE, size == span.size());
+
+ if (mode == XDR_DECODE) {
+ MOZ_ASSERT(span.empty());
+ if (size > 0) {
+ auto* p = alloc.template newArrayUninitialized<T>(size);
+ if (!p) {
+ js::ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ span = mozilla::Span(p, size);
+
+ for (size_t i = 0; i < size; i++) {
+ new (mozilla::KnownNotNull, &span[i]) T();
+ }
+ }
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode, typename T>
+static XDRResult XDRSpanContent(XDRState<mode>* xdr, LifoAlloc& alloc,
+ mozilla::Span<T>& span, uint32_t size) {
+ static_assert(CanCopyDataToDisk<T>::value,
+ "Span cannot be bulk-copied to disk.");
+ MOZ_ASSERT_IF(mode == XDR_ENCODE, size == span.size());
+
+ if (size) {
+ MOZ_TRY(xdr->align32());
+
+ T* data;
+ if constexpr (mode == XDR_ENCODE) {
+ data = span.data();
+ MOZ_TRY(xdr->codeBytes(data, sizeof(T) * size));
+ } else {
+ const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
+ if (options.borrowBuffer) {
+ MOZ_TRY(xdr->borrowedData(&data, sizeof(T) * size));
+ } else {
+ data = alloc.template newArrayUninitialized<T>(size);
+ if (!data) {
+ js::ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ MOZ_TRY(xdr->codeBytes(data, sizeof(T) * size));
+ }
+ }
+ if (mode == XDR_DECODE) {
+ span = mozilla::Span(data, size);
+ }
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode, typename T>
+static XDRResult XDRSpanContent(XDRState<mode>* xdr, LifoAlloc& alloc,
+ mozilla::Span<T>& span) {
+ uint32_t size;
+ if (mode == XDR_ENCODE) {
+ MOZ_ASSERT(span.size() <= UINT32_MAX);
+ size = span.size();
+ }
+
+ MOZ_TRY(xdr->codeUint32(&size));
+
+ return XDRSpanContent(xdr, alloc, span, size);
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeBigInt(XDRState<mode>* xdr,
+ LifoAlloc& alloc,
+ BigIntStencil& stencil) {
+ uint32_t size;
+ if (mode == XDR_ENCODE) {
+ size = stencil.source_.size();
+ }
+ MOZ_TRY(xdr->codeUint32(&size));
+
+ return XDRSpanContent(xdr, alloc, stencil.source_, size);
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeObjLiteral(XDRState<mode>* xdr,
+ LifoAlloc& alloc,
+ ObjLiteralStencil& stencil) {
+ uint8_t kindAndFlags = 0;
+
+ if (mode == XDR_ENCODE) {
+ static_assert(sizeof(ObjLiteralKindAndFlags) == sizeof(uint8_t));
+ kindAndFlags = stencil.kindAndFlags_.toRaw();
+ }
+ MOZ_TRY(xdr->codeUint8(&kindAndFlags));
+ if (mode == XDR_DECODE) {
+ stencil.kindAndFlags_.setRaw(kindAndFlags);
+ }
+
+ MOZ_TRY(xdr->codeUint32(&stencil.propertyCount_));
+
+ MOZ_TRY(XDRSpanContent(xdr, alloc, stencil.code_));
+
+ return Ok();
+}
+
+template <typename ScopeT>
+/* static */ void AssertScopeSpecificDataIsEncodable() {
+ using ScopeDataT = typename ScopeT::ParserData;
+
+ static_assert(CanEncodeNameType<typename ScopeDataT::NameType>::value);
+ static_assert(CanCopyDataToDisk<ScopeDataT>::value,
+ "ScopeData cannot be bulk-copied to disk");
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeScopeData(
+ XDRState<mode>* xdr, LifoAlloc& alloc, ScopeStencil& stencil,
+ BaseParserScopeData*& baseScopeData) {
+ // WasmInstanceScope & WasmFunctionScope should not appear in stencils.
+ MOZ_ASSERT(stencil.kind_ != ScopeKind::WasmInstance);
+ MOZ_ASSERT(stencil.kind_ != ScopeKind::WasmFunction);
+ if (stencil.kind_ == ScopeKind::With) {
+ return Ok();
+ }
+
+ MOZ_TRY(xdr->align32());
+
+ static_assert(offsetof(BaseParserScopeData, length) == 0,
+ "length should be the first field");
+ uint32_t length;
+ if (mode == XDR_ENCODE) {
+ length = baseScopeData->length;
+ } else {
+ MOZ_TRY(xdr->peekUint32(&length));
+ }
+
+ AssertScopeSpecificDataIsEncodable<FunctionScope>();
+ AssertScopeSpecificDataIsEncodable<VarScope>();
+ AssertScopeSpecificDataIsEncodable<LexicalScope>();
+ AssertScopeSpecificDataIsEncodable<ClassBodyScope>();
+ AssertScopeSpecificDataIsEncodable<EvalScope>();
+ AssertScopeSpecificDataIsEncodable<GlobalScope>();
+ AssertScopeSpecificDataIsEncodable<ModuleScope>();
+
+ // In both decoding and encoding, stencil.kind_ is now known, and
+ // can be assumed. This allows the encoding to write out the bytes
+ // for the specialized scope-data type without needing to encode
+ // a distinguishing prefix.
+ uint32_t totalLength = SizeOfParserScopeData(stencil.kind_, length);
+ if constexpr (mode == XDR_ENCODE) {
+ MOZ_TRY(xdr->codeBytes(baseScopeData, totalLength));
+ } else {
+ const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
+ if (options.borrowBuffer) {
+ MOZ_TRY(xdr->borrowedData(&baseScopeData, totalLength));
+ } else {
+ baseScopeData =
+ reinterpret_cast<BaseParserScopeData*>(alloc.alloc(totalLength));
+ if (!baseScopeData) {
+ js::ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ MOZ_TRY(xdr->codeBytes(baseScopeData, totalLength));
+ }
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode>
+/* static */
+XDRResult StencilXDR::codeSharedData(XDRState<mode>* xdr,
+ RefPtr<SharedImmutableScriptData>& sisd) {
+ static_assert(frontend::CanCopyDataToDisk<ImmutableScriptData>::value,
+ "ImmutableScriptData cannot be bulk-copied to disk");
+ static_assert(frontend::CanCopyDataToDisk<jsbytecode>::value,
+ "jsbytecode cannot be bulk-copied to disk");
+ static_assert(frontend::CanCopyDataToDisk<SrcNote>::value,
+ "SrcNote cannot be bulk-copied to disk");
+ static_assert(frontend::CanCopyDataToDisk<ScopeNote>::value,
+ "ScopeNote cannot be bulk-copied to disk");
+ static_assert(frontend::CanCopyDataToDisk<TryNote>::value,
+ "TryNote cannot be bulk-copied to disk");
+
+ uint32_t size;
+ uint32_t hash;
+ if (mode == XDR_ENCODE) {
+ if (sisd) {
+ size = sisd->immutableDataLength();
+ hash = sisd->hash();
+ } else {
+ size = 0;
+ hash = 0;
+ }
+ }
+ MOZ_TRY(xdr->codeUint32(&size));
+
+ // A size of zero is used when the `sisd` is nullptr. This can occur for
+ // certain outer container modes. In this case, there is no further
+ // transcoding to do.
+ if (!size) {
+ MOZ_ASSERT(!sisd);
+ return Ok();
+ }
+
+ MOZ_TRY(xdr->align32());
+ static_assert(alignof(ImmutableScriptData) <= alignof(uint32_t));
+
+ MOZ_TRY(xdr->codeUint32(&hash));
+
+ if constexpr (mode == XDR_ENCODE) {
+ uint8_t* data = const_cast<uint8_t*>(sisd->get()->immutableData().data());
+ MOZ_ASSERT(data == reinterpret_cast<const uint8_t*>(sisd->get()),
+ "Decode below relies on the data placement");
+ MOZ_TRY(xdr->codeBytes(data, size));
+ } else {
+ sisd = SharedImmutableScriptData::create(xdr->fc());
+ if (!sisd) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+
+ const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
+ if (options.usePinnedBytecode) {
+ MOZ_ASSERT(options.borrowBuffer);
+ ImmutableScriptData* isd;
+ MOZ_TRY(xdr->borrowedData(&isd, size));
+ sisd->setExternal(isd, hash);
+ } else {
+ auto isd = ImmutableScriptData::new_(xdr->fc(), size);
+ if (!isd) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ uint8_t* data = reinterpret_cast<uint8_t*>(isd.get());
+ MOZ_TRY(xdr->codeBytes(data, size));
+ sisd->setOwn(std::move(isd), hash);
+ }
+
+ if (!sisd->get()->validateLayout(size)) {
+ MOZ_ASSERT(false, "Bad ImmutableScriptData");
+ return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
+ }
+ }
+
+ if (mode == XDR_DECODE) {
+ if (!SharedImmutableScriptData::shareScriptData(xdr->fc(), sisd)) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+
+ return Ok();
+}
+
+// Called from js::XDRScript.
+template /* static */ XDRResult StencilXDR::codeSharedData(
+ XDRState<XDR_ENCODE>* xdr, RefPtr<SharedImmutableScriptData>& sisd);
+template /* static */ XDRResult StencilXDR::codeSharedData(
+ XDRState<XDR_DECODE>* xdr, RefPtr<SharedImmutableScriptData>& sisd);
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeSharedDataContainer(
+ XDRState<mode>* xdr, SharedDataContainer& sharedData) {
+ if (mode == XDR_ENCODE) {
+ if (sharedData.isBorrow()) {
+ return codeSharedDataContainer(xdr, *sharedData.asBorrow());
+ }
+ }
+
+ enum Kind {
+ Single,
+ Vector,
+ Map,
+ };
+
+ Kind kind;
+ if (mode == XDR_ENCODE) {
+ if (sharedData.isSingle()) {
+ kind = Kind::Single;
+ } else if (sharedData.isVector()) {
+ kind = Kind::Vector;
+ } else {
+ MOZ_ASSERT(sharedData.isMap());
+ kind = Kind::Map;
+ }
+ }
+ MOZ_TRY(xdr->codeEnum32(&kind));
+
+ switch (kind) {
+ case Kind::Single: {
+ RefPtr<SharedImmutableScriptData> ref;
+ if (mode == XDR_ENCODE) {
+ ref = sharedData.asSingle();
+ }
+ MOZ_TRY(codeSharedData<mode>(xdr, ref));
+ if (mode == XDR_DECODE) {
+ sharedData.setSingle(ref.forget());
+ }
+ break;
+ }
+
+ case Kind::Vector: {
+ if (mode == XDR_DECODE) {
+ if (!sharedData.initVector(xdr->fc())) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+ auto& vec = *sharedData.asVector();
+ MOZ_TRY(XDRVectorInitialized(xdr, vec));
+ for (auto& entry : vec) {
+ // NOTE: There can be nullptr, even if we don't perform syntax parsing,
+ // because of constant folding.
+ MOZ_TRY(codeSharedData<mode>(xdr, entry));
+ }
+ break;
+ }
+
+ case Kind::Map: {
+ if (mode == XDR_DECODE) {
+ if (!sharedData.initMap(xdr->fc())) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+ auto& map = *sharedData.asMap();
+ uint32_t count;
+ if (mode == XDR_ENCODE) {
+ count = map.count();
+ }
+ MOZ_TRY(xdr->codeUint32(&count));
+ if (mode == XDR_DECODE) {
+ if (!map.reserve(count)) {
+ js::ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+
+ if (mode == XDR_ENCODE) {
+ for (auto iter = map.iter(); !iter.done(); iter.next()) {
+ uint32_t index = iter.get().key().index;
+ auto& data = iter.get().value();
+ MOZ_TRY(xdr->codeUint32(&index));
+ MOZ_TRY(codeSharedData<mode>(xdr, data));
+ }
+ } else {
+ for (uint32_t i = 0; i < count; i++) {
+ ScriptIndex index;
+ MOZ_TRY(xdr->codeUint32(&index.index));
+
+ RefPtr<SharedImmutableScriptData> data;
+ MOZ_TRY(codeSharedData<mode>(xdr, data));
+
+ if (!map.putNew(index, data)) {
+ js::ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+ }
+
+ break;
+ }
+
+ default:
+ return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeParserAtom(XDRState<mode>* xdr,
+ LifoAlloc& alloc,
+ ParserAtom** atomp) {
+ static_assert(CanCopyDataToDisk<ParserAtom>::value,
+ "ParserAtom cannot be bulk-copied to disk.");
+
+ MOZ_TRY(xdr->align32());
+
+ const ParserAtom* header;
+ if (mode == XDR_ENCODE) {
+ header = *atomp;
+ } else {
+ MOZ_TRY(xdr->peekData(&header));
+ }
+
+ const uint32_t CharSize =
+ header->hasLatin1Chars() ? sizeof(JS::Latin1Char) : sizeof(char16_t);
+ uint32_t totalLength = sizeof(ParserAtom) + (CharSize * header->length());
+
+ if constexpr (mode == XDR_ENCODE) {
+ MOZ_TRY(xdr->codeBytes(*atomp, totalLength));
+ } else {
+ const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
+ if (options.borrowBuffer) {
+ MOZ_TRY(xdr->borrowedData(atomp, totalLength));
+ } else {
+ *atomp = reinterpret_cast<ParserAtom*>(alloc.alloc(totalLength));
+ if (!*atomp) {
+ js::ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ MOZ_TRY(xdr->codeBytes(*atomp, totalLength));
+ }
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode>
+static XDRResult XDRAtomCount(XDRState<mode>* xdr, uint32_t* atomCount) {
+ return xdr->codeUint32(atomCount);
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeParserAtomSpan(
+ XDRState<mode>* xdr, LifoAlloc& alloc, ParserAtomSpan& parserAtomData) {
+ if (mode == XDR_ENCODE) {
+ uint32_t atomVectorLength = parserAtomData.size();
+ MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength));
+
+ uint32_t atomCount = 0;
+ for (const auto& entry : parserAtomData) {
+ if (!entry) {
+ continue;
+ }
+ if (entry->isUsedByStencil()) {
+ atomCount++;
+ }
+ }
+ MOZ_TRY(XDRAtomCount(xdr, &atomCount));
+
+ for (uint32_t i = 0; i < atomVectorLength; i++) {
+ auto& entry = parserAtomData[i];
+ if (!entry) {
+ continue;
+ }
+ if (entry->isUsedByStencil()) {
+ MOZ_TRY(xdr->codeUint32(&i));
+ MOZ_TRY(codeParserAtom(xdr, alloc, &entry));
+ }
+ }
+
+ return Ok();
+ }
+
+ uint32_t atomVectorLength;
+ MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength));
+
+ frontend::ParserAtomSpanBuilder builder(parserAtomData);
+ if (!builder.allocate(xdr->fc(), alloc, atomVectorLength)) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+
+ uint32_t atomCount;
+ MOZ_TRY(XDRAtomCount(xdr, &atomCount));
+
+ for (uint32_t i = 0; i < atomCount; i++) {
+ frontend::ParserAtom* entry = nullptr;
+ uint32_t index;
+ MOZ_TRY(xdr->codeUint32(&index));
+ MOZ_TRY(codeParserAtom(xdr, alloc, &entry));
+ if (mode == XDR_DECODE) {
+ if (index >= atomVectorLength) {
+ return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
+ }
+ }
+ builder.set(frontend::ParserAtomIndex(index), entry);
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeModuleRequest(
+ XDRState<mode>* xdr, StencilModuleRequest& stencil) {
+ MOZ_TRY(xdr->codeUint32(stencil.specifier.rawDataRef()));
+ MOZ_TRY(XDRVectorContent(xdr, stencil.assertions));
+
+ return Ok();
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeModuleRequestVector(
+ XDRState<mode>* xdr, StencilModuleMetadata::RequestVector& vector) {
+ MOZ_TRY(XDRVectorInitialized(xdr, vector));
+
+ for (auto& entry : vector) {
+ MOZ_TRY(codeModuleRequest<mode>(xdr, entry));
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeModuleEntry(
+ XDRState<mode>* xdr, StencilModuleEntry& stencil) {
+ MOZ_TRY(xdr->codeUint32(&stencil.moduleRequest));
+ MOZ_TRY(xdr->codeUint32(stencil.localName.rawDataRef()));
+ MOZ_TRY(xdr->codeUint32(stencil.importName.rawDataRef()));
+ MOZ_TRY(xdr->codeUint32(stencil.exportName.rawDataRef()));
+ MOZ_TRY(xdr->codeUint32(&stencil.lineno));
+ MOZ_TRY(xdr->codeUint32(&stencil.column));
+
+ return Ok();
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeModuleEntryVector(
+ XDRState<mode>* xdr, StencilModuleMetadata::EntryVector& vector) {
+ MOZ_TRY(XDRVectorInitialized(xdr, vector));
+
+ for (auto& entry : vector) {
+ MOZ_TRY(codeModuleEntry<mode>(xdr, entry));
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeModuleMetadata(
+ XDRState<mode>* xdr, StencilModuleMetadata& stencil) {
+ MOZ_TRY(codeModuleRequestVector(xdr, stencil.moduleRequests));
+ MOZ_TRY(codeModuleEntryVector(xdr, stencil.requestedModules));
+ MOZ_TRY(codeModuleEntryVector(xdr, stencil.importEntries));
+ MOZ_TRY(codeModuleEntryVector(xdr, stencil.localExportEntries));
+ MOZ_TRY(codeModuleEntryVector(xdr, stencil.indirectExportEntries));
+ MOZ_TRY(codeModuleEntryVector(xdr, stencil.starExportEntries));
+ MOZ_TRY(XDRVectorContent(xdr, stencil.functionDecls));
+
+ uint8_t isAsync = 0;
+ if (mode == XDR_ENCODE) {
+ if (stencil.isAsync) {
+ isAsync = stencil.isAsync ? 1 : 0;
+ }
+ }
+
+ MOZ_TRY(xdr->codeUint8(&isAsync));
+
+ if (mode == XDR_DECODE) {
+ stencil.isAsync = isAsync == 1;
+ }
+
+ return Ok();
+}
+
+template <XDRMode mode>
+XDRResult XDRCompilationStencilSpanSize(
+ XDRState<mode>* xdr, uint32_t* scriptSize, uint32_t* gcThingSize,
+ uint32_t* scopeSize, uint32_t* scriptExtraSize, uint32_t* regExpSize,
+ uint32_t* bigIntSize, uint32_t* objLiteralSize) {
+ // Compress the series of span sizes, to avoid consuming extra space for
+ // unused/small span sizes.
+ // There will be align32 shortly after this section, so try to make the
+ // padding smaller.
+
+ enum XDRSpanSizeKind {
+ // All of the size values fit in 1 byte each. The entire section takes 7
+ // bytes, and expect no padding.
+ All8Kind,
+
+ // Other cases. All of the size values fit in 4 bytes each. Expect 3 bytes
+ // padding for `sizeKind`.
+ All32Kind,
+ };
+
+ uint8_t sizeKind = All32Kind;
+ if (mode == XDR_ENCODE) {
+ uint32_t mask = (*scriptSize) | (*gcThingSize) | (*scopeSize) |
+ (*scriptExtraSize) | (*regExpSize) | (*bigIntSize) |
+ (*objLiteralSize);
+
+ if (mask <= 0xff) {
+ sizeKind = All8Kind;
+ }
+ }
+ MOZ_TRY(xdr->codeUint8(&sizeKind));
+
+ if (sizeKind == All32Kind) {
+ MOZ_TRY(xdr->codeUint32(scriptSize));
+ MOZ_TRY(xdr->codeUint32(gcThingSize));
+ MOZ_TRY(xdr->codeUint32(scopeSize));
+ MOZ_TRY(xdr->codeUint32(scriptExtraSize));
+ MOZ_TRY(xdr->codeUint32(regExpSize));
+ MOZ_TRY(xdr->codeUint32(bigIntSize));
+ MOZ_TRY(xdr->codeUint32(objLiteralSize));
+ } else {
+ uint8_t scriptSize8 = 0;
+ uint8_t gcThingSize8 = 0;
+ uint8_t scopeSize8 = 0;
+ uint8_t scriptExtraSize8 = 0;
+ uint8_t regExpSize8 = 0;
+ uint8_t bigIntSize8 = 0;
+ uint8_t objLiteralSize8 = 0;
+
+ if (mode == XDR_ENCODE) {
+ scriptSize8 = uint8_t(*scriptSize);
+ gcThingSize8 = uint8_t(*gcThingSize);
+ scopeSize8 = uint8_t(*scopeSize);
+ scriptExtraSize8 = uint8_t(*scriptExtraSize);
+ regExpSize8 = uint8_t(*regExpSize);
+ bigIntSize8 = uint8_t(*bigIntSize);
+ objLiteralSize8 = uint8_t(*objLiteralSize);
+ }
+
+ MOZ_TRY(xdr->codeUint8(&scriptSize8));
+ MOZ_TRY(xdr->codeUint8(&gcThingSize8));
+ MOZ_TRY(xdr->codeUint8(&scopeSize8));
+ MOZ_TRY(xdr->codeUint8(&scriptExtraSize8));
+ MOZ_TRY(xdr->codeUint8(&regExpSize8));
+ MOZ_TRY(xdr->codeUint8(&bigIntSize8));
+ MOZ_TRY(xdr->codeUint8(&objLiteralSize8));
+
+ if (mode == XDR_DECODE) {
+ *scriptSize = scriptSize8;
+ *gcThingSize = gcThingSize8;
+ *scopeSize = scopeSize8;
+ *scriptExtraSize = scriptExtraSize8;
+ *regExpSize = regExpSize8;
+ *bigIntSize = bigIntSize8;
+ *objLiteralSize = objLiteralSize8;
+ }
+ }
+
+ return Ok();
+}
+
+// Marker between each section inside CompilationStencil.
+//
+// These values should meet the following requirement:
+// * No same value (differ more than single bit flip)
+// * Bit pattern that won't frequently appear inside other XDR data
+//
+// Currently they're randomly chosen prime numbers that doesn't have same
+// byte pattern.
+enum class SectionMarker : uint32_t {
+ ParserAtomData = 0xD9C098D3,
+ ScopeData = 0x892C25EF,
+ ScopeNames = 0x638C4FB3,
+ RegExpData = 0xB030C2AF,
+ BigIntData = 0x4B24F449,
+ ObjLiteralData = 0x9AFAAE45,
+ SharedData = 0xAAD52687,
+ GCThingData = 0x1BD8F533,
+ ScriptData = 0x840458FF,
+ ScriptExtra = 0xA90E489D,
+ ModuleMetadata = 0x94FDCE6D,
+ End = 0x16DDA135,
+};
+
+template <XDRMode mode>
+static XDRResult CodeMarker(XDRState<mode>* xdr, SectionMarker marker) {
+ return xdr->codeMarker(uint32_t(marker));
+}
+
+template <XDRMode mode>
+/* static */ XDRResult StencilXDR::codeCompilationStencil(
+ XDRState<mode>* xdr, CompilationStencil& stencil) {
+ MOZ_ASSERT(!stencil.asmJS);
+
+ if constexpr (mode == XDR_DECODE) {
+ const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
+ if (options.borrowBuffer) {
+ stencil.storageType = CompilationStencil::StorageType::Borrowed;
+ } else {
+ stencil.storageType = CompilationStencil::StorageType::Owned;
+ }
+ }
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::ParserAtomData));
+ MOZ_TRY(codeParserAtomSpan(xdr, stencil.alloc, stencil.parserAtomData));
+
+ uint8_t canLazilyParse = 0;
+
+ if (mode == XDR_ENCODE) {
+ canLazilyParse = stencil.canLazilyParse;
+ }
+ MOZ_TRY(xdr->codeUint8(&canLazilyParse));
+ if (mode == XDR_DECODE) {
+ stencil.canLazilyParse = canLazilyParse;
+ // NOTE: stencil.canLazilyParse can be different than
+ // CanLazilyParse(static_cast<XDRStencilDecoder*>(xdr)->options()).
+ // See bug 1726498 for removing the redundancy.
+ }
+
+ MOZ_TRY(xdr->codeUint32(&stencil.functionKey));
+
+ uint32_t scriptSize, gcThingSize, scopeSize, scriptExtraSize;
+ uint32_t regExpSize, bigIntSize, objLiteralSize;
+ if (mode == XDR_ENCODE) {
+ scriptSize = stencil.scriptData.size();
+ gcThingSize = stencil.gcThingData.size();
+ scopeSize = stencil.scopeData.size();
+ MOZ_ASSERT(scopeSize == stencil.scopeNames.size());
+
+ scriptExtraSize = stencil.scriptExtra.size();
+
+ regExpSize = stencil.regExpData.size();
+ bigIntSize = stencil.bigIntData.size();
+ objLiteralSize = stencil.objLiteralData.size();
+ }
+ MOZ_TRY(XDRCompilationStencilSpanSize(
+ xdr, &scriptSize, &gcThingSize, &scopeSize, &scriptExtraSize, &regExpSize,
+ &bigIntSize, &objLiteralSize));
+
+ // All of the vector-indexed data elements referenced by the
+ // main script tree must be materialized first.
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::ScopeData));
+ MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.scopeData, scopeSize));
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::ScopeNames));
+ MOZ_TRY(
+ XDRSpanInitialized(xdr, stencil.alloc, stencil.scopeNames, scopeSize));
+ MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size());
+ for (uint32_t i = 0; i < scopeSize; i++) {
+ MOZ_TRY(codeScopeData(xdr, stencil.alloc, stencil.scopeData[i],
+ stencil.scopeNames[i]));
+ }
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::RegExpData));
+ MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.regExpData, regExpSize));
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::BigIntData));
+ MOZ_TRY(
+ XDRSpanInitialized(xdr, stencil.alloc, stencil.bigIntData, bigIntSize));
+ for (auto& entry : stencil.bigIntData) {
+ MOZ_TRY(codeBigInt(xdr, stencil.alloc, entry));
+ }
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::ObjLiteralData));
+ MOZ_TRY(XDRSpanInitialized(xdr, stencil.alloc, stencil.objLiteralData,
+ objLiteralSize));
+ for (auto& entry : stencil.objLiteralData) {
+ MOZ_TRY(codeObjLiteral(xdr, stencil.alloc, entry));
+ }
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::SharedData));
+ MOZ_TRY(codeSharedDataContainer(xdr, stencil.sharedData));
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::GCThingData));
+ MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.gcThingData, gcThingSize));
+
+ // Now serialize the vector of ScriptStencils.
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::ScriptData));
+ MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.scriptData, scriptSize));
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::ScriptExtra));
+ MOZ_TRY(
+ XDRSpanContent(xdr, stencil.alloc, stencil.scriptExtra, scriptExtraSize));
+
+ // We don't support coding non-initial CompilationStencil.
+ MOZ_ASSERT(stencil.isInitialStencil());
+
+ if (stencil.scriptExtra[CompilationStencil::TopLevelIndex].isModule()) {
+ if (mode == XDR_DECODE) {
+ stencil.moduleMetadata =
+ xdr->fc()->getAllocator()->template new_<StencilModuleMetadata>();
+ if (!stencil.moduleMetadata) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::ModuleMetadata));
+ MOZ_TRY(codeModuleMetadata(xdr, *stencil.moduleMetadata));
+
+ // codeModuleMetadata doesn't guarantee alignment.
+ MOZ_TRY(xdr->align32());
+ }
+
+ MOZ_TRY(CodeMarker(xdr, SectionMarker::End));
+
+ // The result should be aligned.
+ //
+ // NOTE:
+ // If the top-level isn't a module, ScriptData/ScriptExtra sections
+ // guarantee the alignment because there should be at least 1 item,
+ // and XDRSpanContent adds alignment before span content, and the struct size
+ // should also be aligned.
+ static_assert(sizeof(ScriptStencil) % 4 == 0,
+ "size of ScriptStencil should be aligned");
+ static_assert(sizeof(ScriptStencilExtra) % 4 == 0,
+ "size of ScriptStencilExtra should be aligned");
+ MOZ_RELEASE_ASSERT(xdr->isAligned32());
+
+ return Ok();
+}
+
+template <typename Unit>
+struct UnretrievableSourceDecoder {
+ XDRState<XDR_DECODE>* const xdr_;
+ ScriptSource* const scriptSource_;
+ const uint32_t uncompressedLength_;
+
+ public:
+ UnretrievableSourceDecoder(XDRState<XDR_DECODE>* xdr,
+ ScriptSource* scriptSource,
+ uint32_t uncompressedLength)
+ : xdr_(xdr),
+ scriptSource_(scriptSource),
+ uncompressedLength_(uncompressedLength) {}
+
+ XDRResult decode() {
+ auto sourceUnits = xdr_->fc()->getAllocator()->make_pod_array<Unit>(
+ std::max<size_t>(uncompressedLength_, 1));
+ if (!sourceUnits) {
+ return xdr_->fail(JS::TranscodeResult::Throw);
+ }
+
+ MOZ_TRY(xdr_->codeChars(sourceUnits.get(), uncompressedLength_));
+
+ if (!scriptSource_->initializeUnretrievableUncompressedSource(
+ xdr_->fc(), std::move(sourceUnits), uncompressedLength_)) {
+ return xdr_->fail(JS::TranscodeResult::Throw);
+ }
+
+ return Ok();
+ }
+};
+
+template <>
+XDRResult StencilXDR::codeSourceUnretrievableUncompressed<XDR_DECODE>(
+ XDRState<XDR_DECODE>* xdr, ScriptSource* ss, uint8_t sourceCharSize,
+ uint32_t uncompressedLength) {
+ MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);
+
+ if (sourceCharSize == 1) {
+ UnretrievableSourceDecoder<Utf8Unit> decoder(xdr, ss, uncompressedLength);
+ return decoder.decode();
+ }
+
+ UnretrievableSourceDecoder<char16_t> decoder(xdr, ss, uncompressedLength);
+ return decoder.decode();
+}
+
+template <typename Unit>
+struct UnretrievableSourceEncoder {
+ XDRState<XDR_ENCODE>* const xdr_;
+ ScriptSource* const source_;
+ const uint32_t uncompressedLength_;
+
+ UnretrievableSourceEncoder(XDRState<XDR_ENCODE>* xdr, ScriptSource* source,
+ uint32_t uncompressedLength)
+ : xdr_(xdr), source_(source), uncompressedLength_(uncompressedLength) {}
+
+ XDRResult encode() {
+ Unit* sourceUnits =
+ const_cast<Unit*>(source_->uncompressedData<Unit>()->units());
+
+ return xdr_->codeChars(sourceUnits, uncompressedLength_);
+ }
+};
+
+template <>
+/* static */
+XDRResult StencilXDR::codeSourceUnretrievableUncompressed<XDR_ENCODE>(
+ XDRState<XDR_ENCODE>* xdr, ScriptSource* ss, uint8_t sourceCharSize,
+ uint32_t uncompressedLength) {
+ MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);
+
+ if (sourceCharSize == 1) {
+ UnretrievableSourceEncoder<Utf8Unit> encoder(xdr, ss, uncompressedLength);
+ return encoder.encode();
+ }
+
+ UnretrievableSourceEncoder<char16_t> encoder(xdr, ss, uncompressedLength);
+ return encoder.encode();
+}
+
+template <typename Unit, XDRMode mode>
+/* static */
+XDRResult StencilXDR::codeSourceUncompressedData(XDRState<mode>* const xdr,
+ ScriptSource* const ss) {
+ static_assert(
+ std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
+ "should handle UTF-8 and UTF-16");
+
+ if (mode == XDR_ENCODE) {
+ MOZ_ASSERT(ss->isUncompressed<Unit>());
+ } else {
+ MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
+ }
+
+ uint32_t uncompressedLength;
+ if (mode == XDR_ENCODE) {
+ uncompressedLength = ss->uncompressedData<Unit>()->length();
+ }
+ MOZ_TRY(xdr->codeUint32(&uncompressedLength));
+
+ return codeSourceUnretrievableUncompressed(xdr, ss, sizeof(Unit),
+ uncompressedLength);
+}
+
+template <typename Unit, XDRMode mode>
+/* static */
+XDRResult StencilXDR::codeSourceCompressedData(XDRState<mode>* const xdr,
+ ScriptSource* const ss) {
+ static_assert(
+ std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
+ "should handle UTF-8 and UTF-16");
+
+ if (mode == XDR_ENCODE) {
+ MOZ_ASSERT(ss->isCompressed<Unit>());
+ } else {
+ MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
+ }
+
+ uint32_t uncompressedLength;
+ if (mode == XDR_ENCODE) {
+ uncompressedLength =
+ ss->data.as<ScriptSource::Compressed<Unit, SourceRetrievable::No>>()
+ .uncompressedLength;
+ }
+ MOZ_TRY(xdr->codeUint32(&uncompressedLength));
+
+ uint32_t compressedLength;
+ if (mode == XDR_ENCODE) {
+ compressedLength =
+ ss->data.as<ScriptSource::Compressed<Unit, SourceRetrievable::No>>()
+ .raw.length();
+ }
+ MOZ_TRY(xdr->codeUint32(&compressedLength));
+
+ if (mode == XDR_DECODE) {
+ // Compressed data is always single-byte chars.
+ auto bytes = xdr->fc()->getAllocator()->template make_pod_array<char>(
+ compressedLength);
+ if (!bytes) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
+
+ if (!ss->initializeWithUnretrievableCompressedSource<Unit>(
+ xdr->fc(), std::move(bytes), compressedLength,
+ uncompressedLength)) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ } else {
+ void* bytes = const_cast<char*>(ss->compressedData<Unit>()->raw.chars());
+ MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
+ }
+
+ return Ok();
+}
+
+template <typename Unit,
+ template <typename U, SourceRetrievable CanRetrieve> class Data,
+ XDRMode mode>
+/* static */
+void StencilXDR::codeSourceRetrievable(ScriptSource* const ss) {
+ static_assert(
+ std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
+ "should handle UTF-8 and UTF-16");
+
+ if (mode == XDR_ENCODE) {
+ MOZ_ASSERT((ss->data.is<Data<Unit, SourceRetrievable::Yes>>()));
+ } else {
+ MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
+ ss->data = ScriptSource::SourceType(ScriptSource::Retrievable<Unit>());
+ }
+}
+
+template <typename Unit, XDRMode mode>
+/* static */
+void StencilXDR::codeSourceRetrievableData(ScriptSource* ss) {
+ // There's nothing to code for retrievable data. Just be sure to set
+ // retrievable data when decoding.
+ if (mode == XDR_ENCODE) {
+ MOZ_ASSERT(ss->data.is<ScriptSource::Retrievable<Unit>>());
+ } else {
+ MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
+ ss->data = ScriptSource::SourceType(ScriptSource::Retrievable<Unit>());
+ }
+}
+
+template <XDRMode mode>
+/* static */
+XDRResult StencilXDR::codeSourceData(XDRState<mode>* const xdr,
+ ScriptSource* const ss) {
+ // The order here corresponds to the type order in |ScriptSource::SourceType|
+ // so number->internal Variant tag is a no-op.
+ enum class DataType {
+ CompressedUtf8Retrievable,
+ UncompressedUtf8Retrievable,
+ CompressedUtf8NotRetrievable,
+ UncompressedUtf8NotRetrievable,
+ CompressedUtf16Retrievable,
+ UncompressedUtf16Retrievable,
+ CompressedUtf16NotRetrievable,
+ UncompressedUtf16NotRetrievable,
+ RetrievableUtf8,
+ RetrievableUtf16,
+ Missing,
+ };
+
+ DataType tag;
+ {
+ // This is terrible, but we can't do better. When |mode == XDR_DECODE| we
+ // don't have a |ScriptSource::data| |Variant| to match -- the entire XDR
+ // idiom for tagged unions depends on coding a tag-number, then the
+ // corresponding tagged data. So we must manually define a tag-enum, code
+ // it, then switch on it (and ignore the |Variant::match| API).
+ class XDRDataTag {
+ public:
+ DataType operator()(
+ const ScriptSource::Compressed<Utf8Unit, SourceRetrievable::Yes>&) {
+ return DataType::CompressedUtf8Retrievable;
+ }
+ DataType operator()(
+ const ScriptSource::Uncompressed<Utf8Unit, SourceRetrievable::Yes>&) {
+ return DataType::UncompressedUtf8Retrievable;
+ }
+ DataType operator()(
+ const ScriptSource::Compressed<Utf8Unit, SourceRetrievable::No>&) {
+ return DataType::CompressedUtf8NotRetrievable;
+ }
+ DataType operator()(
+ const ScriptSource::Uncompressed<Utf8Unit, SourceRetrievable::No>&) {
+ return DataType::UncompressedUtf8NotRetrievable;
+ }
+ DataType operator()(
+ const ScriptSource::Compressed<char16_t, SourceRetrievable::Yes>&) {
+ return DataType::CompressedUtf16Retrievable;
+ }
+ DataType operator()(
+ const ScriptSource::Uncompressed<char16_t, SourceRetrievable::Yes>&) {
+ return DataType::UncompressedUtf16Retrievable;
+ }
+ DataType operator()(
+ const ScriptSource::Compressed<char16_t, SourceRetrievable::No>&) {
+ return DataType::CompressedUtf16NotRetrievable;
+ }
+ DataType operator()(
+ const ScriptSource::Uncompressed<char16_t, SourceRetrievable::No>&) {
+ return DataType::UncompressedUtf16NotRetrievable;
+ }
+ DataType operator()(const ScriptSource::Retrievable<Utf8Unit>&) {
+ return DataType::RetrievableUtf8;
+ }
+ DataType operator()(const ScriptSource::Retrievable<char16_t>&) {
+ return DataType::RetrievableUtf16;
+ }
+ DataType operator()(const ScriptSource::Missing&) {
+ return DataType::Missing;
+ }
+ };
+
+ uint8_t type;
+ if (mode == XDR_ENCODE) {
+ type = static_cast<uint8_t>(ss->data.match(XDRDataTag()));
+ }
+ MOZ_TRY(xdr->codeUint8(&type));
+
+ if (type > static_cast<uint8_t>(DataType::Missing)) {
+ // Fail in debug, but only soft-fail in release, if the type is invalid.
+ MOZ_ASSERT_UNREACHABLE("bad tag");
+ return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
+ }
+
+ tag = static_cast<DataType>(type);
+ }
+
+ switch (tag) {
+ case DataType::CompressedUtf8Retrievable:
+ codeSourceRetrievable<Utf8Unit, ScriptSource::Compressed, mode>(ss);
+ return Ok();
+
+ case DataType::CompressedUtf8NotRetrievable:
+ return codeSourceCompressedData<Utf8Unit>(xdr, ss);
+
+ case DataType::UncompressedUtf8Retrievable:
+ codeSourceRetrievable<Utf8Unit, ScriptSource::Uncompressed, mode>(ss);
+ return Ok();
+
+ case DataType::UncompressedUtf8NotRetrievable:
+ return codeSourceUncompressedData<Utf8Unit>(xdr, ss);
+
+ case DataType::CompressedUtf16Retrievable:
+ codeSourceRetrievable<char16_t, ScriptSource::Compressed, mode>(ss);
+ return Ok();
+
+ case DataType::CompressedUtf16NotRetrievable:
+ return codeSourceCompressedData<char16_t>(xdr, ss);
+
+ case DataType::UncompressedUtf16Retrievable:
+ codeSourceRetrievable<char16_t, ScriptSource::Uncompressed, mode>(ss);
+ return Ok();
+
+ case DataType::UncompressedUtf16NotRetrievable:
+ return codeSourceUncompressedData<char16_t>(xdr, ss);
+
+ case DataType::Missing: {
+ MOZ_ASSERT(ss->data.is<ScriptSource::Missing>(),
+ "ScriptSource::data is initialized as missing, so neither "
+ "encoding nor decoding has to change anything");
+
+ // There's no data to XDR for missing source.
+ break;
+ }
+
+ case DataType::RetrievableUtf8:
+ codeSourceRetrievableData<Utf8Unit, mode>(ss);
+ return Ok();
+
+ case DataType::RetrievableUtf16:
+ codeSourceRetrievableData<char16_t, mode>(ss);
+ return Ok();
+ }
+
+ // The range-check on |type| far above ought ensure the above |switch| is
+ // exhaustive and all cases will return, but not all compilers understand
+ // this. Make the Missing case break to here so control obviously never flows
+ // off the end.
+ MOZ_ASSERT(tag == DataType::Missing);
+ return Ok();
+}
+
+template <XDRMode mode>
+/* static */
+XDRResult StencilXDR::codeSource(XDRState<mode>* xdr,
+ const JS::DecodeOptions* maybeOptions,
+ RefPtr<ScriptSource>& source) {
+ FrontendContext* fc = xdr->fc();
+
+ if (mode == XDR_DECODE) {
+ // Allocate a new ScriptSource and root it with the holder.
+ source = do_AddRef(fc->getAllocator()->new_<ScriptSource>());
+ if (!source) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+
+ static constexpr uint8_t HasFilename = 1 << 0;
+ static constexpr uint8_t HasDisplayURL = 1 << 1;
+ static constexpr uint8_t HasSourceMapURL = 1 << 2;
+ static constexpr uint8_t MutedErrors = 1 << 3;
+
+ uint8_t flags = 0;
+ if (mode == XDR_ENCODE) {
+ if (source->filename_) {
+ flags |= HasFilename;
+ }
+ if (source->hasDisplayURL()) {
+ flags |= HasDisplayURL;
+ }
+ if (source->hasSourceMapURL()) {
+ flags |= HasSourceMapURL;
+ }
+ if (source->mutedErrors()) {
+ flags |= MutedErrors;
+ }
+ }
+
+ MOZ_TRY(xdr->codeUint8(&flags));
+
+ if (flags & HasFilename) {
+ XDRTranscodeString<char> chars;
+
+ if (mode == XDR_ENCODE) {
+ chars.construct<const char*>(source->filename());
+ }
+ MOZ_TRY(xdr->codeCharsZ(chars));
+ if (mode == XDR_DECODE) {
+ if (!source->setFilename(fc, std::move(chars.ref<UniqueChars>()))) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+ }
+
+ if (flags & HasDisplayURL) {
+ XDRTranscodeString<char16_t> chars;
+
+ if (mode == XDR_ENCODE) {
+ chars.construct<const char16_t*>(source->displayURL());
+ }
+ MOZ_TRY(xdr->codeCharsZ(chars));
+ if (mode == XDR_DECODE) {
+ if (!source->setDisplayURL(fc,
+ std::move(chars.ref<UniqueTwoByteChars>()))) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+ }
+
+ if (flags & HasSourceMapURL) {
+ XDRTranscodeString<char16_t> chars;
+
+ if (mode == XDR_ENCODE) {
+ chars.construct<const char16_t*>(source->sourceMapURL());
+ }
+ MOZ_TRY(xdr->codeCharsZ(chars));
+ if (mode == XDR_DECODE) {
+ if (!source->setSourceMapURL(
+ fc, std::move(chars.ref<UniqueTwoByteChars>()))) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+ }
+
+ MOZ_ASSERT(source->parameterListEnd_ == 0);
+
+ if (flags & MutedErrors) {
+ if (mode == XDR_DECODE) {
+ source->mutedErrors_ = true;
+ }
+ }
+
+ MOZ_TRY(xdr->codeUint32(&source->startLine_));
+ MOZ_TRY(xdr->codeUint32(&source->startColumn_));
+
+ // The introduction info doesn't persist across encode/decode.
+ if (mode == XDR_DECODE) {
+ source->introductionType_ = maybeOptions->introductionType;
+ source->setIntroductionOffset(maybeOptions->introductionOffset);
+ if (maybeOptions->introducerFilename) {
+ if (!source->setIntroducerFilename(fc,
+ maybeOptions->introducerFilename)) {
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ }
+ }
+
+ MOZ_TRY(codeSourceData(xdr, source.get()));
+
+ return Ok();
+}
+
+template /* static */
+ XDRResult
+ StencilXDR::codeSource(XDRState<XDR_ENCODE>* xdr,
+ const JS::DecodeOptions* maybeOptions,
+ RefPtr<ScriptSource>& holder);
+template /* static */
+ XDRResult
+ StencilXDR::codeSource(XDRState<XDR_DECODE>* xdr,
+ const JS::DecodeOptions* maybeOptions,
+ RefPtr<ScriptSource>& holder);
+
+JS_PUBLIC_API bool JS::GetScriptTranscodingBuildId(
+ JS::BuildIdCharVector* buildId) {
+ MOZ_ASSERT(buildId->empty());
+ MOZ_ASSERT(GetBuildId);
+
+ if (!GetBuildId(buildId)) {
+ return false;
+ }
+
+ // Note: the buildId returned here is also used for the bytecode cache MIME
+ // type so use plain ASCII characters.
+
+ if (!buildId->reserve(buildId->length() + 4)) {
+ return false;
+ }
+
+ buildId->infallibleAppend('-');
+
+ // XDR depends on pointer size and endianness.
+ static_assert(sizeof(uintptr_t) == 4 || sizeof(uintptr_t) == 8);
+ buildId->infallibleAppend(sizeof(uintptr_t) == 4 ? '4' : '8');
+ buildId->infallibleAppend(MOZ_LITTLE_ENDIAN() ? 'l' : 'b');
+
+ return true;
+}
+
+template <XDRMode mode>
+static XDRResult VersionCheck(XDRState<mode>* xdr) {
+ JS::BuildIdCharVector buildId;
+ if (!JS::GetScriptTranscodingBuildId(&buildId)) {
+ ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+ MOZ_ASSERT(!buildId.empty());
+
+ uint32_t buildIdLength;
+ if (mode == XDR_ENCODE) {
+ buildIdLength = buildId.length();
+ }
+
+ MOZ_TRY(xdr->codeUint32(&buildIdLength));
+
+ if (mode == XDR_DECODE && buildIdLength != buildId.length()) {
+ return xdr->fail(JS::TranscodeResult::Failure_BadBuildId);
+ }
+
+ if (mode == XDR_ENCODE) {
+ MOZ_TRY(xdr->codeBytes(buildId.begin(), buildIdLength));
+ } else {
+ JS::BuildIdCharVector decodedBuildId;
+
+ // buildIdLength is already checked against the length of current
+ // buildId.
+ if (!decodedBuildId.resize(buildIdLength)) {
+ ReportOutOfMemory(xdr->fc());
+ return xdr->fail(JS::TranscodeResult::Throw);
+ }
+
+ MOZ_TRY(xdr->codeBytes(decodedBuildId.begin(), buildIdLength));
+
+ // We do not provide binary compatibility with older scripts.
+ if (!mozilla::ArrayEqual(decodedBuildId.begin(), buildId.begin(),
+ buildIdLength)) {
+ return xdr->fail(JS::TranscodeResult::Failure_BadBuildId);
+ }
+ }
+
+ return Ok();
+}
+
+XDRResult XDRStencilEncoder::codeStencil(
+ const RefPtr<ScriptSource>& source,
+ const frontend::CompilationStencil& stencil) {
+#ifdef DEBUG
+ auto sanityCheck = mozilla::MakeScopeExit(
+ [&] { MOZ_ASSERT(validateResultCode(fc(), resultCode())); });
+#endif
+
+ MOZ_TRY(frontend::StencilXDR::checkCompilationStencil(this, stencil));
+
+ MOZ_TRY(VersionCheck(this));
+
+ uint32_t dummy = 0;
+ size_t lengthOffset = buf->cursor();
+ MOZ_TRY(codeUint32(&dummy));
+ size_t hashOffset = buf->cursor();
+ MOZ_TRY(codeUint32(&dummy));
+
+ size_t contentOffset = buf->cursor();
+ MOZ_TRY(frontend::StencilXDR::codeSource(
+ this, nullptr, const_cast<RefPtr<ScriptSource>&>(source)));
+ MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(
+ this, const_cast<frontend::CompilationStencil&>(stencil)));
+ size_t endOffset = buf->cursor();
+
+ if (endOffset > UINT32_MAX) {
+ ReportOutOfMemory(fc());
+ return fail(JS::TranscodeResult::Throw);
+ }
+
+ uint32_t length = endOffset - contentOffset;
+ codeUint32At(&length, lengthOffset);
+
+ const uint8_t* contentBegin = buf->bufferAt(contentOffset);
+ uint32_t hash = mozilla::HashBytes(contentBegin, length);
+ codeUint32At(&hash, hashOffset);
+
+ return Ok();
+}
+
+XDRResult XDRStencilEncoder::codeStencil(
+ const frontend::CompilationStencil& stencil) {
+ return codeStencil(stencil.source, stencil);
+}
+
+void StencilIncrementalEncoderPtr::reset() {
+ if (merger_) {
+ js_delete(merger_);
+ }
+ merger_ = nullptr;
+}
+
+bool StencilIncrementalEncoderPtr::setInitial(
+ JSContext* cx,
+ UniquePtr<frontend::ExtensibleCompilationStencil>&& initial) {
+ AutoReportFrontendContext fc(cx);
+ merger_ = fc.getAllocator()->new_<frontend::CompilationStencilMerger>();
+ if (!merger_) {
+ return false;
+ }
+
+ return merger_->setInitial(
+ &fc,
+ std::forward<UniquePtr<frontend::ExtensibleCompilationStencil>>(initial));
+}
+
+bool StencilIncrementalEncoderPtr::addDelazification(
+ JSContext* cx, const frontend::CompilationStencil& delazification) {
+ AutoReportFrontendContext fc(cx);
+ return merger_->addDelazification(&fc, delazification);
+}
+
+XDRResult XDRStencilDecoder::codeStencil(
+ const JS::DecodeOptions& options, frontend::CompilationStencil& stencil) {
+#ifdef DEBUG
+ auto sanityCheck = mozilla::MakeScopeExit(
+ [&] { MOZ_ASSERT(validateResultCode(fc(), resultCode())); });
+#endif
+
+ auto resetOptions = mozilla::MakeScopeExit([&] { options_ = nullptr; });
+ options_ = &options;
+
+ MOZ_TRY(VersionCheck(this));
+
+ uint32_t length;
+ MOZ_TRY(codeUint32(&length));
+
+ uint32_t hash;
+ MOZ_TRY(codeUint32(&hash));
+
+ const uint8_t* contentBegin;
+ MOZ_TRY(peekArray(length, &contentBegin));
+ uint32_t actualHash = mozilla::HashBytes(contentBegin, length);
+
+ if (MOZ_UNLIKELY(actualHash != hash)) {
+ return fail(JS::TranscodeResult::Failure_BadDecode);
+ }
+
+ MOZ_TRY(frontend::StencilXDR::codeSource(this, &options, stencil.source));
+ MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(this, stencil));
+
+ return Ok();
+}
+
+template /* static */ XDRResult StencilXDR::codeCompilationStencil(
+ XDRState<XDR_ENCODE>* xdr, CompilationStencil& stencil);
+
+template /* static */ XDRResult StencilXDR::codeCompilationStencil(
+ XDRState<XDR_DECODE>* xdr, CompilationStencil& stencil);
+
+/* static */ XDRResult StencilXDR::checkCompilationStencil(
+ XDRStencilEncoder* encoder, const CompilationStencil& stencil) {
+ if (stencil.asmJS) {
+ return encoder->fail(JS::TranscodeResult::Failure_AsmJSNotSupported);
+ }
+
+ return Ok();
+}
+
+/* static */ XDRResult StencilXDR::checkCompilationStencil(
+ const ExtensibleCompilationStencil& stencil) {
+ if (stencil.asmJS) {
+ return mozilla::Err(JS::TranscodeResult::Failure_AsmJSNotSupported);
+ }
+
+ return Ok();
+}