Bug 1553482 - Use BinASTInterfaceAndField in Context;r=arai
☠☠ backed out by 6eb35ce88541 ☠ ☠
authorDavid Teller <dteller@mozilla.com>
Fri, 24 May 2019 09:12:22 +0000
changeset 475349 dea9d20a1162715140612c9b76bf94fb3c6ce452
parent 475348 f6e6533c5db2505978749f998b58fd395edcab1b
child 475350 a296439a25ffcafae35dda80b1ecc99e4e88cad3
push id36060
push usercbrindusan@mozilla.com
push dateFri, 24 May 2019 21:47:21 +0000
treeherdermozilla-central@ac95bdf3c0b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1553482
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1553482 - Use BinASTInterfaceAndField in Context;r=arai Depends on D32295 Differential Revision: https://phabricator.services.mozilla.com/D32395
Cargo.lock
js/src/frontend/BinAST.yaml
js/src/frontend/BinASTParser.cpp
js/src/frontend/BinASTParser.h
js/src/frontend/BinASTParserPerTokenizer.cpp
js/src/frontend/BinASTParserPerTokenizer.h
js/src/frontend/BinASTTokenReaderBase.h
js/src/frontend/binast/src/main.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -224,16 +224,17 @@ dependencies = [
 name = "binary-space-partition"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "binast"
 version = "0.1.1"
 dependencies = [
+ "Inflector 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "binjs_meta 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "webidl 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
--- a/js/src/frontend/BinAST.yaml
+++ b/js/src/frontend/BinAST.yaml
@@ -86,16 +86,17 @@ hpp:
 
       // To generate this file, see the documentation in
       // js/src/frontend/binast/README.md.
 
       #ifndef frontend_BinASTParser_h
       #define frontend_BinASTParser_h
 
       #include "mozilla/Maybe.h"
+      #include "mozilla/Variant.h"
 
       #include "frontend/BCEParserHandle.h"
       #include "frontend/BinASTParserPerTokenizer.h"
       #include "frontend/BinASTToken.h"
       #include "frontend/BinASTTokenReaderContext.h"
       #include "frontend/BinASTTokenReaderMultipart.h"
       #include "frontend/FullParseHandler.h"
       #include "frontend/ParseContext.h"
@@ -117,16 +118,19 @@ hpp:
 
         using Tokenizer = Tok;
 
         using BinASTFields = typename Tokenizer::BinASTFields;
         using AutoList = typename Tokenizer::AutoList;
         using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
         using Chars = typename Tokenizer::Chars;
         using Context = typename BinASTTokenReaderBase::Context;
+        using ListContext = typename BinASTTokenReaderBase::ListContext;
+        using FieldContext = typename BinASTTokenReaderBase::FieldContext;
+        using RootContext = typename BinASTTokenReaderBase::RootContext;
 
        public:
         // Auto-generated types.
         using AssertedDeclaredKind = binast::AssertedDeclaredKind;
         using BinaryOperator = binast::BinaryOperator;
         using CompoundAssignmentOperator = binast::CompoundAssignmentOperator;
         using UnaryOperator = binast::UnaryOperator;
         using UpdateOperator = binast::UpdateOperator;
@@ -632,17 +636,17 @@ Block:
     BINJS_TRY_DECL(result, handler_.newLexicalScope(*bindings, statements));
 
 BreakStatement:
   fields:
     label:
       block:
         replace: |
           RootedAtom label(cx_);
-          MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(fieldContext++));
+          MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(Context(FieldContext(BinASTInterfaceAndField::BreakStatement__Label))));
 
   build: |
     if (label) {
       if (!IsIdentifier(label)) {
         return raiseError("Invalid identifier");
       }
     }
 
@@ -776,17 +780,17 @@ ConditionalExpression:
                    handler_.newConditional(test, consequent, alternate));
 
 ContinueStatement:
   fields:
     label:
       block:
         replace: |
           RootedAtom label(cx_);
-          MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(fieldContext++));
+          MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(Context(FieldContext(BinASTInterfaceAndField::ContinueStatement__Label))));
 
   build: |
     if (label) {
       if (!IsIdentifier(label)) {
         return raiseError("ContinueStatement - Label MUST be an identifier");
       }
     }
 
@@ -1289,17 +1293,17 @@ LiteralPropertyName:
     }
 
 LiteralRegExpExpression:
   fields:
     flags:
       block:
         replace: |
           Chars flags(cx_);
-          MOZ_TRY(tokenizer_->readChars(flags, fieldContext++));
+          MOZ_TRY(tokenizer_->readChars(flags, Context(FieldContext(BinASTInterfaceAndField::BreakStatement__Label))));
   build: |
     RegExpFlags reflags = RegExpFlag::NoFlags;
     for (auto c : flags) {
       if (c == 'g' && !reflags.global()) {
         reflags |= RegExpFlag::Global;
       } else if (c == 'i' && !reflags.ignoreCase()) {
         reflags |= RegExpFlag::IgnoreCase;
       } else if (c == 'm' && !reflags.multiline()) {
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -1405,25 +1405,26 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceArrayExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ArrayExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Elements};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   BINJS_MOZ_TRY_DECL(
-      elements, parseListOfOptionalSpreadElementOrExpression(fieldContext++));
+      elements,
+      parseListOfOptionalSpreadElementOrExpression(Context(
+          FieldContext(BinASTInterfaceAndField::ArrayExpression__Elements))));
 
   if (elements->empty()) {
     elements->setHasNonConstInitializer();
   }
   auto result = elements;
   return result;
 }
 
@@ -1453,28 +1454,33 @@ JS::Result<Ok> BinASTParser<Tok>::parseA
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBlockScope(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedBlockScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::DeclaredNames,
                                           BinASTField::HasDirectEval};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Block;
 
-  MOZ_TRY(parseListOfAssertedDeclaredName(scopeKind, fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool(fieldContext++));
+  MOZ_TRY(parseListOfAssertedDeclaredName(
+      scopeKind,
+      Context(FieldContext(
+          BinASTInterfaceAndField::AssertedBlockScope__DeclaredNames))));
+
+  BINJS_MOZ_TRY_DECL(
+      hasDirectEval,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedBlockScope__HasDirectEval))));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
@@ -1509,29 +1515,32 @@ JS::Result<Ok> BinASTParser<Tok>::parseA
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBoundName(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     AssertedScopeKind scopeKind, const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedBoundName);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Name,
                                           BinASTField::IsCaptured};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const bool allowDuplicateName = false;
 
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(isCaptured, tokenizer_->readBool(fieldContext++));
+  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(Context(FieldContext(
+                        BinASTInterfaceAndField::AssertedBoundName__Name))));
+
+  BINJS_MOZ_TRY_DECL(
+      isCaptured,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedBoundName__IsCaptured))));
   ParseContext::Scope* scope;
   DeclarationKind declKind;
   MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
   MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                        allowDuplicateName));
   auto result = Ok();
   return result;
 }
@@ -1562,28 +1571,33 @@ JS::Result<Ok> BinASTParser<Tok>::parseA
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBoundNamesScope(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedBoundNamesScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::BoundNames,
                                           BinASTField::HasDirectEval};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Catch;
 
-  MOZ_TRY(parseListOfAssertedBoundName(scopeKind, fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool(fieldContext++));
+  MOZ_TRY(parseListOfAssertedBoundName(
+      scopeKind,
+      Context(FieldContext(
+          BinASTInterfaceAndField::AssertedBoundNamesScope__BoundNames))));
+
+  BINJS_MOZ_TRY_DECL(
+      hasDirectEval,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedBoundNamesScope__HasDirectEval))));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
@@ -1619,36 +1633,41 @@ JS::Result<Ok> BinASTParser<Tok>::parseA
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedDeclaredName(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     AssertedScopeKind scopeKind, const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedDeclaredName);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {BinASTField::Name, BinASTField::Kind,
                                           BinASTField::IsCaptured};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const bool allowDuplicateName = false;
 
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(kind_, parseAssertedDeclaredKind(fieldContext++));
+  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(Context(FieldContext(
+                        BinASTInterfaceAndField::AssertedDeclaredName__Name))));
+
+  BINJS_MOZ_TRY_DECL(
+      kind_, parseAssertedDeclaredKind(Context(FieldContext(
+                 BinASTInterfaceAndField::AssertedDeclaredName__Kind))));
   if (kind_ == AssertedDeclaredKind::NonConstLexical) {
     return raiseError("Let is not supported in this preview release");
   }
   if (kind_ == AssertedDeclaredKind::ConstLexical) {
     return raiseError("Const is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(isCaptured, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isCaptured,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedDeclaredName__IsCaptured))));
   ParseContext::Scope* scope;
   DeclarationKind declKind;
   MOZ_TRY(getDeclaredScope(scopeKind, kind_, scope, declKind));
   MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                        allowDuplicateName));
   auto result = Ok();
   return result;
 }
@@ -1681,36 +1700,42 @@ JS::Result<Ok> BinASTParser<Tok>::parseA
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedParameterScope(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     MutableHandle<GCVector<JSAtom*>> positionalParams, const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedParameterScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {BinASTField::ParamNames,
                                           BinASTField::HasDirectEval,
                                           BinASTField::IsSimpleParameterList};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Parameter;
 
   MOZ_TRY(parseListOfAssertedMaybePositionalParameterName(
-      scopeKind, positionalParams, fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool(fieldContext++));
+      scopeKind, positionalParams,
+      Context(FieldContext(
+          BinASTInterfaceAndField::AssertedParameterScope__ParamNames))));
+
+  BINJS_MOZ_TRY_DECL(
+      hasDirectEval,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedParameterScope__HasDirectEval))));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   BINJS_MOZ_TRY_DECL(isSimpleParameterList,
-                     tokenizer_->readBool(fieldContext++));
+                     tokenizer_->readBool(Context(FieldContext(
+                         BinASTInterfaceAndField::
+                             AssertedParameterScope__IsSimpleParameterList))));
   (void)isSimpleParameterList;
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
   }
   auto result = Ok();
   return result;
@@ -1718,29 +1743,34 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedPositionalParameterName(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     AssertedScopeKind scopeKind,
     MutableHandle<GCVector<JSAtom*>> positionalParams, const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedPositionalParameterName);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {BinASTField::Index, BinASTField::Name,
                                           BinASTField::IsCaptured};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   bool allowDuplicateName = !pc_->sc()->strict();
 
