Merge mozilla-central to inbound. a=merge CLOSED TREE
authorTiberius Oros <toros@mozilla.com>
Mon, 23 Apr 2018 01:15:49 +0300
changeset 468555 368eefa9989f1f9de901b22419a4524bc47a4745
parent 468554 13bfd624b92e9881cf5d4c7ad7efd246fe56b334 (current diff)
parent 468533 378a8a64401f765bfd0706ff678a4f5db7c05385 (diff)
child 468556 dd464b3666b155e8b1dda1d7652db27a3e64f9ba
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
--- a/js/src/frontend/BinSource-auto.cpp
+++ b/js/src/frontend/BinSource-auto.cpp
@@ -2620,17 +2620,22 @@ BinASTParser<Tok>::parseArrayExpression(
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceArrayExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ArrayExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Elements }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Elements };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(elements, parseListOfOptionalSpreadElementOrExpression());
 
 
     auto result = elements;
     return result;
@@ -2661,17 +2666,22 @@ BinASTParser<Tok>::parseAssertedBlockSco
 }
 
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::parseInterfaceAssertedBlockScope(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::AssertedBlockScope);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::LexicallyDeclaredNames, BinField::CapturedNames, BinField::HasDirectEval }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::LexicallyDeclaredNames, BinField::CapturedNames, BinField::HasDirectEval };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::Let));
     MOZ_TRY(parseAndUpdateCapturedNames());
 
 
 
     BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool());
     if (hasDirectEval) {
         parseContext_->sc()->setHasDirectEval();
@@ -2712,17 +2722,22 @@ BinASTParser<Tok>::parseAssertedParamete
 }
 
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::parseInterfaceAssertedParameterScope(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::AssertedParameterScope);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::ParameterNames, BinField::CapturedNames, BinField::HasDirectEval }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::ParameterNames, BinField::CapturedNames, BinField::HasDirectEval };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     MOZ_TRY(parseAndUpdateScopeNames(parseContext_->functionScope(), DeclarationKind:: PositionalFormalParameter));
     MOZ_TRY(parseAndUpdateCapturedNames());
 
 
 
     BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool());
     if (hasDirectEval) {
         parseContext_->sc()->setHasDirectEval();
@@ -2764,17 +2779,22 @@ BinASTParser<Tok>::parseAssertedVarScope
 }
 
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::parseInterfaceAssertedVarScope(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::AssertedVarScope);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::LexicallyDeclaredNames, BinField::VarDeclaredNames, BinField::CapturedNames, BinField::HasDirectEval }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[4] = { BinField::LexicallyDeclaredNames, BinField::VarDeclaredNames, BinField::CapturedNames, BinField::HasDirectEval };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::Let));
     MOZ_TRY(parseAndUpdateScopeNames(parseContext_->varScope(), DeclarationKind::Var));
     MOZ_TRY(parseAndUpdateCapturedNames());
 
 
 
     BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool());
     if (hasDirectEval) {
@@ -2815,17 +2835,22 @@ BinASTParser<Tok>::parseAssignmentExpres
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceAssignmentExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::AssignmentExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Binding, BinField::Expression }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Binding, BinField::Expression };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(binding, parseAssignmentTarget());
 
 
 
 
@@ -2859,17 +2884,22 @@ BinASTParser<Tok>::parseAssignmentTarget
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceAssignmentTargetIdentifier(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::AssignmentTargetIdentifier);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Name };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
     RootedAtom name(cx_);
     MOZ_TRY_VAR(name, tokenizer_->readAtom());
 
 
     if (!IsIdentifier(name))
         return raiseError("Invalid identifier");
@@ -3017,17 +3047,22 @@ BinASTParser<Tok>::parseBinaryExpression
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceBinaryExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::BinaryExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Operator, BinField::Left, BinField::Right }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::Operator, BinField::Left, BinField::Right };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(operator_, parseBinaryOperator());
 
 
 
 
@@ -3158,17 +3193,22 @@ BinASTParser<Tok>::parseBindingIdentifie
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceBindingIdentifier(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::BindingIdentifier);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Name };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
     RootedAtom name(cx_);
     MOZ_TRY_VAR(name, tokenizer_->readAtom());
 
 
     if (!IsIdentifier(name))
         return raiseError("Invalid identifier");
@@ -3287,17 +3327,22 @@ BinASTParser<Tok>::parseBlock()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceBlock(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::Block);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Scope, BinField::Statements }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Scope, BinField::Statements };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     ParseContext::Statement stmt(parseContext_, StatementKind::Block);
     ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(currentScope.init(parseContext_));
 
 
     MOZ_TRY(parseOptionalAssertedBlockScope());
 
 
@@ -3334,17 +3379,22 @@ BinASTParser<Tok>::parseBreakStatement()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceBreakStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::BreakStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Label }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Label };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     RootedAtom label(cx_);
     MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom());
 
     if (label) {
         if (!IsIdentifier(label))
             return raiseError("Invalid identifier");
 
         auto validity = parseContext_->checkBreakStatement(label->asPropertyName());
@@ -3386,17 +3436,22 @@ BinASTParser<Tok>::parseCallExpression()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceCallExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::CallExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Callee, BinField::Arguments }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Callee, BinField::Arguments };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(callee, parseExpressionOrSuper());
 
 
 
 
@@ -3446,17 +3501,22 @@ BinASTParser<Tok>::parseCatchClause()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceCatchClause(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::CatchClause);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Binding, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Binding, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     ParseContext::Statement stmt(parseContext_, StatementKind::Catch);
     ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(currentScope.init(parseContext_));
 
 
     BINJS_MOZ_TRY_DECL(binding, parseBinding());
 
 
