Bug 1456609 - Expose a method readSkippableSubTree for BinTokenReader{Tester, Multipart}
MozReview-Commit-ID: 2jceBpJOOoS
--- 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 }) => {