Bug 1237508 - Odin: make AsmJSModule derive wasm::Module (r?bbouvier) draft
authorLuke Wagner <luke@mozilla.com>
Wed, 06 Jan 2016 23:24:56 -0600
changeset 319572 1a0ba2f985ebde4e009ff46f7e23d5bf036cab07
parent 319571 164f5f36138a1a4e6666527579b5827bb7c91c4a
child 512613 6731ce4ac7dd1bacfc72f67db60a500d731a3b3b
push id9054
push userlwagner@mozilla.com
push dateThu, 07 Jan 2016 05:25:31 +0000
reviewersbbouvier
bugs1237508
milestone46.0a1
Bug 1237508 - Odin: make AsmJSModule derive wasm::Module (r?bbouvier)
js/public/Utility.h
js/src/asmjs/AsmJS.cpp
js/src/asmjs/AsmJS.h
js/src/asmjs/WasmGenerator.cpp
js/src/asmjs/WasmGenerator.h
js/src/asmjs/WasmModule.cpp
js/src/asmjs/WasmModule.h
js/src/jsobj.cpp
js/src/jsscript.h
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -455,16 +455,24 @@ SCOPED_TEMPLATE(ScopedReleasePtr, Scoped
 
 } /* namespace js */
 
 namespace JS {
 
 template<typename T>
 struct DeletePolicy
 {
+    MOZ_CONSTEXPR DeletePolicy() {}
+
+    template<typename U>
+    MOZ_IMPLICIT DeletePolicy(DeletePolicy<U> other,
+                              typename mozilla::EnableIf<mozilla::IsConvertible<U*, T*>::value,
+                                                         int>::Type dummy = 0)
+    {}
+
     void operator()(const T* ptr) {
         js_delete(const_cast<T*>(ptr));
     }
 };
 
 struct FreePolicy
 {
     void operator()(const void* ptr) {
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -49,28 +49,35 @@ using namespace js;
 using namespace js::frontend;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::Compression::LZ4;
 using mozilla::HashGeneric;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
-using mozilla::MallocSizeOf;
 using mozilla::Move;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::PodZero;
 using mozilla::PositiveInfinity;
 using JS::AsmJSOption;
 using JS::GenericNaN;
 
+
 /*****************************************************************************/
 // asm.js module object
 
+static void
+TraceNameField(JSTracer* trc, PropertyName* const* name, const char* label)
+{
+    if (*name)
+        TraceManuallyBarrieredEdge(trc, const_cast<PropertyName**>(name), label);
+}
+
 // The asm.js spec recognizes this set of builtin Math functions.
 enum AsmJSMathBuiltinFunction
 {
     AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
     AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
     AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
     AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
     AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
@@ -118,604 +125,363 @@ IsSignedIntSimdType(AsmJSSimdType type)
 // Set of known operations, for a given SIMD type (int32x4, float32x4,...)
 enum AsmJSSimdOperation
 {
 #define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op,
     FORALL_SIMD_ASMJS_OP(ASMJSSIMDOPERATION)
 #undef ASMJSSIMDOPERATION
 };
 
-// An AsmJSModule extends (via containment) a wasm::Module with the extra
-// persistent state necessary to represent a compiled asm.js module.
-class js::AsmJSModule
+// An AsmJSGlobal represents a JS global variable in the asm.js module function.
+class AsmJSGlobal
 {
   public:
-    class Global
-    {
-      public:
-        enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
-                     AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation };
-        enum VarInitKind { InitConstant, InitImport };
-        enum ConstantKind { GlobalConstant, MathConstant };
-
-      private:
-        struct CacheablePod {
-            Which which_;
-            union {
-                struct {
-                    uint32_t globalDataOffset_;
-                    VarInitKind initKind_;
-                    union {
-                        wasm::ValType importType_;
-                        wasm::Val val_;
-                    } u;
-                } var;
-                uint32_t ffiIndex_;
-                Scalar::Type viewType_;
-                AsmJSMathBuiltinFunction mathBuiltinFunc_;
-                AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
-                AsmJSSimdType simdCtorType_;
-                struct {
-                    AsmJSSimdType type_;
-                    AsmJSSimdOperation which_;
-                } simdOp;
-                struct {
-                    ConstantKind kind_;
-                    double value_;
-                } constant;
-            } u;
-        } pod;
-        PropertyName* name_;
-
-        friend class AsmJSModule;
-
-        Global(Which which, PropertyName* name) {
-            mozilla::PodZero(&pod);  // zero padding for Valgrind
-            pod.which_ = which;
-            name_ = name;
-            MOZ_ASSERT_IF(name_, name_->isTenured());
-        }
-
-        void trace(JSTracer* trc) {
-            if (name_)
-                TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name");
-        }
-
-      public:
-        Global() {}
-        Which which() const {
-            return pod.which_;
-        }
-        uint32_t varGlobalDataOffset() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            return pod.u.var.globalDataOffset_;
-        }
-        VarInitKind varInitKind() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            return pod.u.var.initKind_;
-        }
-        wasm::Val varInitVal() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
-            return pod.u.var.u.val_;
-        }
-        wasm::ValType varInitImportType() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
-            return pod.u.var.u.importType_;
-        }
-        PropertyName* varImportField() const {
-            MOZ_ASSERT(pod.which_ == Variable);
-            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
-            return name_;
-        }
-        PropertyName* ffiField() const {
-            MOZ_ASSERT(pod.which_ == FFI);
-            return name_;
-        }
-        uint32_t ffiIndex() const {
-            MOZ_ASSERT(pod.which_ == FFI);
-            return pod.u.ffiIndex_;
-        }
-        // When a view is created from an imported constructor:
-        //   var I32 = stdlib.Int32Array;
-        //   var i32 = new I32(buffer);
-        // the second import has nothing to validate and thus has a null field.
-        PropertyName* maybeViewName() const {
-            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
-            return name_;
-        }
-        Scalar::Type viewType() const {
-            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
-            return pod.u.viewType_;
-        }
-        PropertyName* mathName() const {
-            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
-            return name_;
-        }
-        PropertyName* atomicsName() const {
-            MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
-            return name_;
-        }
-        AsmJSMathBuiltinFunction mathBuiltinFunction() const {
-            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
-            return pod.u.mathBuiltinFunc_;
-        }
-        AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
-            MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
-            return pod.u.atomicsBuiltinFunc_;
-        }
-        AsmJSSimdType simdCtorType() const {
-            MOZ_ASSERT(pod.which_ == SimdCtor);
-            return pod.u.simdCtorType_;
-        }
-        PropertyName* simdCtorName() const {
-            MOZ_ASSERT(pod.which_ == SimdCtor);
-            return name_;
-        }
-        PropertyName* simdOperationName() const {
-            MOZ_ASSERT(pod.which_ == SimdOperation);
-            return name_;
-        }
-        AsmJSSimdOperation simdOperation() const {
-            MOZ_ASSERT(pod.which_ == SimdOperation);
-            return pod.u.simdOp.which_;
-        }
-        AsmJSSimdType simdOperationType() const {
-            MOZ_ASSERT(pod.which_ == SimdOperation);
-            return pod.u.simdOp.type_;
-        }
-        PropertyName* constantName() const {
-            MOZ_ASSERT(pod.which_ == Constant);
-            return name_;
-        }
-        ConstantKind constantKind() const {
-            MOZ_ASSERT(pod.which_ == Constant);
-            return pod.u.constant.kind_;
-        }
-        double constantValue() const {
-            MOZ_ASSERT(pod.which_ == Constant);
-            return pod.u.constant.value_;
-        }
-
-        WASM_DECLARE_SERIALIZABLE(Global);
-    };
-
-    typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
-
-    // An import is slightly different than an asm.js FFI function: a single
-    // asm.js FFI function can be called with many different signatures. When
-    // compiled to wasm, each unique FFI function paired with signature
-    // generates a wasm import.
-    class Import
-    {
-        uint32_t ffiIndex_;
-      public:
-        Import() = default;
-        explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
-        uint32_t ffiIndex() const { return ffiIndex_; }
-    };
-
-    typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
-
-    class Export
-    {
-        PropertyName* name_;
-        PropertyName* maybeFieldName_;
-        struct CacheablePod {
-            uint32_t startOffsetInModule_;  // Store module-start-relative offsets
-            uint32_t endOffsetInModule_;    // so preserved by serialization.
-        } pod;
-
-      public:
-        Export() {}
-        Export(PropertyName* name, PropertyName* maybeFieldName,
-               uint32_t startOffsetInModule, uint32_t endOffsetInModule)
-          : name_(name),
-            maybeFieldName_(maybeFieldName)
-        {
-            MOZ_ASSERT(name_->isTenured());
-            MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
-            pod.startOffsetInModule_ = startOffsetInModule;
-            pod.endOffsetInModule_ = endOffsetInModule;
-        }
-
-        void trace(JSTracer* trc) {
-            TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name");
-            if (maybeFieldName_)
-                TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field");
-        }
-
-        PropertyName* name() const {
-            return name_;
-        }
-        PropertyName* maybeFieldName() const {
-            return maybeFieldName_;
-        }
-        uint32_t startOffsetInModule() const {
-            return pod.startOffsetInModule_;
-        }
-        uint32_t endOffsetInModule() const {
-            return pod.endOffsetInModule_;
-        }
-
-        WASM_DECLARE_SERIALIZABLE(Export)
-    };
-
-    typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
-
-    typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule;
+    enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
+                 AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation };
+    enum VarInitKind { InitConstant, InitImport };
+    enum ConstantKind { GlobalConstant, MathConstant };
 
   private:
-    UniqueWasmModule            wasmModule_;
-    wasm::UniqueStaticLinkData  linkData_;
     struct CacheablePod {
-        uint32_t                minHeapLength_;
-        uint32_t                numFFIs_;
-        uint32_t                srcLength_;
-        uint32_t                srcLengthWithRightBrace_;
-        bool                    strict_;
-        bool                    hasArrayView_;
-        bool                    isSharedView_;
+        Which which_;
+        union {
+            struct {
+                uint32_t globalDataOffset_;
+                VarInitKind initKind_;
+                union {
+                    ValType importType_;
+                    Val val_;
+                } u;
+            } var;
+            uint32_t ffiIndex_;
+            Scalar::Type viewType_;
+            AsmJSMathBuiltinFunction mathBuiltinFunc_;
+            AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
+            AsmJSSimdType simdCtorType_;
+            struct {
+                AsmJSSimdType type_;
+                AsmJSSimdOperation which_;
+            } simdOp;
+            struct {
+                ConstantKind kind_;
+                double value_;
+            } constant;
+        } u;
     } pod;
-    const ScriptSourceHolder    scriptSource_;
-    const uint32_t              srcStart_;
-    const uint32_t              srcBodyStart_;
-    GlobalVector                globals_;
-    ImportVector                imports_;
-    ExportVector                exports_;
-    PropertyName*               globalArgumentName_;
-    PropertyName*               importArgumentName_;
-    PropertyName*               bufferArgumentName_;
+    PropertyName* name_;
+
+    friend class ModuleValidator;
 
   public:
-    explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
-                         bool strict)
-      : scriptSource_(scriptSource),
-        srcStart_(srcStart),
-        srcBodyStart_(srcBodyStart),
-        globalArgumentName_(nullptr),
-        importArgumentName_(nullptr),
-        bufferArgumentName_(nullptr)
+    AsmJSGlobal() {}
+    AsmJSGlobal(Which which, PropertyName* name) {
+        mozilla::PodZero(&pod);  // zero padding for Valgrind
+        pod.which_ = which;
+        name_ = name;
+        MOZ_ASSERT_IF(name_, name_->isTenured());
+    }
+    void trace(JSTracer* trc) const {
+        TraceNameField(trc, &name_, "asm.js global name");
+    }
+    Which which() const {
+        return pod.which_;
+    }
+    uint32_t varGlobalDataOffset() const {
+        MOZ_ASSERT(pod.which_ == Variable);
+        return pod.u.var.globalDataOffset_;
+    }
+    VarInitKind varInitKind() const {
+        MOZ_ASSERT(pod.which_ == Variable);
+        return pod.u.var.initKind_;
+    }
+    Val varInitVal() const {
+        MOZ_ASSERT(pod.which_ == Variable);
+        MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
+        return pod.u.var.u.val_;
+    }
+    ValType varInitImportType() const {
+        MOZ_ASSERT(pod.which_ == Variable);
+        MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
+        return pod.u.var.u.importType_;
+    }
+    PropertyName* varImportField() const {
+        MOZ_ASSERT(pod.which_ == Variable);
+        MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
+        return name_;
+    }
+    PropertyName* ffiField() const {
+        MOZ_ASSERT(pod.which_ == FFI);
+        return name_;
+    }
+    uint32_t ffiIndex() const {
+        MOZ_ASSERT(pod.which_ == FFI);
+        return pod.u.ffiIndex_;
+    }
+    // When a view is created from an imported constructor:
+    //   var I32 = stdlib.Int32Array;
+    //   var i32 = new I32(buffer);
+    // the second import has nothing to validate and thus has a null field.
+    PropertyName* maybeViewName() const {
+        MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
+        return name_;
+    }
+    Scalar::Type viewType() const {
+        MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
+        return pod.u.viewType_;
+    }
+    PropertyName* mathName() const {
+        MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
+        return name_;
+    }
+    PropertyName* atomicsName() const {
+        MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
+        return name_;
+    }
+    AsmJSMathBuiltinFunction mathBuiltinFunction() const {
+        MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
+        return pod.u.mathBuiltinFunc_;
+    }
+    AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
+        MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
+        return pod.u.atomicsBuiltinFunc_;
+    }
+    AsmJSSimdType simdCtorType() const {
+        MOZ_ASSERT(pod.which_ == SimdCtor);
+        return pod.u.simdCtorType_;
+    }
+    PropertyName* simdCtorName() const {
+        MOZ_ASSERT(pod.which_ == SimdCtor);
+        return name_;
+    }
+    PropertyName* simdOperationName() const {
+        MOZ_ASSERT(pod.which_ == SimdOperation);
+        return name_;
+    }
+    AsmJSSimdOperation simdOperation() const {
+        MOZ_ASSERT(pod.which_ == SimdOperation);
+        return pod.u.simdOp.which_;
+    }
+    AsmJSSimdType simdOperationType() const {
+        MOZ_ASSERT(pod.which_ == SimdOperation);
+        return pod.u.simdOp.type_;
+    }
+    PropertyName* constantName() const {
+        MOZ_ASSERT(pod.which_ == Constant);
+        return name_;
+    }
+    ConstantKind constantKind() const {
+        MOZ_ASSERT(pod.which_ == Constant);
+        return pod.u.constant.kind_;
+    }
+    double constantValue() const {
+        MOZ_ASSERT(pod.which_ == Constant);
+        return pod.u.constant.value_;
+    }
+
+    WASM_DECLARE_SERIALIZABLE(AsmJSGlobal);
+};
+
+typedef Vector<AsmJSGlobal, 0, SystemAllocPolicy> AsmJSGlobalVector;
+
+// An AsmJSImport is slightly different than an asm.js FFI function: a single
+// asm.js FFI function can be called with many different signatures. When
+// compiled to wasm, each unique FFI function paired with signature generates a
+// wasm import.
+class AsmJSImport
+{
+    uint32_t ffiIndex_;
+  public:
+    AsmJSImport() = default;
+    explicit AsmJSImport(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
+    uint32_t ffiIndex() const { return ffiIndex_; }
+};
+
+typedef Vector<AsmJSImport, 0, SystemAllocPolicy> AsmJSImportVector;
+
+// An AsmJSExport represents a field in the export object.
+class AsmJSExport
+{
+    PropertyName* name_;
+    PropertyName* maybeFieldName_;
+    struct CacheablePod {
+        uint32_t startOffsetInModule_;  // Store module-start-relative offsets
+        uint32_t endOffsetInModule_;    // so preserved by serialization.
+    } pod;
+
+  public:
+    AsmJSExport() {}
+    AsmJSExport(PropertyName* name, PropertyName* maybeFieldName,
+                uint32_t startOffsetInModule, uint32_t endOffsetInModule)
+      : name_(name),
+        maybeFieldName_(maybeFieldName)
     {
-        mozilla::PodZero(&pod);
-        pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
-        pod.strict_ = strict;
-
-        MOZ_ASSERT(srcStart_ <= srcBodyStart_);
-
-        // AsmJSCheckedImmediateRange should be defined to be at most the minimum
-        // heap length so that offsets can be folded into bounds checks.
-        MOZ_ASSERT(pod.minHeapLength_ - jit::AsmJSCheckedImmediateRange <= pod.minHeapLength_);
-    }
-
-    void trace(JSTracer* trc) {
-        if (wasmModule_)
-            wasmModule_->trace(trc);
-        for (Global& global : globals_)
+        MOZ_ASSERT(name_->isTenured());
+        MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
+        pod.startOffsetInModule_ = startOffsetInModule;
+        pod.endOffsetInModule_ = endOffsetInModule;
+    }
+    void trace(JSTracer* trc) const {
+        TraceNameField(trc, &name_, "asm.js export name");
+        TraceNameField(trc, &maybeFieldName_, "asm.js export field");
+    }
+    PropertyName* name() const {
+        return name_;
+    }
+    PropertyName* maybeFieldName() const {
+        return maybeFieldName_;
+    }
+    uint32_t startOffsetInModule() const {
+        return pod.startOffsetInModule_;
+    }
+    uint32_t endOffsetInModule() const {
+        return pod.endOffsetInModule_;
+    }
+
+    WASM_DECLARE_SERIALIZABLE(AsmJSExport)
+};
+
+typedef Vector<AsmJSExport, 0, SystemAllocPolicy> AsmJSExportVector;
+
+// Holds the trivially-memcpy()able, serializable portion of AsmJSModuleData.
+struct AsmJSModuleCacheablePod
+{
+    uint32_t                minHeapLength;
+    uint32_t                numFFIs;
+    uint32_t                srcLength;
+    uint32_t                srcLengthWithRightBrace;
+};
+
+// Holds the immutable guts of an AsmJSModule. This struct is mutably built up
+// by ModuleValidator and then handed over to the AsmJSModule constructor in
+// finish().
+struct AsmJSModuleData
+{
+    AsmJSModuleCacheablePod pod;
+    AsmJSGlobalVector       globals;
+    AsmJSImportVector       imports;
+    AsmJSExportVector       exports;
+    PropertyName*           globalArgumentName;
+    PropertyName*           importArgumentName;
+    PropertyName*           bufferArgumentName;
+
+    // These values are not serialized since they are relative to the
+    // containing script which can be different between serialization and
+    // deserialization contexts. Thus, they must be set explicitly using the
+    // ambient Parser/ScriptSource after deserialization. Cloning, however,
+    // preserves the same exact parsing context and can copy these values.
+    uint32_t                srcStart;
+    uint32_t                srcBodyStart;
+    bool                    strict;
+    ScriptSourceHolder      scriptSource;
+
+    AsmJSModuleData()
+      : globalArgumentName(nullptr),
+        importArgumentName(nullptr),
+        bufferArgumentName(nullptr),
+        srcStart(0),
+        srcBodyStart(0),
+        strict(false)
+    {
+        PodZero(&pod);
+    }
+
+    void trace(JSTracer* trc) const {
+        for (const AsmJSGlobal& global : globals)
             global.trace(trc);
-        for (Export& exp : exports_)
+        for (const AsmJSExport& exp : exports)
             exp.trace(trc);
-        if (globalArgumentName_)
-            TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
-        if (importArgumentName_)
-            TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name");
-        if (bufferArgumentName_)
-            TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name");
-    }
-
-    /*************************************************************************/
-    // These functions may be used as soon as the module is constructed:
-
-    ScriptSource* scriptSource() const {
-        return scriptSource_.get();
-    }
-    bool strict() const {
-        return pod.strict_;
-    }
+        TraceNameField(trc, &globalArgumentName, "asm.js global argument name");
+        TraceNameField(trc, &importArgumentName, "asm.js import argument name");
+        TraceNameField(trc, &bufferArgumentName, "asm.js buffer argument name");
+    }
+
+    WASM_DECLARE_SERIALIZABLE(AsmJSModuleData)
+};
+
+typedef UniquePtr<AsmJSModuleData, JS::DeletePolicy<AsmJSModuleData>> UniqueAsmJSModuleData;
+
+// An AsmJSModule is-a Module with the extra persistent state necessary to
+// represent a compiled asm.js module.
+class js::AsmJSModule final : public Module
+{
+    typedef UniquePtr<const AsmJSModuleData,
+                      JS::DeletePolicy<const AsmJSModuleData>> UniqueConstAsmJSModuleData;
+    typedef UniquePtr<const StaticLinkData,
+                      JS::DeletePolicy<const StaticLinkData>> UniqueConstStaticLinkData;
+
+    const UniqueConstStaticLinkData linkData_;
+    const UniqueConstAsmJSModuleData data_;
+
+  public:
+    AsmJSModule(UniqueModuleData baseData,
+                UniqueStaticLinkData linkData,
+                UniqueAsmJSModuleData data)
+      : Module(Move(baseData), Module::IsAsmJS),
+        linkData_(Move(linkData)),
+        data_(Move(data))
+    {}
+
+    virtual void trace(JSTracer* trc) override {
+        Module::trace(trc);
+        data_->trace(trc);
+    }
+
+    uint32_t minHeapLength() const { return data_->pod.minHeapLength; }
+    uint32_t numFFIs() const { return data_->pod.numFFIs; }
+    bool strict() const { return data_->strict; }
+    ScriptSource* scriptSource() const { return data_->scriptSource.get(); }
+    const AsmJSGlobalVector& asmJSGlobals() const { return data_->globals; }
+    const AsmJSImportVector& asmJSImports() const { return data_->imports; }
+    const AsmJSExportVector& asmJSExports() const { return data_->exports; }
+    PropertyName* globalArgumentName() const { return data_->globalArgumentName; }
+    PropertyName* importArgumentName() const { return data_->importArgumentName; }
+    PropertyName* bufferArgumentName() const { return data_->bufferArgumentName; }
 
     // srcStart() refers to the offset in the ScriptSource to the beginning of
     // the asm.js module function. If the function has been created with the
     // Function constructor, this will be the first character in the function
     // source. Otherwise, it will be the opening parenthesis of the arguments
     // list.
     uint32_t srcStart() const {
-        return srcStart_;
+        return data_->srcStart;
+    }
+    uint32_t srcEndBeforeCurly() const {
+        return data_->srcStart + data_->pod.srcLength;
+    }
+    uint32_t srcEndAfterCurly() const {
+        return data_->srcStart + data_->pod.srcLengthWithRightBrace;
     }
 
     // srcBodyStart() refers to the offset in the ScriptSource to the end
     // of the 'use asm' string-literal token.
     uint32_t srcBodyStart() const {
-        return srcBodyStart_;
-    }
-
-    // While these functions may be accessed at any time, their values will
-    // change as the module is compiled.
-    uint32_t minHeapLength() const {
-        return pod.minHeapLength_;
-    }
-
-    void initGlobalArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT_IF(n, n->isTenured());
-        globalArgumentName_ = n;
-    }
-    void initImportArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT_IF(n, n->isTenured());
-        importArgumentName_ = n;
-    }
-    void initBufferArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT_IF(n, n->isTenured());
-        bufferArgumentName_ = n;
-    }
-    PropertyName* globalArgumentName() const {
-        return globalArgumentName_;
-    }
-    PropertyName* importArgumentName() const {
-        return importArgumentName_;
-    }
-    PropertyName* bufferArgumentName() const {
-        return bufferArgumentName_;
-    }
-
-    bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::Variable, nullptr);
-        g.pod.u.var.initKind_ = Global::InitConstant;
-        g.pod.u.var.u.val_ = v;
-        g.pod.u.var.globalDataOffset_ = globalDataOffset;
-        return globals_.append(g);
-    }
-    bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::Variable, name);
-        g.pod.u.var.initKind_ = Global::InitImport;
-        g.pod.u.var.u.importType_ = importType;
-        g.pod.u.var.globalDataOffset_ = globalDataOffset;
-        return globals_.append(g);
-    }
-    // See Import comment above for FFI vs. Import.
-    bool addFFI(PropertyName* field, uint32_t* ffiIndex) {
-        MOZ_ASSERT(!isFinished());
-        if (pod.numFFIs_ == UINT32_MAX)
-            return false;
-        Global g(Global::FFI, field);
-        g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
-        return globals_.append(g);
-    }
-    bool addArrayView(Scalar::Type vt, PropertyName* maybeField) {
-        MOZ_ASSERT(!isFinished());
-        pod.hasArrayView_ = true;
-        pod.isSharedView_ = false;
-        Global g(Global::ArrayView, maybeField);
-        g.pod.u.viewType_ = vt;
-        return globals_.append(g);
-    }
-    bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT(field);
-        pod.isSharedView_ = false;
-        Global g(Global::ArrayViewCtor, field);
-        g.pod.u.viewType_ = vt;
-        return globals_.append(g);
-    }
-    bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::MathBuiltinFunction, field);
-        g.pod.u.mathBuiltinFunc_ = func;
-        return globals_.append(g);
-    }
-    bool addMathBuiltinConstant(double value, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::Constant, field);
-        g.pod.u.constant.value_ = value;
-        g.pod.u.constant.kind_ = Global::MathConstant;
-        return globals_.append(g);
-    }
-    bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::AtomicsBuiltinFunction, field);
-        g.pod.u.atomicsBuiltinFunc_ = func;
-        return globals_.append(g);
-    }
-    bool addSimdCtor(AsmJSSimdType type, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::SimdCtor, field);
-        g.pod.u.simdCtorType_ = type;
-        return globals_.append(g);
-    }
-    bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::SimdOperation, field);
-        g.pod.u.simdOp.type_ = type;
-        g.pod.u.simdOp.which_ = op;
-        return globals_.append(g);
-    }
-    bool addGlobalConstant(double value, PropertyName* name) {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::Constant, name);
-        g.pod.u.constant.value_ = value;
-        g.pod.u.constant.kind_ = Global::GlobalConstant;
-        return globals_.append(g);
-    }
-    // See Import comment above for FFI vs. Import.
-    bool addImport(uint32_t ffiIndex, uint32_t importIndex) {
-        MOZ_ASSERT(imports_.length() == importIndex);
-        return imports_.emplaceBack(ffiIndex);
-    }
-    bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t begin, uint32_t end) {
-        // The begin/end offsets are given relative to the ScriptSource (the
-        // entire file) and ExportedFunctions store offsets relative to the
-        // beginning of the module (so that they are caching-invariant).
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT(srcStart_ < begin);
-        MOZ_ASSERT(begin < end);
-        uint32_t startOffsetInModule = begin - srcStart_;
-        uint32_t endOffsetInModule = end - srcStart_;
-        return exports_.emplaceBack(name, maybeFieldName, startOffsetInModule, endOffsetInModule);
-    }
-
-    const GlobalVector& globals() const {
-        return globals_;
-    }
-    const ImportVector& imports() const {
-        return imports_;
-    }
-    const ExportVector& exports() const {
-        return exports_;
-    }
-
-    void setViewsAreShared() {
-        if (pod.hasArrayView_)
-            pod.isSharedView_ = true;
-    }
-    bool hasArrayView() const {
-        return pod.hasArrayView_;
-    }
-    bool isSharedView() const {
-        return pod.isSharedView_;
-    }
-    void requireHeapLengthToBeAtLeast(uint32_t len) {
-        MOZ_ASSERT(!isFinished());
-        len = RoundUpToNextValidAsmJSHeapLength(len);
-        if (len > pod.minHeapLength_)
-            pod.minHeapLength_ = len;
-    }
-
-    /*************************************************************************/
-    // A module isFinished() when compilation completes. After being finished,
-    // a module must be statically and dynamically linked before execution.
-
-    bool isFinished() const {
-        return !!wasmModule_;
-    }
-    void finish(wasm::Module* wasmModule, wasm::UniqueStaticLinkData linkData,
-                uint32_t endBeforeCurly, uint32_t endAfterCurly)
-    {
-        MOZ_ASSERT(!isFinished());
-
-        wasmModule_.reset(wasmModule);
-        linkData_ = Move(linkData);
-
-        MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
-        MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
-        pod.srcLength_ = endBeforeCurly - srcStart_;
-        pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
-
-        MOZ_ASSERT(isFinished());
-    }
-
-    /*************************************************************************/
-    // These accessor functions can only be used after finish():
-
-    wasm::Module& wasmModule() const {
-        MOZ_ASSERT(isFinished());
-        return *wasmModule_;
-    }
-    uint32_t numFFIs() const {
-        MOZ_ASSERT(isFinished());
-        return pod.numFFIs_;
-    }
-    uint32_t srcEndBeforeCurly() const {
-        MOZ_ASSERT(isFinished());
-        return srcStart_ + pod.srcLength_;
-    }
-    uint32_t srcEndAfterCurly() const {
-        MOZ_ASSERT(isFinished());
-        return srcStart_ + pod.srcLengthWithRightBrace_;
-    }
+        return data_->srcBodyStart;
+    }
+
     bool staticallyLink(ExclusiveContext* cx) {
-        return wasmModule_->staticallyLink(cx, *linkData_);
-    }
-
-    // See WASM_DECLARE_SERIALIZABLE.
+        return Module::staticallyLink(cx, *linkData_);
+    }
+
+    // Clone this AsmJSModule into a new AsmJSModule that isn't statically or
+    // dynamically linked for cases where a single asm.js module function is
+    // linked several times.
+    bool clone(JSContext* cx, MutableHandleWasmModule moduleObj) const;
+
+    // These are the top-level serialization functions used by caching to
+    // cache an AsmJSModule.
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor) const;
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
-    bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const;
-    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
-};
-
-static void
-AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj)
-{
-    AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
-    if (moduleObj.hasModule())
-        fop->delete_(&moduleObj.module());
-}
-
-static void
-AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj)
-{
-    AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
-    if (moduleObj.hasModule())
-        moduleObj.module().trace(trc);
-}
-
-const Class AsmJSModuleObject::class_ = {
-    "AsmJSModuleObject",
-    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
-    JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    AsmJSModuleObject_finalize,
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    AsmJSModuleObject_trace
+    static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor,
+                                      AsmJSParser& parser, MutableHandleWasmModule moduleObj);
+
+    // This function is called for memory reporting and Overrides
+    // Module::addSizeOfMisc() to add extra memory used for asm.js.
+    virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) override;
 };
 
