new file mode 100644
--- /dev/null
+++ b/js/src/frontend/BinSource.cpp
@@ -0,0 +1,2938 @@
+#include <mozilla/ArrayUtils.h>
+#include <mozilla/Casting.h>
+#include <mozilla/Maybe.h>
+#include <mozilla/Move.h>
+#include <mozilla/PodOperations.h>
+#include <mozilla/Vector.h>
+
+#include <frontend/BinSource.h>
+#include <frontend/Parser.h>
+#include <frontend/ParseNode.h>
+#include <frontend/BinTokenReaderTester.h>
+
+#include <vm/RegExpObject.h>
+
+using namespace mozilla;
+using NameBag = GCHashSet<JSString*>;
+using Names = GCVector<JSString*, 8>;
+using UsedNamePtr = js::frontend::UsedNameTracker::UsedNameMap::Ptr;
+
+namespace js {
+namespace frontend {
+
+using UniqueNode = mozilla::UniquePtr<ParseNode, ParseNodeDeleter>;
+using BinFields = BinTokenReaderTester::BinFields;
+
+// Copied from Parser.cpp
+
+template <typename Scope>
+static typename Scope::Data*
+NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings)
+{
+ size_t allocSize = Scope::sizeOfData(numBindings);
+ typename Scope::Data* bindings = static_cast<typename Scope::Data*>(alloc.alloc(allocSize));
+ if (!bindings) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ PodZero(bindings);
+ return bindings;
+}
+
+const std::string BINJS_SCOPE = "BINJS:Scope";
+const std::string BINJS_VAR_NAME = "BINJS:VarDeclaredNames";
+const std::string BINJS_LET_NAME = "BINJS:LetDeclaredNames";
+const std::string BINJS_CONST_NAME = "BINJS:ConstDeclaredNames";
+const std::string BINJS_CAPTURED_NAME = "BINJS:CapturedNames";
+const std::string BINJS_DIRECT_EVAL = "BINJS:HasDirectEval";
+
+
+Maybe<UniqueNode>
+BinASTParser::parse(const Vector<char>& data) {
+ return this->parse(data.begin(), data.end());
+}
+
+Maybe<UniqueNode>
+BinASTParser::parse(const char* start, const char* stop) {
+ BinTokenReaderTester reader(this->cx);
+ reader.init(start, start, stop, nullptr);
+
+ Directives directives(options().strictOption);
+ GlobalSharedContext globalsc(this->cx, ScopeKind::Global,
+ directives, options().extraWarningsOption);
+ BinParseContext globalpc(this->cx, this, &globalsc, /* newDirectives = */ nullptr);
+ if (!globalpc.init()) {
+ return Nothing();
+ }
+ ParseContext::VarScope varScope(this->cx, &globalpc, usedNames);
+ if (!varScope.init(&globalpc)) {
+ return Nothing();
+ }
+
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!this->parseProgram(&reader, result)) {
+ return Nothing();
+ }
+
+ if (!reader.uninit()) {
+ Unused << this->raiseError(&reader, "parse()");
+ return Nothing();
+ }
+
+ return Move(Some(Move(result)));
+}
+
+bool
+BinASTParser::parseProgram(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Program");
+ }
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "Program");
+ }
+
+ if (kind == BinKind::binjs_null) {
+ return true;
+ }
+ if (kind != BinKind::program) {
+ return this->raiseInvalidKind(reader, "Program", kind);
+ }
+
+ if (!this->parseBlockStatementAux(&sub, kind, fields, out)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::parseBlockStatement(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ // Already parsed.
+ return this->raiseAlreadyParsed(reader, "BlockStatement");
+ }
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "BlockStatement");
+ }
+
+ UniqueNode block(nullptr, this->nodeFree);
+ switch (kind) {
+ case BinKind::binjs_null:
+ return true;
+ case BinKind::block_statement:
+ if (!this->parseBlockStatementAux(&sub, kind, fields, block)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidKind(reader, "BlockStatement", kind);
+ }
+
+ out = Move(block);
+ return true;
+}
+
+bool
+BinASTParser::parseScope(BinTokenReaderTester* reader, ScopeData& out)
+{
+ if (out.isSome()) {
+ // Already parsed.
+ return this->raiseAlreadyParsed(reader, "Scope");
+ }
+
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "Scope");
+ }
+
+ if (kind == BinKind::binjs_null) {
+ return true;
+ }
+ if (kind != BinKind::binjs_scope) {
+ return this->raiseInvalidKind(reader, "Scope", kind);
+ }
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::has_direct_eval:
+ if (!this->readBool(&sub, out.hasDirectEval)) {
+ return false;
+ }
+ break;
+ case BinField::let_decl_names:
+ if (!this->parseStringList(&sub, &out.letNames)) {
+ return false;
+ }
+ break;
+ case BinField::const_decl_names:
+ if (!this->parseStringList(&sub, &out.constNames)) {
+ return false;
+ }
+ break;
+ case BinField::var_decl_names:
+ if (!this->parseStringList(&sub, &out.varNames)) {
+ return false;
+ }
+ break;
+ case BinField::captured_names:
+ if (!this->parseStringSet(&sub, &out.capturedNames)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "Scope", field);
+ }
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::parseBlockStatementAux(BinTokenReaderTester* reader,
+ const BinKind name,
+ const BinFields& fields,
+ UniqueNode& out)
+{
+ UniqueNode body(nullptr, this->nodeFree);
+ UniqueNode directives(nullptr, this->nodeFree); // Ignored
+ ScopeData scope(this->cx);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::binjs_scope:
+ if (!this->parseScope(reader, scope)) {
+ return false;
+ }
+ break;
+ case BinField::body:
+ if (!this->parseStatementList(reader, body)) {
+ return false;
+ }
+ break;
+ case BinField::directives:
+ if (!this->parseDirectiveList(reader, directives)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "BlockStatement", field);
+ }
+ }
+
+ // Check if all fields have been parsed.
+ if (!body || !scope.isSome()) {
+ return this->raiseMissingField(reader, "BlockStatement");
+ }
+
+ if (scope.hasLexNames()) {
+ if (!this->promoteToLexicalScope(body)) {
+ return false;
+ }
+
+ if (!this->storeLexicalScope(body, Move(scope))) {
+ return false;
+ }
+ }
+
+ out = Move(body);
+ return true;
+}
+
+bool
+BinASTParser::storeLexicalScope(UniqueNode& body, ScopeData&& scope) {
+ LexicalScope::Data* bindings = body->pn_u.scope.bindings;
+ bindings->length = 0;
+
+ BindingName* cursor = bindings->names;
+ for (auto& name: *scope.letNames.get()) {
+ JS::Rooted<JSAtom*> atom(cx, AtomizeString(this->cx, name));
+ if (!atom) {
+ return this->raiseOOM();
+ }
+ bool isCaptured = scope.capturedNames->has(name);
+ BindingName binding(atom, isCaptured);
+ PodCopy(cursor, &binding, 1);
+ cursor++;
+ bindings->length++; // Augment progressively in case we need to return early because of an error.
+ }
+ bindings->constStart = bindings->length;
+ for (auto& name: *scope.constNames.get()) {
+ JS::Rooted<JSAtom*> atom(cx, AtomizeString(this->cx, name));
+ if (!atom) {
+ return this->raiseOOM();
+ }
+ bool isCaptured = scope.capturedNames->has(name);
+ BindingName binding(atom, isCaptured);
+ PodCopy(cursor, &binding, 1);
+ cursor++;
+ bindings->length++;
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::promoteToLexicalScope(UniqueNode& node) {
+ if (node->isKind(PNK_LEXICALSCOPE)) {
+ return true;
+ }
+
+ js::UniquePtr<LexicalScope::Data> bindings(NewEmptyBindingData<LexicalScope>(this->cx, this->alloc, 0));
+ if (!bindings) {
+ return this->raiseOOM();
+ }
+ bindings->constStart = 0;
+ UniqueNode result(new_<LexicalScopeNode>(bindings.get(), node.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ Unused << node.release();
+ Unused << bindings.release();
+
+ node = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::parseStringSet(BinTokenReaderTester* reader, MutableHandle<mozilla::Maybe<NameBag>> out) {
+ if (out.get()) {
+ return this->raiseAlreadyParsed(reader, "{String}");
+ }
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+
+ NameBag result(this->cx);
+ if (!result.init()) {
+ return this->raiseOOM();
+ }
+
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "{String}");
+ }
+
+ for (uint32_t i = 0; i < length; ++i) {
+ RootedString string(this->cx);
+ if (!this->readString(&sub, &string)) {
+ return false;
+ }
+
+ if (!result.put(Move(string))) {
+ return this->raiseOOM();
+ }
+ }
+
+ out.set(Move(Some(Move(result))));
+ return true;
+}
+
+bool
+BinASTParser::parseStringList(BinTokenReaderTester* reader, MutableHandle<Maybe<Names>> out) {
+ if (out.get()) {
+ return this->raiseAlreadyParsed(reader, "[String]");
+ }
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+
+ Names result(this->cx);
+
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "[String]");
+ }
+
+ if (!result.reserve(length)) {
+ return this->raiseOOM();
+ }
+ for (uint32_t i = 0; i < length; ++i) {
+ RootedString string(this->cx);
+ if (!this->readString(&sub, &string)) {
+ return false;
+ }
+
+ if (!result.append(Move(string))) {
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(); // Checked in the call to `reserve`.
+ }
+ }
+
+ out.set(Move(Some(Move(result))));
+ return true;
+}
+
+bool
+BinASTParser::parseStatementList(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "[Statement]");
+ }
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+
+ UniqueNode result(new_<ListNode>(PNK_STATEMENTLIST, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "[Statement]");
+ }
+
+ for (uint32_t i = 0; i < length; ++i) {
+ UniqueNode statement(nullptr, this->nodeFree);
+ if (!this->parseStatement(&sub, statement)) {
+ return false;
+ }
+
+ result->append(statement.release()); // `result` knows how to deallocate `statement`.
+ }
+
+ result.swap(out);
+
+ return true;
+}
+
+bool
+BinASTParser::parseStatement(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Statement");
+ }
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "Statement");
+ }
+
+ switch (kind) {
+ case BinKind::binjs_null:
+ return true;
+ case BinKind::empty_statement: {
+ // For compatibility with the source parser, this is not a NOP but an empty statement.
+ UniqueNode result(new_<UnaryNode>(PNK_SEMI, JSOP_NOP, TokenPos(0, 0), nullptr), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ out = Move(result);
+ break;
+ }
+ case BinKind::block_statement: {
+ UniqueNode body(nullptr, this->nodeFree);
+ if (!this->parseBlockStatementAux(&sub, kind, fields, body)) {
+ return false;
+ }
+ if (body) {
+ if (!this->promoteToLexicalScope(body)) {
+ return false;
+ }
+ }
+ out = Move(body);
+ break;
+ }
+ case BinKind::expression_statement: {
+ UniqueNode body(nullptr, this->nodeFree);
+ if (!this->parseExpressionStatementAux(&sub, kind, fields, body)) {
+ return false;
+ }
+ out = Move(body);
+ break;
+ }
+ case BinKind::debugger_statement: {
+ UniqueNode result(new_<DebuggerStatement>(TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ out = Move(result);
+ break;
+ }
+ case BinKind::with_statement: {
+ UniqueNode body(nullptr, this->nodeFree);
+ UniqueNode expr(nullptr, this->nodeFree);
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::body:
+ if (!this->parseStatement(&sub, body)) {
+ return false;
+ }
+ break;
+ case BinField::object:
+ if (!this->parseExpression(&sub, expr)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "WithStatement", field);
+ }
+ }
+ if (!body || !expr) {
+ return this->raiseMissingField(reader, "WithStatement");
+ }
+
+ UniqueNode result(new_<BinaryNode>(PNK_WITH, JSOP_NOP, TokenPos(0, 0),
+ expr.get(), body.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ Unused << expr.release();
+ Unused << body.release();
+
+ out = Move(result);
+ break;
+ }
+ case BinKind::return_statement: {
+ UniqueNode arg(nullptr, this->nodeFree);
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::argument:
+ if (!this->parseExpression(&sub, arg)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "ReturnStatement", field);
+ }
+ }
+
+ // `arg` is optional, so we don't check whether it's `nullptr`.
+ UniqueNode result(new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, TokenPos(0, 0), arg.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << arg.release();
+ out = Move(result);
+ break;
+ }
+ case BinKind::labeled_statement: {
+ UniqueNode label(nullptr, this->nodeFree);
+ UniqueNode body(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::label:
+ if (!this->parsePattern(&sub, label)) {
+ return false;
+ }
+ if (!label || !label->isKind(PNK_NAME)) {
+ return this->raiseError(&sub, "Label MUST be an identifier");
+ }
+ break;
+ case BinField::body:
+ if (!this->parseStatement(&sub, body)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "LabeledStatement", field);
+ }
+ }
+
+ if (!label || !body) {
+ return this->raiseMissingField(&sub, "LabeledStatement");
+ }
+
+ UniqueNode result(new_<LabeledStatement>(label->name(), body.get(), 0), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << body.release();
+ out = Move(result);
+ break;
+ }
+ case BinKind::break_statement: MOZ_FALLTHROUGH;
+ case BinKind::continue_statement: {
+
+ UniqueNode label(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::label:
+ if (!this->parsePattern(&sub, label)) {
+ return false;
+ }
+ if (label && !label->isKind(PNK_NAME)) {
+ return this->raiseError(&sub, "ContinueStatement - Label MUST be an identifier");
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "ContinueStatement", field);
+ }
+ }
+
+ if (kind == BinKind::break_statement) {
+ UniqueNode result(new_<BreakStatement>(label ? label->name() : nullptr, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ out = Move(result);
+ } else {
+ UniqueNode result(new_<ContinueStatement>(label ? label->name() : nullptr, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ out = Move(result);
+ }
+
+ break;
+
+ }
+ case BinKind::if_statement: {
+
+ UniqueNode test(nullptr, this->nodeFree);
+ UniqueNode consequent(nullptr, this->nodeFree);
+ UniqueNode alternate(nullptr, this->nodeFree); // Optional
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::test: {
+ if (!this->parseExpression(&sub, test)) {
+ return false;
+ }
+ break;
+ }
+ case BinField::consequent: {
+ if (!this->parseStatement(&sub, consequent)) {
+ return false;
+ }
+ break;
+ }
+ case BinField::alternate: {
+ if (!this->parseStatement(&sub, alternate)) {
+ return false;
+ }
+ break;
+ }
+ default:
+ return this->raiseInvalidField(&sub, "IfStatement", field);
+ }
+ }
+
+ if (!test || !consequent) {
+ // Do not test `alternate`, since that value is optional.
+ return this->raiseMissingField(&sub, "IfStatement");
+ }
+
+ UniqueNode result(new_<TernaryNode>(PNK_IF, JSOP_NOP, test.get(), consequent.get(), alternate.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << test.release();
+ Unused << consequent.release();
+ Unused << alternate.release();
+
+ out = Move(result);
+ break;
+ }
+ case BinKind::switch_statement: {
+
+ UniqueNode discriminant(nullptr, this->nodeFree);
+ UniqueNode cases(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::discriminant: {
+ if (!this->parseExpression(&sub, discriminant)) {
+ return false;
+ }
+ break;
+ }
+ case BinField::cases: {
+ if (!this->parseSwitchCaseList(&sub, cases)) {
+ return false;
+ }
+ break;
+ }
+ default:
+ return this->raiseInvalidField(&sub, "SwitchStatement", field);
+ }
+ }
+
+ if (!discriminant || !cases) {
+ return this->raiseMissingField(&sub, "SwtichStatement");
+ }
+
+ UniqueNode result(new_<BinaryNode>(PNK_SWITCH, JSOP_NOP, TokenPos(0, 0), discriminant.get(), cases.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << discriminant.release();
+ Unused << cases.release();
+ out = Move(result);
+ break;
+ }
+ case BinKind::switch_case: {
+
+ UniqueNode test(nullptr, this->nodeFree);
+ UniqueNode consequent(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::test:
+ if (!this->parseExpression(&sub, test)) {
+ return false;
+ }
+ break;
+ case BinField::consequent:
+ if (!this->parseStatementList(&sub, consequent)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "SwitchCase", field);
+ }
+ }
+
+ UniqueNode result(new_<CaseClause>(test.get(), consequent.get(), 0), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << test.release();
+ Unused << consequent.release();
+ out = Move(result);
+ break;
+
+ }
+
+ case BinKind::throw_statement: {
+
+ UniqueNode arg(nullptr, this->nodeFree);
+ for (auto field: fields) {
+ if (field == BinField::argument) {
+ if (!this->parseExpression(&sub, arg)) {
+ return false;
+ }
+ } else {
+ return this->raiseInvalidField(&sub, "ThrowStatement", field);
+ }
+ }
+
+ if (!arg) {
+ return this->raiseMissingField(&sub, "ThrowStatement");
+ }
+
+ UniqueNode result(new_<UnaryNode>(PNK_THROW, JSOP_THROW, TokenPos(0, 0), arg.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << arg.release();
+ out = Move(result);
+ break;
+
+ }
+
+ case BinKind::try_statement: {
+
+ UniqueNode block(nullptr, this->nodeFree);
+ UniqueNode handler(nullptr, this->nodeFree);
+ UniqueNode finalizer(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::block:
+ if (!this->parseBlockStatement(&sub, block)) {
+ return false;
+ }
+ break;
+ case BinField::handler:
+ if (!this->parseCatchClause(&sub, handler)) {
+ return false;
+ }
+ break;
+ case BinField::finalizer:
+ if (!this->parseBlockStatement(&sub, finalizer)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "TryStatement", field);
+ }
+ }
+
+ if (!block || (!handler && !finalizer)) {
+ return this->raiseMissingField(&sub, "TryStatement");
+ }
+
+ if (!this->promoteToLexicalScope(block)) {
+ return false;
+ }
+
+ if (finalizer) {
+ if (!this->promoteToLexicalScope(finalizer)) {
+ return false;
+ }
+ }
+
+ UniqueNode result(new_<TernaryNode>(PNK_TRY, JSOP_NOP, block.get(), handler.get(), finalizer.get(), TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << block.release();
+ Unused << handler.release();
+ Unused << finalizer.release();
+
+ out = Move(result);
+ break;
+
+ }
+
+ case BinKind::while_statement: MOZ_FALLTHROUGH;
+ case BinKind::do_while_statement: {
+
+ UniqueNode test(nullptr, this->nodeFree);
+ UniqueNode body(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::test:
+ if (!this->parseExpression(&sub, test)) {
+ return false;
+ }
+ break;
+ case BinField::body:
+ if (!this->parseStatement(&sub, body)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "DoWhileStatement", field);
+ }
+ }
+
+ if (!test || !body) {
+ return this->raiseMissingField(&sub, "DoWhileStatement");
+ }
+
+ if (kind == BinKind::while_statement) {
+ UniqueNode result(new_<BinaryNode>(PNK_WHILE, JSOP_NOP, TokenPos(0, 0), test.get(), body.get()), this->nodeFree);
+
+ if (!result) {
+ return this->raiseOOM();
+ }
+ out = Move(result);
+ } else {
+ UniqueNode result(new_<BinaryNode>(PNK_DOWHILE, JSOP_NOP, TokenPos(0, 0), body.get(), test.get()), this->nodeFree);
+
+ if (!result) {
+ return this->raiseOOM();
+ }
+ out = Move(result);
+ }
+
+ Unused << test.release();
+ Unused << body.release();
+ break;
+ }
+
+ case BinKind::for_statement: {
+
+ UniqueNode init(nullptr, this->nodeFree); // Optional
+ UniqueNode test(nullptr, this->nodeFree); // Optional
+ UniqueNode update(nullptr, this->nodeFree); // Optional
+ ScopeData scope(this->cx); // Optional
+ UniqueNode body(nullptr, this->nodeFree); // Required
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::init:
+ if (!this->parseForHead(&sub, init)) {
+ return false;
+ }
+ break;
+ case BinField::test:
+ if (!this->parseExpression(&sub, test)) {
+ return false;
+ }
+ break;
+ case BinField::update:
+ if (!this->parseExpression(&sub, update)) {
+ return false;
+ }
+ break;
+ case BinField::binjs_scope:
+ if (!this->parseScope(&sub, scope)) {
+ return false;
+ }
+ break;
+ case BinField::body:
+ if (!this->parseStatement(&sub, body)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "ForStatement", field);
+ }
+ }
+
+ if (!body) {
+ return this->raiseMissingField(&sub, "ForStatement");
+ }
+
+ UniqueNode forHead(new_<TernaryNode>(PNK_FORHEAD, JSOP_NOP, init.get(), test.get(), update.get(), TokenPos(0, 0)), this->nodeFree);
+ if (!forHead) {
+ return this->raiseOOM();
+ }
+ Unused << init.release();
+ Unused << update.release();
+ Unused << test.release();
+
+ UniqueNode result(new_<BinaryNode>(PNK_FOR, JSOP_NOP, TokenPos(0, 0),
+ forHead.get(), body.get()), this->nodeFree);
+
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << forHead.release();
+ Unused << body.release();
+
+ out = Move(result);
+ break;
+ }
+
+ case BinKind::for_in_statement: {
+
+ UniqueNode left(nullptr, this->nodeFree);
+ UniqueNode right(nullptr, this->nodeFree);
+ UniqueNode body(nullptr, this->nodeFree);
+ ScopeData scope(this->cx); // Optional
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::left:
+ if (!this->parseForInHead(&sub, left)) {
+ return false;
+ }
+ break;
+ case BinField::right:
+ if (!this->parseExpression(&sub, right)) {
+ return false;
+ }
+ break;
+ case BinField::body:
+ if (!this->parseStatement(&sub, body)) {
+ return false;
+ }
+ break;
+ case BinField::binjs_scope:
+ if (!this->parseScope(&sub, scope)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "ForInStatement", field);
+ }
+ }
+
+ if (!left || !right || !body) {
+ return this->raiseMissingField(&sub, "ForInStatement");
+ }
+
+ if (!this->promoteToLexicalScope(body)) {
+ return false;
+ }
+
+
+ UniqueNode forHead(new_<TernaryNode>(PNK_FORIN, JSOP_NOP, left.get(), nullptr, right.get(), TokenPos(0, 0)), this->nodeFree);
+ if (!forHead) {
+ return this->raiseOOM();
+ }
+
+ Unused << left.release();
+ Unused << right.release();
+
+ UniqueNode result(new_<BinaryNode>(PNK_FOR, JSOP_ITER, TokenPos(0, 0),
+ forHead.get(), body.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << forHead.release();
+ Unused << body.release();
+
+ out = Move(result);
+ break;
+
+ }
+
+ case BinKind::function_declaration: {
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!this->parseFunctionAux(&sub, kind, fields, result)) {
+ return false;
+ }
+
+ out = Move(result);
+ break;
+
+ }
+ case BinKind::variable_declaration:
+ if (!this->parseVariableDeclarationAux(&sub, kind, fields, out)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidKind(&sub, "Statement", kind);
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::parseForHead(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "ForHead");
+ }
+
+ // This can be either a VarDecl or an Expression.
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+ BinKind kind;
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "ForStatement");
+ }
+
+ if (kind == BinKind::binjs_null) {
+ return true;
+ } else if (kind == BinKind::variable_declaration) {
+ return this->parseVariableDeclarationAux(&sub, kind, fields, out);
+ } else /* Parse as expression */ {
+ return this->parseExpressionAux(&sub, kind, fields, out);
+ }
+}
+
+bool
+BinASTParser::parseForInHead(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "ForInHead");
+ }
+
+ // This can be either a VarDecl or a Pattern.
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+ BinKind kind;
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "ForInHead");
+ }
+ if (kind == BinKind::binjs_null) {
+ return true;
+ } else if (kind == BinKind::variable_declaration) {
+ return this->parseVariableDeclarationAux(&sub, kind, fields, out);
+ } else /* Parse as pattern */ {
+ return this->parsePatternAux(&sub, kind, fields, out);
+ }
+}
+
+bool
+BinASTParser::parseFunctionAux(BinTokenReaderTester* reader, const BinKind kind, const BinFields& fields, UniqueNode& out) {
+ MOZ_ASSERT(kind == BinKind::function_declaration || kind == BinKind::function_expression || kind == BinKind::object_method);
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Function");
+ }
+
+ UniqueNode id(nullptr, this->nodeFree);
+ UniqueNode params(nullptr, this->nodeFree);
+ UniqueNode body(nullptr, this->nodeFree);
+ ScopeData scope(this->cx);
+
+ UniqueNode key(nullptr, this->nodeFree); // Methods only
+ Maybe<bool> computed; // Methods only
+ Maybe<std::string> method_kind; // Methods only
+
+ // Allocate the function before walking down the tree.
+ RootedFunction fun(this->cx,
+ NewFunctionWithProto(this->cx,
+ /*native*/nullptr,
+ /*nargs ?*/0,
+ /*flags */ JSFunction::INTERPRETED_NORMAL,
+ /*enclosing env*/nullptr,
+ /*name*/ nullptr, // Will be known later
+ /*proto*/ nullptr,
+ /*alloc*/gc::AllocKind::FUNCTION,
+ TenuredObject
+ ));
+ if (!fun) {
+ return this->raiseOOM();
+ }
+
+ FunctionBox* funbox = alloc.new_<FunctionBox>(this->cx,
+ this->alloc,
+ this->traceListHead,
+ fun,
+ /*toStringStart*/0,
+ /*Directives*/Directives(this->parseContext),
+ /*extraWarning*/false,
+ GeneratorKind::NotGenerator,
+ FunctionAsyncKind::SyncFunction
+ );
+ if (!funbox) {
+ return this->raiseOOM();
+ }
+ this->traceListHead = funbox;
+
+ FunctionSyntaxKind syntax;
+ switch (kind) {
+ case BinKind::function_declaration:
+ syntax = Statement;
+ break;
+ case BinKind::function_expression:
+ syntax = Expression;
+ break;
+ case BinKind::object_method:
+ // FIXME: At this stage of parsing, we do not know about `get`/`set`.
+ syntax = Method;
+ break;
+ default:
+ MOZ_CRASH("Invalid FunctionSyntaxKind");
+ }
+ funbox->initWithEnclosingParseContext(parseContext, syntax);
+
+
+ HandlePropertyName dotThis = this->cx->names().dotThis;
+ const bool declareThis = this->hasUsedName(dotThis) || funbox->bindingsAccessedDynamically() || funbox->isDerivedClassConstructor();
+
+ if (declareThis) {
+ ParseContext::Scope& funScope = this->parseContext->functionScope();
+ ParseContext::Scope::AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis);
+ MOZ_ASSERT(!p);
+ if (!funScope.addDeclaredName(this->parseContext, p, dotThis, DeclarationKind::Var,
+ DeclaredNameInfo::npos))
+ {
+ return false;
+ }
+ funbox->setHasThisBinding();
+ }
+
+ // Push a new ParseContext.
+ BinParseContext funpc(this->cx, this, funbox, /*newDirectives*/ nullptr);
+ if (!funpc.init())
+ return false;
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::id:
+ if (!this->parsePattern(reader, id)) {
+ return false;
+ }
+ if (id && !id->isKind(PNK_NAME)) {
+ return this->raiseError(reader, "Function id MUST be an identifier");
+ }
+ break;
+ case BinField::params:
+ if (!this->parsePatternList(reader, params)) {
+ return false;
+ }
+ break;
+ case BinField::body:
+ if (!this->parseStatement(reader, body)) {
+ return false;
+ }
+ break;
+ case BinField::binjs_scope:
+ if (!this->parseScope(reader, scope)) {
+ return false;
+ }
+ break;
+ case BinField::key:
+ if (kind != BinKind::object_method) {
+ return this->raiseInvalidField(reader, "Functions other than ObjectMethod", field);
+ }
+ if (!this->parseExpression(reader, key)) {
+ return false;
+ }
+ break;
+ case BinField::computed:
+ if (kind != BinKind::object_method) {
+ return this->raiseInvalidField(reader, "Functions other than ObjectMethod", field);
+ }
+ if (!this->readBool(reader, computed)) {
+ return false;
+ }
+ break;
+ case BinField::kind:
+ if (kind != BinKind::object_method) {
+ return this->raiseInvalidField(reader, "Functions other than ObjectMethod", field);
+ }
+ if (!this->readString(reader, method_kind)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "Function", field);
+ }
+ }
+
+ if (!params || !body || !scope.isSome()) {
+ return this->raiseMissingField(reader, "Function");
+ }
+
+ if (kind == BinKind::function_declaration && !id) {
+ // The name is compulsory only for function declarations.
+ return this->raiseMissingField(reader, "FunctionDeclaration");
+ }
+
+ if (kind == BinKind::object_method) {
+ if (!key || !computed || !method_kind) {
+ return this->raiseMissingField(reader, "ObjectMethod");
+ }
+ }
+
+ if (id) {
+ fun->initAtom(id->pn_atom);
+ }
+
+ MOZ_ASSERT(params->isArity(PN_LIST));
+ params->setOp(JSOP_NOP);
+
+ if (!(body->isKind(PNK_LEXICALSCOPE) && body->pn_u.scope.body->isKind(PNK_STATEMENTLIST))) {
+ // Promote to lexical scope + statement list.
+ if (!body->isKind(PNK_STATEMENTLIST)) {
+ UniqueNode list(new_<ListNode>(PNK_STATEMENTLIST, JSOP_NOP, body.get()), this->nodeFree);
+ if (!list) {
+ return this->raiseOOM();
+ }
+
+ Unused << body.release(); // Adopted by `list`.
+ body = Move(list);
+ }
+
+ // Promote to lexical scope.
+ if (!this->promoteToLexicalScope(body)) {
+ return false;
+ }
+ }
+ MOZ_ASSERT(body->getKind() == PNK_LEXICALSCOPE);
+ params->append(body.release());
+
+ const JSOp op =
+ kind == BinKind::object_method
+ ? JSOP_LAMBDA
+ : JSOP_NOP;
+ UniqueNode function(new_<CodeNode>(PNK_FUNCTION, op, TokenPos(0, 0)), this->nodeFree);
+ if (!function) {
+ return this->raiseOOM();
+ }
+
+ function->pn_funbox = funbox;
+ function->pn_body = params.release();
+
+ UniqueNode result(nullptr, this->nodeFree);
+ if (kind == BinKind::object_method) {
+ JSOp op;
+ if (*method_kind == "init") {
+ op = JSOP_INITPROP;
+ } else if (*method_kind == "get") {
+ op = JSOP_INITPROP_GETTER;
+ } else if (*method_kind == "set") {
+ op = JSOP_INITPROP_SETTER;
+ } else {
+ return this->raiseInvalidEnum(reader, "ObjectMethod", *method_kind);
+ }
+ result = UniqueNode(this->new_<BinaryNode>(PNK_COLON, op, key.get(), function.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << key.release();
+ Unused << function.release();
+ } else {
+ result = Move(function);
+ }
+
+ out = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::parseVariableDeclarationAux(BinTokenReaderTester* reader, const BinKind kind, const BinFields& fields, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "VariableDeclaration");
+ }
+
+ if (kind == BinKind::binjs_null) {
+ return true;
+ } else if (kind != BinKind::variable_declaration) {
+ return this->raiseInvalidKind(reader, "VariableDeclaration", kind);
+ }
+
+ ParseNodeKind pnk = PNK_LIMIT;
+ JSOp op = JSOP_NOP;
+ UniqueNode result(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::kind: {
+ Maybe<std::string> kindName;
+ if (!this->readString(reader, kindName)) {
+ return false;
+ }
+ if (kindName.isNothing()) {
+ return this->raiseMissingField(reader, "VariableDeclaration");
+ }
+
+ if (*kindName == "let") {
+ pnk = PNK_LET;
+ op = JSOP_DEFLET;
+ } else if (*kindName == "var") {
+ pnk = PNK_VAR;
+ op = JSOP_DEFVAR;
+ } else if (*kindName == "const") {
+ pnk = PNK_CONST;
+ op = JSOP_DEFCONST;
+ } else {
+ return this->raiseInvalidEnum(reader, "VariableDeclaration", *kindName);
+ }
+ break;
+ }
+ case BinField::declarations: {
+ if (result) {
+ // Already parsed.
+ return this->raiseAlreadyParsed(reader, "VariableDeclaration");
+ }
+
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "VariableDeclaration");
+ }
+
+ if (length == 0) {
+ return this->raiseEmpty(reader, "VariableDeclaration");
+ }
+
+ UniqueNode root(new_<ListNode>(PNK_NOP /*Placeholder*/, JSOP_NOP/*Placeholder*/, TokenPos(0, 0)), this->nodeFree);
+ if (!root) {
+ return this->raiseOOM();
+ }
+
+ UniqueNode first(nullptr, this->nodeFree);
+ if (!this->parseVariableDeclarator(&sub, first)) {
+ return this->raiseOOM();
+ }
+ root->initList(first.release());
+
+
+ for (uint32_t i = 1; i < length; ++i) {
+ UniqueNode current(nullptr, this->nodeFree);
+ if (!this->parseVariableDeclarator(&sub, current)) {
+ return false;
+ }
+ if (!current) {
+ return this->raiseMissingField(&sub, "VariableDeclaration");
+ }
+ root->append(current.release());
+ }
+
+ result = Move(root);
+ break;
+ }
+ default:
+ return this->raiseInvalidField(reader, "VariableDeclaration", field);
+ }
+ }
+
+ if (!result || pnk == PNK_LIMIT) {
+ return this->raiseMissingField(reader, "VariableDeclaration");
+ }
+
+ result->setKind(pnk);
+ result->setOp(op);
+
+ MOZ_ASSERT(!result->isKind(PNK_NOP));
+ out = Move(result);
+
+ return true;
+}
+
+
+bool
+BinASTParser::parseExpression(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Expression");
+ }
+
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+ BinKind kind;
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "Expression");
+ }
+
+ return this->parseExpressionAux(&sub, kind, fields, out);
+}
+
+bool
+BinASTParser::parseExpressionStatementAux(BinTokenReaderTester* reader, const BinKind kind, const BinFields& fields, UniqueNode& out) {
+ MOZ_ASSERT(kind == BinKind::expression_statement);
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "ExpressionStatement");
+ }
+
+ UniqueNode result(nullptr, this->nodeFree);
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::expression:
+ if (!this->parseExpression(reader, result)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "ExpressionStatement", field);
+ }
+ }
+
+ if (!result) {
+ return this->raiseMissingField(reader, "ExpressionStatement");
+ }
+
+ out = UniqueNode(new_<UnaryNode>(PNK_SEMI, JSOP_NOP, TokenPos(0, 0), result.get()), this->nodeFree);
+
+ if (!out) {
+ return this->raiseOOM();
+ }
+ Unused << result.release(); // Now part of `out`.
+
+ return true;
+}
+
+bool
+BinASTParser::parseVariableDeclarator(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "VariableDeclarator");
+ }
+
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "VariableDeclarator");
+ }
+
+ if (kind != BinKind::variable_declarator) {
+ return this->raiseInvalidKind(reader, "VariableDeclarator", kind);
+ }
+
+ UniqueNode id(nullptr, this->nodeFree);
+ UniqueNode init(nullptr, this->nodeFree); // Optional.
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::id:
+ if (!this->parsePattern(&sub, id)) {
+ return false;
+ }
+ break;
+ case BinField::init:
+ if (!this->parseExpression(&sub, init)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "VariableDeclarator", field);
+ }
+ }
+
+ if (!id) {
+ return this->raiseMissingField(reader, "VariableDeclarator");
+ }
+
+ UniqueNode result(nullptr, this->nodeFree);
+
+ // FIXME: Documentation in ParseNode is clearly obsolete.
+ if (id->isKind(PNK_NAME)) {
+ // var foo [= bar]
+ RootedAtom atom(cx, id->pn_atom);
+ result = UniqueNode(new_<NameNode>(PNK_NAME, JSOP_NOP, atom, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ if (init) {
+ result->pn_expr = init.release();
+ }
+ } else {
+ // var pattern = bar
+ if (!init) {
+ // Here, `init` is required.
+ return this->raiseMissingField(reader, "VariableDeclarator");
+ }
+ result = UniqueNode(new_<BinaryNode>(PNK_ASSIGN, JSOP_NOP, TokenPos(0, 0), nullptr, nullptr), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ result->pn_left = id.release();
+ result->pn_right = init.release();
+ }
+
+ out = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::parseExpressionOrElisionList(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "[Expression|null]");
+ }
+
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "[Expression|null]");
+ }
+
+ UniqueNode result(new_<ListNode>(PNK_ARRAY, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ for (uint32_t i = 0; i < length; ++i) {
+ UniqueNode expr(nullptr, this->nodeFree);
+ if (!this->parseExpression(&sub, expr)) {
+ return false;
+ }
+
+ if (!expr) {
+ expr = UniqueNode(new_<NullaryNode>(PNK_ELISION, TokenPos(0, 0)), this->nodeFree);
+ if (!expr) {
+ return this->raiseOOM();
+ }
+ }
+
+ result->append(expr.release());
+ }
+
+ out = Move(result);
+ return true;
+}
+
+
+bool
+BinASTParser::parseSwitchCaseList(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "[SwitchCase]");
+ }
+
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "[SwitchCase]");
+ }
+
+ UniqueNode result(new_<ListNode>(PNK_STATEMENTLIST, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ for (uint32_t i = 0; i < length; ++i) {
+ UniqueNode case_(nullptr, this->nodeFree);
+ if (!this->parseSwitchCase(&sub, case_)) {
+ return false;
+ }
+
+ if (!case_) {
+ return this->raiseEmpty(reader, "[SwitchCase]");
+ }
+
+ result->append(case_.release());
+ }
+
+ if (!this->promoteToLexicalScope(result)) {
+ return false;
+ }
+
+ out = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::parseExpressionAux(BinTokenReaderTester* reader, const BinKind kind, const BinFields& fields, UniqueNode& out) {
+ switch (kind) {
+ case BinKind::binjs_null:
+ return true;
+ case BinKind::identifier: {
+ Rooted<PropertyName*> id(this->cx);
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::name:
+ if (!this->readString(reader, &id)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "Identifier", field);
+ }
+ }
+
+ if (!id) {
+ return this->raiseMissingField(reader, "Identifier");
+ }
+
+ UniqueNode result(new_<NameNode>(PNK_NAME, JSOP_GETNAME, id, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ out = Move(result);
+ break;
+ }
+ case BinKind::boolean_literal: {
+ Maybe<bool> value;
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::value:
+ if (!this->readBool(reader, value)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "BooleanLiteral", field);
+ }
+ }
+
+ if (!value) {
+ return this->raiseMissingField(reader, "BooleanLiteral");
+ }
+
+ UniqueNode result(new_<BooleanLiteral>(*value, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ out = Move(result);
+ break;
+ }
+ case BinKind::directive_literal:
+ // A directive is not truly a literal, so this doesn't make sense
+ // here.
+ return this->raiseError(reader, "DirectiveLiteral (not truly a literal)");
+ case BinKind::null_literal: {
+ UniqueNode result(new_<NullLiteral>(TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ out = Move(result);
+ break;
+ }
+ case BinKind::numeric_literal: {
+ Maybe<double> value;
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::value:
+ if (!this->readNumber(reader, value)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "NumericLiteral", field);
+ }
+ }
+
+ if (!value) {
+ return this->raiseMissingField(reader, "NumericLiteral");
+ }
+
+ UniqueNode result(new_<NullaryNode>(PNK_NUMBER, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ result->initNumber(*value, DecimalPoint::HasDecimal); // HasDecimal is an arbitrary choise.
+ out = Move(result);
+ break;
+ }
+ case BinKind::regexp_literal: {
+ RootedAtom pattern(this->cx);
+ Maybe<std::string> flags;
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::pattern:
+ if (!this->readString(reader, &pattern)) {
+ return false;
+ }
+ break;
+ case BinField::flags:
+ if (!this->readString(reader, flags)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "RegExpLiteral", field);
+ }
+ }
+
+ if (!pattern || !flags) {
+ return this->raiseMissingField(reader, "RegExpLiteral");
+ }
+
+ RegExpFlag reflags = NoFlags;
+ for (char c: *flags) {
+ if (c == 'g' && !(reflags & GlobalFlag))
+ reflags = RegExpFlag(reflags | GlobalFlag);
+ else if (c == 'i' && !(reflags & IgnoreCaseFlag))
+ reflags = RegExpFlag(reflags | IgnoreCaseFlag);
+ else if (c == 'm' && !(reflags & MultilineFlag))
+ reflags = RegExpFlag(reflags | MultilineFlag);
+ else if (c == 'y' && !(reflags & StickyFlag))
+ reflags = RegExpFlag(reflags | StickyFlag);
+ else if (c == 'u' && !(reflags & UnicodeFlag))
+ reflags = RegExpFlag(reflags | UnicodeFlag);
+ else
+ return this->raiseInvalidEnum(reader, "RegExpLiteral", *flags);
+ }
+
+
+ Rooted<RegExpObject*> reobj(this->cx);
+ reobj = RegExpObject::create(this->cx,
+ pattern,
+ reflags,
+ /* options*/ nullptr,
+ /* tokenStream */ nullptr,
+ this->alloc,
+ TenuredObject);
+
+ if (!reobj) {
+ return this->raiseOOM();
+ }
+
+
+ ObjectBox* objbox = this->newObjectBox(reobj);
+ if (!objbox) {
+ return this->raiseOOM();
+ }
+ UniqueNode result(new_<RegExpLiteral>(objbox, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ // FIXME: I *think* that `objbox` is traced.
+ return this->raiseOOM();
+ }
+
+ out = Move(result);
+ break;
+ }
+ case BinKind::string_literal: {
+ RootedAtom value(this->cx);
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::value:
+ if (!this->readString(reader, &value)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "StringLiteral", field);
+ }
+ }
+
+ if (!value) {
+ return this->raiseMissingField(reader, "StringLiteral");
+ }
+
+ UniqueNode result(new_<NullaryNode>(PNK_STRING, JSOP_NOP, TokenPos(0, 0), value), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ out = Move(result);
+ break;
+ }
+ case BinKind::this_expression: {
+ UniqueNode thisName(nullptr, this->nodeFree);
+ if (this->parseContext->sc()->thisBinding() == ThisBinding::Function) {
+ thisName = UniqueNode(new_<NameNode>(PNK_NAME, JSOP_GETNAME, this->cx->names().dotThis, TokenPos(0, 0)), this->nodeFree);
+ if (!thisName) {
+ return this->raiseOOM();
+ }
+ }
+
+ UniqueNode result(new_<ThisLiteral>(TokenPos(0, 0), thisName.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+ Unused << thisName.release();
+
+ out = Move(result);
+ break;
+ }
+ case BinKind::array_expression: {
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!this->parseExpressionOrElisionList(reader, result)) {
+ return false;
+ }
+
+ result->setKind(PNK_ARRAY);
+ result->setOp(JSOP_NEWINIT);
+ out = Move(result);
+ break;
+ }
+ case BinKind::object_expression: {
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!this->parseObjectMemberList(reader, result)) {
+ return false;
+ }
+
+ bool first = true;
+ for (ParseNode* iter = result.get(); iter != nullptr; iter = iter->pn_next) {
+ if (!first) {
+ first = false;
+ MOZ_ASSERT(iter->isKind(PNK_COLON));
+ MOZ_ASSERT(iter->pn_left != nullptr);
+ MOZ_ASSERT(iter->pn_right != nullptr);
+ }
+ }
+
+ result->setKind(PNK_OBJECT);
+ result->setOp(JSOP_NEWINIT);
+ out = Move(result);
+ break;
+ }
+ case BinKind::function_expression: {
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!this->parseFunctionAux(reader, kind, fields, result)) {
+ return false;
+ }
+ result->setOp(JSOP_LAMBDA);
+ out = Move(result);
+ break;
+ }
+ case BinKind::unary_expression:
+ case BinKind::update_expression: {
+
+ UniqueNode expr(nullptr, this->nodeFree);
+ Maybe<std::string> operation;
+ Maybe<bool> prefix; // FIXME: Ignored for unary_expression?
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::operator_:
+ if (!this->readString(reader, operation)) {
+ return false;
+ }
+ break;
+ case BinField::prefix:
+ if (!this->readBool(reader, prefix)) {
+ return false;
+ }
+ break;
+ case BinField::argument:
+ if (!this->parseExpression(reader, expr)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "UpdateExpression", field);
+ }
+ }
+
+ if (!expr || operation.isNothing() || prefix.isNothing()) {
+ return this->raiseMissingField(reader, "UpdateExpression");
+ }
+
+ ParseNodeKind pnk = PNK_LIMIT;
+ JSOp op = JSOP_NOP;
+ if (kind == BinKind::unary_expression) {
+ if (*operation == "-") {
+ pnk = PNK_NEG;
+ op = JSOP_NEG;
+ } else if (*operation == "+") {
+ pnk = PNK_POS;
+ op = JSOP_POS;
+ } else if (*operation == "!") {
+ pnk = PNK_NOT;
+ op = JSOP_NOT;
+ } else if (*operation == "~") {
+ pnk = PNK_BITNOT;
+ op = JSOP_BITNOT;
+ } else if (*operation == "typeof") {
+ if (expr->isKind(PNK_NAME)) {
+ pnk = PNK_TYPEOFNAME;
+ } else {
+ pnk = PNK_TYPEOFEXPR;
+ }
+ } else if (*operation == "void") {
+ pnk = PNK_VOID;
+ op = JSOP_VOID;
+ } else if (*operation == "delete") {
+ switch (expr->getKind()) {
+ case PNK_NAME:
+ expr->setOp(JSOP_DELNAME);
+ pnk = PNK_DELETENAME;
+ break;
+ case PNK_DOT:
+ pnk = PNK_DELETEPROP;
+ break;
+ case PNK_ELEM:
+ pnk = PNK_DELETEELEM;
+ break;
+ default:
+ pnk = PNK_DELETEEXPR;
+ }
+ } else {
+ return this->raiseInvalidEnum(reader, "UnaryOperator", *operation);
+ }
+ } else if (kind == BinKind::update_expression) {
+ if (*operation == "++") {
+ if (*prefix) {
+ pnk = PNK_PREINCREMENT;
+ } else {
+ pnk = PNK_POSTINCREMENT;
+ }
+ } else if (*operation == "--") {
+ if (*prefix) {
+ pnk = PNK_PREDECREMENT;
+ } else {
+ pnk = PNK_POSTDECREMENT;
+ }
+ } else {
+ return this->raiseInvalidEnum(reader, "UpdateOperator", *operation);
+ }
+ }
+
+ UniqueNode result(new_<UnaryNode>(pnk, op, TokenPos(0, 0), expr.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << expr.release();
+ out = Move(result);
+ break;
+ }
+ case BinKind::binary_expression: MOZ_FALLTHROUGH;
+ case BinKind::logical_expression: {
+
+ UniqueNode left(nullptr, this->nodeFree);
+ UniqueNode right(nullptr, this->nodeFree);
+ Maybe<std::string> operation;
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::left:
+ if (!this->parseExpression(reader, left)) {
+ return false;
+ }
+ break;
+ case BinField::right:
+ if (!this->parseExpression(reader, right)) {
+ return false;
+ }
+ break;
+ case BinField::operator_:
+ if (!this->readString(reader, operation)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "LogicalExpression | BinaryExpression", field);
+ }
+ }
+
+ if (!left || !right || operation.isNothing()) {
+ return this->raiseMissingField(reader, "LogicalExpression | BinaryExpression");
+ }
+
+ // FIXME: Instead of std::string, we should use atoms and comparison
+ // between atom ptr.
+ // FIXME: We should probably turn associative operations into lists.
+ ParseNodeKind pnk = PNK_LIMIT;
+ JSOp op;
+ if (*operation == "==") {
+ pnk = PNK_EQ;
+ op = JSOP_EQ;
+ } else if (*operation == "!=") {
+ pnk = PNK_NE;
+ op = JSOP_NE;
+ } else if (*operation == "===") {
+ pnk = PNK_STRICTEQ;
+ op = JSOP_STRICTEQ;
+ } else if (*operation == "!==") {
+ pnk = PNK_STRICTNE;
+ op = JSOP_STRICTNE;
+ } else if (*operation == "<") {
+ pnk = PNK_LT;
+ op = JSOP_LT;
+ } else if (*operation == "<=") {
+ pnk = PNK_LE;
+ op = JSOP_LE;
+ } else if (*operation == ">") {
+ pnk = PNK_GT;
+ op = JSOP_GT;
+ } else if (*operation == ">=") {
+ pnk = PNK_GE;
+ op = JSOP_GE;
+ } else if (*operation == "<<") {
+ pnk = PNK_LSH;
+ op = JSOP_LSH;
+ } else if (*operation == ">>") {
+ pnk = PNK_RSH;
+ op = JSOP_RSH;
+ } else if (*operation == ">>>") {
+ pnk = PNK_URSH;
+ op = JSOP_URSH;
+ } else if (*operation == "+") {
+ pnk = PNK_ADD;
+ op = JSOP_ADD;
+ } else if (*operation == "-") {
+ pnk = PNK_SUB;
+ op = JSOP_SUB;
+ } else if (*operation == "*") {
+ pnk = PNK_STAR;
+ op = JSOP_MUL;
+ } else if (*operation == "/") {
+ pnk = PNK_DIV;
+ op = JSOP_DIV;
+ } else if (*operation == "%") {
+ pnk = PNK_MOD;
+ op = JSOP_MOD;
+ } else if (*operation == "|") {
+ pnk = PNK_BITOR;
+ op = JSOP_BITOR;
+ } else if (*operation == "^") {
+ pnk = PNK_BITXOR;
+ op = JSOP_BITXOR;
+ } else if (*operation == "&") {
+ pnk = PNK_BITAND;
+ op = JSOP_BITAND;
+ } else if (*operation == "in") {
+ pnk = PNK_IN;
+ op = JSOP_IN;
+ } else if (*operation == "instanceof") {
+ pnk = PNK_INSTANCEOF;
+ op = JSOP_INSTANCEOF;
+ } else if (*operation == "||") {
+ pnk = PNK_OR;
+ op = JSOP_OR;
+ } else if (*operation == "&&" ) {
+ pnk = PNK_AND;
+ op = JSOP_AND;
+ } else if (*operation == "**" ) {
+ pnk = PNK_POW;
+ op = JSOP_POW;
+ } else {
+ return this->raiseInvalidEnum(reader, "BinaryOperator | LogicalOperator", *operation);
+ }
+
+ if (left->isKind(pnk) && pnk != PNK_POW /*Not associative*/) {
+ // Regroup associative operations into lists.
+ left->append(right.release());
+ out = Move(left);
+ } else {
+ UniqueNode list(new_<ListNode>(pnk, JSOP_NOP, TokenPos(0, 0)), this->nodeFree);
+ if (!list) {
+ return this->raiseOOM();
+ }
+ list->makeEmpty();
+ list->append(left.release());
+ list->append(right.release());
+ out = Move(list);
+ }
+
+ break;
+ }
+ case BinKind::assignment_expression: {
+ UniqueNode left(nullptr, this->nodeFree);
+ UniqueNode right(nullptr, this->nodeFree);
+ Maybe<std::string> operation;
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::left:
+ if (!this->parseExpression(reader, left)) {
+ return false;
+ }
+ break;
+ case BinField::right:
+ if (!this->parseExpression(reader, right)) {
+ return false;
+ }
+ break;
+ case BinField::operator_:
+ if (!this->readString(reader, operation)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "AssignmentExpression", field);
+ }
+ }
+
+ if (!left || !right || operation.isNothing()) {
+ return this->raiseMissingField(reader, "AssignmentExpression");
+ }
+
+ // FIXME: Instead of std::string, we should use atoms and comparison
+ // between atom ptr.
+ // FIXME: We should probably turn associative operations into lists.
+ ParseNodeKind pnk = PNK_LIMIT;
+ JSOp op = JSOP_NOP;
+ if (*operation == "=") {
+ pnk = PNK_ASSIGN;
+ } else if (*operation == "+=") {
+ pnk = PNK_ADDASSIGN;
+ op = JSOP_ADD;
+ } else if (*operation == "-=") {
+ pnk = PNK_SUBASSIGN;
+ op = JSOP_SUB;
+ } else if (*operation == "*=") {
+ pnk = PNK_MULASSIGN;
+ op = JSOP_MUL;
+ } else if (*operation == "/=") {
+ pnk = PNK_DIVASSIGN;
+ op = JSOP_DIV;
+ } else if (*operation == "%=") {
+ pnk = PNK_MODASSIGN;
+ op = JSOP_MOD;
+ } else if (*operation == "<<=") {
+ pnk = PNK_LSHASSIGN;
+ op = JSOP_LSH;
+ } else if (*operation == ">>=") {
+ pnk = PNK_RSHASSIGN;
+ op = JSOP_RSH;
+ } else if (*operation == ">>>=") {
+ pnk = PNK_URSHASSIGN;
+ op = JSOP_URSH;
+ } else if (*operation == "|=") {
+ pnk = PNK_BITORASSIGN;
+ op = JSOP_BITOR;
+ } else if (*operation == "^=") {
+ pnk = PNK_BITXORASSIGN;
+ op = JSOP_BITXOR;
+ } else if (*operation == "&=") {
+ pnk = PNK_BITANDASSIGN;
+ op = JSOP_BITAND;
+ } else {
+ return this->raiseInvalidEnum(reader, "AssignmentOperator", *operation);
+ }
+
+ UniqueNode list(new_<BinaryNode>(pnk, JSOP_NOP, TokenPos(0, 0), left.get(), right.get()), this->nodeFree);
+ if (!list) {
+ return this->raiseOOM();
+ }
+ Unused << left.release();
+ Unused << right.release();
+
+ out = Move(list);
+ break;
+ }
+ case BinKind::member_expression: {
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!this->parseMemberExpressionAux(reader, kind, fields, result)) {
+ return false;
+ }
+ out = Move(result);
+ break;
+ }
+ case BinKind::conditional_expression: {
+ UniqueNode test(nullptr, this->nodeFree);
+ UniqueNode alternate(nullptr, this->nodeFree);
+ UniqueNode consequent(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::test:
+ if (!this->parseExpression(reader, test)) {
+ return false;
+ }
+ break;
+ case BinField::alternate:
+ if (!this->parseExpression(reader, alternate)) {
+ return false;
+ }
+ break;
+ case BinField::consequent:
+ if (!this->parseExpression(reader, consequent)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "ConditionalExpression", field);
+ }
+ }
+
+ if (!test || !alternate || !consequent) {
+ return this->raiseMissingField(reader, "ConditionalExpression");
+ }
+
+ UniqueNode result(new_<ConditionalExpression>(test.get(), consequent.get(), alternate.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << test.release();
+ Unused << alternate.release();
+ Unused << consequent.release();
+
+ out = Move(result);
+ break;
+ }
+ case BinKind::call_expression: MOZ_FALLTHROUGH;
+ case BinKind::new_expression: {
+ UniqueNode callee(nullptr, this->nodeFree);
+ UniqueNode arguments(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::callee:
+ if (!this->parseExpression(reader, callee)) {
+ return false;
+ }
+ break;
+ case BinField::arguments:
+ if (!this->parseExpressionOrElisionList(reader, arguments)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "NewExpression", field);
+ }
+ }
+
+ if (!callee || !arguments) {
+ return this->raiseMissingField(reader, "NewExpression");
+ }
+
+ ParseNodeKind pnk =
+ kind == BinKind::call_expression
+ ? PNK_CALL
+ : PNK_NEW;
+ arguments->setKind(pnk);
+ arguments->prepend(callee.release());
+
+ out = Move(arguments);
+ break;
+ }
+ case BinKind::sequence_expression: {
+ UniqueNode sequence(nullptr, this->nodeFree);
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::expressions:
+ if (!this->parseExpressionOrElisionList(reader, sequence)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "SequenceExpression", field);
+ }
+ }
+
+ if (!sequence) {
+ this->raiseMissingField(reader, "SequenceExpression");
+ }
+
+ sequence->setKind(PNK_COMMA);
+ out = Move(sequence);
+ break;
+ }
+ default:
+ return this->raiseInvalidKind(reader, "Expression", kind);
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::parseMemberExpressionAux(BinTokenReaderTester* reader, const BinKind kind, const BinFields& fields, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "MemberExpression");
+ }
+
+ UniqueNode object(nullptr, this->nodeFree);
+ UniqueNode property(nullptr, this->nodeFree);
+ Maybe<bool> isComputed;
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::object:
+ if (!this->parseExpression(reader, object)) {
+ return false;
+ }
+ break;
+ case BinField::property:
+ if (!this->parseExpression(reader, property)) {
+ return false;
+ }
+ break;
+ case BinField::computed:
+ if (!this->readBool(reader, isComputed)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "MemberExpression", field);
+ }
+ }
+
+ if (!object || !property || !isComputed) {
+ return this->raiseMissingField(reader, "MemberExpression");
+ }
+
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!*isComputed) {
+ if (!property->isKind(PNK_NAME)) {
+ return this->raiseError(reader, "MemberExpression (computed implies name)");
+ }
+ PropertyName* name = property->pn_atom->asPropertyName();
+ result = UniqueNode(new_<PropertyAccess>(object.get(), name, 0, 0), this->nodeFree);
+ } else {
+ result = UniqueNode(new_<PropertyByValue>(object.get(), property.get(), 0, 0), this->nodeFree);
+ }
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << object.release();
+ Unused << property.release();
+
+ out = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::parseDirective(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Directive");
+ }
+
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "Directive");
+ }
+
+ if (kind != BinKind::directive) {
+ return this->raiseInvalidKind(reader, "Directive", kind);
+ }
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::value:
+ if (!this->parseDirectiveLiteral(&sub, out)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "Directive", field);
+ }
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::parseDirectiveLiteral(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "DirectiveLiteral");
+ }
+
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "DirectiveLiteral");
+ }
+
+ if (kind != BinKind::directive_literal) {
+ return this->raiseInvalidKind(reader, "DirectiveLiteral", kind);
+ }
+
+ RootedAtom value(this->cx);
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::value:
+ if (!this->readString(&sub, &value)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "DirectiveLiteral", field);
+ }
+ }
+
+ if (!value) {
+ return this->raiseMissingField(reader, "DirectiveLiteral");
+ }
+
+ UniqueNode result(new_<NullaryNode>(PNK_STRING, JSOP_NOP, TokenPos(0, 0), value), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ out = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::parseDirectiveList(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "[Directive]");
+ }
+
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "[Directive]");
+ }
+
+ for (uint32_t i = 0; i < length; ++i) {
+ UniqueNode directive(nullptr, this->nodeFree); // Ignored
+ if (!this->parseDirective(&sub, directive)) {
+ return false;
+ }
+ if (!directive) {
+ return this->raiseEmpty(&sub, "[Directive]");
+ }
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::parseSwitchCase(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "SwitchCase");
+ }
+
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "SwitchCase");
+ }
+
+ if (kind != BinKind::switch_case) {
+ return this->raiseInvalidKind(reader, "SwitchCase", kind);
+ }
+
+ UniqueNode test(nullptr, this->nodeFree); // Optional.
+ UniqueNode statements(nullptr, this->nodeFree); // Required.
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::test:
+ if (!this->parseExpression(&sub, test)) {
+ return false;
+ }
+ break;
+ case BinField::consequent:
+ if (!this->parseStatementList(&sub, statements)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "SwitchCase", field);
+ }
+ }
+
+ if (!statements) {
+ return this->raiseMissingField(&sub, "SwitchCase");
+ }
+ MOZ_ASSERT(statements->isKind(PNK_STATEMENTLIST));
+
+ UniqueNode result(nullptr, this->nodeFree);
+
+ result = UniqueNode(new_<CaseClause>(test.get(), statements.get(), 0), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << test.release();
+ Unused << statements.release();
+
+ out = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::parseCatchClause(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "CatchClause");
+ }
+
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "CatchClause");
+ }
+
+ if (kind == BinKind::binjs_null) {
+ return true;
+ }
+ if (kind != BinKind::catch_clause) {
+ return this->raiseInvalidKind(reader, "CatchClause", kind);
+ }
+
+ UniqueNode param(nullptr, this->nodeFree);
+ UniqueNode body(nullptr, this->nodeFree);
+ ScopeData scope(this->cx); // Optional
+
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::param:
+ if (!this->parsePattern(&sub, param)) {
+ return false;
+ }
+ break;
+ case BinField::body:
+ if (!this->parseBlockStatement(&sub, body)) {
+ return false;
+ }
+ break;
+ case BinField::binjs_scope:
+ if (!this->parseScope(&sub, scope)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "CatchClause", field);
+ }
+ }
+
+ if (!param || !body) {
+ return this->raiseMissingField(&sub, "CatchClause");
+ }
+
+ if (!this->promoteToLexicalScope(body)) {
+ return false;
+ }
+
+ UniqueNode catchClause(new_<TernaryNode>(PNK_CATCH, JSOP_NOP, param.get(), nullptr, body.get()), this->nodeFree);
+ if (!catchClause) {
+ return this->raiseOOM();
+ }
+
+ Unused << param.release();
+ Unused << body.release();
+
+ if (!this->promoteToLexicalScope(catchClause)) {
+ return false;
+ }
+
+ if (scope.isSome() && scope.hasLexNames()) {
+ if (!this->storeLexicalScope(catchClause, Move(scope))) {
+ return false;
+ }
+ }
+
+ UniqueNode catchList(new_<ListNode>(PNK_CATCHLIST, TokenPos(0, 0)), this->nodeFree);
+ if (!catchList) {
+ return this->raiseOOM();
+ }
+
+ catchList->append(catchClause.release());
+
+ out = Move(catchList);
+ return true;
+}
+
+bool
+BinASTParser::parsePatternList(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "[Pattern]");
+ }
+
+ UniqueNode result(this->new_<ListNode>(PNK_PARAMSBODY,
+ TokenPos(0, 0)), this->nodeFree);
+
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "[Pattern]");
+ }
+
+ for (uint32_t i = 0; i < length; ++i) {
+ UniqueNode pattern(nullptr, this->nodeFree);
+ if (!this->parsePattern(&sub, pattern)) {
+ return false;
+ }
+
+ if (!pattern) {
+ return this->raiseEmpty(&sub, "[Pattern]");
+ }
+
+ result->append(pattern.release());
+ }
+
+ out = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::parsePattern(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Pattern");
+ }
+
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "Pattern");
+ }
+
+ return this->parsePatternAux(&sub, kind, fields, out);
+}
+
+bool
+BinASTParser::parsePatternAux(BinTokenReaderTester* reader, const BinKind kind, const BinFields& fields, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Pattern | null");
+ }
+
+ switch (kind) {
+ case BinKind::binjs_null:
+ return true;
+ case BinKind::member_expression: {
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!this->parseMemberExpressionAux(reader, kind, fields, result)) {
+ return false;
+ }
+ out = Move(result);
+ break;
+ }
+ case BinKind::identifier: {
+ RootedAtom id(cx);
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::name:
+ if (!this->readString(reader, &id)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(reader, "Identifier as Pattern", field);
+ }
+ }
+
+ if (!id) {
+ return this->raiseMissingField(reader, "Identifier as Pattern");
+ }
+
+ UniqueNode result(new_<NameNode>(PNK_NAME, JSOP_GETNAME, id, TokenPos(0, 0)), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ out = Move(result);
+ break;
+ }
+ default:
+ return this->raiseInvalidKind(reader, "Pattern | null", kind);
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::parseObjectMember(BinTokenReaderTester* reader, UniqueNode& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "ObjectMember");
+ }
+
+ BinKind kind;
+ BinFields fields(this->cx);
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->taggedTuple(kind, fields, &sub)) {
+ return this->raiseTokenError(reader, "ObjectMember");
+ }
+
+ switch (kind) {
+ case BinKind::object_property: {
+ UniqueNode key(nullptr, this->nodeFree);
+ UniqueNode value(nullptr, this->nodeFree);
+ Maybe<bool> computed;
+ for (auto field: fields) {
+ switch (field) {
+ case BinField::key:
+ if (!this->parseExpression(&sub, key)) {
+ return false;
+ }
+ if (key) {
+ if (key->isKind(PNK_NAME)) {
+ UniqueNode name(new_<NullaryNode>(PNK_OBJECT_PROPERTY_NAME, JSOP_NOP, key->pn_pos, key->pn_atom), this->nodeFree);
+ key->pn_atom = nullptr;
+ key = Move(name);
+ }
+ }
+ break;
+ case BinField::computed:
+ if (!this->readBool(&sub, computed)) {
+ return false;
+ }
+ break;
+ case BinField::value:
+ if (!this->parseExpression(&sub, value)) {
+ return false;
+ }
+ break;
+ default:
+ return this->raiseInvalidField(&sub, "ObjectMember", field);
+ }
+ }
+
+ if (!key || !computed || !value) {
+ return this->raiseMissingField(&sub, "ObjectMember");
+ }
+ if (!(key->isKind(PNK_NUMBER) || key->isKind(PNK_OBJECT_PROPERTY_NAME)
+ || key->isKind(PNK_STRING) || key->isKind(PNK_COMPUTED_NAME)
+ || key->isKind(PNK_NAME))) {
+ return this->raiseError(&sub, "ObjectMember key kind");
+ }
+
+ UniqueNode result(this->new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, key.get(), value.get()), this->nodeFree);
+ if (!result) {
+ return this->raiseOOM();
+ }
+
+ Unused << key.release();
+ Unused << value.release();
+
+ out = Move(result);
+ return true;
+ }
+ case BinKind::object_method: {
+ UniqueNode result(nullptr, this->nodeFree);
+ if (!this->parseFunctionAux(&sub, kind, fields, result)) {
+ return false;
+ }
+ if (!result) {
+ return this->raiseEmpty(&sub, "ObjectMethod");
+ }
+
+ MOZ_ASSERT(result->isKind(PNK_COLON));
+
+ out = Move(result);
+ return true;
+ }
+ default:
+ return this->raiseInvalidKind(&sub, "ObjectMember", kind);
+ }
+}
+
+bool
+BinASTParser::parseObjectMemberList(BinTokenReaderTester* reader, UniqueNode& out) {
+ uint32_t length;
+ BinTokenReaderTester sub(this->cx);
+
+ if (!reader->readList(length, sub)) {
+ return this->raiseTokenError(reader, "[ObjectMember]");
+ }
+
+ UniqueNode result(this->new_<ListNode>(PNK_NOP /*Placeholder*/, TokenPos(0, 0)), this->nodeFree);
+ for (uint32_t i = 0; i < length; ++i) {
+ UniqueNode keyValue(nullptr, this->nodeFree);
+ if (!this->parseObjectMember(&sub, keyValue)) {
+ return false;
+ }
+ if (!keyValue) {
+ return this->raiseEmpty(&sub, "[ObjectMember]");
+ }
+
+ result->append(keyValue.release());
+ }
+
+ out = Move(result);
+ return true;
+}
+
+bool
+BinASTParser::readString(BinTokenReaderTester* reader, MutableHandleString out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "String");
+ }
+
+ RootedAtom atom(this->cx);
+ if (!this->readString(reader, &atom)) {
+ return false;
+ }
+
+ out.set(atom);
+ return true;
+}
+
+bool
+BinASTParser::readString(BinTokenReaderTester* reader, MutableHandleAtom out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "String");
+ }
+
+ Maybe<std::string> string;
+ if (!this->readString(reader, string)) {
+ return false;
+ }
+
+ if (!string) {
+ return true;
+ }
+
+ RootedAtom atom(cx, Atomize(this->cx, string->data(), string->length()));
+ if (!atom) {
+ return this->raiseOOM();
+ }
+
+ out.set(Move(atom));
+ return true;
+}
+
+bool
+BinASTParser::readString(BinTokenReaderTester* reader, MutableHandle<PropertyName*> out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "String");
+ }
+
+ RootedAtom atom(cx);
+
+ if (!this->readString(reader, &atom)) {
+ return false;
+ }
+
+ if (!atom) {
+ out.set(nullptr);
+ } else {
+ out.set(atom->asPropertyName());
+ }
+ return true;
+}
+
+bool
+BinASTParser::readString(BinTokenReaderTester* reader, Maybe<std::string>& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "String");
+ }
+
+ if (!reader->readMaybeString(out)) {
+ return this->raiseTokenError(reader, "String");
+ }
+
+ return true;
+}
+
+bool
+BinASTParser::readNumber(BinTokenReaderTester* reader, Maybe<double>& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Number");
+ }
+
+ return reader->readMaybeF64(&out);
+}
+
+bool
+BinASTParser::readBool(BinTokenReaderTester* reader, Maybe<bool>& out) {
+ if (out) {
+ return this->raiseAlreadyParsed(reader, "Bool");
+ }
+
+ return reader->readMaybeBool(&out);
+}
+
+bool
+BinASTParser::raiseInvalidKind(BinTokenReaderTester* reader, const std::string& superKind, const BinKind kind) {
+ JS_ReportErrorASCII(cx, "BinAST parse error: in %s, invalid kind %d", superKind.c_str(), kind);
+ return this->raiseError(reader, "raiseInvalidKind");
+}
+
+bool
+BinASTParser::raiseInvalidField(BinTokenReaderTester* reader, const std::string& kind, const BinField field) {
+ JS_ReportErrorASCII(cx, "BinAST parse error: in %s, invalid field %d", kind.c_str(), field);
+ return this->raiseError(reader, "raiseInvalidField");
+}
+
+bool
+BinASTParser::raiseInvalidEnum(BinTokenReaderTester* reader, const std::string& kind, const std::string& value) {
+ JS_ReportErrorASCII(cx, "BinAST parse error: in %s, invalid enum value %s", kind.c_str(), value.c_str());
+ return this->raiseError(reader, "raiseInvalidEnum");
+}
+
+bool
+BinASTParser::raiseMissingField(BinTokenReaderTester* reader, const std::string& kind) {
+ JS_ReportErrorASCII(cx, "BinAST parse error: in %s, missing field", kind.c_str());
+ return this->raiseError(reader, "raiseMissingField");
+}
+
+bool
+BinASTParser::raiseTokenError(BinTokenReaderTester* reader, const std::string& kind) {
+ return this->raiseError(reader, "raiseTokenError");
+}
+
+bool
+BinASTParser::raiseAlreadyParsed(BinTokenReaderTester* reader, const std::string& kind) {
+ JS_ReportErrorASCII(cx, "BinAST parse error: in %s, already parsed", kind.c_str());
+ return this->raiseError(reader, "raiseAlreadyParsed");
+}
+
+bool
+BinASTParser::raiseEmpty(BinTokenReaderTester* reader, const std::string& kind) {
+ JS_ReportErrorASCII(cx, "BinAST parse error: in %s, empty data", kind.c_str());
+ return this->raiseError(reader, "raiseEmpty");
+}
+
+bool
+BinASTParser::raiseOOM() {
+ cx->recoverFromOutOfMemory();
+ MOZ_CRASH("Debugging OOM");
+ return false;
+}
+
+
+bool
+BinASTParser::raiseError(BinTokenReaderTester* reader, const std::string& method) {
+ TokenPos pos;
+ reader->latestToken(pos);
+ fprintf(stderr, "DEBUG: BinASTParser::raiseError() from %s at %u -> %u\n",
+ method.c_str(),
+ pos.begin,
+ pos.end);
+ MOZ_CRASH("Debugging...");
+ return false;
+}
+
+void
+BinASTParser::reportErrorNoOffset(unsigned errorNumber, ...) {
+ va_list args;
+ va_start(args, errorNumber);
+
+ ErrorMetadata metadata;
+ metadata.filename = this->getFilename();
+ metadata.lineNumber = 0;
+ metadata.columnNumber = 0; // FIXME: We should keep the tokenizer somewhere
+ ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
+
+ va_end(args);
+}
+
+bool
+BinASTParser::hasUsedName(HandlePropertyName name) {
+ if (UsedNamePtr p = this->usedNames.lookup(name)) {
+ return p->value().isUsedInScript(this->parseContext->scriptId());
+ }
+ return false;
+}
+
+void
+TraceBinParser(JSTracer* trc, AutoGCRooter* parser)
+{
+ static_cast<BinASTParser*>(parser)->trace(trc);
+}
+
+} // namespace frontend
+} // namespace js