-  BINJS_MOZ_TRY_DECL(index, tokenizer_->readUnsignedLong(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      index,
+      tokenizer_->readUnsignedLong(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedPositionalParameterName__Index))));
 
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(fieldContext++));
+  MOZ_TRY_VAR(
+      name,
+      tokenizer_->readIdentifierName(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedPositionalParameterName__Name))));
   // `positionalParams` vector can be shorter than the actual
   // parameter length. Resize on demand.
   // (see also ListOfAssertedMaybePositionalParameterName)
   size_t prevLength = positionalParams.get().length();
   if (index >= prevLength) {
     // This is implementation limit, which is not in the spec.
     if (index >= ARGNO_LIMIT - 1) {
       return raiseError("AssertedPositionalParameterName.index is too big");
@@ -1753,17 +1783,20 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
   }
 
   if (positionalParams.get()[index]) {
     return raiseError(
         "AssertedPositionalParameterName has duplicate entry for the same "
         "index");
   }
   positionalParams.get()[index] = name;
-  BINJS_MOZ_TRY_DECL(isCaptured, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(isCaptured,
+                     tokenizer_->readBool(Context(FieldContext(
+                         BinASTInterfaceAndField::
+                             AssertedPositionalParameterName__IsCaptured))));
   ParseContext::Scope* scope;
   DeclarationKind declKind;
   MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
   MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                        allowDuplicateName));
   auto result = Ok();
   return result;
 }
@@ -1794,28 +1827,33 @@ JS::Result<Ok> BinASTParser<Tok>::parseA
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedScriptGlobalScope(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedScriptGlobalScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::DeclaredNames,
                                           BinASTField::HasDirectEval};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Global;
 
-  MOZ_TRY(parseListOfAssertedDeclaredName(scopeKind, fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool(fieldContext++));
+  MOZ_TRY(parseListOfAssertedDeclaredName(
+      scopeKind,
+      Context(FieldContext(
+          BinASTInterfaceAndField::AssertedScriptGlobalScope__DeclaredNames))));
+
+  BINJS_MOZ_TRY_DECL(
+      hasDirectEval,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedScriptGlobalScope__HasDirectEval))));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
@@ -1850,28 +1888,33 @@ JS::Result<Ok> BinASTParser<Tok>::parseA
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedVarScope(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedVarScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::DeclaredNames,
                                           BinASTField::HasDirectEval};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Var;
 
-  MOZ_TRY(parseListOfAssertedDeclaredName(scopeKind, fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool(fieldContext++));
+  MOZ_TRY(parseListOfAssertedDeclaredName(
+      scopeKind,
+      Context(FieldContext(
+          BinASTInterfaceAndField::AssertedVarScope__DeclaredNames))));
+
+  BINJS_MOZ_TRY_DECL(
+      hasDirectEval,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::AssertedVarScope__HasDirectEval))));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
@@ -1881,49 +1924,54 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceAssignmentExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssignmentExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Binding,
                                           BinASTField::Expression};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(binding, parseAssignmentTarget(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(expression, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      binding, parseAssignmentTarget(Context(FieldContext(
+                   BinASTInterfaceAndField::AssignmentExpression__Binding))));
+
+  BINJS_MOZ_TRY_DECL(
+      expression,
+      parseExpression(Context(FieldContext(
+          BinASTInterfaceAndField::AssignmentExpression__Expression))));
 
   BINJS_TRY_DECL(result, handler_.newAssignment(ParseNodeKind::AssignExpr,
                                                 binding, expression));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceAssignmentTargetIdentifier(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::AssignmentTargetIdentifier);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Name};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(fieldContext++));
+  MOZ_TRY_VAR(name,
+              tokenizer_->readIdentifierName(Context(FieldContext(
+                  BinASTInterfaceAndField::AssignmentTargetIdentifier__Name))));
 
   BINJS_TRY(usedNames_.noteUse(cx_, name, pc_->scriptId(),
                                pc_->innermostScope()->id()));
   BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                           tokenizer_->pos(start), cx_));
   return result;
 }
 
@@ -1936,29 +1984,34 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBinaryExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::BinaryExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::Operator, BinASTField::Left, BinASTField::Right};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(operator_, parseBinaryOperator(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(left, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(right, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      operator_, parseBinaryOperator(Context(FieldContext(
+                     BinASTInterfaceAndField::BinaryExpression__Operator))));
+
+  BINJS_MOZ_TRY_DECL(left,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::BinaryExpression__Left))));
+
+  BINJS_MOZ_TRY_DECL(right,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::BinaryExpression__Right))));
 
   ParseNodeKind pnk;
   switch (operator_) {
     case BinaryOperator::Comma:
       pnk = ParseNodeKind::CommaExpr;
       break;
     case BinaryOperator::LogicalOr:
       pnk = ParseNodeKind::OrExpr;
@@ -2075,25 +2128,25 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBindingIdentifier(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::BindingIdentifier);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Name};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(fieldContext++));
+  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(Context(FieldContext(
+                        BinASTInterfaceAndField::BindingIdentifier__Name))));
 
   BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                           tokenizer_->pos(start), cx_));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBindingWithInitializer(
@@ -2128,51 +2181,53 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBlock(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::Block);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Scope,
                                           BinASTField::Statements};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::Block);
   ParseContext::Scope currentScope(cx_, pc_, usedNames_);
   BINJS_TRY(currentScope.init(pc_));
 
-  MOZ_TRY(parseAssertedBlockScope(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(statements, parseListOfStatement(fieldContext++));
+  MOZ_TRY(parseAssertedBlockScope(
+      Context(FieldContext(BinASTInterfaceAndField::Block__Scope))));
+
+  BINJS_MOZ_TRY_DECL(statements,
+                     parseListOfStatement(Context(FieldContext(
+                         BinASTInterfaceAndField::Block__Statements))));
 
   MOZ_TRY(checkClosedVars(currentScope));
   BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, pc_));
   BINJS_TRY_DECL(result, handler_.newLexicalScope(*bindings, statements));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBreakStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::BreakStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Label};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   RootedAtom label(cx_);
-  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(fieldContext++));
+  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(Context(FieldContext(
+                         BinASTInterfaceAndField::BreakStatement__Label))));
 
   if (label) {
     if (!IsIdentifier(label)) {
       return raiseError("Invalid identifier");
     }
   }
 
   auto validity =
@@ -2195,27 +2250,30 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceCallExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::CallExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Callee,
                                           BinASTField::Arguments};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(callee, parseExpressionOrSuper(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(arguments, parseArguments(fieldContext++));
+  BINJS_MOZ_TRY_DECL(callee,
+                     parseExpressionOrSuper(Context(FieldContext(
+                         BinASTInterfaceAndField::CallExpression__Callee))));
+
+  BINJS_MOZ_TRY_DECL(arguments,
+                     parseArguments(Context(FieldContext(
+                         BinASTInterfaceAndField::CallExpression__Arguments))));
 
   auto op = JSOP_CALL;
 
   // Try to optimize funcall and funapply at the bytecode level
   if (PropertyName* prop = handler_.maybeDottedProperty(callee)) {
     if (prop == cx_->names().apply) {
       op = JSOP_FUNAPPLY;
       if (pc_->isFunctionBox()) {
@@ -2270,35 +2328,38 @@ JS::Result<LexicalScopeNode*> BinASTPars
 }
 
 template <typename Tok>
 JS::Result<LexicalScopeNode*> BinASTParser<Tok>::parseInterfaceCatchClause(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::CatchClause);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::BindingScope, BinASTField::Binding, BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::Catch);
   ParseContext::Scope currentScope(cx_, pc_, usedNames_);
   BINJS_TRY(currentScope.init(pc_));
 
-  MOZ_TRY(parseAssertedBoundNamesScope(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(binding, parseBinding(fieldContext++));
+  MOZ_TRY(parseAssertedBoundNamesScope(Context(
+      FieldContext(BinASTInterfaceAndField::CatchClause__BindingScope))));
+
+  BINJS_MOZ_TRY_DECL(binding,
+                     parseBinding(Context(FieldContext(
+                         BinASTInterfaceAndField::CatchClause__Binding))));
   if (!currentScope.lookupDeclaredName(
           binding->template as<NameNode>().atom())) {
     return raiseError("Missing catch variable in scope");
   }
-  BINJS_MOZ_TRY_DECL(body, parseBlock(fieldContext++));
+  BINJS_MOZ_TRY_DECL(body, parseBlock(Context(FieldContext(
+                               BinASTInterfaceAndField::CatchClause__Body))));
 
   MOZ_TRY(checkClosedVars(currentScope));
   BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, pc_));
   BINJS_TRY_DECL(result, handler_.newLexicalScope(*bindings, body));
   BINJS_TRY(handler_.setupCatchScope(result, binding, body));
   return result;
 }
 
@@ -2320,30 +2381,37 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceCompoundAssignmentExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::CompoundAssignmentExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::Operator, BinASTField::Binding, BinASTField::Expression};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(operator_,
-                     parseCompoundAssignmentOperator(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(binding, parseSimpleAssignmentTarget(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(expression, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      operator_,
+      parseCompoundAssignmentOperator(Context(FieldContext(
+          BinASTInterfaceAndField::CompoundAssignmentExpression__Operator))));
+
+  BINJS_MOZ_TRY_DECL(
+      binding,
+      parseSimpleAssignmentTarget(Context(FieldContext(
+          BinASTInterfaceAndField::CompoundAssignmentExpression__Binding))));
+
+  BINJS_MOZ_TRY_DECL(
+      expression,
+      parseExpression(Context(FieldContext(
+          BinASTInterfaceAndField::CompoundAssignmentExpression__Expression))));
 
   ParseNodeKind pnk;
   switch (operator_) {
     case CompoundAssignmentOperator::PlusAssign:
       pnk = ParseNodeKind::AddAssignExpr;
       break;
     case CompoundAssignmentOperator::MinusAssign:
       pnk = ParseNodeKind::SubAssignExpr;
@@ -2385,51 +2453,60 @@ BinASTParser<Tok>::parseInterfaceCompoun
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceComputedMemberAssignmentTarget(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ComputedMemberAssignmentTarget);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Object,
                                           BinASTField::Expression};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(expression, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      object,
+      parseExpressionOrSuper(Context(FieldContext(
+          BinASTInterfaceAndField::ComputedMemberAssignmentTarget__Object))));
+
+  BINJS_MOZ_TRY_DECL(expression,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::
+                             ComputedMemberAssignmentTarget__Expression))));
 
   BINJS_TRY_DECL(result, handler_.newPropertyByValue(object, expression,
                                                      tokenizer_->offset()));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceComputedMemberExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ComputedMemberExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Object,
                                           BinASTField::Expression};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(expression, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      object, parseExpressionOrSuper(Context(FieldContext(
+                  BinASTInterfaceAndField::ComputedMemberExpression__Object))));
+
+  BINJS_MOZ_TRY_DECL(
+      expression,
+      parseExpression(Context(FieldContext(
+          BinASTInterfaceAndField::ComputedMemberExpression__Expression))));
 
   BINJS_TRY_DECL(result, handler_.newPropertyByValue(object, expression,
                                                      tokenizer_->offset()));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceComputedPropertyName(
@@ -2441,48 +2518,55 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceConditionalExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ConditionalExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::Test, BinASTField::Consequent, BinASTField::Alternate};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(test, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(consequent, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(alternate, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      test, parseExpression(Context(FieldContext(
+                BinASTInterfaceAndField::ConditionalExpression__Test))));
+
+  BINJS_MOZ_TRY_DECL(
+      consequent,
+      parseExpression(Context(FieldContext(
+          BinASTInterfaceAndField::ConditionalExpression__Consequent))));
+
+  BINJS_MOZ_TRY_DECL(
+      alternate,
+      parseExpression(Context(FieldContext(
+          BinASTInterfaceAndField::ConditionalExpression__Alternate))));
 
   BINJS_TRY_DECL(result, handler_.newConditional(test, consequent, alternate));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceContinueStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ContinueStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Label};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   RootedAtom label(cx_);
-  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(fieldContext++));
+  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(Context(FieldContext(
+                         BinASTInterfaceAndField::ContinueStatement__Label))));
 
   if (label) {
     if (!IsIdentifier(label)) {
       return raiseError("ContinueStatement - Label MUST be an identifier");
     }
   }
 
   auto validity =
