Bug 1544882 - XDR ScriptSource data almost entirely by switching on the type of data stored in |ScriptSource::data|. r=arai
authorJeff Walden <jwalden@mit.edu>
Wed, 17 Apr 2019 00:57:46 -0700
changeset 530447 808585ce159839b9b1babe1f90be0120c79298ca
parent 530446 641a364dcb54ae09689e73d7b8e1d30f12fa7705
child 530448 b3053863705de3052ab27429f8a3df023581354e
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1544882
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1544882 - XDR ScriptSource data almost entirely by switching on the type of data stored in |ScriptSource::data|. r=arai Differential Revision: https://phabricator.services.mozilla.com/D28924
js/src/vm/JSScript.cpp
js/src/vm/JSScript.h
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -2132,26 +2132,32 @@ MOZ_MUST_USE bool ScriptSource::setBinAS
     ReportOutOfMemory(cx);
     return false;
   }
   MOZ_ASSERT(data.is<Missing>());
   data = SourceType(BinAST(std::move(*deduped)));
   return true;
 }
 
-MOZ_MUST_USE bool ScriptSource::setBinASTSource(JSContext* cx,
-                                                UniqueChars&& buf, size_t len) {
+MOZ_MUST_USE bool ScriptSource::initializeBinAST(
+    JSContext* cx, UniqueChars&& buf, size_t len,
+    UniquePtr<frontend::BinASTSourceMetadata> metadata) {
+  MOZ_ASSERT(data.is<Missing>(),
+             "should only be initializing a fresh ScriptSource");
+  MOZ_ASSERT(binASTMetadata_ == nullptr, "shouldn't have BinAST metadata yet");
+
   auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
   auto deduped = cache.getOrCreate(std::move(buf), len);
   if (!deduped) {
     ReportOutOfMemory(cx);
     return false;
   }
-  MOZ_ASSERT(data.is<Missing>());
+
   data = SourceType(BinAST(std::move(*deduped)));
+  binASTMetadata_ = std::move(metadata);
   return true;
 }
 
 const uint8_t* ScriptSource::binASTSource() {
   MOZ_ASSERT(hasBinASTSource());
   return reinterpret_cast<const uint8_t*>(data.as<BinAST>().string.chars());
 }
 
@@ -2281,19 +2287,21 @@ template bool ScriptSource::assignSource
                                          SourceText<char16_t>& srcBuf);
 template bool ScriptSource::assignSource(JSContext* cx,
                                          const ReadOnlyCompileOptions& options,
                                          SourceText<Utf8Unit>& srcBuf);
 
 void ScriptSource::trace(JSTracer* trc) {
 #ifdef JS_BUILD_BINAST
   if (binASTMetadata_) {
+    MOZ_ASSERT(data.is<BinAST>());
     binASTMetadata_->trace(trc);
   }
 #else
+  MOZ_ASSERT(!data.is<BinAST>());
   MOZ_ASSERT(!binASTMetadata_);
 #endif  // JS_BUILD_BINAST
 }
 
 static MOZ_MUST_USE bool reallocUniquePtr(UniqueChars& unique, size_t size) {
   auto newPtr = static_cast<char*>(js_realloc(unique.get(), size));
   if (!newPtr) {
     return false;
@@ -2587,144 +2595,231 @@ XDRResult ScriptSource::xdrUncompressedS
   SourceEncoder<char16_t> encoder(xdr, this, uncompressedLength);
   return encoder.encode();
 }
 
 }  // namespace js
 
 template <XDRMode mode>
 /* static */