-static AsmJSModuleObject*
-NewAsmJSModuleObject(ExclusiveContext* cx)
-{
-    AutoSetNewObjectMetadata metadata(cx);
-    JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr);
-    if (!obj)
-        return nullptr;
-
-    return &obj->as<AsmJSModuleObject>();
-}
-
-bool
-AsmJSModuleObject::hasModule() const
-{
-    MOZ_ASSERT(is<AsmJSModuleObject>());
-    return !getReservedSlot(MODULE_SLOT).isUndefined();
-}
-
-void
-AsmJSModuleObject::setModule(AsmJSModule* newModule)
-{
-    MOZ_ASSERT(is<AsmJSModuleObject>());
-    if (hasModule())
-        js_delete(&module());
-    setReservedSlot(MODULE_SLOT, PrivateValue(newModule));
-}
-
-AsmJSModule&
-AsmJSModuleObject::module() const
-{
-    MOZ_ASSERT(is<AsmJSModuleObject>());
-    return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate();
-}
-
-void
-AsmJSModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
-{
-    if (hasModule())
-        module().addSizeOfMisc(mallocSizeOf, code, data);
-}
-
 /*****************************************************************************/
 // ParseNode utilities
 
 static inline ParseNode*
 NextNode(ParseNode* pn)
 {
     return pn->pn_next;
 }
@@ -1241,17 +1007,17 @@ class NumLit
         MOZ_ASSERT(which_ != OutOfRangeInt);
         return u.scalar_;
     }
 
     bool isSimd() const {
         return which_ == Int32x4 || which_ == Float32x4 || which_ == Bool32x4;
     }
 
-    const jit::SimdConstant& simdValue() const {
+    const SimdConstant& simdValue() const {
         MOZ_ASSERT(isSimd());
         return u.simd_;
     }
 
     bool valid() const {
         return which_ != OutOfRangeInt;
     }
 
@@ -1575,18 +1341,16 @@ class Type
           case Void:        return "void";
         }
         MOZ_CRASH("Invalid Type");
     }
 };
 
 static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 
-namespace {
-
 // 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
@@ -1839,87 +1603,45 @@ class MOZ_STACK_CLASS ModuleValidator
     };
 
   private:
     typedef HashMap<PropertyName*, Global*> GlobalMap;
     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
     typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
     typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap;
     typedef Vector<ArrayView> ArrayViewVector;
-
-  public:
     typedef HashMap<ImportDescriptor, unsigned, ImportDescriptor> ImportMap;
 
-  private:
-    ExclusiveContext*    cx_;
-    AsmJSParser&         parser_;
-
-    ModuleGenerator      mg_;
-    AsmJSModule*         module_;
-
-    LifoAlloc            validationLifo_;
-    FuncVector           functions_;
-    FuncPtrTableVector   funcPtrTables_;
-    GlobalMap            globals_;
-    ArrayViewVector      arrayViews_;
-    ImportMap            imports_;
-
-    MathNameMap          standardLibraryMathNames_;
-    AtomicsNameMap       standardLibraryAtomicsNames_;
-    SimdOperationNameMap standardLibrarySimdOpNames_;
-
-    ParseNode*           moduleFunctionNode_;
-    PropertyName*        moduleFunctionName_;
-
-    UniqueChars          errorString_;
-    uint32_t             errorOffset_;
-    bool                 errorOverRecursed_;
-
-    bool                 supportsSimd_;
-    bool                 atomicsPresent_;
-
-  public:
-    ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
-      : cx_(cx),
-        parser_(parser),
-        mg_(cx),
-        validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
-        functions_(cx),
-        funcPtrTables_(cx),
-        globals_(cx),
-        arrayViews_(cx),
-        imports_(cx),
-        standardLibraryMathNames_(cx),
-        standardLibraryAtomicsNames_(cx),
-        standardLibrarySimdOpNames_(cx),
-        moduleFunctionNode_(parser.pc->maybeFunction),
-        moduleFunctionName_(nullptr),
-        errorString_(nullptr),
-        errorOffset_(UINT32_MAX),
-        errorOverRecursed_(false),
-        supportsSimd_(cx->jitSupportsSimd()),
-        atomicsPresent_(false)
-    {
-        MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
-    }
-
-    ~ModuleValidator() {
-        if (errorString_) {
-            MOZ_ASSERT(errorOffset_ != UINT32_MAX);
-            tokenStream().reportAsmJSError(errorOffset_,
-                                           JSMSG_USE_ASM_TYPE_FAIL,
-                                           errorString_.get());
-        }
-        if (errorOverRecursed_)
-            ReportOverRecursed(cx_);
-    }
-
-  private:
-
-    // Helpers
+    ExclusiveContext*     cx_;
+    AsmJSParser&          parser_;
+    ParseNode*            moduleFunctionNode_;
+    PropertyName*         moduleFunctionName_;
+    MathNameMap           standardLibraryMathNames_;
+    AtomicsNameMap        standardLibraryAtomicsNames_;
+    SimdOperationNameMap  standardLibrarySimdOpNames_;
+
+    // Validation-internal state:
+    LifoAlloc             validationLifo_;
+    FuncVector            functions_;
+    FuncPtrTableVector    funcPtrTables_;
+    GlobalMap             globalMap_;
+    ImportMap             importMap_;
+    ArrayViewVector       arrayViews_;
+    bool                  atomicsPresent_;
+
+    // State used to build the AsmJSModule in finish():
+    ModuleGenerator       mg_;
+    UniqueAsmJSModuleData moduleData_;
+
+    // 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) {
@@ -1938,19 +1660,62 @@ class MOZ_STACK_CLASS ModuleValidator
     bool addStandardLibrarySimdOpName(const char* name, AsmJSSimdOperation op) {
         JSAtom* atom = Atomize(cx_, name, strlen(name));
         if (!atom)
             return false;
         return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
     }
 
   public:
-
-    bool init(HandleAsmJSModule moduleObj) {
-        if (!globals_.init() || !imports_.init())
+    ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* moduleFunctionNode)
+      : cx_(cx),
+        parser_(parser),
+        moduleFunctionNode_(moduleFunctionNode),
+        moduleFunctionName_(FunctionName(moduleFunctionNode)),
+        standardLibraryMathNames_(cx),
+        standardLibraryAtomicsNames_(cx),
+        standardLibrarySimdOpNames_(cx),
+        validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
+        functions_(cx),
+        funcPtrTables_(cx),
+        globalMap_(cx),
+        importMap_(cx),
+        arrayViews_(cx),
+        atomicsPresent_(false),
+        mg_(cx),
+        errorString_(nullptr),
+        errorOffset_(UINT32_MAX),
+        errorOverRecursed_(false)
+    {
+
+    }
+
+    ~ModuleValidator() {
+        if (errorString_) {
+            MOZ_ASSERT(errorOffset_ != UINT32_MAX);
+            tokenStream().reportAsmJSError(errorOffset_,
+                                           JSMSG_USE_ASM_TYPE_FAIL,
+                                           errorString_.get());
+        }
+        if (errorOverRecursed_)
+            ReportOverRecursed(cx_);
+    }
+
+    bool init() {
+        moduleData_ = cx_->make_unique<AsmJSModuleData>();
+        if (!moduleData_)
+            return false;
+
+        moduleData_->pod.minHeapLength = RoundUpToNextValidAsmJSHeapLength(0);
+        moduleData_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin;
+        moduleData_->srcBodyStart = parser_.tokenStream.currentToken().pos.end;
+        moduleData_->strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict();
+        moduleData_->scriptSource.reset(parser_.ss);
+
+        if (!globalMap_.init() || !importMap_.init())
             return false;
 
         if (!standardLibraryMathNames_.init() ||
             !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
             !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
             !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
             !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
             !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) ||
@@ -1999,216 +1764,243 @@ class MOZ_STACK_CLASS ModuleValidator
 #define ADDSTDLIBSIMDOPNAME(op) || !addStandardLibrarySimdOpName(#op, AsmJSSimdOperation_##op)
         if (!standardLibrarySimdOpNames_.init()
             FORALL_SIMD_ASMJS_OP(ADDSTDLIBSIMDOPNAME))
         {
             return false;
         }
 #undef ADDSTDLIBSIMDOPNAME
 
-        uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
-        uint32_t srcBodyStart = tokenStream().currentToken().pos.end;
-
-        // "use strict" should be added to the source if we are in an implicit
-        // strict context, see also comment above addUseStrict in
-        // js::FunctionToString.
-        bool strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict();
-
-        module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict);
-        if (!module_)
-            return false;
-
-        moduleObj->setModule(module_);
-
         return mg_.init();
     }
 
-    bool finish(SlowFunctionVector* slowFuncs) {
-        uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
-        TokenPos pos;
-        JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
-        uint32_t endAfterCurly = pos.end;
-
-        HeapUsage heapUsage = module_->hasArrayView()
-                              ? module_->isSharedView()
-                                ? HeapUsage::Shared
-                                : HeapUsage::Unshared
-                              : HeapUsage::None;
-
-        auto mutedErrors = Module::MutedBool(parser_.ss->mutedErrors());
-
-        CacheableChars filename;
-        if (parser_.ss->filename()) {
-            filename = make_string_copy(parser_.ss->filename());
-            if (!filename)
-                return false;
-        }
-
-        CacheableTwoByteChars displayURL;
-        if (parser_.ss->hasDisplayURL()) {
-            uint32_t length = js_strlen(parser_.ss->displayURL());
-            displayURL.reset(js_pod_calloc<char16_t>(length + 1));
-            if (!displayURL)
-                return false;
-            PodCopy(displayURL.get(), parser_.ss->displayURL(), length);
-        }
-
-        UniqueStaticLinkData linkData;
-        Module* wasm = mg_.finish(heapUsage, mutedErrors, Move(filename), Move(displayURL),
-                                  &linkData, slowFuncs);
-        if (!wasm)
-            return false;
-
-        module_->finish(wasm, Move(linkData), endBeforeCurly, endAfterCurly);
-        return true;
-    }
-
-    // Mutable interface.
-    void initModuleFunctionName(PropertyName* name) { moduleFunctionName_ = name; }
-    void initGlobalArgumentName(PropertyName* n)    { module().initGlobalArgumentName(n); }
-    void initImportArgumentName(PropertyName* n)    { module().initImportArgumentName(n); }
-    void initBufferArgumentName(PropertyName* n)    { module().initBufferArgumentName(n); }
-
+    ExclusiveContext* cx() const             { return cx_; }
+    PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
+    PropertyName* globalArgumentName() const { return moduleData_->globalArgumentName; }
+    PropertyName* importArgumentName() const { return moduleData_->importArgumentName; }
+    PropertyName* bufferArgumentName() const { return moduleData_->bufferArgumentName; }
+    ModuleGenerator& mg()                    { return mg_; }
+    AsmJSParser& parser() const              { return parser_; }
+    TokenStream& tokenStream() const         { return parser_.tokenStream; }
+    bool supportsSimd() const                { return cx_->jitSupportsSimd(); }
+    bool atomicsPresent() const              { return atomicsPresent_; }
+    uint32_t minHeapLength() const           { return moduleData_->pod.minHeapLength; }
+
+    void initModuleFunctionName(PropertyName* name) {
+        MOZ_ASSERT(!moduleFunctionName_);
+        moduleFunctionName_ = name;
+    }
+    void initGlobalArgumentName(PropertyName* n) {
+        MOZ_ASSERT(n->isTenured());
+        moduleData_->globalArgumentName = n;
+    }
+    void initImportArgumentName(PropertyName* n) {
+        MOZ_ASSERT(n->isTenured());
+        moduleData_->importArgumentName = n;
+    }
+    void initBufferArgumentName(PropertyName* n) {
+        MOZ_ASSERT(n->isTenured());
+        moduleData_->bufferArgumentName = n;
+    }
     bool addGlobalVarInit(PropertyName* var, const NumLit& lit, bool isConst) {
         uint32_t globalDataOffset;
         if (!mg_.allocateGlobalVar(lit.type(), &globalDataOffset))
             return false;
+
         Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
         Global* global = validationLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.globalDataOffset_ = globalDataOffset;
         global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : Type::var(lit.type())).which();
         if (isConst)
             global->u.varOrConst.literalValue_ = lit;
-        return globals_.putNew(var, global) &&
-               module().addGlobalVarInit(lit.value(), globalDataOffset);
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
+        g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
+        g.pod.u.var.u.val_ = lit.value();
+        g.pod.u.var.globalDataOffset_ = globalDataOffset;
+        return moduleData_->globals.append(g);
     }
     bool addGlobalVarImport(PropertyName* var, PropertyName* field, ValType type, bool isConst) {
         uint32_t globalDataOffset;
         if (!mg_.allocateGlobalVar(type, &globalDataOffset))
             return false;
+
         Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
         Global* global = validationLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.globalDataOffset_ = globalDataOffset;
         global->u.varOrConst.type_ = Type::var(type).which();
-        return globals_.putNew(var, global) &&
-               module().addGlobalVarImport(field, type, globalDataOffset);
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::Variable, field);
+        g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
+        g.pod.u.var.u.importType_ = type;
+        g.pod.u.var.globalDataOffset_ = globalDataOffset;
+        return moduleData_->globals.append(g);
     }
     bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) {
         if (!arrayViews_.append(ArrayView(var, vt)))
             return false;
+
         Global* global = validationLifo_.new_<Global>(Global::ArrayView);
         if (!global)
             return false;
         global->u.viewInfo.viewType_ = vt;
-        return globals_.putNew(var, global) &&
-               module().addArrayView(vt, maybeField);
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::ArrayView, maybeField);
+        g.pod.u.viewType_ = vt;
+        return moduleData_->globals.append(g);
     }
     bool addMathBuiltinFunction(PropertyName* var, AsmJSMathBuiltinFunction func,
                                 PropertyName* field)
     {
         Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction);
         if (!global)
             return false;
         global->u.mathBuiltinFunc_ = func;