@@ -3591,17 +3651,22 @@ BinASTParser<Tok>::parseCompoundAssignme
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceCompoundAssignmentExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::CompoundAssignmentExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Operator, BinField::Binding, BinField::Expression }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::Operator, BinField::Binding, BinField::Expression };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(operator_, parseCompoundAssignmentOperator());
 
 
 
 
@@ -3680,17 +3745,22 @@ BinASTParser<Tok>::parseComputedMemberAs
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceComputedMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ComputedMemberAssignmentTarget);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Expression }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Object, BinField::Expression };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
 
 
 
@@ -3725,17 +3795,22 @@ BinASTParser<Tok>::parseComputedMemberEx
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceComputedMemberExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ComputedMemberExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Expression }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Object, BinField::Expression };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
 
 
 
@@ -3799,17 +3874,22 @@ BinASTParser<Tok>::parseConditionalExpre
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceConditionalExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ConditionalExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Test, BinField::Consequent, BinField::Alternate }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::Test, BinField::Consequent, BinField::Alternate };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(test, parseExpression());
 
 
 
 
@@ -3848,17 +3928,22 @@ BinASTParser<Tok>::parseContinueStatemen
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceContinueStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ContinueStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Label }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Label };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     RootedAtom label(cx_);
     MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom());
 
     if (label) {
         if (!IsIdentifier(label))
             return raiseError("ContinueStatement - Label MUST be an identifier");
 
         auto validity = parseContext_->checkContinueStatement(label ? label->asPropertyName() : nullptr);
@@ -3900,17 +3985,22 @@ BinASTParser<Tok>::parseDataProperty()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceDataProperty(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::DataProperty);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name, BinField::Expression }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Name, BinField::Expression };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(name, parsePropertyName());
 
 
 
 
@@ -3974,17 +4064,22 @@ BinASTParser<Tok>::parseDirective()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceDirective(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::Directive);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::RawValue }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::RawValue };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
     RootedAtom rawValue(cx_);
     MOZ_TRY_VAR(rawValue, tokenizer_->readAtom());
 
 
     TokenPos pos = tokenizer_->pos(start);
     BINJS_TRY_DECL(result, factory_.newStringLiteral(rawValue, pos));
@@ -4015,17 +4110,22 @@ BinASTParser<Tok>::parseDoWhileStatement
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceDoWhileStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::DoWhileStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Test, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Test, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     ParseContext::Statement stmt(parseContext_, StatementKind::DoLoop);
 
 
     BINJS_MOZ_TRY_DECL(test, parseExpression());
 
 
 
 
@@ -4097,17 +4197,22 @@ BinASTParser<Tok>::parseEagerFunctionDec
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerFunctionDeclaration);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::IsAsync, BinField::IsGenerator, BinField::ParameterScope, BinField::BodyScope, BinField::Name, BinField::Params, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[7] = { BinField::IsAsync, BinField::IsGenerator, BinField::ParameterScope, BinField::BodyScope, BinField::Name, BinField::Params, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     const auto syntax = FunctionSyntaxKind::Statement;
 
 
     BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool());
 
 
 
 
@@ -4188,17 +4293,22 @@ BinASTParser<Tok>::parseEagerFunctionExp
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerFunctionExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::IsAsync, BinField::IsGenerator, BinField::ParameterScope, BinField::BodyScope, BinField::Name, BinField::Params, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[7] = { BinField::IsAsync, BinField::IsGenerator, BinField::ParameterScope, BinField::BodyScope, BinField::Name, BinField::Params, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     const auto syntax = FunctionSyntaxKind::Expression;
 
 
     BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool());
 
 
 
 
@@ -4275,17 +4385,22 @@ BinASTParser<Tok>::parseEagerGetter()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerGetter(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerGetter);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::BodyScope, BinField::Name, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::BodyScope, BinField::Name, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     MOZ_TRY(parseOptionalAssertedVarScope());
 
 
 
 
@@ -4344,17 +4459,22 @@ BinASTParser<Tok>::parseEagerMethod()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerMethod(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerMethod);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::IsAsync, BinField::IsGenerator, BinField::ParameterScope, BinField::BodyScope, BinField::Name, BinField::Params, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[7] = { BinField::IsAsync, BinField::IsGenerator, BinField::ParameterScope, BinField::BodyScope, BinField::Name, BinField::Params, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     const auto syntax = FunctionSyntaxKind::Method;
 
 
     BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool());
 
 
 
 
