Bug 1516697 - Split {Module,Function}Validator classes into {Module,Function}Validator{,Shared} to segregate source-unit-agnostic parts from source-unit-aware parts, use the correct types in all signatures, and use *ValidatorShared::* instead of *Validator::* for nested classes that are common to both source types. r=arai
authorJeff Walden <jwalden@mit.edu>
Fri, 28 Dec 2018 11:07:24 -0600
changeset 509932 cf7153f42f1cbe530b7f86189b8555a9598929c1
parent 509931 6bdb62915f688c4cb729c1e9fcbbee4bf56a42bd
child 509933 11d9ca021f121d9bc5ef628ecee2caa02ed4e54b
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1516697
milestone66.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 1516697 - Split {Module,Function}Validator classes into {Module,Function}Validator{,Shared} to segregate source-unit-agnostic parts from source-unit-aware parts, use the correct types in all signatures, and use *ValidatorShared::* instead of *Validator::* for nested classes that are common to both source types. r=arai
js/src/wasm/AsmJS.cpp
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -198,16 +198,17 @@ class AsmJSGlobal {
       struct {
         ConstantKind kind_;
         double value_;
       } constant;
     } u;
   } pod;
   CacheableChars field_;
 
+  friend class ModuleValidatorShared;
   friend class ModuleValidator;
 
  public:
   AsmJSGlobal() = default;
   AsmJSGlobal(Which which, UniqueChars field) {
     mozilla::PodZero(&pod);  // zero padding for Valgrind
     pod.which_ = which;
     field_ = std::move(field);
@@ -1032,31 +1033,17 @@ class Type {
         return "void";
     }
     MOZ_CRASH("Invalid Type");
   }
 };
 
 static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 
