Bug 1439855 - Multipart tokenizer, tokenized (WIP) - also, let's optimize variants draft
authorDavid Teller <dteller@mozilla.com>
Thu, 22 Mar 2018 15:46:02 +0100
changeset 771145 11ebb100a38e7f9232bd2b19536d9b43b30fb436
parent 770864 ee9e9eff9b5aae0d22ff63f911d75411c4030648
push id103577
push userdteller@mozilla.com
push dateThu, 22 Mar 2018 14:46:32 +0000
bugs1439855
milestone61.0a1
Bug 1439855 - Multipart tokenizer, tokenized (WIP) - also, let's optimize variants MozReview-Commit-ID: BoTUlGzapfH
js/src/frontend/BinSource-auto.cpp
js/src/frontend/BinSource.yaml
js/src/frontend/BinSourceRuntimeSupport.h
js/src/frontend/BinTokenReaderMultipart.cpp
js/src/frontend/BinTokenReaderMultipart.h
js/src/frontend/BinTokenReaderTester.cpp
js/src/frontend/BinTokenReaderTester.h
--- a/js/src/frontend/BinSource-auto.cpp
+++ b/js/src/frontend/BinSource-auto.cpp
@@ -2283,17 +2283,18 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceArrayExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ArrayExpression);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Elements }));
 
 
     MOZ_TRY_DECL(elements, parseListOfOptionalSpreadElementOrExpression());
 
-    auto result = elements;return result;
+    auto result = elements;
+    return result;
 }
 
 
 /*
  interface ArrowExpression : Node {
     bool isAsync;
     AssertedParameterScope? parameterScope;
     AssertedVarScope? bodyScope;
@@ -2363,17 +2364,18 @@ BinASTParser<Tok>::parseInterfaceAsserte
         parseContext_->sc()->setBindingsAccessedDynamically();
     }
 
     if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
         // In non-strict mode code, direct calls to eval can
         // add variables to the call object.
         parseContext_->functionBox()->setHasExtensibleScope();
     }
-    auto result = Ok();return result;
+    auto result = Ok();
+    return result;
 }
 
 
 /*
  interface AssertedParameterScope : Node {
     FrozenArray<IdentifierName> parameterNames;
     FrozenArray<IdentifierName> capturedNames;
     bool hasDirectEval;
@@ -2411,17 +2413,18 @@ BinASTParser<Tok>::parseInterfaceAsserte
         parseContext_->sc()->setBindingsAccessedDynamically();
     }
 
     if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
         // In non-strict mode code, direct calls to eval can
         // add variables to the call object.
         parseContext_->functionBox()->setHasExtensibleScope();
     }
-    auto result = Ok();return result;
+    auto result = Ok();
+    return result;
 }
 
 
 /*
  interface AssertedVarScope : Node {
     FrozenArray<IdentifierName> lexicallyDeclaredNames;
     FrozenArray<IdentifierName> varDeclaredNames;
     FrozenArray<IdentifierName> capturedNames;
@@ -2461,17 +2464,18 @@ BinASTParser<Tok>::parseInterfaceAsserte
         parseContext_->sc()->setBindingsAccessedDynamically();
     }
 
     if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
         // In non-strict mode code, direct calls to eval can
         // add variables to the call object.
         parseContext_->functionBox()->setHasExtensibleScope();
     }
-    auto result = Ok();return result;
+    auto result = Ok();
+    return result;
 }
 
 
 /*
  interface AssignmentExpression : Node {
     AssignmentTarget binding;
     Expression expression;
  }
@@ -2499,17 +2503,18 @@ BinASTParser<Tok>::parseInterfaceAssignm
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Binding, BinField::Expression }));
 
 
     MOZ_TRY_DECL(binding, parseAssignmentTarget());
 
 
     MOZ_TRY_DECL(expression, parseExpression());
 
-    TRY_DECL(result, factory_.newAssignment(ParseNodeKind::Assign, binding, expression));return result;
+    TRY_DECL(result, factory_.newAssignment(ParseNodeKind::Assign, binding, expression));
+    return result;
 }
 
 
 /*
  interface AssignmentTargetIdentifier : Node {
     Identifier name;
  }
 */
@@ -2535,17 +2540,18 @@ BinASTParser<Tok>::parseInterfaceAssignm
     MOZ_ASSERT(kind == BinKind::AssignmentTargetIdentifier);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name }));
 
     RootedAtom name((cx_));
     MOZ_TRY_VAR(name, tokenizer_->readAtom());
 
     if (!IsIdentifier(name))
         return raiseError("Invalid identifier");
-    TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));return result;
+    TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
+    return result;
 }
 
 
 /*
  interface AssignmentTargetPropertyIdentifier : Node {
     AssignmentTargetIdentifier binding;
     Expression? init;
  }
@@ -2784,17 +2790,18 @@ BinASTParser<Tok>::parseInterfaceBinaryE
         left->appendWithoutOrderAssumption(right);
         result = left;
     } else {
         TRY_DECL(list, factory_.newList(pnk, tokenizer_->pos(start)));
 
         list->appendWithoutOrderAssumption(left);
         list->appendWithoutOrderAssumption(right);
         result = list;
-    }return result;
+    }
+    return result;
 }
 
 
 /*
  interface BindingIdentifier : Node {
     Identifier name;
  }
 */
@@ -2820,17 +2827,18 @@ BinASTParser<Tok>::parseInterfaceBinding
     MOZ_ASSERT(kind == BinKind::BindingIdentifier);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name }));
 
     RootedAtom name((cx_));
     MOZ_TRY_VAR(name, tokenizer_->readAtom());
 
     if (!IsIdentifier(name))
         return raiseError("Invalid identifier");
-    TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));return result;
+    TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
+    return result;
 }
 
 
 /*
  interface BindingPropertyIdentifier : Node {
     BindingIdentifier binding;
     Expression? init;
  }
@@ -2950,17 +2958,18 @@ BinASTParser<Tok>::parseInterfaceBlock(c
 
     MOZ_TRY(parseOptionalAssertedBlockScope());
 
 
     MOZ_TRY_DECL(statements, parseListOfStatement());
 
     TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
     TRY_DECL(result, factory_.newLexicalScope(*bindings, statements));
-    fprintf(stderr, "Block: POP parse context\n");return result;
+    fprintf(stderr, "Block: POP parse context\n");
+    return result;
 }
 
 
 /*
  interface BreakStatement : Node {
     Label? label;
  }
 */