-        return globals_.putNew(var, global) &&
-               module().addMathBuiltinFunction(func, field);
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::MathBuiltinFunction, field);
+        g.pod.u.mathBuiltinFunc_ = func;
+        return moduleData_->globals.append(g);
     }
   private:
     bool addGlobalDoubleConstant(PropertyName* var, double constant) {
         Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral);
         if (!global)
             return false;
         global->u.varOrConst.type_ = Type::Double;
         global->u.varOrConst.literalValue_ = NumLit(NumLit::Double, DoubleValue(constant));
-        return globals_.putNew(var, global);
+        return globalMap_.putNew(var, global);
     }
   public:
     bool addMathBuiltinConstant(PropertyName* var, double constant, PropertyName* field) {
-        return addGlobalDoubleConstant(var, constant) &&
-               module().addMathBuiltinConstant(constant, field);
+        if (!addGlobalDoubleConstant(var, constant))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::Constant, field);
+        g.pod.u.constant.value_ = constant;
+        g.pod.u.constant.kind_ = AsmJSGlobal::MathConstant;
+        return moduleData_->globals.append(g);
     }
     bool addGlobalConstant(PropertyName* var, double constant, PropertyName* field) {
-        return addGlobalDoubleConstant(var, constant) &&
-               module().addGlobalConstant(constant, field);
+        if (!addGlobalDoubleConstant(var, constant))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::Constant, field);
+        g.pod.u.constant.value_ = constant;
+        g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant;
+        return moduleData_->globals.append(g);
     }
     bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func,
                                    PropertyName* field)
     {
+        atomicsPresent_ = true;
+
         Global* global = validationLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
         if (!global)
             return false;
-        atomicsPresent_ = true;
         global->u.atomicsBuiltinFunc_ = func;
-        return globals_.putNew(var, global) &&
-               module().addAtomicsBuiltinFunction(func, field);
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::AtomicsBuiltinFunction, field);
+        g.pod.u.atomicsBuiltinFunc_ = func;
+        return moduleData_->globals.append(g);
     }
     bool addSimdCtor(PropertyName* var, AsmJSSimdType type, PropertyName* field) {
         Global* global = validationLifo_.new_<Global>(Global::SimdCtor);
         if (!global)
             return false;
         global->u.simdCtorType_ = type;
-        return globals_.putNew(var, global) &&
-               module().addSimdCtor(type, field);
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::SimdCtor, field);
+        g.pod.u.simdCtorType_ = type;
+        return moduleData_->globals.append(g);
     }
     bool addSimdOperation(PropertyName* var, AsmJSSimdType type, AsmJSSimdOperation op,
                           PropertyName* opName)
     {
         Global* global = validationLifo_.new_<Global>(Global::SimdOperation);
         if (!global)
             return false;
         global->u.simdOp.type_ = type;
         global->u.simdOp.which_ = op;
-        return globals_.putNew(var, global) &&
-               module().addSimdOperation(type, op, opName);
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::SimdOperation, opName);
+        g.pod.u.simdOp.type_ = type;
+        g.pod.u.simdOp.which_ = op;
+        return moduleData_->globals.append(g);
     }
     bool addArrayViewCtor(PropertyName* var, Scalar::Type vt, PropertyName* field) {
         Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
         if (!global)
             return false;
         global->u.viewInfo.viewType_ = vt;
-        return globals_.putNew(var, global) &&
-               module().addArrayViewCtor(vt, field);
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::ArrayViewCtor, field);
+        g.pod.u.viewType_ = vt;
+        return moduleData_->globals.append(g);
     }
     bool addFFI(PropertyName* var, PropertyName* field) {
+        if (moduleData_->pod.numFFIs == UINT32_MAX)
+            return false;
+        uint32_t ffiIndex = moduleData_->pod.numFFIs++;
+
         Global* global = validationLifo_.new_<Global>(Global::FFI);
         if (!global)
             return false;
-        uint32_t index;
-        if (!module().addFFI(field, &index))
-            return false;
-        global->u.ffiIndex_ = index;
-        return globals_.putNew(var, global);
+        global->u.ffiIndex_ = ffiIndex;
+        if (!globalMap_.putNew(var, global))
+            return false;
+
+        AsmJSGlobal g(AsmJSGlobal::FFI, field);
+        g.pod.u.ffiIndex_ = ffiIndex;
+        return moduleData_->globals.append(g);
     }
     bool addExport(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) {
         MallocSig::ArgVector args;
         if (!args.appendAll(func.sig().args()))
             return false;
         MallocSig sig(Move(args), func.sig().ret());
         return mg_.declareExport(Move(sig), func.index()) &&
-               module().addExport(func.name(), maybeFieldName, func.srcBegin(), func.srcEnd());
+               moduleData_->exports.emplaceBack(func.name(), maybeFieldName,
+                                                func.srcBegin() - moduleData_->srcStart,
+                                                func.srcEnd() - moduleData_->srcStart);
     }
   private:
     const LifoSig* getLifoSig(const LifoSig& sig) {
         return &sig;
     }
     const LifoSig* getLifoSig(const MallocSig& sig) {
         return mg_.newLifoSig(sig);
     }
   public:
     bool addFunction(PropertyName* name, uint32_t firstUse, const MallocSig& sig, Func** func) {
         uint32_t funcIndex = numFunctions();
         Global* global = validationLifo_.new_<Global>(Global::Function);
         if (!global)
             return false;
         global->u.funcIndex_ = funcIndex;
-        if (!globals_.putNew(name, global))
+        if (!globalMap_.putNew(name, global))
             return false;
         const LifoSig* lifoSig = getLifoSig(sig);
         if (!lifoSig)
             return false;
         *func = validationLifo_.new_<Func>(name, firstUse, *lifoSig, funcIndex);
         return *func && functions_.append(*func);
     }
     template <class SigT>
@@ -2217,17 +2009,17 @@ class MOZ_STACK_CLASS ModuleValidator
     {
         if (!mg_.declareFuncPtrTable(/* numElems = */ mask + 1, index))
             return false;
         MOZ_ASSERT(*index == numFuncPtrTables());
         Global* global = validationLifo_.new_<Global>(Global::FuncPtrTable);
         if (!global)
             return false;
         global->u.funcPtrTableIndex_ = *index;
-        if (!globals_.putNew(name, global))
+        if (!globalMap_.putNew(name, global))
             return false;
         const LifoSig* lifoSig = getLifoSig(sig);
         if (!lifoSig)
             return false;
         FuncPtrTable* t = validationLifo_.new_<FuncPtrTable>(cx_, name, firstUse, *lifoSig, mask);
         return t && funcPtrTables_.append(t);
     }
     bool defineFuncPtrTable(uint32_t funcPtrTableIndex, const Vector<uint32_t>& elems) {
@@ -2237,43 +2029,44 @@ class MOZ_STACK_CLASS ModuleValidator
         table.define();
         mg_.defineFuncPtrTable(funcPtrTableIndex, elems);
         return true;
     }
     bool addImport(PropertyName* name, MallocSig&& sig, unsigned ffiIndex, unsigned* importIndex,
                  const LifoSig** lifoSig)
     {
         ImportDescriptor::Lookup lookup(name, sig);
-        ImportMap::AddPtr p = imports_.lookupForAdd(lookup);
+        ImportMap::AddPtr p = importMap_.lookupForAdd(lookup);
         if (p) {
             *lifoSig = &p->key().sig();
             *importIndex = p->value();
             return true;
         }
         *lifoSig = getLifoSig(sig);
         if (!*lifoSig)
             return false;
-        return mg_.declareImport(Move(sig), importIndex) &&
-               imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) &&
-               module().addImport(ffiIndex, *importIndex);
+        if (!mg_.declareImport(Move(sig), importIndex))
+            return false;
+        if (!importMap_.add(p, ImportDescriptor(name, **lifoSig), *importIndex))
+            return false;
+        MOZ_ASSERT(moduleData_->imports.length() == *importIndex);
+        return moduleData_->imports.emplaceBack(ffiIndex);
     }
 
     bool tryConstantAccess(uint64_t start, uint64_t width) {
         MOZ_ASSERT(UINT64_MAX - start > width);
-        uint64_t end = start + width;
-        if (end > uint64_t(INT32_MAX) + 1)
-            return false;
-        module().requireHeapLengthToBeAtLeast(end);
+        uint64_t len = start + width;
+        if (len > uint64_t(INT32_MAX) + 1)
+            return false;
+        len = RoundUpToNextValidAsmJSHeapLength(len);
+        if (len > moduleData_->pod.minHeapLength)
+            moduleData_->pod.minHeapLength = len;
         return true;
     }
 
-    bool usesSharedMemory() const {
-        return atomicsPresent_;
-    }
-
     // Error handling.
     bool hasAlreadyFailed() const {
         return !!errorString_;
     }
 
     bool failOffset(uint32_t offset, const char* str) {
         MOZ_ASSERT(!hasAlreadyFailed());
         MOZ_ASSERT(errorOffset_ == UINT32_MAX);
@@ -2325,26 +2118,16 @@ class MOZ_STACK_CLASS ModuleValidator
         return failNameOffset(pn->pn_pos.begin, fmt, name);
     }
 
     bool failOverRecursed() {
         errorOverRecursed_ = true;
         return false;
     }
 
-    // Read-only interface
-    ExclusiveContext* cx() const             { return cx_; }
-    ParseNode* moduleFunctionNode() const    { return moduleFunctionNode_; }
-    PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
-    ModuleGenerator& mg()                    { return mg_; }
-    AsmJSModule& module() const              { return *module_; }
-    AsmJSParser& parser() const              { return parser_; }
-    TokenStream& tokenStream() const         { return parser_.tokenStream; }
-    bool supportsSimd() const                { return supportsSimd_; }
-
     unsigned numArrayViews() const {
         return arrayViews_.length();
     }
     const ArrayView& arrayView(unsigned i) const {
         return arrayViews_[i];
     }
     unsigned numFunctions() const {
         return functions_.length();
@@ -2355,23 +2138,23 @@ class MOZ_STACK_CLASS ModuleValidator
     unsigned numFuncPtrTables() const {
         return funcPtrTables_.length();
     }
     FuncPtrTable& funcPtrTable(unsigned i) const {
         return *funcPtrTables_[i];
     }
 
     const Global* lookupGlobal(PropertyName* name) const {
-        if (GlobalMap::Ptr p = globals_.lookup(name))
+        if (GlobalMap::Ptr p = globalMap_.lookup(name))
             return p->value();
         return nullptr;
     }
 
     Func* lookupFunction(PropertyName* name) {
-        if (GlobalMap::Ptr p = globals_.lookup(name)) {
+        if (GlobalMap::Ptr p = globalMap_.lookup(name)) {
             Global* value = p->value();
             if (value->which() == Global::Function)
                 return functions_[value->funcIndex()];
         }
         return nullptr;
     }
 
     bool lookupStandardLibraryMathName(PropertyName* name, MathBuiltin* mathBuiltin) const {
@@ -2393,32 +2176,86 @@ class MOZ_STACK_CLASS ModuleValidator
             *op = p->value();
             return true;
         }
         return false;
     }
 
     bool startFunctionBodies() {
         if (atomicsPresent_) {
-#if defined(ENABLE_SHARED_ARRAY_BUFFER)
-            module().setViewsAreShared();
-#else
+#if !defined(ENABLE_SHARED_ARRAY_BUFFER)
             return failOffset(parser_.tokenStream.currentToken().pos.begin,
                               "shared memory and atomics not supported by this build");
 #endif
         }
         return true;
     }
     bool finishFunctionBodies() {
         return mg_.finishFuncs();
     }
+    bool finish(MutableHandleWasmModule moduleObj, SlowFunctionVector* slowFuncs) {
+        moduleObj.set(WasmModuleObject::create(cx_));
+        if (!moduleObj)
+            return false;
+
+        HeapUsage heapUsage = arrayViews_.empty()
+                              ? HeapUsage::None
+                              : atomicsPresent_
+                                ? HeapUsage::Shared
+                                : HeapUsage::Unshared;
+
+        auto mutedErrors = MutedErrorsBool(parser_.ss->mutedErrors());
+
+        CacheableChars filename;
+        if (parser_.ss->filename()) {
+            filename = make_string_copy(parser_.ss->filename());
+            if (!filename)
+                return false;
+        }
+
+        CacheableTwoByteChars displayURL;
+        if (parser_.ss->hasDisplayURL()) {
+            uint32_t length = js_strlen(parser_.ss->displayURL());
+            displayURL.reset(js_pod_calloc<char16_t>(length + 1));
+            if (!displayURL)
+                return false;
+            PodCopy(displayURL.get(), parser_.ss->displayURL(), length);
+        }
+
+
+        uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
+        moduleData_->pod.srcLength = endBeforeCurly - moduleData_->srcStart;
+
+        TokenPos pos;
+        JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
+        uint32_t endAfterCurly = pos.end;
+        moduleData_->pod.srcLengthWithRightBrace = endAfterCurly - moduleData_->srcStart;
+
+        UniqueModuleData baseData;
+        UniqueStaticLinkData linkData;
+        if (!mg_.finish(heapUsage,
+                        mutedErrors,
+                        Move(filename),
+                        Move(displayURL),
+                        &baseData,
+                        &linkData,
+                        slowFuncs))
+        {
+            return false;
+        }
+
+        auto module = cx_->make_unique<AsmJSModule>(Move(baseData), Move(linkData), Move(moduleData_));
+        if (!module)
+            return false;
+
+        moduleObj->initModule(Move(module));
+        return true;
+    }
 };
 