-// The ModuleValidator encapsulates the entire validation of an asm.js module.
-// Its lifetime goes from the validation of the top components of an asm.js
-// module (all the globals), the emission of bytecode for all the functions in
-// the module and the validation of function's pointer tables. It also finishes
-// the compilation of all the module's stubs.
-//
-// Rooting note: ModuleValidator is a stack class that contains unrooted
-// PropertyName (JSAtom) pointers.  This is safe because it cannot be
-// constructed without a TokenStream reference.  TokenStream is itself a stack
-// class that cannot be constructed without an AutoKeepAtoms being live on the
-// stack, which prevents collection of atoms.
-//
-// ModuleValidator is marked as rooted in the rooting analysis.  Don't add
-// non-JSAtom pointers, or this will break!
-class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator {
+class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidatorShared {
  public:
   class Func {
     PropertyName* name_;
     uint32_t sigIndex_;
     uint32_t firstUse_;
     uint32_t funcDefIndex_;
 
     bool defined_;
@@ -1203,16 +1190,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
       // |varOrConst|, through |varOrConst.literalValue_|, has a
       // non-trivial constructor and therefore MUST be placement-new'd
       // into existence.
       MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
       U() : funcDefIndex_(0) {}
       MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
     } u;
 
+    friend class ModuleValidatorShared;
     friend class ModuleValidator;
     friend class js::LifoAlloc;
 
     explicit Global(Which which) : which_(which) {}
 
    public:
     Which which() const { return which_; }
     Type varOrConstType() const {
@@ -1275,17 +1263,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
 
   struct ArrayView {
     ArrayView(PropertyName* name, Scalar::Type type) : name(name), type(type) {}
 
     PropertyName* name;
     Scalar::Type type;
   };
 
- private:
+ protected:
   class HashableSig {
     uint32_t sigIndex_;
     const TypeDefVector& types_;
 
    public:
     HashableSig(uint32_t sigIndex, const TypeDefVector& types)
         : sigIndex_(sigIndex), types_(types) {}
     uint32_t sigIndex() const { return sigIndex_; }
@@ -1323,23 +1311,23 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
   };
 
   using SigSet = HashSet<HashableSig, HashableSig>;
   using FuncImportMap = HashMap<NamedSig, uint32_t, NamedSig>;
   using GlobalMap = HashMap<PropertyName*, Global*>;
   using MathNameMap = HashMap<PropertyName*, MathBuiltin>;
   using ArrayViewVector = Vector<ArrayView>;
 
+protected:
   JSContext* cx_;
-  AsmJSParser& parser_;
   CodeNode* moduleFunctionNode_;
   PropertyName* moduleFunctionName_;
-  PropertyName* globalArgumentName_;
-  PropertyName* importArgumentName_;
-  PropertyName* bufferArgumentName_;
+  PropertyName* globalArgumentName_ = nullptr;
+  PropertyName* importArgumentName_ = nullptr;
+  PropertyName* bufferArgumentName_ = nullptr;
   MathNameMap standardLibraryMathNames_;
   RootedFunction dummyFunction_;
 
   // Validation-internal state:
   LifoAlloc validationLifo_;
   FuncVector funcDefs_;
   TableVector tables_;
   GlobalMap globalMap_;
@@ -1348,195 +1336,130 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
   ArrayViewVector arrayViews_;
 
   // State used to build the AsmJSModule in finish():
   CompilerEnvironment compilerEnv_;
   ModuleEnvironment env_;
   MutableAsmJSMetadata asmJSMetadata_;
 
   // Error reporting:
-  UniqueChars errorString_;
-  uint32_t errorOffset_;
-  bool errorOverRecursed_;
-
-  // Helpers:
-  bool addStandardLibraryMathName(const char* name,
-                                  AsmJSMathBuiltinFunction func) {
-    JSAtom* atom = Atomize(cx_, name, strlen(name));
-    if (!atom) {
-      return false;
-    }
-    MathBuiltin builtin(func);
-    return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
-  }
-  bool addStandardLibraryMathName(const char* name, double cst) {
-    JSAtom* atom = Atomize(cx_, name, strlen(name));
-    if (!atom) {
-      return false;
-    }
-    MathBuiltin builtin(cst);
-    return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
-  }
-  bool newSig(FuncType&& sig, uint32_t* sigIndex) {
-    if (env_.types.length() >= MaxTypes) {
-      return failCurrentOffset("too many signatures");
-    }
-
-    *sigIndex = env_.types.length();
-    return env_.types.append(std::move(sig));
-  }
-  bool declareSig(FuncType&& sig, uint32_t* sigIndex) {
-    SigSet::AddPtr p = sigSet_.lookupForAdd(sig);
-    if (p) {
-      *sigIndex = p->sigIndex();
-      MOZ_ASSERT(env_.types[*sigIndex].funcType() == sig);
-      return true;
-    }
-
-    return newSig(std::move(sig), sigIndex) &&
-           sigSet_.add(p, HashableSig(*sigIndex, env_.types));
-  }
-
- public:
-  ModuleValidator(JSContext* cx, AsmJSParser& parser,
-                  CodeNode* moduleFunctionNode)
+  UniqueChars errorString_ = nullptr;
+  uint32_t errorOffset_ = UINT32_MAX;
+  bool errorOverRecursed_ = false;
+
+ protected:
+  ModuleValidatorShared(JSContext* cx, CodeNode* moduleFunctionNode)
       : cx_(cx),
-        parser_(parser),
         moduleFunctionNode_(moduleFunctionNode),
         moduleFunctionName_(FunctionName(moduleFunctionNode)),
-        globalArgumentName_(nullptr),
-        importArgumentName_(nullptr),
-        bufferArgumentName_(nullptr),
         standardLibraryMathNames_(cx),
         dummyFunction_(cx),
         validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
         funcDefs_(cx),
         tables_(cx),
         globalMap_(cx),
         sigSet_(cx),
         funcImportMap_(cx),
         arrayViews_(cx),
         compilerEnv_(CompileMode::Once, Tier::Optimized, OptimizedBackend::Ion,
                      DebugEnabled::False, HasGcTypes::False),
         env_(HasGcTypes::False, &compilerEnv_, Shareable::False,
-             ModuleKind::AsmJS),
-        errorString_(nullptr),
-        errorOffset_(UINT32_MAX),
-        errorOverRecursed_(false) {
+             ModuleKind::AsmJS) {
     compilerEnv_.computeParameters(HasGcTypes::False);
     env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
   }
 
-  ~ModuleValidator() {
-    if (errorString_) {
-      MOZ_ASSERT(errorOffset_ != UINT32_MAX);
-      typeFailure(errorOffset_, errorString_.get());
-    }
-    if (errorOverRecursed_) {
-      ReportOverRecursed(cx_);
-    }
-  }
-
- private:
-  void typeFailure(uint32_t offset, ...) {
-    va_list args;
-    va_start(args, offset);
-
-    auto& ts = tokenStream();
-    ErrorMetadata metadata;
-    if (ts.computeErrorMetadata(&metadata, offset)) {
-      if (ts.anyCharsAccess().options().throwOnAsmJSValidationFailureOption) {
-        ReportCompileError(cx_, std::move(metadata), nullptr, JSREPORT_ERROR,
-                           JSMSG_USE_ASM_TYPE_FAIL, args);
-      } else {
-        // asm.js type failure is indicated by calling one of the fail*
-        // functions below.  These functions always return false to
-        // halt asm.js parsing.  Whether normal parsing is attempted as
-        // fallback, depends whether an exception is also set.
-        //
-        // If warning succeeds, no exception is set.  If warning fails,
-        // an exception is set and execution will halt.  Thus it's safe
-        // and correct to ignore the return value here.
-        Unused << ts.anyCharsAccess().compileWarning(
-            std::move(metadata), nullptr, JSREPORT_WARNING,
-            JSMSG_USE_ASM_TYPE_FAIL, args);
+ protected:
+  MOZ_MUST_USE bool addStandardLibraryMathInfo() {
+    static constexpr struct {
+      const char* name;
+      AsmJSMathBuiltinFunction func;
+    } functions[] = {
+        {"sin", AsmJSMathBuiltin_sin},       {"cos", AsmJSMathBuiltin_cos},
+        {"tan", AsmJSMathBuiltin_tan},       {"asin", AsmJSMathBuiltin_asin},
+        {"acos", AsmJSMathBuiltin_acos},     {"atan", AsmJSMathBuiltin_atan},
+        {"ceil", AsmJSMathBuiltin_ceil},     {"floor", AsmJSMathBuiltin_floor},
+        {"exp", AsmJSMathBuiltin_exp},       {"log", AsmJSMathBuiltin_log},
+        {"pow", AsmJSMathBuiltin_pow},       {"sqrt", AsmJSMathBuiltin_sqrt},
+        {"abs", AsmJSMathBuiltin_abs},       {"atan2", AsmJSMathBuiltin_atan2},
+        {"imul", AsmJSMathBuiltin_imul},     {"clz32", AsmJSMathBuiltin_clz32},
+        {"fround", AsmJSMathBuiltin_fround}, {"min", AsmJSMathBuiltin_min},
+        {"max", AsmJSMathBuiltin_max},
+    };
+
+    auto AddMathFunction = [this](const char* name,
+                                  AsmJSMathBuiltinFunction func) {
+      JSAtom* atom = Atomize(cx_, name, strlen(name));
+      if (!atom) {
+        return false;
+      }
+      MathBuiltin builtin(func);
+      return this->standardLibraryMathNames_.putNew(atom->asPropertyName(),
+                                                    builtin);
+    };
+
+    for (const auto& info : functions) {
+      if (!AddMathFunction(info.name, info.func)) {
+        return false;
       }
     }
 
-    va_end(args);
-  }
-
- public:
-  bool init() {
-    asmJSMetadata_ = cx_->new_<AsmJSMetadata>();
-    if (!asmJSMetadata_) {
-      return false;
-    }
-
-    asmJSMetadata_->toStringStart =
-        moduleFunctionNode_->funbox()->toStringStart;
-    asmJSMetadata_->srcStart = moduleFunctionNode_->body()->pn_pos.begin;
-    asmJSMetadata_->strict =
-        parser_.pc->sc()->strict() && !parser_.pc->sc()->hasExplicitUseStrict();
-    asmJSMetadata_->scriptSource.reset(parser_.ss);
-
-    if (!addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
-        !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
-        !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
-        !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
-        !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) ||
-        !addStandardLibraryMathName("atan", AsmJSMathBuiltin_atan) ||
-        !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) ||
-        !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) ||
-        !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) ||
-        !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) ||
-        !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) ||
-        !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) ||
-        !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
-        !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
-        !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
-        !addStandardLibraryMathName("clz32", AsmJSMathBuiltin_clz32) ||
-        !addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) ||
-        !addStandardLibraryMathName("min", AsmJSMathBuiltin_min) ||
-        !addStandardLibraryMathName("max", AsmJSMathBuiltin_max) ||
-        !addStandardLibraryMathName("E", M_E) ||
-        !addStandardLibraryMathName("LN10", M_LN10) ||
-        !addStandardLibraryMathName("LN2", M_LN2) ||
-        !addStandardLibraryMathName("LOG2E", M_LOG2E) ||
-        !addStandardLibraryMathName("LOG10E", M_LOG10E) ||
-        !addStandardLibraryMathName("PI", M_PI) ||
-        !addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) ||
-        !addStandardLibraryMathName("SQRT2", M_SQRT2)) {
-      return false;
-    }
-
+    static constexpr struct {
+      const char* name;
+      double value;
+    } constants[] = {
+        {"E", M_E},
+        {"LN10", M_LN10},
+        {"LN2", M_LN2},
+        {"LOG2E", M_LOG2E},
+        {"LOG10E", M_LOG10E},
+        {"PI", M_PI},
+        {"SQRT1_2", M_SQRT1_2},
+        {"SQRT2", M_SQRT2},
+    };
+
+    auto AddMathConstant = [this](const char* name, double cst) {
+      JSAtom* atom = Atomize(cx_, name, strlen(name));
+      if (!atom) {
+        return false;
+      }
+      MathBuiltin builtin(cst);
+      return this->standardLibraryMathNames_.putNew(atom->asPropertyName(),
+                                                    builtin);
+    };
+
+    for (const auto& info : constants) {
+      if (!AddMathConstant(info.name, info.value)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  MOZ_MUST_USE bool initDummyFunction() {
     // This flows into FunctionBox, so must be tenured.
     dummyFunction_ = NewScriptedFunction(
         cx_, 0, JSFunction::INTERPRETED, nullptr,
         /* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject);
     if (!dummyFunction_) {
       return false;
     }
 
     return true;
   }
 
+ public:
   JSContext* cx() const { return cx_; }
   PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
   PropertyName* globalArgumentName() const { return globalArgumentName_; }
   PropertyName* importArgumentName() const { return importArgumentName_; }
   PropertyName* bufferArgumentName() const { return bufferArgumentName_; }
   const ModuleEnvironment& env() { return env_; }
 
-  AsmJSParser& parser() const { return parser_; }
-
-  auto tokenStream() const -> decltype(parser_.tokenStream)& {
-    return parser_.tokenStream;
-  }
-
   RootedFunction& dummyFunction() { return dummyFunction_; }
   uint32_t minMemoryLength() const { return env_.minMemoryLength; }
 
   void initModuleFunctionName(PropertyName* name) {
     MOZ_ASSERT(!moduleFunctionName_);
     moduleFunctionName_ = name;
   }
   MOZ_MUST_USE bool initGlobalArgumentName(PropertyName* n) {
@@ -1789,80 +1712,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     }
 
     // The exported function might have already been exported in which case
     // the index will refer into the range of AsmJSExports.
     return asmJSMetadata_->asmJSExports.emplaceBack(
         funcIndex, func.srcBegin() - asmJSMetadata_->srcStart,
         func.srcEnd() - asmJSMetadata_->srcStart);
   }
-  bool addFuncDef(PropertyName* name, uint32_t firstUse, FuncType&& sig,
-                  Func** func) {
-    uint32_t sigIndex;
-    if (!declareSig(std::move(sig), &sigIndex)) {
-      return false;
-    }
-
-    uint32_t funcDefIndex = funcDefs_.length();
-    if (funcDefIndex >= MaxFuncs) {
-      return failCurrentOffset("too many functions");
-    }
-
-    Global* global = validationLifo_.new_<Global>(Global::Function);
-    if (!global) {
-      return false;
-    }
-    new (&global->u.funcDefIndex_) uint32_t(funcDefIndex);
-    if (!globalMap_.putNew(name, global)) {
-      return false;
-    }
-    if (!funcDefs_.emplaceBack(name, sigIndex, firstUse, funcDefIndex)) {
-      return false;
-    }
-    *func = &funcDefs_.back();
-    return true;
-  }
-  bool declareFuncPtrTable(FuncType&& sig, PropertyName* name,
-                           uint32_t firstUse, uint32_t mask,
-                           uint32_t* tableIndex) {
-    if (mask > MaxTableInitialLength) {
-      return failCurrentOffset("function pointer table too big");
-    }
-
-    MOZ_ASSERT(env_.tables.length() == tables_.length());
-    *tableIndex = env_.tables.length();
-
-    uint32_t sigIndex;
-    if (!newSig(std::move(sig), &sigIndex)) {
-      return false;
-    }
-
-    MOZ_ASSERT(sigIndex >= env_.asmJSSigToTableIndex.length());
-    if (!env_.asmJSSigToTableIndex.resize(sigIndex + 1)) {
-      return false;
-    }
-
-    env_.asmJSSigToTableIndex[sigIndex] = env_.tables.length();
-    if (!env_.tables.emplaceBack(TableKind::TypedFunction, Limits(mask + 1))) {
-      return false;
-    }
-
-    Global* global = validationLifo_.new_<Global>(Global::Table);
-    if (!global) {
-      return false;
-    }
-
-    new (&global->u.tableIndex_) uint32_t(*tableIndex);
-    if (!globalMap_.putNew(name, global)) {
-      return false;
-    }
-
-    Table* t = validationLifo_.new_<Table>(sigIndex, name, firstUse, mask);
-    return t && tables_.append(t);
-  }
+
   bool defineFuncPtrTable(uint32_t tableIndex, Uint32Vector&& elems) {
     Table& table = *tables_[tableIndex];
     if (table.defined()) {
       return false;
     }
 
     table.define();
 
@@ -1874,44 +1734,16 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     if (!seg) {
       return false;
     }
     seg->tableIndex = tableIndex;
     seg->offsetIfActive = Some(InitExpr(LitVal(uint32_t(0))));
     seg->elemFuncIndices = std::move(elems);
     return env_.elemSegments.append(std::move(seg));
   }
-  bool declareImport(PropertyName* name, FuncType&& sig, unsigned ffiIndex,
-                     uint32_t* importIndex) {
-    FuncImportMap::AddPtr p =
-        funcImportMap_.lookupForAdd(NamedSig::Lookup(name, sig));
-    if (p) {
-      *importIndex = p->value();
-      return true;
-    }
-
-    *importIndex = funcImportMap_.count();
-    MOZ_ASSERT(*importIndex == asmJSMetadata_->asmJSImports.length());
-
-    if (*importIndex >= MaxImports) {
-      return failCurrentOffset("too many imports");
-    }
-
-    if (!asmJSMetadata_->asmJSImports.emplaceBack(ffiIndex)) {
-      return false;
-    }
-
-    uint32_t sigIndex;
-    if (!declareSig(std::move(sig), &sigIndex)) {
-      return false;
-    }
-
-    return funcImportMap_.add(p, NamedSig(name, sigIndex, env_.types),
-                              *importIndex);
-  }
 
   bool tryConstantAccess(uint64_t start, uint64_t width) {
     MOZ_ASSERT(UINT64_MAX - start > width);
     uint64_t len = start + width;
     if (len > uint64_t(INT32_MAX) + 1) {
       return false;
     }
     len = RoundUpToNextValidAsmJSHeapLength(len);
@@ -1928,21 +1760,16 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     MOZ_ASSERT(!hasAlreadyFailed());
     MOZ_ASSERT(errorOffset_ == UINT32_MAX);
     MOZ_ASSERT(str);
     errorOffset_ = offset;
     errorString_ = DuplicateString(str);
     return false;
   }
 
-  bool failCurrentOffset(const char* str) {
-    return failOffset(tokenStream().anyCharsAccess().currentToken().pos.begin,
-                      str);
-  }
-
   bool fail(ParseNode* pn, const char* str) {
     return failOffset(pn->pn_pos.begin, str);
   }
 
   bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap)
       MOZ_FORMAT_PRINTF(3, 0) {
     MOZ_ASSERT(!hasAlreadyFailed());
     MOZ_ASSERT(errorOffset_ == UINT32_MAX);
@@ -2023,16 +1850,234 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
   bool startFunctionBodies() {
     if (!arrayViews_.empty()) {
       env_.memoryUsage = MemoryUsage::Unshared;
     } else {
       env_.memoryUsage = MemoryUsage::None;
     }
     return true;
   }
+};
+
+// The ModuleValidator encapsulates the entire validation of an asm.js module.
+// Its lifetime goes from the validation of the top components of an asm.js
+// module (all the globals), the emission of bytecode for all the functions in
+// the module and the validation of function's pointer tables. It also finishes
+// the compilation of all the module's stubs.
+//
+// Rooting note: ModuleValidator is a stack class that contains unrooted
+// PropertyName (JSAtom) pointers.  This is safe because it cannot be
+// constructed without a TokenStream reference.  TokenStream is itself a stack
+// class that cannot be constructed without an AutoKeepAtoms being live on the
+// stack, which prevents collection of atoms.
+//
+// ModuleValidator is marked as rooted in the rooting analysis.  Don't add
+// non-JSAtom pointers, or this will break!
+class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator
+    : public ModuleValidatorShared {
+ private:
+  AsmJSParser& parser_;
+
+ public:
+  ModuleValidator(JSContext* cx, AsmJSParser& parser,
+                  CodeNode* moduleFunctionNode)
+      : ModuleValidatorShared(cx, moduleFunctionNode), parser_(parser) {}
+
+  ~ModuleValidator() {
+    if (errorString_) {
+      MOZ_ASSERT(errorOffset_ != UINT32_MAX);
+      typeFailure(errorOffset_, errorString_.get());
+    }
+    if (errorOverRecursed_) {
+      ReportOverRecursed(cx_);
+    }
+  }
+
+ private:
+  // Helpers:
+  bool newSig(FuncType&& sig, uint32_t* sigIndex) {
+    if (env_.types.length() >= MaxTypes) {
+      return failCurrentOffset("too many signatures");
+    }
+
+    *sigIndex = env_.types.length();
+    return env_.types.append(std::move(sig));
+  }
+  bool declareSig(FuncType&& sig, uint32_t* sigIndex) {
+    SigSet::AddPtr p = sigSet_.lookupForAdd(sig);
+    if (p) {
+      *sigIndex = p->sigIndex();
+      MOZ_ASSERT(env_.types[*sigIndex].funcType() == sig);
+      return true;
+    }
+
+    return newSig(std::move(sig), sigIndex) &&
+           sigSet_.add(p, HashableSig(*sigIndex, env_.types));
+  }
+
+ private:
+  void typeFailure(uint32_t offset, ...) {
+    va_list args;
+    va_start(args, offset);
+
+    auto& ts = tokenStream();
+    ErrorMetadata metadata;
+    if (ts.computeErrorMetadata(&metadata, offset)) {
+      if (ts.anyCharsAccess().options().throwOnAsmJSValidationFailureOption) {
+        ReportCompileError(cx_, std::move(metadata), nullptr, JSREPORT_ERROR,
+                           JSMSG_USE_ASM_TYPE_FAIL, args);
+      } else {
+        // asm.js type failure is indicated by calling one of the fail*
+        // functions below.  These functions always return false to
+        // halt asm.js parsing.  Whether normal parsing is attempted as
+        // fallback, depends whether an exception is also set.
+        //
+        // If warning succeeds, no exception is set.  If warning fails,
+        // an exception is set and execution will halt.  Thus it's safe
+        // and correct to ignore the return value here.
+        Unused << ts.anyCharsAccess().compileWarning(
+            std::move(metadata), nullptr, JSREPORT_WARNING,
+            JSMSG_USE_ASM_TYPE_FAIL, args);
+      }
+    }
+
+    va_end(args);
+  }
+
+ public:
+  bool init() {
+    asmJSMetadata_ = cx_->new_<AsmJSMetadata>();
+    if (!asmJSMetadata_) {
+      return false;
+    }
+
+    asmJSMetadata_->toStringStart =
+        moduleFunctionNode_->funbox()->toStringStart;
+    asmJSMetadata_->srcStart = moduleFunctionNode_->body()->pn_pos.begin;
+    asmJSMetadata_->strict =
+        parser_.pc->sc()->strict() && !parser_.pc->sc()->hasExplicitUseStrict();
+    asmJSMetadata_->scriptSource.reset(parser_.ss);
+
+    if (!addStandardLibraryMathInfo()) {
+      return false;
+    }
+
+    if (!initDummyFunction()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  AsmJSParser& parser() const { return parser_; }
+
+  auto tokenStream() const -> decltype(parser_.tokenStream)& {
+    return parser_.tokenStream;
+  }
+
+ public:
+  bool addFuncDef(PropertyName* name, uint32_t firstUse, FuncType&& sig,
+                  Func** func) {
+    uint32_t sigIndex;
+    if (!declareSig(std::move(sig), &sigIndex)) {
+      return false;
+    }
+
+    uint32_t funcDefIndex = funcDefs_.length();
+    if (funcDefIndex >= MaxFuncs) {
+      return failCurrentOffset("too many functions");
+    }
+
+    Global* global = validationLifo_.new_<Global>(Global::Function);
+    if (!global) {
+      return false;
+    }
+    new (&global->u.funcDefIndex_) uint32_t(funcDefIndex);
+    if (!globalMap_.putNew(name, global)) {
+      return false;
+    }
+    if (!funcDefs_.emplaceBack(name, sigIndex, firstUse, funcDefIndex)) {
+      return false;
+    }
+    *func = &funcDefs_.back();
+    return true;
+  }
+  bool declareFuncPtrTable(FuncType&& sig, PropertyName* name,
+                           uint32_t firstUse, uint32_t mask,
+                           uint32_t* tableIndex) {
+    if (mask > MaxTableInitialLength) {
+      return failCurrentOffset("function pointer table too big");
+    }
+
+    MOZ_ASSERT(env_.tables.length() == tables_.length());
+    *tableIndex = env_.tables.length();
+
+    uint32_t sigIndex;
+    if (!newSig(std::move(sig), &sigIndex)) {
+      return false;
+    }
+
+    MOZ_ASSERT(sigIndex >= env_.asmJSSigToTableIndex.length());
+    if (!env_.asmJSSigToTableIndex.resize(sigIndex + 1)) {
+      return false;
+    }
+
+    env_.asmJSSigToTableIndex[sigIndex] = env_.tables.length();
+    if (!env_.tables.emplaceBack(TableKind::TypedFunction, Limits(mask + 1))) {
+      return false;
+    }
+
+    Global* global = validationLifo_.new_<Global>(Global::Table);
+    if (!global) {
+      return false;
+    }
+
+    new (&global->u.tableIndex_) uint32_t(*tableIndex);
+    if (!globalMap_.putNew(name, global)) {
+      return false;
+    }
+
+    Table* t = validationLifo_.new_<Table>(sigIndex, name, firstUse, mask);
+    return t && tables_.append(t);
+  }
+  bool declareImport(PropertyName* name, FuncType&& sig, unsigned ffiIndex,
+                     uint32_t* importIndex) {
+    FuncImportMap::AddPtr p =
+        funcImportMap_.lookupForAdd(NamedSig::Lookup(name, sig));
+    if (p) {
+      *importIndex = p->value();
+      return true;
+    }
+
+    *importIndex = funcImportMap_.count();
+    MOZ_ASSERT(*importIndex == asmJSMetadata_->asmJSImports.length());
+
+    if (*importIndex >= MaxImports) {
+      return failCurrentOffset("too many imports");
+    }
+
+    if (!asmJSMetadata_->asmJSImports.emplaceBack(ffiIndex)) {
+      return false;
+    }
+
+    uint32_t sigIndex;
+    if (!declareSig(std::move(sig), &sigIndex)) {
+      return false;
+    }
+
+    return funcImportMap_.add(p, NamedSig(name, sigIndex, env_.types),
+                              *importIndex);
+  }
+
+  // Error handling.
+  bool failCurrentOffset(const char* str) {
+    return failOffset(tokenStream().anyCharsAccess().currentToken().pos.begin,
+                      str);
+  }
+
   SharedModule finish(UniqueLinkData* linkData) {
     MOZ_ASSERT(env_.funcTypes.empty());
     if (!env_.funcTypes.resize(funcImportMap_.count() + funcDefs_.length())) {
       return nullptr;
     }
     for (FuncImportMap::Range r = funcImportMap_.all(); !r.empty();
          r.popFront()) {
       uint32_t funcIndex = r.front().value();
@@ -2132,34 +2177,34 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
 static bool IsNumericNonFloatLiteral(ParseNode* pn) {
   // Note: '-' is never rolled into the number; numbers are always positive
   // and negations must be applied manually.
   return pn->isKind(ParseNodeKind::NumberExpr) ||
          (pn->isKind(ParseNodeKind::NegExpr) &&
           UnaryKid(pn)->isKind(ParseNodeKind::NumberExpr));
 }
 
-static bool IsCallToGlobal(ModuleValidator& m, ParseNode* pn,
-                           const ModuleValidator::Global** global) {
+static bool IsCallToGlobal(ModuleValidatorShared& m, ParseNode* pn,
+                           const ModuleValidatorShared::Global** global) {
   if (!pn->isKind(ParseNodeKind::CallExpr)) {
     return false;
   }
 
   ParseNode* callee = CallCallee(pn);
   if (!callee->isKind(ParseNodeKind::Name)) {
     return false;
   }
 
   *global = m.lookupGlobal(callee->as<NameNode>().name());
   return !!*global;
 }
 
-static bool IsCoercionCall(ModuleValidator& m, ParseNode* pn, Type* coerceTo,
-                           ParseNode** coercedExpr) {
-  const ModuleValidator::Global* global;
+static bool IsCoercionCall(ModuleValidatorShared& m, ParseNode* pn,
+                           Type* coerceTo, ParseNode** coercedExpr) {
+  const ModuleValidatorShared::Global* global;
   if (!IsCallToGlobal(m, pn, &global)) {
     return false;
   }
 
   if (CallArgListLength(pn) != 1) {
     return false;
   }
 
@@ -2171,37 +2216,30 @@ static bool IsCoercionCall(ModuleValidat
       global->mathBuiltinFunction() == AsmJSMathBuiltin_fround) {
     *coerceTo = Type::Float;
     return true;
   }
 
   return false;
 }
 
-static bool IsFloatLiteral(ModuleValidator& m, ParseNode* pn) {
+static bool IsFloatLiteral(ModuleValidatorShared& m, ParseNode* pn) {
   ParseNode* coercedExpr;
   Type coerceTo;
   if (!IsCoercionCall(m, pn, &coerceTo, &coercedExpr)) {
     return false;
   }
   // Don't fold into || to avoid clang/memcheck bug (bug 1077031).
   if (!coerceTo.isFloat()) {
     return false;
   }
   return IsNumericNonFloatLiteral(coercedExpr);
 }
 
-static bool IsNumericLiteral(ModuleValidator& m, ParseNode* pn);
-
-static NumLit ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn);
-
-static inline bool IsLiteralInt(ModuleValidator& m, ParseNode* pn,
-                                uint32_t* u32);
-
-static bool IsNumericLiteral(ModuleValidator& m, ParseNode* pn) {
+static bool IsNumericLiteral(ModuleValidatorShared& m, ParseNode* pn) {
   return IsNumericNonFloatLiteral(pn) || IsFloatLiteral(m, pn);
 }
 
 // The JS grammar treats -42 as -(42) (i.e., with separate grammar
 // productions) for the unary - and literal 42). However, the asm.js spec
 // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
 // so fold the two potential parse nodes into a single double value.
 static double ExtractNumericNonFloatValue(ParseNode* pn,
@@ -2214,17 +2252,17 @@ static double ExtractNumericNonFloatValu
       *out = pn;
     }
     return -NumberNodeValue(pn);
   }
 
   return NumberNodeValue(pn);
 }
 
-static NumLit ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn) {
+static NumLit ExtractNumericLiteral(ModuleValidatorShared& m, ParseNode* pn) {
   MOZ_ASSERT(IsNumericLiteral(m, pn));
 
   if (pn->isKind(ParseNodeKind::CallExpr)) {
     // Float literals are explicitly coerced and thus the coerced literal may be
     // any valid (non-float) numeric literal.
     MOZ_ASSERT(CallArgListLength(pn) == 1);
     pn = CallArgList(pn);
     double d = ExtractNumericNonFloatValue(pn);
@@ -2275,79 +2313,86 @@ static inline bool IsLiteralInt(const Nu
     case NumLit::Double:
     case NumLit::Float:
     case NumLit::OutOfRangeInt:
       return false;
   }
   MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal type");
 }
 
-static inline bool IsLiteralInt(ModuleValidator& m, ParseNode* pn,
+static inline bool IsLiteralInt(ModuleValidatorShared& m, ParseNode* pn,
                                 uint32_t* u32) {
   return IsNumericLiteral(m, pn) &&
          IsLiteralInt(ExtractNumericLiteral(m, pn), u32);
 }
 
 /*****************************************************************************/
 
 namespace {
 
 typedef Vector<PropertyName*, 4, SystemAllocPolicy> LabelVector;
 
-// Encapsulates the building of an asm bytecode function from an asm.js function
-// source code, packing the asm.js code into the asm bytecode form that can
-// be decoded and compiled with a FunctionCompiler.
-class MOZ_STACK_CLASS FunctionValidator {
+class MOZ_STACK_CLASS FunctionValidatorShared {
  public:
   struct Local {
     Type type;
     unsigned slot;
     Local(Type t, unsigned slot) : type(t), slot(slot) {
       MOZ_ASSERT(type.isCanonicalValType());
     }
   };
 
- private:
-  typedef HashMap<PropertyName*, Local> LocalMap;
-  typedef HashMap<PropertyName*, uint32_t> LabelMap;
-
-  ModuleValidator& m_;
+ protected:
+  using LocalMap = HashMap<PropertyName*, Local>;
+  using LabelMap = HashMap<PropertyName*, uint32_t>;
+
+  // This is also a ModuleValidator& after the appropriate static_cast<>.
+  ModuleValidatorShared& m_;
+
   ParseNode* fn_;
   Bytes bytes_;
   Encoder encoder_;
   Uint32Vector callSiteLineNums_;
   LocalMap locals_;
 
   // Labels
   LabelMap breakLabels_;
   LabelMap continueLabels_;
   Uint32Vector breakableStack_;
   Uint32Vector continuableStack_;
   uint32_t blockDepth_;
 
   bool hasAlreadyReturned_;
   ExprType ret_;
 
- public:
-  FunctionValidator(ModuleValidator& m, ParseNode* fn)
+ private:
+  FunctionValidatorShared(ModuleValidatorShared& m, ParseNode* fn,
+                          JSContext* cx)
       : m_(m),
         fn_(fn),
         encoder_(bytes_),
-        locals_(m.cx()),
-        breakLabels_(m.cx()),
-        continueLabels_(m.cx()),
+        locals_(cx),
+        breakLabels_(cx),
+        continueLabels_(cx),
         blockDepth_(0),
         hasAlreadyReturned_(false),
         ret_(ExprType::Limit) {}
 
-  ModuleValidator& m() const { return m_; }
+ protected:
+  FunctionValidatorShared(ModuleValidator& m, ParseNode* fn, JSContext* cx)
+      : FunctionValidatorShared(static_cast<ModuleValidatorShared&>(m), fn,
+                                cx) {}
+
+ public:
+  ModuleValidatorShared& m() const { return m_; }
+
   JSContext* cx() const { return m_.cx(); }
   ParseNode* fn() const { return fn_; }
 
-  void define(ModuleValidator::Func* func, unsigned line) {
+  void define(ModuleValidatorShared::Func* func, unsigned line) {
     MOZ_ASSERT(!blockDepth_);
     MOZ_ASSERT(breakableStack_.empty());
     MOZ_ASSERT(continuableStack_.empty());
     MOZ_ASSERT(breakLabels_.empty());
     MOZ_ASSERT(continueLabels_.empty());
     func->define(fn_, line, std::move(bytes_), std::move(callSiteLineNums_));
   }
 
@@ -2521,17 +2566,17 @@ class MOZ_STACK_CLASS FunctionValidator 
 
   const Local* lookupLocal(PropertyName* name) const {
     if (auto p = locals_.lookup(name)) {
       return &p->value();
     }
     return nullptr;
   }
 
-  const ModuleValidator::Global* lookupGlobal(PropertyName* name) const {
+  const ModuleValidatorShared::Global* lookupGlobal(PropertyName* name) const {
     if (locals_.has(name)) {
       return nullptr;
     }
     return m_.lookupGlobal(name);
   }
 
   size_t numLocals() const { return locals_.count(); }
 
@@ -2554,16 +2599,31 @@ class MOZ_STACK_CLASS FunctionValidator 
       case NumLit::Double:
         return encoder().writeOp(Op::F64Const) &&
                encoder().writeFixedF64(lit.toDouble());
       case NumLit::OutOfRangeInt:
         break;
     }
     MOZ_CRASH("unexpected literal type");
   }
+};
+
+// Encapsulates the building of an asm bytecode function from an asm.js function
+// source code, packing the asm.js code into the asm bytecode form that can
+// be decoded and compiled with a FunctionCompiler.
+class MOZ_STACK_CLASS FunctionValidator : public FunctionValidatorShared {
+ public:
+  FunctionValidator(ModuleValidator& m, ParseNode* fn)
+      : FunctionValidatorShared(m, fn, m.cx()) {}
+
+ public:
+  ModuleValidator& m() const {
+    return static_cast<ModuleValidator&>(FunctionValidatorShared::m());
+  }
+
   MOZ_MUST_USE bool writeCall(ParseNode* pn, Op op) {
     if (!encoder().writeOp(op)) {
       return false;
     }
 
     return appendCallSiteLineNumber(pn);
   }
   MOZ_MUST_USE bool writeCall(ParseNode* pn, MozOp op) {
@@ -2586,53 +2646,53 @@ class MOZ_STACK_CLASS FunctionValidator 
   }
 };
 
 } /* anonymous namespace */
 
 /*****************************************************************************/
 // asm.js type-checking and code-generation algorithm
 
-static bool CheckIdentifier(ModuleValidator& m, ParseNode* usepn,
+static bool CheckIdentifier(ModuleValidatorShared& m, ParseNode* usepn,
                             PropertyName* name) {
   if (name == m.cx()->names().arguments || name == m.cx()->names().eval) {
     return m.failName(usepn, "'%s' is not an allowed identifier", name);
   }
   return true;
 }
 
-static bool CheckModuleLevelName(ModuleValidator& m, ParseNode* usepn,
+static bool CheckModuleLevelName(ModuleValidatorShared& m, ParseNode* usepn,
                                  PropertyName* name) {
   if (!CheckIdentifier(m, usepn, name)) {
     return false;
   }
 
   if (name == m.moduleFunctionName() || name == m.globalArgumentName() ||
       name == m.importArgumentName() || name == m.bufferArgumentName() ||
       m.lookupGlobal(name)) {
     return m.failName(usepn, "duplicate name '%s' not allowed", name);
   }
 
   return true;
 }
 
-static bool CheckFunctionHead(ModuleValidator& m, CodeNode* funNode) {
+static bool CheckFunctionHead(ModuleValidatorShared& m, CodeNode* funNode) {
   FunctionBox* funbox = funNode->funbox();
   MOZ_ASSERT(!funbox->hasExprBody());
 
   if (funbox->hasRest()) {
     return m.fail(funNode, "rest args not allowed");
   }
   if (funbox->hasDestructuringArgs) {
     return m.fail(funNode, "destructuring args not allowed");
   }
   return true;
 }
 
-static bool CheckArgument(ModuleValidator& m, ParseNode* arg,
+static bool CheckArgument(ModuleValidatorShared& m, ParseNode* arg,
                           PropertyName** name) {
   *name = nullptr;
 
   if (!arg->isKind(ParseNodeKind::Name)) {
     return m.fail(arg, "argument is not a plain name");
   }
 
   PropertyName* argName = arg->as<NameNode>().name();
@@ -2640,30 +2700,30 @@ static bool CheckArgument(ModuleValidato
   if (!CheckIdentifier(m, arg, argName)) {
     return false;
   }
 
   *name = argName;
   return true;
 }
 
-static bool CheckModuleArgument(ModuleValidator& m, ParseNode* arg,
+static bool CheckModuleArgument(ModuleValidatorShared& m, ParseNode* arg,
                                 PropertyName** name) {
   if (!CheckArgument(m, arg, name)) {
     return false;
   }
 
   if (!CheckModuleLevelName(m, arg, *name)) {
     return false;
   }
 
   return true;
 }
 
-static bool CheckModuleArguments(ModuleValidator& m, CodeNode* funNode) {
+static bool CheckModuleArguments(ModuleValidatorShared& m, CodeNode* funNode) {
   unsigned numFormals;
   ParseNode* arg1 = FunctionFormalParametersList(funNode, &numFormals);
   ParseNode* arg2 = arg1 ? NextNode(arg1) : nullptr;
   ParseNode* arg3 = arg2 ? NextNode(arg2) : nullptr;
 
   if (numFormals > 3) {
     return m.fail(funNode, "asm.js modules takes at most 3 argument");
   }
@@ -2690,48 +2750,49 @@ static bool CheckModuleArguments(ModuleV
   }
   if (!m.initBufferArgumentName(arg3Name)) {
     return false;
   }
 
   return true;
 }
 
-static bool CheckPrecedingStatements(ModuleValidator& m, ParseNode* stmtList) {
+static bool CheckPrecedingStatements(ModuleValidatorShared& m,
+                                     ParseNode* stmtList) {
   MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
 
   ParseNode* stmt = ListHead(stmtList);
   for (unsigned i = 0, n = ListLength(stmtList); i < n; i++) {
     if (!IsIgnoredDirective(m.cx(), stmt)) {
       return m.fail(stmt, "invalid asm.js statement");
     }
   }
 
   return true;
 }
 
-static bool CheckGlobalVariableInitConstant(ModuleValidator& m,
+static bool CheckGlobalVariableInitConstant(ModuleValidatorShared& m,
                                             PropertyName* varName,
                                             ParseNode* initNode, bool isConst) {
   NumLit lit = ExtractNumericLiteral(m, initNode);
   if (!lit.valid()) {
     return m.fail(initNode,
                   "global initializer is out of representable integer range");
   }
 
   Type canonicalType = Type::canonicalize(Type::lit(lit));
   if (!canonicalType.isGlobalVarType()) {
     return m.fail(initNode, "global variable type not allowed");
   }
 
   return m.addGlobalVarInit(varName, lit, canonicalType, isConst);
 }
 
-static bool CheckTypeAnnotation(ModuleValidator& m, ParseNode* coercionNode,
-                                Type* coerceTo,
+static bool CheckTypeAnnotation(ModuleValidatorShared& m,
+                                ParseNode* coercionNode, Type* coerceTo,
                                 ParseNode** coercedExpr = nullptr) {
   switch (coercionNode->getKind()) {
     case ParseNodeKind::BitOrExpr: {
       ParseNode* rhs = BitwiseRight(coercionNode);
       uint32_t i;
       if (!IsLiteralInt(m, rhs, &i) || i != 0) {
         return m.fail(rhs, "must use |0 for argument/return coercion");
       }
@@ -2755,17 +2816,17 @@ static bool CheckTypeAnnotation(ModuleVa
       break;
     }
     default:;
   }
 
   return m.fail(coercionNode, "must be of the form +x, x|0 or fround(x)");
 }
 
-static bool CheckGlobalVariableInitImport(ModuleValidator& m,
+static bool CheckGlobalVariableInitImport(ModuleValidatorShared& m,
                                           PropertyName* varName,
                                           ParseNode* initNode, bool isConst) {
   Type coerceTo;
   ParseNode* coercedExpr;
   if (!CheckTypeAnnotation(m, initNode, &coerceTo, &coercedExpr)) {
     return false;
   }
 
@@ -2789,17 +2850,17 @@ static bool CheckGlobalVariableInitImpor
   if (!IsUseOfName(base, importName)) {
     return m.failName(coercedExpr, "base of import expression must be '%s'",
                       importName);
   }
 
   return m.addGlobalVarImport(varName, field, coerceTo, isConst);
 }
 
-static bool IsArrayViewCtorName(ModuleValidator& m, PropertyName* name,
+static bool IsArrayViewCtorName(ModuleValidatorShared& m, PropertyName* name,
                                 Scalar::Type* type) {
   JSAtomState& names = m.cx()->names();
   if (name == names.Int8Array) {
     *type = Scalar::Int8;
   } else if (name == names.Uint8Array) {
     *type = Scalar::Uint8;
   } else if (name == names.Int16Array) {
     *type = Scalar::Int16;
@@ -2814,17 +2875,17 @@ static bool IsArrayViewCtorName(ModuleVa
   } else if (name == names.Float64Array) {
     *type = Scalar::Float64;
   } else {
     return false;
   }
   return true;
 }
 
-static bool CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* newExpr,
+static bool CheckNewArrayViewArgs(ModuleValidatorShared& m, ParseNode* newExpr,
                                   PropertyName* bufferName) {
   ParseNode* ctorExpr = BinaryLeft(newExpr);
   ParseNode* ctorArgs = BinaryRight(newExpr);
   ParseNode* bufArg = ListHead(ctorArgs);
   if (!bufArg || NextNode(bufArg) != nullptr) {
     return m.fail(ctorExpr,
                   "array view constructor takes exactly one argument");
   }
@@ -2832,17 +2893,17 @@ static bool CheckNewArrayViewArgs(Module
   if (!IsUseOfName(bufArg, bufferName)) {
     return m.failName(bufArg, "argument to array view constructor must be '%s'",
                       bufferName);
   }
 
   return true;
 }
 
-static bool CheckNewArrayView(ModuleValidator& m, PropertyName* varName,
+static bool CheckNewArrayView(ModuleValidatorShared& m, PropertyName* varName,
                               ParseNode* newExpr) {
   PropertyName* globalName = m.globalArgumentName();
   if (!globalName) {
     return m.fail(
         newExpr, "cannot create array view without an asm.js global parameter");
   }
 
   PropertyName* bufferName = m.bufferArgumentName();
@@ -2868,60 +2929,60 @@ static bool CheckNewArrayView(ModuleVali
     }
   } else {
     if (!ctorExpr->isKind(ParseNodeKind::Name)) {
       return m.fail(ctorExpr,
                     "expecting name of imported array view constructor");
     }
 
     PropertyName* globalName = ctorExpr->as<NameNode>().name();
-    const ModuleValidator::Global* global = m.lookupGlobal(globalName);
+    const ModuleValidatorShared::Global* global = m.lookupGlobal(globalName);
     if (!global) {
       return m.failName(ctorExpr, "%s not found in module global scope",
                         globalName);
     }
 
-    if (global->which() != ModuleValidator::Global::ArrayViewCtor) {
+    if (global->which() != ModuleValidatorShared::Global::ArrayViewCtor) {
       return m.failName(ctorExpr,
                         "%s must be an imported array view constructor",
                         globalName);
     }
 
     field = nullptr;
     type = global->viewType();
   }
 
   if (!CheckNewArrayViewArgs(m, newExpr, bufferName)) {
     return false;
   }
 
   return m.addArrayView(varName, type, field);
 }
 
-static bool CheckGlobalMathImport(ModuleValidator& m, ParseNode* initNode,
+static bool CheckGlobalMathImport(ModuleValidatorShared& m, ParseNode* initNode,
                                   PropertyName* varName, PropertyName* field) {
   // Math builtin, with the form glob.Math.[[builtin]]
-  ModuleValidator::MathBuiltin mathBuiltin;
+  ModuleValidatorShared::MathBuiltin mathBuiltin;
   if (!m.lookupStandardLibraryMathName(field, &mathBuiltin)) {
     return m.failName(initNode, "'%s' is not a standard Math builtin", field);
   }
 
   switch (mathBuiltin.kind) {
-    case ModuleValidator::MathBuiltin::Function:
+    case ModuleValidatorShared::MathBuiltin::Function:
       return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
-    case ModuleValidator::MathBuiltin::Constant:
+    case ModuleValidatorShared::MathBuiltin::Constant:
       return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
     default:
       break;
   }
   MOZ_CRASH("unexpected or uninitialized math builtin type");
 }
 
-static bool CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName,
-                                 ParseNode* initNode) {
+static bool CheckGlobalDotImport(ModuleValidatorShared& m,
+                                 PropertyName* varName, ParseNode* initNode) {
   ParseNode* base = DotBase(initNode);
   PropertyName* field = DotMember(initNode);
 
   if (base->isKind(ParseNodeKind::DotExpr)) {
     ParseNode* global = DotBase(base);
     PropertyName* math = DotMember(base);
 
     PropertyName* globalName = m.globalArgumentName();
@@ -2970,17 +3031,17 @@ static bool CheckGlobalDotImport(ModuleV
 
   if (baseName != m.importArgumentName()) {
     return m.fail(base, "expected global or import name");
   }
 
   return m.addFFI(varName, field);
 }
 
-static bool CheckModuleGlobal(ModuleValidator& m, ParseNode* var,
+static bool CheckModuleGlobal(ModuleValidatorShared& m, ParseNode* var,
                               bool isConst) {
   if (!var->isKind(ParseNodeKind::Name)) {
     return m.fail(var, "import variable is not a plain name");
   }
 
   PropertyName* varName = var->as<NameNode>().name();
   if (!CheckModuleLevelName(m, var, varName)) {
     return false;
@@ -3054,25 +3115,25 @@ static bool CheckModuleGlobals(ModuleVal
         return false;
       }
     }
   }
 
   return true;
 }
 
-static bool ArgFail(FunctionValidator& f, PropertyName* argName,
+static bool ArgFail(FunctionValidatorShared& f, PropertyName* argName,
                     ParseNode* stmt) {
   return f.failName(stmt,
                     "expecting argument type declaration for '%s' of the "
                     "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'",
                     argName);
 }
 
-static bool CheckArgumentType(FunctionValidator& f, ParseNode* stmt,
+static bool CheckArgumentType(FunctionValidatorShared& f, ParseNode* stmt,
                               PropertyName* name, Type* type) {
   if (!stmt || !IsExpressionStatement(stmt)) {
     return ArgFail(f, name, stmt ? stmt : f.fn());
   }
 
   ParseNode* initNode = ExpressionStatementExpr(stmt);
   if (!initNode->isKind(ParseNodeKind::AssignExpr)) {
     return ArgFail(f, name, stmt);
@@ -3096,29 +3157,29 @@ static bool CheckArgumentType(FunctionVa
 
   if (!IsUseOfName(coercedExpr, name)) {
     return ArgFail(f, name, stmt);
   }
 
   return true;
 }
 
-static bool CheckProcessingDirectives(ModuleValidator& m,
+static bool CheckProcessingDirectives(ModuleValidatorShared& m,
                                       ParseNode** stmtIter) {
   ParseNode* stmt = *stmtIter;
 
   while (stmt && IsIgnoredDirective(m.cx(), stmt)) {
     stmt = NextNode(stmt);
   }
 
   *stmtIter = stmt;
   return true;
 }
 
-static bool CheckArguments(FunctionValidator& f, ParseNode** stmtIter,
+static bool CheckArguments(FunctionValidatorShared& f, ParseNode** stmtIter,
                            ValTypeVector* argTypes) {
   ParseNode* stmt = *stmtIter;
 
   unsigned numFormals;
   ParseNode* argpn = FunctionFormalParametersList(f.fn(), &numFormals);
 
   for (unsigned i = 0; i < numFormals;
        i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) {
@@ -3140,38 +3201,39 @@ static bool CheckArguments(FunctionValid
       return false;
     }
   }
 
   *stmtIter = stmt;
   return true;
 }
 
-static bool IsLiteralOrConst(FunctionValidator& f, ParseNode* pn, NumLit* lit) {
+static bool IsLiteralOrConst(FunctionValidatorShared& f, ParseNode* pn,
+                             NumLit* lit) {
   if (pn->isKind(ParseNodeKind::Name)) {
-    const ModuleValidator::Global* global =
+    const ModuleValidatorShared::Global* global =
         f.lookupGlobal(pn->as<NameNode>().name());
     if (!global ||
-        global->which() != ModuleValidator::Global::ConstantLiteral) {
+        global->which() != ModuleValidatorShared::Global::ConstantLiteral) {
       return false;
     }
 
     *lit = global->constLiteralValue();
     return true;
   }
 
   if (!IsNumericLiteral(f.m(), pn)) {
     return false;
   }
 
   *lit = ExtractNumericLiteral(f.m(), pn);
   return true;
 }
 
-static bool CheckFinalReturn(FunctionValidator& f,
+static bool CheckFinalReturn(FunctionValidatorShared& f,
                              ParseNode* lastNonEmptyStmt) {
   if (!f.encoder().writeOp(Op::End)) {
     return false;
   }
 
   if (!f.hasAlreadyReturned()) {
     f.setReturnedType(ExprType::Void);
     return true;
@@ -3181,17 +3243,17 @@ static bool CheckFinalReturn(FunctionVal
       !IsVoid(f.returnedType())) {
     return f.fail(lastNonEmptyStmt,
                   "void incompatible with previous return type");
   }
 
   return true;
 }
 
-static bool CheckVariable(FunctionValidator& f, ParseNode* var,
+static bool CheckVariable(FunctionValidatorShared& f, ParseNode* var,
                           ValTypeVector* types, Vector<NumLit>* inits) {
   if (!var->isKind(ParseNodeKind::Name)) {
     return f.fail(var, "local variable is not a plain name");
   }
 
   PropertyName* name = var->as<NameNode>().name();
 
   if (!CheckIdentifier(f.m(), var, name)) {
@@ -3216,17 +3278,17 @@ static bool CheckVariable(FunctionValida
   }
 
   Type type = Type::canonicalize(Type::lit(lit));
 
   return f.addLocal(var, name, type) &&
          types->append(type.canonicalToValType()) && inits->append(lit);
 }
 
-static bool CheckVariables(FunctionValidator& f, ParseNode** stmtIter) {
+static bool CheckVariables(FunctionValidatorShared& f, ParseNode** stmtIter) {
   ParseNode* stmt = *stmtIter;
 
   uint32_t firstVar = f.numLocals();
 
   ValTypeVector types;
   Vector<NumLit> inits(f.cx());
 
   for (; stmt && stmt->isKind(ParseNodeKind::VarStmt);
@@ -3271,59 +3333,60 @@ static bool CheckNumericLiteral(Function
   NumLit lit = ExtractNumericLiteral(f.m(), num);
   if (!lit.valid()) {
     return f.fail(num, "numeric literal out of representable integer range");
   }
   *type = Type::lit(lit);
   return f.writeConstExpr(lit);
 }
 
-static bool CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type) {
+static bool CheckVarRef(FunctionValidatorShared& f, ParseNode* varRef,
+                        Type* type) {
   PropertyName* name = varRef->as<NameNode>().name();
 
-  if (const FunctionValidator::Local* local = f.lookupLocal(name)) {
+  if (const FunctionValidatorShared::Local* local = f.lookupLocal(name)) {
     if (!f.encoder().writeOp(Op::GetLocal)) {
       return false;
     }
     if (!f.encoder().writeVarU32(local->slot)) {
       return false;
     }
     *type = local->type;
     return true;
   }
 
-  if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
+  if (const ModuleValidatorShared::Global* global = f.lookupGlobal(name)) {
     switch (global->which()) {
-      case ModuleValidator::Global::ConstantLiteral:
+      case ModuleValidatorShared::Global::ConstantLiteral:
         *type = global->varOrConstType();
         return f.writeConstExpr(global->constLiteralValue());
-      case ModuleValidator::Global::ConstantImport:
-      case ModuleValidator::Global::Variable: {
+      case ModuleValidatorShared::Global::ConstantImport:
+      case ModuleValidatorShared::Global::Variable: {
         *type = global->varOrConstType();
         return f.encoder().writeOp(Op::GetGlobal) &&
                f.encoder().writeVarU32(global->varOrConstIndex());
       }
-      case ModuleValidator::Global::Function:
-      case ModuleValidator::Global::FFI:
-      case ModuleValidator::Global::MathBuiltinFunction:
-      case ModuleValidator::Global::Table:
-      case ModuleValidator::Global::ArrayView:
-      case ModuleValidator::Global::ArrayViewCtor:
+      case ModuleValidatorShared::Global::Function:
+      case ModuleValidatorShared::Global::FFI:
+      case ModuleValidatorShared::Global::MathBuiltinFunction:
+      case ModuleValidatorShared::Global::Table:
+      case ModuleValidatorShared::Global::ArrayView:
+      case ModuleValidatorShared::Global::ArrayViewCtor:
         break;
     }
     return f.failName(varRef,
                       "'%s' may not be accessed by ordinary expressions", name);
   }
 
   return f.failName(varRef, "'%s' not found in local or asm.js module scope",
                     name);
 }
 
-static inline bool IsLiteralOrConstInt(FunctionValidator& f, ParseNode* pn,
-                                       uint32_t* u32) {
+static inline bool IsLiteralOrConstInt(FunctionValidatorShared& f,
+                                       ParseNode* pn, uint32_t* u32) {
   NumLit lit;
   if (!IsLiteralOrConst(f, pn, &lit)) {
     return false;
   }
 
   return IsLiteralInt(lit, u32);
 }
 
@@ -3331,17 +3394,17 @@ static const int32_t NoMask = -1;
 
 static bool CheckArrayAccess(FunctionValidator& f, ParseNode* viewName,
                              ParseNode* indexExpr, Scalar::Type* viewType) {
   if (!viewName->isKind(ParseNodeKind::Name)) {
     return f.fail(viewName,
                   "base of array access must be a typed array view name");
   }
 
-  const ModuleValidator::Global* global =
+  const ModuleValidatorShared::Global* global =
       f.lookupGlobal(viewName->as<NameNode>().name());
   if (!global || !global->isAnyArrayView()) {
     return f.fail(viewName,
                   "base of array access must be a typed array view name");
   }
 
   *viewType = global->viewType();
 
@@ -3412,17 +3475,18 @@ static bool CheckArrayAccess(FunctionVal
   // for a shift of zero.
   if (mask != NoMask) {
     return f.writeInt32Lit(mask) && f.encoder().writeOp(Op::I32And);
   }
 
   return true;
 }
 
-static bool WriteArrayAccessFlags(FunctionValidator& f, Scalar::Type viewType) {
+static bool WriteArrayAccessFlags(FunctionValidatorShared& f,
+                                  Scalar::Type viewType) {
   // asm.js only has naturally-aligned accesses.
   size_t align = TypedArrayElemSize(viewType);
   MOZ_ASSERT(IsPowerOfTwo(align));
   if (!f.encoder().writeFixedU8(CeilingLog2(align))) {
     return false;
   }
 
   // asm.js doesn't have constant offsets, so just encode a 0.
@@ -3584,17 +3648,17 @@ static bool CheckStoreArray(FunctionVali
   *type = rhsType;
   return true;
 }
 
 static bool CheckAssignName(FunctionValidator& f, ParseNode* lhs,
                             ParseNode* rhs, Type* type) {
   RootedPropertyName name(f.cx(), lhs->as<NameNode>().name());
 
-  if (const FunctionValidator::Local* lhsVar = f.lookupLocal(name)) {
+  if (const FunctionValidatorShared::Local* lhsVar = f.lookupLocal(name)) {
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsType)) {
       return false;
     }
 
     if (!f.encoder().writeOp(Op::TeeLocal)) {
       return false;
     }
@@ -3605,18 +3669,18 @@ static bool CheckAssignName(FunctionVali
     if (!(rhsType <= lhsVar->type)) {
       return f.failf(lhs, "%s is not a subtype of %s", rhsType.toChars(),
                      lhsVar->type.toChars());
     }
     *type = rhsType;
     return true;
   }
 
-  if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
-    if (global->which() != ModuleValidator::Global::Variable) {
+  if (const ModuleValidatorShared::Global* global = f.lookupGlobal(name)) {
+    if (global->which() != ModuleValidatorShared::Global::Variable) {
       return f.failName(lhs, "'%s' is not a mutable variable", name);
     }
 
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsType)) {
       return false;
     }
 
@@ -3817,17 +3881,17 @@ static bool CheckMathMinMax(FunctionVali
         return false;
       }
     }
   }
 
   return true;
 }
 
-using CheckArgType = bool (*)(FunctionValidator& f, ParseNode* argNode,
+using CheckArgType = bool (*)(FunctionValidatorShared& f, ParseNode* argNode,
                               Type type);
 
 template <CheckArgType checkArg>
 static bool CheckCallArgs(FunctionValidator& f, ParseNode* callNode,
                           ValTypeVector* args) {
   ParseNode* argNode = CallArgList(callNode);
   for (unsigned i = 0; i < CallArgListLength(callNode);
        i++, argNode = NextNode(argNode)) {
@@ -3842,18 +3906,18 @@ static bool CheckCallArgs(FunctionValida
 
     if (!args->append(Type::canonicalize(type).canonicalToValType())) {
       return false;
     }
   }
   return true;
 }
 
-static bool CheckSignatureAgainstExisting(ModuleValidator& m, ParseNode* usepn,
-                                          const FuncType& sig,
+static bool CheckSignatureAgainstExisting(ModuleValidatorShared& m,
+                                          ParseNode* usepn, const FuncType& sig,
                                           const FuncType& existing) {
   if (sig.args().length() != existing.args().length()) {
     return m.failf(usepn,
                    "incompatible number of arguments (%zu"
                    " here vs. %zu before)",
                    sig.args().length(), existing.args().length());
   }
 
@@ -3871,22 +3935,22 @@ static bool CheckSignatureAgainstExistin
   }
 
   MOZ_ASSERT(sig == existing);
   return true;
 }
 
 static bool CheckFunctionSignature(ModuleValidator& m, ParseNode* usepn,
                                    FuncType&& sig, PropertyName* name,
-                                   ModuleValidator::Func** func) {
+                                   ModuleValidatorShared::Func** func) {
   if (sig.args().length() > MaxParams) {
     return m.failf(usepn, "too many parameters");
   }
 
-  ModuleValidator::Func* existing = m.lookupFuncDef(name);
+  ModuleValidatorShared::Func* existing = m.lookupFuncDef(name);
   if (!existing) {
     if (!CheckModuleLevelName(m, usepn, name)) {
       return false;
     }
     return m.addFuncDef(name, usepn->pn_pos.begin, std::move(sig), func);
   }
 
   const FuncTypeWithId& existingSig =
@@ -3895,17 +3959,17 @@ static bool CheckFunctionSignature(Modul
   if (!CheckSignatureAgainstExisting(m, usepn, sig, existingSig)) {
     return false;
   }
 
   *func = existing;
   return true;
 }
 
-static bool CheckIsArgType(FunctionValidator& f, ParseNode* argNode,
+static bool CheckIsArgType(FunctionValidatorShared& f, ParseNode* argNode,
                            Type type) {
   if (!type.isArgType()) {
     return f.failf(argNode, "%s is not a subtype of int, float, or double",
                    type.toChars());
   }
   return true;
 }
 
@@ -3915,17 +3979,17 @@ static bool CheckInternalCall(FunctionVa
 
   ValTypeVector args;
   if (!CheckCallArgs<CheckIsArgType>(f, callNode, &args)) {
     return false;
   }
 
   FuncType sig(std::move(args), ret.canonicalToExprType());
 
-  ModuleValidator::Func* callee;
+  ModuleValidatorShared::Func* callee;
   if (!CheckFunctionSignature(f.m(), callNode, std::move(sig), calleeName,
                               &callee)) {
     return false;
   }
 
   if (!f.writeCall(callNode, MozOp::OldCallDirect)) {
     return false;
   }
@@ -3938,22 +4002,22 @@ static bool CheckInternalCall(FunctionVa
   return true;
 }
 
 static bool CheckFuncPtrTableAgainstExisting(ModuleValidator& m,
                                              ParseNode* usepn,
                                              PropertyName* name, FuncType&& sig,
                                              unsigned mask,
                                              uint32_t* tableIndex) {
-  if (const ModuleValidator::Global* existing = m.lookupGlobal(name)) {
-    if (existing->which() != ModuleValidator::Global::Table) {
+  if (const ModuleValidatorShared::Global* existing = m.lookupGlobal(name)) {
+    if (existing->which() != ModuleValidatorShared::Global::Table) {
       return m.failName(usepn, "'%s' is not a function-pointer table", name);
     }
 
-    ModuleValidator::Table& table = m.table(existing->tableIndex());
+    ModuleValidatorShared::Table& table = m.table(existing->tableIndex());
     if (mask != table.mask()) {
       return m.failf(usepn, "mask does not match previous value (%u)",
                      table.mask());
     }
 
     if (!CheckSignatureAgainstExisting(
             m, usepn, sig, m.env().types[table.sigIndex()].funcType())) {
       return false;
@@ -3983,18 +4047,18 @@ static bool CheckFuncPtrCall(FunctionVal
   ParseNode* tableNode = ElemBase(callee);
   ParseNode* indexExpr = ElemIndex(callee);
 
   if (!tableNode->isKind(ParseNodeKind::Name)) {
     return f.fail(tableNode, "expecting name of function-pointer array");
   }
 
   PropertyName* name = tableNode->as<NameNode>().name();
-  if (const ModuleValidator::Global* existing = f.lookupGlobal(name)) {
-    if (existing->which() != ModuleValidator::Global::Table) {
+  if (const ModuleValidatorShared::Global* existing = f.lookupGlobal(name)) {
+    if (existing->which() != ModuleValidatorShared::Global::Table) {
       return f.failName(
           tableNode, "'%s' is not the name of a function-pointer array", name);
     }
   }
 
   if (!indexExpr->isKind(ParseNodeKind::BitAndExpr)) {
     return f.fail(indexExpr,
                   "function-pointer table index expression needs & mask");
@@ -4042,17 +4106,17 @@ static bool CheckFuncPtrCall(FunctionVal
   if (!f.encoder().writeVarU32(f.m().table(tableIndex).sigIndex())) {
     return false;
   }
 
   *type = Type::ret(ret);
   return true;
 }
 
-static bool CheckIsExternType(FunctionValidator& f, ParseNode* argNode,
+static bool CheckIsExternType(FunctionValidatorShared& f, ParseNode* argNode,
                               Type type) {
   if (!type.isExtern()) {
     return f.failf(argNode, "%s is not a subtype of extern", type.toChars());
   }
   return true;
 }
 
 static bool CheckFFICall(FunctionValidator& f, ParseNode* callNode,
@@ -4085,18 +4149,18 @@ static bool CheckFFICall(FunctionValidat
   if (!f.encoder().writeVarU32(importIndex)) {
     return false;
   }
 
   *type = Type::ret(ret);
   return true;
 }
 
-static bool CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode,
-                                  Type inputType) {
+static bool CheckFloatCoercionArg(FunctionValidatorShared& f,
+                                  ParseNode* inputNode, Type inputType) {
   if (inputType.isMaybeDouble()) {
     return f.encoder().writeOp(Op::F32DemoteF64);
   }
   if (inputType.isSigned()) {
     return f.encoder().writeOp(Op::F32ConvertSI32);
   }
   if (inputType.isUnsigned()) {
     return f.encoder().writeOp(Op::F32ConvertUI32);
@@ -4305,30 +4369,30 @@ static bool CheckMathBuiltinCall(Functio
   *type = opIsDouble ? Type::Double : Type::Floatish;
   return true;
 }
 
 static bool CheckUncoercedCall(FunctionValidator& f, ParseNode* expr,
                                Type* type) {
   MOZ_ASSERT(expr->isKind(ParseNodeKind::CallExpr));
 
-  const ModuleValidator::Global* global;
+  const ModuleValidatorShared::Global* global;
   if (IsCallToGlobal(f.m(), expr, &global) && global->isMathFunction()) {
     return CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), type);
   }
 
   return f.fail(
       expr,
       "all function calls must be calls to standard lib math functions,"
       " ignored (via f(); or comma-expression), coerced to signed (via f()|0),"
       " coerced to float (via fround(f())), or coerced to double (via +f())");
 }
 
-static bool CoerceResult(FunctionValidator& f, ParseNode* expr, Type expected,
-                         Type actual, Type* type) {
+static bool CoerceResult(FunctionValidatorShared& f, ParseNode* expr,
+                         Type expected, Type actual, Type* type) {
   MOZ_ASSERT(expected.isCanonical());
 
   // At this point, the bytecode resembles this:
   //      | the thing we wanted to coerce | current position |>
   switch (expected.which()) {
     case Type::Void:
       if (!actual.isVoid()) {
         if (!f.encoder().writeOp(Op::Drop)) {
@@ -4409,31 +4473,32 @@ static bool CheckCoercedCall(FunctionVal
   }
 
   if (!callee->isKind(ParseNodeKind::Name)) {
     return f.fail(callee, "unexpected callee expression type");
   }
 
   PropertyName* calleeName = callee->as<NameNode>().name();
 
-  if (const ModuleValidator::Global* global = f.lookupGlobal(calleeName)) {
+  if (const ModuleValidatorShared::Global* global =
+          f.lookupGlobal(calleeName)) {
     switch (global->which()) {
-      case ModuleValidator::Global::FFI:
+      case ModuleValidatorShared::Global::FFI:
         return CheckFFICall(f, call, global->ffiIndex(), ret, type);
-      case ModuleValidator::Global::MathBuiltinFunction:
+      case ModuleValidatorShared::Global::MathBuiltinFunction:
         return CheckCoercedMathBuiltinCall(
             f, call, global->mathBuiltinFunction(), ret, type);
-      case ModuleValidator::Global::ConstantLiteral:
-      case ModuleValidator::Global::ConstantImport:
-      case ModuleValidator::Global::Variable:
-      case ModuleValidator::Global::Table:
-      case ModuleValidator::Global::ArrayView:
-      case ModuleValidator::Global::ArrayViewCtor:
+      case ModuleValidatorShared::Global::ConstantLiteral:
+      case ModuleValidatorShared::Global::ConstantImport:
+      case ModuleValidatorShared::Global::Variable:
+      case ModuleValidatorShared::Global::Table:
+      case ModuleValidatorShared::Global::ArrayView:
+      case ModuleValidatorShared::Global::ArrayViewCtor:
         return f.failName(callee, "'%s' is not callable function", calleeName);
-      case ModuleValidator::Global::Function:
+      case ModuleValidatorShared::Global::Function:
         break;
     }
   }
 
   return CheckInternalCall(f, call, calleeName, ret, type);
 }
 
 static bool CheckPos(FunctionValidator& f, ParseNode* pos, Type* type) {
@@ -5472,17 +5537,17 @@ recurse:
     if (!f.popIf()) {
       return false;
     }
   }
 
   return true;
 }
 
-static bool CheckCaseExpr(FunctionValidator& f, ParseNode* caseExpr,
+static bool CheckCaseExpr(FunctionValidatorShared& f, ParseNode* caseExpr,
                           int32_t* value) {
   if (!IsNumericLiteral(f.m(), caseExpr)) {
     return f.fail(caseExpr,
                   "switch case expression must be an integer literal");
   }
 
   NumLit lit = ExtractNumericLiteral(f.m(), caseExpr);
   switch (lit.which()) {
@@ -5497,27 +5562,27 @@ static bool CheckCaseExpr(FunctionValida
     case NumLit::Float:
       return f.fail(caseExpr,
                     "switch case expression must be an integer literal");
   }
 
   return true;
 }
 
-static bool CheckDefaultAtEnd(FunctionValidator& f, ParseNode* stmt) {
+static bool CheckDefaultAtEnd(FunctionValidatorShared& f, ParseNode* stmt) {
   for (; stmt; stmt = NextNode(stmt)) {
     if (IsDefaultCase(stmt) && NextNode(stmt) != nullptr) {
       return f.fail(stmt, "default label must be at the end");
     }
   }
 
   return true;
 }
 
-static bool CheckSwitchRange(FunctionValidator& f, ParseNode* stmt,
+static bool CheckSwitchRange(FunctionValidatorShared& f, ParseNode* stmt,
                              int32_t* low, int32_t* high,
                              uint32_t* tableLength) {
   if (IsDefaultCase(stmt)) {
     *low = 0;
     *high = -1;
     *tableLength = 0;
     return true;
   }
@@ -5718,17 +5783,18 @@ static bool CheckSwitch(FunctionValidato
 
   // Close the wrapping block.
   if (!f.popBreakableBlock()) {
     return false;
   }
   return true;
 }
 
-static bool CheckReturnType(FunctionValidator& f, ParseNode* usepn, Type ret) {
+static bool CheckReturnType(FunctionValidatorShared& f, ParseNode* usepn,
+                            Type ret) {
   if (!f.hasAlreadyReturned()) {
     f.setReturnedType(ret.canonicalToExprType());
     return true;
   }
 
   if (f.returnedType() != ret.canonicalToExprType()) {
     return f.failf(usepn, "%s incompatible with previous return of type %s",
                    Type::ret(ret).toChars(), ToCString(f.returnedType()));
@@ -5790,17 +5856,17 @@ static bool CheckLexicalScope(FunctionVa
   LexicalScopeNode* lexicalScope = &node->as<LexicalScopeNode>();
   if (!lexicalScope->isEmptyScope()) {
     return f.fail(lexicalScope, "cannot have 'let' or 'const' declarations");
   }
 
   return CheckStatement(f, lexicalScope->scopeBody());
 }
 
-static bool CheckBreakOrContinue(FunctionValidator& f, bool isBreak,
+static bool CheckBreakOrContinue(FunctionValidatorShared& f, bool isBreak,
                                  ParseNode* stmt) {
   if (PropertyName* maybeLabel = LoopControlMaybeLabel(stmt)) {
     return f.writeLabeledBreakOrContinue(maybeLabel, isBreak);
   }
   return f.writeUnlabeledBreakOrContinue(isBreak);
 }
 
 static bool CheckStatement(FunctionValidator& f, ParseNode* stmt) {
@@ -5952,36 +6018,36 @@ static bool CheckFunction(ModuleValidato
       return false;
     }
   }
 
   if (!CheckFinalReturn(f, lastNonEmptyStmt)) {
     return false;
   }
 
-  ModuleValidator::Func* func = nullptr;
+  ModuleValidatorShared::Func* func = nullptr;
   if (!CheckFunctionSignature(m, funNode,
                               FuncType(std::move(args), f.returnedType()),
                               FunctionName(funNode), &func)) {
     return false;
   }
 
   if (func->defined()) {
     return m.failName(funNode, "function '%s' already defined",
                       FunctionName(funNode));
   }
 
   f.define(func, line);
 
   return true;
 }
 
-static bool CheckAllFunctionsDefined(ModuleValidator& m) {
+static bool CheckAllFunctionsDefined(ModuleValidatorShared& m) {
   for (unsigned i = 0; i < m.numFuncDefs(); i++) {
-    const ModuleValidator::Func& f = m.funcDef(i);
+    const ModuleValidatorShared::Func& f = m.funcDef(i);
     if (!f.defined()) {
       return m.failNameOffset(f.firstUse(), "missing definition of function %s",
                               f.name());
     }
   }
 
   return true;
 }
@@ -6030,17 +6096,17 @@ static bool CheckFuncPtrTable(ModuleVali
   const FuncType* sig = nullptr;
   for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
     if (!elem->isKind(ParseNodeKind::Name)) {
       return m.fail(
           elem, "function-pointer table's elements must be names of functions");
     }
 
     PropertyName* funcName = elem->as<NameNode>().name();
-    const ModuleValidator::Func* func = m.lookupFuncDef(funcName);
+    const ModuleValidatorShared::Func* func = m.lookupFuncDef(funcName);
     if (!func) {
       return m.fail(
           elem, "function-pointer table's elements must be names of functions");
     }
 
     const FuncType& funcSig = m.env().types[func->sigIndex()].funcType();
     if (sig) {
       if (*sig != funcSig) {
@@ -6085,43 +6151,44 @@ static bool CheckFuncPtrTables(ModuleVal
     for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
       if (!CheckFuncPtrTable(m, var)) {
         return false;
       }
     }
   }
 
   for (unsigned i = 0; i < m.numFuncPtrTables(); i++) {
-    ModuleValidator::Table& table = m.table(i);
+    ModuleValidatorShared::Table& table = m.table(i);
     if (!table.defined()) {
       return m.failNameOffset(table.firstUse(),
                               "function-pointer table %s wasn't defined",
                               table.name());
     }
   }
 
   return true;
 }
 
-static bool CheckModuleExportFunction(ModuleValidator& m, ParseNode* pn,
+static bool CheckModuleExportFunction(ModuleValidatorShared& m, ParseNode* pn,
                                       PropertyName* maybeFieldName = nullptr) {
   if (!pn->isKind(ParseNodeKind::Name)) {
     return m.fail(pn, "expected name of exported function");
   }
 
   PropertyName* funcName = pn->as<NameNode>().name();
-  const ModuleValidator::Func* func = m.lookupFuncDef(funcName);
+  const ModuleValidatorShared::Func* func = m.lookupFuncDef(funcName);
   if (!func) {
     return m.failName(pn, "function '%s' not found", funcName);
   }
 
   return m.addExportField(*func, maybeFieldName);
 }
 
-static bool CheckModuleExportObject(ModuleValidator& m, ParseNode* object) {
+static bool CheckModuleExportObject(ModuleValidatorShared& m,
+                                    ParseNode* object) {
   MOZ_ASSERT(object->isKind(ParseNodeKind::ObjectExpr));
 
   for (ParseNode* pn = ListHead(object); pn; pn = NextNode(pn)) {
     if (!IsNormalObjectField(pn)) {
       return m.fail(pn,
                     "only normal object properties may be used in the export "
                     "object literal");
     }