@@ -2998,17 +3007,18 @@ BinASTParser<Tok>::parseInterfaceBreakSt
             switch (validity.unwrapErr()) {
             case ParseContext::BreakStatementError::ToughBreak:
                 return raiseError(kind, "Not in a loop");
             case ParseContext::BreakStatementError::LabelNotFound:
                 return raiseError(kind, "Label not found");
             }
         }
     }
-    TRY_DECL(result, factory_.newBreakStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newBreakStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface CallExpression : Node {
     (Expression or Super) callee;
     Arguments arguments;
  }
@@ -3051,17 +3061,18 @@ BinASTParser<Tok>::parseInterfaceCallExp
                 return raiseMissingDirectEvalInAssertedScope();
 
             op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
         }
     }
     auto result = arguments;
     result->setKind(ParseNodeKind::Call);
     result->prepend(callee);
-    result->setOp(op);return result;
+    result->setOp(op);
+    return result;
 }
 
 
 /*
  interface CatchClause : Node {
     Binding binding;
     Block body;
  }
@@ -3099,17 +3110,18 @@ BinASTParser<Tok>::parseInterfaceCatchCl
     // Export implicit variables to the scope.
     // FIXME: Handle cases other than Name.
     MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
     auto ptr = currentScope.lookupDeclaredNameForAdd(binding->name());
     TRY(currentScope.addDeclaredName(parseContext_, ptr, binding->name(), DeclarationKind::Let, start));
 
     TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
     TRY_DECL(result, factory_.newLexicalScope(*bindings, body));
-    TRY(factory_.setupCatchScope(result, binding, body));return result;
+    TRY(factory_.setupCatchScope(result, binding, body));
+    return result;
 }
 
 
 /*
  interface ClassDeclaration : Node {
     BindingIdentifier name;
     Expression? super;
     FrozenArray<ClassElement> elements;
@@ -3269,17 +3281,18 @@ BinASTParser<Tok>::parseInterfaceCompoun
         break;
       case CompoundAssignmentOperator::BitXorAssign:
         pnk = ParseNodeKind::BitXorAssign;
         break;
       case CompoundAssignmentOperator::BitAndAssign:
         pnk = ParseNodeKind::BitAndAssign;
         break;
     }
-    TRY_DECL(result, factory_.newAssignment(pnk, binding, expression));return result;
+    TRY_DECL(result, factory_.newAssignment(pnk, binding, expression));
+    return result;
 }
 
 
 /*
  interface ComputedMemberAssignmentTarget : Node {
     (Expression or Super) object;
     Expression expression;
  }
@@ -3307,17 +3320,18 @@ BinASTParser<Tok>::parseInterfaceCompute
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Expression }));
 
 
     MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
 
     MOZ_TRY_DECL(expression, parseExpression());
 
-    TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));return result;
+    TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
+    return result;
 }
 
 
 /*
  interface ComputedMemberExpression : Node {
     (Expression or Super) object;
     Expression expression;
  }
@@ -3345,17 +3359,18 @@ BinASTParser<Tok>::parseInterfaceCompute
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Expression }));
 
 
     MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
 
     MOZ_TRY_DECL(expression, parseExpression());
 
-    TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));return result;
+    TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
+    return result;
 }
 
 
 /*
  interface ComputedPropertyName : Node {
     Expression expression;
  }
 */
@@ -3415,17 +3430,18 @@ BinASTParser<Tok>::parseInterfaceConditi
     MOZ_TRY_DECL(test, parseExpression());
 
 
     MOZ_TRY_DECL(consequent, parseExpression());
 
 
     MOZ_TRY_DECL(alternate, parseExpression());
 
-    TRY_DECL(result, factory_.newConditional(test, consequent, alternate));return result;
+    TRY_DECL(result, factory_.newConditional(test, consequent, alternate));
+    return result;
 }
 
 
 /*
  interface ContinueStatement : Node {
     Label? label;
  }
 */
@@ -3463,17 +3479,18 @@ BinASTParser<Tok>::parseInterfaceContinu
               case ParseContext::ContinueStatementError::NotInALoop:
                 return raiseError(kind, "Not in a loop");
               case ParseContext::ContinueStatementError::LabelNotFound:
                 return raiseError(kind, "Label not found");
             }
         }
     }
 
-    TRY_DECL(result, factory_.newContinueStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newContinueStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface DataProperty : Node {
     PropertyName name;
     Expression expression;
  }
@@ -3504,17 +3521,18 @@ BinASTParser<Tok>::parseInterfaceDataPro
     MOZ_TRY_DECL(name, parsePropertyName());
 
 
     MOZ_TRY_DECL(expression, parseExpression());
 
     if (!factory_.isUsableAsObjectPropertyName(name))
         return raiseError("DataProperty key kind");
 
-    TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, expression, AccessorType::None));return result;
+    TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, expression, AccessorType::None));
+    return result;
 }
 
 
 /*
  interface DebuggerStatement : Node {
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
@@ -3566,17 +3584,18 @@ BinASTParser<Tok>::parseInterfaceDirecti
 {
     MOZ_ASSERT(kind == BinKind::Directive);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::RawValue }));
 
     RootedAtom rawValue((cx_));
     MOZ_TRY_VAR(rawValue, tokenizer_->readAtom());
 
     TokenPos pos = tokenizer_->pos(start);
-    TRY_DECL(result, factory_.newStringLiteral(rawValue, pos));return result;
+    TRY_DECL(result, factory_.newStringLiteral(rawValue, pos));
+    return result;
 }
 
 
 /*
  interface DoWhileStatement : Node {
     Expression test;
     Statement body;
  }
@@ -3604,17 +3623,18 @@ BinASTParser<Tok>::parseInterfaceDoWhile
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Test, BinField::Body }));
     ParseContext::Statement stmt(parseContext_, StatementKind::DoLoop);
 
     MOZ_TRY_DECL(test, parseExpression());
 
 
     MOZ_TRY_DECL(body, parseStatement());
 
-    TRY_DECL(result, factory_.newDoWhileStatement(body, test, tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newDoWhileStatement(body, test, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface EmptyStatement : Node {
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
@@ -3634,17 +3654,18 @@ BinASTParser<Tok>::parseEmptyStatement()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEmptyStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EmptyStatement);
     MOZ_TRY(tokenizer_->checkFields0(kind, fields));
 
-    TRY_DECL(result, factory_.newEmptyStatement(tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newEmptyStatement(tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface Export : Node {
     (FunctionDeclaration or ClassDeclaration or VariableDeclaration) declaration;
  }
 */