@@ -2505,27 +2589,29 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDataProperty(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::DataProperty);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Name,
                                           BinASTField::Expression};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(name, parsePropertyName(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(expression, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(name, parsePropertyName(Context(FieldContext(
+                               BinASTInterfaceAndField::DataProperty__Name))));
+
+  BINJS_MOZ_TRY_DECL(expression,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::DataProperty__Expression))));
 
   if (!handler_.isUsableAsObjectPropertyName(name)) {
     return raiseError("DataProperty key kind");
   }
 
   ParseNode* result;
   if (name->template is<NameNode>() &&
       name->template as<NameNode>().atom() == cx_->names().proto) {
@@ -2571,48 +2657,51 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDirective(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::Directive);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::RawValue};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom rawValue(cx_);
-  MOZ_TRY_VAR(rawValue, tokenizer_->readAtom(fieldContext++));
+  MOZ_TRY_VAR(rawValue, tokenizer_->readAtom(Context(FieldContext(
+                            BinASTInterfaceAndField::Directive__RawValue))));
 
   TokenPos pos = tokenizer_->pos(start);
   BINJS_TRY_DECL(result, handler_.newStringLiteral(rawValue, pos));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDoWhileStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::DoWhileStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Test, BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::DoLoop);
 
-  BINJS_MOZ_TRY_DECL(test, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(body, parseStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(test,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::DoWhileStatement__Test))));
+
+  BINJS_MOZ_TRY_DECL(body,
+                     parseStatement(Context(FieldContext(
+                         BinASTInterfaceAndField::DoWhileStatement__Body))));
 
   BINJS_TRY_DECL(
       result, handler_.newDoWhileStatement(body, test, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
@@ -2636,40 +2725,52 @@ BinASTParser<Tok>::parseInterfaceEagerAr
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerFunctionDeclaration(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerFunctionDeclaration);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[6] = {
       BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
       BinASTField::Length,  BinASTField::Directives,  BinASTField::Contents};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Statement;
 
-  BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isAsync,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionDeclaration__IsAsync))));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(isGenerator, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isGenerator,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionDeclaration__IsGenerator))));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(name, parseBindingIdentifier(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(directives, parseListOfDirective(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      name, parseBindingIdentifier(Context(FieldContext(
+                BinASTInterfaceAndField::EagerFunctionDeclaration__Name))));
+
+  BINJS_MOZ_TRY_DECL(
+      length, tokenizer_->readUnsignedLong(Context(FieldContext(
+                  BinASTInterfaceAndField::EagerFunctionDeclaration__Length))));
+
+  BINJS_MOZ_TRY_DECL(
+      directives,
+      parseListOfDirective(Context(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionDeclaration__Directives))));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -2685,57 +2786,71 @@ BinASTParser<Tok>::parseInterfaceEagerFu
   BINJS_TRY(funpc.init());
   pc_->functionScope().useAsVarScope(pc_);
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
-  MOZ_TRY(
-      parseFunctionOrMethodContents(length, &params, &body, fieldContext++));
+  MOZ_TRY(parseFunctionOrMethodContents(
+      length, &params, &body,
+      Context(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionDeclaration__Contents))));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(result,
                      buildFunction(start, kind, name, params, bodyScope));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerFunctionExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerFunctionExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[6] = {
       BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
       BinASTField::Length,  BinASTField::Directives,  BinASTField::Contents};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Expression;
 
-  BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isAsync,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionExpression__IsAsync))));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(isGenerator, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isGenerator,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionExpression__IsGenerator))));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(name, parseOptionalBindingIdentifier(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(directives, parseListOfDirective(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      name, parseOptionalBindingIdentifier(Context(FieldContext(
+                BinASTInterfaceAndField::EagerFunctionExpression__Name))));
+
+  BINJS_MOZ_TRY_DECL(
+      length, tokenizer_->readUnsignedLong(Context(FieldContext(
+                  BinASTInterfaceAndField::EagerFunctionExpression__Length))));
+
+  BINJS_MOZ_TRY_DECL(
+      directives,
+      parseListOfDirective(Context(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionExpression__Directives))));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -2751,49 +2866,53 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   BINJS_TRY(funpc.init());
   pc_->functionScope().useAsVarScope(pc_);
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
-  MOZ_TRY(
-      parseFunctionExpressionContents(length, &params, &body, fieldContext++));
+  MOZ_TRY(parseFunctionExpressionContents(
+      length, &params, &body,
+      Context(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionExpression__Contents))));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(result,
                      buildFunction(start, kind, name, params, bodyScope));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerGetter(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerGetter);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::Name, BinASTField::Directives, BinASTField::Contents};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Setter;
   const bool isGenerator = false;
   const bool isAsync = false;
   const auto accessorType = AccessorType::Getter;
   const uint32_t length = 0;
 