-} // namespace
-
 /*****************************************************************************/
 // Numeric literal utilities
 
 static bool
 IsNumericNonFloatLiteral(ParseNode* pn)
 {
     // Note: '-' is never rolled into the number; numbers are always positive
     // and negations must be applied manually.
@@ -2742,17 +2579,16 @@ class MOZ_STACK_CLASS FunctionValidator
       : m_(m),
         fn_(fn),
         locals_(m.cx()),
         labels_(m.cx()),
         hasAlreadyReturned_(false)
     {}
 
     ModuleValidator& m() const        { return m_; }
-    const AsmJSModule& module() const { return m_.module(); }
     FuncIR& funcIR() const            { return fg_.func(); }
     ExclusiveContext* cx() const      { return m_.cx(); }
     ParseNode* fn() const             { return fn_; }
 
     bool init(PropertyName* name, unsigned line, unsigned column) {
         return locals_.init() &&
                labels_.init() &&
                m_.mg().startFunc(name, line, column, &fg_);
@@ -2966,19 +2802,19 @@ CheckIdentifier(ModuleValidator& m, Pars
 
 static bool
 CheckModuleLevelName(ModuleValidator& m, ParseNode* usepn, PropertyName* name)
 {
     if (!CheckIdentifier(m, usepn, name))
         return false;
 
     if (name == m.moduleFunctionName() ||
-        name == m.module().globalArgumentName() ||
-        name == m.module().importArgumentName() ||
-        name == m.module().bufferArgumentName() ||
+        name == m.globalArgumentName() ||
+        name == m.importArgumentName() ||
+        name == m.bufferArgumentName() ||
         m.lookupGlobal(name))
     {
         return m.failName(usepn, "duplicate name '%s' not allowed", name);
     }
 
     return true;
 }
 
@@ -3113,17 +2949,17 @@ CheckGlobalVariableImportExpr(ModuleVali
                               ParseNode* coercedExpr, bool isConst)
 {
     if (!coercedExpr->isKind(PNK_DOT))
         return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
 
     ParseNode* base = DotBase(coercedExpr);
     PropertyName* field = DotMember(coercedExpr);
 
-    PropertyName* importName = m.module().importArgumentName();
+    PropertyName* importName = m.importArgumentName();
     if (!importName)
         return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter");
     if (!IsUseOfName(base, importName))
         return m.failName(coercedExpr, "base of import expression must be '%s'", importName);
 
     return m.addGlobalVarImport(varName, field, coerceTo, isConst);
 }
 
@@ -3175,21 +3011,21 @@ CheckNewArrayViewArgs(ModuleValidator& m
         return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
 
     return true;
 }
 
 static bool
 CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
 {
-    PropertyName* globalName = m.module().globalArgumentName();
+    PropertyName* globalName = m.globalArgumentName();
     if (!globalName)
         return m.fail(newExpr, "cannot create array view without an asm.js global parameter");
 
-    PropertyName* bufferName = m.module().bufferArgumentName();
+    PropertyName* bufferName = m.bufferArgumentName();
     if (!bufferName)
         return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
 
     ParseNode* ctorExpr = ListHead(newExpr);
 
     PropertyName* field;
     Scalar::Type type;
     if (ctorExpr->isKind(PNK_DOT)) {
@@ -3333,17 +3169,17 @@ CheckGlobalDotImport(ModuleValidator& m,
 {
     ParseNode* base = DotBase(initNode);
     PropertyName* field = DotMember(initNode);
 
     if (base->isKind(PNK_DOT)) {
         ParseNode* global = DotBase(base);
         PropertyName* mathOrAtomicsOrSimd = DotMember(base);
 
-        PropertyName* globalName = m.module().globalArgumentName();
+        PropertyName* globalName = m.globalArgumentName();
         if (!globalName)
             return m.fail(base, "import statement requires the module have a stdlib parameter");
 
         if (!IsUseOfName(global, globalName)) {
             if (global->isKind(PNK_DOT)) {
                 return m.failName(base, "imports can have at most two dot accesses "
                                         "(e.g. %s.Math.sin)", globalName);
             }
@@ -3357,30 +3193,30 @@ CheckGlobalDotImport(ModuleValidator& m,
         if (mathOrAtomicsOrSimd == m.cx()->names().SIMD)
             return CheckGlobalSimdImport(m, initNode, varName, field);
         return m.failName(base, "expecting %s.{Math|SIMD}", globalName);
     }
 
     if (!base->isKind(PNK_NAME))
         return m.fail(base, "expected name of variable or parameter");
 
-    if (base->name() == m.module().globalArgumentName()) {
+    if (base->name() == m.globalArgumentName()) {
         if (field == m.cx()->names().NaN)
             return m.addGlobalConstant(varName, GenericNaN(), field);
         if (field == m.cx()->names().Infinity)
             return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
 
         Scalar::Type type;
         if (IsArrayViewCtorName(m, field, &type))
             return m.addArrayViewCtor(varName, type, field);
 
         return m.failName(initNode, "'%s' is not a standard constant or typed array name", field);
     }
 
-    if (base->name() == m.module().importArgumentName())
+    if (base->name() == m.importArgumentName())
         return m.addFFI(varName, field);
 
     const ModuleValidator::Global* global = m.lookupGlobal(base->name());
     if (!global)
         return m.failName(initNode, "%s not found in module global scope", base->name());
 
     if (!global->isSimdCtor())
         return m.failName(base, "expecting SIMD constructor name, got %s", field);
@@ -3706,17 +3542,17 @@ FoldMaskedArrayIndex(FunctionValidator& 
 
     uint32_t mask2;
     if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
         // Flag the access to skip the bounds check if the mask ensures that an
         // 'out of bounds' access can not occur based on the current heap length
         // constraint. The unsigned maximum of a masked index is the mask
         // itself, so check that the mask is not negative and compare the mask
         // to the known minimum heap length.
-        if (int32_t(mask2) >= 0 && mask2 < f.m().module().minHeapLength())
+        if (int32_t(mask2) >= 0 && mask2 < f.m().minHeapLength())
             *needsBoundsCheck = NO_BOUNDS_CHECK;
         *mask &= mask2;
         *indexExpr = indexNode;
         return true;
     }
 
     return false;
 }
@@ -4190,18 +4026,20 @@ CheckSharedArrayAtomicAccess(FunctionVal
 {
     if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask))
         return false;
 
     // Atomic accesses may be made on shared integer arrays only.
 
     // The global will be sane, CheckArrayAccess checks it.
     const ModuleValidator::Global* global = f.lookupGlobal(viewName->name());
-    if (global->which() != ModuleValidator::Global::ArrayView || !f.m().module().isSharedView())
-        return f.fail(viewName, "base of array access must be a shared typed array view name");
+    if (global->which() != ModuleValidator::Global::ArrayView)
+        return f.fail(viewName, "base of array access must be a typed array view");
+
+    MOZ_ASSERT(f.m().atomicsPresent());
 
     switch (*viewType) {
       case Scalar::Int8:
       case Scalar::Int16:
       case Scalar::Int32:
       case Scalar::Uint8:
       case Scalar::Uint16:
       case Scalar::Uint32:
@@ -7191,35 +7029,32 @@ CheckModuleEnd(ModuleValidator &m)
                             "top-level export (return) must be the last statement");
     }
 
     m.parser().tokenStream.ungetToken();
     return true;
 }
 
 static bool
-CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, HandleAsmJSModule obj,
-            unsigned* time, SlowFunctionVector* slowFuncs)
+CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList,
+            MutableHandleWasmModule moduleObj, unsigned* time, SlowFunctionVector* slowFuncs)
 {
     int64_t before = PRMJ_Now();
 
-    ModuleValidator m(cx, parser);
-    if (!m.init(obj))
-        return false;
-
-    if (PropertyName* moduleFunctionName = FunctionName(m.moduleFunctionNode())) {
-        if (!CheckModuleLevelName(m, m.moduleFunctionNode(), moduleFunctionName))
-            return false;
-        m.initModuleFunctionName(moduleFunctionName);
-    }
-
-    if (!CheckFunctionHead(m, m.moduleFunctionNode()))
-        return false;
-
-    if (!CheckModuleArguments(m, m.moduleFunctionNode()))
+    ParseNode* moduleFunctionNode = parser.pc->maybeFunction;
+    MOZ_ASSERT(moduleFunctionNode);
+
+    ModuleValidator m(cx, parser, moduleFunctionNode);
+    if (!m.init())
+        return false;
+
+    if (!CheckFunctionHead(m, moduleFunctionNode))
+        return false;
+
+    if (!CheckModuleArguments(m, moduleFunctionNode))
         return false;
 
     if (!CheckPrecedingStatements(m, stmtList))
         return false;
 
     if (!CheckModuleProcessingDirectives(m))
         return false;
 
@@ -7239,52 +7074,52 @@ CheckModule(ExclusiveContext* cx, AsmJSP
         return false;
 
     if (!CheckModuleReturn(m))
         return false;
 
     if (!CheckModuleEnd(m))
         return false;
 
-    if (!m.finish(slowFuncs))
+    if (!m.finish(moduleObj, slowFuncs))
         return false;
 
     *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
     return true;
 }
 
 /*****************************************************************************/
 // Link-time validation
 
-static AsmJSModuleObject&
+static WasmModuleObject&
 FunctionToModuleObject(JSFunction* fun)
 {
     MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun));
     const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_MODULE_SLOT);
-    return v.toObject().as<AsmJSModuleObject>();
+    return v.toObject().as<WasmModuleObject>();
 }
 
 static unsigned
 FunctionToExportIndex(JSFunction* fun)
 {
     MOZ_ASSERT(IsAsmJSFunction(fun));
     const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT);
     return v.toInt32();
 }
 
 static bool
 CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedFunction callee(cx, &args.callee().as<JSFunction>());
 
-    AsmJSModule& module = FunctionToModuleObject(callee).module();
+    Module& module = FunctionToModuleObject(callee).module();
     uint32_t exportIndex = FunctionToExportIndex(callee);
 
-    return module.wasmModule().callExport(cx, exportIndex, args);
+    return module.callExport(cx, exportIndex, args);
 }
 
 static bool
 LinkFail(JSContext* cx, const char* str)
 {
     JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage,
                                  nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
     return false;
@@ -7335,23 +7170,23 @@ HasPureCoercion(JSContext* cx, HandleVal
     {
         return true;
     }
 
     return false;
 }
 
 static bool
-ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData,
+ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, uint8_t* globalData,
                        HandleValue importVal)
 {
     void* datum = globalData + global.varGlobalDataOffset();
 
     switch (global.varInitKind()) {
-      case AsmJSModule::Global::InitConstant: {
+      case AsmJSGlobal::InitConstant: {
         Val v = global.varInitVal();
         switch (v.type()) {
           case ValType::I32:
             *(int32_t*)datum = v.i32();
             break;
           case ValType::I64:
             MOZ_CRASH("int64");
           case ValType::F32:
@@ -7367,17 +7202,17 @@ ValidateGlobalVariable(JSContext* cx, co
             break;
           case ValType::F32x4:
             memcpy(datum, v.f32x4(), Simd128DataSize);
             break;
         }
         break;
       }
 
-      case AsmJSModule::Global::InitImport: {
+      case AsmJSGlobal::InitImport: {
         RootedPropertyName field(cx, global.varImportField());
         RootedValue v(cx);
         if (!GetDataProperty(cx, importVal, field, &v))
             return false;
 
         if (!v.isPrimitive() && !HasPureCoercion(cx, v))
             return LinkFail(cx, "Imported values must be primitives");
 
@@ -7422,33 +7257,33 @@ ValidateGlobalVariable(JSContext* cx, co
         break;
       }
     }
 
     return true;
 }
 
 static bool
-ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal,
+ValidateFFI(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal,
             AutoVectorRooter<JSFunction*>* ffis)
 {
     RootedPropertyName field(cx, global.ffiField());
     RootedValue v(cx);
     if (!GetDataProperty(cx, importVal, field, &v))
         return false;
 
     if (!v.isObject() || !v.toObject().is<JSFunction>())
         return LinkFail(cx, "FFI imports must be functions");
 
     (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
     return true;
 }
 
 static bool
-ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+ValidateArrayView(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
 {
     RootedPropertyName field(cx, global.maybeViewName());
     if (!field)
         return true;
 
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, field, &v))
         return false;
@@ -7456,17 +7291,17 @@ ValidateArrayView(JSContext* cx, const A
     bool tac = IsTypedArrayConstructor(v, global.viewType());
     if (!tac)
         return LinkFail(cx, "bad typed array constructor");
 
     return true;
 }
 
 static bool
-ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+ValidateMathBuiltinFunction(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
 {
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
         return false;
 
     RootedPropertyName field(cx, global.mathName());
     if (!GetDataProperty(cx, v, field, &v))
         return false;
@@ -7518,25 +7353,25 @@ AsmJSSimdTypeToTypeDescrType(AsmJSSimdTy
       case AsmJSSimdType_int32x4: return Int32x4::type;
       case AsmJSSimdType_float32x4: return Float32x4::type;
       case AsmJSSimdType_bool32x4: return Bool32x4::type;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType");
 }
 
 static bool
-ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal,
+ValidateSimdType(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal,
                  MutableHandleValue out)
 {
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v))
         return false;
 
     AsmJSSimdType type;
-    if (global.which() == AsmJSModule::Global::SimdCtor)
+    if (global.which() == AsmJSGlobal::SimdCtor)
         type = global.simdCtorType();
     else
         type = global.simdOperationType();
 
     RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type));
     if (!GetDataProperty(cx, v, simdTypeName, &v))
         return false;
 
@@ -7550,24 +7385,24 @@ ValidateSimdType(JSContext* cx, const As
     if (AsmJSSimdTypeToTypeDescrType(type) != simdDesc->as<SimdTypeDescr>().type())
         return LinkFail(cx, "bad SIMD type");
 
     out.set(v);
     return true;
 }
 
 static bool
-ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+ValidateSimdType(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
 {
     RootedValue _(cx);
     return ValidateSimdType(cx, global, globalVal, &_);
 }
 
 static bool
-ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+ValidateSimdOperation(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
 {
     // SIMD operations are loaded from the SIMD type, so the type must have been
     // validated before the operation.
     RootedValue v(cx);
     JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v));
 
     RootedPropertyName opName(cx, global.simdOperationName());
     if (!GetDataProperty(cx, v, opName, &v))
@@ -7610,17 +7445,17 @@ ValidateSimdOperation(JSContext* cx, con
 #undef SET_NATIVE
     }
     if (!native || !IsNativeFunction(v, native))
         return LinkFail(cx, "bad SIMD.type.* operation");
     return true;
 }
 
 static bool
-ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
 {
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, cx->names().Atomics, &v))
         return false;
     RootedPropertyName field(cx, global.atomicsName());
     if (!GetDataProperty(cx, v, field, &v))
         return false;
 
@@ -7641,22 +7476,22 @@ ValidateAtomicsBuiltinFunction(JSContext
 
     if (!IsNativeFunction(v, native))
         return LinkFail(cx, "bad Atomics.* builtin function");
 
     return true;
 }
 
 static bool
-ValidateConstant(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+ValidateConstant(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
 {
     RootedPropertyName field(cx, global.constantName());
     RootedValue v(cx, globalVal);
 
-    if (global.constantKind() == AsmJSModule::Global::MathConstant) {
+    if (global.constantKind() == AsmJSGlobal::MathConstant) {
         if (!GetDataProperty(cx, v, cx->names().Math, &v))
             return false;
     }
 
     if (!GetDataProperty(cx, v, field, &v))
         return false;
 
     if (!v.isNumber())
@@ -7673,20 +7508,20 @@ ValidateConstant(JSContext* cx, const As
 
     return true;
 }
 
 static bool
 CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal,
             MutableHandle<ArrayBufferObjectMaybeShared*> buffer)
 {
-    if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal))
+    if (module.hasSharedHeap() && !IsSharedArrayBuffer(bufferVal))
         return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer");
 
-    if (!module.isSharedView() && !IsArrayBuffer(bufferVal))
+    if (!module.hasSharedHeap() && !IsArrayBuffer(bufferVal))
         return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer");
 
     buffer.set(&AsAnyArrayBuffer(bufferVal));
     uint32_t heapLength = buffer->byteLength();
 
     if (!IsValidAsmJSHeapLength(heapLength)) {
         UniqueChars msg(
             JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
@@ -7706,96 +7541,96 @@ CheckBuffer(JSContext* cx, AsmJSModule& 
                         heapLength,
                         module.minHeapLength()));
         return LinkFail(cx, msg.get());
     }
 
     // Shell builtins may have disabled signal handlers since the module we're
     // cloning was compiled. LookupAsmJSModuleInCache checks for signal handlers
     // as well for the caching case.
-    if (module.wasmModule().compileArgs() != CompileArgs(cx))
+    if (module.compileArgs() != CompileArgs(cx))
         return LinkFail(cx, "Signals have been toggled since compilation");
 
     if (buffer->is<ArrayBufferObject>()) {
         Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
-        bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB;
+        bool useSignalHandlers = module.compileArgs().useSignalHandlersForOOB;
         if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers))
             return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
     }
 
     return true;
 }
 
 static bool
 DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module)
 {
     HandleValue globalVal = args.get(0);
     HandleValue importVal = args.get(1);
     HandleValue bufferVal = args.get(2);
 
     Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
-    if (module.hasArrayView() && !CheckBuffer(cx, module, bufferVal, &buffer))
+    if (module.usesHeap() && !CheckBuffer(cx, module, bufferVal, &buffer))
         return false;
 
     AutoVectorRooter<JSFunction*> ffis(cx);
     if (!ffis.resize(module.numFFIs()))
         return false;
 
-    for (const AsmJSModule::Global& global : module.globals()) {
+    for (const AsmJSGlobal& global : module.asmJSGlobals()) {
         switch (global.which()) {
-          case AsmJSModule::Global::Variable:
-            if (!ValidateGlobalVariable(cx, global, module.wasmModule().globalData(), importVal))
+          case AsmJSGlobal::Variable:
+            if (!ValidateGlobalVariable(cx, global, module.globalData(), importVal))
                 return false;
             break;
-          case AsmJSModule::Global::FFI:
+          case AsmJSGlobal::FFI:
             if (!ValidateFFI(cx, global, importVal, &ffis))
                 return false;
             break;
-          case AsmJSModule::Global::ArrayView:
-          case AsmJSModule::Global::ArrayViewCtor:
+          case AsmJSGlobal::ArrayView:
+          case AsmJSGlobal::ArrayViewCtor:
             if (!ValidateArrayView(cx, global, globalVal))
                 return false;
             break;
-          case AsmJSModule::Global::MathBuiltinFunction:
+          case AsmJSGlobal::MathBuiltinFunction:
             if (!ValidateMathBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
-          case AsmJSModule::Global::AtomicsBuiltinFunction:
+          case AsmJSGlobal::AtomicsBuiltinFunction:
             if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
-          case AsmJSModule::Global::Constant:
+          case AsmJSGlobal::Constant:
             if (!ValidateConstant(cx, global, globalVal))
                 return false;
             break;
-          case AsmJSModule::Global::SimdCtor:
+          case AsmJSGlobal::SimdCtor:
             if (!ValidateSimdType(cx, global, globalVal))
                 return false;
             break;
-          case AsmJSModule::Global::SimdOperation:
+          case AsmJSGlobal::SimdOperation:
             if (!ValidateSimdOperation(cx, global, globalVal))
                 return false;
             break;
         }
     }
 
     AutoVectorRooter<JSFunction*> imports(cx);
-    for (const AsmJSModule::Import& import : module.imports()) {
+    for (const AsmJSImport& import : module.asmJSImports()) {
         if (!imports.append(ffis[import.ffiIndex()]))
             return false;
     }
 
-    return module.wasmModule().dynamicallyLink(cx, buffer, imports);
+    return module.dynamicallyLink(cx, buffer, imports);
 }
 
 static JSFunction*
-NewExportedFunction(JSContext* cx, const AsmJSModule& module, const AsmJSModule::Export& func,
+NewExportedFunction(JSContext* cx, const Module& module, const AsmJSExport& func,
                     HandleObject moduleObj, unsigned exportIndex)
 {
-    unsigned numArgs = module.wasmModule().exports()[exportIndex].sig().args().length();
+    unsigned numArgs = module.exports()[exportIndex].sig().args().length();
 
     RootedPropertyName name(cx, func.name());
     JSFunction* fun =
         NewNativeConstructor(cx, CallAsmJS, numArgs, name,
                              gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
                              JSFunction::ASMJS_CTOR);
     if (!fun)
         return nullptr;
@@ -7870,34 +7705,34 @@ HandleDynamicLinkFailure(JSContext* cx, 
         return false;
 
     // Call the function we just recompiled.
     args.setCallee(ObjectValue(*fun));
     return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
 }
 
 static JSObject*
-CreateExportObject(JSContext* cx, HandleAsmJSModule moduleObj)
-{
-    AsmJSModule& module = moduleObj->module();
-    const AsmJSModule::ExportVector& exports = module.exports();
+CreateExportObject(JSContext* cx, HandleWasmModule moduleObj)
+{
+    AsmJSModule& module = moduleObj->module().asAsmJS();
+    const AsmJSExportVector& exports = module.asmJSExports();
 
     if (exports.length() == 1) {
-        const AsmJSModule::Export& func = exports[0];
+        const AsmJSExport& func = exports[0];
         if (!func.maybeFieldName())
             return NewExportedFunction(cx, module, func, moduleObj, 0);
     }
 
     gc::AllocKind allocKind = gc::GetGCObjectKind(exports.length());
     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
     if (!obj)
         return nullptr;
 
     for (unsigned i = 0; i < exports.length(); i++) {
-        const AsmJSModule::Export& func = exports[i];
+        const AsmJSExport& func = exports[i];
 
         RootedFunction fun(cx, NewExportedFunction(cx, module, func, moduleObj, i));
         if (!fun)
             return nullptr;
 
         MOZ_ASSERT(func.maybeFieldName() != nullptr);
         RootedId id(cx, NameToId(func.maybeFieldName()));
         RootedValue val(cx, ObjectValue(*fun));
@@ -7912,34 +7747,30 @@ CreateExportObject(JSContext* cx, Handle
 static bool
 LinkAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended
     // function and stores its module in an extended slot.
     RootedFunction fun(cx, &args.callee().as<JSFunction>());
-    Rooted<AsmJSModuleObject*> moduleObj(cx, &FunctionToModuleObject(fun));
+    Rooted<WasmModuleObject*> moduleObj(cx, &FunctionToModuleObject(fun));
 
     // When a module is linked, it is dynamically specialized to the given
     // arguments (buffer, ffis). Thus, if the module is linked again (it is just
     // a function so it can be called multiple times), we need to clone a new
     // module.
-    if (moduleObj->module().wasmModule().dynamicallyLinked()) {
-        Rooted<AsmJSModuleObject*> clone(cx, NewAsmJSModuleObject(cx));
-        if (!clone)
-            return false;
-
-        if (!moduleObj->module().clone(cx, clone))
-            return false;
-
-        moduleObj = clone;
-    }
-
-    AsmJSModule& module = moduleObj->module();
+    if (moduleObj->module().dynamicallyLinked()) {
+        if (!moduleObj->module().asAsmJS().clone(cx, &moduleObj))
+            return false;
+        if (!moduleObj->module().asAsmJS().staticallyLink(cx))
+            return false;
+    }
+
+    AsmJSModule& module = moduleObj->module().asAsmJS();
 
     // Link the module by performing the link-time validation checks in the
     // asm.js spec and then patching the generated module to associate it with
     // the given heap (ArrayBuffer) and a new global data segment (the closure
     // state shared by the inner asm.js functions).
     if (!DynamicallyLinkModule(cx, args, module)) {
         // Linking failed, so reparse the entire asm.js module from scratch to
         // get normal interpreted bytecode which we can simply Invoke. Very slow.
@@ -7974,180 +7805,246 @@ NewModuleFunction(ExclusiveContext* cx, 
     moduleFun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
     return moduleFun;
 }
 
 /*****************************************************************************/
 // Caching and cloning
 
 uint8_t*
-AsmJSModule::Global::serialize(uint8_t* cursor) const
+AsmJSGlobal::serialize(uint8_t* cursor) const
 {
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     cursor = SerializeName(cursor, name_);
     return cursor;
 }
 
 size_t
-AsmJSModule::Global::serializedSize() const
+AsmJSGlobal::serializedSize() const
 {
     return sizeof(pod) +
            SerializedNameSize(name_);
 }
 
 const uint8_t*
-AsmJSModule::Global::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+AsmJSGlobal::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
     (cursor = DeserializeName(cx, cursor, &name_));
     return cursor;
 }
 
 bool
-AsmJSModule::Global::clone(JSContext* cx, Global* out) const
+AsmJSGlobal::clone(JSContext* cx, AsmJSGlobal* out) const
 {
     *out = *this;
     return true;
 }
 
 uint8_t*
-AsmJSModule::Export::serialize(uint8_t* cursor) const
+AsmJSExport::serialize(uint8_t* cursor) const
 {
     cursor = SerializeName(cursor, name_);
     cursor = SerializeName(cursor, maybeFieldName_);
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     return cursor;
 }
 
 size_t
-AsmJSModule::Export::serializedSize() const
+AsmJSExport::serializedSize() const
 {
     return SerializedNameSize(name_) +
            SerializedNameSize(maybeFieldName_) +
            sizeof(pod);
 }
 
 const uint8_t*
-AsmJSModule::Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+AsmJSExport::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
 {
     (cursor = DeserializeName(cx, cursor, &name_)) &&
     (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) &&
     (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
     return cursor;
 }
 
 bool
-AsmJSModule::Export::clone(JSContext* cx, Export* out) const
+AsmJSExport::clone(JSContext* cx, AsmJSExport* out) const
 {
     out->name_ = name_;
     out->maybeFieldName_ = maybeFieldName_;
     out->pod = pod;
     return true;
 }
 
 size_t
-AsmJSModule::serializedSize() const
-{
-    MOZ_ASSERT(isFinished());
-    return wasmModule_->serializedSize() +
-           linkData_->serializedSize() +
-           sizeof(pod) +
-           SerializedVectorSize(globals_) +
-           SerializedPodVectorSize(imports_) +
-           SerializedVectorSize(exports_) +
-           SerializedNameSize(globalArgumentName_) +
-           SerializedNameSize(importArgumentName_) +
-           SerializedNameSize(bufferArgumentName_);
+AsmJSModuleData::serializedSize() const
+{
+    return sizeof(pod) +
+           SerializedVectorSize(globals) +
+           SerializedPodVectorSize(imports) +
+           SerializedVectorSize(exports) +
+           SerializedNameSize(globalArgumentName) +
+           SerializedNameSize(importArgumentName) +
+           SerializedNameSize(bufferArgumentName);
 }
 
 uint8_t*
-AsmJSModule::serialize(uint8_t* cursor) const
-{
-    MOZ_ASSERT(isFinished());
-    cursor = wasmModule_->serialize(cursor);
-    cursor = linkData_->serialize(cursor);
+AsmJSModuleData::serialize(uint8_t* cursor) const
+{
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
-    cursor = SerializeVector(cursor, globals_);
-    cursor = SerializePodVector(cursor, imports_);
-    cursor = SerializeVector(cursor, exports_);
-    cursor = SerializeName(cursor, globalArgumentName_);
-    cursor = SerializeName(cursor, importArgumentName_);
-    cursor = SerializeName(cursor, bufferArgumentName_);
+    cursor = SerializeVector(cursor, globals);
+    cursor = SerializePodVector(cursor, imports);
+    cursor = SerializeVector(cursor, exports);
+    cursor = SerializeName(cursor, globalArgumentName);
+    cursor = SerializeName(cursor, importArgumentName);
+    cursor = SerializeName(cursor, bufferArgumentName);
     return cursor;
 }
 
 const uint8_t*
-AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    linkData_ = cx->make_unique<StaticLinkData>();
-    if (!linkData_)
-        return nullptr;
-
-    // To avoid GC-during-deserialization corner cases, prevent atoms from
-    // being collected.
-    AutoKeepAtoms aka(cx->perThreadData);
-
-    (cursor = Module::deserialize(cx, cursor, &wasmModule_)) &&
-    (cursor = linkData_->deserialize(cx, cursor)) &&
+AsmJSModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
     (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
-    (cursor = DeserializeVector(cx, cursor, &globals_)) &&
-    (cursor = DeserializePodVector(cx, cursor, &imports_)) &&
-    (cursor = DeserializeVector(cx, cursor, &exports_)) &&
-    (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) &&
-    (cursor = DeserializeName(cx, cursor, &importArgumentName_)) &&
-    (cursor = DeserializeName(cx, cursor, &bufferArgumentName_));
-
+    (cursor = DeserializeVector(cx, cursor, &globals)) &&
+    (cursor = DeserializePodVector(cx, cursor, &imports)) &&
+    (cursor = DeserializeVector(cx, cursor, &exports)) &&
+    (cursor = DeserializeName(cx, cursor, &globalArgumentName)) &&
+    (cursor = DeserializeName(cx, cursor, &importArgumentName)) &&
+    (cursor = DeserializeName(cx, cursor, &bufferArgumentName));
     return cursor;
 }
 
 bool
-AsmJSModule::clone(JSContext* cx, HandleAsmJSModule obj) const
-{
-    auto out = cx->new_<AsmJSModule>(scriptSource(), srcStart_, srcBodyStart_, pod.strict_);
-    if (!out)
-        return false;
-
-    obj->setModule(out);
-
-    out->wasmModule_ = wasmModule_->clone(cx, *linkData_);
-    if (!out->wasmModule_)
-        return false;
-
-    out->linkData_ = cx->make_unique<StaticLinkData>();
-    if (!out->linkData_ || !linkData_->clone(cx, out->linkData_.get()))
-        return false;
-
+AsmJSModuleData::clone(JSContext* cx, AsmJSModuleData* out) const
+{
     out->pod = pod;
-
-    if (!CloneVector(cx, globals_, &out->globals_) ||
-        !ClonePodVector(cx, imports_, &out->imports_) ||
-        !CloneVector(cx, exports_, &out->exports_))
-    {
-        return false;
-    }
-
-    out->globalArgumentName_ = globalArgumentName_;
-    out->importArgumentName_ = importArgumentName_;
-    out->bufferArgumentName_ = bufferArgumentName_;
-    return true;
+    out->globalArgumentName = globalArgumentName;
+    out->importArgumentName = importArgumentName;
+    out->bufferArgumentName = bufferArgumentName;
+    out->srcStart = srcStart;
+    out->srcBodyStart = srcBodyStart;
+    out->strict = strict;
+    out->scriptSource.reset(scriptSource.get());
+    return CloneVector(cx, globals, &out->globals) &&
+           ClonePodVector(cx, imports, &out->imports) &&
+           CloneVector(cx, exports, &out->exports);
+}
+
+size_t
+AsmJSModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return globals.sizeOfExcludingThis(mallocSizeOf) +
+           imports.sizeOfExcludingThis(mallocSizeOf) +
+           exports.sizeOfExcludingThis(mallocSizeOf);
+}
+
+size_t
+AsmJSModule::serializedSize() const
+{
+    return baseData().serializedSize() +
+           linkData_->serializedSize() +
+           data_->serializedSize();
+}
+
+uint8_t*
+AsmJSModule::serialize(uint8_t* cursor) const
+{
+    cursor = baseData().serialize(cursor);
+    cursor = linkData_->serialize(cursor);
+    cursor = data_->serialize(cursor);
+    return cursor;
+}
+
+/* static */ const uint8_t*
+AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor, AsmJSParser& parser,
+                         MutableHandleWasmModule moduleObj)
+{
+    moduleObj.set(WasmModuleObject::create(cx));
+    if (!moduleObj)
+        return nullptr;
+
+    // Deserialization GC-allocates a bunch of atoms and stores them in unrooted
+    // Vectors so, for simplicity, inhibit GC of the atoms zone.
+    AutoKeepAtoms aka(cx->perThreadData);
+
+    UniqueModuleData baseData = cx->make_unique<ModuleData>();
+    if (!baseData)
+        return nullptr;
+    cursor = baseData->deserialize(cx, cursor);
+    if (!cursor)
+        return nullptr;
+
+    MOZ_ASSERT(!baseData->loadedFromCache);
+    baseData->loadedFromCache = true;
+
+    UniqueStaticLinkData linkData = cx->make_unique<StaticLinkData>();
+    if (!linkData)
+        return nullptr;
+    cursor = linkData->deserialize(cx, cursor);
+    if (!cursor)
+        return nullptr;
+
+    UniqueAsmJSModuleData data = cx->make_unique<AsmJSModuleData>();
+    if (!data)
+        return nullptr;
+    cursor = data->deserialize(cx, cursor);
+    if (!cursor)
+        return nullptr;
+
+    // See AsmJSModuleData comment as well as ModuleValidator::init().
+    data->srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
+    data->srcBodyStart = parser.tokenStream.currentToken().pos.end;
+    data->strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
+    data->scriptSource.reset(parser.ss);
+
+    auto module = cx->make_unique<AsmJSModule>(Move(baseData), Move(linkData), Move(data));
+    if (!module)
+        return nullptr;
+
+    moduleObj->initModule(Move(module));
+    return cursor;
+}
+
+bool
+AsmJSModule::clone(JSContext* cx, MutableHandleWasmModule moduleObj) const
+{
+    moduleObj.set(WasmModuleObject::create(cx));
+    if (!moduleObj)
+        return false;
+
+    // Prevent any GC that may move the temporarily-unrooted atoms being cloned.
+    AutoKeepAtoms aka(cx->perThreadData);
+
+    UniqueModuleData baseData = cx->make_unique<ModuleData>();
+    if (!baseData || !this->baseData().clone(cx, baseData.get()))
+        return false;
+
+    UniqueStaticLinkData linkData = cx->make_unique<StaticLinkData>();
+    if (!linkData || !linkData_->clone(cx, linkData.get()))
+        return false;
+
+    UniqueAsmJSModuleData data = cx->make_unique<AsmJSModuleData>();
+    if (!data || !data_->clone(cx, data.get()))
+        return false;
+
+    auto module = cx->make_unique<AsmJSModule>(Move(baseData), Move(linkData), Move(data));
+    if (!module)
+        return false;
+
+    moduleObj->initModule(Move(module));
+
+    return Module::clone(cx, *linkData_, &moduleObj->module());
 }
 
 void
-AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
-{
-    if (wasmModule_)
-        wasmModule_->addSizeOfMisc(mallocSizeOf, code, data);
-
-    if (linkData_)
-        *data += linkData_->sizeOfExcludingThis(mallocSizeOf);
-
-    *data += mallocSizeOf(this) +
-             globals_.sizeOfExcludingThis(mallocSizeOf) +
-             imports_.sizeOfExcludingThis(mallocSizeOf) +
-             exports_.sizeOfExcludingThis(mallocSizeOf);
+AsmJSModule::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
+{
+    Module::addSizeOfMisc(mallocSizeOf, code, data);
+    *data += mallocSizeOf(linkData_.get()) + linkData_->sizeOfExcludingThis(mallocSizeOf);
+    *data += mallocSizeOf(data_.get()) + data_->sizeOfExcludingThis(mallocSizeOf);
 }
 
 namespace {
 
 struct PropertyNameWrapper
 {
     PropertyName* name;
 
@@ -8354,18 +8251,18 @@ StoreAsmJSModuleInCache(AsmJSParser& par
     cursor = moduleChars.serialize(cursor);
     cursor = module.serialize(cursor);
 
     MOZ_ASSERT(cursor == entry.memory + serializedSize);
     return JS::AsmJSCache_Success;
 }
 
 static bool
-LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSModule moduleObj,
-                         bool* loadedFromCache, UniqueChars* compilationTimeReport)
+LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loadedFromCache,
+                         MutableHandleWasmModule moduleObj, UniqueChars* compilationTimeReport)
 {
     int64_t usecBefore = PRMJ_Now();
 
     *loadedFromCache = false;
 
     MachineId machineId;
     if (!machineId.extractCurrentState(cx))
         return true;
@@ -8390,41 +8287,34 @@ LookupAsmJSModuleInCache(ExclusiveContex
     if (machineId != cachedMachineId)
         return true;
 
     ModuleCharsForLookup moduleChars;
     cursor = moduleChars.deserialize(cx, cursor);
     if (!moduleChars.match(parser))
         return true;
 
-    uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
-    uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end;
-    bool strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
-
-    AsmJSModule* module = cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict);
-    if (!module)
-        return false;
-
-    moduleObj->setModule(module);
-
-    cursor = module->deserialize(cx, cursor);
+    cursor = AsmJSModule::deserialize(cx, cursor, parser, moduleObj);
     if (!cursor)
         return false;
 
     bool atEnd = cursor == entry.memory + entry.serializedSize;
     MOZ_ASSERT(atEnd, "Corrupt cache file");
     if (!atEnd)
         return true;
 
-    if (module->wasmModule().compileArgs() != CompileArgs(cx))
+    AsmJSModule& module = moduleObj->module().asAsmJS();
+
+    if (module.compileArgs() != CompileArgs(cx))
         return true;
 
-    module->staticallyLink(cx);
-
-    if (!parser.tokenStream.advance(module->srcEndBeforeCurly()))
+    if (!module.staticallyLink(cx))
+        return false;
+
+    if (!parser.tokenStream.advance(module.srcEndBeforeCurly()))
         return false;
 
     *loadedFromCache = true;
 
     int64_t usecAfter = PRMJ_Now();
     int ms = (usecAfter - usecBefore) / PRMJ_USEC_PER_MSEC;
     *compilationTimeReport = UniqueChars(JS_smprintf("loaded from cache in %dms", ms));
     return true;
@@ -8556,42 +8446,40 @@ bool
 js::CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, bool* validated)
 {
     *validated = false;
 
     // Various conditions disable asm.js optimizations.
     if (!EstablishPreconditions(cx, parser))
         return NoExceptionPending(cx);
 
-    Rooted<AsmJSModuleObject*> moduleObj(cx, NewAsmJSModuleObject(cx));
-    if (!moduleObj)
-        return false;
 
     // Before spending any time parsing the module, try to look it up in the
     // embedding's cache using the chars about to be parsed as the key.
+    Rooted<WasmModuleObject*> moduleObj(cx);
     bool loadedFromCache;
     UniqueChars message;
-    if (!LookupAsmJSModuleInCache(cx, parser, moduleObj, &loadedFromCache, &message))
+    if (!LookupAsmJSModuleInCache(cx, parser, &loadedFromCache, &moduleObj, &message))
         return false;
 
     // If not present in the cache, parse, validate and generate code in a
     // single linear pass over the chars of the asm.js module.
     if (!loadedFromCache) {
         // "Checking" parses, validates and compiles, producing a fully compiled
-        // AsmJSModuleObject as result.
+        // WasmModuleObject as result.
         unsigned time;
         SlowFunctionVector slowFuncs(cx);
-        if (!CheckModule(cx, parser, stmtList, moduleObj, &time, &slowFuncs))
+        if (!CheckModule(cx, parser, stmtList, &moduleObj, &time, &slowFuncs))
             return NoExceptionPending(cx);
 
         // Try to store the AsmJSModule in the embedding's cache. The
         // AsmJSModule must be stored before static linking since static linking
         // specializes the AsmJSModule to the current process's address space
         // and therefore must be executed after a cache hit.
-        AsmJSModule& module = moduleObj->module();
+        AsmJSModule& module = moduleObj->module().asAsmJS();
         JS::AsmJSCacheResult cacheResult = StoreAsmJSModuleInCache(parser, module, cx);
         if (!module.staticallyLink(cx))
             return false;
 
         message = BuildConsoleMessage(cx, module, time, slowFuncs, cacheResult);
         if (!message)
             return NoExceptionPending(cx);
     }
@@ -8703,17 +8591,17 @@ js::IsAsmJSModuleLoadedFromCache(JSConte
     JSFunction* fun;
     if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
                              "argument passed to isAsmJSModuleLoadedFromCache is not a "
                              "validated asm.js module");
         return false;
     }
 
-    bool loadedFromCache = FunctionToModuleObject(fun).module().wasmModule().loadedFromCache();
+    bool loadedFromCache = FunctionToModuleObject(fun).module().loadedFromCache();
 
     args.rval().set(BooleanValue(loadedFromCache));
     return true;
 }
 
 /*****************************************************************************/
 // asm.js toString/toSource support
 
@@ -8736,17 +8624,17 @@ AppendUseStrictSource(JSContext* cx, Han
     return out.appendSubstring(src, 0, bodyStart) &&
            out.append("\n\"use strict\";\n") &&
            out.appendSubstring(src, bodyStart, src->length() - bodyStart);
 }
 
 JSString*
 js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda)
 {
-    AsmJSModule& module = FunctionToModuleObject(fun).module();
+    AsmJSModule& module = FunctionToModuleObject(fun).module().asAsmJS();
 
     uint32_t begin = module.srcStart();
     uint32_t end = module.srcEndAfterCurly();
     ScriptSource* source = module.scriptSource();
     StringBuffer out(cx);
 
     if (addParenToLambda && fun->isLambda() && !out.append("("))
         return nullptr;
@@ -8809,18 +8697,18 @@ js::AsmJSModuleToString(JSContext* cx, H
         return nullptr;
 
     return out.finishString();
 }
 
 JSString*
 js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun)
 {
-    AsmJSModule& module = FunctionToModuleObject(fun).module();
-    const AsmJSModule::Export& f = module.exports()[FunctionToExportIndex(fun)];
+    AsmJSModule& module = FunctionToModuleObject(fun).module().asAsmJS();
+    const AsmJSExport& f = module.asmJSExports()[FunctionToExportIndex(fun)];
     uint32_t begin = module.srcStart() + f.startOffsetInModule();
     uint32_t end = module.srcStart() + f.endOffsetInModule();
 
     ScriptSource* source = module.scriptSource();
     StringBuffer out(cx);
 
     if (!out.append("function "))
         return nullptr;
@@ -8873,30 +8761,30 @@ js::AsmJSFunctionToString(JSContext* cx,
 static const size_t MinHeapLength = 64 * 1024;
 static_assert(MinHeapLength % AsmJSPageSize == 0, "Invalid page size");
 
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
 
 // Targets define AsmJSImmediateRange to be the size of an address immediate,
 // and AsmJSCheckedImmediateRange, to be the size of an address immediate that
 // can be supported by signal-handler OOB handling.
-static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange,
+static_assert(AsmJSCheckedImmediateRange <= AsmJSImmediateRange,
               "AsmJSImmediateRange should be the size of an unconstrained "
               "address immediate");
 
 // To support the use of signal handlers for catching Out Of Bounds accesses,
 // the internal ArrayBuffer data array is inflated to 4GiB (only the
 // byteLength portion of which is accessible) so that out-of-bounds accesses
 // (made using a uint32 index) are guaranteed to raise a SIGSEGV.
 // Then, an additional extent is added to permit folding of immediate
 // values into addresses. And finally, unaligned accesses and mask optimizations
 // might also try to access a few bytes after this limit, so just inflate it by
 // AsmJSPageSize.
 const size_t js::AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL +
-                                   jit::AsmJSImmediateRange +
+                                   AsmJSImmediateRange +
                                    AsmJSPageSize;
 
 #endif // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
 
 // From the asm.js spec Linking section:
 //  the heap object's byteLength must be either
 //    2^n for n in [12, 24)
 //  or
--- a/js/src/asmjs/AsmJS.h
+++ b/js/src/asmjs/AsmJS.h
@@ -18,50 +18,26 @@
 
 #ifndef asmjs_asmjs_h
 #define asmjs_asmjs_h
 
 #include "vm/NativeObject.h"
 
 namespace js {
 
-class AsmJSModule;
 class ExclusiveContext;
 namespace frontend {
     template <typename ParseHandler> class Parser;
     template <typename ParseHandler> struct ParseContext;
     class FullParseHandler;
     class ParseNode;
 }
 typedef frontend::Parser<frontend::FullParseHandler> AsmJSParser;
 typedef frontend::ParseContext<frontend::FullParseHandler> AsmJSParseContext;
 
-// An AsmJSModuleObject is an internal implementation object (i.e., not exposed
-// directly to user script) which traces and owns an AsmJSModule. The
-// AsmJSModuleObject is referenced by the extended slots of the content-visible
-// module and export JSFunctions.
-
-class AsmJSModuleObject : public NativeObject
-{
-    static const unsigned MODULE_SLOT = 0;
-
-  public:
-    static const unsigned RESERVED_SLOTS = 1;
-
-    bool hasModule() const;
-    void setModule(AsmJSModule* module);
-    AsmJSModule& module() const;
-
-    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
-
-    static const Class class_;
-};
-
-typedef Handle<AsmJSModuleObject*> HandleAsmJSModule;
-
 // This function takes over parsing of a function starting with "use asm". The
 // return value indicates whether an error was reported which the caller should
 // propagate. If no error was reported, the function may still fail to validate
 // as asm.js. In this case, the parser.tokenStream has been advanced an
 // indeterminate amount and the entire function should be reparsed from the
 // beginning.
 
 extern bool
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -30,31 +30,28 @@ using namespace js::wasm;
 // ****************************************************************************
 // ModuleGenerator
 
 static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
 
 ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
   : cx_(cx),
-    args_(cx),
-    globalBytes_(InitialGlobalDataBytes),
     slowFuncs_(cx),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())),
     alloc_(&lifo_),
     masm_(MacroAssembler::AsmJSToken(), alloc_),
     sigs_(cx),
+    funcEntryOffsets_(cx),
+    exportFuncIndices_(cx),
     parallel_(false),
     outstanding_(0),
     tasks_(cx),
     freeTasks_(cx),
-    funcBytes_(0),
-    funcEntryOffsets_(cx),
-    exportFuncIndices_(cx),
     activeFunc_(nullptr),
     finishedFuncs_(false)
 {
     MOZ_ASSERT(IsCompilingAsmJS());
 }
 
 ModuleGenerator::~ModuleGenerator()
 {
@@ -104,18 +101,25 @@ ParallelCompilationEnabled(ExclusiveCont
     // If 'cx' isn't a JSContext, then we are already off the main thread so
     // off-thread compilation must be enabled.
     return !cx->isJSContext() || cx->asJSContext()->runtime()->canUseOffthreadIonCompilation();
 }
 
 bool
 ModuleGenerator::init()
 {
-    staticLinkData_ = cx_->make_unique<StaticLinkData>();
-    if (!staticLinkData_)
+    moduleData_ = cx_->make_unique<ModuleData>();
+    if (!moduleData_)
+        return false;
+
+    moduleData_->pod.globalBytes = InitialGlobalDataBytes;
+    moduleData_->pod.compileArgs = CompileArgs(cx_);
+
+    linkData_ = cx_->make_unique<StaticLinkData>();
+    if (!linkData_)
         return false;
 
     if (!sigs_.init())
         return false;
 
     uint32_t numTasks;
     if (ParallelCompilationEnabled(cx_) &&
         HelperThreadState().wasmCompilationInProgress.compareExchange(false, true))
@@ -134,36 +138,40 @@ ModuleGenerator::init()
     } else {
         numTasks = 1;
     }
 
     if (!tasks_.initCapacity(numTasks))
         return false;
     JSRuntime* runtime = cx_->compartment()->runtimeFromAnyThread();
     for (size_t i = 0; i < numTasks; i++)
-        tasks_.infallibleEmplaceBack(runtime, args_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
+        tasks_.infallibleEmplaceBack(runtime, args(), COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
 
     if (!freeTasks_.reserve(numTasks))
         return false;
     for (size_t i = 0; i < numTasks; i++)
         freeTasks_.infallibleAppend(&tasks_[i]);
 
     return true;
 }
 
 bool
 ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
 {
-    uint32_t pad = ComputeByteAlignment(globalBytes_, align);
-    if (UINT32_MAX - globalBytes_ < pad + bytes)
+    uint32_t globalBytes = moduleData_->pod.globalBytes;
+
+    uint32_t pad = ComputeByteAlignment(globalBytes, align);
+    if (UINT32_MAX - globalBytes < pad + bytes)
         return false;
 
-    globalBytes_ += pad;
-    *globalDataOffset = globalBytes_;
-    globalBytes_ += bytes;
+    globalBytes += pad;
+    *globalDataOffset = globalBytes;
+    globalBytes += bytes;
+
+    moduleData_->pod.globalBytes = globalBytes;
     return true;
 }
 
 bool
 ModuleGenerator::finishOutstandingTask()
 {
     MOZ_ASSERT(parallel_);
 
@@ -212,20 +220,20 @@ ModuleGenerator::finishTask(IonCompileTa
     if (!masm_.asmMergeWith(results.masm()))
         return false;
     MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size());
 
     // Add the CodeRange for this function.
     CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func.name());
     if (!funcName)
         return false;
-    uint32_t nameIndex = funcNames_.length();
-    if (!funcNames_.emplaceBack(Move(funcName)))
+    uint32_t nameIndex = moduleData_->funcNames.length();
+    if (!moduleData_->funcNames.emplaceBack(Move(funcName)))
         return false;
-    if (!codeRanges_.emplaceBack(nameIndex, func.line(), results.offsets()))
+    if (!moduleData_->codeRanges.emplaceBack(nameIndex, func.line(), results.offsets()))
         return false;
 
     // Keep a record of slow functions for printing in the final console message.
     unsigned totalTime = func.generateTime() + results.compileTime();
     if (totalTime >= SlowFunction::msThreshold) {
         if (!slowFuncs_.emplaceBack(func.name(), totalTime, func.line(), func.column()))
             return false;
     }
@@ -275,78 +283,78 @@ bool
 ModuleGenerator::declareImport(MallocSig&& sig, unsigned* index)
 {
     static_assert(Module::SizeOfImportExit % sizeof(void*) == 0, "word aligned");
 
     uint32_t globalDataOffset;
     if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
         return false;
 
-    *index = unsigned(imports_.length());
-    return imports_.emplaceBack(Move(sig), globalDataOffset);
+    *index = unsigned(moduleData_->imports.length());
+    return moduleData_->imports.emplaceBack(Move(sig), globalDataOffset);
 }
 
 uint32_t
 ModuleGenerator::numDeclaredImports() const
 {
-    return imports_.length();
+    return moduleData_->imports.length();
 }
 
 uint32_t
 ModuleGenerator::importExitGlobalDataOffset(uint32_t index) const
 {
-    return imports_[index].exitGlobalDataOffset();
+    return moduleData_->imports[index].exitGlobalDataOffset();
 }
 
 const MallocSig&
 ModuleGenerator::importSig(uint32_t index) const
 {
-    return imports_[index].sig();
+    return moduleData_->imports[index].sig();
 }
 
 bool
 ModuleGenerator::defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit)
 {
-    Import& import = imports_[index];
+    Import& import = moduleData_->imports[index];
     import.initInterpExitOffset(interpExit.begin);
     import.initJitExitOffset(jitExit.begin);
-    return codeRanges_.emplaceBack(CodeRange::ImportInterpExit, interpExit) &&
-           codeRanges_.emplaceBack(CodeRange::ImportJitExit, jitExit);
+    return moduleData_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExit) &&
+           moduleData_->codeRanges.emplaceBack(CodeRange::ImportJitExit, jitExit);
 }
 
 bool
 ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex)
 {
-    return exports_.emplaceBack(Move(sig)) &&
+    return moduleData_->exports.emplaceBack(Move(sig)) &&
            exportFuncIndices_.append(funcIndex);
 }
 
 uint32_t
 ModuleGenerator::exportFuncIndex(uint32_t index) const
 {
     return exportFuncIndices_[index];
 }
 
 const MallocSig&
 ModuleGenerator::exportSig(uint32_t index) const
 {
-    return exports_[index].sig();
+    return moduleData_->exports[index].sig();
 }
 
 uint32_t
 ModuleGenerator::numDeclaredExports() const
 {
-    return exports_.length();
+    return moduleData_->exports.length();
 }
 
 bool
 ModuleGenerator::defineExport(uint32_t index, Offsets offsets)
 {
-    exports_[index].initStubOffset(offsets.begin);
-    return codeRanges_.emplaceBack(CodeRange::Entry, offsets);
+    moduleData_->exports[index].initStubOffset(offsets.begin);
+    return moduleData_->codeRanges.emplaceBack(CodeRange::Entry, offsets);
 }
 
 bool
 ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
                            FunctionGenerator* fg)
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(!finishedFuncs_);
@@ -414,17 +422,17 @@ ModuleGenerator::finishFuncs()
         if (!cs.isInternal())
             continue;
         MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
         uint32_t callerOffset = cs.returnAddressOffset();
         uint32_t calleeOffset = funcEntryOffsets_[cs.targetIndex()];
         masm_.patchCall(callerOffset, calleeOffset);
     }
 
-    funcBytes_ = masm_.size();
+    moduleData_->pod.functionBytes = masm_.size();
     finishedFuncs_ = true;
     return true;
 }
 
 bool
 ModuleGenerator::declareFuncPtrTable(uint32_t numElems, uint32_t* index)
 {
     // Here just add an uninitialized FuncPtrTable and claim space in the global
@@ -434,197 +442,190 @@ ModuleGenerator::declareFuncPtrTable(uin
     // Avoid easy way to OOM the process.
     if (numElems > 1024 * 1024)
         return false;
 
     uint32_t globalDataOffset;
     if (!allocateGlobalBytes(numElems * sizeof(void*), sizeof(void*), &globalDataOffset))
         return false;
 
-    StaticLinkData::FuncPtrTableVector& tables = staticLinkData_->funcPtrTables;
+    StaticLinkData::FuncPtrTableVector& tables = linkData_->funcPtrTables;
 
     *index = tables.length();
     if (!tables.emplaceBack(globalDataOffset))
         return false;
 
     if (!tables.back().elemOffsets.resize(numElems))
         return false;
 
     return true;
 }
 
 uint32_t
 ModuleGenerator::funcPtrTableGlobalDataOffset(uint32_t index) const
 {
-    return staticLinkData_->funcPtrTables[index].globalDataOffset;
+    return linkData_->funcPtrTables[index].globalDataOffset;
 }
 
 void
 ModuleGenerator::defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elemFuncIndices)
 {
     MOZ_ASSERT(finishedFuncs_);
 
-    StaticLinkData::FuncPtrTable& table = staticLinkData_->funcPtrTables[index];
+    StaticLinkData::FuncPtrTable& table = linkData_->funcPtrTables[index];
     MOZ_ASSERT(table.elemOffsets.length() == elemFuncIndices.length());
 
     for (size_t i = 0; i < elemFuncIndices.length(); i++)
         table.elemOffsets[i] = funcEntryOffsets_[elemFuncIndices[i]];
 }
 
 bool
 ModuleGenerator::defineInlineStub(Offsets offsets)
 {
     MOZ_ASSERT(finishedFuncs_);
-    return codeRanges_.emplaceBack(CodeRange::Inline, offsets);
+    return moduleData_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
 }
 
 bool
 ModuleGenerator::defineSyncInterruptStub(ProfilingOffsets offsets)
 {
     MOZ_ASSERT(finishedFuncs_);
-    return codeRanges_.emplaceBack(CodeRange::Interrupt, offsets);
+    return moduleData_->codeRanges.emplaceBack(CodeRange::Interrupt, offsets);
 }
 
 bool
 ModuleGenerator::defineAsyncInterruptStub(Offsets offsets)
 {
     MOZ_ASSERT(finishedFuncs_);
-    staticLinkData_->pod.interruptOffset = offsets.begin;
-    return codeRanges_.emplaceBack(CodeRange::Inline, offsets);
+    linkData_->pod.interruptOffset = offsets.begin;
+    return moduleData_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
 }
 
 bool
 ModuleGenerator::defineOutOfBoundsStub(Offsets offsets)
 {
     MOZ_ASSERT(finishedFuncs_);
-    staticLinkData_->pod.outOfBoundsOffset = offsets.begin;
-    return codeRanges_.emplaceBack(CodeRange::Inline, offsets);
+    linkData_->pod.outOfBoundsOffset = offsets.begin;
+    return moduleData_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
 }
 
-Module*
+bool
 ModuleGenerator::finish(HeapUsage heapUsage,
-                        Module::MutedBool mutedErrors,
+                        MutedErrorsBool mutedErrors,
                         CacheableChars filename,
                         CacheableTwoByteChars displayURL,
+                        UniqueModuleData* moduleData,
                         UniqueStaticLinkData* staticLinkData,
                         SlowFunctionVector* slowFuncs)
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(finishedFuncs_);
 
+    moduleData_->pod.heapUsage = heapUsage;
+    moduleData_->pod.mutedErrors = mutedErrors;
+    moduleData_->filename = Move(filename);
+
     if (!GenerateStubs(*this, UsesHeap(heapUsage)))
-        return nullptr;
+        return false;
 
     masm_.finish();
     if (masm_.oom())
-        return nullptr;
+        return false;
 
     // Start global data on a new page so JIT code may be given independent
     // protection flags. Note assumption that global data starts right after
     // code below.
-    uint32_t codeBytes = AlignBytes(masm_.bytesNeeded(), AsmJSPageSize);
+    moduleData_->pod.codeBytes = AlignBytes(masm_.bytesNeeded(), AsmJSPageSize);
 
     // Inflate the global bytes up to page size so that the total bytes are a
     // page size (as required by the allocator functions).
-    globalBytes_ = AlignBytes(globalBytes_, AsmJSPageSize);
-    uint32_t totalBytes = codeBytes + globalBytes_;
+    moduleData_->pod.globalBytes = AlignBytes(moduleData_->pod.globalBytes, AsmJSPageSize);
 
     // Allocate the code (guarded by a UniquePtr until it is given to the Module).
-    UniqueCodePtr code = AllocateCode(cx_, totalBytes);
-    if (!code)
-        return nullptr;
+    moduleData_->code = AllocateCode(cx_, moduleData_->pod.totalBytes());
+    if (!moduleData_->code)
+        return false;
 
     // Delay flushing until Module::dynamicallyLink. The flush-inhibited range
     // is set by executableCopy.
     AutoFlushICache afc("ModuleGenerator::finish", /* inhibit = */ true);
-    masm_.executableCopy(code.get());
+    uint8_t* code = moduleData_->code.get();
+    masm_.executableCopy(code);
 
     // c.f. JitCode::copyFrom
     MOZ_ASSERT(masm_.jumpRelocationTableBytes() == 0);
     MOZ_ASSERT(masm_.dataRelocationTableBytes() == 0);
     MOZ_ASSERT(masm_.preBarrierTableBytes() == 0);
     MOZ_ASSERT(!masm_.hasSelfReference());
 
     // Convert the CallSiteAndTargetVector (needed during generation) to a
     // CallSiteVector (what is stored in the Module).
-    CallSiteVector callSites;
-    if (!callSites.appendAll(masm_.callSites()))
-        return nullptr;
+    if (!moduleData_->callSites.appendAll(masm_.callSites()))
+        return false;
+
+    // The MacroAssembler has accumulated all the heap accesses during codegen.
+    moduleData_->heapAccesses = masm_.extractHeapAccesses();
 
     // Add links to absolute addresses identified symbolically.
-    StaticLinkData::SymbolicLinkArray& symbolicLinks = staticLinkData_->symbolicLinks;
+    StaticLinkData::SymbolicLinkArray& symbolicLinks = linkData_->symbolicLinks;
     for (size_t i = 0; i < masm_.numAsmJSAbsoluteAddresses(); i++) {
         AsmJSAbsoluteAddress src = masm_.asmJSAbsoluteAddress(i);
         if (!symbolicLinks[src.target].append(src.patchAt.offset()))
-            return nullptr;
+            return false;
     }
 
     // Relative link metadata: absolute addresses that refer to another point within
     // the asm.js module.
 
     // CodeLabels are used for switch cases and loads from floating-point /
     // SIMD values in the constant pool.
     for (size_t i = 0; i < masm_.numCodeLabels(); i++) {
         CodeLabel cl = masm_.codeLabel(i);
         StaticLinkData::InternalLink link(StaticLinkData::InternalLink::CodeLabel);
         link.patchAtOffset = masm_.labelToPatchOffset(*cl.patchAt());
         link.targetOffset = cl.target()->offset();
-        if (!staticLinkData_->internalLinks.append(link))
-            return nullptr;
+        if (!linkData_->internalLinks.append(link))
+            return false;
     }
 
 #if defined(JS_CODEGEN_X86)
     // Global data accesses in x86 need to be patched with the absolute
     // address of the global. Globals are allocated sequentially after the
     // code section so we can just use an InternalLink.
     for (size_t i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) {
         AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i);
         StaticLinkData::InternalLink link(StaticLinkData::InternalLink::RawPointer);
         link.patchAtOffset = masm_.labelToPatchOffset(a.patchAt);
-        link.targetOffset = codeBytes + a.globalDataOffset;
-        if (!staticLinkData_->internalLinks.append(link))
-            return nullptr;
+        link.targetOffset = moduleData_->pod.codeBytes + a.globalDataOffset;
+        if (!linkData_->internalLinks.append(link))
+            return false;
     }
 #endif
 
 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     // On MIPS we need to update all the long jumps because they contain an
     // absolute adress. The values are correctly patched for the current address
     // space, but not after serialization or profiling-mode toggling.
     for (size_t i = 0; i < masm_.numLongJumps(); i++) {
         size_t off = masm_.longJump(i);
         StaticLinkData::InternalLink link(StaticLinkData::InternalLink::InstructionImmediate);
         link.patchAtOffset = off;
-        link.targetOffset = Assembler::ExtractInstructionImmediate(code.get() + off) -
-                            uintptr_t(code.get());
-        if (!staticLinkData_->internalLinks.append(link))
-            return nullptr;
+        link.targetOffset = Assembler::ExtractInstructionImmediate(code + off) - uintptr_t(code);
+        if (!linkData_->internalLinks.append(link))
+            return false;
     }
 #endif
 
 #if defined(JS_CODEGEN_X64)
     // Global data accesses on x64 use rip-relative addressing and thus do
     // not need patching after deserialization.
-    uint8_t* globalData = code.get() + codeBytes;
+    uint8_t* globalData = code + moduleData_->pod.codeBytes;
     for (size_t i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) {
         AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i);
-        masm_.patchAsmJSGlobalAccess(a.patchAt, code.get(), globalData, a.globalDataOffset);
+        masm_.patchAsmJSGlobalAccess(a.patchAt, code, globalData, a.globalDataOffset);
     }
 #endif
 
-    *staticLinkData = Move(staticLinkData_);
+    *moduleData = Move(moduleData_);
+    *staticLinkData = Move(linkData_);
     *slowFuncs = Move(slowFuncs_);
-    return cx_->new_<Module>(args_,
-                             funcBytes_,
-                             codeBytes,
-                             globalBytes_,
-                             heapUsage,
-                             mutedErrors,
-                             Move(code),
-                             Move(imports_),
-                             Move(exports_),
-                             masm_.extractHeapAccesses(),
-                             Move(codeRanges_),
-                             Move(callSites),
-                             Move(funcNames_),
-                             Move(filename),
-                             Move(displayURL));
+    return true;
 }
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -60,60 +60,52 @@ class MOZ_STACK_CLASS ModuleGenerator
     {
         typedef const MallocSig& Lookup;
         static HashNumber hash(Lookup l) { return l.hash(); }
         static bool match(const LifoSig* lhs, Lookup rhs) { return *lhs == rhs; }
     };
     typedef HashSet<const LifoSig*, SigHashPolicy> SigSet;
 
     ExclusiveContext*             cx_;
-    CompileArgs                   args_;
-
-    // Data handed over to the Module in finish()
-    uint32_t                      globalBytes_;
-    ImportVector                  imports_;
-    ExportVector                  exports_;
-    CodeRangeVector               codeRanges_;
-    CacheableCharsVector          funcNames_;
 
     // Data handed back to the caller in finish()
-    UniqueStaticLinkData          staticLinkData_;
+    UniqueModuleData              moduleData_;
+    UniqueStaticLinkData          linkData_;
     SlowFunctionVector            slowFuncs_;
 
     // Data scoped to the ModuleGenerator's lifetime
     LifoAlloc                     lifo_;
     jit::JitContext               jcx_;
     jit::TempAllocator            alloc_;
     jit::MacroAssembler           masm_;
     SigSet                        sigs_;
+    FuncOffsetVector              funcEntryOffsets_;
+    FuncIndexVector               exportFuncIndices_;
 
     // Parallel compilation
     bool                          parallel_;
     uint32_t                      outstanding_;
     Vector<IonCompileTask>        tasks_;
     Vector<IonCompileTask*>       freeTasks_;
 
-    // Function compilation
-    uint32_t                      funcBytes_;
-    FuncOffsetVector              funcEntryOffsets_;
-    FuncIndexVector               exportFuncIndices_;
+    // Assertions
     DebugOnly<FunctionGenerator*> activeFunc_;
     DebugOnly<bool>               finishedFuncs_;
 
     bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
     bool finishOutstandingTask();
     bool finishTask(IonCompileTask* task);
 
   public:
     explicit ModuleGenerator(ExclusiveContext* cx);
     ~ModuleGenerator();
 
     bool init();
 
-    CompileArgs args() const { return args_; }
+    CompileArgs args() const { return moduleData_->pod.compileArgs; }
     jit::MacroAssembler& masm() { return masm_; }
     const FuncOffsetVector& funcEntryOffsets() const { return funcEntryOffsets_; }
 
     const LifoSig* newLifoSig(const MallocSig& sig);
 
     // Global data:
     bool allocateGlobalVar(ValType type, uint32_t* globalDataOffset);
 
@@ -142,24 +134,26 @@ class MOZ_STACK_CLASS ModuleGenerator
     void defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elemFuncIndices);
 
     // Stubs:
     bool defineInlineStub(Offsets offsets);
     bool defineSyncInterruptStub(ProfilingOffsets offsets);
     bool defineAsyncInterruptStub(Offsets offsets);
     bool defineOutOfBoundsStub(Offsets offsets);
 
-    // Null return indicates failure. The caller must immediately root a
-    // non-null return value.
-    Module* finish(HeapUsage heapUsage,
-                   Module::MutedBool mutedErrors,
-                   CacheableChars filename,
-                   CacheableTwoByteChars displayURL,
-                   UniqueStaticLinkData* staticLinkData,
-                   SlowFunctionVector* slowFuncs);
+    // Return a ModuleData object which may be used to construct a Module, the
+    // StaticLinkData required to call Module::staticallyLink, and the list of
+    // functions that took a long time to compile.
+    bool finish(HeapUsage heapUsage,
+                MutedErrorsBool mutedErrors,
+                CacheableChars filename,
+                CacheableTwoByteChars displayURL,
+                UniqueModuleData* moduleData,
+                UniqueStaticLinkData* staticLinkData,
+                SlowFunctionVector* slowFuncs);
 };
 
 // A FunctionGenerator encapsulates the generation of a single function body.
 // ModuleGenerator::startFunc must be called after construction and before doing
 // anything else. After the body is complete, ModuleGenerator::finishFunc must
 // be called before the FunctionGenerator is destroyed and the next function is
 // started.
 class MOZ_STACK_CLASS FunctionGenerator
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -71,16 +71,17 @@ wasm::AllocateCode(ExclusiveContext* cx,
     MOZ_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
 
     return UniqueCodePtr((uint8_t*)p, CodeDeleter(bytes));
 }
 
 void
 CodeDeleter::operator()(uint8_t* p)
 {
+    MOZ_ASSERT(bytes_ != 0);
     DeallocateExecutableMemory(p, bytes_, AsmJSPageSize);
 }
 
 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
 // On MIPS, CodeLabels are instruction immediates so InternalLinks only
 // patch instruction immediates.
 StaticLinkData::InternalLink::InternalLink(Kind kind)
 {
@@ -457,41 +458,114 @@ CacheableUniquePtr<CharT>::clone(JSConte
         return false;
 
     PodCopy(chars.get(), this->get(), length);
 
     *out = Move(chars);
     return true;
 }
 
+template <class CharT>
+size_t
+CacheableUniquePtr<CharT>::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return mallocSizeOf(this->get());
+}
+
 namespace js {
 namespace wasm {
 template struct CacheableUniquePtr<char>;
 template struct CacheableUniquePtr<char16_t>;
 }
 }
 
-class Module::AutoMutateCode
+size_t
+ModuleData::serializedSize() const
+{
+    return sizeof(pod) +
+           pod.codeBytes +
+           SerializedVectorSize(imports) +
+           SerializedVectorSize(exports) +
+           SerializedPodVectorSize(heapAccesses) +
+           SerializedPodVectorSize(codeRanges) +
+           SerializedPodVectorSize(callSites) +
+           SerializedVectorSize(funcNames) +
+           filename.serializedSize() +
+           displayURL.serializedSize();
+}
+
+uint8_t*
+ModuleData::serialize(uint8_t* cursor) const
 {
-    AutoWritableJitCode awjc_;
-    AutoFlushICache afc_;
+    cursor = WriteBytes(cursor, &pod, sizeof(pod));
+    cursor = WriteBytes(cursor, code.get(), pod.codeBytes);
+    cursor = SerializeVector(cursor, imports);
+    cursor = SerializeVector(cursor, exports);
+    cursor = SerializePodVector(cursor, heapAccesses);
+    cursor = SerializePodVector(cursor, codeRanges);
+    cursor = SerializePodVector(cursor, callSites);
+    cursor = SerializeVector(cursor, funcNames);
+    cursor = filename.serialize(cursor);
+    cursor = displayURL.serialize(cursor);
+    return cursor;
+}
+
+/* static */ const uint8_t*
+ModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+    cursor = ReadBytes(cursor, &pod, sizeof(pod));
+
+    code = AllocateCode(cx, pod.totalBytes());
+    if (!code)
+        return nullptr;
+    cursor = ReadBytes(cursor, code.get(), pod.codeBytes);
 
-   public:
-    AutoMutateCode(JSContext* cx, Module& module, const char* name)
-      : awjc_(cx->runtime(), module.code(), module.pod.codeBytes_),
-        afc_(name)
-    {
-        AutoFlushICache::setRange(uintptr_t(module.code()), module.pod.codeBytes_);
-    }
-};
+    (cursor = DeserializeVector(cx, cursor, &imports)) &&
+    (cursor = DeserializeVector(cx, cursor, &exports)) &&
+    (cursor = DeserializePodVector(cx, cursor, &heapAccesses)) &&
+    (cursor = DeserializePodVector(cx, cursor, &codeRanges)) &&
+    (cursor = DeserializePodVector(cx, cursor, &callSites)) &&
+    (cursor = DeserializeVector(cx, cursor, &funcNames)) &&
+    (cursor = filename.deserialize(cx, cursor)) &&
+    (cursor = displayURL.deserialize(cx, cursor));
+    return cursor;
+}
+
+bool
+ModuleData::clone(JSContext* cx, ModuleData* out) const
+{
+    out->pod = pod;
+
+    out->code = AllocateCode(cx, pod.totalBytes());
+    if (!out->code)
+        return false;
+    memcpy(out->code.get(), code.get(), pod.codeBytes);
 
-uint32_t
-Module::totalBytes() const
+    return CloneVector(cx, imports, &out->imports) &&
+           CloneVector(cx, exports, &out->exports) &&
+           ClonePodVector(cx, heapAccesses, &out->heapAccesses) &&
+           ClonePodVector(cx, codeRanges, &out->codeRanges) &&
+           ClonePodVector(cx, callSites, &out->callSites) &&
+           CloneVector(cx, funcNames, &out->funcNames) &&
+           filename.clone(cx, &out->filename) &&
+           displayURL.clone(cx, &out->displayURL);
+}
+
+size_t
+ModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
-    return pod.codeBytes_ + pod.globalBytes_;
+    // Module::addSizeOfMisc takes care of code and global memory.
+    return SizeOfVectorExcludingThis(imports, mallocSizeOf) +
+           SizeOfVectorExcludingThis(exports, mallocSizeOf) +
+           heapAccesses.sizeOfExcludingThis(mallocSizeOf) +
+           codeRanges.sizeOfExcludingThis(mallocSizeOf) +
+           callSites.sizeOfExcludingThis(mallocSizeOf) +
+           funcNames.sizeOfExcludingThis(mallocSizeOf) +
+           filename.sizeOfExcludingThis(mallocSizeOf) +
+           displayURL.sizeOfExcludingThis(mallocSizeOf);
 }
 
 uint8_t*
 Module::rawHeapPtr() const
 {
     return const_cast<Module*>(this)->rawHeapPtr();
 }
 
@@ -519,40 +593,40 @@ Module::specializeToHeap(ArrayBufferObje
     uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - protected by Module methods*/);
     uint32_t heapLength = heap->byteLength();
 #if defined(JS_CODEGEN_X86)
     // An access is out-of-bounds iff
     //      ptr + offset + data-type-byte-size > heapLength
     // i.e. ptr > heapLength - data-type-byte-size - offset. data-type-byte-size
     // and offset are already included in the addend so we
     // just have to add the heap length here.
-    for (const HeapAccess& access : heapAccesses_) {
+    for (const HeapAccess& access : data_->heapAccesses) {
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength);
         void* addr = access.patchHeapPtrImmAt(code());
         uint32_t disp = reinterpret_cast<uint32_t>(X86Encoding::GetPointer(addr));
         MOZ_ASSERT(disp <= INT32_MAX);
         X86Encoding::SetPointer(addr, (void*)(ptrBase + disp));
     }
 #elif defined(JS_CODEGEN_X64)
     // Even with signal handling being used for most bounds checks, there may be
     // atomic operations that depend on explicit checks.
     //
     // If we have any explicit bounds checks, we need to patch the heap length
     // checks at the right places. All accesses that have been recorded are the
     // only ones that need bound checks (see also
     // CodeGeneratorX64::visitAsmJS{Load,Store,CompareExchange,Exchange,AtomicBinop}Heap)
-    for (const HeapAccess& access : heapAccesses_) {
+    for (const HeapAccess& access : data_->heapAccesses) {
         // See comment above for x86 codegen.
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength);
     }
 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
       defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-    for (const HeapAccess& access : heapAccesses_)
+    for (const HeapAccess& access : data_->heapAccesses)
         Assembler::UpdateBoundsCheck(heapLength, (Instruction*)(access.insnOffset() + code()));
 #endif
 
     heap_ = heap;
     rawHeapPtr() = ptrBase;
 }
 
 void