-XDRResult ScriptSource::xdrDataMember(XDRState<mode>* xdr, ScriptSource* ss) {
-  uint8_t hasSource = ss->hasSourceText();
-  MOZ_TRY(xdr->codeUint8(&hasSource));
-
-  uint8_t hasBinSource = ss->hasBinASTSource();
-  MOZ_TRY(xdr->codeUint8(&hasBinSource));
-
-  uint8_t retrievable = ss->sourceRetrievable_;
+XDRResult ScriptSource::xdrData(XDRState<mode>* const xdr,
+                                ScriptSource* const ss) {
+  // Retrievability is kept outside |ScriptSource::data| (and not solely as
+  // distinct variant types within it) because retrievable compressed or
+  // uncompressed data need not be XDR'd.
+  uint8_t retrievable;
+  if (mode == XDR_ENCODE) {
+    retrievable = ss->sourceRetrievable_;
+  }
   MOZ_TRY(xdr->codeUint8(&retrievable));
-  ss->sourceRetrievable_ = retrievable;
-
-  if ((hasSource || hasBinSource) && !retrievable) {
+  if (mode == XDR_DECODE) {
+    ss->sourceRetrievable_ = retrievable != 0;
+  }
+
+  // The order here corresponds to the type order in |ScriptSource::SourceType|
+  // for simplicity, but it isn't truly necessary that it do so.
+  enum class DataType {
+    CompressedUtf8,
+    UncompressedUtf8,
+    CompressedUtf16,
+    UncompressedUtf16,
+    Missing,
+    BinAST,
+  };
+
+  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 Compressed<Utf8Unit>&) {
+        return DataType::CompressedUtf8;
+      }
+      DataType operator()(const Uncompressed<Utf8Unit>&) {
+        return DataType::UncompressedUtf8;
+      }
+      DataType operator()(const Compressed<char16_t>&) {
+        return DataType::CompressedUtf16;
+      }
+      DataType operator()(const Uncompressed<char16_t>&) {
+        return DataType::UncompressedUtf16;
+      }
+      DataType operator()(const Missing&) { return DataType::Missing; }
+      DataType operator()(const BinAST&) { return DataType::BinAST; }
+    };
+
+    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::BinAST)) {
+      // 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);
+  }
+
+  auto CodeCompressedData = [xdr, ss, &retrievable](auto unit) -> XDRResult {
+    using Unit = decltype(unit);
+
+    static_assert(std::is_same<Unit, Utf8Unit>::value ||
+                      std::is_same<Unit, char16_t>::value,
+                  "should handle UTF-8 and UTF-16");
+
+    if (mode == XDR_ENCODE) {
+      MOZ_ASSERT(ss->data.is<Compressed<Unit>>());
+    }
+
+    if (retrievable) {
+      // It's unnecessary to code compressed data if it can just be retrieved
+      // using the source hook.
+      return Ok();
+    }
+
     uint32_t uncompressedLength = 0;
     if (mode == XDR_ENCODE) {
-      uncompressedLength = ss->length();
+      uncompressedLength = ss->data.as<Compressed<Unit>>().uncompressedLength;
     }
     MOZ_TRY(xdr->codeUint32(&uncompressedLength));
 
-    if (hasBinSource) {
-      if (mode == XDR_DECODE) {
-#if defined(JS_BUILD_BINAST)
-        auto bytes = xdr->cx()->template make_pod_array<char>(
-            Max<size_t>(uncompressedLength, 1));
-        if (!bytes) {
-          return xdr->fail(JS::TranscodeResult_Throw);
-        }
-        MOZ_TRY(xdr->codeBytes(bytes.get(), uncompressedLength));
-
-        if (!ss->setBinASTSource(xdr->cx(), std::move(bytes),
-                                 uncompressedLength)) {
-          return xdr->fail(JS::TranscodeResult_Throw);
-        }
-#else
-        MOZ_ASSERT(mode != XDR_ENCODE);
+    uint32_t compressedLength;
+    if (mode == XDR_ENCODE) {
+      compressedLength = ss->data.as<Compressed<Unit>>().raw.length();
+    }
+    MOZ_TRY(xdr->codeUint32(&compressedLength));
+
+    if (mode == XDR_DECODE) {
+      // Compressed data is always single-byte chars.
+      auto bytes = xdr->cx()->template make_pod_array<char>(compressedLength);
+      if (!bytes) {
         return xdr->fail(JS::TranscodeResult_Throw);
-#endif /* JS_BUILD_BINAST */
-      } else {
-        void* bytes = ss->binASTData();
-        MOZ_TRY(xdr->codeBytes(bytes, uncompressedLength));
+      }
+      MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
+
+      if (!ss->initializeWithCompressedSource<Unit>(xdr->cx(), std::move(bytes),
+                                                    compressedLength,
+                                                    uncompressedLength)) {
+        return xdr->fail(JS::TranscodeResult_Throw);
       }
     } else {
-      // A compressed length of 0 indicates source is uncompressed
-      uint32_t compressedLength;
-      if (mode == XDR_ENCODE) {
-        compressedLength = ss->compressedLengthOrZero();
-      }
-      MOZ_TRY(xdr->codeUint32(&compressedLength));
-
-      uint8_t srcCharSize;
-      if (mode == XDR_ENCODE) {
-        srcCharSize = ss->sourceCharSize();
-      }
-      MOZ_TRY(xdr->codeUint8(&srcCharSize));
-
-      if (srcCharSize != 1 && srcCharSize != 2) {
-        // Fail in debug, but only soft-fail in release, if the source-char
-        // size is invalid.
-        MOZ_ASSERT_UNREACHABLE("bad XDR source chars size");
-        return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
+      void* bytes =
+          const_cast<char*>(ss->data.as<Compressed<Unit>>().raw.chars());
+      MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
+    }
+
+    return Ok();
+  };
+
+  auto CodeUncompressedData = [xdr, ss, &retrievable](auto unit) -> XDRResult {
+    using Unit = decltype(unit);
+
+    static_assert(std::is_same<Unit, Utf8Unit>::value ||
+                      std::is_same<Unit, char16_t>::value,
+                  "should handle UTF-8 and UTF-16");
+
+    if (mode == XDR_ENCODE) {
+      MOZ_ASSERT(ss->data.is<Uncompressed<Unit>>());
+    }
+
+    if (retrievable) {
+      // It's unnecessary to code uncompressed data if it can just be retrieved
+      // using the source hook.
+      return Ok();
+    }
+
+    uint32_t uncompressedLength = 0;
+    if (mode == XDR_ENCODE) {
+      uncompressedLength = ss->data.as<Uncompressed<Unit>>().length();
+    }
+    MOZ_TRY(xdr->codeUint32(&uncompressedLength));
+
+    return ss->xdrUncompressedSource(xdr, sizeof(Unit), uncompressedLength);
+  };
+
+  auto CodeBinASTData = [xdr, ss]() -> XDRResult {
+#if !defined(JS_BUILD_BINAST)
+    return xdr->fail(JS::TranscodeResult_Throw);
+#else
+    // XDR the length of the BinAST data.
+    uint32_t binASTLength;
+    if (mode == XDR_ENCODE) {
+      binASTLength = ss->data.as<BinAST>().string.length();
+    }
+    MOZ_TRY(xdr->codeUint32(&binASTLength));
+
+    // XDR the BinAST data.
+    UniquePtr<char[], JS::FreePolicy> bytes;
+    if (mode == XDR_DECODE) {
+      bytes = xdr->cx()->template make_pod_array<char>(
+          Max<size_t>(binASTLength, 1));
+      if (!bytes) {
+        return xdr->fail(JS::TranscodeResult_Throw);
       }
-
-      if (compressedLength) {
-        if (mode == XDR_DECODE) {
-          // Compressed data is always single-byte chars.
-          auto bytes =
-              xdr->cx()->template make_pod_array<char>(compressedLength);
-          if (!bytes) {
-            return xdr->fail(JS::TranscodeResult_Throw);
-          }
-          MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
-
-          if (!(srcCharSize == 1 ? ss->initializeWithCompressedSource<Utf8Unit>(
-                                       xdr->cx(), std::move(bytes),
-                                       compressedLength, uncompressedLength)
-                                 : ss->initializeWithCompressedSource<char16_t>(
-                                       xdr->cx(), std::move(bytes),
-                                       compressedLength, uncompressedLength))) {
-            return xdr->fail(JS::TranscodeResult_Throw);
-          }
-        } else {
-          void* bytes = srcCharSize == 1 ? ss->compressedData<Utf8Unit>()
-                                         : ss->compressedData<char16_t>();
-          MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
-        }
-      } else {
-        MOZ_TRY(
-            ss->xdrUncompressedSource(xdr, srcCharSize, uncompressedLength));
-      }
+      MOZ_TRY(xdr->codeBytes(bytes.get(), binASTLength));
+    } else {
+      void* bytes = ss->binASTData();
+      MOZ_TRY(xdr->codeBytes(bytes, binASTLength));
     }
 
-    uint8_t hasMetadata = !!ss->binASTMetadata_;
+    // XDR any BinAST metadata.
+    uint8_t hasMetadata;
+    if (mode == XDR_ENCODE) {
+      hasMetadata = ss->binASTMetadata_ != nullptr;
+    }
     MOZ_TRY(xdr->codeUint8(&hasMetadata));
+
+    UniquePtr<frontend::BinASTSourceMetadata> freshMetadata;
     if (hasMetadata) {
-#if defined(JS_BUILD_BINAST)
-      UniquePtr<frontend::BinASTSourceMetadata>& binASTMetadata =
-          ss->binASTMetadata_;
+      // If we're decoding, we decode into fresh metadata.  If we're encoding,
+      // we encode *from* the stored metadata.
+      auto& binASTMetadata =
+          mode == XDR_DECODE ? freshMetadata : ss->binASTMetadata_;
+
       uint32_t numBinASTKinds;
       uint32_t numStrings;
       if (mode == XDR_ENCODE) {
         numBinASTKinds = binASTMetadata->numBinASTKinds();
         numStrings = binASTMetadata->numStrings();
       }
       MOZ_TRY(xdr->codeUint32(&numBinASTKinds));
       MOZ_TRY(xdr->codeUint32(&numStrings));
 
       if (mode == XDR_DECODE) {
         // Use calloc, since we're storing this immediately, and filling it
         // might GC, to avoid marking bogus atoms.
-        auto metadata = static_cast<frontend::BinASTSourceMetadata*>(
-            js_calloc(frontend::BinASTSourceMetadata::totalSize(numBinASTKinds,
-                                                                numStrings)));
-        if (!metadata) {
+        void* mem = js_calloc(frontend::BinASTSourceMetadata::totalSize(
+            numBinASTKinds, numStrings));
+        if (!mem) {
           return xdr->fail(JS::TranscodeResult_Throw);
         }
-        new (metadata)
+
+        auto metadata = new (mem)
             frontend::BinASTSourceMetadata(numBinASTKinds, numStrings);
-        ss->setBinASTSourceMetadata(metadata);
+        binASTMetadata.reset(metadata);
       }
 
+      frontend::BinASTKind* binASTKindBase = binASTMetadata->binASTKindBase();
       for (uint32_t i = 0; i < numBinASTKinds; i++) {
-        frontend::BinASTKind* binASTKindBase = binASTMetadata->binASTKindBase();
         MOZ_TRY(xdr->codeEnum32(&binASTKindBase[i]));
       }
 
       RootedAtom atom(xdr->cx());
       JSAtom** atomsBase = binASTMetadata->atomsBase();
       auto slices = binASTMetadata->sliceBase();
-      auto sourceBase = reinterpret_cast<const char*>(ss->binASTSource());
+      const char* sourceBase = mode == XDR_ENCODE
+                                   ? bytes.get()
+                                   : ss->data.as<BinAST>().string.chars();
 
       for (uint32_t i = 0; i < numStrings; i++) {
         uint8_t isNull;
         if (mode == XDR_ENCODE) {
           atom = binASTMetadata->getAtom(i);
           isNull = !atom;
         }
         MOZ_TRY(xdr->codeUint8(&isNull));
@@ -2748,24 +2843,66 @@ XDRResult ScriptSource::xdrDataMember(XD
         MOZ_TRY(xdr->codeUint64(&sliceOffset));
         MOZ_TRY(xdr->codeUint32(&sliceLen));
 
         if (mode == XDR_DECODE) {
           new (&slices[i]) frontend::BinASTSourceMetadata::CharSlice(
               sourceBase + sliceOffset, sliceLen);
         }
       }
-#else
-      // No BinAST, no BinASTMetadata
-      MOZ_ASSERT(mode != XDR_ENCODE);
-      return xdr->fail(JS::TranscodeResult_Throw);
-#endif  // JS_BUILD_BINAST
+    }
+
+    if (mode == XDR_DECODE) {
+      if (!ss->initializeBinAST(xdr->cx(), std::move(bytes), binASTLength,
+                                std::move(freshMetadata))) {
+        return xdr->fail(JS::TranscodeResult_Throw);
+      }
+    } else {
+      MOZ_ASSERT(freshMetadata == nullptr);
     }
-  }
-
+
+    return Ok();
+#endif  // !defined(JS_BUILD_BINAST)
+  };
+
+  switch (tag) {
+    case DataType::CompressedUtf8:
+      // The argument here is just for overloading -- its value doesn't matter.
+      return CodeCompressedData(Utf8Unit('0'));
+
+    case DataType::UncompressedUtf8:
+      // The argument here is just for overloading -- its value doesn't matter.
+      return CodeUncompressedData(Utf8Unit('0'));
+
+    case DataType::CompressedUtf16:
+      // The argument here is just for overloading -- its value doesn't matter.
+      return CodeCompressedData(char16_t('0'));
+
+    case DataType::UncompressedUtf16:
+      // The argument here is just for overloading -- its value doesn't matter.
+      return CodeUncompressedData(char16_t('0'));
+
+    case DataType::Missing: {
+      MOZ_ASSERT(ss->data.is<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::BinAST:
+      return CodeBinASTData();
+  }
+
+  // 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 ScriptSource::XDR(XDRState<mode>* xdr,
                             const mozilla::Maybe<JS::CompileOptions>& options,
                             MutableHandle<ScriptSourceHolder> holder) {
@@ -2786,17 +2923,17 @@ XDRResult ScriptSource::XDR(XDRState<mod
     // Most CompileOptions fields aren't used by ScriptSourceObject, and those
     // that are (element; elementAttributeName) aren't preserved by XDR. So
     // this can be simple.
     if (!ss->initFromOptions(cx, *options)) {
       return xdr->fail(JS::TranscodeResult_Throw);
     }
   }
 
-  MOZ_TRY(xdrDataMember(xdr, ss));
+  MOZ_TRY(xdrData(xdr, ss));
 
   uint8_t haveSourceMap = ss->hasSourceMapURL();
   MOZ_TRY(xdr->codeUint8(&haveSourceMap));
 
   if (haveSourceMap) {
     UniqueTwoByteChars& sourceMapURL(ss->sourceMapURL_);
     uint32_t sourceMapURLLen =
         (mode == XDR_DECODE) ? 0 : js_strlen(sourceMapURL.get());
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -618,20 +618,27 @@ class ScriptSource {
   // unique anymore.
   uint32_t id_;
 
   // How many ids have been handed out to sources.
   static mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent,
                          mozilla::recordreplay::Behavior::DontPreserve>
       idCount_;
 
-  // True if we can call JSRuntime::sourceHook to load the source on
-  // demand. If sourceRetrievable_ and hasSourceText() are false, it is not
-  // possible to get source at all.
+  // If |data| contains compressed, uncompressed, or missing data, then:
+  //
+  //   * If this field is true, we can call JSRuntime::sourceHook to load the
+  //     source on demand (and so we're free to discard compressed/uncompressed
+  //     source, because we can use the source hook to load it when we need it).
+  //   * If this field is false, then either source is completely missing, or
+  //     we can't discard compressed/uncompressed source for this.
+  //
+  // This field is not meaningful for BinAST data.
   bool sourceRetrievable_ : 1;
+
   bool hasIntroductionOffset_ : 1;
   bool containsAsmJS_ : 1;
 
   UniquePtr<frontend::BinASTSourceMetadata> binASTMetadata_;
 
   template <typename Unit>
   const Unit* chunkUnits(JSContext* cx,
                          UncompressedSourceCache::AutoHoldEntry& holder,
@@ -864,40 +871,16 @@ class ScriptSource {
 
  public:
   template <typename Unit>
   bool hasSourceType() const {
     return data.match(SourceTypeMatcher<Unit>());
   }
 
  private:
-  struct SourceCharSizeMatcher {
-    template <template <typename C> class Data, typename Unit>
-    uint8_t operator()(const Data<Unit>& data) {
-      static_assert(std::is_same<Unit, mozilla::Utf8Unit>::value ||
-                        std::is_same<Unit, char16_t>::value,
-                    "should only have UTF-8 or UTF-16 source char");
-      return sizeof(Unit);
-    }
-
-    uint8_t operator()(const BinAST&) {
-      MOZ_CRASH("BinAST source has no source-char size");
-      return 0;
-    }
-
-    uint8_t operator()(const Missing&) {
-      MOZ_CRASH("missing source has no source-char size");
-      return 0;
-    }
-  };
-
- public:
-  uint8_t sourceCharSize() const { return data.match(SourceCharSizeMatcher()); }
-
- private:
   struct UncompressedLengthMatcher {
     template <typename Unit>
     size_t operator()(const Uncompressed<Unit>& u) {
       return u.length();
     }
 
     template <typename Unit>
     size_t operator()(const Compressed<Unit>& u) {
@@ -913,44 +896,16 @@ class ScriptSource {
   };
 
  public:
   size_t length() const {
     MOZ_ASSERT(hasSourceText() || hasBinASTSource());
     return data.match(UncompressedLengthMatcher());
   }
 
- private:
-  struct CompressedLengthOrZeroMatcher {
-    template <typename Unit>
-    size_t operator()(const Uncompressed<Unit>&) {
-      return 0;
-    }
-
-    template <typename Unit>
-    size_t operator()(const Compressed<Unit>& c) {
-      return c.raw.length();
-    }
-
-    size_t operator()(const BinAST&) {
-      MOZ_CRASH("trying to get compressed length for BinAST data");
-      return 0;
-    }
-
-    size_t operator()(const Missing&) {
-      MOZ_CRASH("missing source data");
-      return 0;
-    }
-  };
-
- public:
-  size_t compressedLengthOrZero() const {
-    return data.match(CompressedLengthOrZeroMatcher());
-  }
-
   JSFlatString* substring(JSContext* cx, size_t start, size_t stop);
   JSFlatString* substringDontDeflate(JSContext* cx, size_t start, size_t stop);
 
   MOZ_MUST_USE bool appendSubstring(JSContext* cx, js::StringBuffer& buf,
                                     size_t start, size_t stop);
 
   bool isFunctionBody() { return parameterListEnd_ != 0; }
   JSFlatString* functionBodyString(JSContext* cx);
@@ -1004,21 +959,22 @@ class ScriptSource {
    * Do not take ownership of the given `buf`. Store the canonical, shared
    * and de-duplicated version. If there is no extant shared version of
    * `buf`, make a copy.
    */
   MOZ_MUST_USE bool setBinASTSourceCopy(JSContext* cx, const uint8_t* buf,
                                         size_t len);
 
   /*
-   * Take ownership of the given `buf` and return the canonical, shared and
-   * de-duplicated version.
+   * Initialize this as containing BinAST data for |buf|/|len|, using a shared,
+   * deduplicated version of |buf| if necessary.
    */
-  MOZ_MUST_USE bool setBinASTSource(JSContext* cx, UniqueChars&& buf,
-                                    size_t len);
+  MOZ_MUST_USE bool initializeBinAST(
+      JSContext* cx, UniqueChars&& buf, size_t len,
+      UniquePtr<frontend::BinASTSourceMetadata> metadata);
 
   const uint8_t* binASTSource();
 
 #endif /* JS_BUILD_BINAST */
 
  private:
   void performTaskWork(SourceCompressionTask* task);
 
@@ -1146,18 +1102,18 @@ class ScriptSource {
   // Inform `this` source that it has been fully parsed.
   void recordParseEnded() {
     MOZ_ASSERT(parseEnded_.IsNull());
     parseEnded_ = ReallyNow();
   }
 
  private:
   template <XDRMode mode>
-  static MOZ_MUST_USE XDRResult xdrDataMember(XDRState<mode>* xdr,
-                                              ScriptSource* ss);
+  static MOZ_MUST_USE XDRResult xdrData(XDRState<mode>* const xdr,
+                                        ScriptSource* const ss);
 
  public:
   template <XDRMode mode>
   static MOZ_MUST_USE XDRResult
   XDR(XDRState<mode>* xdr, const mozilla::Maybe<JS::CompileOptions>& options,
       MutableHandle<ScriptSourceHolder> ss);
 
   void trace(JSTracer* trc);