Bug 1597152 - Part 1: Set lazy function position after reading tree. r=Yoric
authorTooru Fujisawa <arai_a@mac.com>
Fri, 22 Nov 2019 14:19:29 +0000
changeset 503376 24087ff1dfa9c14fc5f665eab2d06a606989dc30
parent 503375 da0c2ed5c0e3326277f21d7a6fc1c95382c92bd0
child 503377 c883249bab17b93315a682ce490df4264e63959b
push id36833
push userbtara@mozilla.com
push dateFri, 22 Nov 2019 21:40:53 +0000
treeherdermozilla-central@2c912e46295e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersYoric
bugs1597152
milestone72.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 1597152 - Part 1: Set lazy function position after reading tree. r=Yoric Differential Revision: https://phabricator.services.mozilla.com/D53368
js/src/frontend/BinASTParserPerTokenizer.cpp
js/src/frontend/BinASTTokenReaderContext.cpp
js/src/frontend/BinASTTokenReaderContext.h
js/src/frontend/BinASTTokenReaderMultipart.h
js/src/vm/JSScript.h
--- a/js/src/frontend/BinASTParserPerTokenizer.cpp
+++ b/js/src/frontend/BinASTParserPerTokenizer.cpp
@@ -132,16 +132,18 @@ JS::Result<ParseNode*> BinASTParserPerTo
   }
 
   MOZ_TRY(tokenizer_->readHeader());
 
   ParseNode* result(nullptr);
   const auto topContext = RootContext();
   MOZ_TRY_VAR(result, asFinalParser()->parseProgram(topContext));
 
+  MOZ_TRY(tokenizer_->readTreeFooter());
+
   mozilla::Maybe<GlobalScope::Data*> bindings =
       NewGlobalScopeData(cx_, varScope, alloc_, pc_);
   if (MOZ_UNLIKELY(!bindings)) {
     return cx_->alreadyReportedError();
   }
   globalsc->bindings = *bindings;
 
   if (metadataPtr) {
@@ -371,16 +373,18 @@ JS::Result<Ok> BinASTParserPerTokenizer<
                      /* lineno = */ 0, start, ParseGoal::Script));
 
   if (funbox->strict()) {
     lazy->setStrict();
   }
   MOZ_ASSERT(lazy->isBinAST());
   funbox->initLazyScript(lazy);
 