@@ -3867,17 +3888,18 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceExpressionStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ExpressionStatement);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Expression }));
 
 
     MOZ_TRY_DECL(expression, parseExpression());
 
-    TRY_DECL(result, factory_.newExprStatement(expression, tokenizer_->offset()));return result;
+    TRY_DECL(result, factory_.newExprStatement(expression, tokenizer_->offset()));
+    return result;
 }
 
 
 /*
  interface ForInOfBinding : Node {
     VariableDeclarationKind kind;
     Binding binding;
  }
@@ -3913,17 +3935,18 @@ BinASTParser<Tok>::parseInterfaceForInOf
     // Restored by `kindGuard`.
     variableDeclarationKind = kind_;
     MOZ_TRY(checkBinding(binding->pn_atom->asPropertyName()));
     auto pnk =
         kind_ == VariableDeclarationKind::Let
             ? ParseNodeKind::Let
             : ParseNodeKind::Var;
     TRY_DECL(result, factory_.newDeclarationList(pnk, tokenizer_->pos(start)));
-    factory_.addList(result, binding);return result;
+    factory_.addList(result, binding);
+    return result;
 }
 
 
 /*
  interface ForInStatement : Node {
     (ForInOfBinding or AssignmentTarget) left;
     Expression right;
     Statement body;
@@ -3967,17 +3990,18 @@ BinASTParser<Tok>::parseInterfaceForInSt
     MOZ_TRY_DECL(body, parseStatement());
 
     TRY_DECL(forHead, factory_.newForInOrOfHead(ParseNodeKind::ForIn, left, right, tokenizer_->pos(start)));
     TRY_DECL(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));
 
     if (!scope.isEmpty()) {
         TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
         TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
-    }return result;
+    }
+    return result;
 }
 
 
 /*
  interface ForOfStatement : Node {
     (ForInOfBinding or AssignmentTarget) left;
     Expression right;
     Statement body;
@@ -4055,17 +4079,18 @@ BinASTParser<Tok>::parseInterfaceForStat
     MOZ_TRY_DECL(body, parseStatement());
 
     TRY_DECL(forHead, factory_.newForHead(init, test, update, tokenizer_->pos(start)));
     TRY_DECL(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));
 
     if (!scope.isEmpty()) {
         TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
         TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
-    }return result;
+    }
+    return result;
 }
 
 
 /*
  interface FormalParameters : Node {
     FrozenArray<Parameter> items;
     Binding? rest;
  }
@@ -4097,17 +4122,18 @@ BinASTParser<Tok>::parseInterfaceFormalP
 
 
     MOZ_TRY_DECL(rest, parseOptionalBinding());
 
     auto result = items;
     if (rest) {
         TRY_DECL(spread, factory_.newSpread(start, rest));
         factory_.addList(result, spread);
-    }return result;
+    }
+    return result;
 }
 
 
 /*
  interface FunctionBody : Node {
     FrozenArray<Directive> directives;
     FrozenArray<Statement> statements;
  }
@@ -4135,17 +4161,18 @@ BinASTParser<Tok>::parseInterfaceFunctio
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Directives, BinField::Statements }));
 
 
     MOZ_TRY_DECL(directives, parseListOfDirective());
 
 
     MOZ_TRY_DECL(statements, parseListOfStatement());
 
-    MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));return result;
+    MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
+    return result;
 }
 
 
 /*
  interface FunctionDeclaration : Node {
     bool isAsync;
     bool isGenerator;
     AssertedParameterScope? parameterScope;
@@ -4210,17 +4237,18 @@ BinASTParser<Tok>::parseInterfaceFunctio
 
     MOZ_TRY_DECL(params, parseFormalParameters());
 
 
     MOZ_TRY_DECL(body, parseFunctionBody());
 
     TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
     TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
-    MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));return result;
+    MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
+    return result;
 }
 
 
 /*
  interface FunctionExpression : Node {
     bool isAsync;
     bool isGenerator;
     AssertedParameterScope? parameterScope;
@@ -4285,17 +4313,18 @@ BinASTParser<Tok>::parseInterfaceFunctio
 
     MOZ_TRY_DECL(params, parseFormalParameters());
 
 
     MOZ_TRY_DECL(body, parseFunctionBody());
 
     TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
     TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
-    MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));return result;
+    MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
+    return result;
 }
 
 
 /*
  interface Getter : Node {
     AssertedVarScope? bodyScope;
     PropertyName name;
     FunctionBody body;
@@ -4351,17 +4380,18 @@ BinASTParser<Tok>::parseInterfaceIdentif
     MOZ_ASSERT(kind == BinKind::IdentifierExpression);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name }));
 
     RootedAtom name((cx_));
     MOZ_TRY_VAR(name, tokenizer_->readAtom());
 
     if (!IsIdentifier(name))
         return raiseError("Invalid identifier");
-    TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));return result;
+    TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
+    return result;
 }
 
 
 /*
  interface IfStatement : Node {
     Expression test;
     Statement consequent;
     Statement? alternate;
@@ -4393,17 +4423,18 @@ BinASTParser<Tok>::parseInterfaceIfState
     MOZ_TRY_DECL(test, parseExpression());
 
 
     MOZ_TRY_DECL(consequent, parseStatement());
 
 
     MOZ_TRY_DECL(alternate, parseOptionalStatement());
 
-    TRY_DECL(result, factory_.newIfStatement(start, test, consequent, alternate));return result;
+    TRY_DECL(result, factory_.newIfStatement(start, test, consequent, alternate));
+    return result;
 }
 
 
 /*
  interface Import : Node {
     string moduleSpecifier;
     BindingIdentifier? defaultBinding;
     FrozenArray<ImportSpecifier> namedImports;
@@ -4524,17 +4555,18 @@ BinASTParser<Tok>::parseInterfaceLabelle
     MOZ_TRY_VAR(label, tokenizer_->readAtom());
     if (!IsIdentifier(label))
         return raiseError("Invalid identifier");
     ParseContext::LabelStatement stmt(parseContext_, label);
 
 
     MOZ_TRY_DECL(body, parseStatement());
 
-    TRY_DECL(result, factory_.newLabeledStatement(label->asPropertyName(), body, start));return result;
+    TRY_DECL(result, factory_.newLabeledStatement(label->asPropertyName(), body, start));
+    return result;
 }
 
 
 /*
  interface LiteralBooleanExpression : Node {
     bool value;
  }
 */