-  BINJS_MOZ_TRY_DECL(name, parsePropertyName(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(directives, parseListOfDirective(fieldContext++));
+  BINJS_MOZ_TRY_DECL(name, parsePropertyName(Context(FieldContext(
+                               BinASTInterfaceAndField::EagerGetter__Name))));
+
+  BINJS_MOZ_TRY_DECL(directives,
+                     parseListOfDirective(Context(FieldContext(
+                         BinASTInterfaceAndField::EagerGetter__Directives))));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -2809,17 +2928,19 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   BINJS_TRY(funpc.init());
   pc_->functionScope().useAsVarScope(pc_);
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
-  MOZ_TRY(parseGetterContents(length, &params, &body, fieldContext++));
+  MOZ_TRY(parseGetterContents(
+      length, &params, &body,
+      Context(FieldContext(BinASTInterfaceAndField::EagerGetter__Contents))));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(method,
                      buildFunction(start, kind, name, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
@@ -2827,41 +2948,49 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerMethod(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerMethod);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[6] = {
       BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
       BinASTField::Length,  BinASTField::Directives,  BinASTField::Contents};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Method;
   const auto accessorType = AccessorType::None;
 
-  BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(isAsync,
+                     tokenizer_->readBool(Context(FieldContext(
+                         BinASTInterfaceAndField::EagerMethod__IsAsync))));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(isGenerator, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(isGenerator,
+                     tokenizer_->readBool(Context(FieldContext(
+                         BinASTInterfaceAndField::EagerMethod__IsGenerator))));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(name, parsePropertyName(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(directives, parseListOfDirective(fieldContext++));
+  BINJS_MOZ_TRY_DECL(name, parsePropertyName(Context(FieldContext(
+                               BinASTInterfaceAndField::EagerMethod__Name))));
+
+  BINJS_MOZ_TRY_DECL(
+      length, tokenizer_->readUnsignedLong(Context(
+                  FieldContext(BinASTInterfaceAndField::EagerMethod__Length))));
+
+  BINJS_MOZ_TRY_DECL(directives,
+                     parseListOfDirective(Context(FieldContext(
+                         BinASTInterfaceAndField::EagerMethod__Directives))));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -2877,18 +3006,19 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   BINJS_TRY(funpc.init());
   pc_->functionScope().useAsVarScope(pc_);
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
-  MOZ_TRY(
-      parseFunctionOrMethodContents(length, &params, &body, fieldContext++));
+  MOZ_TRY(parseFunctionOrMethodContents(
+      length, &params, &body,
+      Context(FieldContext(BinASTInterfaceAndField::EagerMethod__Contents))));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(method,
                      buildFunction(start, kind, name, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
@@ -2896,34 +3026,38 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerSetter(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerSetter);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[4] = {
       BinASTField::Name, BinASTField::Length, BinASTField::Directives,
       BinASTField::Contents};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Setter;
   const bool isGenerator = false;
   const bool isAsync = false;
   const auto accessorType = AccessorType::Setter;
 
-  BINJS_MOZ_TRY_DECL(name, parsePropertyName(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(directives, parseListOfDirective(fieldContext++));
+  BINJS_MOZ_TRY_DECL(name, parsePropertyName(Context(FieldContext(
+                               BinASTInterfaceAndField::EagerSetter__Name))));
+
+  BINJS_MOZ_TRY_DECL(
+      length, tokenizer_->readUnsignedLong(Context(
+                  FieldContext(BinASTInterfaceAndField::EagerSetter__Length))));
+
+  BINJS_MOZ_TRY_DECL(directives,
+                     parseListOfDirective(Context(FieldContext(
+                         BinASTInterfaceAndField::EagerSetter__Directives))));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -2939,17 +3073,19 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   BINJS_TRY(funpc.init());
   pc_->functionScope().useAsVarScope(pc_);
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
-  MOZ_TRY(parseSetterContents(length, &params, &body, fieldContext++));
+  MOZ_TRY(parseSetterContents(
+      length, &params, &body,
+      Context(FieldContext(BinASTInterfaceAndField::EagerSetter__Contents))));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(method,
                      buildFunction(start, kind, name, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
@@ -2969,48 +3105,53 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceExpressionStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ExpressionStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Expression};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(expression, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      expression,
+      parseExpression(Context(FieldContext(
+          BinASTInterfaceAndField::ExpressionStatement__Expression))));
 
   BINJS_TRY_DECL(result,
                  handler_.newExprStatement(expression, tokenizer_->offset()));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForInOfBinding(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ForInOfBinding);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Kind,
                                           BinASTField::Binding};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   AutoVariableDeclarationKind kindGuard(this);
 
-  BINJS_MOZ_TRY_DECL(kind_, parseVariableDeclarationKind(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(binding, parseBinding(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      kind_, parseVariableDeclarationKind(Context(
+                 FieldContext(BinASTInterfaceAndField::ForInOfBinding__Kind))));
+
+  BINJS_MOZ_TRY_DECL(binding,
+                     parseBinding(Context(FieldContext(
+                         BinASTInterfaceAndField::ForInOfBinding__Binding))));
 
   // Restored by `kindGuard`.
   variableDeclarationKind_ = kind_;
   MOZ_TRY(
       checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
   ParseNodeKind pnk;
   switch (kind_) {
     case VariableDeclarationKind::Var:
@@ -3028,37 +3169,41 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForInStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ForInStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {BinASTField::Left, BinASTField::Right,
                                           BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, 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_, pc_, usedNames_);
   BINJS_TRY(scope.init(pc_));
 
-  BINJS_MOZ_TRY_DECL(left,
-                     parseForInOfBindingOrAssignmentTarget(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(right, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(body, parseStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      left, parseForInOfBindingOrAssignmentTarget(Context(
+                FieldContext(BinASTInterfaceAndField::ForInStatement__Left))));
+
+  BINJS_MOZ_TRY_DECL(right,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::ForInStatement__Right))));
+
+  BINJS_MOZ_TRY_DECL(
+      body, parseStatement(Context(
+                FieldContext(BinASTInterfaceAndField::ForInStatement__Body))));
 
   BINJS_TRY_DECL(forHead,
                  handler_.newForInOrOfHead(ParseNodeKind::ForIn, left, right,
                                            tokenizer_->pos(start)));
   ParseNode* result;
   BINJS_TRY_VAR(result, handler_.newForStatement(start, forHead, body,
                                                  /* iflags = */ 0));
 
@@ -3078,40 +3223,44 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ForStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[4] = {BinASTField::Init, BinASTField::Test,
                                           BinASTField::Update,
                                           BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, 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_, pc_, usedNames_);
   BINJS_TRY(scope.init(pc_));
 
   BINJS_MOZ_TRY_DECL(
-      init, parseOptionalVariableDeclarationOrExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(test, parseOptionalExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(update, parseOptionalExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(body, parseStatement(fieldContext++));
+      init, parseOptionalVariableDeclarationOrExpression(Context(
+                FieldContext(BinASTInterfaceAndField::ForStatement__Init))));
+
+  BINJS_MOZ_TRY_DECL(test, parseOptionalExpression(Context(FieldContext(
+                               BinASTInterfaceAndField::ForStatement__Test))));
+
+  BINJS_MOZ_TRY_DECL(update,
+                     parseOptionalExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::ForStatement__Update))));
+
+  BINJS_MOZ_TRY_DECL(body, parseStatement(Context(FieldContext(
+                               BinASTInterfaceAndField::ForStatement__Body))));
 
   BINJS_TRY_DECL(
       forHead, handler_.newForHead(init, test, update, tokenizer_->pos(start)));
   ParseNode* result;
   BINJS_TRY_VAR(result, handler_.newForStatement(start, forHead, body,
                                                  /* iflags = */ 0));
 
   if (!scope.isEmpty()) {
@@ -3147,27 +3296,30 @@ JS::Result<ListNode*> BinASTParser<Tok>:
 }
 
 template <typename Tok>
 JS::Result<ListNode*> BinASTParser<Tok>::parseInterfaceFormalParameters(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::FormalParameters);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Items,
                                           BinASTField::Rest};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(items, parseListOfParameter(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(rest, parseOptionalBinding(fieldContext++));
+  BINJS_MOZ_TRY_DECL(items,
+                     parseListOfParameter(Context(FieldContext(
+                         BinASTInterfaceAndField::FormalParameters__Items))));
+
+  BINJS_MOZ_TRY_DECL(rest,
+                     parseOptionalBinding(Context(FieldContext(
+                         BinASTInterfaceAndField::FormalParameters__Rest))));
 
   auto result = items;
   if (rest) {
     return raiseError(
         "Rest parameter is not supported in this preview release");
   }
   return result;
 }
@@ -3205,49 +3357,63 @@ JS::Result<Ok> BinASTParser<Tok>::parseF
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceFunctionExpressionContents(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::FunctionExpressionContents);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[6] = {BinASTField::IsFunctionNameCaptured,
                                           BinASTField::IsThisCaptured,
                                           BinASTField::ParameterScope,
                                           BinASTField::Params,
                                           BinASTField::BodyScope,
                                           BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(isFunctionNameCaptured,
-                     tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isFunctionNameCaptured,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::
+              FunctionExpressionContents__IsFunctionNameCaptured))));
   // Per spec, isFunctionNameCaptured can be true for anonymous
   // function.  Check isFunctionNameCaptured only for named
   // function.
   if (pc_->functionBox()->isNamedLambda() &&
       isFunctionNameCaptured) {
     captureFunctionName();
   }
-  BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(isThisCaptured,
+                     tokenizer_->readBool(Context(FieldContext(
+                         BinASTInterfaceAndField::
+                             FunctionExpressionContents__IsThisCaptured))));
   // TODO: Use this in BinASTParser::buildFunction.
   (void)isThisCaptured;
   Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
-  MOZ_TRY(parseAssertedParameterScope(&positionalParams, fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(params, parseFormalParameters(fieldContext++));
+  MOZ_TRY(parseAssertedParameterScope(
+      &positionalParams,
+      Context(FieldContext(BinASTInterfaceAndField::
+                               FunctionExpressionContents__ParameterScope))));
+
+  BINJS_MOZ_TRY_DECL(
+      params,
+      parseFormalParameters(Context(FieldContext(
+          BinASTInterfaceAndField::FunctionExpressionContents__Params))));
   MOZ_TRY(checkFunctionLength(funLength));
   MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
-  MOZ_TRY(parseAssertedVarScope(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(body, parseFunctionBody(fieldContext++));
+  MOZ_TRY(parseAssertedVarScope(Context(FieldContext(
+      BinASTInterfaceAndField::FunctionExpressionContents__BodyScope))));
+
+  BINJS_MOZ_TRY_DECL(
+      body, parseFunctionBody(Context(FieldContext(
+                BinASTInterfaceAndField::FunctionExpressionContents__Body))));
 
   *paramsOut = params;
   *bodyOut = body;
   auto result = Ok();
   return result;
 }
 
 /*
@@ -3282,37 +3448,47 @@ JS::Result<Ok> BinASTParser<Tok>::parseF
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceFunctionOrMethodContents(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::FunctionOrMethodContents);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[5] = {
       BinASTField::IsThisCaptured, BinASTField::ParameterScope,
       BinASTField::Params, BinASTField::BodyScope, BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isThisCaptured,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::FunctionOrMethodContents__IsThisCaptured))));
   // TODO: Use this in BinASTParser::buildFunction.
   (void)isThisCaptured;
   Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
-  MOZ_TRY(parseAssertedParameterScope(&positionalParams, fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(params, parseFormalParameters(fieldContext++));
+  MOZ_TRY(parseAssertedParameterScope(
+      &positionalParams,
+      Context(FieldContext(
+          BinASTInterfaceAndField::FunctionOrMethodContents__ParameterScope))));
+
+  BINJS_MOZ_TRY_DECL(
+      params, parseFormalParameters(Context(FieldContext(
+                  BinASTInterfaceAndField::FunctionOrMethodContents__Params))));
   MOZ_TRY(checkFunctionLength(funLength));
   MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
-  MOZ_TRY(parseAssertedVarScope(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(body, parseFunctionBody(fieldContext++));
+  MOZ_TRY(parseAssertedVarScope(Context(FieldContext(
+      BinASTInterfaceAndField::FunctionOrMethodContents__BodyScope))));
+
+  BINJS_MOZ_TRY_DECL(
+      body, parseFunctionBody(Context(FieldContext(
+                BinASTInterfaceAndField::FunctionOrMethodContents__Body))));
 
   *paramsOut = params;
   *bodyOut = body;
   auto result = Ok();
   return result;
 }
 
 /*
@@ -3346,31 +3522,36 @@ JS::Result<Ok> BinASTParser<Tok>::parseG
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceGetterContents(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::GetterContents);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::IsThisCaptured, BinASTField::BodyScope, BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isThisCaptured,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::GetterContents__IsThisCaptured))));
   // TODO: Use this in BinASTParser::buildFunction.
   (void)isThisCaptured;
-  MOZ_TRY(parseAssertedVarScope(fieldContext++));
+  MOZ_TRY(parseAssertedVarScope(Context(
+      FieldContext(BinASTInterfaceAndField::GetterContents__BodyScope))));
 
   BINJS_TRY_DECL(params, handler_.newParamsBody(tokenizer_->pos(start)));
-  BINJS_MOZ_TRY_DECL(body, parseFunctionBody(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      body, parseFunctionBody(Context(
+                FieldContext(BinASTInterfaceAndField::GetterContents__Body))));
 
   *paramsOut = params;
   *bodyOut = body;
   auto result = Ok();
   return result;
 }
 
 /*
@@ -3398,79 +3579,85 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceIdentifierExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::IdentifierExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Name};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(fieldContext++));
+  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(Context(FieldContext(
+                        BinASTInterfaceAndField::IdentifierExpression__Name))));
 
   BINJS_TRY(usedNames_.noteUse(cx_, name, pc_->scriptId(),
                                pc_->innermostScope()->id()));
   BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                           tokenizer_->pos(start), cx_));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceIfStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::IfStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::Test, BinASTField::Consequent, BinASTField::Alternate};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(test, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(consequent, parseStatement(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(alternate, parseOptionalStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(test, parseExpression(Context(FieldContext(
+                               BinASTInterfaceAndField::IfStatement__Test))));
+
+  BINJS_MOZ_TRY_DECL(consequent,
+                     parseStatement(Context(FieldContext(
+                         BinASTInterfaceAndField::IfStatement__Consequent))));
+
+  BINJS_MOZ_TRY_DECL(alternate,
+                     parseOptionalStatement(Context(FieldContext(
+                         BinASTInterfaceAndField::IfStatement__Alternate))));
 
   BINJS_TRY_DECL(result,
                  handler_.newIfStatement(start, test, consequent, alternate));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLabelledStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::LabelledStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Label,
                                           BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom label(cx_);
-  MOZ_TRY_VAR(label, tokenizer_->readAtom(fieldContext++));
+  MOZ_TRY_VAR(label, tokenizer_->readAtom(Context(FieldContext(
+                         BinASTInterfaceAndField::LabelledStatement__Label))));
   if (!IsIdentifier(label)) {
     return raiseError("Invalid identifier");
   }
   ParseContext::LabelStatement stmt(pc_, label);
-  BINJS_MOZ_TRY_DECL(body, parseStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(body,
+                     parseStatement(Context(FieldContext(
+                         BinASTInterfaceAndField::LabelledStatement__Body))));
 
   BINJS_TRY_DECL(result, handler_.newLabeledStatement(label->asPropertyName(),
                                                       body, start));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
@@ -3493,44 +3680,58 @@ BinASTParser<Tok>::parseInterfaceLazyArr
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyFunctionDeclaration(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::LazyFunctionDeclaration);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[7] = {
       BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
       BinASTField::Length,  BinASTField::Directives,  BinASTField::ContentsSkip,
       BinASTField::Contents};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Statement;
 
-  BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isAsync,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionDeclaration__IsAsync))));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(isGenerator, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isGenerator,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionDeclaration__IsGenerator))));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(name, parseBindingIdentifier(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(directives, parseListOfDirective(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(contentsSkip,
-                     tokenizer_->readSkippableSubTree(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      name, parseBindingIdentifier(Context(FieldContext(
+                BinASTInterfaceAndField::LazyFunctionDeclaration__Name))));
+
+  BINJS_MOZ_TRY_DECL(
+      length, tokenizer_->readUnsignedLong(Context(FieldContext(
+                  BinASTInterfaceAndField::LazyFunctionDeclaration__Length))));
+
+  BINJS_MOZ_TRY_DECL(
+      directives,
+      parseListOfDirective(Context(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionDeclaration__Directives))));
+
+  BINJS_MOZ_TRY_DECL(
+      contentsSkip,
+      tokenizer_->readSkippableSubTree(Context(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionDeclaration__ContentsSkip))));
   // Don't parse the contents until we delazify.
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax, name));
@@ -3561,44 +3762,57 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyFunctionExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::LazyFunctionExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[7] = {
       BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
       BinASTField::Length,  BinASTField::Directives,  BinASTField::ContentsSkip,
       BinASTField::Contents};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Expression;
 
-  BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isAsync, tokenizer_->readBool(Context(FieldContext(
+                   BinASTInterfaceAndField::LazyFunctionExpression__IsAsync))));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(isGenerator, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isGenerator,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionExpression__IsGenerator))));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(name, parseOptionalBindingIdentifier(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(directives, parseListOfDirective(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(contentsSkip,
-                     tokenizer_->readSkippableSubTree(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      name, parseOptionalBindingIdentifier(Context(FieldContext(
+                BinASTInterfaceAndField::LazyFunctionExpression__Name))));
+
+  BINJS_MOZ_TRY_DECL(
+      length, tokenizer_->readUnsignedLong(Context(FieldContext(
+                  BinASTInterfaceAndField::LazyFunctionExpression__Length))));
+
+  BINJS_MOZ_TRY_DECL(
+      directives,
+      parseListOfDirective(Context(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionExpression__Directives))));
+
+  BINJS_MOZ_TRY_DECL(
+      contentsSkip,
+      tokenizer_->readSkippableSubTree(Context(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionExpression__ContentsSkip))));
   // Don't parse the contents until we delazify.
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax, name));
@@ -3654,24 +3868,25 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralBooleanExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralBooleanExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Value};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(value, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      value, tokenizer_->readBool(Context(FieldContext(
+                 BinASTInterfaceAndField::LiteralBooleanExpression__Value))));
 
   BINJS_TRY_DECL(result,
                  handler_.newBooleanLiteral(value, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
@@ -3697,45 +3912,47 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralNumericExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralNumericExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Value};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(value, tokenizer_->readDouble(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      value, tokenizer_->readDouble(Context(FieldContext(
+                 BinASTInterfaceAndField::LiteralNumericExpression__Value))));
 
   BINJS_TRY_DECL(result, handler_.newNumber(value, DecimalPoint::HasDecimal,
                                             tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralPropertyName(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralPropertyName);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Value};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom value(cx_);
-  MOZ_TRY_VAR(value, tokenizer_->readAtom(fieldContext++));
+  MOZ_TRY_VAR(value,
+              tokenizer_->readAtom(Context(FieldContext(
+                  BinASTInterfaceAndField::LiteralPropertyName__Value))));
 
   ParseNode* result;
   uint32_t index;
   if (value->isIndex(&index)) {
     BINJS_TRY_VAR(result,
                   handler_.newNumber(index, NoDecimal,
                                      TokenPos(start, tokenizer_->offset())));
   } else {
@@ -3746,28 +3963,31 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralRegExpExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralRegExpExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Pattern,
                                           BinASTField::Flags};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom pattern(cx_);
-  MOZ_TRY_VAR(pattern, tokenizer_->readAtom(fieldContext++));
+  MOZ_TRY_VAR(pattern,
+              tokenizer_->readAtom(Context(FieldContext(
+                  BinASTInterfaceAndField::LiteralRegExpExpression__Pattern))));
   Chars flags(cx_);
-  MOZ_TRY(tokenizer_->readChars(flags, fieldContext++));
+  MOZ_TRY(tokenizer_->readChars(
+      flags,
+      Context(FieldContext(BinASTInterfaceAndField::BreakStatement__Label))));
 
   RegExpFlags reflags = RegExpFlag::NoFlags;
   for (auto c : flags) {
     if (c == 'g' && !reflags.global()) {
       reflags |= RegExpFlag::Global;
     } else if (c == 'i' && !reflags.ignoreCase()) {
       reflags |= RegExpFlag::IgnoreCase;
     } else if (c == 'm' && !reflags.multiline()) {
@@ -3791,25 +4011,26 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralStringExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralStringExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Value};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom value(cx_);
-  MOZ_TRY_VAR(value, tokenizer_->readAtom(fieldContext++));
+  MOZ_TRY_VAR(value,
+              tokenizer_->readAtom(Context(FieldContext(
+                  BinASTInterfaceAndField::LiteralStringExpression__Value))));
 
   BINJS_TRY_DECL(result,
                  handler_.newStringLiteral(value, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceModule(
@@ -3820,27 +4041,30 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceNewExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::NewExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Callee,
                                           BinASTField::Arguments};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(callee, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(arguments, parseArguments(fieldContext++));
+  BINJS_MOZ_TRY_DECL(callee,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::NewExpression__Callee))));
+
+  BINJS_MOZ_TRY_DECL(arguments,
+                     parseArguments(Context(FieldContext(
+                         BinASTInterfaceAndField::NewExpression__Arguments))));
 
   BINJS_TRY_DECL(result,
                  handler_.newNewExpression(tokenizer_->pos(start).begin, callee,
                                            arguments, /* isSpread = */ false));
   return result;
 }
 
 template <typename Tok>
@@ -3870,74 +4094,80 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceObjectExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ObjectExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Properties};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(properties, parseListOfObjectProperty(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      properties, parseListOfObjectProperty(Context(FieldContext(
+                      BinASTInterfaceAndField::ObjectExpression__Properties))));
 
   auto result = properties;
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceReturnStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ReturnStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Expression};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   if (!pc_->isFunctionBox()) {
     // Return statements are permitted only inside functions.
     return raiseInvalidKind("Toplevel Statement", kind);
   }
 
   pc_->functionBox()->usesReturn = true;
 
-  BINJS_MOZ_TRY_DECL(expression, parseOptionalExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      expression, parseOptionalExpression(Context(FieldContext(
+                      BinASTInterfaceAndField::ReturnStatement__Expression))));
 
   BINJS_TRY_DECL(
       result, handler_.newReturnStatement(expression, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceScript(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::Script);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::Scope, BinASTField::Directives, BinASTField::Statements};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  MOZ_TRY(parseAssertedScriptGlobalScope(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(directives, parseListOfDirective(fieldContext++));
+  MOZ_TRY(parseAssertedScriptGlobalScope(
+      Context(FieldContext(BinASTInterfaceAndField::Script__Scope))));
+
+  BINJS_MOZ_TRY_DECL(directives,
+                     parseListOfDirective(Context(FieldContext(
+                         BinASTInterfaceAndField::Script__Directives))));
   forceStrictIfNecessary(pc_->sc(), directives);
-  BINJS_MOZ_TRY_DECL(statements, parseListOfStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(statements,
+                     parseListOfStatement(Context(FieldContext(
+                         BinASTInterfaceAndField::Script__Statements))));
 
   MOZ_TRY(checkClosedVars(pc_->varScope()));
   MOZ_TRY(prependDirectivesToBody(/* body = */ statements, directives));
   auto result = statements;
   return result;
 }
 
 /*
@@ -3973,59 +4203,70 @@ JS::Result<Ok> BinASTParser<Tok>::parseS
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceSetterContents(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::SetterContents);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[5] = {
       BinASTField::IsThisCaptured, BinASTField::ParameterScope,
       BinASTField::Param, BinASTField::BodyScope, BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isThisCaptured,
+      tokenizer_->readBool(Context(FieldContext(
+          BinASTInterfaceAndField::SetterContents__IsThisCaptured))));
   // TODO: Use this in BinASTParser::buildFunction.
   (void)isThisCaptured;
   Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
-  MOZ_TRY(parseAssertedParameterScope(&positionalParams, fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(param, parseParameter(fieldContext++));
+  MOZ_TRY(parseAssertedParameterScope(
+      &positionalParams,
+      Context(FieldContext(
+          BinASTInterfaceAndField::SetterContents__ParameterScope))));
+
+  BINJS_MOZ_TRY_DECL(param,
+                     parseParameter(Context(FieldContext(
+                         BinASTInterfaceAndField::SetterContents__Param))));
   BINJS_TRY_DECL(params, handler_.newParamsBody(param->pn_pos));
   handler_.addList(params, param);
   MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
-  MOZ_TRY(parseAssertedVarScope(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(body, parseFunctionBody(fieldContext++));
+  MOZ_TRY(parseAssertedVarScope(Context(
+      FieldContext(BinASTInterfaceAndField::SetterContents__BodyScope))));
+
+  BINJS_MOZ_TRY_DECL(
+      body, parseFunctionBody(Context(
+                FieldContext(BinASTInterfaceAndField::SetterContents__Body))));
 
   *paramsOut = params;
   *bodyOut = body;
   auto result = Ok();
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceShorthandProperty(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ShorthandProperty);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Name};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(name, parseIdentifierExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(name,
+                     parseIdentifierExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::ShorthandProperty__Name))));
 
   MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
   MOZ_ASSERT(!handler_.isUsableAsObjectPropertyName(name));
   BINJS_TRY_DECL(propName, handler_.newObjectLiteralPropertyName(
                                name->template as<NameNode>().name(),
                                tokenizer_->pos(start)));
 
   BINJS_TRY_DECL(result,
@@ -4043,60 +4284,69 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceStaticMemberAssignmentTarget(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::StaticMemberAssignmentTarget);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Object,
                                           BinASTField::Property};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   size_t nameStart;
 
-  BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      object,
+      parseExpressionOrSuper(Context(FieldContext(
+          BinASTInterfaceAndField::StaticMemberAssignmentTarget__Object))));
 
   RootedAtom property(cx_);
   {
     nameStart = tokenizer_->offset();
-    MOZ_TRY_VAR(property, tokenizer_->readPropertyKey(fieldContext++));
+    MOZ_TRY_VAR(
+        property,
+        tokenizer_->readPropertyKey(Context(FieldContext(
+            BinASTInterfaceAndField::StaticMemberAssignmentTarget__Property))));
   }
 
   BINJS_TRY_DECL(name, handler_.newPropertyName(property->asPropertyName(),
                                                 tokenizer_->pos(nameStart)));
   BINJS_TRY_DECL(result, handler_.newPropertyAccess(object, name));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceStaticMemberExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::StaticMemberExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Object,
                                           BinASTField::Property};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   size_t nameStart;
 
-  BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      object, parseExpressionOrSuper(Context(FieldContext(
+                  BinASTInterfaceAndField::StaticMemberExpression__Object))));
 
   RootedAtom property(cx_);
   {
     nameStart = tokenizer_->offset();
-    MOZ_TRY_VAR(property, tokenizer_->readPropertyKey(fieldContext++));
+    MOZ_TRY_VAR(
+        property,
+        tokenizer_->readPropertyKey(Context(FieldContext(
+            BinASTInterfaceAndField::StaticMemberExpression__Property))));
   }
 
   BINJS_TRY_DECL(name, handler_.newPropertyName(property->asPropertyName(),
                                                 tokenizer_->pos(nameStart)));
   BINJS_TRY_DECL(result, handler_.newPropertyAccess(object, name));
   return result;
 }
 
@@ -4134,27 +4384,29 @@ JS::Result<CaseClause*> BinASTParser<Tok
 }
 
 template <typename Tok>
 JS::Result<CaseClause*> BinASTParser<Tok>::parseInterfaceSwitchCase(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::SwitchCase);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Test,
                                           BinASTField::Consequent};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(test, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(consequent, parseListOfStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(test, parseExpression(Context(FieldContext(
+                               BinASTInterfaceAndField::SwitchCase__Test))));
+
+  BINJS_MOZ_TRY_DECL(consequent,
+                     parseListOfStatement(Context(FieldContext(
+                         BinASTInterfaceAndField::SwitchCase__Consequent))));
 
   BINJS_TRY_DECL(result, handler_.newCaseOrDefault(start, test, consequent));
   return result;
 }
 
 /*
  interface SwitchDefault : Node {
     FrozenArray<Statement> consequent;
@@ -4180,76 +4432,92 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceSwitchDefault(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::SwitchDefault);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Consequent};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(consequent, parseListOfStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(consequent,
+                     parseListOfStatement(Context(FieldContext(
+                         BinASTInterfaceAndField::SwitchDefault__Consequent))));
 
   BINJS_TRY_DECL(result, handler_.newCaseOrDefault(start, nullptr, consequent));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceSwitchStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::SwitchStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Discriminant,
                                           BinASTField::Cases};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(discriminant, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      discriminant,
+      parseExpression(Context(FieldContext(
+          BinASTInterfaceAndField::SwitchStatement__Discriminant))));
   ParseContext::Statement stmt(pc_, StatementKind::Switch);
-  BINJS_MOZ_TRY_DECL(cases, parseListOfSwitchCase(fieldContext++));
+  BINJS_MOZ_TRY_DECL(cases,
+                     parseListOfSwitchCase(Context(FieldContext(
+                         BinASTInterfaceAndField::SwitchStatement__Cases))));
 
   BINJS_TRY_DECL(scope, handler_.newLexicalScope(nullptr, cases));
   BINJS_TRY_DECL(result, handler_.newSwitchStatement(start, discriminant, scope,
                                                      /* hasDefault = */ false));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceSwitchStatementWithDefault(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::SwitchStatementWithDefault);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[4] = {
       BinASTField::Discriminant, BinASTField::PreDefaultCases,
       BinASTField::DefaultCase, BinASTField::PostDefaultCases};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(discriminant, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      discriminant,
+      parseExpression(Context(FieldContext(
+          BinASTInterfaceAndField::SwitchStatementWithDefault__Discriminant))));
   ParseContext::Statement stmt(pc_, StatementKind::Switch);
-  BINJS_MOZ_TRY_DECL(preDefaultCases, parseListOfSwitchCase(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(defaultCase, parseSwitchDefault(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(postDefaultCases, parseListOfSwitchCase(fieldContext++));
+  BINJS_MOZ_TRY_DECL(preDefaultCases,
+                     parseListOfSwitchCase(Context(FieldContext(
+                         BinASTInterfaceAndField::
+                             SwitchStatementWithDefault__PreDefaultCases))));
+
+  BINJS_MOZ_TRY_DECL(
+      defaultCase,
+      parseSwitchDefault(Context(FieldContext(
+          BinASTInterfaceAndField::SwitchStatementWithDefault__DefaultCase))));
+
+  BINJS_MOZ_TRY_DECL(postDefaultCases,
+                     parseListOfSwitchCase(Context(FieldContext(
+                         BinASTInterfaceAndField::
+                             SwitchStatementWithDefault__PostDefaultCases))));
 
   // Concatenate `preDefaultCase`, `defaultCase`, `postDefaultCase`
   auto cases = preDefaultCases;
   handler_.addList(cases, defaultCase);
   ParseNode* iter = postDefaultCases->head();
   while (iter) {
     ParseNode* next = iter->pn_next;
     handler_.addList(cases, iter);
@@ -4296,114 +4564,127 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceThrowStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::ThrowStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[1] = {BinASTField::Expression};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(expression, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      expression, parseExpression(Context(FieldContext(
+                      BinASTInterfaceAndField::ThrowStatement__Expression))));
 
   BINJS_TRY_DECL(
       result, handler_.newThrowStatement(expression, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceTryCatchStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::TryCatchStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Body,
                                           BinASTField::CatchClause};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   ParseNode* body;
   {
     ParseContext::Statement stmt(pc_, StatementKind::Try);
     ParseContext::Scope scope(cx_, pc_, usedNames_);
     BINJS_TRY(scope.init(pc_));
-    MOZ_TRY_VAR(body, parseBlock(fieldContext++));
+    MOZ_TRY_VAR(body, parseBlock(Context(FieldContext(
+                          BinASTInterfaceAndField::TryCatchStatement__Body))));
   }
 
-  BINJS_MOZ_TRY_DECL(catchClause, parseCatchClause(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      catchClause,
+      parseCatchClause(Context(FieldContext(
+          BinASTInterfaceAndField::TryCatchStatement__CatchClause))));
 
   BINJS_TRY_DECL(result,
                  handler_.newTryStatement(start, body, catchClause,
                                           /* finallyBlock = */ nullptr));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceTryFinallyStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::TryFinallyStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::Body, BinASTField::CatchClause, BinASTField::Finalizer};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   ParseNode* body;
   {
     ParseContext::Statement stmt(pc_, StatementKind::Try);
     ParseContext::Scope scope(cx_, pc_, usedNames_);
     BINJS_TRY(scope.init(pc_));
-    MOZ_TRY_VAR(body, parseBlock(fieldContext++));
+    MOZ_TRY_VAR(body,
+                parseBlock(Context(FieldContext(
+                    BinASTInterfaceAndField::TryFinallyStatement__Body))));
   }
 
-  BINJS_MOZ_TRY_DECL(catchClause, parseOptionalCatchClause(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      catchClause,
+      parseOptionalCatchClause(Context(FieldContext(
+          BinASTInterfaceAndField::TryFinallyStatement__CatchClause))));
 
   ParseNode* finalizer;
   {
     ParseContext::Statement stmt(pc_, StatementKind::Finally);
     ParseContext::Scope scope(cx_, pc_, usedNames_);
     BINJS_TRY(scope.init(pc_));
-    MOZ_TRY_VAR(finalizer, parseBlock(fieldContext++));
+    MOZ_TRY_VAR(finalizer,
+                parseBlock(Context(FieldContext(
+                    BinASTInterfaceAndField::TryFinallyStatement__Finalizer))));
   }
 
   BINJS_TRY_DECL(result,
                  handler_.newTryStatement(start, body, catchClause, finalizer));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceUnaryExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::UnaryExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Operator,
                                           BinASTField::Operand};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(operator_, parseUnaryOperator(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(operand, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(operator_,
+                     parseUnaryOperator(Context(FieldContext(
+                         BinASTInterfaceAndField::UnaryExpression__Operator))));
+
+  BINJS_MOZ_TRY_DECL(operand,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::UnaryExpression__Operand))));
 
   ParseNodeKind pnk;
   switch (operator_) {
     case UnaryOperator::Minus:
       pnk = ParseNodeKind::NegExpr;
       break;
     case UnaryOperator::Plus:
       pnk = ParseNodeKind::PosExpr;
@@ -4449,29 +4730,34 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceUpdateExpression(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::UpdateExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[3] = {
       BinASTField::IsPrefix, BinASTField::Operator, BinASTField::Operand};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(isPrefix, tokenizer_->readBool(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(operator_, parseUpdateOperator(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(operand, parseSimpleAssignmentTarget(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      isPrefix, tokenizer_->readBool(Context(FieldContext(
+                    BinASTInterfaceAndField::UpdateExpression__IsPrefix))));
+
+  BINJS_MOZ_TRY_DECL(
+      operator_, parseUpdateOperator(Context(FieldContext(
+                     BinASTInterfaceAndField::UpdateExpression__Operator))));
+
+  BINJS_MOZ_TRY_DECL(operand,
+                     parseSimpleAssignmentTarget(Context(FieldContext(
+                         BinASTInterfaceAndField::UpdateExpression__Operand))));
 
   ParseNodeKind pnk;
   switch (operator_) {
     case UpdateOperator::Incr:
       pnk = isPrefix ? ParseNodeKind::PreIncrementExpr
                      : ParseNodeKind::PostIncrementExpr;
       break;
     case UpdateOperator::Decr:
@@ -4484,40 +4770,45 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceVariableDeclaration(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::VariableDeclaration);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Kind,
                                           BinASTField::Declarators};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   AutoVariableDeclarationKind kindGuard(this);
 
-  BINJS_MOZ_TRY_DECL(kind_, parseVariableDeclarationKind(fieldContext++));
+  BINJS_MOZ_TRY_DECL(kind_,
+                     parseVariableDeclarationKind(Context(FieldContext(
+                         BinASTInterfaceAndField::VariableDeclaration__Kind))));
   // Restored by `kindGuard`.
   variableDeclarationKind_ = kind_;
   ParseNodeKind declarationListKind;
   switch (kind_) {
     case VariableDeclarationKind::Var:
       declarationListKind = ParseNodeKind::VarStmt;
       break;
     case VariableDeclarationKind::Let:
       return raiseError("Let is not supported in this preview release");
     case VariableDeclarationKind::Const:
       return raiseError("Const is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(declarators, parseListOfVariableDeclarator(
-                                      declarationListKind, fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      declarators,
+      parseListOfVariableDeclarator(
+          declarationListKind,
+          Context(FieldContext(
+              BinASTInterfaceAndField::VariableDeclaration__Declarators))));
 
   // By specification, the list may not be empty.
   if (declarators->empty()) {
     return raiseEmpty("VariableDeclaration");
   }
 
   auto result = declarators;
   return result;
@@ -4549,27 +4840,30 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceVariableDeclarator(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::VariableDeclarator);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Binding,
                                           BinASTField::Init};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(binding, parseBinding(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(init, parseOptionalExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      binding, parseBinding(Context(FieldContext(
+                   BinASTInterfaceAndField::VariableDeclarator__Binding))));
+
+  BINJS_MOZ_TRY_DECL(init,
+                     parseOptionalExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::VariableDeclarator__Init))));
 
   ParseNode* result;
   if (binding->isKind(ParseNodeKind::Name)) {
     // `var foo [= bar]``
     NameNode* bindingNameNode = &binding->template as<NameNode>();
     MOZ_TRY(checkBinding(bindingNameNode->atom()->asPropertyName()));
     if (init) {
       BINJS_TRY_VAR(
@@ -4595,50 +4889,55 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceWhileStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::WhileStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Test, BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::WhileLoop);
 
-  BINJS_MOZ_TRY_DECL(test, parseExpression(fieldContext++));
-
-  BINJS_MOZ_TRY_DECL(body, parseStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(
+      test, parseExpression(Context(
+                FieldContext(BinASTInterfaceAndField::WhileStatement__Test))));
+
+  BINJS_MOZ_TRY_DECL(
+      body, parseStatement(Context(
+                FieldContext(BinASTInterfaceAndField::WhileStatement__Body))));
 
   BINJS_TRY_DECL(result, handler_.newWhileStatement(start, test, body));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceWithStatement(
     const size_t start, const BinASTKind kind, const BinASTFields& fields,
     const Context& context) {
   MOZ_ASSERT(kind == BinASTKind::WithStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  Context fieldContext = Context::firstField(kind);
 
 #if defined(DEBUG)
   const BinASTField expected_fields[2] = {BinASTField::Object,
                                           BinASTField::Body};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
-  BINJS_MOZ_TRY_DECL(object, parseExpression(fieldContext++));
+  BINJS_MOZ_TRY_DECL(object,
+                     parseExpression(Context(FieldContext(
+                         BinASTInterfaceAndField::WithStatement__Object))));
 
   ParseContext::Statement stmt(pc_, StatementKind::With);
-  BINJS_MOZ_TRY_DECL(body, parseStatement(fieldContext++));
+  BINJS_MOZ_TRY_DECL(body, parseStatement(Context(FieldContext(
+                               BinASTInterfaceAndField::WithStatement__Body))));
 
   pc_->sc()->setBindingsAccessedDynamically();
   BINJS_TRY_DECL(result, handler_.newWithStatement(start, object, body));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceYieldExpression(
@@ -4912,18 +5211,19 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newList(ParseNodeKind::Arguments,
                                           tokenizer_->pos(start)));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(Context(ListContext(
+        context.as<FieldContext>().position, BinASTList::Arguments)));
     BINJS_MOZ_TRY_DECL(item, parseSpreadElementOrExpression(childContext));
     handler_.addList(/* list = */ result, /* kid = */ item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
@@ -4932,18 +5232,19 @@ JS::Result<ListNode*> BinASTParser<Tok>:
     const Context& context) {
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newStatementList(tokenizer_->pos(start)));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(Context(ListContext(
+        context.as<FieldContext>().position, BinASTList::FunctionBody)));
     BINJS_MOZ_TRY_DECL(item, parseStatement(childContext));
     handler_.addStatementToList(result, item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
@@ -4953,18 +5254,20 @@ JS::Result<Ok> BinASTParser<Tok>::parseL
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   (void)start;
   auto result = Ok();
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(
+        Context(ListContext(context.as<FieldContext>().position,
+                            BinASTList::ListOfAssertedBoundName)));
     MOZ_TRY(parseAssertedBoundName(scopeKind, childContext));
     // Nothing to do here.
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
@@ -4974,18 +5277,20 @@ JS::Result<Ok> BinASTParser<Tok>::parseL
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   (void)start;
   auto result = Ok();
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(
+        Context(ListContext(context.as<FieldContext>().position,
+                            BinASTList::ListOfAssertedDeclaredName)));
     MOZ_TRY(parseAssertedDeclaredName(scopeKind, childContext));
     // Nothing to do here.
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
@@ -5005,18 +5310,20 @@ BinASTParser<Tok>::parseListOfAssertedMa
   // list items can be greater than the actual parameters, or more than
   // ARGNO_LIMIT even if the number of parameters fits into ARGNO_LIMIT.
   // Also, the number of parameters can be greater than this list's length
   // if one of destructuring parameter is empty.
   //
   // We resize `positionalParams` vector on demand, to keep the vector
   // length match to the known maximum positional parameter index + 1.
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(Context(
+        ListContext(context.as<FieldContext>().position,
+                    BinASTList::ListOfAssertedMaybePositionalParameterName)));
     MOZ_TRY(parseAssertedMaybePositionalParameterName(
         scopeKind, positionalParams, childContext));
     // Nothing to do here.
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
@@ -5026,18 +5333,19 @@ JS::Result<ListNode*> BinASTParser<Tok>:
     const Context& context) {
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newStatementList(tokenizer_->pos(start)));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(Context(ListContext(
+        context.as<FieldContext>().position, BinASTList::ListOfDirective)));
     BINJS_MOZ_TRY_DECL(item, parseDirective(childContext));
     handler_.addStatementToList(result, item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
@@ -5046,18 +5354,20 @@ JS::Result<ListNode*> BinASTParser<Tok>:
     const Context& context) {
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newObjectLiteral(start));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(
+        Context(ListContext(context.as<FieldContext>().position,
+                            BinASTList::ListOfObjectProperty)));
     BINJS_MOZ_TRY_DECL(item, parseObjectProperty(childContext));
     if (!item->isConstant()) result->setHasNonConstInitializer();
     result->appendWithoutOrderAssumption(item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
@@ -5068,18 +5378,20 @@ BinASTParser<Tok>::parseListOfOptionalSp
     const Context& context) {
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newArrayLiteral(start));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(Context(
+        ListContext(context.as<FieldContext>().position,
+                    BinASTList::ListOfOptionalSpreadElementOrExpression)));
     BINJS_MOZ_TRY_DECL(item,
                        parseOptionalSpreadElementOrExpression(childContext));
     if (item) {
       handler_.addArrayElement(result, item);  // Infallible.
     } else {
       BINJS_TRY(handler_.addElision(result, tokenizer_->pos(start)));
     }
   }
@@ -5093,18 +5405,19 @@ JS::Result<ListNode*> BinASTParser<Tok>:
     const Context& context) {
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newParamsBody(tokenizer_->pos(start)));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(Context(ListContext(
+        context.as<FieldContext>().position, BinASTList::ListOfParameter)));
     BINJS_MOZ_TRY_DECL(item, parseParameter(childContext));
     handler_.addList(/* list = */ result, /* kid = */ item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
@@ -5113,18 +5426,19 @@ JS::Result<ListNode*> BinASTParser<Tok>:
     const Context& context) {
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newStatementList(tokenizer_->pos(start)));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(Context(ListContext(
+        context.as<FieldContext>().position, BinASTList::ListOfStatement)));
     BINJS_MOZ_TRY_DECL(item, parseStatement(childContext));
     handler_.addStatementToList(result, item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
@@ -5133,18 +5447,19 @@ JS::Result<ListNode*> BinASTParser<Tok>:
     const Context& context) {
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newStatementList(tokenizer_->pos(start)));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(Context(ListContext(
+        context.as<FieldContext>().position, BinASTList::ListOfSwitchCase)));
     BINJS_MOZ_TRY_DECL(item, parseSwitchCase(childContext));
     handler_.addCaseStatementToList(result, item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
@@ -5154,18 +5469,20 @@ JS::Result<ListNode*> BinASTParser<Tok>:
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, context, guard));
   BINJS_TRY_DECL(result, handler_.newDeclarationList(declarationListKind,
                                                      tokenizer_->pos(start)));
 
-  const Context childContext(context.arrayElement());
   for (uint32_t i = 0; i < length; ++i) {
+    const Context childContext(
+        Context(ListContext(context.as<FieldContext>().position,
+                            BinASTList::ListOfVariableDeclarator)));
     BINJS_MOZ_TRY_DECL(item, parseVariableDeclarator(childContext));
     result->appendWithoutOrderAssumption(item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
--- a/js/src/frontend/BinASTParser.h
+++ b/js/src/frontend/BinASTParser.h
@@ -8,16 +8,17 @@
 
 // To generate this file, see the documentation in
 // js/src/frontend/binast/README.md.
 
 #ifndef frontend_BinASTParser_h
 #define frontend_BinASTParser_h
 
 #include "mozilla/Maybe.h"
+#include "mozilla/Variant.h"
 
 #include "frontend/BCEParserHandle.h"
 #include "frontend/BinASTParserPerTokenizer.h"
 #include "frontend/BinASTToken.h"
 #include "frontend/BinASTTokenReaderContext.h"
 #include "frontend/BinASTTokenReaderMultipart.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseContext.h"
@@ -39,16 +40,19 @@ class BinASTParser : public BinASTParser
 
   using Tokenizer = Tok;
 
   using BinASTFields = typename Tokenizer::BinASTFields;
   using AutoList = typename Tokenizer::AutoList;
   using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
   using Chars = typename Tokenizer::Chars;
   using Context = typename BinASTTokenReaderBase::Context;
+  using ListContext = typename BinASTTokenReaderBase::ListContext;
+  using FieldContext = typename BinASTTokenReaderBase::FieldContext;
+  using RootContext = typename BinASTTokenReaderBase::RootContext;
 
  public:
   // Auto-generated types.
   using AssertedDeclaredKind = binast::AssertedDeclaredKind;
   using BinaryOperator = binast::BinaryOperator;
   using CompoundAssignmentOperator = binast::CompoundAssignmentOperator;
   using UnaryOperator = binast::UnaryOperator;
   using UpdateOperator = binast::UpdateOperator;
--- a/js/src/frontend/BinASTParserPerTokenizer.cpp
+++ b/js/src/frontend/BinASTParserPerTokenizer.cpp
@@ -128,17 +128,17 @@ JS::Result<ParseNode*> BinASTParserPerTo
   ParseContext::VarScope varScope(cx_, &globalpc, usedNames_);
   if (!varScope.init(&globalpc)) {
     return cx_->alreadyReportedError();
   }
 
   MOZ_TRY(tokenizer_->readHeader());
 
   ParseNode* result(nullptr);
-  const Context topContext(Context::topLevel());
+  const Context topContext((RootContext()));
   MOZ_TRY_VAR(result, asFinalParser()->parseProgram(topContext));
 
   mozilla::Maybe<GlobalScope::Data*> bindings =
       NewGlobalScopeData(cx_, varScope, alloc_, pc_);
   if (!bindings) {
     return cx_->alreadyReportedError();
   }
   globalsc->bindings = *bindings;
@@ -191,17 +191,17 @@ JS::Result<FunctionNode*> BinASTParserPe
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* tmpBody;
   auto parseFunc = isExpr ? &FinalParser::parseFunctionExpressionContents
                           : &FinalParser::parseFunctionOrMethodContents;
 
   // Inject a toplevel context (i.e. no parent) to parse the lazy content.
   // In the future, we may move this to a more specific context.
-  const Context context(Context::topLevel());
+  const Context context((RootContext()));
   MOZ_TRY(
       (asFinalParser()->*parseFunc)(func->nargs(), &params, &tmpBody, context));
 
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(body, handler_.newLexicalScope(*lexicalScopeData, tmpBody));
 
   auto binASTKind = isExpr ? BinASTKind::LazyFunctionExpression
--- a/js/src/frontend/BinASTParserPerTokenizer.h
+++ b/js/src/frontend/BinASTParserPerTokenizer.h
@@ -52,16 +52,17 @@ class BinASTParserPerTokenizer : public 
                                  public BCEParserHandle {
  public:
   using Tokenizer = Tok;
 
   using AutoList = typename Tokenizer::AutoList;
   using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
   using BinASTFields = typename Tokenizer::BinASTFields;
   using Chars = typename Tokenizer::Chars;
+  using RootContext = BinASTTokenReaderBase::RootContext;
   using Context = BinASTTokenReaderBase::Context;
 
  public:
   // Auto-generated types.
   using AssertedDeclaredKind = binast::AssertedDeclaredKind;
   using VariableDeclarationKind = binast::VariableDeclarationKind;
 
  public:
--- a/js/src/frontend/BinASTTokenReaderBase.h
+++ b/js/src/frontend/BinASTTokenReaderBase.h
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef frontend_BinASTTokenReaderBase_h
 #define frontend_BinASTTokenReaderBase_h
 
 #include <string.h>
 
+#include "mozilla/Variant.h"
+
 #include "frontend/BinASTToken.h"
 #include "frontend/ErrorReporter.h"
 #include "frontend/TokenStream.h"
 
 #include "js/Result.h"
 #include "js/TypeDecls.h"
 
 namespace js {
@@ -22,63 +24,39 @@ namespace frontend {
 // A constant used by tokenizers to represent a null float.
 extern const uint64_t NULL_FLOAT_REPRESENTATION;
 
 class MOZ_STACK_CLASS BinASTTokenReaderBase {
  public:
   template <typename T>
   using ErrorResult = mozilla::GenericErrorResult<T>;
 
-  // The context in which we read a token.
-  struct Context {
-    // Construct a context for a root node.
-    constexpr static Context topLevel() {
-      return Context(BinASTKind::_Null, 0, ElementOf::TaggedTuple);
-    }
-
-    Context arrayElement() const {
-      return Context(kind, fieldIndex, ElementOf::Array);
-    }
-
-    // Construct a context for a field of a tagged tuple.
-    constexpr static Context firstField(BinASTKind kind) {
-      return Context(kind, 0, ElementOf::TaggedTuple);
-    }
-
-    const Context operator++(int) {
-      MOZ_ASSERT(elementOf == ElementOf::TaggedTuple);
-      Context result = *this;
-      fieldIndex++;
-      return result;
-    }
+  // Part of variant `Context`
+  // Reading the root of the tree, before we enter any tagged tuple.
+  struct RootContext {};
 
-    // The kind of the tagged tuple containing the token.
-    //
-    // If the parent is the root, use `BinASTKind::_Null`.
-    BinASTKind kind;
-
-    // The index of the token as a field of the parent.
-    uint8_t fieldIndex;
-
-    enum class ElementOf {
-      // This token is an element of an array.
-      Array,
+  // Part of variant `Context`
+  // Reading an element from a list.
+  struct ListContext {
+    const BinASTInterfaceAndField position;
+    const BinASTList content;
+    ListContext(const BinASTInterfaceAndField position,
+                const BinASTList content)
+        : position(position), content(content) {}
+  };
 
-      // This token is a field of a tagged tuple.
-      TaggedTuple,
-    };
-    ElementOf elementOf;
+  // Part of variant `Context`
+  // Reading a field from an interface.
+  struct FieldContext {
+    const BinASTInterfaceAndField position;
+    FieldContext(const BinASTInterfaceAndField position) : position(position) {}
+  };
 
-    Context() = delete;
-
-   private:
-    constexpr Context(BinASTKind kind_, uint8_t fieldIndex_,
-                      ElementOf elementOf_)
-        : kind(kind_), fieldIndex(fieldIndex_), elementOf(elementOf_) {}
-  };
+  // The context in which we read a token.
+  typedef mozilla::Variant<RootContext, ListContext, FieldContext> Context;
 
   // The information needed to skip a subtree.
   class SkippableSubTree {
    public:
     SkippableSubTree(const size_t startOffset, const size_t length)
         : startOffset_(startOffset), length_(length) {}
 
     // The position in the source buffer at which the subtree starts.
--- a/js/src/frontend/binast/src/main.rs
+++ b/js/src/frontend/binast/src/main.rs
@@ -1092,17 +1092,17 @@ enum class BinASTInterfaceAndField: uint
                                 )),
                                 TypeSpec::NamedType(ref name) => match spec.get_type_by_name(name).expect("By now, all types MUST exist") {
                                     NamedType::Typedef(alias_type) => {
                                         if alias_type.is_optional() {
                                             return get_field_type_name(spec, alias_type.as_ref(), true)
                                         }
                                         // Keep the simple name of sums and lists if there is one.
                                         match *alias_type.spec() {
-                                            TypeSpec::TypeSum(ref contents ) => {
+                                            TypeSpec::TypeSum(_) => {
                                                 if optional {
                                                     Cow::from(format!("OPTIONAL_SUM({name})", name = name.to_cpp_enum_case()))
                                                 } else {
                                                     Cow::from(format!("SUM({name})", name = name.to_cpp_enum_case()))
                                                 }
                                             }
                                             TypeSpec::Array { ref contents, .. } => {
                                                 let contents = TypeName::type_(contents);
@@ -1627,25 +1627,26 @@ impl CPPExporter {
 {{
     uint32_t length;
     AutoList guard(*tokenizer_);
 
     const auto start = tokenizer_->offset();
     MOZ_TRY(tokenizer_->enterList(length, context, guard));{empty_check}
 {init}
 
-    const Context childContext(context.arrayElement());
     for (uint32_t i = 0; i < length; ++i) {{
+        const Context childContext(Context(ListContext(context.as<FieldContext>().position, BinASTList::{content_kind})));
 {call}
 {append}    }}
 
     MOZ_TRY(guard.done());
     return result;
 }}\n",
             first_line = first_line,
+            content_kind = parser.name.to_class_cases(),
             empty_check =
                 if parser.supports_empty {
                     "".to_string()
                 } else {
                     format!("
     if (length == 0) {{
         return raiseEmpty(\"{kind}\");
     }}
@@ -1958,17 +1959,19 @@ impl CPPExporter {
         let fields_type_list = format!("{{ {} }}", interface.contents()
             .fields()
             .iter()
             .map(|field| format!("BinASTField::{}", field.name().to_cpp_enum_case()))
             .format(", "));
 
         let mut fields_implem = String::new();
         for field in interface.contents().fields() {
-            let context = "fieldContext++";
+            let context = format!("Context(FieldContext(BinASTInterfaceAndField::{kind}__{field}))",
+                kind = name.to_cpp_enum_case(),
+                field = field.name().to_cpp_enum_case());
 
             let rules_for_this_field = rules_for_this_interface.by_field.get(field.name())
                 .cloned()
                 .unwrap_or_default();
             let needs_block = rules_for_this_field.block_before_field.is_some() || rules_for_this_field.block_after_field.is_some();
 
             let var_name = field.name().to_cpp_field_case();
             let (decl_var, parse_var) = match field.type_().get_primitive(&self.syntax) {
@@ -2160,33 +2163,28 @@ impl CPPExporter {
 #endif // defined(DEBUG)",
                     fields_type_list = fields_type_list,
                     number_of_fields = number_of_fields)
             };
             buffer.push_str(&format!("{first_line}
 {{
     MOZ_ASSERT(kind == BinASTKind::{kind});
     BINJS_TRY(CheckRecursionLimit(cx_));
-{maybe_field_context}{check_fields}
+{check_fields}
 {pre}{fields_implem}
 {post}    return result;
 }}
 
 ",
                 check_fields = check_fields,
                 fields_implem = fields_implem,
                 pre = init.newline_if_not_empty(),
                 post = build_result.newline_if_not_empty(),
                 kind = name.to_cpp_enum_case(),
                 first_line = first_line,
-                maybe_field_context = if interface.contents().fields().len() > 0 {
-                    "    Context fieldContext = Context::firstField(kind);\n"
-                } else {
-                    ""
-                },
             ));
         }
     }
 
     /// Generate C++ code for SpiderMonkey
     fn to_spidermonkey_cpp(&self) -> String {
         let mut buffer = String::new();