+  MOZ_TRY(tokenizer_->registerLazyScript(lazy));
+
   return Ok();
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParserPerTokenizer<Tok>::addScopeName(
     AssertedScopeKind scopeKind, HandleAtom name, ParseContext::Scope* scope,
     DeclarationKind declKind, bool isCaptured, bool allowDuplicateName) {
   auto ptr = scope->lookupDeclaredNameForAdd(name);
--- a/js/src/frontend/BinASTTokenReaderContext.cpp
+++ b/js/src/frontend/BinASTTokenReaderContext.cpp
@@ -1026,17 +1026,18 @@ using Sum = HuffmanPreludeReader::Sum;
 using UnsignedLong = HuffmanPreludeReader::UnsignedLong;
 
 BinASTTokenReaderContext::BinASTTokenReaderContext(JSContext* cx,
                                                    ErrorReporter* er,
                                                    const uint8_t* start,
                                                    const size_t length)
     : BinASTTokenReaderBase(cx, er, start, length),
       metadata_(nullptr),
-      posBeforeTree_(nullptr) {
+      posBeforeTree_(nullptr),
+      lazyScripts_(cx) {
   MOZ_ASSERT(er);
 }
 
 BinASTTokenReaderContext::~BinASTTokenReaderContext() {
   if (metadata_ && metadataOwned_ == MetadataOwnership::Owned) {
     UniqueBinASTSourceMetadataPtr ptr(metadata_);
   }
 }
@@ -1102,16 +1103,48 @@ JS::Result<Ok> BinASTTokenReaderContext:
 #endif  // BINAST_CX_MAGIC_HEADER
 
   MOZ_TRY(readStringPrelude());
   MOZ_TRY(readHuffmanPrelude());
 
   return Ok();
 }
 
+JS::Result<Ok> BinASTTokenReaderContext::readTreeFooter() {
+  flushBitStream();
+
+  BINJS_MOZ_TRY_DECL(numLazy, readVarU32<Compression::No>());
+  if (numLazy != lazyScripts_.length()) {
+    return raiseError("The number of lazy functions does not match");
+  }
+
+  for (size_t i = 0; i < numLazy; i++) {
+    BINJS_MOZ_TRY_DECL(len, readVarU32<Compression::No>());
+    // Use sourceEnd as temporary space to store length of each script.
+    lazyScripts_[i]->setPositions(0, len, 0, len);
+  }
+
+  for (size_t i = 0; i < numLazy; i++) {
+    uint32_t begin = offset();
+    uint32_t len = lazyScripts_[i]->sourceEnd();
+
+    current_ += len;
+
+    lazyScripts_[0]->setPositions(begin, begin + len, begin, begin + len);
+    lazyScripts_[0]->setColumn(begin);
+  }
+
+  return Ok();
+}
+
+void BinASTTokenReaderContext::flushBitStream() {
+  current_ -= bitBuffer.numUnusedBytes();
+  bitBuffer.flush();
+}
+
 JS::Result<Ok> BinASTTokenReaderContext::readStringPrelude() {
   BINJS_MOZ_TRY_DECL(stringsNumberOfEntries, readVarU32<Compression::No>());
 
   const uint32_t MAX_NUMBER_OF_STRINGS = 32768;
 
   if (MOZ_UNLIKELY(stringsNumberOfEntries > MAX_NUMBER_OF_STRINGS)) {
     return raiseError("Too many entries in strings dictionary");
   }
@@ -1423,17 +1456,23 @@ JS::Result<BinASTVariant> BinASTTokenRea
 JS::Result<uint32_t> BinASTTokenReaderContext::readUnsignedLong(
     const FieldContext& context) {
   BINJS_MOZ_TRY_DECL(result, readFieldFromTable(context.position_));
   return result.toUnsignedLong();
 }
 
 JS::Result<BinASTTokenReaderBase::SkippableSubTree>
 BinASTTokenReaderContext::readSkippableSubTree(const FieldContext&) {
-  return raiseError("Not Yet Implemented");
+  // Postions are set when reading lazy functions after the tree.
+  return SkippableSubTree(0, 0);
+}
+
+JS::Result<Ok> BinASTTokenReaderContext::registerLazyScript(LazyScript* lazy) {
+  BINJS_TRY(lazyScripts_.append(lazy));
+  return Ok();
 }
 
 JS::Result<Ok> BinASTTokenReaderContext::enterSum(
     BinASTKind& tag, const FieldOrRootContext& context) {
   return context.match(
       [this, &tag](const BinASTTokenReaderBase::FieldContext& asFieldContext)
           -> JS::Result<Ok> {
         // This tuple is the value of the field we're currently reading.
--- a/js/src/frontend/BinASTTokenReaderContext.h
+++ b/js/src/frontend/BinASTTokenReaderContext.h
@@ -1479,16 +1479,22 @@ class MOZ_STACK_CLASS BinASTTokenReaderC
     template <Compression Compression>
     MOZ_MUST_USE JS::Result<HuffmanLookup> getHuffmanLookup(
         BinASTTokenReaderContext& owner);
 
     // Advance the bit buffer by `bitLength` bits.
     template <Compression Compression>
     void advanceBitBuffer(const uint8_t bitLength);
 
+    // Returns the number of buffered but unused bytes.
+    size_t numUnusedBytes() const { return bitLength_ / 8; }
+
+    // Release all buffer.
+    void flush() { bitLength_ = 0; }
+
    private:
     // The contents of the buffer.
     //
     // - Bytes are added in the same order as the bytestream.
     // - Individual bits within bytes are mirrored.
     //
     // In other words, if the byte stream starts with
     // `0b_HGFE_DCBA`, `0b_PONM_LKJI`, `0b_0000_0000`,
@@ -1536,16 +1542,28 @@ class MOZ_STACK_CLASS BinASTTokenReaderC
 
  public:
   /**
    * Read the header of the file.
    */
   MOZ_MUST_USE JS::Result<Ok> readHeader();
 
   /**
+   * Read the footer of the tree, that contains lazy functions.
+   */
+  MOZ_MUST_USE JS::Result<Ok> readTreeFooter();
+
+ private:
+  /**
+   * Stop reading bit stream and unget unused buffer.
+   */
+  void flushBitStream();
+
+ public:
+  /**
    * Read the string dictionary from the header of the file.
    */
   MOZ_MUST_USE JS::Result<Ok> readStringPrelude();
 
   /**
    * Read the huffman dictionary from the header of the file.
    */
   MOZ_MUST_USE JS::Result<Ok> readHuffmanPrelude();
@@ -1607,16 +1625,21 @@ class MOZ_STACK_CLASS BinASTTokenReaderC
    *
    * This does *not* attempt to parse the subtree itself. Rather, the
    * returned `SkippableSubTree` contains the necessary information
    * to parse/tokenize the subtree at a later stage
    */
   MOZ_MUST_USE JS::Result<SkippableSubTree> readSkippableSubTree(
       const FieldContext&);
 
+  /**
+   * Register LazyScript for later modification.
+   */
+  MOZ_MUST_USE JS::Result<Ok> registerLazyScript(LazyScript* lazy);
+
   // --- Composite values.
   //
   // The underlying format does NOT allows for a `null` composite value.
   //
   // Reading will return an error either in case of I/O error or in case of
   // a format problem. Reading from a poisoned tokenizer is an error and
   // will cause assertion failures.
 
@@ -1742,16 +1765,20 @@ class MOZ_STACK_CLASS BinASTTokenReaderC
 
  private:
   enum class MetadataOwnership { Owned, Unowned };
   MetadataOwnership metadataOwned_ = MetadataOwnership::Owned;
   BinASTSourceMetadataContext* metadata_;
 
   const uint8_t* posBeforeTree_;
 
+  // LazyScript created while reading the tree.
+  // After reading tree, the start/end offset are set to correct value.
+  Rooted<GCVector<LazyScript*>> lazyScripts_;
+
  public:
   BinASTTokenReaderContext(const BinASTTokenReaderContext&) = delete;
   BinASTTokenReaderContext(BinASTTokenReaderContext&&) = delete;
   BinASTTokenReaderContext& operator=(BinASTTokenReaderContext&) = delete;
 
  public:
   void traceMetadata(JSTracer* trc);
   BinASTSourceMetadata* takeMetadata();
--- a/js/src/frontend/BinASTTokenReaderMultipart.h
+++ b/js/src/frontend/BinASTTokenReaderMultipart.h
@@ -63,16 +63,21 @@ class MOZ_STACK_CLASS BinASTTokenReaderM
 
   ~BinASTTokenReaderMultipart();
 
   /**
    * Read the header of the file.
    */
   MOZ_MUST_USE JS::Result<Ok> readHeader();
 
+  /**
+   * Read the footer of the tree, that multipart format doesn't have.
+   */
+  MOZ_MUST_USE JS::Result<Ok> readTreeFooter() { return Ok(); }
+
   // --- Primitive values.
   //
   // Note that the underlying format allows for a `null` value for primitive
   // values.
   //
   // Reading will return an error either in case of I/O error or in case of
   // a format problem. Reading if an exception in pending is an error and
   // will cause assertion failures. Do NOT attempt to read once an exception
@@ -135,16 +140,24 @@ class MOZ_STACK_CLASS BinASTTokenReaderM
    *
    * This does *not* attempt to parse the subtree itself. Rather, the
    * returned `SkippableSubTree` contains the necessary information
    * to parse/tokenize the subtree at a later stage
    */
   MOZ_MUST_USE JS::Result<SkippableSubTree> readSkippableSubTree(
       const FieldContext&);
 
+  /**
+   * Register LazyScript for later modification.
+   * Not used in multipart format.
+   */
+  MOZ_MUST_USE JS::Result<Ok> registerLazyScript(LazyScript* lazy) {
+    return Ok();
+  }
+
   // --- Composite values.
   //
   // The underlying format does NOT allows for a `null` composite value.
   //
   // Reading will return an error either in case of I/O error or in case of
   // a format problem. Reading from a poisoned tokenizer is an error and
   // will cause assertion failures.
 
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1668,16 +1668,39 @@ class BaseScript : public gc::TenuredCel
   }
 
   uint32_t sourceStart() const { return sourceStart_; }
   uint32_t sourceEnd() const { return sourceEnd_; }
   uint32_t sourceLength() const { return sourceEnd_ - sourceStart_; }
   uint32_t toStringStart() const { return toStringStart_; }
   uint32_t toStringEnd() const { return toStringEnd_; }
 
+#if defined(JS_BUILD_BINAST)
+  // Set the position of the function in the source code.
+  //
+  // BinAST file format can put lazy functions after the entire tree,
+  // and in that case LazyScript::Create will be called with
+  // dummy values for those positions, and then once it reaches to the lazy
+  // function part, this function is called to set those positions to
+  // correct value.
+  void setPositions(uint32_t sourceStart, uint32_t sourceEnd,
+                    uint32_t toStringStart, uint32_t toStringEnd) {
+    MOZ_ASSERT(toStringStart <= sourceStart);
+    MOZ_ASSERT(sourceStart <= sourceEnd);
+    MOZ_ASSERT(sourceEnd <= toStringEnd);
+
+    sourceStart_ = sourceStart;
+    sourceEnd_ = sourceEnd;
+    toStringStart_ = toStringStart;
+    toStringEnd_ = toStringEnd;
+  }
+
+  void setColumn(uint32_t column) { column_ = column; }
+#endif
+
   void setToStringEnd(uint32_t toStringEnd) {
     MOZ_ASSERT(toStringStart_ <= toStringEnd);
     MOZ_ASSERT(toStringEnd_ >= sourceEnd_);
     toStringEnd_ = toStringEnd;
   }
 
   uint32_t lineno() const { return lineno_; }
   uint32_t column() const { return column_; }