@@ -4558,17 +4590,18 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceLiteralBooleanExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralBooleanExpression);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Value }));
 
 
     MOZ_TRY_DECL(value, tokenizer_->readBool());
 
-    TRY_DECL(result, factory_.newBooleanLiteral(value, tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newBooleanLiteral(value, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface LiteralInfinityExpression : Node {
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
@@ -4615,17 +4648,18 @@ BinASTParser<Tok>::parseLiteralNullExpre
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralNullExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralNullExpression);
     MOZ_TRY(tokenizer_->checkFields0(kind, fields));
 
-    TRY_DECL(result, factory_.newNullLiteral(tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newNullLiteral(tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface LiteralNumericExpression : Node {
     number value;
  }
 */
@@ -4649,17 +4683,18 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceLiteralNumericExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralNumericExpression);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Value }));
 
 
     MOZ_TRY_DECL(value, tokenizer_->readDouble());
 
-    TRY_DECL(result, factory_.newNumber(value, DecimalPoint::HasDecimal, tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newNumber(value, DecimalPoint::HasDecimal, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface LiteralPropertyName : Node {
     string value;
  }
 */
@@ -4688,17 +4723,18 @@ BinASTParser<Tok>::parseInterfaceLiteral
     RootedAtom value((cx_));
     MOZ_TRY_VAR(value, tokenizer_->readAtom());
 
     ParseNode* result;
     uint32_t index;
     if (value->isIndex(&index))
         TRY_VAR(result, factory_.newNumber(index, NoDecimal, TokenPos(start, tokenizer_->offset())));
     else
-        TRY_VAR(result, factory_.newObjectLiteralPropertyName(value, tokenizer_->pos(start)));return result;
+        TRY_VAR(result, factory_.newObjectLiteralPropertyName(value, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface LiteralRegExpExpression : Node {
     string pattern;
     string flags;
  }
@@ -4722,50 +4758,46 @@ BinASTParser<Tok>::parseLiteralRegExpExp
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralRegExpExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralRegExpExpression);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Pattern, BinField::Flags }));
 
     RootedAtom pattern((cx_));
     MOZ_TRY_VAR(pattern, tokenizer_->readAtom());
-    RootedAtom flags(cx_);
-    MOZ_TRY_VAR(flags, tokenizer_->readAtom());
+    Chars flags(cx_);
+    MOZ_TRY(tokenizer_->readChars(flags));
 
     RegExpFlag reflags = NoFlags;
-    JSAutoByteString autoBytes;
-    const char* bytes = AtomToPrintableString(cx_, flags.get(), &autoBytes);
-
-    if (bytes) {
-        for (; *bytes != 0; ++bytes) {
-            const char c = *bytes;
-            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 raiseError("Invalid flags");
-        }
+
+    for (auto 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 raiseError("Invalid flags");
     }
 
 
     Rooted<RegExpObject*> reobj(cx_);
     TRY_VAR(reobj, RegExpObject::create(cx_,
         pattern,
         reflags,
         alloc_,
         TenuredObject));
 
-    TRY_DECL(result, factory_.newRegExp(reobj, tokenizer_->pos(start), *this));return result;
+    TRY_DECL(result, factory_.newRegExp(reobj, tokenizer_->pos(start), *this));
+    return result;
 }
 
 
 /*
  interface LiteralStringExpression : Node {
     string value;
  }
 */
@@ -4789,17 +4821,18 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceLiteralStringExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralStringExpression);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Value }));
 
     RootedAtom value((cx_));
     MOZ_TRY_VAR(value, tokenizer_->readAtom());
 
-    TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface Method : Node {
     bool isAsync;
     bool isGenerator;
     AssertedParameterScope? parameterScope;
@@ -4863,17 +4896,18 @@ BinASTParser<Tok>::parseInterfaceMethod(
 
 
     MOZ_TRY_DECL(params, parseFormalParameters());
 
 
     MOZ_TRY_DECL(body, parseFunctionBody());
 
     MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-    TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::None));return result;
+    TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::None));
+    return result;
 }
 
 
 /*
  interface Module : Node {
     AssertedVarScope? scope;
     FrozenArray<Directive> directives;
     FrozenArray<(ImportDeclaration or ExportDeclaration or Statement)> items;
@@ -4933,17 +4967,18 @@ BinASTParser<Tok>::parseInterfaceNewExpr
 
     MOZ_TRY_DECL(callee, parseExpression());
 
 
     MOZ_TRY_DECL(arguments, parseArguments());
 
     auto result = arguments;
     result->setKind(ParseNodeKind::New);
-    result->prepend(callee);return result;
+    result->prepend(callee);
+    return result;
 }
 
 
 /*
  interface NewTargetExpression : Node {
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
@@ -5050,17 +5085,18 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceObjectExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ObjectExpression);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Properties }));
 
 
     MOZ_TRY_DECL(properties, parseListOfObjectProperty());
 
-    auto result = properties;return result;
+    auto result = properties;
+    return result;
 }
 
 
 /*
  interface ReturnStatement : Node {
     Expression? expression;
  }
 */
@@ -5089,17 +5125,18 @@ BinASTParser<Tok>::parseInterfaceReturnS
         // Return statements are permitted only inside functions.
         return raiseInvalidKind("Toplevel Statement", kind);
     }
 
     parseContext_->functionBox()->usesReturn = true;
 
     MOZ_TRY_DECL(expression, parseOptionalExpression());
 