@@ -562,69 +636,69 @@ Module::despecializeFromHeap(ArrayBuffer
     // another dynamically-linked module which we are despecializing from that
     // module's heap.
     MOZ_ASSERT_IF(heap_, heap_ == heap);
     MOZ_ASSERT_IF(rawHeapPtr(), rawHeapPtr() == heap->dataPointerEither().unwrap());
 
 #if defined(JS_CODEGEN_X86)
     uint32_t heapLength = heap->byteLength();
     uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - used for value*/);
-    for (unsigned i = 0; i < heapAccesses_.length(); i++) {
-        const HeapAccess& access = heapAccesses_[i];
+    for (unsigned i = 0; i < data_->heapAccesses.length(); i++) {
+        const HeapAccess& access = data_->heapAccesses[i];
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength);
         void* addr = access.patchHeapPtrImmAt(code());
         uint8_t* ptr = reinterpret_cast<uint8_t*>(X86Encoding::GetPointer(addr));
         MOZ_ASSERT(ptr >= ptrBase);
         X86Encoding::SetPointer(addr, reinterpret_cast<void*>(ptr - ptrBase));
     }
 #elif defined(JS_CODEGEN_X64)
     uint32_t heapLength = heap->byteLength();
-    for (unsigned i = 0; i < heapAccesses_.length(); i++) {
-        const HeapAccess& access = heapAccesses_[i];
+    for (unsigned i = 0; i < data_->heapAccesses.length(); i++) {
+        const HeapAccess& access = data_->heapAccesses[i];
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength);
     }
 #endif
 
     heap_ = nullptr;
     rawHeapPtr() = nullptr;
 }
 
 void
 Module::sendCodeRangesToProfiler(JSContext* cx)
 {
 #ifdef JS_ION_PERF
     if (PerfFuncEnabled()) {
-        for (const CodeRange& codeRange : codeRanges_) {
+        for (const CodeRange& codeRange : data_->codeRanges) {
             if (!codeRange.isFunction())
                 continue;
 
             uintptr_t start = uintptr_t(code() + codeRange.begin());
             uintptr_t end = uintptr_t(code() + codeRange.end());
             uintptr_t size = end - start;
-            const char* file = filename_.get();
+            const char* file = data_->filename.get();
             unsigned line = codeRange.funcLineNumber();
             unsigned column = 0;
-            const char* name = funcNames_[codeRange.funcNameIndex()].get();
+            const char* name = data_->funcNames[codeRange.funcNameIndex()].get();
 
             writePerfSpewerAsmJSFunctionMap(start, size, file, line, column, name);
         }
     }
 #endif
 #ifdef MOZ_VTUNE
     if (IsVTuneProfilingActive()) {
-        for (const CodeRange& codeRange : codeRanges_) {
+        for (const CodeRange& codeRange : data_->codeRanges) {
             if (!codeRange.isFunction())
                 continue;
 
             uintptr_t start = uintptr_t(code() + codeRange.begin());
             uintptr_t end = uintptr_t(code() + codeRange.end());
             uintptr_t size = end - start;
-            const char* name = funcNames_[codeRange.funcNameIndex()].get();
+            const char* name = data_->funcNames[codeRange.funcNameIndex()].get();
 
             unsigned method_id = iJIT_GetNewMethodID();
             if (method_id == 0)
                 return;
             iJIT_Method_Load method;
             method.method_id = method_id;
             method.method_name = const_cast<char*>(name);
             method.method_load_address = (void*)start;
@@ -649,44 +723,46 @@ Module::setProfilingEnabled(JSContext* c
     if (profilingEnabled_ == enabled)
         return true;
 
     // When enabled, generate profiling labels for every name in funcNames_
     // that is the name of some Function CodeRange. This involves malloc() so
     // do it now since, once we start sampling, we'll be in a signal-handing
     // context where we cannot malloc.
     if (enabled) {
-        if (!funcLabels_.resize(funcNames_.length())) {
+        if (!funcLabels_.resize(data_->funcNames.length())) {
             ReportOutOfMemory(cx);
             return false;
         }
-        for (const CodeRange& codeRange : codeRanges_) {
+        for (const CodeRange& codeRange : data_->codeRanges) {
             if (!codeRange.isFunction())
                 continue;
             unsigned lineno = codeRange.funcLineNumber();
-            const char* name = funcNames_[codeRange.funcNameIndex()].get();
-            UniqueChars label(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno));
+            const char* name = data_->funcNames[codeRange.funcNameIndex()].get();
+            UniqueChars label(JS_smprintf("%s (%s:%u)", name, data_->filename.get(), lineno));
             if (!label) {
                 ReportOutOfMemory(cx);
                 return false;
             }
             funcLabels_[codeRange.funcNameIndex()] = Move(label);
         }
     } else {
         funcLabels_.clear();
     }
 
     // Patch callsites and returns to execute profiling prologues/epililogues.
     {
-        AutoMutateCode amc(cx, *this, "Module::setProfilingEnabled");
+        AutoWritableJitCode awjc(cx->runtime(), code(), codeBytes());
+        AutoFlushICache afc("Module::setProfilingEnabled");
+        AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
 
-        for (const CallSite& callSite : callSites_)
+        for (const CallSite& callSite : data_->callSites)
             EnableProfilingPrologue(*this, callSite, enabled);
 
-        for (const CodeRange& codeRange : codeRanges_)
+        for (const CodeRange& codeRange : data_->codeRanges)
             EnableProfilingEpilogue(*this, codeRange, enabled);
     }
 
     // Update the function-pointer tables to point to profiling prologues.
     for (FuncPtrTable& funcPtrTable : funcPtrTables_) {
         auto array = reinterpret_cast<void**>(globalData() + funcPtrTable.globalDataOffset);
         for (size_t i = 0; i < funcPtrTable.numElems; i++) {
             const CodeRange* codeRange = lookupCodeRange(array[i]);
@@ -704,188 +780,135 @@ Module::setProfilingEnabled(JSContext* c
 }
 
 Module::ImportExit&
 Module::importToExit(const Import& import)
 {
     return *reinterpret_cast<ImportExit*>(globalData() + import.exitGlobalDataOffset());
 }
 
-/* static */ Module::CacheablePod
-Module::zeroPod()
+bool
+Module::clone(JSContext* cx, const StaticLinkData& linkData, Module* out) const
 {
-    CacheablePod pod = {0, 0, 0, HeapUsage::None, false, false, false};
-    return pod;
+    MOZ_ASSERT(dynamicallyLinked_);
+
+    // The out->data_ field was already cloned and initialized when 'out' was
+    // constructed. This function should clone the rest.
+    MOZ_ASSERT(out->data_);
+
+    out->isAsmJS_ = isAsmJS_;
+    out->profilingEnabled_ = profilingEnabled_;
+
+    if (!CloneVector(cx, funcLabels_, &out->funcLabels_))
+        return false;
+
+#ifdef DEBUG
+    // Put the symbolic links back to -1 so PatchDataWithValueCheck assertions
+    // in Module::staticallyLink are valid.
+    for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
+        void* callee = AddressOf(imm, cx);
+        const StaticLinkData::OffsetVector& offsets = linkData.symbolicLinks[imm];
+        for (uint32_t offset : offsets) {
+            jit::Assembler::PatchDataWithValueCheck(jit::CodeLocationLabel(out->code() + offset),
+                                                    jit::PatchedImmPtr((void*)-1),
+                                                    jit::PatchedImmPtr(callee));
+        }
+    }
+#endif
+
+    // If the copied machine code has been specialized to the heap, it must be
+    // unspecialized in the copy.
+    if (usesHeap())
+        out->despecializeFromHeap(heap_);
+
+    return true;
 }
 
-void
-Module::init()
+
+Module::Module(UniqueModuleData data, AsmJSBool isAsmJS)
+  : data_(Move(data)),
+    isAsmJS_(bool(isAsmJS)),
+    staticallyLinked_(false),
+    interrupt_(nullptr),
+    outOfBounds_(nullptr),
+    dynamicallyLinked_(false),
+    profilingEnabled_(false)
 {
-   staticallyLinked_ = false;
-   interrupt_ = nullptr;
-   outOfBounds_ = nullptr;
-   dynamicallyLinked_ = false;
-
     *(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN();
     *(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN();
 }
 
-// Private constructor used for deserialization and cloning.
-Module::Module(const CacheablePod& pod,
-               UniqueCodePtr code,
-               ImportVector&& imports,
-               ExportVector&& exports,
-               HeapAccessVector&& heapAccesses,
-               CodeRangeVector&& codeRanges,
-               CallSiteVector&& callSites,
-               CacheableCharsVector&& funcNames,
-               CacheableChars filename,
-               CacheableTwoByteChars displayURL,
-               CacheBool loadedFromCache,
-               ProfilingBool profilingEnabled,
-               FuncLabelVector&& funcLabels)
-  : pod(pod),
-    code_(Move(code)),
-    imports_(Move(imports)),
-    exports_(Move(exports)),
-    heapAccesses_(Move(heapAccesses)),
-    codeRanges_(Move(codeRanges)),
-    callSites_(Move(callSites)),
-    funcNames_(Move(funcNames)),
-    filename_(Move(filename)),
-    displayURL_(Move(displayURL)),
-    loadedFromCache_(loadedFromCache),
-    profilingEnabled_(profilingEnabled),
-    funcLabels_(Move(funcLabels))
-{
-    MOZ_ASSERT_IF(!profilingEnabled, funcLabels_.empty());
-    MOZ_ASSERT_IF(profilingEnabled, funcNames_.length() == funcLabels_.length());
-    init();
-}
-
-// Public constructor for compilation.
-Module::Module(CompileArgs args,
-               uint32_t functionBytes,
-               uint32_t codeBytes,
-               uint32_t globalBytes,
-               HeapUsage heapUsage,
-               MutedBool mutedErrors,
-               UniqueCodePtr code,
-               ImportVector&& imports,
-               ExportVector&& exports,
-               HeapAccessVector&& heapAccesses,
-               CodeRangeVector&& codeRanges,
-               CallSiteVector&& callSites,
-               CacheableCharsVector&& funcNames,
-               CacheableChars filename,
-               CacheableTwoByteChars displayURL)
-  : pod(zeroPod()),
-    code_(Move(code)),
-    imports_(Move(imports)),
-    exports_(Move(exports)),
-    heapAccesses_(Move(heapAccesses)),
-    codeRanges_(Move(codeRanges)),
-    callSites_(Move(callSites)),
-    funcNames_(Move(funcNames)),
-    filename_(Move(filename)),
-    displayURL_(Move(displayURL)),
-    loadedFromCache_(false),
-    profilingEnabled_(false)
-{
-    // Work around MSVC 2013 bug around {} member initialization.
-    const_cast<uint32_t&>(pod.functionBytes_) = functionBytes;
-    const_cast<uint32_t&>(pod.codeBytes_) = codeBytes;
-    const_cast<uint32_t&>(pod.globalBytes_) = globalBytes;
-    const_cast<HeapUsage&>(pod.heapUsage_) = heapUsage;
-    const_cast<bool&>(pod.mutedErrors_) = bool(mutedErrors);
-    const_cast<bool&>(pod.usesSignalHandlersForOOB_) = args.useSignalHandlersForOOB;
-    const_cast<bool&>(pod.usesSignalHandlersForInterrupt_) = args.useSignalHandlersForInterrupt;
-
-    init();
-}
-
 Module::~Module()
 {
-    if (code_) {
-        for (unsigned i = 0; i < imports_.length(); i++) {
-            ImportExit& exit = importToExit(imports_[i]);
-            if (exit.baselineScript)
-                exit.baselineScript->removeDependentWasmModule(*this, i);
-        }
+    for (unsigned i = 0; i < imports().length(); i++) {
+        ImportExit& exit = importToExit(imports()[i]);
+        if (exit.baselineScript)
+            exit.baselineScript->removeDependentWasmModule(*this, i);
     }
 }
 
 void
 Module::trace(JSTracer* trc)
 {
-    for (const Import& import : imports_) {
+    for (const Import& import : imports()) {
         if (importToExit(import).fun)
             TraceEdge(trc, &importToExit(import).fun, "wasm function import");
     }
 
     if (heap_)
         TraceEdge(trc, &heap_, "wasm buffer");
 }
 
-CompileArgs
-Module::compileArgs() const
-{
-    CompileArgs args;
-    args.useSignalHandlersForOOB = pod.usesSignalHandlersForOOB_;
-    args.useSignalHandlersForInterrupt = pod.usesSignalHandlersForInterrupt_;
-    return args;
-}
-
 bool
 Module::containsFunctionPC(void* pc) const
 {
-    return pc >= code() && pc < (code() + pod.functionBytes_);
+    return pc >= code() && pc < (code() + data_->pod.functionBytes);
 }
 
 bool
 Module::containsCodePC(void* pc) const
 {
-    return pc >= code() && pc < (code() + pod.codeBytes_);
+    return pc >= code() && pc < (code() + codeBytes());
 }
 
 struct CallSiteRetAddrOffset
 {
     const CallSiteVector& callSites;
     explicit CallSiteRetAddrOffset(const CallSiteVector& callSites) : callSites(callSites) {}
     uint32_t operator[](size_t index) const {
         return callSites[index].returnAddressOffset();
     }
 };
 
 const CallSite*
 Module::lookupCallSite(void* returnAddress) const
 {
     uint32_t target = ((uint8_t*)returnAddress) - code();
     size_t lowerBound = 0;
-    size_t upperBound = callSites_.length();
+    size_t upperBound = data_->callSites.length();
 
     size_t match;
-    if (!BinarySearch(CallSiteRetAddrOffset(callSites_), lowerBound, upperBound, target, &match))
+    if (!BinarySearch(CallSiteRetAddrOffset(data_->callSites), lowerBound, upperBound, target, &match))
         return nullptr;
 
-    return &callSites_[match];
+    return &data_->callSites[match];
 }
 
 const CodeRange*
 Module::lookupCodeRange(void* pc) const
 {
     CodeRange::PC target((uint8_t*)pc - code());
     size_t lowerBound = 0;
-    size_t upperBound = codeRanges_.length();
+    size_t upperBound = data_->codeRanges.length();
 
     size_t match;
-    if (!BinarySearch(codeRanges_, lowerBound, upperBound, target, &match))
+    if (!BinarySearch(data_->codeRanges, lowerBound, upperBound, target, &match))
         return nullptr;
 
-    return &codeRanges_[match];
+    return &data_->codeRanges[match];
 }
 
 struct HeapAccessOffset
 {
     const HeapAccessVector& accesses;
     explicit HeapAccessOffset(const HeapAccessVector& accesses) : accesses(accesses) {}
     uintptr_t operator[](size_t index) const {
         return accesses[index].insnOffset();
@@ -894,38 +917,38 @@ struct HeapAccessOffset
 
 const HeapAccess*
 Module::lookupHeapAccess(void* pc) const
 {
     MOZ_ASSERT(containsFunctionPC(pc));
 
     uint32_t target = ((uint8_t*)pc) - code();
     size_t lowerBound = 0;
-    size_t upperBound = heapAccesses_.length();
+    size_t upperBound = data_->heapAccesses.length();
 
     size_t match;
-    if (!BinarySearch(HeapAccessOffset(heapAccesses_), lowerBound, upperBound, target, &match))
+    if (!BinarySearch(HeapAccessOffset(data_->heapAccesses), lowerBound, upperBound, target, &match))
         return nullptr;
 
-    return &heapAccesses_[match];
+    return &data_->heapAccesses[match];
 }
 
 bool
 Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData)
 {
     MOZ_ASSERT(!dynamicallyLinked_);
     MOZ_ASSERT(!staticallyLinked_);
     staticallyLinked_ = true;
 
     // Push a JitContext for benefit of IsCompilingAsmJS and delay flushing
     // until Module::dynamicallyLink.
     JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
     MOZ_ASSERT(IsCompilingAsmJS());
     AutoFlushICache afc("Module::staticallyLink", /* inhibit = */ true);
-    AutoFlushICache::setRange(uintptr_t(code()), pod.codeBytes_);
+    AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
 
     interrupt_ = code() + linkData.pod.interruptOffset;
     outOfBounds_ = code() + linkData.pod.outOfBoundsOffset;
 
     for (StaticLinkData::InternalLink link : linkData.internalLinks) {
         uint8_t* patchAt = code() + link.patchAtOffset;
         void* target = code() + link.targetOffset;
 
@@ -978,45 +1001,45 @@ Module::staticallyLink(ExclusiveContext*
         return false;
     }
 
     return true;
 }
 
 bool
 Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
-                        const AutoVectorRooter<JSFunction*>& imports)
+                        const AutoVectorRooter<JSFunction*>& importArgs)
 {
     MOZ_ASSERT(staticallyLinked_);
     MOZ_ASSERT(!dynamicallyLinked_);
     dynamicallyLinked_ = true;
 
     // Push a JitContext for benefit of IsCompilingAsmJS and flush the ICache.
     // We've been inhibiting flushing up to this point so flush it all now.
     JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
     MOZ_ASSERT(IsCompilingAsmJS());
     AutoFlushICache afc("Module::dynamicallyLink");
-    AutoFlushICache::setRange(uintptr_t(code()), pod.codeBytes_);
+    AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
 
     // Initialize imports with actual imported values.
-    MOZ_ASSERT(imports.length() == imports_.length());
-    for (size_t i = 0; i < imports_.length(); i++) {
-        const Import& import = imports_[i];
+    MOZ_ASSERT(importArgs.length() == imports().length());
+    for (size_t i = 0; i < imports().length(); i++) {
+        const Import& import = imports()[i];
         ImportExit& exit = importToExit(import);
         exit.code = code() + import.interpExitCodeOffset();
-        exit.fun = imports[i];
+        exit.fun = importArgs[i];
         exit.baselineScript = nullptr;
     }
 
     // Specialize code to the actual heap.
     if (usesHeap())
         specializeToHeap(heap);
 
     // See AllocateCode comment above.
-    if (!ExecutableAllocator::makeExecutable(code(), pod.codeBytes_)) {
+    if (!ExecutableAllocator::makeExecutable(code(), codeBytes())) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     sendCodeRangesToProfiler(cx);
     return true;
 }
 
@@ -1038,28 +1061,28 @@ Module::heapLength() const
     MOZ_ASSERT(usesHeap());
     return heap_->byteLength();
 }
 
 void
 Module::deoptimizeImportExit(uint32_t importIndex)
 {
     MOZ_ASSERT(dynamicallyLinked_);
-    const Import& import = imports_[importIndex];
+    const Import& import = imports()[importIndex];
     ImportExit& exit = importToExit(import);
     exit.code = code() + import.interpExitCodeOffset();
     exit.baselineScript = nullptr;
 }
 
 bool
 Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
 {
     MOZ_ASSERT(dynamicallyLinked_);
 
-    const Export& exp = exports_[exportIndex];
+    const Export& exp = exports()[exportIndex];
 
     // Enable/disable profiling in the Module to match the current global
     // profiling state. Don't do this if the Module is already active on the
     // stack since this would leave the Module in a state where profiling is
     // enabled but the stack isn't unwindable.
     if (profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !activation()) {
         if (!setProfilingEnabled(cx, cx->runtime()->spsProfiler.enabled()))
             return false;
@@ -1185,17 +1208,17 @@ Module::callExport(JSContext* cx, uint32
 }
 
 bool
 Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv,
                    MutableHandleValue rval)
 {
     MOZ_ASSERT(dynamicallyLinked_);
 
-    const Import& import = imports_[importIndex];
+    const Import& import = imports()[importIndex];
 
     RootedValue fval(cx, ObjectValue(*importToExit(import).fun));
     if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval))
         return false;
 
     ImportExit& exit = importToExit(import);
 
     // The exit may already have become optimized.
@@ -1259,216 +1282,96 @@ Module::callImport(JSContext* cx, uint32
 const char*
 Module::profilingLabel(uint32_t funcIndex) const
 {
     MOZ_ASSERT(dynamicallyLinked_);
     MOZ_ASSERT(profilingEnabled_);
     return funcLabels_[funcIndex].get();
 }
 
-size_t
-Module::serializedSize() const
+void
+Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
 {
-    return sizeof(pod) +
-           pod.codeBytes_ +
-           SerializedVectorSize(imports_) +
-           SerializedVectorSize(exports_) +
-           SerializedPodVectorSize(heapAccesses_) +
-           SerializedPodVectorSize(codeRanges_) +
-           SerializedPodVectorSize(callSites_) +
-           SerializedVectorSize(funcNames_) +
-           filename_.serializedSize() +
-           displayURL_.serializedSize();
-}
-
-uint8_t*
-Module::serialize(uint8_t* cursor) const
-{
-    MOZ_ASSERT(!profilingEnabled_, "assumed by Module::deserialize");
-
-    cursor = WriteBytes(cursor, &pod, sizeof(pod));
-    cursor = WriteBytes(cursor, code(), pod.codeBytes_);
-    cursor = SerializeVector(cursor, imports_);
-    cursor = SerializeVector(cursor, exports_);
-    cursor = SerializePodVector(cursor, heapAccesses_);
-    cursor = SerializePodVector(cursor, codeRanges_);
-    cursor = SerializePodVector(cursor, callSites_);
-    cursor = SerializeVector(cursor, funcNames_);
-    cursor = filename_.serialize(cursor);
-    cursor = displayURL_.serialize(cursor);
-    return cursor;
+    *code += codeBytes();
+    *data += mallocSizeOf(this) +
+             globalBytes() +
+             mallocSizeOf(data_.get()) +
+             data_->sizeOfExcludingThis(mallocSizeOf) +
+             funcPtrTables_.sizeOfExcludingThis(mallocSizeOf) +
+             SizeOfVectorExcludingThis(funcLabels_, mallocSizeOf);
 }
 
-/* static */ const uint8_t*
-Module::deserialize(ExclusiveContext* cx, const uint8_t* cursor, UniqueModule* out)
-{
-    CacheablePod pod = zeroPod();
-    cursor = ReadBytes(cursor, &pod, sizeof(pod));
-    if (!cursor)
-        return nullptr;
-
-    UniqueCodePtr code = AllocateCode(cx, pod.codeBytes_ + pod.globalBytes_);
-    if (!code)
-        return nullptr;
-
-    cursor = ReadBytes(cursor, code.get(), pod.codeBytes_);
-
-    ImportVector imports;
-    cursor = DeserializeVector(cx, cursor, &imports);
-    if (!cursor)
-        return nullptr;
-
-    ExportVector exports;
-    cursor = DeserializeVector(cx, cursor, &exports);
-    if (!cursor)
-        return nullptr;
+const Class WasmModuleObject::class_ = {
+    "WasmModuleObject",
+    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
+    JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS),
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    WasmModuleObject::finalize,
+    nullptr, /* call */
+    nullptr, /* hasInstance */
+    nullptr, /* construct */
+    WasmModuleObject::trace
+};
 
-    HeapAccessVector heapAccesses;
-    cursor = DeserializePodVector(cx, cursor, &heapAccesses);
-    if (!cursor)
-        return nullptr;
+bool
+WasmModuleObject::hasModule() const
+{
+    MOZ_ASSERT(is<WasmModuleObject>());
+    return !getReservedSlot(MODULE_SLOT).isUndefined();
+}
 
-    CodeRangeVector codeRanges;
-    cursor = DeserializePodVector(cx, cursor, &codeRanges);
-    if (!cursor)
-        return nullptr;
+/* static */ void
+WasmModuleObject::finalize(FreeOp* fop, JSObject* obj)
+{
+    WasmModuleObject& moduleObj = obj->as<WasmModuleObject>();
+    if (moduleObj.hasModule())
+        fop->delete_(&moduleObj.module());
+}
 
-    CallSiteVector callSites;
-    cursor = DeserializePodVector(cx, cursor, &callSites);
-    if (!cursor)
-        return nullptr;
+/* static */ void
+WasmModuleObject::trace(JSTracer* trc, JSObject* obj)
+{
+    WasmModuleObject& moduleObj = obj->as<WasmModuleObject>();
+    if (moduleObj.hasModule())
+        moduleObj.module().trace(trc);
+}
 
-    CacheableCharsVector funcNames;
-    cursor = DeserializeVector(cx, cursor, &funcNames);
-    if (!cursor)
-        return nullptr;
-
-    CacheableChars filename;
-    cursor = filename.deserialize(cx, cursor);
-    if (!cursor)
-        return nullptr;
-
-    CacheableTwoByteChars displayURL;
-    cursor = displayURL.deserialize(cx, cursor);
-    if (!cursor)
+/* static */ WasmModuleObject*
+WasmModuleObject::create(ExclusiveContext* cx)
+{
+    AutoSetNewObjectMetadata metadata(cx);
+    JSObject* obj = NewObjectWithGivenProto(cx, &WasmModuleObject::class_, nullptr);
+    if (!obj)
         return nullptr;
 
-    *out = cx->make_unique<Module>(pod,
-                                   Move(code),
-                                   Move(imports),
-                                   Move(exports),
-                                   Move(heapAccesses),
-                                   Move(codeRanges),
-                                   Move(callSites),
-                                   Move(funcNames),
-                                   Move(filename),
-                                   Move(displayURL),
-                                   Module::LoadedFromCache,
-                                   Module::ProfilingDisabled,
-                                   FuncLabelVector());
-
-    return cursor;
-}
-
-Module::UniqueModule
-Module::clone(JSContext* cx, const StaticLinkData& linkData) const
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-
-    UniqueCodePtr code = AllocateCode(cx, totalBytes());
-    if (!code)
-        return nullptr;
-
-    memcpy(code.get(), this->code(), pod.codeBytes_);
-
-#ifdef DEBUG
-    // Put the symbolic links back to -1 so PatchDataWithValueCheck assertions
-    // in Module::staticallyLink are valid.
-    for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
-        void* callee = AddressOf(imm, cx);
-        const StaticLinkData::OffsetVector& offsets = linkData.symbolicLinks[imm];
-        for (uint32_t offset : offsets) {
-            jit::Assembler::PatchDataWithValueCheck(jit::CodeLocationLabel(code.get() + offset),
-                                                    jit::PatchedImmPtr((void*)-1),
-                                                    jit::PatchedImmPtr(callee));
-        }
-    }
-#endif
-
-    ImportVector imports;
-    if (!CloneVector(cx, imports_, &imports))
-        return nullptr;
-
-    ExportVector exports;
-    if (!CloneVector(cx, exports_, &exports))
-        return nullptr;
-
-    HeapAccessVector heapAccesses;
-    if (!ClonePodVector(cx, heapAccesses_, &heapAccesses))
-        return nullptr;
-
-    CodeRangeVector codeRanges;
-    if (!ClonePodVector(cx, codeRanges_, &codeRanges))
-        return nullptr;
-
-    CallSiteVector callSites;
-    if (!ClonePodVector(cx, callSites_, &callSites))
-        return nullptr;
-
-    CacheableCharsVector funcNames;
-    if (!CloneVector(cx, funcNames_, &funcNames))
-        return nullptr;
-
-    CacheableChars filename;
-    if (!filename_.clone(cx, &filename))
-        return nullptr;
-
-    CacheableTwoByteChars displayURL;
-    if (!displayURL_.clone(cx, &displayURL))
-        return nullptr;
-
-    FuncLabelVector funcLabels;
-    if (!CloneVector(cx, funcLabels_, &funcLabels))
-        return nullptr;
-
-    // Must not GC between Module allocation and (successful) return.
-    auto out = cx->make_unique<Module>(pod,
-                                       Move(code),
-                                       Move(imports),
-                                       Move(exports),
-                                       Move(heapAccesses),
-                                       Move(codeRanges),
-                                       Move(callSites),
-                                       Move(funcNames),
-                                       Move(filename),
-                                       Move(displayURL),
-                                       CacheBool::NotLoadedFromCache,
-                                       ProfilingBool(profilingEnabled_),
-                                       Move(funcLabels));
-    if (!out)
-        return nullptr;
-
-    // If the copied machine code has been specialized to the heap, it must be
-    // unspecialized in the copy.
-    if (usesHeap())
-        out->despecializeFromHeap(heap_);
-
-    if (!out->staticallyLink(cx, linkData))
-        return nullptr;
-
-    return Move(out);
+    return &obj->as<WasmModuleObject>();
 }
 
 void
-Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode, size_t* asmJSModuleData)
+WasmModuleObject::initModule(UniqueModule module)
 {
-    *asmJSModuleCode += pod.codeBytes_;
-    *asmJSModuleData += mallocSizeOf(this) +
-                        pod.globalBytes_ +
-                        SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
-                        SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
-                        heapAccesses_.sizeOfExcludingThis(mallocSizeOf) +
-                        codeRanges_.sizeOfExcludingThis(mallocSizeOf) +
-                        callSites_.sizeOfExcludingThis(mallocSizeOf) +
-                        funcNames_.sizeOfExcludingThis(mallocSizeOf) +
-                        funcPtrTables_.sizeOfExcludingThis(mallocSizeOf);
+    MOZ_ASSERT(is<WasmModuleObject>());
+    MOZ_ASSERT(!hasModule());
+    MOZ_ASSERT(module);
+    setReservedSlot(MODULE_SLOT, PrivateValue(module.release()));
 }
 
+Module&
+WasmModuleObject::module() const
+{
+    MOZ_ASSERT(is<WasmModuleObject>());
+    MOZ_ASSERT(hasModule());
+    return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate();
+}
+
+void
+WasmModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
+{
+    if (hasModule())
+        module().addSizeOfMisc(mallocSizeOf, code, data);
+}
+
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -20,27 +20,27 @@
 #define wasm_module_h
 
 #include "asmjs/WasmTypes.h"
 #include "gc/Barrier.h"
 #include "vm/MallocProvider.h"
 
 namespace js {
 
+class AsmJSModule;
 class WasmActivation;
 namespace jit { struct BaselineScript; }
 
 namespace wasm {
 
 // A wasm Module and everything it contains must support serialization,
 // deserialization and cloning. Some data can be simply copied as raw bytes and,
 // as a convention, is stored in an inline CacheablePod struct. Everything else
 // should implement the below methods which are called recusively by the
-// containing Module. The implementation of all these methods are grouped
-// together in WasmSerialize.cpp.
+// containing Module. See comments for these methods in wasm::Module.
 
 #define WASM_DECLARE_SERIALIZABLE(Type)                                         \
     size_t serializedSize() const;                                              \
     uint8_t* serialize(uint8_t* cursor) const;                                  \
     const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);    \
     bool clone(JSContext* cx, Type* out) const;                                 \
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
@@ -307,16 +307,17 @@ typedef Vector<CacheableChars, 0, System
 
 // A UniqueCodePtr owns allocated executable code. Code passed to the Module
 // constructor must be allocated via AllocateCode.
 
 class CodeDeleter
 {
     uint32_t bytes_;
   public:
+    CodeDeleter() : bytes_(0) {}
     explicit CodeDeleter(uint32_t bytes) : bytes_(bytes) {}
     void operator()(uint8_t* p);
 };
 typedef JS::UniquePtr<uint8_t, CodeDeleter> UniqueCodePtr;
 
 UniqueCodePtr
 AllocateCode(ExclusiveContext* cx, size_t bytes);
 
@@ -331,16 +332,64 @@ enum class HeapUsage
 };
 
 static inline bool
 UsesHeap(HeapUsage heapUsage)
 {
     return bool(heapUsage);
 }
 
+// See mutedErrors comment in jsapi.h.
+
+enum class MutedErrorsBool
+{
+    DontMuteErrors = false,
+    MuteErrors = true
+};
+
+// ModuleData holds the trivially-memcpy()able serializable portion of
+// ModuleData.
+
+struct ModuleCacheablePod
+{
+    uint32_t              functionBytes;
+    uint32_t              codeBytes;
+    uint32_t              globalBytes;
+    HeapUsage             heapUsage;
+    MutedErrorsBool       mutedErrors;
+    CompileArgs           compileArgs;
+
+    uint32_t totalBytes() const { return codeBytes + globalBytes; }
+};
+
+// ModuleData holds the guts of a Module. ModuleData is mutably built up by
+// ModuleGenerator and then handed over to the Module constructor in finish(),
+// where it is stored immutably.
+
+struct ModuleData
+{
+    ModuleData() : loadedFromCache(false) { mozilla::PodZero(&pod); }
+
+    ModuleCacheablePod    pod;
+    UniqueCodePtr         code;
+    ImportVector          imports;
+    ExportVector          exports;
+    HeapAccessVector      heapAccesses;
+    CodeRangeVector       codeRanges;
+    CallSiteVector        callSites;
+    CacheableCharsVector  funcNames;
+    CacheableChars        filename;
+    CacheableTwoByteChars displayURL;
+    bool                  loadedFromCache;
+
+    WASM_DECLARE_SERIALIZABLE(ModuleData);
+};
+
+typedef UniquePtr<ModuleData, JS::DeletePolicy<ModuleData>> UniqueModuleData;
+
 // Module represents a compiled WebAssembly module which lives until the last
 // reference to any exported functions is dropped. Modules must be wrapped by a
 // rooted JSObject immediately after creation so that Module::trace() is called
 // during GC. Modules are created after compilation completes and start in a
 // a fully unlinked state. After creation, a module must be first statically
 // linked and then dynamically linked:
 //
 //  - Static linking patches code or global data that relies on absolute
@@ -353,16 +402,17 @@ UsesHeap(HeapUsage heapUsage)
 //    once. However, a dynamically-linked module may be cloned so that the clone
 //    can be independently dynamically linked.
 //
 // Once fully dynamically linked, a Module can have its exports invoked via
 // callExport().
 
 class Module
 {
+    typedef UniquePtr<const ModuleData, JS::DeletePolicy<const ModuleData>> UniqueConstModuleData;
     struct ImportExit {
         void* code;
         jit::BaselineScript* baselineScript;
         HeapPtrFunction fun;
         static_assert(sizeof(HeapPtrFunction) == sizeof(void*), "for JIT access");
     };
     struct EntryArg {
         uint64_t lo;
@@ -377,126 +427,85 @@ class Module
             numElems(table.elemOffsets.length())
         {}
     };
     typedef Vector<FuncPtrTable, 0, SystemAllocPolicy> FuncPtrTableVector;
     typedef Vector<CacheableChars, 0, SystemAllocPolicy> FuncLabelVector;
     typedef RelocatablePtrArrayBufferObjectMaybeShared BufferPtr;
 
     // Initialized when constructed:
-    struct CacheablePod {
-        const uint32_t           functionBytes_;
-        const uint32_t           codeBytes_;
-        const uint32_t           globalBytes_;
-        const HeapUsage          heapUsage_;
-        const bool               mutedErrors_;
-        const bool               usesSignalHandlersForOOB_;
-        const bool               usesSignalHandlersForInterrupt_;
-    } pod;
-    const UniqueCodePtr          code_;
-    const ImportVector           imports_;
-    const ExportVector           exports_;
-    const HeapAccessVector       heapAccesses_;
-    const CodeRangeVector        codeRanges_;
-    const CallSiteVector         callSites_;
-    const CacheableCharsVector   funcNames_;
-    const CacheableChars         filename_;
-    const CacheableTwoByteChars  displayURL_;
-    const bool                   loadedFromCache_;
+    const UniqueConstModuleData  data_;
+    bool                         isAsmJS_;
 
     // Initialized during staticallyLink:
     bool                         staticallyLinked_;
     uint8_t*                     interrupt_;
     uint8_t*                     outOfBounds_;
     FuncPtrTableVector           funcPtrTables_;
 
     // Initialized during dynamicallyLink:
     bool                         dynamicallyLinked_;
     BufferPtr                    heap_;
 
     // Mutated after dynamicallyLink:
     bool                         profilingEnabled_;
     FuncLabelVector              funcLabels_;
 
-    class AutoMutateCode;
-
-    uint32_t totalBytes() const;
     uint8_t* rawHeapPtr() const;
     uint8_t*& rawHeapPtr();
     WasmActivation*& activation();
     void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
     void despecializeFromHeap(ArrayBufferObjectMaybeShared* heap);
     void sendCodeRangesToProfiler(JSContext* cx);
     MOZ_WARN_UNUSED_RESULT bool setProfilingEnabled(JSContext* cx, bool enabled);
     ImportExit& importToExit(const Import& import);
 
-    enum CacheBool { NotLoadedFromCache = false, LoadedFromCache = true };
-    enum ProfilingBool { ProfilingDisabled = false, ProfilingEnabled = true };
+    friend class js::WasmActivation;
 
-    static CacheablePod zeroPod();
-    void init();
-    Module(const CacheablePod& pod,
-           UniqueCodePtr code,
-           ImportVector&& imports,
-           ExportVector&& exports,
-           HeapAccessVector&& heapAccesses,
-           CodeRangeVector&& codeRanges,
-           CallSiteVector&& callSites,
-           CacheableCharsVector&& funcNames,
-           CacheableChars filename,
-           CacheableTwoByteChars displayURL,
-           CacheBool loadedFromCache,
-           ProfilingBool profilingEnabled,
-           FuncLabelVector&& funcLabels);
-
-    template <class> friend struct js::MallocProvider;
-    friend class js::WasmActivation;
+  protected:
+    enum AsmJSBool { NotAsmJS = false, IsAsmJS = true };
+    const ModuleData& baseData() const { return *data_; }
+    bool clone(JSContext* cx, const StaticLinkData& linkData, Module* clone) const;
 
   public:
     static const unsigned SizeOfImportExit = sizeof(ImportExit);
     static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
     static const unsigned SizeOfEntryArg = sizeof(EntryArg);
 
-    enum MutedBool { DontMuteErrors = false, MuteErrors = true };
+    explicit Module(UniqueModuleData data, AsmJSBool = NotAsmJS);
+    virtual ~Module();
+    virtual void trace(JSTracer* trc);
 
-    Module(CompileArgs args,
-           uint32_t functionBytes,
-           uint32_t codeBytes,
-           uint32_t globalBytes,
-           HeapUsage heapUsage,
-           MutedBool mutedErrors,
-           UniqueCodePtr code,
-           ImportVector&& imports,
-           ExportVector&& exports,
-           HeapAccessVector&& heapAccesses,
-           CodeRangeVector&& codeRanges,
-           CallSiteVector&& callSites,
-           CacheableCharsVector&& funcNames,
-           CacheableChars filename,
-           CacheableTwoByteChars displayURL);
-    ~Module();
-    void trace(JSTracer* trc);
-
-    uint8_t* code() const { return code_.get(); }
-    uint8_t* globalData() const { return code() + pod.codeBytes_; }
-    uint32_t globalBytes() const { return pod.globalBytes_; }
-    HeapUsage heapUsage() const { return pod.heapUsage_; }
-    bool usesHeap() const { return UsesHeap(pod.heapUsage_); }
-    bool hasSharedHeap() const { return pod.heapUsage_ == HeapUsage::Shared; }
-    bool mutedErrors() const { return pod.mutedErrors_; }
-    CompileArgs compileArgs() const;
-    const ImportVector& imports() const { return imports_; }
-    const ExportVector& exports() const { return exports_; }
-    const char* functionName(uint32_t i) const { return funcNames_[i].get(); }
-    const char* filename() const { return filename_.get(); }
-    const char16_t* displayURL() const { return displayURL_.get(); }
-    bool loadedFromCache() const { return loadedFromCache_; }
+    uint8_t* code() const { return data_->code.get(); }
+    uint32_t codeBytes() const { return data_->pod.codeBytes; }
+    uint8_t* globalData() const { return code() + data_->pod.codeBytes; }
+    uint32_t globalBytes() const { return data_->pod.globalBytes; }
+    HeapUsage heapUsage() const { return data_->pod.heapUsage; }
+    bool usesHeap() const { return UsesHeap(data_->pod.heapUsage); }
+    bool hasSharedHeap() const { return data_->pod.heapUsage == HeapUsage::Shared; }
+    bool mutedErrors() const { return bool(data_->pod.mutedErrors); }
+    CompileArgs compileArgs() const { return data_->pod.compileArgs; }
+    const ImportVector& imports() const { return data_->imports; }
+    const ExportVector& exports() const { return data_->exports; }
+    const char* functionName(uint32_t i) const { return data_->funcNames[i].get(); }
+    const char* filename() const { return data_->filename.get(); }
+    const char16_t* displayURL() const { return data_->displayURL.get(); }
+    bool loadedFromCache() const { return data_->loadedFromCache; }
     bool staticallyLinked() const { return staticallyLinked_; }
     bool dynamicallyLinked() const { return dynamicallyLinked_; }
 
+    // Some wasm::Module's have the most-derived type AsmJSModule. The
+    // AsmJSModule stores the extra metadata necessary to implement asm.js (JS)
+    // semantics. The asAsmJS() member may be used as a checked downcast when
+    // isAsmJS() is true.
+
+    bool isAsmJS() const { return isAsmJS_; }
+    AsmJSModule& asAsmJS() { MOZ_ASSERT(isAsmJS_); return *(AsmJSModule*)this; }
+    const AsmJSModule& asAsmJS() const { MOZ_ASSERT(isAsmJS_); return *(const AsmJSModule*)this; }
+
     // The range [0, functionBytes) is a subrange of [0, codeBytes) that
     // contains only function body code, not the stub code. This distinction is
     // used by the async interrupt handler to only interrupt when the pc is in
     // function code which, in turn, simplifies reasoning about how stubs
     // enter/exit.
 
     bool containsFunctionPC(void* pc) const;
     bool containsCodePC(void* pc) const;
@@ -549,23 +558,43 @@ class Module
     // Each Module has a profilingEnabled state which is updated to match
     // SPSProfiler::enabled() on the next Module::callExport when there are no
     // frames from the Module on the stack. The ProfilingFrameIterator only
     // shows frames for Module activations that have profilingEnabled.
 
     bool profilingEnabled() const { return profilingEnabled_; }
     const char* profilingLabel(uint32_t funcIndex) const;
 
-    // See WASM_DECLARE_SERIALIZABLE.
-    size_t serializedSize() const;
-    uint8_t* serialize(uint8_t* cursor) const;
-    typedef UniquePtr<Module, JS::DeletePolicy<Module>> UniqueModule;
-    static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor,
-                                      UniqueModule* out);
-    UniqueModule clone(JSContext* cx, const StaticLinkData& linkData) const;
-    void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
-                       size_t* asmJSModuleData);
+    // This function reports the code/data memory usage of this module.
+
+    virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
 };
 
-} // namespace js
+typedef UniquePtr<Module, JS::DeletePolicy<Module>> UniqueModule;
+
 } // namespace wasm
 
+// An WasmModuleObject is an internal object (i.e., not exposed directly to user
+// code) which traces and owns a wasm::Module. The WasmModuleObject is
+// referenced by the extended slots of exported JSFunctions and serves to keep
+// the wasm::Module alive until its last GC reference is dead.
+
+class WasmModuleObject : public NativeObject
+{
+    static const unsigned MODULE_SLOT = 0;
+    bool hasModule() const;
+    static void finalize(FreeOp* fop, JSObject* obj);
+    static void trace(JSTracer* trc, JSObject* obj);
+  public:
+    static const unsigned RESERVED_SLOTS = 1;
+    static WasmModuleObject* create(ExclusiveContext* cx);
+    void initModule(wasm::UniqueModule module);
+    wasm::Module& module() const;
+    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
+    static const Class class_;
+};
+
+typedef Handle<WasmModuleObject*> HandleWasmModule;
+typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModule;
+
+} // namespace js
+
 #endif // wasm_module_h
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -33,17 +33,17 @@
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jswin.h"
 #include "jswrapper.h"
 
-#include "asmjs/AsmJS.h"
+#include "asmjs/WasmModule.h"
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "builtin/SymbolObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
 #include "js/Proxy.h"
@@ -3786,19 +3786,19 @@ JSObject::addSizeOfExcludingThis(mozilla
     } else if (is<RegExpStaticsObject>()) {
         info->objectsMallocHeapMisc += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
     } else if (is<PropertyIteratorObject>()) {
         info->objectsMallocHeapMisc += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
     } else if (is<ArrayBufferObject>()) {
         ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
     } else if (is<SharedArrayBufferObject>()) {
         SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
-    } else if (is<AsmJSModuleObject>()) {
-        as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS,
-                                              &info->objectsMallocHeapMisc);
+    } else if (is<WasmModuleObject>()) {
+        as<WasmModuleObject>().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS,
+                                             &info->objectsMallocHeapMisc);
 #ifdef JS_HAS_CTYPES
     } else {
         // This must be the last case.
         info->objectsMallocHeapMisc +=
             js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject*>(this));
 #endif
     }
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -811,24 +811,34 @@ class ScriptSource
   private:
     size_t computedSizeOfData() const;
 };
 
 class ScriptSourceHolder
 {
     ScriptSource* ss;
   public:
+    ScriptSourceHolder()
+      : ss(nullptr)
+    {}
     explicit ScriptSourceHolder(ScriptSource* ss)
       : ss(ss)
     {
         ss->incref();
     }
     ~ScriptSourceHolder()
     {
-        ss->decref();
+        if (ss)
+            ss->decref();
+    }
+    void reset(ScriptSource* newss) {
+        if (ss)
+            ss->decref();
+        ss = newss;
+        ss->incref();
     }
     ScriptSource* get() const {
         return ss;
     }
 };
 
 struct CompressedSourceHasher
 {