Bug 1459555 - Part 1: Allow throwing BinAST errors off main thread. (r=arai)
authorEric Faust <efausbmo@gmail.com>
Tue, 02 Oct 2018 01:16:51 -0700
changeset 494889 1487cec16a9c092cf2affd5ef0ecab41c88fb02d
parent 494888 8820b0b524e07926b62e72b8dc4120e46f5405a5
child 494890 ac5bef60b83b7d6e9d9445996305b98efd00e200
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1459555
milestone64.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 1459555 - Part 1: Allow throwing BinAST errors off main thread. (r=arai)
js/src/frontend/BinSource.cpp
js/src/frontend/BinTokenReaderBase.cpp
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/js.msg
js/src/jsapi-tests/testBinTokenReaderTester.cpp
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -122,17 +122,17 @@ BinASTParser<Tok>::parse(GlobalSharedCon
 
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseAux(GlobalSharedContext* globalsc,
                             const uint8_t* start, const size_t length)
 {
     MOZ_ASSERT(globalsc);
 
-    tokenizer_.emplace(cx_, start, length);
+    tokenizer_.emplace(cx_, this, start, length);
 
     BinParseContext globalpc(cx_, this, globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init()) {
         return cx_->alreadyReportedError();
     }
 
     ParseContext::VarScope varScope(cx_, &globalpc, usedNames_);
     if (!varScope.init(&globalpc)) {
--- a/js/src/frontend/BinTokenReaderBase.cpp
+++ b/js/src/frontend/BinTokenReaderBase.cpp
@@ -26,19 +26,24 @@ BinTokenReaderBase::updateLatestKnownGoo
     MOZ_ASSERT(update >= latestKnownGoodPos_);
     latestKnownGoodPos_ = update;
 }
 
 ErrorResult<JS::Error&>
 BinTokenReaderBase::raiseError(const char* description)
 {
     MOZ_ASSERT(!cx_->isExceptionPending());
-    TokenPos pos = this->pos();
-    JS_ReportErrorASCII(cx_, "BinAST parsing error: %s at offsets %u => %u",
-                        description, pos.begin, pos.end);
+    if (MOZ_LIKELY(errorReporter_)) {
+        errorReporter_->reportErrorNoOffset(JSMSG_BINAST, description);
+    } else {
+        // Only true in testing code.
+        TokenPos pos = this->pos();
+        JS_ReportErrorASCII(cx_, "BinAST parsing error: %s at offsets %u => %u",
+                            description, pos.begin, pos.end);
+    }
     return cx_->alreadyReportedError();
 }
 
 ErrorResult<JS::Error&>
 BinTokenReaderBase::raiseOOM()
 {
     ReportOutOfMemory(cx_);
     return cx_->alreadyReportedError();
--- a/js/src/frontend/BinTokenReaderBase.h
+++ b/js/src/frontend/BinTokenReaderBase.h
@@ -3,16 +3,17 @@
  * 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/. */
 
 #ifndef frontend_BinTokenReaderBase_h
 #define frontend_BinTokenReaderBase_h
 
 #include "frontend/BinToken.h"
+#include "frontend/ErrorReporter.h"
 #include "frontend/TokenStream.h"
 
 #include "js/Result.h"
 #include "js/TypeDecls.h"
 
 namespace js {
 namespace frontend {
 
@@ -68,18 +69,19 @@ class MOZ_STACK_CLASS BinTokenReaderBase
     MOZ_MUST_USE ErrorResult<JS::Error&> raiseError(const char* description);
     MOZ_MUST_USE ErrorResult<JS::Error&> raiseOOM();
     MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidNumberOfFields(
         const BinKind kind, const uint32_t expected, const uint32_t got);
     MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidField(const char* kind,
         const BinField field);
 
   protected:
-    BinTokenReaderBase(JSContext* cx, const uint8_t* start, const size_t length)
+    BinTokenReaderBase(JSContext* cx, ErrorReporter* er, const uint8_t* start, const size_t length)
         : cx_(cx)
+        , errorReporter_(er)
         , poisoned_(false)
         , start_(start)
         , current_(start)
         , stop_(start + length)
         , latestKnownGoodPos_(0)
     { }
 
     /**
@@ -147,16 +149,18 @@ class MOZ_STACK_CLASS BinTokenReaderBase
     void updateLatestKnownGood();
 
 #ifdef DEBUG
     bool hasRaisedError() const;
 #endif
 
     JSContext* cx_;
 
+    ErrorReporter* errorReporter_;
+
     // `true` if we have encountered an error. Errors are non recoverable.
     // Attempting to read from a poisoned tokenizer will cause assertion errors.
     bool poisoned_;
 
     // The first byte of the buffer. Not owned.
     const uint8_t* start_;
 
     // The current position.
--- a/js/src/frontend/BinTokenReaderMultipart.cpp
+++ b/js/src/frontend/BinTokenReaderMultipart.cpp
@@ -40,23 +40,25 @@ const char COMPRESSION_IDENTITY[] = "ide
 const uint32_t MAX_NUMBER_OF_STRINGS = 32768;
 
 using AutoList = BinTokenReaderMultipart::AutoList;
 using AutoTaggedTuple = BinTokenReaderMultipart::AutoTaggedTuple;
 using AutoTuple = BinTokenReaderMultipart::AutoTuple;
 using CharSlice = BinaryASTSupport::CharSlice;
 using Chars = BinTokenReaderMultipart::Chars;
 
-BinTokenReaderMultipart::BinTokenReaderMultipart(JSContext* cx, const uint8_t* start, const size_t length)
-  : BinTokenReaderBase(cx, start, length)
+BinTokenReaderMultipart::BinTokenReaderMultipart(JSContext* cx, ErrorReporter* er, const uint8_t* start, const size_t length)
+  : BinTokenReaderBase(cx, er, start, length)
   , grammarTable_(cx)
   , atomsTable_(cx, AtomVector(cx))
   , slicesTable_(cx)
   , posBeforeTree_(nullptr)
-{ }
+{
+    MOZ_ASSERT(er);
+}
 
 JS::Result<Ok>
 BinTokenReaderMultipart::readHeader()
 {
     // Check that we don't call this function twice.
     MOZ_ASSERT(!posBeforeTree_);
 
     // Read global headers.
--- a/js/src/frontend/BinTokenReaderMultipart.h
+++ b/js/src/frontend/BinTokenReaderMultipart.h
@@ -56,24 +56,24 @@ class MOZ_STACK_CLASS BinTokenReaderMult
     };
 
   public:
     /**
      * Construct a token reader.
      *
      * Does NOT copy the buffer.
      */
-    BinTokenReaderMultipart(JSContext* cx, const uint8_t* start, const size_t length);
+    BinTokenReaderMultipart(JSContext* cx, ErrorReporter* er, const uint8_t* start, const size_t length);
 
     /**
      * Construct a token reader.
      *
      * Does NOT copy the buffer.
      */
-    BinTokenReaderMultipart(JSContext* cx, const Vector<uint8_t>& chars);
+    BinTokenReaderMultipart(JSContext* cx, ErrorReporter* er, const Vector<uint8_t>& chars);
 
     /**
      * Read the header of the file.
      */
     MOZ_MUST_USE JS::Result<Ok> readHeader();
 
     // --- Primitive values.
     //
--- a/js/src/frontend/BinTokenReaderTester.cpp
+++ b/js/src/frontend/BinTokenReaderTester.cpp
@@ -18,22 +18,22 @@
 namespace js {
 namespace frontend {
 
 using BinFields = BinTokenReaderTester::BinFields;
 using AutoList = BinTokenReaderTester::AutoList;
 using AutoTaggedTuple = BinTokenReaderTester::AutoTaggedTuple;
 using AutoTuple = BinTokenReaderTester::AutoTuple;
 
-BinTokenReaderTester::BinTokenReaderTester(JSContext* cx, const uint8_t* start, const size_t length)
-    : BinTokenReaderBase(cx, start, length)
+BinTokenReaderTester::BinTokenReaderTester(JSContext* cx, ErrorReporter* er, const uint8_t* start, const size_t length)
+    : BinTokenReaderBase(cx, er, start, length)
 { }
 
-BinTokenReaderTester::BinTokenReaderTester(JSContext* cx, const Vector<uint8_t>& buf)
-    : BinTokenReaderBase(cx, buf.begin(), buf.length())
+BinTokenReaderTester::BinTokenReaderTester(JSContext* cx, ErrorReporter* er, const Vector<uint8_t>& buf)
+    : BinTokenReaderBase(cx, er, buf.begin(), buf.length())
 { }
 
 JS::Result<Ok>
 BinTokenReaderTester::readHeader()
 {
     // This format does not have a header.
     return Ok();
 }
--- a/js/src/frontend/BinTokenReaderTester.h
+++ b/js/src/frontend/BinTokenReaderTester.h
@@ -63,24 +63,24 @@ class MOZ_STACK_CLASS BinTokenReaderTest
     class AutoTaggedTuple;
 
   public:
     /**
      * Construct a token reader.
      *
      * Does NOT copy the buffer.
      */
-    BinTokenReaderTester(JSContext* cx, const uint8_t* start, const size_t length);
+    BinTokenReaderTester(JSContext* cx, ErrorReporter* er, const uint8_t* start, const size_t length);
 
     /**
      * Construct a token reader.
      *
      * Does NOT copy the buffer.
      */
-    BinTokenReaderTester(JSContext* cx, const Vector<uint8_t>& chars);
+    BinTokenReaderTester(JSContext* cx, ErrorReporter* er, const Vector<uint8_t>& chars);
 
     /**
      * Read the header of the file.
      */
     MOZ_MUST_USE JS::Result<Ok> readHeader();
 
     // --- Primitive values.
     //
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -672,8 +672,11 @@ MSG_DEF(JSMSG_RESPONSE_ALREADY_CONSUMED,
 MSG_DEF(JSMSG_BIGINT_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert BigInt to number")
 MSG_DEF(JSMSG_NUMBER_TO_BIGINT, 0, JSEXN_RANGEERR, "can't convert non-finite number to BigInt")
 MSG_DEF(JSMSG_BIGINT_TOO_LARGE, 0, JSEXN_RANGEERR, "BigInt is too large to allocate")
 MSG_DEF(JSMSG_BIGINT_DIVISION_BY_ZERO, 0, JSEXN_RANGEERR, "BigInt division by zero")
 MSG_DEF(JSMSG_BIGINT_NEGATIVE_EXPONENT, 0, JSEXN_RANGEERR, "BigInt negative exponent")
 MSG_DEF(JSMSG_BIGINT_INVALID_SYNTAX, 0, JSEXN_SYNTAXERR, "invalid BigInt syntax")
 MSG_DEF(JSMSG_NOT_BIGINT, 0, JSEXN_TYPEERR, "not a BigInt")
 MSG_DEF(JSMSG_BIGINT_NOT_SERIALIZABLE, 0, JSEXN_TYPEERR, "BigInt value can't be serialized in JSON")
+
+// BinAST
+MSG_DEF(JSMSG_BINAST,                                    1, JSEXN_SYNTAXERR, "BinAST Parsing Error: {0}")
--- a/js/src/jsapi-tests/testBinTokenReaderTester.cpp
+++ b/js/src/jsapi-tests/testBinTokenReaderTester.cpp
@@ -135,66 +135,66 @@ void readFull(const char* path, js::Vect
 }
 
 
 // Reading a simple string.
 BEGIN_TEST(testBinTokenReaderTesterSimpleString)
 {
     js::Vector<uint8_t> contents(cx);
     readFull("jsapi-tests/binast/tokenizer/tester/test-simple-string.binjs", contents);
-    Tokenizer tokenizer(cx, contents);
+    Tokenizer tokenizer(cx, nullptr, contents);
 
     Chars found(cx);
     CHECK(tokenizer.readChars(found).isOk());
 
     CHECK(Tokenizer::equals(found, "simple string")); // FIXME: Find a way to make CHECK_EQUAL use `Tokenizer::equals`.
 
     return true;
 }
 END_TEST(testBinTokenReaderTesterSimpleString)
 
 // Reading a string with embedded 0.
 BEGIN_TEST(testBinTokenReaderTesterStringWithEscapes)
 {
     js::Vector<uint8_t> contents(cx);
     readFull("jsapi-tests/binast/tokenizer/tester/test-string-with-escapes.binjs", contents);
-    Tokenizer tokenizer(cx, contents);
+    Tokenizer tokenizer(cx, nullptr, contents);
 
     Chars found(cx);
     CHECK(tokenizer.readChars(found).isOk());
 
     CHECK(Tokenizer::equals(found, "string with escapes \0\1\0")); // FIXME: Find a way to make CHECK_EQUAL use `Tokenizer::equals`.
 
     return true;
 }
 END_TEST(testBinTokenReaderTesterStringWithEscapes)
 
 // Reading an empty untagged tuple
 BEGIN_TEST(testBinTokenReaderTesterEmptyUntaggedTuple)
 {
     js::Vector<uint8_t> contents(cx);
     readFull("jsapi-tests/binast/tokenizer/tester/test-empty-untagged-tuple.binjs", contents);
-    Tokenizer tokenizer(cx, contents);
+    Tokenizer tokenizer(cx, nullptr, contents);
 
     {
         Tokenizer::AutoTuple guard(tokenizer);
         CHECK(tokenizer.enterUntaggedTuple(guard).isOk());
         CHECK(guard.done().isOk());
     }
 
     return true;
 }
 END_TEST(testBinTokenReaderTesterEmptyUntaggedTuple)
 
 // Reading a untagged tuple with two strings
 BEGIN_TEST(testBinTokenReaderTesterTwoStringsInTuple)
 {
     js::Vector<uint8_t> contents(cx);
     readFull("jsapi-tests/binast/tokenizer/tester/test-trivial-untagged-tuple.binjs", contents);
-    Tokenizer tokenizer(cx, contents);
+    Tokenizer tokenizer(cx, nullptr, contents);
 
     {
         Tokenizer::AutoTuple guard(tokenizer);
         CHECK(tokenizer.enterUntaggedTuple(guard).isOk());
 
         Chars found_0(cx);
         CHECK(tokenizer.readChars(found_0).isOk());
         CHECK(Tokenizer::equals(found_0, "foo")); // FIXME: Find a way to make CHECK_EQUAL use `Tokenizer::equals`.
@@ -210,17 +210,17 @@ BEGIN_TEST(testBinTokenReaderTesterTwoSt
 }
 END_TEST(testBinTokenReaderTesterTwoStringsInTuple)
 
 // Reading a tagged tuple `Pattern { id: "foo", value: 3.1415}`
 BEGIN_TEST(testBinTokenReaderTesterSimpleTaggedTuple)
 {
     js::Vector<uint8_t> contents(cx);
     readFull("jsapi-tests/binast/tokenizer/tester/test-simple-tagged-tuple.binjs", contents);
-    Tokenizer tokenizer(cx, contents);
+    Tokenizer tokenizer(cx, nullptr, contents);
 
     {
         js::frontend::BinKind tag;
         Tokenizer::BinFields fields(cx);
         Tokenizer::AutoTaggedTuple guard(tokenizer);
         CHECK(tokenizer.enterTaggedTuple(tag, fields, guard).isOk());
 
         CHECK(tag == js::frontend::BinKind::BindingIdentifier);
@@ -244,17 +244,17 @@ BEGIN_TEST(testBinTokenReaderTesterSimpl
 END_TEST(testBinTokenReaderTesterSimpleTaggedTuple)
 
 
 // Reading an empty list
 BEGIN_TEST(testBinTokenReaderTesterEmptyList)
 {
     js::Vector<uint8_t> contents(cx);
     readFull("jsapi-tests/binast/tokenizer/tester/test-empty-list.binjs", contents);
-    Tokenizer tokenizer(cx, contents);
+    Tokenizer tokenizer(cx, nullptr, contents);
 
     {
         uint32_t length;
         Tokenizer::AutoList guard(tokenizer);
         CHECK(tokenizer.enterList(length, guard).isOk());
 
         CHECK(length == 0);
         CHECK(guard.done().isOk());
@@ -264,17 +264,17 @@ BEGIN_TEST(testBinTokenReaderTesterEmpty
 }
 END_TEST(testBinTokenReaderTesterEmptyList)
 
 // Reading `["foo", "bar"]`
 BEGIN_TEST(testBinTokenReaderTesterSimpleList)
 {
     js::Vector<uint8_t> contents(cx);
     readFull("jsapi-tests/binast/tokenizer/tester/test-trivial-list.binjs", contents);
-    Tokenizer tokenizer(cx, contents);
+    Tokenizer tokenizer(cx, nullptr, contents);
 
     {
         uint32_t length;
         Tokenizer::AutoList guard(tokenizer);
         CHECK(tokenizer.enterList(length, guard).isOk());
 
         CHECK(length == 2);
 
@@ -294,17 +294,17 @@ BEGIN_TEST(testBinTokenReaderTesterSimpl
 END_TEST(testBinTokenReaderTesterSimpleList)
 
 
 // Reading `[["foo", "bar"]]`
 BEGIN_TEST(testBinTokenReaderTesterNestedList)
 {
     js::Vector<uint8_t> contents(cx);
     readFull("jsapi-tests/binast/tokenizer/tester/test-nested-lists.binjs", contents);
-    Tokenizer tokenizer(cx, contents);
+    Tokenizer tokenizer(cx, nullptr, contents);
 
     {
         uint32_t outerLength;
         Tokenizer::AutoList outerGuard(tokenizer);
         CHECK(tokenizer.enterList(outerLength, outerGuard).isOk());
         CHECK_EQUAL(outerLength, (uint32_t)1);
 
         {