-    TRY_DECL(result, factory_.newReturnStatement(expression, tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newReturnStatement(expression, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface Script : Node {
     AssertedVarScope? scope;
     FrozenArray<Directive> directives;
     FrozenArray<Statement> statements;
@@ -5131,17 +5168,18 @@ BinASTParser<Tok>::parseInterfaceScript(
     MOZ_TRY(parseOptionalAssertedVarScope());
 
 
     MOZ_TRY_DECL(directives, parseListOfDirective());
 
 
     MOZ_TRY_DECL(statements, parseListOfStatement());
 
-    MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));return result;
+    MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
+    return result;
 }
 
 
 /*
  interface Setter : Node {
     AssertedParameterScope? parameterScope;
     AssertedVarScope? bodyScope;
     PropertyName name;
@@ -5199,17 +5237,18 @@ BinASTParser<Tok>::parseInterfaceSetter(
 
     MOZ_TRY_DECL(param, parseParameter());
 
 
     MOZ_TRY_DECL(body, parseFunctionBody());
 
     TRY_DECL(params, factory_.newList(ParseNodeKind::ParamsBody, param));
     MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-    TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));return result;
+    TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));
+    return result;
 }
 
 
 /*
  interface ShorthandProperty : Node {
     IdentifierExpression name;
  }
 */
@@ -5236,17 +5275,18 @@ BinASTParser<Tok>::parseInterfaceShortha
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name }));
 
 
     MOZ_TRY_DECL(name, parseIdentifierExpression());
 
     if (!factory_.isUsableAsObjectPropertyName(name))
         TRY_VAR(name, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
 
-    TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, name, AccessorType::None));return result;
+    TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, name, AccessorType::None));
+    return result;
 }
 
 
 /*
  interface SpreadElement : Node {
     Expression expression;
  }
 */
@@ -5302,17 +5342,18 @@ BinASTParser<Tok>::parseInterfaceStaticM
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Property }));
 
 
     MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
     RootedAtom property((cx_));
     MOZ_TRY_VAR(property, tokenizer_->readAtom());
 
-    TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));return result;
+    TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
+    return result;
 }
 
 
 /*
  interface StaticMemberExpression : Node {
     (Expression or Super) object;
     IdentifierName property;
  }
@@ -5340,17 +5381,18 @@ BinASTParser<Tok>::parseInterfaceStaticM
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Property }));
 
 
     MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
     RootedAtom property((cx_));
     MOZ_TRY_VAR(property, tokenizer_->readAtom());
 
-    TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));return result;
+    TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
+    return result;
 }
 
 
 /*
  interface Super : Node {
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
@@ -5405,17 +5447,18 @@ BinASTParser<Tok>::parseInterfaceSwitchC
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Test, BinField::Consequent }));
 
 
     MOZ_TRY_DECL(test, parseExpression());
 
 
     MOZ_TRY_DECL(consequent, parseListOfStatement());
 
-    TRY_DECL(result, factory_.newCaseOrDefault(start, test, consequent));return result;
+    TRY_DECL(result, factory_.newCaseOrDefault(start, test, consequent));
+    return result;
 }
 
 
 /*
  interface SwitchDefault : Node {
     FrozenArray<Statement> consequent;
  }
 */
@@ -5439,17 +5482,18 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceSwitchDefault(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::SwitchDefault);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Consequent }));
 
 
     MOZ_TRY_DECL(consequent, parseListOfStatement());
 
-    TRY_DECL(result, factory_.newCaseOrDefault(start, nullptr, consequent));return result;
+    TRY_DECL(result, factory_.newCaseOrDefault(start, nullptr, consequent));
+    return result;
 }
 
 
 /*
  interface SwitchStatement : Node {
     Expression discriminant;
     FrozenArray<SwitchCase> cases;
  }
@@ -5478,17 +5522,18 @@ BinASTParser<Tok>::parseInterfaceSwitchS
 
 
     MOZ_TRY_DECL(discriminant, parseExpression());
 
 
     MOZ_TRY_DECL(cases, parseListOfSwitchCase());
 
     TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
-    TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));return result;
+    TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));
+    return result;
 }
 
 
 /*
  interface SwitchStatementWithDefault : Node {
     Expression discriminant;
     FrozenArray<SwitchCase> preDefaultCases;
     SwitchDefault defaultCase;
@@ -5534,17 +5579,18 @@ BinASTParser<Tok>::parseInterfaceSwitchS
     factory_.addList(cases, defaultCase);
     ParseNode* iter = postDefaultCases->pn_head;
     while (iter) {
         ParseNode* next = iter->pn_next;
         factory_.addList(cases, iter);
         iter = next;
     }
     TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
-    TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));return result;
+    TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));
+    return result;
 }
 
 
 /*
  interface TemplateElement : Node {
     string rawValue;
  }
 */
@@ -5629,17 +5675,18 @@ BinASTParser<Tok>::parseInterfaceThisExp
     if (parseContext_->isFunctionBox())
         parseContext_->functionBox()->usesThis = true;
 
     TokenPos pos = tokenizer_->pos(start);
     ParseNode* thisName(nullptr);
     if (parseContext_->sc()->thisBinding() == ThisBinding::Function)
         TRY_VAR(thisName, factory_.newName(cx_->names().dotThis, pos, cx_));
 
-    TRY_DECL(result, factory_.newThisLiteral(pos, thisName));return result;
+    TRY_DECL(result, factory_.newThisLiteral(pos, thisName));
+    return result;
 }
 
 
 /*
  interface ThrowStatement : Node {
     Expression expression;
  }
 */
@@ -5663,17 +5710,18 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceThrowStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ThrowStatement);
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Expression }));
 
 
     MOZ_TRY_DECL(expression, parseExpression());
 
