Bug 1456609 - Expose a method readSkippableSubTree for BinTokenReader{Tester, Multipart} draft
authorDavid Teller <dteller@mozilla.com>
Tue, 24 Apr 2018 22:29:19 +0200
changeset 791274 c7d4073d3bbb09eac719975a6b6b30d403f42a3b
parent 791268 ab5a5dbfa17fa515843f85e9625fb8233ce08065
push id108773
push userdteller@mozilla.com
push dateThu, 03 May 2018 22:40:03 +0000
bugs1456609
milestone61.0a1
Bug 1456609 - Expose a method readSkippableSubTree for BinTokenReader{Tester, Multipart} MozReview-Commit-ID: 2jceBpJOOoS
js/src/frontend/BinTokenReaderBase.h
js/src/frontend/BinTokenReaderMultipart.cpp
js/src/frontend/BinTokenReaderMultipart.h
js/src/frontend/BinTokenReaderTester.cpp
js/src/frontend/BinTokenReaderTester.h
js/src/frontend/binsource/src/main.rs
--- a/js/src/frontend/BinTokenReaderBase.h
+++ b/js/src/frontend/BinTokenReaderBase.h
@@ -24,16 +24,40 @@ using namespace JS;
 // A constant used by tokenizers to represent a null float.
 extern const uint64_t NULL_FLOAT_REPRESENTATION;
 
 class MOZ_STACK_CLASS BinTokenReaderBase
 {
   public:
     template<typename T> using ErrorResult = mozilla::GenericErrorResult<T>;
 
+    // The information needed to skip a subtree.
+    class SkippableSubTree {
+      public:
+        SkippableSubTree(const uint8_t* start, const size_t length)
+          : start_(start)
+          , length_(length)
+        { }
+
+        // The position in the source buffer at which the subtree starts.
+        //
+        // `SkippableSubTree` does *not* attempt to keep anything alive.
+        const uint8_t* start() const {
+            return start_;
+        }
+
+        // The length of the subtree.
+        size_t length() const {
+            return length_;
+        }
+      private:
+        const uint8_t* start_;
+        const size_t length_;
+    };
+
     /**
      * Return the position of the latest token.
      */
     TokenPos pos();
     TokenPos pos(size_t startOffset);
     size_t offset() const;
 
      /**
--- a/js/src/frontend/BinTokenReaderMultipart.cpp
+++ b/js/src/frontend/BinTokenReaderMultipart.cpp
@@ -269,16 +269,32 @@ BinTokenReaderMultipart::readVariant()
         return raiseError("Invalid string enum variant");
 
     if (!variantsTable_.add(variantsPtr, index, *variant))
         return raiseOOM();
 
     return *variant;
 }
 
+JS::Result<BinTokenReaderBase::SkippableSubTree>
+BinTokenReaderMultipart::readSkippableSubTree()
+{
+    updateLatestKnownGood();
+    BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
+
+    if (current_ + byteLen > stop_ || current_ + byteLen < current_)
+        return raiseError("Invalid byte length in readSkippableSubTree");
+
+    const auto start = current_;
+
+    current_ += byteLen;
+
+    return BinTokenReaderBase::SkippableSubTree(start, byteLen);
+}
+
 // Untagged tuple:
 // - contents (specified by the higher-level grammar);
 JS::Result<Ok>
 BinTokenReaderMultipart::enterUntaggedTuple(AutoTuple& guard)
 {
     guard.init();
     return Ok();
 }
--- a/js/src/frontend/BinTokenReaderMultipart.h
+++ b/js/src/frontend/BinTokenReaderMultipart.h
@@ -112,16 +112,25 @@ class MOZ_STACK_CLASS BinTokenReaderMult
     MOZ_MUST_USE JS::Result<Ok> readChars(Chars&);
 
     /**
      * Read a single `BinVariant | null` value.
      */
     MOZ_MUST_USE JS::Result<Maybe<BinVariant>> readMaybeVariant();
     MOZ_MUST_USE JS::Result<BinVariant> readVariant();
 
+    /**
+     * Read over a single `[Skippable]` subtree value.
+     *
+     * 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();
+
     // --- 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/frontend/BinTokenReaderTester.cpp
+++ b/js/src/frontend/BinTokenReaderTester.cpp
@@ -213,16 +213,32 @@ BinTokenReaderTester::readVariant()
     BINJS_MOZ_TRY_DECL(variant, cx_->runtime()->binast().binVariant(cx_, slice));
     if (!variant)
         return raiseError("Not a variant");
 
     MOZ_TRY(readConst("</string>"));
     return *variant;
 }
 
+JS::Result<BinTokenReaderBase::SkippableSubTree>
+BinTokenReaderTester::readSkippableSubTree()
+{
+    updateLatestKnownGood();
+    BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
+
+    if (current_ + byteLen > stop_ || current_ + byteLen < current_)
+        return raiseError("Invalid byte length in readSkippableSubTree");
+
+    const auto start = current_;
+
+    current_ += byteLen;
+
+    return BinTokenReaderBase::SkippableSubTree(start, byteLen);
+}
+
 // Untagged tuple:
 // - "<tuple>";
 // - contents (specified by the higher-level grammar);
 // - "</tuple>"
 JS::Result<Ok>
 BinTokenReaderTester::enterUntaggedTuple(AutoTuple& guard)
 {
     MOZ_TRY(readConst("<tuple>"));
--- a/js/src/frontend/BinTokenReaderTester.h
+++ b/js/src/frontend/BinTokenReaderTester.h
@@ -133,16 +133,25 @@ class MOZ_STACK_CLASS BinTokenReaderTest
     MOZ_MUST_USE JS::Result<Ok> readChars(Chars&);
 
     /**
      * Read a single `BinVariant | null` value.
      */
     MOZ_MUST_USE JS::Result<Maybe<BinVariant>> readMaybeVariant();
     MOZ_MUST_USE JS::Result<BinVariant> readVariant();
 
+    /**
+     * Read over a single `[Skippable]` subtree value.
+     *
+     * 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();
+
     // --- 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/frontend/binsource/src/main.rs
+++ b/js/src/frontend/binsource/src/main.rs
@@ -1043,21 +1043,21 @@ impl CPPExporter {
                         Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readBool());", var_name = var_name)))
                     } else {
                         (None,
                         Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readBool());", var_name = var_name)))
                     }
                 }
                 Some(IsNullable { is_nullable: false, content: Primitive::Offset }) => {
                     if needs_block {
-                        (Some(format!("uint32_t {var_name};", var_name = var_name)),
-                        Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readOffset());", var_name = var_name)))
+                        (Some(format!("BinTokenReaderBase::SkippableSubTree {var_name};", var_name = var_name)),
+                        Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readSkippableSubTree());", var_name = var_name)))
                     } else {
                         (None,
-                        Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readOffset());", var_name = var_name)))
+                        Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readSkippableSubTree());", var_name = var_name)))
                     }
                 }
                 Some(IsNullable { content: Primitive::Void, .. }) => {
                     warn!("Internal error: We shouldn't have any `void` types at this stage.");
                     (Some(format!("// Skipping void field {}", field.name().to_str())),
                         None)
                 }
                 Some(IsNullable { is_nullable: false, content: Primitive::String }) => {