@@ -4432,17 +4552,22 @@ BinASTParser<Tok>::parseEagerSetter()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerSetter(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerSetter);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::ParameterScope, BinField::BodyScope, BinField::Name, BinField::Param, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[5] = { BinField::ParameterScope, BinField::BodyScope, BinField::Name, BinField::Param, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     MOZ_TRY(parseOptionalAssertedParameterScope());
 
 
 
 
@@ -4505,17 +4630,17 @@ 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);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields0(kind, fields));
+MOZ_TRY(tokenizer_->checkFields0(kind, fields));
 
     BINJS_TRY_DECL(result, factory_.newEmptyStatement(tokenizer_->pos(start)));
     return result;
 }
 
 
 /*
  interface Export : Node {
@@ -4738,17 +4863,22 @@ BinASTParser<Tok>::parseExpressionStatem
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceExpressionStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ExpressionStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Expression }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Expression };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(expression, parseExpression());
 
 
     BINJS_TRY_DECL(result, factory_.newExprStatement(expression, tokenizer_->offset()));
     return result;
@@ -4778,17 +4908,22 @@ BinASTParser<Tok>::parseForInOfBinding()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceForInOfBinding(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ForInOfBinding);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Kind, BinField::Binding }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Kind, BinField::Binding };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     AutoVariableDeclarationKind kindGuard(this);
 
 
     BINJS_MOZ_TRY_DECL(kind_, parseVariableDeclarationKind());
 
 
 
 
@@ -4832,17 +4967,22 @@ BinASTParser<Tok>::parseForInStatement()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceForInStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ForInStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Left, BinField::Right, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::Left, BinField::Right, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     ParseContext::Statement stmt(parseContext_, StatementKind::ForInLoop);
 
     // Implicit scope around the `for`, used to store `for (let x in  ...)`
     // or `for (const x in ...)`-style declarations. Detail on the
     // declaration is stored as part of `scope`.
     ParseContext::Scope scope(cx_, parseContext_, usedNames_);
     BINJS_TRY(scope.init(parseContext_));
 
@@ -4926,17 +5066,22 @@ BinASTParser<Tok>::parseForStatement()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceForStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ForStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Init, BinField::Test, BinField::Update, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[4] = { BinField::Init, BinField::Test, BinField::Update, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     ParseContext::Statement stmt(parseContext_, StatementKind::ForLoop);
 
     // Implicit scope around the `for`, used to store `for (let x; ...; ...)`
     // or `for (const x; ...; ...)`-style declarations. Detail on the
     // declaration is stored as part of `BINJS_Scope`.
     ParseContext::Scope scope(cx_, parseContext_, usedNames_);
     BINJS_TRY(scope.init(parseContext_));
 
@@ -4993,17 +5138,22 @@ BinASTParser<Tok>::parseFormalParameters
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceFormalParameters(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::FormalParameters);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Items, BinField::Rest }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Items, BinField::Rest };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(items, parseListOfParameter());
 
 
 
 
@@ -5042,17 +5192,22 @@ BinASTParser<Tok>::parseFunctionBody()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceFunctionBody(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::FunctionBody);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Directives, BinField::Statements }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Directives, BinField::Statements };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
 
 
 
 
@@ -5086,17 +5241,22 @@ BinASTParser<Tok>::parseIdentifierExpres
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceIdentifierExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::IdentifierExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Name };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
     RootedAtom name(cx_);
     MOZ_TRY_VAR(name, tokenizer_->readAtom());
 
 
     if (!IsIdentifier(name))
         return raiseError("Invalid identifier");
@@ -5129,17 +5289,22 @@ BinASTParser<Tok>::parseIfStatement()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceIfStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::IfStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Test, BinField::Consequent, BinField::Alternate }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::Test, BinField::Consequent, BinField::Alternate };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(test, parseExpression());
 
 
 
 
@@ -5268,17 +5433,22 @@ BinASTParser<Tok>::parseLabelledStatemen
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLabelledStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LabelledStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Label, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Label, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
     RootedAtom label(cx_);
     MOZ_TRY_VAR(label, tokenizer_->readAtom());
     if (!IsIdentifier(label))
         return raiseError("Invalid identifier");
     ParseContext::LabelStatement stmt(parseContext_, label);
 
@@ -5314,17 +5484,22 @@ BinASTParser<Tok>::parseLiteralBooleanEx
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralBooleanExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralBooleanExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Value }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Value };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(value, tokenizer_->readBool());
 
 
     BINJS_TRY_DECL(result, factory_.newBooleanLiteral(value, tokenizer_->pos(start)));
     return result;
@@ -5379,17 +5554,17 @@ 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);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields0(kind, fields));
+MOZ_TRY(tokenizer_->checkFields0(kind, fields));
 
     BINJS_TRY_DECL(result, factory_.newNullLiteral(tokenizer_->pos(start)));
     return result;
 }
 
 
 /*
  interface LiteralNumericExpression : Node {
@@ -5413,17 +5588,22 @@ BinASTParser<Tok>::parseLiteralNumericEx
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralNumericExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralNumericExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Value }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Value };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(value, tokenizer_->readDouble());
 
 
     BINJS_TRY_DECL(result, factory_.newNumber(value, DecimalPoint::HasDecimal, tokenizer_->pos(start)));
     return result;
@@ -5452,17 +5632,22 @@ BinASTParser<Tok>::parseLiteralPropertyN
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralPropertyName(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralPropertyName);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Value }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Value };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
     RootedAtom value(cx_);
     MOZ_TRY_VAR(value, tokenizer_->readAtom());
 
 
     ParseNode* result;
     uint32_t index;
@@ -5497,17 +5682,22 @@ 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);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Pattern, BinField::Flags }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Pattern, BinField::Flags };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
     RootedAtom pattern(cx_);
     MOZ_TRY_VAR(pattern, tokenizer_->readAtom());
 
     Chars flags(cx_);
     MOZ_TRY(tokenizer_->readChars(flags));
 
@@ -5562,17 +5752,22 @@ BinASTParser<Tok>::parseLiteralStringExp
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralStringExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::LiteralStringExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Value }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Value };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
     RootedAtom value(cx_);
     MOZ_TRY_VAR(value, tokenizer_->readAtom());
 
 
     BINJS_TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start)));
     return result;
@@ -5632,17 +5827,22 @@ BinASTParser<Tok>::parseNewExpression()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceNewExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::NewExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Callee, BinField::Arguments }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Callee, BinField::Arguments };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(callee, parseExpression());
 
 
 
 
@@ -5761,17 +5961,22 @@ BinASTParser<Tok>::parseObjectExpression
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceObjectExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ObjectExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Properties }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Properties };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(properties, parseListOfObjectProperty());
 
 
     auto result = properties;
     return result;
@@ -5800,17 +6005,22 @@ BinASTParser<Tok>::parseReturnStatement(
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceReturnStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ReturnStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Expression }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Expression };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     if (!parseContext_->isFunctionBox()) {
         // Return statements are permitted only inside functions.
         return raiseInvalidKind("Toplevel Statement", kind);
     }
 
     parseContext_->functionBox()->usesReturn = true;
 
 
@@ -5846,17 +6056,22 @@ BinASTParser<Tok>::parseScript()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceScript(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::Script);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Scope, BinField::Directives, BinField::Statements }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::Scope, BinField::Directives, BinField::Statements };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     MOZ_TRY(parseOptionalAssertedVarScope());
 
 
 
 
@@ -5895,17 +6110,22 @@ BinASTParser<Tok>::parseShorthandPropert
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceShorthandProperty(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ShorthandProperty);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Name }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Name };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(name, parseIdentifierExpression());
 
 
     if (!factory_.isUsableAsObjectPropertyName(name))
         BINJS_TRY_VAR(name, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
@@ -6134,17 +6354,22 @@ BinASTParser<Tok>::parseStaticMemberAssi
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceStaticMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::StaticMemberAssignmentTarget);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Property }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Object, BinField::Property };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
 
 
     RootedAtom property(cx_);
@@ -6179,17 +6404,22 @@ BinASTParser<Tok>::parseStaticMemberExpr
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceStaticMemberExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::StaticMemberExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Property }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Object, BinField::Property };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper());
 
 
 
     RootedAtom property(cx_);
@@ -6251,17 +6481,22 @@ BinASTParser<Tok>::parseSwitchCase()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceSwitchCase(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::SwitchCase);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Test, BinField::Consequent }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Test, BinField::Consequent };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(test, parseExpression());
 
 
 
 
@@ -6295,17 +6530,22 @@ BinASTParser<Tok>::parseSwitchDefault()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceSwitchDefault(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::SwitchDefault);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Consequent }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Consequent };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(consequent, parseListOfStatement());
 
 
     BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, nullptr, consequent));
     return result;
@@ -6335,17 +6575,22 @@ BinASTParser<Tok>::parseSwitchStatement(
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceSwitchStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::SwitchStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Discriminant, BinField::Cases }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Discriminant, BinField::Cases };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(discriminant, parseExpression());
 
 
 
 
@@ -6383,17 +6628,22 @@ BinASTParser<Tok>::parseSwitchStatementW
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceSwitchStatementWithDefault(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::SwitchStatementWithDefault);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Discriminant, BinField::PreDefaultCases, BinField::DefaultCase, BinField::PostDefaultCases }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[4] = { BinField::Discriminant, BinField::PreDefaultCases, BinField::DefaultCase, BinField::PostDefaultCases };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(discriminant, parseExpression());
 
 
 
 
@@ -6503,17 +6753,17 @@ BinASTParser<Tok>::parseThisExpression()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceThisExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ThisExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields0(kind, fields));
+MOZ_TRY(tokenizer_->checkFields0(kind, fields));
 
     if (parseContext_->isFunctionBox())
         parseContext_->functionBox()->usesThis = true;
 
     TokenPos pos = tokenizer_->pos(start);
     ParseNode* thisName(nullptr);
     if (parseContext_->sc()->thisBinding() == ThisBinding::Function)
         BINJS_TRY_VAR(thisName, factory_.newName(cx_->names().dotThis, pos, cx_));
@@ -6545,17 +6795,22 @@ BinASTParser<Tok>::parseThrowStatement()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceThrowStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::ThrowStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Expression }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[1] = { BinField::Expression };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(expression, parseExpression());
 
 
     BINJS_TRY_DECL(result, factory_.newThrowStatement(expression, tokenizer_->pos(start)));
     return result;
@@ -6585,17 +6840,22 @@ BinASTParser<Tok>::parseTryCatchStatemen
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceTryCatchStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::TryCatchStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Body, BinField::CatchClause }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Body, BinField::CatchClause };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
     ParseNode* body;
     {
         ParseContext::Statement stmt(parseContext_, StatementKind::Try);
         ParseContext::Scope scope(cx_, parseContext_, usedNames_);
         BINJS_TRY(scope.init(parseContext_));
         MOZ_TRY_VAR(body, parseBlock());
 
@@ -6635,17 +6895,22 @@ BinASTParser<Tok>::parseTryFinallyStatem
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceTryFinallyStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::TryFinallyStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Body, BinField::CatchClause, BinField::Finalizer }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::Body, BinField::CatchClause, BinField::Finalizer };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
     ParseNode* body;
     {
         ParseContext::Statement stmt(parseContext_, StatementKind::Try);
         ParseContext::Scope scope(cx_, parseContext_, usedNames_);
         BINJS_TRY(scope.init(parseContext_));
         MOZ_TRY_VAR(body, parseBlock());
 
@@ -6693,17 +6958,22 @@ BinASTParser<Tok>::parseUnaryExpression(
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceUnaryExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::UnaryExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Operator, BinField::Operand }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Operator, BinField::Operand };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(operator_, parseUnaryOperator());
 
 
 
 
@@ -6781,17 +7051,22 @@ BinASTParser<Tok>::parseUpdateExpression
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceUpdateExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::UpdateExpression);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::IsPrefix, BinField::Operator, BinField::Operand }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::IsPrefix, BinField::Operator, BinField::Operand };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(isPrefix, tokenizer_->readBool());
 
 
 
 
@@ -6842,17 +7117,22 @@ BinASTParser<Tok>::parseVariableDeclarat
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceVariableDeclaration(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::VariableDeclaration);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Kind, BinField::Declarators }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Kind, BinField::Declarators };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     AutoVariableDeclarationKind kindGuard(this);
 
 
     BINJS_MOZ_TRY_DECL(kind_, parseVariableDeclarationKind());
     // Restored by `kindGuard`.
     variableDeclarationKind_ = kind_;
 
 
@@ -6905,17 +7185,22 @@ BinASTParser<Tok>::parseVariableDeclarat
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceVariableDeclarator(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::VariableDeclarator);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Binding, BinField::Init }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Binding, BinField::Init };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(binding, parseBinding());
 
 
 
 
@@ -6967,17 +7252,22 @@ BinASTParser<Tok>::parseWhileStatement()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceWhileStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::WhileStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Test, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Test, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
     ParseContext::Statement stmt(parseContext_, StatementKind::WhileLoop);
 
 
     BINJS_MOZ_TRY_DECL(test, parseExpression());
 
 
 
 
@@ -7012,17 +7302,22 @@ BinASTParser<Tok>::parseWithStatement()
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceWithStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::WithStatement);
     CheckRecursionLimit(cx_);
 
-    MOZ_TRY(tokenizer_->checkFields(kind, fields, { BinField::Object, BinField::Body }));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Object, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
 
 
 
     BINJS_MOZ_TRY_DECL(object, parseExpression());
 
 
 
 
--- a/js/src/frontend/binsource/src/main.rs
+++ b/js/src/frontend/binsource/src/main.rs
@@ -1111,25 +1111,32 @@ impl CPPExporter {
 ",
                 kind = kind.to_str(),
                 first_line = first_line,
             ));
         } else {
             let check_fields = if number_of_fields == 0 {
                 format!("MOZ_TRY(tokenizer_->checkFields0(kind, fields));")
             } else {
-                format!("MOZ_TRY(tokenizer_->checkFields(kind, fields, {fields_type_list}));",
-                    fields_type_list = fields_type_list)
+                // The following strategy is designed for old versions of clang.
+                format!("
+#if defined(DEBUG)
+    const BinField expected_fields[{number_of_fields}] = {fields_type_list};
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+",
+                    fields_type_list = fields_type_list,
+                    number_of_fields = number_of_fields)
             };
             buffer.push_str(&format!("{first_line}
 {{
     MOZ_ASSERT(kind == BinKind::{kind});
     CheckRecursionLimit(cx_);
 
-    {check_fields}
+{check_fields}
 {pre}{fields_implem}
 {post}
     return result;
 }}
 
 ",
                 check_fields = check_fields,
                 fields_implem = fields_implem,
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -6,38 +6,35 @@
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(
     this, "env", "@mozilla.org/process/environment;1", "nsIEnvironment");
 ChromeUtils.defineModuleGetter(this, "Log",
     "resource://gre/modules/Log.jsm");
+const {
+  EnvironmentPrefs,
+  MarionettePrefs,
+} = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
 ChromeUtils.defineModuleGetter(this, "Preferences",
     "resource://gre/modules/Preferences.jsm");
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let log = Log.repository.getLogger("Marionette");
   log.addAppender(new Log.DumpAppender());
   return log;
 });
 
-const PREF_ENABLED = "marionette.enabled";
-const PREF_LOG_LEVEL_FALLBACK = "marionette.logging";
-const PREF_LOG_LEVEL = "marionette.log.level";
-const PREF_PORT_FALLBACK = "marionette.defaultPrefs.port";
-const PREF_PORT = "marionette.port";
-const PREF_RECOMMENDED = "marionette.prefs.recommended";
-
-const DEFAULT_LOG_LEVEL = "info";
 const NOTIFY_RUNNING = "remote-active";
 
 // Complements -marionette flag for starting the Marionette server.
 // We also set this if Marionette is running in order to start the server
 // again after a Firefox restart.
 const ENV_ENABLED = "MOZ_MARIONETTE";
+const PREF_ENABLED = "marionette.enabled";
 
 // Besides starting based on existing prefs in a profile and a command
 // line flag, we also support inheriting prefs out of an env var, and to
 // start Marionette that way.
 //
 // This allows marionette prefs to persist when we do a restart into
 // a different profile in order to test things like Firefox refresh.
 // The environment variable itself, if present, is interpreted as a
@@ -269,138 +266,47 @@ const RECOMMENDED_PREFS = new Map([
   // Prevent starting into safe mode after application crashes
   ["toolkit.startup.max_resumed_crashes", -1],
 
 ]);
 
 const isRemote = Services.appinfo.processType ==
     Services.appinfo.PROCESS_TYPE_CONTENT;
 
-const LogLevel = {
-  get(level) {
-    let levels = new Map([
-      ["fatal", Log.Level.Fatal],
-      ["error", Log.Level.Error],
-      ["warn", Log.Level.Warn],
-      ["info", Log.Level.Info],
-      ["config", Log.Level.Config],
-      ["debug", Log.Level.Debug],
-      ["trace", Log.Level.Trace],
-    ]);
-
-    let s = String(level).toLowerCase();
-    if (!levels.has(s)) {
-      return DEFAULT_LOG_LEVEL;
-    }
-    return levels.get(s);
-  },
-};
-
-function getPrefVal(pref) {
-  const {PREF_STRING, PREF_BOOL, PREF_INT, PREF_INVALID} = Ci.nsIPrefBranch;
-
-  let type = Services.prefs.getPrefType(pref);
-  switch (type) {
-    case PREF_STRING:
-      return Services.prefs.getStringPref(pref);
-
-    case PREF_BOOL:
-      return Services.prefs.getBoolPref(pref);
-
-    case PREF_INT:
-      return Services.prefs.getIntPref(pref);
-
-    case PREF_INVALID:
-      return undefined;
-
-    default:
-      throw new TypeError(`Unexpected preference type (${type}) for ${pref}`);
-  }
-}
-
-// Get preference value of |preferred|, falling back to |fallback|
-// if |preferred| is not user-modified and |fallback| exists.
-function getPref(preferred, fallback) {
-  if (!Services.prefs.prefHasUserValue(preferred) &&
-      Services.prefs.getPrefType(fallback) != Ci.nsIPrefBranch.PREF_INVALID) {
-    return getPrefVal(fallback, getPrefVal(preferred));
-  }
-  return getPrefVal(preferred);
-}
-
-// Marionette preferences recently changed names.  This is an abstraction
-// that first looks for the new name, but falls back to using the old name
-// if the new does not exist.
-//
-// This shim can be removed when Firefox 55 ships.
-const prefs = {
-  get port() {
-    return getPref(PREF_PORT, PREF_PORT_FALLBACK);
-  },
-
-  get logLevel() {
-    let s = getPref(PREF_LOG_LEVEL, PREF_LOG_LEVEL_FALLBACK);
-    return LogLevel.get(s);
-  },
-
-  readFromEnvironment(key) {
-    const env = Cc["@mozilla.org/process/environment;1"]
-        .getService(Ci.nsIEnvironment);
-
-    if (env.exists(key)) {
-      let prefs;
-      try {
-        prefs = JSON.parse(env.get(key));
-      } catch (e) {
-        Cu.reportError(
-            "Invalid Marionette preferences in environment; " +
-            "preferences will not have been applied");
-        Cu.reportError(e);
-      }
-
-      if (prefs) {
-        for (let prefName of Object.keys(prefs)) {
-          Preferences.set(prefName, prefs[prefName]);
-        }
-      }
-    }
-  },
-};
-
 class MarionetteMainProcess {
   constructor() {
     this.server = null;
 
     // holds reference to ChromeWindow
     // used to run GFX sanity tests on Windows
     this.gfxWindow = null;
 
     // indicates that all pending window checks have been completed
     // and that we are ready to start the Marionette server
     this.finalUIStartup = false;
 
-    log.level = prefs.logLevel;
+    log.level = MarionettePrefs.logLevel;
 
     this.enabled = env.exists(ENV_ENABLED);
     this.alteredPrefs = new Set();
 
     Services.prefs.addObserver(PREF_ENABLED, this);
     Services.ppmm.addMessageListener("Marionette:IsRunning", this);
   }
 
   get running() {
     return this.server && this.server.alive;
   }
 
   set enabled(value) {
-    Services.prefs.setBoolPref(PREF_ENABLED, value);
+    MarionettePrefs.enabled = value;
   }
 
   get enabled() {
-    return Services.prefs.getBoolPref(PREF_ENABLED);
+    return MarionettePrefs.enabled;
   }
 
   receiveMessage({name}) {
     switch (name) {
       case "Marionette:IsRunning":
         return this.running;
 
       default:
@@ -409,28 +315,30 @@ class MarionetteMainProcess {
     }
   }
 
   observe(subject, topic) {
     log.debug(`Received observer notification ${topic}`);
 
     switch (topic) {
       case "nsPref:changed":
-        if (Services.prefs.getBoolPref(PREF_ENABLED)) {
+        if (this.enabled) {
           this.init();
         } else {
           this.uninit();
         }
         break;
 
       case "profile-after-change":
         Services.obs.addObserver(this, "command-line-startup");
         Services.obs.addObserver(this, "sessionstore-windows-restored");
 
-        prefs.readFromEnvironment(ENV_PRESERVE_PREFS);
+        for (let [pref, value] of EnvironmentPrefs.from(ENV_PRESERVE_PREFS)) {
+          Preferences.set(pref, value);
+        }
         break;
 
       // In safe mode the command line handlers are getting parsed after the
       // safe mode dialog has been closed. To allow Marionette to start
       // earlier, use the CLI startup observer notification for
       // special-cased handlers, which gets fired before the dialog appears.
       case "command-line-startup":
         Services.obs.removeObserver(this, topic);
@@ -516,29 +424,29 @@ class MarionetteMainProcess {
       // ... and for startup tests
       let startupRecorder = Promise.resolve();
       if ("@mozilla.org/test/startuprecorder;1" in Cc) {
         startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"]
             .getService().wrappedJSObject.done;
       }
       await startupRecorder;
 
-      if (Preferences.get(PREF_RECOMMENDED)) {
+      if (MarionettePrefs.recommendedPrefs) {
         for (let [k, v] of RECOMMENDED_PREFS) {
           if (!Preferences.isSet(k)) {
             log.debug(`Setting recommended pref ${k} to ${v}`);
             Preferences.set(k, v);
             this.alteredPrefs.add(k);
           }
         }
       }
 
       try {
         ChromeUtils.import("chrome://marionette/content/server.js");
-        let listener = new server.TCPListener(prefs.port);
+        let listener = new server.TCPListener(MarionettePrefs.port);
         listener.start();
         this.server = listener;
       } catch (e) {
         log.fatal("Remote protocol server failed to start", e);
         this.uninit();
         Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
       }
 
new file mode 100644
--- /dev/null
+++ b/testing/marionette/doc/internals/prefs.rst
@@ -0,0 +1,17 @@
+prefs module
+============
+
+Branch
+------
+.. js:autoclass:: Branch
+  :members:
+
+EnvironmentPrefs
+----------------
+.. js:autoclass:: EnvironmentPrefs
+  :members:
+
+MarionetteBranch
+----------------
+.. js:autoclass:: MarionetteBranch
+  :members:
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -31,16 +31,17 @@ marionette.jar:
   content/session.js (session.js)
   content/transport.js (transport.js)
   content/packets.js (packets.js)
   content/stream-utils.js (stream-utils.js)
   content/reftest.js (reftest.js)
   content/reftest.xul (reftest.xul)
   content/dom.js (dom.js)
   content/format.js (format.js)
+  content/prefs.js (prefs.js)
 #ifdef ENABLE_TESTS
   content/test.xul (chrome/test.xul)
   content/test2.xul (chrome/test2.xul)
   content/test_dialog.dtd (chrome/test_dialog.dtd)
   content/test_dialog.properties (chrome/test_dialog.properties)
   content/test_dialog.xul (chrome/test_dialog.xul)
   content/test_nested_iframe.xul (chrome/test_nested_iframe.xul)
   content/test_anonymous_content.xul (chrome/test_anonymous_content.xul)
new file mode 100644
--- /dev/null
+++ b/testing/marionette/prefs.js
@@ -0,0 +1,245 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/Log.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "env",
+    "@mozilla.org/process/environment;1",
+    "nsIEnvironment");
+
+const {
+  PREF_BOOL,
+  PREF_COMPLEX,
+  PREF_INT,
+  PREF_INVALID,
+  PREF_STRING,
+} = Ci.nsIPrefBranch;
+
+this.EXPORTED_SYMBOLS = [
+  "Branch",
+  "MarionettePrefs",
+];
+
+class Branch {
+  /**
+   * @param {string=} branch
+   *     Preference subtree.  Uses root tree given `null`.
+   */
+  constructor(branch) {
+    this._branch = Services.prefs.getBranch(branch);
+  }
+
+  /**
+   * Gets value of `pref` in its known type.
+   *
+   * @param {string} pref
+   *     Preference name.
+   * @param {?=} fallback
+   *     Fallback value to return if `pref` does not exist.
+   *
+   * @return {(string|boolean|number)}
+   *     Value of `pref`, or the `fallback` value if `pref` does
+   *     not exist.
+   *
+   * @throws {TypeError}
+   *     If `pref` is not a recognised preference and no `fallback`
+   *     value has been provided.
+   */
+  get(pref, fallback = null) {
+    switch (this._branch.getPrefType(pref)) {
+      case PREF_STRING:
+        return this._branch.getStringPref(pref);
+
+      case PREF_BOOL:
+        return this._branch.getBoolPref(pref);
+
+      case PREF_INT:
+        return this._branch.getIntPref(pref);
+
+      case PREF_COMPLEX:
+        throw new TypeError(`Unsupported complex preference: ${pref}`);
+
+      case PREF_INVALID:
+      default:
+        if (fallback != null) {
+          return fallback;
+        }
+        throw new TypeError(`Unrecognised preference: ${pref}`);
+    }
+  }
+
+  /**
+   * Sets the value of `pref`.
+   *
+   * @param {string} pref
+   *     Preference name.
+   * @param {(string|boolean|number)} value
+   *     `pref`'s new value.
+   *
+   * @throws {TypeError}
+   *     If `value` is not the correct type for `pref`.
+   */
+  set(pref, value) {
+    let typ;
+    if (typeof value != "undefined" && value != null) {
+      typ = value.constructor.name;
+    }
+
+    switch (typ) {
+      case "String":
+        // Unicode compliant
+        return this._branch.setStringPref(pref, value);
+
+      case "Boolean":
+        return this._branch.setBoolPref(pref, value);
+
+      case "Number":
+        return this._branch.setIntPref(pref, value);
+
+      default:
+        throw new TypeError(`Illegal preference type value: ${typ}`);
+    }
+  }
+}
+
+/**
+ * Provides shortcuts for lazily getting and setting typed Marionette
+ * preferences.
+ *
+ * Some of Marionette's preferences are stored using primitive values
+ * that internally are represented by complex types.  One such example
+ * is `marionette.log.level` which stores a string such as `info` or
+ * `DEBUG`, and which is represented as `Log.Level`.
+ *
+ * Because we cannot trust the input of many of these preferences,
+ * this class provides abstraction that lets us safely deal with
+ * potentially malformed input.  In the `marionette.log.level` example,
+ * `DEBUG`, `Debug`, and `dEbUg` are considered valid inputs and the
+ * `LogBranch` specialisation deserialises the string value to the
+ * correct `Log.Level` by sanitising the input data first.
+ *
+ * A further complication is that we cannot rely on `Preferences.jsm`
+ * in Marionette.  See https://bugzilla.mozilla.org/show_bug.cgi?id=1357517
+ * for further details.
+ *
+ * Usage::
+ *
+ *     ChromeUtils.import("resource://gre/modules/Log.jsm");
+ *     const {MarionettePrefs} = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
+ *
+ *     const log = Log.repository.getLogger("Marionette");
+ *     log.level = MarionettePrefs.log.level;
+ */
+class MarionetteBranch extends Branch {
+  constructor(branch = "marionette.") {
+    super(branch);
+  }
+
+  /**
+   * The `marionette.enabled` preference.  When it returns true,
+   * this signifies that the Marionette server is running.
+   *
+   * @return {boolean}
+   */
+  get enabled() {
+    return this.get("enabled", false);
+  }
+
+  set enabled(isEnabled) {
+    this.set("enabled", isEnabled);
+  }
+
+  /**
+   * The `marionette.port` preference, detailing which port
+   * the TCP server should listen on.
+   *
+   * @return {number}
+   */
+  get port() {
+    return this.get("port", 2828);
+  }
+
+  set port(newPort) {
+    this.set("port", newPort);
+  }
+
+  /**
+   * Fail-safe return of the current log level from preference
+   * `marionette.log.level`.
+   *
+   * @return {Log.Level}
+   */
+  get logLevel() {
+    switch (this.get("log.level", "info").toLowerCase()) {
+      case "fatal":
+        return Log.Level.Fatal;
+      case "error":
+        return Log.Level.Error;
+      case "warn":
+        return Log.Level.Warn;
+      case "config":
+        return Log.Level.Config;
+      case "debug":
+        return Log.Level.Debug;
+      case "trace":
+        return Log.Level.Trace;
+      case "info":
+      default:
+        return Log.Level.Info;
+    }
+  }
+
+  /**
+   * Gets the `marionette.prefs.recommended` preference, signifying
+   * whether recommended automation preferences will be set when
+   * Marionette is started.
+   *
+   * @return {boolean}
+   */
+  get recommendedPrefs() {
+    return this.get("prefs.recommended", true);
+  }
+}
+
+/** Reads a JSON serialised blob stored in the environment. */
+class EnvironmentPrefs {
+  /**
+   * Reads the environment variable `key` and tries to parse it as
+   * JSON Object, then provides an iterator over its keys and values.
+   *
+   * If the environment variable is not set, this function returns empty.
+   *
+   * @param {string} key
+   *     Environment variable.
+   *
+   * @return {Iterable.<string, (string|boolean|number)>
+   */
+  static* from(key) {
+    if (!env.exists(key)) {
+      return;
+    }
+
+    let prefs;
+    try {
+      prefs = JSON.parse(env.get(key));
+    } catch (e) {
+      throw new TypeError(`Unable to parse prefs from ${key}`, e);
+    }
+
+    for (let prefName of Object.keys(prefs)) {
+      yield [prefName, prefs[prefName]];
+    }
+  }
+}
+
+this.Branch = Branch;
+this.EnvironmentPrefs = EnvironmentPrefs;
+
+// There is a future potential of exposing this as Marionette.prefs.port
+// if we introduce a Marionette.jsm module.
+this.MarionettePrefs = new MarionetteBranch();
--- a/testing/marionette/prefs/marionette.js
+++ b/testing/marionette/prefs/marionette.js
@@ -18,8 +18,14 @@ pref("marionette.debugging.clicktostart"
 // "warn", "info", "config", "debug", and "trace".
 pref("marionette.log.level", "info");
 
 // Port to start Marionette server on.
 pref("marionette.port", 2828);
 
 // Sets recommended automation preferences when Marionette is started.
 pref("marionette.prefs.recommended", true);
+
+// Whether content scripts can be safely reused.
+//
+// Deprecated and scheduled for removal
+// with https://bugzil.la/marionette-window-tracking
+pref("marionette.contentListener", false);
new file mode 100644
--- /dev/null
+++ b/testing/marionette/test/unit/test_prefs.js
@@ -0,0 +1,125 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/Log.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(
+    this, "env", "@mozilla.org/process/environment;1", "nsIEnvironment");
+
+const {
+  Branch,
+  EnvironmentPrefs,
+  MarionettePrefs,
+} = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
+
+function reset() {
+  Services.prefs.setBoolPref("test.bool", false);
+  Services.prefs.setStringPref("test.string", "foo");
+  Services.prefs.setIntPref("test.int", 777);
+}
+
+// Give us something to work with:
+reset();
+
+add_test(function test_Branch_get_root() {
+  let root = new Branch(null);
+  equal(false, root.get("test.bool"));
+  equal("foo", root.get("test.string"));
+  equal(777, root.get("test.int"));
+  Assert.throws(() => root.get("doesnotexist"), /TypeError/);
+
+  run_next_test();
+});
+
+add_test(function test_Branch_get_branch() {
+  let test = new Branch("test.");
+  equal(false, test.get("bool"));
+  equal("foo", test.get("string"));
+  equal(777, test.get("int"));
+  Assert.throws(() => test.get("doesnotexist"), /TypeError/);
+
+  run_next_test();
+});
+
+add_test(function test_Branch_set_root() {
+  let root = new Branch(null);
+
+  try {
+    root.set("test.string", "bar");
+    root.set("test.in", 777);
+    root.set("test.bool", true);
+
+    equal("bar", Services.prefs.getStringPref("test.string"));
+    equal(777, Services.prefs.getIntPref("test.int"));
+    equal(true, Services.prefs.getBoolPref("test.bool"));
+  } finally {
+    reset();
+  }
+
+  run_next_test();
+});
+
+add_test(function test_Branch_set_branch() {
+  let test = new Branch("test.");
+
+  try {
+    test.set("string", "bar");
+    test.set("int", 888);
+    test.set("bool", true);
+
+    equal("bar", Services.prefs.getStringPref("test.string"));
+    equal(888, Services.prefs.getIntPref("test.int"));
+    equal(true, Services.prefs.getBoolPref("test.bool"));
+  } finally {
+    reset();
+  }
+
+  run_next_test();
+});
+
+add_test(function test_EnvironmentPrefs_from() {
+  let prefsTable = {
+    "test.bool": true,
+    "test.int": 888,
+    "test.string": "bar",
+  };
+  env.set("FOO", JSON.stringify(prefsTable));
+
+  try {
+    for (let [key, value] of EnvironmentPrefs.from("FOO")) {
+      equal(prefsTable[key], value);
+    }
+  } finally {
+    env.set("FOO", null);
+  }
+
+  run_next_test();
+});
+
+add_test(function test_MarionettePrefs_getters() {
+  equal(false, MarionettePrefs.enabled);
+  equal(2828, MarionettePrefs.port);
+  equal(Log.Level.Info, MarionettePrefs.logLevel);
+  equal(true, MarionettePrefs.recommendedPrefs);
+
+  run_next_test();
+});
+
+add_test(function test_MarionettePrefs_setters() {
+  try {
+    MarionettePrefs.enabled = true;
+    MarionettePrefs.port = 777;
+    equal(true, MarionettePrefs.enabled);
+    equal(777, MarionettePrefs.port);
+  } finally {
+    Services.prefs.clearUserPref("marionette.enabled");
+    Services.prefs.clearUserPref("marionette.port");
+  }
+
+  run_next_test();
+});
--- a/testing/marionette/test/unit/xpcshell.ini
+++ b/testing/marionette/test/unit/xpcshell.ini
@@ -1,22 +1,21 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-# xpcshell unit tests for Marionette
-
 [DEFAULT]
 skip-if = appname == "thunderbird"
 
 [test_action.js]
 [test_assert.js]
 [test_browser.js]
 [test_cookie.js]
 [test_dom.js]
 [test_element.js]
 [test_error.js]
 [test_evaluate.js]
 [test_format.js]
 [test_message.js]
 [test_navigate.js]
+[test_prefs.js]
 [test_session.js]
 [test_sync.js]
--- a/toolkit/content/tests/chrome/test_bug570192.xul
+++ b/toolkit/content/tests/chrome/test_bug570192.xul
@@ -27,25 +27,26 @@ https://bugzilla.mozilla.org/show_bug.cg
     <div id="content" style="display: none">
     </div>
     <pre id="test">
     </pre>
   </body>
 
   <script type="application/javascript">
     <![CDATA[
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
     addLoadEvent(function() {
       try {
         var content = document.getElementById("content");
-        content.unsafeSetInnerHTML('<textbox newlines="pasteintact" ' +
-          'xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>');
-        var textbox = content.firstChild;
-        ok(textbox, "created the textbox");
-        ok(!textbox.editor, "do we have an editor?");
+        let textbox = document.createElementNS(XUL_NS, "textbox");
+        textbox.setAttribute("newlines", "pasteintact");
+        content.appendChild(textbox);
+        ok(content.firstChild, "created the textbox");
+        ok(!content.firstChild.editor, "do we have an editor?");
       } catch (e) {
         ok(false, "Got an exception: " + e);
       }
       SimpleTest.finish();
     });
     SimpleTest.waitForExplicitFinish();
 
    ]]>