-    TRY_DECL(result, factory_.newThrowStatement(expression, tokenizer_->pos(start)));return result;
+    TRY_DECL(result, factory_.newThrowStatement(expression, tokenizer_->pos(start)));
+    return result;
 }
 
 
 /*
  interface TryCatchStatement : Node {
     Block body;
     CatchClause catchClause;
  }
@@ -5707,17 +5755,18 @@ BinASTParser<Tok>::parseInterfaceTryCatc
         TRY(scope.init(parseContext_));
         MOZ_TRY_VAR(body, parseBlock());
 
     }
 
 
     MOZ_TRY_DECL(catchClause, parseCatchClause());
 
-    TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, /* finally = */ nullptr));return result;
+    TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, /* finally = */ nullptr));
+    return result;
 }
 
 
 /*
  interface TryFinallyStatement : Node {
     Block body;
     CatchClause? catchClause;
     Block finalizer;
@@ -5761,17 +5810,18 @@ BinASTParser<Tok>::parseInterfaceTryFina
     {
         ParseContext::Statement stmt(parseContext_, StatementKind::Finally);
         ParseContext::Scope scope(cx_, parseContext_, usedNames_);
         TRY(scope.init(parseContext_));
         MOZ_TRY_VAR(finalizer, parseBlock());
 
     }
 
-    TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, finalizer));return result;
+    TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, finalizer));
+    return result;
 }
 
 
 /*
  interface UnaryExpression : Node {
     UnaryOperator operator;
     Expression operand;
  }
@@ -5841,17 +5891,18 @@ BinASTParser<Tok>::parseInterfaceUnaryEx
             pnk = ParseNodeKind::DeleteElem;
             break;
           default:
             pnk = ParseNodeKind::DeleteExpr;
         }
         break;
       }
     }
-    TRY_DECL(result, factory_.newUnary(pnk, start, operand));return result;
+    TRY_DECL(result, factory_.newUnary(pnk, start, operand));
+    return result;
 }
 
 
 /*
  interface UpdateExpression : Node {
     bool isPrefix;
     UpdateOperator operator;
     SimpleAssignmentTarget operand;
@@ -5894,17 +5945,18 @@ BinASTParser<Tok>::parseInterfaceUpdateE
         pnk = isPrefix ? ParseNodeKind::PreIncrement
                        : ParseNodeKind::PostIncrement;
         break;
       case UpdateOperator::Decr:
         pnk = isPrefix ? ParseNodeKind::PreDecrement
                        : ParseNodeKind::PostDecrement;
         break;
     }
-    TRY_DECL(result, factory_.newUnary(pnk, start, operand));return result;
+    TRY_DECL(result, factory_.newUnary(pnk, start, operand));
+    return result;
 }
 
 
 /*
  interface VariableDeclaration : Node {
     VariableDeclarationKind kind;
     FrozenArray<VariableDeclarator> declarators;
  }
@@ -5952,17 +6004,18 @@ BinASTParser<Tok>::parseInterfaceVariabl
       case VariableDeclarationKind::Let:
         pnk = ParseNodeKind::Let;
         break;
       case VariableDeclarationKind::Const:
         pnk = ParseNodeKind::Const;
         break;
     }
     declarators->setKind(pnk);
-    auto result = declarators;return result;
+    auto result = declarators;
+    return result;
 }
 
 
 /*
  interface VariableDeclarator : Node {
     Binding binding;
     Expression? init;
  }
@@ -6007,17 +6060,18 @@ BinASTParser<Tok>::parseInterfaceVariabl
         // `var pattern = bar`
         if (!init) {
             // Here, `init` is required.
             return raiseMissingField("VariableDeclarator (with non-trivial pattern)", BinField::Init);
         }
 
         MOZ_CRASH("Unimplemented: AssertedScope check for BindingPattern variable declaration");
         TRY_VAR(result, factory_.newAssignment(ParseNodeKind::Assign, binding, init));
-    }return result;
+    }
+    return result;
 }
 
 
 /*
  interface WhileStatement : Node {
     Expression test;
     Statement body;
  }
@@ -6045,17 +6099,18 @@ BinASTParser<Tok>::parseInterfaceWhileSt
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Test, BinField::Body }));
     ParseContext::Statement stmt(parseContext_, StatementKind::WhileLoop);
 
     MOZ_TRY_DECL(test, parseExpression());
 
 
     MOZ_TRY_DECL(body, parseStatement());
 
-    TRY_DECL(result, factory_.newWhileStatement(start, test, body));return result;
+    TRY_DECL(result, factory_.newWhileStatement(start, test, body));
+    return result;
 }
 
 
 /*
  interface WithStatement : Node {
     Expression object;
     Statement body;
  }
@@ -6083,17 +6138,18 @@ BinASTParser<Tok>::parseInterfaceWithSta
     MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Body }));
 
 
     MOZ_TRY_DECL(object, parseExpression());
 
 
     MOZ_TRY_DECL(body, parseStatement());
 
-    TRY_DECL(result, factory_.newWithStatement(start, object, body));return result;
+    TRY_DECL(result, factory_.newWithStatement(start, object, body));
+    return result;
 }
 
 
 /*
  interface YieldExpression : Node {
     Expression? expression;
  }
 */
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -737,39 +737,34 @@ LiteralPropertyName:
         else
             TRY_VAR(result, factory_.newObjectLiteralPropertyName(value, tokenizer_->pos(start)));
 
 LiteralRegExpExpression:
     fields:
         flags:
             block:
                 replace: |
-                    RootedAtom flags(cx_);
-                    MOZ_TRY_VAR(flags, tokenizer_->readAtom());
+                    Chars flags(cx_);
+                    MOZ_TRY(tokenizer_->readChars(flags));
     build: |
         RegExpFlag reflags = NoFlags;
-        JSAutoByteString autoBytes;
-        const char* bytes = AtomToPrintableString(cx_, flags.get(), &autoBytes);
 
-        if (bytes) {
-            for (; *bytes != 0; ++bytes) {
-                const char c = *bytes;
-                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 raiseError("Invalid flags");
-            }
+        for (auto 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 raiseError("Invalid flags");
         }
 
 
         Rooted<RegExpObject*> reobj(cx_);
         TRY_VAR(reobj, RegExpObject::create(cx_,
             pattern,
             reflags,
             alloc_,
--- a/js/src/frontend/BinSourceRuntimeSupport.h
+++ b/js/src/frontend/BinSourceRuntimeSupport.h
@@ -24,17 +24,17 @@ struct BinaryASTSupport {
     using BinVariant  = js::frontend::BinVariant;
     using BinField = js::frontend::BinField;
     using BinKind  = js::frontend::BinKind;
 
     // A structure designed to perform fast char* + length lookup
     // without copies.
     struct CharSlice {
         const char* start_;
-        const uint32_t byteLen_;
+        uint32_t byteLen_;
         CharSlice(const CharSlice& other) :
             start_(other.start_),
             byteLen_(other.byteLen_)
         {  }
         CharSlice(const char* start, const uint32_t byteLen) :
             start_(start),
             byteLen_(byteLen)
         { }
@@ -53,21 +53,16 @@ struct BinaryASTSupport {
         }
 #endif // DEBUG
 
         typedef const CharSlice Lookup;
         static js::HashNumber hash(Lookup l) {
             return mozilla::HashString(l.start_, l.byteLen_);
         }
         static bool match(const Lookup key, Lookup lookup) {
-            fprintf(stderr, "CharSlice::match: ");
-            key.dump();
-            fprintf(stderr, " vs ");
-            key.dump();
-            fprintf(stderr, "\n");
             if (key.byteLen_ != lookup.byteLen_)
                 return false;
             return strncmp(key.start_, lookup.start_, key.byteLen_) == 0;
         }
     };
 
     JS::Result<mozilla::Maybe<BinVariant>>  binVariant(JSContext*, const CharSlice);
     JS::Result<mozilla::Maybe<BinField>> binField(JSContext*, const CharSlice);
--- a/js/src/frontend/BinTokenReaderMultipart.cpp
+++ b/js/src/frontend/BinTokenReaderMultipart.cpp
@@ -27,22 +27,23 @@ const char COMPRESSION_IDENTITY[] = "ide
 // The maximal number of distinct strings that may be declared in a
 // single file.
 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)
-    , CustomAutoRooter(cx)
     , grammarTable(cx)
     , atomsTable(cx, AtomVector(cx))
+    , slicesTable(cx)
     , posBeforeTree(nullptr)
 { }
 
 JS::Result<Ok>
 BinTokenReaderMultipart::readHeader()
 {
     // Check that we don't call this function twice.
     MOZ_ASSERT(!posBeforeTree);
@@ -102,16 +103,18 @@ BinTokenReaderMultipart::readHeader()
     MOZ_TRY_DECL(stringsNumberOfEntries, readInternalUint32());
     if (stringsNumberOfEntries > MAX_NUMBER_OF_STRINGS) // Sanity check.
         return raiseError("Too many entries in strings table");
 
     // This table maps String index -> String.
     // Initialize and populate.
     if (!atomsTable.reserve(stringsNumberOfEntries))
         return raiseOOM();
+    if (!slicesTable.reserve(stringsNumberOfEntries))
+        return raiseOOM();
     if (!variantsTable.init())
         return cx_->alreadyReportedError();
 
     RootedAtom atom(cx_);
     for (uint32_t i = 0; i < stringsNumberOfEntries; ++i) {
         MOZ_TRY_DECL(byteLen, readInternalUint32());
         if (byteLen > stop_ - current_)
             return raiseError("Invalid byte length in individual string");
@@ -120,30 +123,22 @@ BinTokenReaderMultipart::readHeader()
         if (byteLen == 2 && *current_ == 255 && *(current_ + 1) == 0) {
             atom = nullptr;
         } else {
             TRY_VAR(atom, Atomize(cx_, (const char*)current_, byteLen));
         }
 
         // Populate `atomsTable`: i => atom.
         if (!atomsTable.append(atom))
-            MOZ_CRASH(); // We have reserved before entering the loop above.
-
-        // Populate `variantsTable`: i => variant, if this atom maps to a variant.
-        if (atom) {
-            CharSlice slice((const char*)current_, byteLen);
-            MOZ_TRY_DECL(variant, cx_->runtime()->binast().binVariant(cx_, slice));
+            MOZ_CRASH(); // We have reserved before entering the loop.
 
-            if (variant) {
-                auto ptr = variantsTable.lookupForAdd(i);
-                MOZ_ASSERT(!ptr);
-                if (!variantsTable.add(ptr, i, *variant))
-                    return raiseOOM();
-            }
-        }
+        // Populate `slicesTable`: i => slice
+        Chars slice((const char*)current_, byteLen);
+        if (!slicesTable.append(Move(slice)))
+            MOZ_CRASH(); // We have reserved before entering the loop.
 
         current_ += byteLen;
     }
 
     if (current_ - posBeforeStrings != stringsByteLen)
         return raiseError("The length of the strings table didn't match its contents.");
 
     // Start reading AST.
@@ -250,30 +245,58 @@ BinTokenReaderMultipart::readAtom()
     MOZ_TRY_DECL(maybe, readMaybeAtom());
 
     if (!maybe)
         return raiseError("Empty string");
 
     return maybe;
 }
 
+JS::Result<Ok>
+BinTokenReaderMultipart::readChars(Chars& out)
+{
+    updateLatestKnownGood();
+    MOZ_TRY_DECL(index, readInternalUint32());
+
+    if (index >= slicesTable.length())
+        return raiseError("Invalid index to strings table for string enum");
+
+    out = slicesTable[index];
+    return Ok();
+}
+
 JS::Result<BinVariant>
 BinTokenReaderMultipart::readVariant()
 {
     updateLatestKnownGood();
     MOZ_TRY_DECL(index, readInternalUint32());
 
-    if (index >= atomsTable.length())
+    if (index >= slicesTable.length())
         return raiseError("Invalid index to strings table for string enum");
 
-    auto ptr = variantsTable.lookup(index);
-    if (ptr)
-        return ptr->value();
+    auto variantsPtr = variantsTable.lookupForAdd(index);
+    if (variantsPtr)
+        return variantsPtr->value();
+
+    // Either we haven't cached the result yet or this is not a variant.
+    // Check in the slices table and, in case of success, cache the result.
 
-    return raiseError("Invalid string enum variant");
+    // Note that we stop parsing if we attempt to readVariant() with an
+    // ill-formed variant, so we don't run the risk of feching an ill-variant
+    // more than once.
+    Chars slice = slicesTable[index]; // We have checked `index` above.
+    MOZ_TRY_DECL(variant, cx_->runtime()->binast().binVariant(cx_, slice));
+
+    if (!variant)
+        return raiseError("Invalid string enum variant");
+
+    if (!variantsTable.add(variantsPtr, index, *variant))
+        return raiseOOM();
+
+    return *variant;
 }
 
 // Untagged tuple:
 // - contents (specified by the higher-level grammar);
 JS::Result<Ok>
 BinTokenReaderMultipart::enterUntaggedTuple(AutoTuple& guard)
 {
     guard.init();
--- a/js/src/frontend/BinTokenReaderMultipart.h
+++ b/js/src/frontend/BinTokenReaderMultipart.h
@@ -19,32 +19,38 @@ using namespace JS;
  * implementation of the BinAST compression suite, is designed to be
  * space- and time-efficient.
  *
  * As other token readers for the BinAST:
  *
  * - the reader does not support error recovery;
  * - the reader does not support lookahead or pushback.
  */
-class MOZ_STACK_CLASS BinTokenReaderMultipart: public BinTokenReaderBase, private CustomAutoRooter
+class MOZ_STACK_CLASS BinTokenReaderMultipart: public BinTokenReaderBase
 {
   public:
     class AutoList;
     class AutoTuple;
     class AutoTaggedTuple;
 
+    using CharSlice = BinaryASTSupport::CharSlice;
+
     // This implementation of `BinFields` is effectively `void`.
     class BinFields {
       public:
         BinFields(JSContext*) {}
     };
-    struct Chars: public BinaryASTSupport::CharSlice {
+    struct Chars: public CharSlice {
         Chars(JSContext*)
-          : BinaryASTSupport::CharSlice(nullptr, 0)
+          : CharSlice(nullptr, 0)
         { }
+        Chars(const char* start, const uint32_t byteLen)
+          : CharSlice(start, byteLen)
+        { }
+        Chars(const Chars& other) = default;
     };
 
   public:
     /**
      * Construct a token reader.
      *
      * Does NOT copy the buffer.
      */
@@ -87,16 +93,24 @@ class MOZ_STACK_CLASS BinTokenReaderMult
     /**
      * Read a single `string | null` value.
      *
      * Fails if that string is not valid UTF-8.
      */
     MOZ_MUST_USE JS::Result<JSAtom*> readMaybeAtom();
     MOZ_MUST_USE JS::Result<JSAtom*> readAtom();
 
+
+    /**
+     * Read a single `string | null` value.
+     *
+     * MAY check if that string is not valid UTF-8.
+     */
+    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();
 
     // --- Composite values.
     //
@@ -156,24 +170,28 @@ class MOZ_STACK_CLASS BinTokenReaderMult
     MOZ_MUST_USE JS::Result<uint32_t> readInternalUint32();
 
   private:
     // A mapping grammar index => BinKind, as defined by the [grammar]
     // section of the file. Populated during readHeader().
     Vector<BinKind> grammarTable;
 
     // A mapping string index => BinVariant as extracted from the [strings]
-    // section of the file. Populated during readHeader().
+    // section of the file. Populated lazily.
     js::HashMap<uint32_t, BinVariant, DefaultHasher<uint32_t>, SystemAllocPolicy> variantsTable;
 
-    // A mapping string index => JSAtom, as defined by the [strings]
+    // A mapping index => JSAtom, as defined by the [strings]
     // section of the file. Populated during readHeader().
     using AtomVector = GCVector<JSAtom*>;
     JS::Rooted<AtomVector> atomsTable;
 
+    // The same mapping, but as CharSlice. Populated during readHeader().
+    // The slices are into the source buffer.
+    Vector<Chars> slicesTable;
+
     const uint8_t* posBeforeTree;
 
     BinTokenReaderMultipart(const BinTokenReaderMultipart&) = delete;
     BinTokenReaderMultipart(BinTokenReaderMultipart&&) = delete;
     BinTokenReaderMultipart& operator=(BinTokenReaderMultipart&) = delete;
 
   public:
     // The following classes are used whenever we encounter a tuple/tagged tuple/list
@@ -251,20 +269,16 @@ class MOZ_STACK_CLASS BinTokenReaderMult
             return false;
 
         if (!std::equal(left.start_, left.start_ + left.byteLen_, right))
           return false;
 
         return true;
     }
 
-    virtual void trace(JSTracer* trc) {
-        MOZ_CRASH("Not implemented");
-    }
-
     template<size_t N>
     static JS::Result<Ok, JS::Error&>
     checkFields(const BinKind kind, const BinFields& actual, const BinField (&expected)[N]) {
       // Not implemented in this tokenizer.
       return Ok();
     }
 
     static JS::Result<Ok, JS::Error&>
--- a/js/src/frontend/BinTokenReaderTester.cpp
+++ b/js/src/frontend/BinTokenReaderTester.cpp
@@ -153,16 +153,57 @@ BinTokenReaderTester::readMaybeAtom()
         TRY_VAR(result, Atomize(cx_, (const char*)current_, byteLen));
     }
 
     current_ += byteLen;
     MOZ_TRY(readConst("</string>"));
     return result.get();
 }
 
+
+// Nullable strings:
+// - "<string>" (not counted in byte length)
+// - byte length (not counted in byte length)
+// - bytes (UTF-8)
+// - "</string>" (not counted in byte length)
+//
+// The special sequence of bytes `[255, 0]` (which is an invalid UTF-8 sequence)
+// is reserved to `null`.
+JS::Result<Ok>
+BinTokenReaderTester::readChars(Chars& out)
+{
+    updateLatestKnownGood();
+
+    MOZ_TRY(readConst("<string>"));
+
+    // 1. Read byteLength
+    MOZ_TRY_DECL(byteLen, readInternalUint32());
+
+    // 2. Reject if we can't read
+    if (current_ + byteLen < current_) // Check for overflows
+        return raiseError("Arithmetics overflow: string is too long");
+
+    if (current_ + byteLen > stop_)
+        return raiseError("Not enough bytes to read chars");
+
+    if (byteLen == 2 && *current_ == 255 && *(current_ + 1) == 0) {
+        // 3. Special case: null string.
+        return raiseError("Empty string");
+    }
+
+    // 4. Other strings (bytes are copied)
+    if (!out.resize(byteLen))
+        return raiseOOM();
+
+    PodCopy(out.begin(), current_, byteLen);
+
+    MOZ_TRY(readConst("</string>"));
+    return Ok();
+}
+
 JS::Result<JSAtom*>
 BinTokenReaderTester::readAtom()
 {
     RootedAtom atom(cx_);
     MOZ_TRY_VAR(atom, readMaybeAtom());
 
     if (!atom)
         return raiseError("Empty string");
--- a/js/src/frontend/BinTokenReaderTester.h
+++ b/js/src/frontend/BinTokenReaderTester.h
@@ -119,16 +119,24 @@ class MOZ_STACK_CLASS BinTokenReaderTest
     /**
      * Read a single `string | null` value.
      *
      * Fails if that string is not valid UTF-8.
      */
     MOZ_MUST_USE JS::Result<JSAtom*> readMaybeAtom();
     MOZ_MUST_USE JS::Result<JSAtom*> readAtom();
 
+
+    /**
+     * Read a single `string | null` value.
+     *
+     * MAY check if that string is not valid UTF-8.
+     */
+    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();
 
     // --- Composite values.
     //