Bug 1235631 - Odin: remove change-heap support (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Wed, 30 Dec 2015 12:32:47 -0600
changeset 277925 e7a9bcc4b922cfb1d8d87f299dda9ee4f492ef24
parent 277924 3119ae778211ff35d5a8c3d5bdc26589bf8a9452
child 277926 7b7cf50cb54618e49ee58e2b8e061672779b1a99
push id29838
push userkwierso@gmail.com
push dateThu, 31 Dec 2015 01:36:02 +0000
treeherdermozilla-central@22f51211915b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1235631
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1235631 - Odin: remove change-heap support (r=bbouvier)
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/asmjs/WasmSignalHandlers.cpp
js/src/asmjs/WasmStubs.cpp
js/src/asmjs/WasmStubs.h
js/src/asmjs/WasmTypes.cpp
js/src/asmjs/WasmTypes.h
js/src/builtin/AtomicsObject.cpp
js/src/jit-test/tests/asm.js/testBug1100237.js
js/src/jit-test/tests/asm.js/testNeuter.js
js/src/jit-test/tests/asm.js/testProfiling.js
js/src/jit-test/tests/asm.js/testResize.js
js/src/jit-test/tests/asm.js/testTimeout7-nosignals.js
js/src/jit-test/tests/asm.js/testTimeout7.js
js/src/jit-test/tests/basic/testArrayBufferTransfer.js
js/src/jsfun.h
js/src/tests/js1_8_5/extensions/clone-transferables.js
js/src/tests/js1_8_5/extensions/shell.js
js/src/vm/ArrayBufferObject.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -127,17 +127,17 @@ enum AsmJSSimdOperation
 // persistent state necessary to represent a compiled asm.js module.
 class js::AsmJSModule
 {
   public:
     class Global
     {
       public:
         enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
-                     AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength };
+                     AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation };
         enum VarInitKind { InitConstant, InitImport };
         enum ConstantKind { GlobalConstant, MathConstant };
 
       private:
         struct CacheablePod {
             Which which_;
             union {
                 struct {
@@ -296,31 +296,29 @@ class js::AsmJSModule
 
     typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
 
     class Export
     {
         PropertyName* name_;
         PropertyName* maybeFieldName_;
         struct CacheablePod {
-            uint32_t wasmIndex_;
             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 wasmIndex,
+        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.wasmIndex_ = wasmIndex;
             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");
@@ -333,46 +331,35 @@ class js::AsmJSModule
             return maybeFieldName_;
         }
         uint32_t startOffsetInModule() const {
             return pod.startOffsetInModule_;
         }
         uint32_t endOffsetInModule() const {
             return pod.endOffsetInModule_;
         }
-        static const uint32_t ChangeHeap = UINT32_MAX;
-        bool isChangeHeap() const {
-            return pod.wasmIndex_ == ChangeHeap;
-        }
-        uint32_t wasmIndex() const {
-            MOZ_ASSERT(!isChangeHeap());
-            return pod.wasmIndex_;
-        }
 
         WASM_DECLARE_SERIALIZABLE(Export)
     };
 
     typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
 
     typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule;
 
   private:
     UniqueWasmModule            wasmModule_;
     wasm::UniqueStaticLinkData  linkData_;
     struct CacheablePod {
         uint32_t                minHeapLength_;
-        uint32_t                maxHeapLength_;
-        uint32_t                heapLengthMask_;
         uint32_t                numFFIs_;
         uint32_t                srcLength_;
         uint32_t                srcLengthWithRightBrace_;
         bool                    strict_;
         bool                    hasArrayView_;
         bool                    isSharedView_;
-        bool                    hasFixedMinHeapLength_;
     } pod;
     const ScriptSourceHolder    scriptSource_;
     const uint32_t              srcStart_;
     const uint32_t              srcBodyStart_;
     GlobalVector                globals_;
     ImportVector                imports_;
     ExportVector                exports_;
     PropertyName*               globalArgumentName_;
@@ -386,17 +373,16 @@ class js::AsmJSModule
         srcStart_(srcStart),
         srcBodyStart_(srcBodyStart),
         globalArgumentName_(nullptr),
         importArgumentName_(nullptr),
         bufferArgumentName_(nullptr)
     {
         mozilla::PodZero(&pod);
         pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
-        pod.maxHeapLength_ = 0x80000000;
         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_);
     }
@@ -441,23 +427,16 @@ class js::AsmJSModule
         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_;
     }
-    uint32_t maxHeapLength() const {
-        return pod.maxHeapLength_;
-    }
-    uint32_t heapLengthMask() const {
-        MOZ_ASSERT(pod.hasFixedMinHeapLength_);
-        return pod.heapLengthMask_;
-    }
 
     void initGlobalArgumentName(PropertyName* n) {
         MOZ_ASSERT(!isFinished());
         MOZ_ASSERT_IF(n, n->isTenured());
         globalArgumentName_ = n;
     }
     void initImportArgumentName(PropertyName* n) {
         MOZ_ASSERT(!isFinished());
@@ -515,21 +494,16 @@ class js::AsmJSModule
     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 addByteLength() {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::ByteLength, nullptr);
-        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());
@@ -564,40 +538,26 @@ class js::AsmJSModule
         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 wasmIndex,
-                   uint32_t funcSrcBegin, uint32_t funcSrcEnd)
-    {
-        // NB: funcSrcBegin/funcSrcEnd 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).
+    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_ < funcSrcBegin);
-        MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
-        return exports_.emplaceBack(name, maybeFieldName, wasmIndex,
-                                    funcSrcBegin - srcStart_, funcSrcEnd - srcStart_);
-    }
-    bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
-        MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
-        MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
-        MOZ_ASSERT(max <= pod.maxHeapLength_);
-        MOZ_ASSERT(min <= max);
-        pod.heapLengthMask_ = mask;
-        pod.minHeapLength_ = min;
-        pod.maxHeapLength_ = max;
-        pod.hasFixedMinHeapLength_ = true;
-        return true;
+        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_;
     }
@@ -610,26 +570,21 @@ class js::AsmJSModule
             pod.isSharedView_ = true;
     }
     bool hasArrayView() const {
         return pod.hasArrayView_;
     }
     bool isSharedView() const {
         return pod.isSharedView_;
     }
-    bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
+    void requireHeapLengthToBeAtLeast(uint32_t len) {
         MOZ_ASSERT(!isFinished());
-        if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
-            return false;
-        if (len > pod.maxHeapLength_)
-            return false;
         len = RoundUpToNextValidAsmJSHeapLength(len);
         if (len > pod.minHeapLength_)
             pod.minHeapLength_ = len;
-        return true;
     }
 
     /*************************************************************************/
     // A module isFinished() when compilation completes. After being finished,
     // a module must be statically and dynamically linked before execution.
 
     bool isFinished() const {
         return !!wasmModule_;
@@ -1715,19 +1670,17 @@ class MOZ_STACK_CLASS ModuleValidator
             Function,
             FuncPtrTable,
             FFI,
             ArrayView,
             ArrayViewCtor,
             MathBuiltinFunction,
             AtomicsBuiltinFunction,
             SimdCtor,
-            SimdOperation,
-            ByteLength,
-            ChangeHeap
+            SimdOperation
         };
 
       private:
         Which which_;
         union {
             struct {
                 Type::Which type_;
                 uint32_t globalDataOffset_;
@@ -1741,20 +1694,16 @@ class MOZ_STACK_CLASS ModuleValidator
             } viewInfo;
             AsmJSMathBuiltinFunction mathBuiltinFunc_;
             AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
             AsmJSSimdType simdCtorType_;
             struct {
                 AsmJSSimdType type_;
                 AsmJSSimdOperation which_;
             } simdOp;
-            struct {
-                uint32_t srcBegin_;
-                uint32_t srcEnd_;
-            } changeHeap;
         } u;
 
         friend class ModuleValidator;
         friend class js::LifoAlloc;
 
         explicit Global(Which which) : which_(which) {}
 
       public:
@@ -1822,24 +1771,16 @@ class MOZ_STACK_CLASS ModuleValidator
         AsmJSSimdOperation simdOperation() const {
             MOZ_ASSERT(which_ == SimdOperation);
             return u.simdOp.which_;
         }
         AsmJSSimdType simdOperationType() const {
             MOZ_ASSERT(which_ == SimdOperation);
             return u.simdOp.type_;
         }
-        uint32_t changeHeapSrcBegin() const {
-            MOZ_ASSERT(which_ == ChangeHeap);
-            return u.changeHeap.srcBegin_;
-        }
-        uint32_t changeHeapSrcEnd() const {
-            MOZ_ASSERT(which_ == ChangeHeap);
-            return u.changeHeap.srcEnd_;
-        }
     };
 
     struct MathBuiltin
     {
         enum Kind { Function, Constant };
         Kind kind;
 
         union {
@@ -1926,18 +1867,16 @@ class MOZ_STACK_CLASS ModuleValidator
 
     ParseNode*           moduleFunctionNode_;
     PropertyName*        moduleFunctionName_;
 
     UniqueChars          errorString_;
     uint32_t             errorOffset_;
     bool                 errorOverRecursed_;
 
-    bool                 canValidateChangeHeap_;
-    bool                 hasChangeHeap_;
     bool                 supportsSimd_;
     bool                 atomicsPresent_;
 
   public:
     ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
       : cx_(cx),
         parser_(parser),
         mg_(cx),
@@ -1950,18 +1889,16 @@ class MOZ_STACK_CLASS ModuleValidator
         standardLibraryMathNames_(cx),
         standardLibraryAtomicsNames_(cx),
         standardLibrarySimdOpNames_(cx),
         moduleFunctionNode_(parser.pc->maybeFunction),
         moduleFunctionName_(nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false),
-        canValidateChangeHeap_(false),
-        hasChangeHeap_(false),
         supportsSimd_(cx->jitSupportsSimd()),
         atomicsPresent_(false)
     {
         MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
     }
 
     ~ModuleValidator() {
         if (errorString_) {
@@ -2084,36 +2021,39 @@ class MOZ_STACK_CLASS ModuleValidator
     }
 
     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;
 
-        auto usesHeap = Module::HeapBool(module_->hasArrayView());
-        auto sharedHeap = Module::SharedBool(module_->isSharedView());
+        HeapUsage heapUsage = module_->hasArrayView()
+                              ? module_->isSharedView()
+                                ? HeapUsage::Shared
+                                : HeapUsage::Unshared
+                              : HeapUsage::None;
+
         auto mutedErrors = Module::MutedBool(parser_.ss->mutedErrors());
 
         CacheableChars 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(usesHeap, sharedHeap, mutedErrors,
-                                  Move(filename), Move(displayURL),
+        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;
     }
 
@@ -2214,33 +2154,16 @@ class MOZ_STACK_CLASS ModuleValidator
         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);
     }
-    bool addByteLength(PropertyName* name) {
-        canValidateChangeHeap_ = true;
-        Global* global = validationLifo_.new_<Global>(Global::ByteLength);
-        return global &&
-               globals_.putNew(name, global) &&
-               module().addByteLength();
-    }
-    bool addChangeHeap(PropertyName* name, ParseNode* fn, uint32_t mask, uint32_t min, uint32_t max) {
-        hasChangeHeap_ = true;
-        Global* global = validationLifo_.new_<Global>(Global::ChangeHeap);
-        if (!global)
-            return false;
-        global->u.changeHeap.srcBegin_ = fn->pn_pos.begin;
-        global->u.changeHeap.srcEnd_ = fn->pn_pos.end;
-        return globals_.putNew(name, global) &&
-               module().addChangeHeap(mask, min, max);
-    }
     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);
     }
@@ -2254,27 +2177,18 @@ class MOZ_STACK_CLASS ModuleValidator
         global->u.ffiIndex_ = index;
         return globals_.putNew(var, global);
     }
     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());
-        uint32_t wasmIndex;
-        if (!mg_.declareExport(Move(sig), func.index(), &wasmIndex))
-            return false;
-        if (wasmIndex == AsmJSModule::Export::ChangeHeap)
-            return fail(pn, "too many exports");
-        return module().addExport(func.name(), maybeFieldName, wasmIndex,
-                                  func.srcBegin(), func.srcEnd());
-    }
-    bool addChangeHeapExport(PropertyName* name, const Global& g, PropertyName* maybeFieldName) {
-        return module().addExport(name, maybeFieldName, AsmJSModule::Export::ChangeHeap,
-                                  g.changeHeapSrcBegin(), g.changeHeapSrcEnd());
+        return mg_.declareExport(Move(sig), func.index()) &&
+               module().addExport(func.name(), maybeFieldName, func.srcBegin(), func.srcEnd());
     }
   private:
     const LifoSig* getLifoSig(const LifoSig& sig) {
         return &sig;
     }
     const LifoSig* getLifoSig(const MallocSig& sig) {
         return mg_.newLifoSig(sig);
     }
@@ -2333,29 +2247,23 @@ class MOZ_STACK_CLASS ModuleValidator
         *lifoSig = getLifoSig(sig);
         if (!*lifoSig)
             return false;
         return mg_.declareImport(Move(sig), importIndex) &&
                imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) &&
                module().addImport(ffiIndex, *importIndex);
     }
 
-    bool tryOnceToValidateChangeHeap() {
-        bool ret = canValidateChangeHeap_;
-        canValidateChangeHeap_ = false;
-        return ret;
-    }
-    bool hasChangeHeap() const {
-        return hasChangeHeap_;
-    }
-    bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
-        return module().tryRequireHeapLengthToBeAtLeast(len);
-    }
-    uint32_t minHeapLength() const {
-        return module().minHeapLength();
+    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);
+        return true;
     }
 
     bool usesSharedMemory() const {
         return atomicsPresent_;
     }
 
     // Error handling.
     bool hasAlreadyFailed() const {
@@ -2817,28 +2725,25 @@ class MOZ_STACK_CLASS FunctionValidator
     ModuleValidator&  m_;
     ParseNode*        fn_;
 
     FunctionGenerator fg_;
 
     LocalMap          locals_;
     LabelMap          labels_;
 
-    unsigned          heapExpressionDepth_;
-
     bool              hasAlreadyReturned_;
     ExprType          ret_;
 
   public:
     FunctionValidator(ModuleValidator& m, ParseNode* fn)
       : m_(m),
         fn_(fn),
         locals_(m.cx()),
         labels_(m.cx()),
-        heapExpressionDepth_(0),
         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_; }
@@ -2882,29 +2787,16 @@ class MOZ_STACK_CLASS FunctionValidator
         LocalMap::AddPtr p = locals_.lookupForAdd(name);
         if (p)
             return failName(pn, "duplicate local name '%s' not allowed", name);
         if (!locals_.add(p, name, Local(init.type(), locals_.count())))
             return false;
         return funcIR().addVariable(init.value());
     }
 
-    /*************************************************************************/
-
-    void enterHeapExpression() {
-        heapExpressionDepth_++;
-    }
-    void leaveHeapExpression() {
-        MOZ_ASSERT(heapExpressionDepth_ > 0);
-        heapExpressionDepth_--;
-    }
-    bool canCall() const {
-        return heapExpressionDepth_ == 0 || !m_.hasChangeHeap();
-    }
-
     /****************************** For consistency of returns in a function */
 
     bool hasAlreadyReturned() const {
         return hasAlreadyReturned_;
     }
 
     ExprType returnedType() const {
         return ret_;
@@ -3466,18 +3358,16 @@ CheckGlobalDotImport(ModuleValidator& m,
     if (!base->isKind(PNK_NAME))
         return m.fail(base, "expected name of variable or parameter");
 
     if (base->name() == m.module().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);
-        if (field == m.cx()->names().byteLength)
-            return m.addByteLength(varName);
 
         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);
     }
 
@@ -3778,18 +3668,16 @@ CheckVarRef(FunctionValidator& f, ParseN
           case ModuleValidator::Global::FFI:
           case ModuleValidator::Global::MathBuiltinFunction:
           case ModuleValidator::Global::AtomicsBuiltinFunction:
           case ModuleValidator::Global::FuncPtrTable:
           case ModuleValidator::Global::ArrayView:
           case ModuleValidator::Global::ArrayViewCtor:
           case ModuleValidator::Global::SimdCtor:
           case ModuleValidator::Global::SimdOperation:
-          case ModuleValidator::Global::ByteLength:
-          case ModuleValidator::Global::ChangeHeap:
             return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
         }
         return true;
     }
 
     return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
 }
 
@@ -3814,17 +3702,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().minHeapLength())
+        if (int32_t(mask2) >= 0 && mask2 < f.m().module().minHeapLength())
             *needsBoundsCheck = NO_BOUNDS_CHECK;
         *mask &= mask2;
         *indexExpr = indexNode;
         return true;
     }
 
     return false;
 }
@@ -3844,26 +3732,19 @@ CheckArrayAccess(FunctionValidator& f, P
     if (!global || !global->isAnyArrayView())
         return f.fail(viewName, "base of array access must be a typed array view name");
 
     *viewType = global->viewType();
 
     uint32_t index;
     if (IsLiteralOrConstInt(f, indexExpr, &index)) {
         uint64_t byteOffset = uint64_t(index) << TypedArrayShift(*viewType);
-        if (byteOffset > INT32_MAX)
+        if (!f.m().tryConstantAccess(byteOffset, TypedArrayElemSize(*viewType)))
             return f.fail(indexExpr, "constant index out of range");
 
-        unsigned elementSize = TypedArrayElemSize(*viewType);
-        if (!f.m().tryRequireHeapLengthToBeAtLeast(byteOffset + elementSize)) {
-            return f.failf(indexExpr, "constant index outside heap size range declared by the "
-                                      "change-heap function (0x%x - 0x%x)",
-                                      f.m().minHeapLength(), f.m().module().maxHeapLength());
-        }
-
         *mask = NoMask;
         *needsBoundsCheck = NO_BOUNDS_CHECK;
         f.writeInt32Lit(byteOffset);
         return true;
     }
 
     // Mask off the low bits to account for the clearing effect of a right shift
     // followed by the left shift implicit in the array access. E.g., H32[i>>2]
@@ -3881,47 +3762,39 @@ CheckArrayAccess(FunctionValidator& f, P
         if (shift != requiredShift)
             return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift);
 
         ParseNode* pointerNode = BitwiseLeft(indexExpr);
 
         if (pointerNode->isKind(PNK_BITAND))
             FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck);
 
-        f.enterHeapExpression();
-
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerType))
             return false;
 
-        f.leaveHeapExpression();
-
         if (!pointerType.isIntish())
             return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
     } else {
         // For legacy compatibility, accept Int8/Uint8 accesses with no shift.
         if (TypedArrayShift(*viewType) != 0)
             return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
 
         MOZ_ASSERT(*mask == NoMask);
         bool folded = false;
 
         ParseNode* pointerNode = indexExpr;
 
         if (pointerNode->isKind(PNK_BITAND))
             folded = FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck);
 
-        f.enterHeapExpression();
-
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerType))
             return false;
 
-        f.leaveHeapExpression();
-
         if (folded) {
             if (!pointerType.isIntish())
                 return f.failf(pointerNode, "%s is not a subtype of intish", pointerType.toChars());
         } else {
             if (!pointerType.isInt())
                 return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
         }
     }
@@ -4005,24 +3878,20 @@ CheckStoreArray(FunctionValidator& f, Pa
     size_t needsBoundsCheckAt = f.tempU8();
 
     Scalar::Type viewType;
     NeedsBoundsCheck needsBoundsCheck;
     int32_t mask;
     if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType, &needsBoundsCheck, &mask))
         return false;
 
-    f.enterHeapExpression();
-
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsType))
         return false;
 
-    f.leaveHeapExpression();
-
     switch (viewType) {
       case Scalar::Int8:
       case Scalar::Int16:
       case Scalar::Int32:
       case Scalar::Uint8:
       case Scalar::Uint16:
       case Scalar::Uint32:
         if (!rhsType.isIntish())
@@ -4652,21 +4521,16 @@ WriteCallLineCol(FunctionValidator& f, P
     f.writeU32(line);
     f.writeU32(column);
 }
 
 static bool
 CheckInternalCall(FunctionValidator& f, ParseNode* callNode, PropertyName* calleeName,
                   ExprType ret, Type* type)
 {
-    if (!f.canCall()) {
-        return f.fail(callNode, "call expressions may not be nested inside heap expressions "
-                                "when the module contains a change-heap function");
-    }
-
     switch (ret) {
       case ExprType::Void:   f.writeOp(Stmt::CallInternal);  break;
       case ExprType::I32:    f.writeOp(I32::CallInternal);   break;
       case ExprType::I64:    MOZ_CRASH("no int64 in asm.js");
       case ExprType::F32:    f.writeOp(F32::CallInternal);   break;
       case ExprType::F64:    f.writeOp(F64::CallInternal);   break;
       case ExprType::I32x4:  f.writeOp(I32X4::CallInternal); break;
       case ExprType::F32x4:  f.writeOp(F32X4::CallInternal); break;
@@ -4723,21 +4587,16 @@ CheckFuncPtrTableAgainstExisting(ModuleV
         return m.fail(usepn, "table too big");
 
     return true;
 }
 
 static bool
 CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type* type)
 {
-    if (!f.canCall()) {
-        return f.fail(callNode, "function-pointer call expressions may not be nested inside heap "
-                                "expressions when the module contains a change-heap function");
-    }
-
     ParseNode* callee = CallCallee(callNode);
     ParseNode* tableNode = ElemBase(callee);
     ParseNode* indexExpr = ElemIndex(callee);
 
     if (!tableNode->isKind(PNK_NAME))
         return f.fail(tableNode, "expecting name of function-pointer array");
 
     PropertyName* name = tableNode->name();
@@ -4808,21 +4667,16 @@ CheckIsExternType(FunctionValidator& f, 
         return f.failf(argNode, "%s is not a subtype of extern", type.toChars());
     return true;
 }
 
 static bool
 CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprType ret,
              Type* type)
 {
-    if (!f.canCall()) {
-        return f.fail(callNode, "FFI call expressions may not be nested inside heap "
-                                "expressions when the module contains a change-heap function");
-    }
-
     PropertyName* calleeName = CallCallee(callNode)->name();
 
     if (ret == ExprType::F32)
         return f.fail(callNode, "FFI calls can't return float");
     if (IsSimdType(ret))
         return f.fail(callNode, "FFI calls can't return SIMD values");
 
     switch (ret) {
@@ -5499,40 +5353,31 @@ CheckSimdLoadStoreArgs(FunctionValidator
       case AsmJSSimdType_int32x4:   *viewType = Scalar::Int32x4;   break;
       case AsmJSSimdType_float32x4: *viewType = Scalar::Float32x4; break;
       case AsmJSSimdType_bool32x4:  MOZ_CRASH("Cannot load/store boolean SIMD type");
     }
 
     ParseNode* indexExpr = NextNode(view);
     uint32_t indexLit;
     if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) {
-        if (indexLit > INT32_MAX)
+        if (!f.m().tryConstantAccess(indexLit, Simd128DataSize))
             return f.fail(indexExpr, "constant index out of range");
 
-        if (!f.m().tryRequireHeapLengthToBeAtLeast(indexLit + Simd128DataSize)) {
-            return f.failf(indexExpr, "constant index outside heap size range declared by the "
-                                      "change-heap function (0x%x - 0x%x)",
-                                      f.m().minHeapLength(), f.m().module().maxHeapLength());
-        }
-
         *needsBoundsCheck = NO_BOUNDS_CHECK;
         f.writeInt32Lit(indexLit);
         return true;
     }
 
-    f.enterHeapExpression();
-
     Type indexType;
     if (!CheckExpr(f, indexExpr, &indexType))
         return false;
+
     if (!indexType.isIntish())
         return f.failf(indexExpr, "%s is not a subtype of intish", indexType.toChars());
 
-    f.leaveHeapExpression();
-
     return true;
 }
 
 static bool
 CheckSimdLoad(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
               unsigned numElems, Type* type)
 {
     unsigned numArgs = CallArgListLength(call);
@@ -5948,18 +5793,16 @@ CheckCoercedCall(FunctionValidator& f, P
           case ModuleValidator::Global::AtomicsBuiltinFunction:
             return CheckCoercedAtomicsBuiltinCall(f, call, global->atomicsBuiltinFunction(), ret, type);
           case ModuleValidator::Global::ConstantLiteral:
           case ModuleValidator::Global::ConstantImport:
           case ModuleValidator::Global::Variable:
           case ModuleValidator::Global::FuncPtrTable:
           case ModuleValidator::Global::ArrayView:
           case ModuleValidator::Global::ArrayViewCtor:
-          case ModuleValidator::Global::ByteLength:
-          case ModuleValidator::Global::ChangeHeap:
             return f.failName(callee, "'%s' is not callable function", callee->name());
           case ModuleValidator::Global::SimdCtor:
           case ModuleValidator::Global::SimdOperation:
             return CheckCoercedSimdCall(f, call, global, ret, type);
           case ModuleValidator::Global::Function:
             break;
         }
     }
@@ -7016,235 +6859,16 @@ CheckStatement(FunctionValidator& f, Par
                                                           Stmt::Continue, Stmt::ContinueLabel);
       default:;
     }
 
     return f.fail(stmt, "unexpected statement kind");
 }
 
 static bool
-CheckByteLengthCall(ModuleValidator& m, ParseNode* pn, PropertyName* newBufferName)
-{
-    if (!pn->isKind(PNK_CALL) || !CallCallee(pn)->isKind(PNK_NAME))
-        return m.fail(pn, "expecting call to imported byteLength");
-
-    const ModuleValidator::Global* global = m.lookupGlobal(CallCallee(pn)->name());
-    if (!global || global->which() != ModuleValidator::Global::ByteLength)
-        return m.fail(pn, "expecting call to imported byteLength");
-
-    if (CallArgListLength(pn) != 1 || !IsUseOfName(CallArgList(pn), newBufferName))
-        return m.failName(pn, "expecting %s as argument to byteLength call", newBufferName);
-
-    return true;
-}
-
-static bool
-CheckHeapLengthCondition(ModuleValidator& m, ParseNode* cond, PropertyName* newBufferName,
-                         uint32_t* mask, uint32_t* minLength, uint32_t* maxLength)
-{
-    if (!cond->isKind(PNK_OR) || !AndOrLeft(cond)->isKind(PNK_OR))
-        return m.fail(cond, "expecting byteLength & K || byteLength <= L || byteLength > M");
-
-    ParseNode* cond1 = AndOrLeft(AndOrLeft(cond));
-    ParseNode* cond2 = AndOrRight(AndOrLeft(cond));
-    ParseNode* cond3 = AndOrRight(cond);
-
-    if (!cond1->isKind(PNK_BITAND))
-        return m.fail(cond1, "expecting byteLength & K");
-
-    if (!CheckByteLengthCall(m, BitwiseLeft(cond1), newBufferName))
-        return false;
-
-    ParseNode* maskNode = BitwiseRight(cond1);
-    if (!IsLiteralInt(m, maskNode, mask))
-        return m.fail(maskNode, "expecting integer literal mask");
-    if (*mask == UINT32_MAX)
-        return m.fail(maskNode, "invalid mask value");
-    if ((*mask & 0xffffff) != 0xffffff)
-        return m.fail(maskNode, "mask value must have the bits 0xffffff set");
-
-    if (!cond2->isKind(PNK_LE))
-        return m.fail(cond2, "expecting byteLength <= L");
-
-    if (!CheckByteLengthCall(m, RelationalLeft(cond2), newBufferName))
-        return false;
-
-    ParseNode* minLengthNode = RelationalRight(cond2);
-    uint32_t minLengthExclusive;
-    if (!IsLiteralInt(m, minLengthNode, &minLengthExclusive))
-        return m.fail(minLengthNode, "expecting integer literal");
-    if (minLengthExclusive < 0xffffff || minLengthExclusive == UINT32_MAX)
-        return m.fail(minLengthNode, "literal must be >= 0xffffff and < 0xffffffff");
-
-    // Add one to convert from exclusive (the branch rejects if ==) to inclusive.
-    *minLength = minLengthExclusive + 1;
-
-    if (!cond3->isKind(PNK_GT))
-        return m.fail(cond3, "expecting byteLength > M");
-
-    if (!CheckByteLengthCall(m, RelationalLeft(cond3), newBufferName))
-        return false;
-
-    ParseNode* maxLengthNode = RelationalRight(cond3);
-    if (!IsLiteralInt(m, maxLengthNode, maxLength))
-        return m.fail(maxLengthNode, "expecting integer literal");
-    if (*maxLength > 0x80000000)
-        return m.fail(maxLengthNode, "literal must be <= 0x80000000");
-
-    if (*maxLength < *minLength)
-        return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
-
-    return true;
-}
-
-static bool
-CheckReturnBoolLiteral(ModuleValidator& m, ParseNode* stmt, bool retval)
-{
-    if (stmt->isKind(PNK_STATEMENTLIST)) {
-        ParseNode* next = SkipEmptyStatements(ListHead(stmt));
-        if (!next)
-            return m.fail(stmt, "expected return statement");
-        stmt = next;
-        if (NextNonEmptyStatement(stmt))
-            return m.fail(stmt, "expected single return statement");
-    }
-
-    if (!stmt->isKind(PNK_RETURN))
-        return m.fail(stmt, "expected return statement");
-
-    ParseNode* returnExpr = ReturnExpr(stmt);
-    if (!returnExpr || !returnExpr->isKind(retval ? PNK_TRUE : PNK_FALSE))
-        return m.failf(stmt, "expected 'return %s;'", retval ? "true" : "false");
-
-    return true;
-}
-
-static bool
-CheckReassignmentTo(ModuleValidator& m, ParseNode* stmt, PropertyName* lhsName, ParseNode** rhs)
-{
-    if (!stmt->isKind(PNK_SEMI))
-        return m.fail(stmt, "missing reassignment");
-
-    ParseNode* assign = UnaryKid(stmt);
-    if (!assign || !assign->isKind(PNK_ASSIGN))
-        return m.fail(stmt, "missing reassignment");
-
-    ParseNode* lhs = BinaryLeft(assign);
-    if (!IsUseOfName(lhs, lhsName))
-        return m.failName(lhs, "expecting reassignment of %s", lhsName);
-
-    *rhs = BinaryRight(assign);
-    return true;
-}
-
-static bool
-CheckChangeHeap(ModuleValidator& m, ParseNode* fn, bool* validated)
-{
-    MOZ_ASSERT(fn->isKind(PNK_FUNCTION));
-
-    // We don't yet know whether this is a change-heap function.
-    // The point at which we know we have a change-heap function is once we see
-    // whether the argument is coerced according to the normal asm.js rules. If
-    // it is coerced, it's not change-heap and must validate according to normal
-    // rules; otherwise it must validate as a change-heap function.
-    *validated = false;
-
-    PropertyName* changeHeapName = FunctionName(fn);
-    if (!CheckModuleLevelName(m, fn, changeHeapName))
-        return false;
-
-    unsigned numFormals;
-    ParseNode* arg = FunctionArgsList(fn, &numFormals);
-    if (numFormals != 1)
-        return true;
-
-    PropertyName* newBufferName;
-    if (!CheckArgument(m, arg, &newBufferName))
-        return false;
-
-    ParseNode* stmtIter = SkipEmptyStatements(ListHead(FunctionStatementList(fn)));
-    if (!stmtIter || !stmtIter->isKind(PNK_IF))
-        return true;
-
-    // We can now issue validation failures if we see something that isn't a
-    // valid change-heap function.
-    *validated = true;
-
-    PropertyName* bufferName = m.module().bufferArgumentName();
-    if (!bufferName)
-        return m.fail(fn, "to change heaps, the module must have a buffer argument");
-
-    ParseNode* cond = TernaryKid1(stmtIter);
-    ParseNode* thenStmt = TernaryKid2(stmtIter);
-    if (ParseNode* elseStmt = TernaryKid3(stmtIter))
-        return m.fail(elseStmt, "unexpected else statement");
-
-    uint32_t mask, min = 0, max;  // initialize min to silence GCC warning
-    if (!CheckHeapLengthCondition(m, cond, newBufferName, &mask, &min, &max))
-        return false;
-
-    if (!CheckReturnBoolLiteral(m, thenStmt, false))
-        return false;
-
-    ParseNode* next = NextNonEmptyStatement(stmtIter);
-
-    for (unsigned i = 0; i < m.numArrayViews(); i++, next = NextNonEmptyStatement(stmtIter)) {
-        if (!next)
-            return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
-        stmtIter = next;
-
-        const ModuleValidator::ArrayView& view = m.arrayView(i);
-
-        ParseNode* rhs;
-        if (!CheckReassignmentTo(m, stmtIter, view.name, &rhs))
-            return false;
-
-        if (!rhs->isKind(PNK_NEW))
-            return m.failName(rhs, "expecting assignment of new array view to %s", view.name);
-
-        ParseNode* ctorExpr = ListHead(rhs);
-        if (!ctorExpr->isKind(PNK_NAME))
-            return m.fail(rhs, "expecting name of imported typed array constructor");
-
-        const ModuleValidator::Global* global = m.lookupGlobal(ctorExpr->name());
-        if (!global || global->which() != ModuleValidator::Global::ArrayViewCtor)
-            return m.fail(rhs, "expecting name of imported typed array constructor");
-        if (global->viewType() != view.type)
-            return m.fail(rhs, "can't change the type of a global view variable");
-
-        if (!CheckNewArrayViewArgs(m, ctorExpr, newBufferName))
-            return false;
-    }
-
-    if (!next)
-        return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
-    stmtIter = next;
-
-    ParseNode* rhs;
-    if (!CheckReassignmentTo(m, stmtIter, bufferName, &rhs))
-        return false;
-    if (!IsUseOfName(rhs, newBufferName))
-        return m.failName(stmtIter, "expecting assignment of new buffer to %s", bufferName);
-
-    next = NextNonEmptyStatement(stmtIter);
-    if (!next)
-        return m.failOffset(stmtIter->pn_pos.end, "expected return statement");
-    stmtIter = next;
-
-    if (!CheckReturnBoolLiteral(m, stmtIter, true))
-        return false;
-
-    stmtIter = NextNonEmptyStatement(stmtIter);
-    if (stmtIter)
-        return m.fail(stmtIter, "expecting end of function");
-
-    return m.addChangeHeap(changeHeapName, fn, mask, min, max);
-}
-
-static bool
 ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line, unsigned* column)
 {
     TokenStream& tokenStream = m.tokenStream();
 
     tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
     tokenStream.srcCoords.lineNumAndColumnIndex(tokenStream.currentToken().pos.end, line, column);
 
     RootedPropertyName name(m.cx());
@@ -7314,24 +6938,16 @@ CheckFunction(ModuleValidator& m)
     ParseNode* fn = nullptr;
     unsigned line = 0, column = 0;
     if (!ParseFunction(m, &fn, &line, &column))
         return false;
 
     if (!CheckFunctionHead(m, fn))
         return false;
 
-    if (m.tryOnceToValidateChangeHeap()) {
-        bool validated;
-        if (!CheckChangeHeap(m, fn, &validated))
-            return false;
-        if (validated)
-            return true;
-    }
-
     FunctionValidator f(m, fn);
     if (!f.init(FunctionName(fn), line, column))
         return m.fail(fn, "internal compiler failure (probably out of memory)");
 
     ParseNode* stmtIter = ListHead(FunctionStatementList(fn));
 
     if (!CheckProcessingDirectives(m, &stmtIter))
         return false;
@@ -7488,23 +7104,20 @@ CheckModuleExportFunction(ModuleValidato
     if (!pn->isKind(PNK_NAME))
         return m.fail(pn, "expected name of exported function");
 
     PropertyName* funcName = pn->name();
     const ModuleValidator::Global* global = m.lookupGlobal(funcName);
     if (!global)
         return m.failName(pn, "exported function name '%s' not found", funcName);
 
-    if (global->which() == ModuleValidator::Global::Function)
-        return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName);
-
-    if (global->which() == ModuleValidator::Global::ChangeHeap)
-        return m.addChangeHeapExport(funcName, *global, maybeFieldName);
-
-    return m.failName(pn, "'%s' is not a function", funcName);
+    if (global->which() != ModuleValidator::Global::Function)
+        return m.failName(pn, "'%s' is not a function", funcName);
+
+    return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName);
 }
 
 static bool
 CheckModuleExportObject(ModuleValidator& m, ParseNode* object)
 {
     MOZ_ASSERT(object->isKind(PNK_OBJECT));
 
     for (ParseNode* pn = ListHead(object); pn; pn = NextNode(pn)) {
@@ -7631,86 +7244,45 @@ CheckModule(ExclusiveContext* cx, AsmJSP
     if (!m.finish(slowFuncs))
         return false;
 
     *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
     return true;
 }
 
 /*****************************************************************************/
-// Runtime calls to asm.js module exports
+// Link-time validation
 
 static AsmJSModuleObject&
 FunctionToModuleObject(JSFunction* fun)
 {
     MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun));
-    const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_MODULE_SLOT);
+    const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_MODULE_SLOT);
     return v.toObject().as<AsmJSModuleObject>();
 }
 
 static unsigned
 FunctionToExportIndex(JSFunction* fun)
 {
     MOZ_ASSERT(IsAsmJSFunction(fun));
-    const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT);
+    const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT);
     return v.toInt32();
 }
 
 static bool
-ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args)
-{
-    HandleValue bufferArg = args.get(0);
-    if (!IsArrayBuffer(bufferArg)) {
-        ReportIncompatible(cx, args);
-        return false;
-    }
-
-    Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
-    uint32_t heapLength = newBuffer->byteLength();
-    if (heapLength & module.heapLengthMask() ||
-        heapLength < module.minHeapLength() ||
-        heapLength > module.maxHeapLength())
-    {
-        args.rval().set(BooleanValue(false));
-        return true;
-    }
-
-    if (!module.hasArrayView()) {
-        args.rval().set(BooleanValue(true));
-        return true;
-    }
-
-    MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
-
-    bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB;
-    if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers))
-        return false;
-
-    args.rval().set(BooleanValue(module.wasmModule().changeHeap(newBuffer, cx)));
-    return true;
-}
-
-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();
-    const AsmJSModule::Export& exp = module.exports()[FunctionToExportIndex(callee)];
-
-    // The heap-changing function is a special-case and is implemented by C++.
-    if (exp.isChangeHeap())
-        return ChangeHeap(cx, module, args);
-
-    return module.wasmModule().callExport(cx, exp.wasmIndex(), args);
-}
-
-/*****************************************************************************/
-// Link-time validation
+    uint32_t exportIndex = FunctionToExportIndex(callee);
+
+    return module.wasmModule().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;
 }
@@ -7881,40 +7453,16 @@ ValidateArrayView(JSContext* cx, const A
     bool tac = IsTypedArrayConstructor(v, global.viewType());
     if (!tac)
         return LinkFail(cx, "bad typed array constructor");
 
     return true;
 }
 
 static bool
-ValidateByteLength(JSContext* cx, HandleValue globalVal)
-{
-    RootedPropertyName field(cx, cx->names().byteLength);
-    RootedValue v(cx);
-    if (!GetDataProperty(cx, globalVal, field, &v))
-        return false;
-
-    if (!v.isObject() || !v.toObject().isBoundFunction())
-        return LinkFail(cx, "byteLength must be a bound function object");
-
-    RootedFunction fun(cx, &v.toObject().as<JSFunction>());
-
-    RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget()));
-    if (!IsNativeFunction(boundTarget, fun_call))
-        return LinkFail(cx, "bound target of byteLength must be Function.prototype.call");
-
-    RootedValue boundThis(cx, fun->getBoundFunctionThis());
-    if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter))
-        return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor");
-
-    return true;
-}
-
-static bool
 ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& 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))
@@ -8146,30 +7694,22 @@ CheckBuffer(JSContext* cx, AsmJSModule& 
     }
 
     // This check is sufficient without considering the size of the loaded datum because heap
     // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
     MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
     if (heapLength < module.minHeapLength()) {
         UniqueChars msg(
             JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
-                        "by const heap accesses and/or change-heap minimum-length requirements).",
+                        "by const heap accesses).",
                         heapLength,
                         module.minHeapLength()));
         return LinkFail(cx, msg.get());
     }
 
-    if (heapLength > module.maxHeapLength()) {
-        UniqueChars msg(
-            JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
-                        heapLength,
-                        module.maxHeapLength()));
-        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))
         return LinkFail(cx, "Signals have been toggled since compilation");
 
     if (buffer->is<ArrayBufferObject>()) {
         Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
@@ -8206,20 +7746,16 @@ DynamicallyLinkModule(JSContext* cx, con
             if (!ValidateFFI(cx, global, importVal, &ffis))
                 return false;
             break;
           case AsmJSModule::Global::ArrayView:
           case AsmJSModule::Global::ArrayViewCtor:
             if (!ValidateArrayView(cx, global, globalVal))
                 return false;
             break;
-          case AsmJSModule::Global::ByteLength:
-            if (!ValidateByteLength(cx, globalVal))
-                return false;
-            break;
           case AsmJSModule::Global::MathBuiltinFunction:
             if (!ValidateMathBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
           case AsmJSModule::Global::AtomicsBuiltinFunction:
             if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
@@ -8246,30 +7782,28 @@ DynamicallyLinkModule(JSContext* cx, con
 
     return module.wasmModule().dynamicallyLink(cx, buffer, imports);
 }
 
 static JSFunction*
 NewExportedFunction(JSContext* cx, const AsmJSModule& module, const AsmJSModule::Export& func,
                     HandleObject moduleObj, unsigned exportIndex)
 {
-    unsigned numArgs = func.isChangeHeap()
-                       ? 1
-                       : module.wasmModule().exports()[func.wasmIndex()].sig().args().length();
+    unsigned numArgs = module.wasmModule().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;
 
-    fun->setExtendedSlot(FunctionExtended::ASM_MODULE_SLOT, ObjectValue(*moduleObj));
-    fun->setExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
+    fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
+    fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
     return fun;
 }
 
 static bool
 HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& module,
                          HandlePropertyName name)
 {
     if (cx->isExceptionPending())
@@ -8429,17 +7963,17 @@ NewModuleFunction(ExclusiveContext* cx, 
                                                   : JSFunction::ASMJS_CTOR;
     JSFunction* moduleFun =
         NewNativeConstructor(cx, LinkAsmJS, origFun->nargs(), name,
                              gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
                              flags);
     if (!moduleFun)
         return nullptr;
 
-    moduleFun->setExtendedSlot(FunctionExtended::ASM_MODULE_SLOT, ObjectValue(*moduleObj));
+    moduleFun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
     return moduleFun;
 }
 
 /*****************************************************************************/
 // Caching and cloning
 
 uint8_t*
 AsmJSModule::Global::serialize(uint8_t* cursor) const
@@ -9385,19 +8919,8 @@ js::RoundUpToNextValidAsmJSHeapLength(ui
         return MinHeapLength;
 
     if (length <= 16 * 1024 * 1024)
         return mozilla::RoundUpPow2(length);
 
     MOZ_ASSERT(length <= 0xff000000);
     return (length + 0x00ffffff) & ~0x00ffffff;
 }
-
-bool
-js::OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer)
-{
-    for (Module* m = cx->runtime()->linkedWasmModules; m; m = m->nextLinked()) {
-        if (buffer == m->maybeBuffer() && !m->detachHeap(cx))
-            return false;
-    }
-    return true;
-}
-
--- a/js/src/asmjs/AsmJS.h
+++ b/js/src/asmjs/AsmJS.h
@@ -104,19 +104,16 @@ AsmJSModuleToString(JSContext* cx, Handl
 // asm.js heap:
 
 extern bool
 IsValidAsmJSHeapLength(uint32_t length);
 
 extern uint32_t
 RoundUpToNextValidAsmJSHeapLength(uint32_t length);
 
-extern bool
-OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer);
-
 // The assumed page size; dynamically checked in CompileAsmJS.
 #ifdef _MIPS_ARCH_LOONGSON3A
 static const size_t AsmJSPageSize = 16384;
 #else
 static const size_t AsmJSPageSize = 4096;
 #endif
 
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -307,19 +307,18 @@ ModuleGenerator::defineImport(uint32_t i
     Import& import = imports_[index];
     import.initInterpExitOffset(interpExit.begin);
     import.initJitExitOffset(jitExit.begin);
     return codeRanges_.emplaceBack(CodeRange::ImportInterpExit, interpExit) &&
            codeRanges_.emplaceBack(CodeRange::ImportJitExit, jitExit);
 }
 
 bool
-ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* index)
+ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex)
 {
-    *index = exports_.length();
     return exports_.emplaceBack(Move(sig), funcIndex);
 }
 
 uint32_t
 ModuleGenerator::exportFuncIndex(uint32_t index) const
 {
     return exports_[index].funcIndex();
 }
@@ -494,28 +493,27 @@ bool
 ModuleGenerator::defineOutOfBoundsStub(Offsets offsets)
 {
     MOZ_ASSERT(finishedFuncs_);
     staticLinkData_->pod.outOfBoundsOffset = offsets.begin;
     return codeRanges_.emplaceBack(CodeRange::Inline, offsets);
 }
 
 Module*
-ModuleGenerator::finish(Module::HeapBool usesHeap,
-                        Module::SharedBool sharedHeap,
+ModuleGenerator::finish(HeapUsage heapUsage,
                         Module::MutedBool mutedErrors,
                         CacheableChars filename,
                         CacheableTwoByteChars displayURL,
                         UniqueStaticLinkData* staticLinkData,
                         SlowFunctionVector* slowFuncs)
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(finishedFuncs_);
 
-    if (!GenerateStubs(*this, usesHeap))
+    if (!GenerateStubs(*this, UsesHeap(heapUsage)))
         return nullptr;
 
     masm_.finish();
     if (masm_.oom())
         return nullptr;
 
     // Start global data on a new page so JIT code may be given independent
     // protection flags. Note assumption that global data starts right after
@@ -611,18 +609,17 @@ ModuleGenerator::finish(Module::HeapBool
 #endif
 
     *staticLinkData = Move(staticLinkData_);
     *slowFuncs = Move(slowFuncs_);
     return cx_->new_<Module>(args_,
                              funcBytes_,
                              codeBytes,
                              globalBytes_,
-                             usesHeap,
-                             sharedHeap,
+                             heapUsage,
                              mutedErrors,
                              Move(code),
                              Move(imports_),
                              Move(exports_),
                              masm_.extractHeapAccesses(),
                              Move(codeRanges_),
                              Move(callSites),
                              Move(funcNames_),
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -118,17 +118,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     // Imports:
     bool declareImport(MallocSig&& sig, uint32_t* index);
     uint32_t numDeclaredImports() const;
     uint32_t importExitGlobalDataOffset(uint32_t index) const;
     const MallocSig& importSig(uint32_t index) const;
     bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
 
     // Exports:
-    bool declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* index);
+    bool declareExport(MallocSig&& sig, uint32_t funcIndex);
     uint32_t numDeclaredExports() const;
     uint32_t exportFuncIndex(uint32_t index) const;
     const MallocSig& exportSig(uint32_t index) const;
     bool defineExport(uint32_t index, Offsets offsets);
 
     // Functions:
     bool startFunc(PropertyName* name, unsigned line, unsigned column, FunctionGenerator* fg);
     bool finishFunc(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime, FunctionGenerator* fg);
@@ -142,18 +142,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     // 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(Module::HeapBool usesHeap,
-                   Module::SharedBool sharedHeap,
+    Module* finish(HeapUsage heapUsage,
                    Module::MutedBool mutedErrors,
                    CacheableChars filename,
                    CacheableTwoByteChars displayURL,
                    UniqueStaticLinkData* staticLinkData,
                    SlowFunctionVector* slowFuncs);
 };
 
 // A FunctionGenerator encapsulates the generation of a single function body.
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -506,18 +506,19 @@ Module::activation()
 {
     MOZ_ASSERT(dynamicallyLinked_);
     return *reinterpret_cast<WasmActivation**>(globalData() + ActivationGlobalDataOffset);
 }
 
 void
 Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
 {
+    MOZ_ASSERT(usesHeap());
     MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isAsmJS());
-    MOZ_ASSERT(!maybeHeap_);
+    MOZ_ASSERT(!heap_);
     MOZ_ASSERT(!rawHeapPtr());
 
     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
@@ -545,24 +546,27 @@ Module::specializeToHeap(ArrayBufferObje
             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_)
         Assembler::UpdateBoundsCheck(heapLength, (Instruction*)(access.insnOffset() + code()));
 #endif
 
-    maybeHeap_ = heap;
+    heap_ = heap;
     rawHeapPtr() = ptrBase;
 }
 
 void
 Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap)
 {
-    MOZ_ASSERT_IF(maybeHeap_, maybeHeap_ == heap);
+    // heap_/rawHeapPtr can be null if this module holds cloned code from
+    // 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];
         if (access.hasLengthCheck())
@@ -576,17 +580,17 @@ Module::despecializeFromHeap(ArrayBuffer
     uint32_t heapLength = heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         const HeapAccess& access = heapAccesses_[i];
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength);
     }
 #endif
 
-    maybeHeap_ = nullptr;
+    heap_ = nullptr;
     rawHeapPtr() = nullptr;
 }
 
 void
 Module::sendCodeRangesToProfiler(JSContext* cx)
 {
 #ifdef JS_ION_PERF
     if (PerfFuncEnabled()) {
@@ -695,30 +699,27 @@ Module::ImportExit&
 Module::importToExit(const Import& import)
 {
     return *reinterpret_cast<ImportExit*>(globalData() + import.exitGlobalDataOffset());
 }
 
 /* static */ Module::CacheablePod
 Module::zeroPod()
 {
-    CacheablePod pod = {0, 0, 0, false, false, false, false, false};
+    CacheablePod pod = {0, 0, 0, HeapUsage::None, false, false, false};
     return pod;
 }
 
 void
 Module::init()
 {
    staticallyLinked_ = false;
    interrupt_ = nullptr;
    outOfBounds_ = nullptr;
    dynamicallyLinked_ = false;
-   prev_ = nullptr;
-   next_ = nullptr;
-   interrupted_ = false;
 
     *(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN();
     *(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN();
 }
 
 // Private constructor used for deserialization and cloning.
 Module::Module(const CacheablePod& pod,
                UniqueCodePtr code,
@@ -752,18 +753,17 @@ Module::Module(const CacheablePod& pod,
     init();
 }
 
 // Public constructor for compilation.
 Module::Module(CompileArgs args,
                uint32_t functionBytes,
                uint32_t codeBytes,
                uint32_t globalBytes,
-               HeapBool usesHeap,
-               SharedBool sharedHeap,
+               HeapUsage heapUsage,
                MutedBool mutedErrors,
                UniqueCodePtr code,
                ImportVector&& imports,
                ExportVector&& exports,
                HeapAccessVector&& heapAccesses,
                CodeRangeVector&& codeRanges,
                CallSiteVector&& callSites,
                CacheableCharsVector&& funcNames,
@@ -781,54 +781,45 @@ Module::Module(CompileArgs args,
     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<bool&>(pod.usesHeap_) = bool(usesHeap);
-    const_cast<bool&>(pod.sharedHeap_) = bool(sharedHeap);
+    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;
 
-    MOZ_ASSERT_IF(sharedHeap, usesHeap);
     init();
 }
 
 Module::~Module()
 {
-    MOZ_ASSERT(!interrupted_);
-
     if (code_) {
         for (unsigned i = 0; i < imports_.length(); i++) {
             ImportExit& exit = importToExit(imports_[i]);
             if (exit.baselineScript)
                 exit.baselineScript->removeDependentWasmModule(*this, i);
         }
     }
-
-    if (prev_)
-        *prev_ = next_;
-    if (next_)
-        next_->prev_ = prev_;
 }
 
 void
 Module::trace(JSTracer* trc)
 {
     for (const Import& import : imports_) {
         if (importToExit(import).fun)
             TraceEdge(trc, &importToExit(import).fun, "wasm function import");
     }
 
-    if (maybeHeap_)
-        TraceEdge(trc, &maybeHeap_, "wasm buffer");
+    if (heap_)
+        TraceEdge(trc, &heap_, "wasm buffer");
 }
 
 CompileArgs
 Module::compileArgs() const
 {
     CompileArgs args;
     args.useSignalHandlersForOOB = pod.usesSignalHandlersForOOB_;
     args.useSignalHandlersForInterrupt = pod.usesSignalHandlersForInterrupt_;
@@ -977,23 +968,16 @@ Module::staticallyLink(ExclusiveContext*
 bool
 Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
                         const AutoVectorRooter<JSFunction*>& imports)
 {
     MOZ_ASSERT(staticallyLinked_);
     MOZ_ASSERT(!dynamicallyLinked_);
     dynamicallyLinked_ = true;
 
-    // Add this module to the JSRuntime-wide list of dynamically-linked modules.
-    next_ = cx->runtime()->linkedWasmModules;
-    prev_ = &cx->runtime()->linkedWasmModules;
-    cx->runtime()->linkedWasmModules = this;
-    if (next_)
-        next_->prev_ = &next_;
-
     // 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_);
 
     // Initialize imports with actual imported values.
@@ -1002,120 +986,56 @@ Module::dynamicallyLink(JSContext* cx, H
         const Import& import = imports_[i];
         ImportExit& exit = importToExit(import);
         exit.code = code() + import.interpExitCodeOffset();
         exit.fun = imports[i];
         exit.baselineScript = nullptr;
     }
 
     // Specialize code to the actual heap.
-    if (heap)
+    if (usesHeap())
         specializeToHeap(heap);
 
     // See AllocateCode comment above.
     ExecutableAllocator::makeExecutable(code(), pod.codeBytes_);
 
     sendCodeRangesToProfiler(cx);
     return true;
 }
 
-ArrayBufferObjectMaybeShared*
-Module::maybeBuffer() const
+SharedMem<uint8_t*>
+Module::heap() const
 {
     MOZ_ASSERT(dynamicallyLinked_);
-    return maybeHeap_;
-}
-
-SharedMem<uint8_t*>
-Module::maybeHeap() const
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    MOZ_ASSERT_IF(!pod.usesHeap_, rawHeapPtr() == nullptr);
-    return pod.sharedHeap_
+    MOZ_ASSERT(usesHeap());
+    MOZ_ASSERT(rawHeapPtr());
+    return hasSharedHeap()
            ? SharedMem<uint8_t*>::shared(rawHeapPtr())
            : SharedMem<uint8_t*>::unshared(rawHeapPtr());
 }
 
 size_t
 Module::heapLength() const
 {
     MOZ_ASSERT(dynamicallyLinked_);
-    return maybeHeap_ ? maybeHeap_->byteLength() : 0;
+    MOZ_ASSERT(usesHeap());
+    return heap_->byteLength();
 }
 
 void
 Module::deoptimizeImportExit(uint32_t importIndex)
 {
     MOZ_ASSERT(dynamicallyLinked_);
     const Import& import = imports_[importIndex];
     ImportExit& exit = importToExit(import);
     exit.code = code() + import.interpExitCodeOffset();
     exit.baselineScript = nullptr;
 }
 
 bool
-Module::changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext* cx)
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    MOZ_ASSERT(pod.usesHeap_);
-
-    // Content JS should not be able to run (and change heap) from within an
-    // interrupt callback, but in case it does, fail to change heap. Otherwise,
-    // the heap can change at every single instruction which would prevent
-    // future optimizations like heap-base hoisting.
-    if (interrupted_)
-        return false;
-
-    AutoMutateCode amc(cx, *this, "Module::changeHeap");
-    if (maybeHeap_)
-        despecializeFromHeap(maybeHeap_);
-    specializeToHeap(newHeap);
-    return true;
-}
-
-bool
-Module::detachHeap(JSContext* cx)
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    MOZ_ASSERT(pod.usesHeap_);
-
-    // Content JS should not be able to run (and detach heap) from within an
-    // interrupt callback, but in case it does, fail. Otherwise, the heap can
-    // change at an arbitrary instruction and break the assumption below.
-    if (interrupted_) {
-        JS_ReportError(cx, "attempt to detach from inside interrupt handler");
-        return false;
-    }
-
-    // Even if this->active(), to reach here, the activation must have called
-    // out via an import exit stub. FFI stubs check if heapDatum() is null on
-    // reentry and throw an exception if so.
-    MOZ_ASSERT_IF(activation(), activation()->exitReason() == ExitReason::ImportJit ||
-                                activation()->exitReason() == ExitReason::ImportInterp);
-
-    AutoMutateCode amc(cx, *this, "Module::detachHeap");
-    despecializeFromHeap(maybeHeap_);
-    return true;
-}
-
-void
-Module::setInterrupted(bool interrupted)
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    interrupted_ = interrupted;
-}
-
-Module*
-Module::nextLinked() const
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    return next_;
-}
-
-bool
 Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
 {
     MOZ_ASSERT(dynamicallyLinked_);
 
     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
@@ -1174,27 +1094,16 @@ Module::callExport(JSContext* cx, uint32
                 return false;
             // Bool32x4 uses the same representation as Int32x4.
             memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
             break;
           }
         }
     }
 
-    // The correct way to handle this situation would be to allocate a new range
-    // of PROT_NONE memory and module.changeHeap to this memory. That would
-    // cause every access to take the out-of-bounds signal-handler path which
-    // does the right thing. For now, just throw an out-of-memory exception
-    // since these can technically pop out anywhere and the full fix may
-    // actually OOM when trying to allocate the PROT_NONE memory.
-    if (usesHeap() && !maybeHeap_) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
-        return false;
-    }
-
     {
         // Push a WasmActivation to describe the wasm frames we're about to push
         // when running this module. Additionally, push a JitActivation so that
         // the optimized wasm-to-Ion FFI call path (which we want to be very
         // fast) can avoid doing so. The JitActivation is marked as inactive so
         // stack iteration will skip over it.
         WasmActivation activation(cx, *this);
         JitActivation jitActivation(cx, /* active */ false);
@@ -1513,18 +1422,18 @@ Module::clone(JSContext* cx, const Stati
                                        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 (maybeHeap_)
-        out->despecializeFromHeap(maybeHeap_);
+    if (usesHeap())
+        out->despecializeFromHeap(heap_);
 
     if (!out->staticallyLink(cx, linkData))
         return nullptr;
 
     return Move(out);
 }
 
 void
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -320,16 +320,32 @@ class CodeDeleter
     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);
 
+// A wasm module can either use no heap, a unshared heap (ArrayBuffer) or shared
+// heap (SharedArrayBuffer).
+
+enum class HeapUsage
+{
+    None = false,
+    Unshared = 1,
+    Shared = 2
+};
+
+static inline bool
+UsesHeap(HeapUsage heapUsage)
+{
+    return bool(heapUsage);
+}
+
 // 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
@@ -370,18 +386,17 @@ class Module
     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 bool               usesHeap_;
-        const bool               sharedHeap_;
+        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_;
@@ -395,24 +410,21 @@ class Module
     // Initialized during staticallyLink:
     bool                         staticallyLinked_;
     uint8_t*                     interrupt_;
     uint8_t*                     outOfBounds_;
     FuncPtrTableVector           funcPtrTables_;
 
     // Initialized during dynamicallyLink:
     bool                         dynamicallyLinked_;
-    BufferPtr                    maybeHeap_;
-    Module**                     prev_;
-    Module*                      next_;
+    BufferPtr                    heap_;
 
     // Mutated after dynamicallyLink:
     bool                         profilingEnabled_;
     FuncLabelVector              funcLabels_;
-    bool                         interrupted_;
 
     class AutoMutateCode;
 
     uint32_t totalBytes() const;
     uint8_t* rawHeapPtr() const;
     uint8_t*& rawHeapPtr();
     WasmActivation*& activation();
     void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
@@ -443,44 +455,42 @@ class Module
     template <class> friend struct js::MallocProvider;
     friend class js::WasmActivation;
 
   public:
     static const unsigned SizeOfImportExit = sizeof(ImportExit);
     static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
     static const unsigned SizeOfEntryArg = sizeof(EntryArg);
 
-    enum HeapBool { DoesntUseHeap = false, UsesHeap = true };
-    enum SharedBool { UnsharedHeap = false, SharedHeap = true };
     enum MutedBool { DontMuteErrors = false, MuteErrors = true };
 
     Module(CompileArgs args,
            uint32_t functionBytes,
            uint32_t codeBytes,
            uint32_t globalBytes,
-           HeapBool usesHeap,
-           SharedBool sharedHeap,
+           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_; }
-    bool usesHeap() const { return pod.usesHeap_; }
-    bool sharedHeap() const { return pod.sharedHeap_; }
+    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_; }
@@ -510,31 +520,19 @@ class Module
     // buffer must be given. The given import vector must match the module's
     // ImportVector.
 
     bool dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
                          const AutoVectorRooter<JSFunction*>& imports);
 
     // The wasm heap, established by dynamicallyLink.
 
-    ArrayBufferObjectMaybeShared* maybeBuffer() const;
-    SharedMem<uint8_t*> maybeHeap() const;
+    SharedMem<uint8_t*> heap() const;
     size_t heapLength() const;
 
-    // asm.js may detach and change the heap at any time. As an internal detail,
-    // the heap may not be changed while the module has been asynchronously
-    // interrupted.
-    //
-    // N.B. These methods and asm.js change-heap support will be removed soon.
-
-    bool changeHeap(Handle<ArrayBufferObject*> newBuffer, JSContext* cx);
-    bool detachHeap(JSContext* cx);
-    void setInterrupted(bool interrupted);
-    Module* nextLinked() const;
-
     // The exports of a wasm module are called by preparing an array of
     // arguments (coerced to the corresponding types of the Export signature)
     // and calling the export's entry trampoline.
 
     bool callExport(JSContext* cx, uint32_t exportIndex, CallArgs args);
 
     // Initially, calls to imports in wasm code call out through the generic
     // callImport method. If the imported callee gets JIT compiled and the types
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -622,17 +622,17 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
     MOZ_RELEASE_ASSERT(address.disp() >= 0);
     MOZ_RELEASE_ASSERT(address.base() == HeapReg.code());
     MOZ_RELEASE_ASSERT(!address.hasIndex() || address.index() != HeapReg.code());
     MOZ_RELEASE_ASSERT(address.scale() == 0);
     if (address.hasBase()) {
         uintptr_t base;
         StoreValueFromGPReg(SharedMem<void*>::unshared(&base), sizeof(uintptr_t),
                             AddressOfGPRegisterSlot(context, address.base()));
-        MOZ_RELEASE_ASSERT(reinterpret_cast<uint8_t*>(base) == module.maybeHeap());
+        MOZ_RELEASE_ASSERT(reinterpret_cast<uint8_t*>(base) == module.heap());
     }
     if (address.hasIndex()) {
         uintptr_t index;
         StoreValueFromGPReg(SharedMem<void*>::unshared(&index), sizeof(uintptr_t),
                             AddressOfGPRegisterSlot(context, address.index()));
         MOZ_RELEASE_ASSERT(uint32_t(index) == index);
     }
 #endif
@@ -640,38 +640,38 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
     // Determine the actual effective address of the faulting access. We can't
     // rely on the faultingAddress given to us by the OS, because we need the
     // address of the start of the access, and the OS may sometimes give us an
     // address somewhere in the middle of the heap access.
     uint8_t* accessAddress = ComputeAccessAddress(context, address);
     MOZ_RELEASE_ASSERT(size_t(faultingAddress - accessAddress) < access.size(),
                        "Given faulting address does not appear to be within computed "
                        "faulting address range");
-    MOZ_RELEASE_ASSERT(accessAddress >= module.maybeHeap(),
+    MOZ_RELEASE_ASSERT(accessAddress >= module.heap(),
                        "Access begins outside the asm.js heap");
-    MOZ_RELEASE_ASSERT(accessAddress + access.size() <= module.maybeHeap() + AsmJSMappedSize,
+    MOZ_RELEASE_ASSERT(accessAddress + access.size() <= module.heap() + AsmJSMappedSize,
                        "Access extends beyond the asm.js heap guard region");
-    MOZ_RELEASE_ASSERT(accessAddress + access.size() > module.maybeHeap() + module.heapLength(),
+    MOZ_RELEASE_ASSERT(accessAddress + access.size() > module.heap() + module.heapLength(),
                        "Computed access address is not actually out of bounds");
 
     // The basic sandbox model is that all heap accesses are a heap base
     // register plus an index, and the index is always computed with 32-bit
     // operations, so we know it can only be 4 GiB off of the heap base.
     //
     // However, we wish to support the optimization of folding immediates
     // and scaled indices into addresses, and any address arithmetic we fold
     // gets done at full pointer width, so it doesn't get properly wrapped.
     // We support this by extending AsmJSMappedSize to the greatest size
     // that could be reached by such an unwrapped address, and then when we
     // arrive here in the signal handler for such an access, we compute the
     // fully wrapped address, and perform the load or store on it.
     //
     // Taking a signal is really slow, but in theory programs really shouldn't
     // be hitting this anyway.
-    intptr_t unwrappedOffset = accessAddress - module.maybeHeap().unwrap(/*safe - for value*/);
+    intptr_t unwrappedOffset = accessAddress - module.heap().unwrap(/*safe - for value*/);
     uint32_t wrappedOffset = uint32_t(unwrappedOffset);
     size_t size = access.size();
     MOZ_RELEASE_ASSERT(wrappedOffset + size > wrappedOffset);
     bool inBounds = wrappedOffset < module.heapLength() &&
                     wrappedOffset + size < module.heapLength();
 
     // If this is storing Z of an XYZ, check whether X is also in bounds, so
     // that we don't store anything before throwing.
@@ -679,20 +679,20 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
     uint32_t wrappedBaseOffset = uint32_t(unwrappedOffset - heapAccess->offsetWithinWholeSimdVector());
     if (wrappedBaseOffset >= module.heapLength())
         inBounds = false;
 
     if (inBounds) {
         // We now know that this is an access that is actually in bounds when
         // properly wrapped. Complete the load or store with the wrapped
         // address.
-        SharedMem<uint8_t*> wrappedAddress = module.maybeHeap() + wrappedOffset;
-        MOZ_RELEASE_ASSERT(wrappedAddress >= module.maybeHeap());
+        SharedMem<uint8_t*> wrappedAddress = module.heap() + wrappedOffset;
+        MOZ_RELEASE_ASSERT(wrappedAddress >= module.heap());
         MOZ_RELEASE_ASSERT(wrappedAddress + size > wrappedAddress);
-        MOZ_RELEASE_ASSERT(wrappedAddress + size <= module.maybeHeap() + module.heapLength());
+        MOZ_RELEASE_ASSERT(wrappedAddress + size <= module.heap() + module.heapLength());
         switch (access.kind()) {
           case Disassembler::HeapAccess::Load:
             SetRegisterToLoadedValue(context, wrappedAddress.cast<void*>(), size, access.otherOperand());
             break;
           case Disassembler::HeapAccess::LoadSext32:
             SetRegisterToLoadedValueSext32(context, wrappedAddress.cast<void*>(), size, access.otherOperand());
             break;
           case Disassembler::HeapAccess::Store:
@@ -757,19 +757,19 @@ HandleFault(PEXCEPTION_POINTERS exceptio
     if (!activation)
         return false;
 
     const Module& module = activation->module();
 
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
-    if (!module.maybeHeap() ||
-        faultingAddress < module.maybeHeap() ||
-        faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
+    if (!module.usesHeap() ||
+        faultingAddress < module.heap() ||
+        faultingAddress >= module.heap() + AsmJSMappedSize)
     {
         return false;
     }
 
     if (!module.containsFunctionPC(pc)) {
         // On Windows, it is possible for InterruptRunningCode to execute
         // between a faulting heap access and the handling of the fault due
         // to InterruptRunningCode's use of SuspendThread. When this happens,
@@ -902,19 +902,19 @@ HandleMachException(JSRuntime* rt, const
 
     const Module& module = activation->module();
     if (!module.containsFunctionPC(pc))
         return false;
 
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
-    if (!module.maybeHeap() ||
-        faultingAddress < module.maybeHeap() ||
-        faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
+    if (!module.usesHeap() ||
+        faultingAddress < module.heap() ||
+        faultingAddress >= module.heap() + AsmJSMappedSize)
     {
         return false;
     }
 
     const HeapAccess* heapAccess = module.lookupHeapAccess(pc);
     if (!heapAccess)
         return false;
 
@@ -1112,19 +1112,19 @@ HandleFault(int signum, siginfo_t* info,
 
     const Module& module = activation->module();
     if (!module.containsFunctionPC(pc))
         return false;
 
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
-    if (!module.maybeHeap() ||
-        faultingAddress < module.maybeHeap() ||
-        faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
+    if (!module.usesHeap() ||
+        faultingAddress < module.heap() ||
+        faultingAddress >= module.heap() + AsmJSMappedSize)
     {
         return false;
     }
 
     const HeapAccess* heapAccess = module.lookupHeapAccess(pc);
     if (!heapAccess)
         return false;
 
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -94,17 +94,17 @@ static const unsigned FramePushedAfterSa
 #endif
 static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
 
 // Generate a stub that enters wasm from a C++ caller via the native ABI.
 // The signature of the entry point is Module::CodePtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
 // must map from the ABI of CodePtr to the export's signature's ABI.
 static bool
-GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, Module::HeapBool usesHeap)
+GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
 {
     MacroAssembler& masm = mg.masm();
     const MallocSig& sig = mg.exportSig(exportIndex);
 
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
     offsets.begin = masm.currentOffset();
@@ -327,40 +327,22 @@ FillArgumentArray(MacroAssembler& masm, 
                 masm.canonicalizeDouble(ScratchDoubleReg);
                 masm.storeDouble(ScratchDoubleReg, dstAddr);
             }
             break;
         }
     }
 }
 
-// If an import call detaches its heap (viz., via ArrayBuffer.transfer), it must
-// call change-heap to another heap (viz., the new heap returned by transfer)
-// before returning to asm.js code. If the application fails to do this (if the
-// heap pointer is null), jump to a stub.
-static void
-CheckForHeapDetachment(MacroAssembler& masm, Register scratch, Label* onDetached)
-{
-    MOZ_ASSERT(int(masm.framePushed()) >= int(ShadowStackSpace));
-    AssertStackAlignment(masm, ABIStackAlignment);
-#if defined(JS_CODEGEN_X86)
-    CodeOffset offset = masm.movlWithPatch(PatchedAbsoluteAddress(), scratch);
-    masm.append(AsmJSGlobalAccess(offset, HeapGlobalDataOffset));
-    masm.branchTestPtr(Assembler::Zero, scratch, scratch, onDetached);
-#else
-    masm.branchTestPtr(Assembler::Zero, HeapReg, HeapReg, onDetached);
-#endif
-}
-
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into an appropriate InvokeImport C++
 // function, having boxed all the ABI arguments into a homogeneous Value array.
 static bool
-GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool usesHeap,
-                       Label* throwLabel, Label* onDetached, ProfilingOffsets* offsets)
+GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLabel,
+                       ProfilingOffsets* offsets)
 {
     MacroAssembler& masm = mg.masm();
     const MallocSig& sig = mg.importSig(importIndex);
 
     masm.setFramePushed(0);
 
     // Argument types for InvokeImport_*:
     static const MIRType typeArray[] = { MIRType_Pointer,   // ImportExit
@@ -435,23 +417,16 @@ GenerateInterpExitStub(ModuleGenerator& 
         masm.loadDouble(argv, ReturnDoubleReg);
         break;
       case ExprType::I32x4:
       case ExprType::F32x4:
       case ExprType::B32x4:
         MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
     }
 
-    // The heap pointer may have changed during the FFI, so reload it and test
-    // for detachment.
-    if (usesHeap) {
-        masm.loadAsmJSHeapRegisterFromGlobalData();
-        CheckForHeapDetachment(masm, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
-    }
-
     GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, offsets);
 
     if (masm.oom())
         return false;
 
     offsets->end = masm.currentOffset();
     return true;
 }
@@ -461,18 +436,18 @@ static const unsigned MaybeSavedGlobalRe
 #else
 static const unsigned MaybeSavedGlobalReg = 0;
 #endif
 
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into a compatible JIT function,
 // having boxed all the ABI arguments into the JIT stack frame layout.
 static bool
-GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool usesHeap,
-                    Label* throwLabel, Label* onDetached, ProfilingOffsets* offsets)
+GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
+                    Label* throwLabel, ProfilingOffsets* offsets)
 {
     MacroAssembler& masm = mg.masm();
     const MallocSig& sig = mg.importSig(importIndex);
 
     masm.setFramePushed(0);
 
     // JIT calls use the following stack layout (sp grows to the left):
     //   | retaddr | descriptor | callee | argc | this | arg1..N |
@@ -535,18 +510,17 @@ GenerateJitExitStub(ModuleGenerator& mg,
     FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch);
     argOffset += sig.args().length() * sizeof(Value);
     MOZ_ASSERT(argOffset == jitFrameBytes);
 
     // 6. Jit code will clobber all registers, even non-volatiles. GlobalReg and
     //    HeapReg are removed from the general register set for asm.js code, so
     //    these will not have been saved by the caller like all other registers,
     //    so they must be explicitly preserved. Only save GlobalReg since
-    //    HeapReg must be reloaded (from global data) after the call since the
-    //    heap may change during the FFI call.
+    //    HeapReg can be reloaded (from global data) after the call.
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting");
     masm.storePtr(GlobalReg, Address(masm.getStackPointer(), jitFrameBytes));
 #endif
 
     {
         // Enable Activation.
         //
@@ -693,22 +667,20 @@ GenerateJitExitStub(ModuleGenerator& mg,
       case ExprType::F32x4:
       case ExprType::B32x4:
         MOZ_CRASH("SIMD types shouldn't be returned from an import");
     }
 
     Label done;
     masm.bind(&done);
 
-    // The heap pointer has to be reloaded anyway since JIT code could have
-    // clobbered it. Additionally, the import may have detached the heap buffer.
-    if (usesHeap) {
+    // Ion code does not respect system callee-saved register conventions so
+    // reload the heap register.
+    if (usesHeap)
         masm.loadAsmJSHeapRegisterFromGlobalData();
-        CheckForHeapDetachment(masm, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
-    }
 
     GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, offsets);
 
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
         masm.setFramePushed(nativeFramePushed);
 
         // Coercion calls use the following stack layout (sp grows to the left):
@@ -759,42 +731,16 @@ GenerateJitExitStub(ModuleGenerator& mg,
 
     if (masm.oom())
         return false;
 
     offsets->end = masm.currentOffset();
     return true;
 }
 
-// Generate a stub that is called when returning from an exit where the module's
-// buffer has been detached. This stub first calls a C++ function to report an
-// exception and then jumps to the generic throw stub to pop everything off the
-// stack.
-static bool
-GenerateOnDetachedStub(ModuleGenerator& mg, Label* onDetached, Label* throwLabel)
-{
-    MacroAssembler& masm = mg.masm();
-
-    masm.haltingAlign(CodeAlignment);
-    Offsets offsets;
-    offsets.begin = masm.currentOffset();
-    masm.bind(onDetached);
-
-    // For now, OnDetached always throws (see OnDetached comment).
-    masm.assertStackAlignment(ABIStackAlignment);
-    masm.call(SymbolicAddress::OnDetached);
-    masm.jump(throwLabel);
-
-    if (masm.oom())
-        return false;
-
-    offsets.end = masm.currentOffset();
-    return mg.defineInlineStub(offsets);
-}
-
 // Generate a stub that is called immediately after the prologue when there is a
 // stack overflow. This stub calls a C++ function to report the error and then
 // jumps to the throw stub to pop the activation.
 static bool
 GenerateStackOverflowStub(ModuleGenerator& mg, Label* throwLabel)
 {
     MacroAssembler& masm = mg.masm();
 
@@ -924,17 +870,17 @@ static const LiveRegisterSet AllRegsExce
 // code. That means we must first save *all* registers and restore *all*
 // registers (except the stack pointer) when we resume. The address to resume to
 // (assuming that js::HandleExecutionInterrupt doesn't indicate that the
 // execution should be aborted) is stored in WasmActivation::resumePC_.
 // Unfortunately, loading this requires a scratch register which we don't have
 // after restoring all registers. To hack around this, push the resumePC on the
 // stack so that it can be popped directly into PC.
 static bool
-GenerateAsyncInterruptStub(ModuleGenerator& mg, Module::HeapBool usesHeap, Label* throwLabel)
+GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
 {
     MacroAssembler& masm = mg.masm();
 
     masm.haltingAlign(CodeAlignment);
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
@@ -1121,45 +1067,36 @@ GenerateThrowStub(ModuleGenerator& mg, L
     if (masm.oom())
         return false;
 
     offsets.end = masm.currentOffset();
     return mg.defineInlineStub(offsets);
 }
 
 bool
-wasm::GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap)
+wasm::GenerateStubs(ModuleGenerator& mg, bool usesHeap)
 {
     for (unsigned i = 0; i < mg.numDeclaredExports(); i++) {
         if (!GenerateEntry(mg, i, usesHeap))
             return false;
     }
 
     Label onThrow;
 
-    {
-        Label onDetached;
-
-        for (size_t i = 0; i < mg.numDeclaredImports(); i++) {
-            ProfilingOffsets interp;
-            if (!GenerateInterpExitStub(mg, i, usesHeap, &onThrow, &onDetached, &interp))
-                return false;
+    for (size_t i = 0; i < mg.numDeclaredImports(); i++) {
+        ProfilingOffsets interp;
+        if (!GenerateInterpExitStub(mg, i, &onThrow, &interp))
+            return false;
 
-            ProfilingOffsets jit;
-            if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &onDetached, &jit))
-                return false;
+        ProfilingOffsets jit;
+        if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &jit))
+            return false;
 
-            if (!mg.defineImport(i, interp, jit))
-                return false;
-        }
-
-        if (onDetached.used()) {
-            if (!GenerateOnDetachedStub(mg, &onDetached, &onThrow))
-                return false;
-        }
+        if (!mg.defineImport(i, interp, jit))
+            return false;
     }
 
     if (mg.masm().asmStackOverflowLabel()->used()) {
         if (!GenerateStackOverflowStub(mg, &onThrow))
             return false;
     }
 
     if (mg.masm().asmSyncInterruptLabel()->used()) {
@@ -1173,17 +1110,17 @@ wasm::GenerateStubs(ModuleGenerator& mg,
     }
 
     // Generate unconditionally: the out-of-bounds exit may be used later even
     // if signal handling isn't used for out-of-bounds at the moment.
     if (!GenerateOutOfBoundsStub(mg, &onThrow))
         return false;
 
     // Generate unconditionally: the async interrupt may be taken at any time.
-    if (!GenerateAsyncInterruptStub(mg, usesHeap, &onThrow))
+    if (!GenerateAsyncInterruptStub(mg, &onThrow))
         return false;
 
     if (onThrow.used()) {
         if (!GenerateThrowStub(mg, &onThrow))
             return false;
     }
 
     return true;
--- a/js/src/asmjs/WasmStubs.h
+++ b/js/src/asmjs/WasmStubs.h
@@ -20,14 +20,14 @@
 #define wasm_stubs_h
 
 #include "asmjs/WasmGenerator.h"
 
 namespace js {
 namespace wasm {
 
 bool
-GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap);
+GenerateStubs(ModuleGenerator& mg, bool usesHeap);
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_stubs_h
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -38,44 +38,26 @@ extern MOZ_EXPORT int64_t
 __aeabi_idivmod(int, int);
 
 extern MOZ_EXPORT int64_t
 __aeabi_uidivmod(int, int);
 
 }
 #endif
 
-namespace js {
-namespace wasm {
-
-void
-ReportOverRecursed()
+static void
+WasmReportOverRecursed()
 {
-    JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
-    ReportOverRecursed(cx);
+    ReportOverRecursed(JSRuntime::innermostWasmActivation()->cx());
 }
 
-bool
-HandleExecutionInterrupt()
+static bool
+WasmHandleExecutionInterrupt()
 {
-    WasmActivation* act = JSRuntime::innermostWasmActivation();
-    act->module().setInterrupted(true);
-    bool ret = CheckForInterrupt(act->cx());
-    act->module().setInterrupted(false);
-    return ret;
-}
-
-} // namespace wasm
-} // namespace js
-
-static void
-OnDetached()
-{
-    JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
-    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
+    return CheckForInterrupt(JSRuntime::innermostWasmActivation()->cx());
 }
 
 static void
 OnOutOfBounds()
 {
     JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
 }
@@ -182,25 +164,23 @@ wasm::AddressOf(SymbolicAddress imm, Exc
     switch (imm) {
       case SymbolicAddress::Runtime:
         return cx->runtimeAddressForJit();
       case SymbolicAddress::RuntimeInterruptUint32:
         return cx->runtimeAddressOfInterruptUint32();
       case SymbolicAddress::StackLimit:
         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
       case SymbolicAddress::ReportOverRecursed:
-        return FuncCast(wasm::ReportOverRecursed, Args_General0);
-      case SymbolicAddress::OnDetached:
-        return FuncCast(OnDetached, Args_General0);
+        return FuncCast(WasmReportOverRecursed, Args_General0);
       case SymbolicAddress::OnOutOfBounds:
         return FuncCast(OnOutOfBounds, Args_General0);
       case SymbolicAddress::OnImpreciseConversion:
         return FuncCast(OnImpreciseConversion, Args_General0);
       case SymbolicAddress::HandleExecutionInterrupt:
-        return FuncCast(wasm::HandleExecutionInterrupt, Args_General0);
+        return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
       case SymbolicAddress::InvokeImport_Void:
         return FuncCast(InvokeImport_Void, Args_General3);
       case SymbolicAddress::InvokeImport_I32:
         return FuncCast(InvokeImport_I32, Args_General3);
       case SymbolicAddress::InvokeImport_F64:
         return FuncCast(InvokeImport_F64, Args_General3);
       case SymbolicAddress::CoerceInPlace_ToInt32:
         return FuncCast(CoerceInPlace_ToInt32, Args_General1);
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -552,17 +552,16 @@ enum class SymbolicAddress
     ExpD,
     LogD,
     PowD,
     ATan2D,
     Runtime,
     RuntimeInterruptUint32,
     StackLimit,
     ReportOverRecursed,
-    OnDetached,
     OnOutOfBounds,
     OnImpreciseConversion,
     HandleExecutionInterrupt,
     InvokeImport_Void,
     InvokeImport_I32,
     InvokeImport_F64,
     CoerceInPlace_ToInt32,
     CoerceInPlace_ToNumber,
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -519,17 +519,17 @@ js::atomics_isLockFree(JSContext* cx, un
 // simulator build with ARMHWCAP=vfp set.  Do not set any other flags; other
 // vfp/neon flags force ARMv7 to be set.
 
 static void
 GetCurrentAsmJSHeap(SharedMem<void*>* heap, size_t* length)
 {
     JSRuntime* rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
     wasm::Module& module = rt->wasmActivationStack()->module();
-    *heap = module.maybeHeap().cast<void*>();
+    *heap = module.heap().cast<void*>();
     *length = module.heapLength();
 }
 
 int32_t
 js::atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value)
 {
     SharedMem<void*> heap;
     size_t heapLength;
deleted file mode 100644
--- a/js/src/jit-test/tests/asm.js/testBug1100237.js
+++ /dev/null
@@ -1,33 +0,0 @@
-load(libdir + "asm.js");
-
-var byteLength = Function.prototype.call.bind(
-    Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get
-);
-var m = asmCompile("glob", "s", "b", `
-    "use asm";
-    var I32 = glob.Int32Array;
-    var i32 = new I32(b);
-    var len = glob.byteLength;
-    function ch(b2) {
-        if (len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 80000000) {
-            return false;
-        }
-        i32 = new I32(b2);
-        b = b2;
-        return true
-    }
-    function get(i) {
-        i = i | 0;
-        return i32[i >> 2] | 0
-    }
-    return {
-        get: get,
-        changeHeap: ch
-    }
-`);
-var buf1 = new ArrayBuffer(16777216)
-var { get, changeHeap } = asmLink(m, this, null, buf1)
-assertEq(changeHeap(new ArrayBuffer(33554432)), true)
-assertEq(get(), 0)
-assertEq(changeHeap(buf1), true);
-get();
--- a/js/src/jit-test/tests/asm.js/testNeuter.js
+++ b/js/src/jit-test/tests/asm.js/testNeuter.js
@@ -18,97 +18,25 @@ var m = asmCompile('stdlib', 'foreign', 
                        return i32[i>>2]|0
                    }
                    return {get:get, set:set}`);
 
 var buffer = new ArrayBuffer(BUF_MIN);
 var {get, set} = asmLink(m, this, null, buffer);
 set(4, 42);
 assertEq(get(4), 42);
-neuter(buffer, "change-data");
-neuter(buffer, "same-data");
-assertThrowsInstanceOf(() => get(4), InternalError);
-
-var buf1 = new ArrayBuffer(BUF_MIN);
-var buf2 = new ArrayBuffer(BUF_MIN);
-var {get:get1, set:set1} = asmLink(m, this, null, buf1);
-var {get:get2, set:set2} = asmLink(m, this, null, buf2);
-set1(0, 13);
-set2(0, 42);
-neuter(buf1, "change-data");
-assertThrowsInstanceOf(() => get1(0), InternalError);
-assertEq(get2(0), 42);
+assertThrowsInstanceOf(() => neuter(buffer, "change-data"), InternalError);
+assertThrowsInstanceOf(() => neuter(buffer, "same-data"), InternalError);
 
 var m = asmCompile('stdlib', 'foreign', 'buffer',
                   `"use asm";
                    var i32 = new stdlib.Int32Array(buffer);
                    var ffi = foreign.ffi;
                    function inner(i) {
                        i=i|0;
                        ffi();
                        return i32[i>>2]|0
                    }
                    return inner`);
 
 var buffer = new ArrayBuffer(BUF_MIN);
-function ffi1() { neuter(buffer, "change-data"); }
+function ffi1() { assertThrowsInstanceOf(() => neuter(buffer, "change-data"), InternalError) }
 var inner = asmLink(m, this, {ffi:ffi1}, buffer);
-assertThrowsInstanceOf(() => inner(8), InternalError);
-
-var byteLength = Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
-var m = asmCompile('stdlib', 'foreign', 'buffer',
-                  `"use asm";
-                   var ffi = foreign.ffi;
-                   var I32 = stdlib.Int32Array;
-                   var i32 = new I32(buffer);
-                   var len = stdlib.byteLength;
-                   function changeHeap(newBuffer) {
-                       if (len(newBuffer) & 0xffffff || len(newBuffer) <= 0xffffff || len(newBuffer) > 0x80000000)
-                           return false;
-                       i32 = new I32(newBuffer);
-                       buffer = newBuffer;
-                       return true;
-                   }
-                   function get(i) {
-                       i=i|0;
-                       return i32[i>>2]|0;
-                   }
-                   function inner(i) {
-                       i=i|0;
-                       ffi();
-                       return get(i)|0;
-                   }
-                   return {changeHeap:changeHeap, get:get, inner:inner}`);
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-var buf3 = new ArrayBuffer(BUF_CHANGE_MIN);
-var buf4 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf2)[13] = 42;
-new Int32Array(buf3)[13] = 1024;
-new Int32Array(buf4)[13] = 1337;
-
-function ffi2() { neuter(buf1, "change-data"); assertEq(changeHeap(buf2), true); }
-var {changeHeap, get:get2, inner} = asmLink(m, this, {ffi:ffi2}, buf1);
-assertEq(inner(13*4), 42);
-
-function ffi3() {
-    assertEq(get2(13*4), 42);
-    assertEq(get2(BUF_CHANGE_MIN), 0)
-    assertEq(get3(13*4), 42);
-    assertEq(get3(BUF_CHANGE_MIN), 0)
-    neuter(buf2, "change-data");
-    assertThrowsInstanceOf(()=>get2(13*4), InternalError);
-    assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);
-    assertThrowsInstanceOf(()=>get3(13*4), InternalError);
-    assertThrowsInstanceOf(()=>get3(BUF_CHANGE_MIN), InternalError);
-    assertEq(changeHeap(buf3), true);
-    assertThrowsInstanceOf(()=>get2(13*4), InternalError);
-    assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);
-    assertEq(get3(13*4), 1024);
-    assertEq(get3(BUF_CHANGE_MIN), 0);
-    assertEq(changeHeap(buf4), true);
-}
-var {changeHeap, get:get3, inner} = asmLink(m, this, {ffi:ffi3}, buf2);
-assertEq(inner(13*4), 1337);
-assertThrowsInstanceOf(()=>get2(0), InternalError);
-assertEq(get3(BUF_CHANGE_MIN), 0);
-assertEq(get3(13*4), 1337);
--- a/js/src/jit-test/tests/asm.js/testProfiling.js
+++ b/js/src/jit-test/tests/asm.js/testProfiling.js
@@ -197,26 +197,16 @@ assertStackContainsSeq(stacks, ">,f1,>,<
 for (var i = 0; i < 20; i++)
     assertEq(f1(), 32);
 enableSingleStepProfiling();
 assertEq(f1(), 32);
 var stacks = disableSingleStepProfiling();
 assertStackContainsSeq(stacks, ">,f1,>,<,f1,>,>,<,f1,>,f2,>,<,f1,>,<,f2,>,<,f1,>,f2,>,<,f1,>,>,<,f1,>,<,f1,>,f1,>,>");
 
 
-// Detachment exit
-var buf = new ArrayBuffer(BUF_CHANGE_MIN);
-var ffi = function() { neuter(buf, 'change-data') }
-var f = asmLink(asmCompile('g','ffis','buf', USE_ASM + 'var ffi = ffis.ffi; var i32 = new g.Int32Array(buf); function f() { ffi() } return f'), this, {ffi:ffi}, buf);
-enableSingleStepProfiling();
-assertThrowsInstanceOf(f, InternalError);
-var stacks = disableSingleStepProfiling();
-assertStackContainsSeq(stacks, ">,f,>,<,f,>,inline stub,f,>,<,f,>,inline stub,f,>");
-
-
 if (isSimdAvailable() && typeof SIMD !== 'undefined') {
     // SIMD out-of-bounds exit
     var buf = new ArrayBuffer(0x10000);
     var f = asmLink(asmCompile('g','ffi','buf', USE_ASM + 'var f4=g.SIMD.float32x4; var f4l=f4.load; var u8=new g.Uint8Array(buf); function f(i) { i=i|0; return f4l(u8, 0xFFFF + i | 0); } return f'), this, {}, buf);
     enableSingleStepProfiling();
     assertThrowsInstanceOf(() => f(4), RangeError);
     var stacks = disableSingleStepProfiling();
     // TODO check that expected is actually the correctly expected string, when
deleted file mode 100644
--- a/js/src/jit-test/tests/asm.js/testResize.js
+++ /dev/null
@@ -1,366 +0,0 @@
-// |jit-test| test-also-noasmjs
-load(libdir + "asm.js");
-load(libdir + "asserts.js");
-
-// Tests for importing typed array view constructors
-
-assertAsmTypeFail('glob', USE_ASM + "var I32=glob.Int32Arra; function f() {} return f");
-var m = asmCompile('glob', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
-assertAsmLinkFail(m, {});
-assertAsmLinkFail(m, {Int32Array:null});
-assertAsmLinkFail(m, {Int32Array:{}});
-assertAsmLinkFail(m, {Int32Array:Uint32Array});
-assertEq(asmLink(m, {Int32Array:Int32Array})(), undefined);
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
-assertEq(asmLink(m, this)(), undefined);
-assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
-
-assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I3(buf); function f() {} return f');
-assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=0; var i32=new I32(buf); function f() {} return f');
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I32(buf); function f() {} return f');
-assertAsmLinkFail(m, this, null, {});
-assertAsmLinkAlwaysFail(m, this, null, null);
-assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
-assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
-
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
-assertAsmLinkFail(m, this, null, {});
-assertAsmLinkAlwaysFail(m, this, null, null);
-assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
-assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
-
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var F32=glob.Float32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
-assertAsmLinkFail(m, this, null, {});
-assertAsmLinkAlwaysFail(m, this, null, null);
-assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
-assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
-
-// Tests for link-time validation of byteLength import
-
-assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var byteLength=glob.byteLength; function f() { return byteLength(1)|0 } return f');
-
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var byteLength=glob.byteLength; function f() { return 42 } return f');
-assertEq('byteLength' in this, false);
-assertAsmLinkFail(m, this);
-this['byteLength'] = null;
-assertAsmLinkFail(m, this);
-this['byteLength'] = {};
-assertAsmLinkFail(m, this);
-this['byteLength'] = function(){}
-assertAsmLinkFail(m, this);
-this['byteLength'] = (function(){}).bind(null);
-assertAsmLinkFail(m, this);
-this['byteLength'] = Function.prototype.call.bind();
-assertAsmLinkFail(m, this);
-this['byteLength'] = Function.prototype.call.bind({});
-assertAsmLinkFail(m, this);
-this['byteLength'] = Function.prototype.call.bind(function f() {});
-assertAsmLinkFail(m, this);
-this['byteLength'] = Function.prototype.call.bind(Math.sin);
-assertAsmLinkFail(m, this);
-this['byteLength'] =
-  Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
-assertEq(asmLink(m, this)(), 42);
-
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var b1=glob.byteLength, b2=glob.byteLength; function f() { return 43 } return f');
-assertEq(asmLink(m, this)(), 43);
-
-// Tests for validation of change-heap function
-
-const BYTELENGTH_IMPORT = "var len = glob.byteLength; ";
-const IMPORT0 = BYTELENGTH_IMPORT;
-const IMPORT1 = "var I8=glob.Int8Array; var i8=new I8(b); " + BYTELENGTH_IMPORT;
-const IMPORT2 = "var I8=glob.Int8Array; var i8=new I8(b); var I32=glob.Int32Array; var i32=new I32(b); var II32=glob.Int32Array; " + BYTELENGTH_IMPORT;
-
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f() { return 42 } function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function b(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2=1) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2,xyz) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(...r) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2,...r) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch({b2}) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { ;if((len((b2))) & (0xffffff) || (len((b2)) <= (0xffffff)) || len(b2) > 0x80000000) {;;return false;;} ; i8=new I8(b2);; b=b2;; return true;; } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function ch2(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { 3; if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { b2=b2|0; if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || 1 || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || 1 || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(i8(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(xyz) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff && len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) | 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) == 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xfffffe || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0x1ffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0x7fffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) < 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xfffffe || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) < 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || 1 > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0.0) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0xffffff) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffffff || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x1000001) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000001) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) ; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) {} i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) {return false} i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return true; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT0 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i7=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=1; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new 1; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I7(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new b(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(1); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2,1); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); xyz=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=1; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; 1; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return 1 } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return false } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; if (0) return true; 1 } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I32(b2); i32=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new II32(b2); b=b2; return true } function f() { return 42 } return f');
-
-// Tests for no calls in heap index expressions
-
-const CHANGE_FUN = 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true }';
-const SETUP = USE_ASM + IMPORT2 + 'var imul=glob.Math.imul; var ffi=ffis.ffi;' + CHANGE_FUN;
-
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { i32[0] } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { i32[0] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(imul(i,i)|0) >> 2] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = (imul(i,i)|0) } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(ffi()|0) >> 2] } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(g()|0) >> 2] } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(TBL[i&0]()|0) >> 2] } function g() { return 0 } var TBL=[g]; return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(g()|0) >> 2] = 0 } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = g()|0 } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i32[(g()|0)>>2] >> 2] } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i32[(g()|0)>>2] >> 2] = 0 } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = i32[(g()|0)>>2] } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[((i32[i>>2]|0) + (g()|0)) >> 2] } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[((i32[i>>2]|0) + (g()|0)) >> 2] = 0 } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = (i32[i>>2]|0) + (g()|0) } function g() { return 0 } return f');
-if (isSimdAvailable() && typeof SIMD !== 'undefined')
-    asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'var i4 = glob.SIMD.Int32x4; var ext = i4.extractLane; var add = i4.add;' + CHANGE_FUN + 'function f(i) { i=i|0; i32[ext(i4(i,1,2,i),0) >> 2]; i32[ext(add(i4(0,0,0,0),i4(1,1,1,1)),0) >> 2]; } return f');
-
-// Tests for constant heap accesses when change-heap is used
-
-const HEADER = USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= MIN || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } ';
-assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { i8[0x1000000] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { i8[0xffffff] = 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { i8[0x1000001] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { i8[0x1000000] = 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { return i8[0x1000000]|0 } return f');
-       asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { return i8[0xffffff]|0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { return i8[0x1000001]|0 } return f');
-       asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { return i8[0x1000000]|0 } return f');
-
-// Tests for validation of heap length
-
-var body = USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1ffffff || len(b2) > 0x4000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return ch';
-var m = asmCompile('glob', 'ffis', 'b', body);
-assertAsmLinkFail(m, this, null, new ArrayBuffer(BUF_CHANGE_MIN));
-assertAsmLinkFail(m, this, null, new ArrayBuffer(0x1000000));
-var changeHeap = asmLink(m, this, null, new ArrayBuffer(0x2000000));
-assertEq(changeHeap(new ArrayBuffer(0x1000000)), false);
-assertEq(changeHeap(new ArrayBuffer(0x2000000)), true);
-assertEq(changeHeap(new ArrayBuffer(0x2000001)), false);
-assertEq(changeHeap(new ArrayBuffer(0x4000000)), true);
-assertEq(changeHeap(new ArrayBuffer(0x5000000)), false);
-assertThrowsInstanceOf(() => changeHeap(null), TypeError);
-assertThrowsInstanceOf(() => changeHeap({}), TypeError);
-assertThrowsInstanceOf(() => changeHeap(new Int32Array(100)), TypeError);
-
-var detached = new ArrayBuffer(BUF_CHANGE_MIN);
-neuter(detached, "change-data");
-assertEq(changeHeap(detached), false);
-
-// Tests for runtime changing heap
-
-const CHANGE_HEAP = 'var changeHeap = glob.byteLength;';
-
-var changeHeapSource = `function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }`;
-var body = `var I32=glob.Int32Array; var i32=new I32(b);
-            var len=glob.byteLength;` +
-            changeHeapSource +
-           `function get(i) { i=i|0; return i32[i>>2]|0 }
-            function set(i, v) { i=i|0; v=v|0; i32[i>>2] = v }
-            return {get:get, set:set, changeHeap:ch}`;
-
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM + body);
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-var {get, set, changeHeap} = asmLink(m, this, null, buf1);
-
-assertEq(m.toString(), "function anonymous(glob, ffis, b) {\n" + USE_ASM + body + "\n}");
-assertEq(m.toSource(), "(function anonymous(glob, ffis, b) {\n" + USE_ASM + body + "\n})");
-assertEq(changeHeap.toString(), changeHeapSource);
-assertEq(changeHeap.toSource(), changeHeapSource);
-
-set(0, 42);
-set(4, 13);
-set(4, 13);
-assertEq(get(0), 42);
-assertEq(get(4), 13);
-set(BUF_CHANGE_MIN, 262);
-assertEq(get(BUF_CHANGE_MIN), 0);
-var buf2 = new ArrayBuffer(2*BUF_CHANGE_MIN);
-assertEq(changeHeap(buf2), true);
-assertEq(get(0), 0);
-assertEq(get(4), 0);
-set(BUF_CHANGE_MIN, 262);
-assertEq(get(BUF_CHANGE_MIN), 262);
-set(2*BUF_CHANGE_MIN, 262);
-assertEq(get(2*BUF_CHANGE_MIN), 0);
-changeHeap(buf1);
-assertEq(get(0), 42);
-assertEq(get(4), 13);
-set(BUF_CHANGE_MIN, 262);
-assertEq(get(BUF_CHANGE_MIN), 0);
-
-if (ArrayBuffer.transfer) {
-    var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-    var {get, set, changeHeap} = asmLink(m, this, null, buf1);
-    set(0, 100);
-    set(BUF_CHANGE_MIN - 4, 101);
-    set(BUF_CHANGE_MIN, 102);
-    var buf2 = ArrayBuffer.transfer(buf1);
-    assertEq(changeHeap(buf2), true);
-    assertEq(buf1.byteLength, 0);
-    assertEq(buf2.byteLength, BUF_CHANGE_MIN);
-    assertEq(get(0), 100);
-    assertEq(get(BUF_CHANGE_MIN-4), 101);
-    assertEq(get(BUF_CHANGE_MIN), 0);
-    assertEq(get(2*BUF_CHANGE_MIN-4), 0);
-    var buf3 = ArrayBuffer.transfer(buf2, 3*BUF_CHANGE_MIN);
-    assertEq(changeHeap(buf3), true);
-    assertEq(buf2.byteLength, 0);
-    assertEq(buf3.byteLength, 3*BUF_CHANGE_MIN);
-    assertEq(get(0), 100);
-    assertEq(get(BUF_CHANGE_MIN-4), 101);
-    assertEq(get(BUF_CHANGE_MIN), 0);
-    assertEq(get(2*BUF_CHANGE_MIN), 0);
-    set(BUF_CHANGE_MIN, 102);
-    set(2*BUF_CHANGE_MIN, 103);
-    assertEq(get(BUF_CHANGE_MIN), 102);
-    assertEq(get(2*BUF_CHANGE_MIN), 103);
-    var buf4 = ArrayBuffer.transfer(buf3, 2*BUF_CHANGE_MIN);
-    assertEq(changeHeap(buf4), true);
-    assertEq(buf3.byteLength, 0);
-    assertEq(buf4.byteLength, 2*BUF_CHANGE_MIN);
-    assertEq(get(0), 100);
-    assertEq(get(BUF_CHANGE_MIN-4), 101);
-    assertEq(get(BUF_CHANGE_MIN), 102);
-    assertEq(get(2*BUF_CHANGE_MIN), 0);
-    var buf5 = ArrayBuffer.transfer(buf4, 3*BUF_CHANGE_MIN);
-    assertEq(changeHeap(buf5), true);
-    assertEq(buf4.byteLength, 0);
-    assertEq(buf5.byteLength, 3*BUF_CHANGE_MIN);
-    assertEq(get(0), 100);
-    assertEq(get(BUF_CHANGE_MIN-4), 101);
-    assertEq(get(BUF_CHANGE_MIN), 102);
-    assertEq(get(2*BUF_CHANGE_MIN), 0);
-    var buf6 = ArrayBuffer.transfer(buf5, 0);
-    assertEq(buf5.byteLength, 0);
-    assertEq(buf6.byteLength, 0);
-    assertEq(changeHeap(buf6), false);
-}
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
-                   `var len=glob.byteLength;
-                    function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true }
-                    return ch`);
-var changeHeap = asmLink(m, this, null, buf1);
-assertEq(changeHeap(buf2), true);
-neuter(buf2, "change-data");
-assertEq(changeHeap(buf1), true);
-neuter(buf1, "change-data");
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf1)[0] = 13;
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf2)[0] = 42;
-
-// Tests for changing heap during an FFI:
-
-// Set the warmup to '2' so we can hit both interp and ion FFI exits
-setJitCompilerOption("ion.warmup.trigger", 2);
-setJitCompilerOption("baseline.warmup.trigger", 0);
-setJitCompilerOption("offthread-compilation.enable", 0);
-
-var changeToBuf = null;
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
-                   `var ffi=ffis.ffi;
-                    var I32=glob.Int32Array; var i32=new I32(b);
-                    var len=glob.byteLength;
-                    function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
-                    function test(i) { i=i|0; var sum=0; sum = i32[i>>2]|0; sum = (sum + (ffi()|0))|0; sum = (sum + (i32[i>>2]|0))|0; return sum|0 }
-                    return {test:test, changeHeap:ch}`);
-var ffi = function() { changeHeap(changeToBuf); return 1 }
-var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf1);
-changeToBuf = buf1;
-assertEq(test(0), 27);
-changeToBuf = buf2;
-assertEq(test(0), 56);
-changeToBuf = buf2;
-assertEq(test(0), 85);
-changeToBuf = buf1;
-assertEq(test(0), 56);
-changeToBuf = buf1;
-assertEq(test(0), 27);
-
-var ffi = function() { return { valueOf:function() { changeHeap(changeToBuf); return 100 } } };
-var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf1);
-changeToBuf = buf1;
-assertEq(test(0), 126);
-changeToBuf = buf2;
-assertEq(test(0), 155);
-changeToBuf = buf2;
-assertEq(test(0), 184);
-changeToBuf = buf1;
-assertEq(test(0), 155);
-changeToBuf = buf1;
-assertEq(test(0), 126);
-
-if (ArrayBuffer.transfer) {
-    var buf = new ArrayBuffer(BUF_CHANGE_MIN);
-    new Int32Array(buf)[0] = 3;
-    var ffi = function() {
-        var buf2 = ArrayBuffer.transfer(buf, 2*BUF_CHANGE_MIN);
-        new Int32Array(buf2)[BUF_CHANGE_MIN/4] = 13;
-        assertEq(changeHeap(buf2), true);
-        return 1
-    }
-    var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf);
-    assertEq(test(BUF_CHANGE_MIN), 14);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/asm.js/testTimeout7-nosignals.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// |jit-test| exitstatus: 6;
-load(libdir + "asm.js");
-
-// This test may iloop for valid reasons if not compiled with asm.js (namely,
-// inlining may allow the heap load to be hoisted out of the loop).
-if (!isAsmJSCompilationAvailable())
-    quit(6);
-
-setJitCompilerOption("signals.enable", 0);
-
-var byteLength =
-  Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf1)[0] = 13;
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf2)[0] = 42;
-
-// Test changeHeap from interrupt (as if that could ever happen...)
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
-                   `var I32=glob.Int32Array; var i32=new I32(b);
-                    var len=glob.byteLength;
-                    function changeHeap(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
-                    function f() {}
-                    function loop(i) { i=i|0; while((i32[i>>2]|0) == 13) { f() } }
-                    return {loop:loop, changeHeap:changeHeap}`);
-var { loop, changeHeap } = asmLink(m, this, null, buf1);
-timeout(1, function() { assertEq(changeHeap(buf2), false); return false });
-loop(0);
deleted file mode 100644
--- a/js/src/jit-test/tests/asm.js/testTimeout7.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// |jit-test| exitstatus: 6;
-load(libdir + "asm.js");
-
-// This test may iloop for valid reasons if not compiled with asm.js (namely,
-// inlining may allow the heap load to be hoisted out of the loop).
-if (!isAsmJSCompilationAvailable())
-    quit(6);
-
-var byteLength =
-  Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf1)[0] = 13;
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf2)[0] = 42;
-
-// Test changeHeap from interrupt (as if that could ever happen...)
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
-                   `var I32=glob.Int32Array; var i32=new I32(b);
-                    var len=glob.byteLength;
-                    function changeHeap(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
-                    function f() {}
-                    function loop(i) { i=i|0; while((i32[i>>2]|0) == 13) { f() } }
-                    return {loop:loop, changeHeap:changeHeap}`);
-var { loop, changeHeap } = asmLink(m, this, null, buf1);
-timeout(1, function() { assertEq(changeHeap(buf2), false); return false });
-loop(0);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testArrayBufferTransfer.js
+++ /dev/null
@@ -1,123 +0,0 @@
-load(libdir + "asserts.js");
-load(libdir + "asm.js");
-
-// Currently, ArrayBuffer.transfer is #ifdef NIGHTLY_BUILD. When
-// ArrayBuffer.transfer is enabled on release, this test should be removed.
-if (!ArrayBuffer.transfer)
-    quit();
-
-var XF = ArrayBuffer.transfer;
-
-assertEq(typeof XF, "function");
-assertEq(XF.length, 2);
-
-// arg 1 errors
-assertThrowsInstanceOf(()=>XF(), Error);
-assertThrowsInstanceOf(()=>XF(undefined), Error);
-assertThrowsInstanceOf(()=>XF(null), Error);
-assertThrowsInstanceOf(()=>XF({}), Error);
-assertThrowsInstanceOf(()=>XF(new Int32Array(1)), Error);
-var buf = new ArrayBuffer(1);
-neuter(buf, 'change-data');
-assertThrowsInstanceOf(()=>XF(buf), TypeError);
-
-// arg 2 errors
-var buf = new ArrayBuffer(1);
-assertThrowsInstanceOf(()=>XF(buf, -1), Error);
-assertThrowsInstanceOf(()=>XF(buf, {valueOf() { return -1 }}), Error);
-assertThrowsInstanceOf(()=>XF(buf, {toString() { return "-1" }}), Error);
-assertThrowsValue(()=>XF(buf, {valueOf() { throw "wee" }}), "wee");
-
-// arg 2 is coerced via ToInt32
-var buf = new ArrayBuffer(1);
-assertThrowsInstanceOf(()=>XF(buf, Math.pow(2,31)), Error);
-buf = XF(buf, Math.pow(2,32));
-assertEq(buf.byteLength, 0);
-buf = XF(buf, Math.pow(2,32) + 10);
-assertEq(buf.byteLength, 10);
-
-assertThrowsInstanceOf(()=>XF(buf, {valueOf() { neuter(buf, "change-data"); return 10; }}), TypeError);
-var buf = new ArrayBuffer(100);
-assertThrowsInstanceOf(()=>XF(buf, {valueOf() { ArrayBuffer.transfer(buf, 0); return 100; }}), TypeError);
-
-// on undefined second argument, stay the same size:
-var buf1 = new ArrayBuffer(0);
-var buf2 = XF(buf1);
-assertEq(buf1.byteLength, 0);
-assertEq(buf2.byteLength, 0);
-assertThrowsInstanceOf(()=>XF(buf1), TypeError);
-
-var buf1 = new ArrayBuffer(3);
-var buf2 = XF(buf1);
-assertEq(buf1.byteLength, 0);
-assertEq(buf2.byteLength, 3);
-assertThrowsInstanceOf(()=>XF(buf1), TypeError);
-
-var buf1 = new ArrayBuffer(9);
-var buf2 = XF(buf1, undefined);
-assertEq(buf1.byteLength, 0);
-assertEq(buf2.byteLength, 9);
-assertThrowsInstanceOf(()=>XF(buf1), TypeError);
-
-// cross-compartment wrapper
-var buf3 = newGlobal().eval("new ArrayBuffer(10)");
-var buf4 = XF(buf3, 20);
-assertEq(buf4.byteLength, 20);
-assertThrowsInstanceOf(()=>XF(buf3), TypeError);
-
-// test going to from various sizes
-function test(N1, N2) {
-    var buf1 = new ArrayBuffer(N1);
-    var i32 = new Int32Array(buf1);
-    for (var i = 0; i < i32.length; i++)
-        i32[i] = i;
-
-    var buf2 = XF(buf1, N2);
-
-    assertEq(buf1.byteLength, 0);
-    assertEq(i32.length, 0);
-    assertEq(buf2.byteLength, N2);
-    var i32 = new Int32Array(buf2);
-    for (var i = 0; i < Math.min(N1, N2)/4; i++)
-        assertEq(i32[i], i);
-    for (var i = Math.min(N1, N2)/4; i < i32.length; i++) {
-        assertEq(i32[i], 0);
-        i32[i] = -i;
-    }
-}
-test(0, 0);
-test(0, 4);
-test(4, 0);
-test(4, 4);
-test(0, 1000);
-test(4, 1000);
-test(1000, 0);
-test(1000, 4);
-test(1000, 1000);
-
-// asm.js:
-function testAsmJS(N1, N2) {
-    var buf1 = new ArrayBuffer(N1);
-    asmLink(asmCompile('stdlib', 'ffis', 'buf', USE_ASM + "var i32=new stdlib.Int32Array(buf); function f() {} return f"), this, null, buf1);
-    var i32 = new Int32Array(buf1);
-    for (var i = 0; i < i32.length; i+=100)
-        i32[i] = i;
-
-    var buf2 = XF(buf1, N2);
-
-    assertEq(buf1.byteLength, 0);
-    assertEq(i32.length, 0);
-    assertEq(buf2.byteLength, N2);
-    var i32 = new Int32Array(buf2);
-    var i = 0;
-    for (; i < Math.min(N1, N2)/4; i+=100)
-        assertEq(i32[i], i);
-    for (; i < i32.length; i+=100) {
-        assertEq(i32[i], 0);
-        i32[i] = -i;
-    }
-}
-testAsmJS(BUF_MIN, 0);
-testAsmJS(BUF_MIN, BUF_MIN);
-testAsmJS(BUF_MIN, 2*BUF_MIN);
-testAsmJS(2*BUF_MIN, BUF_MIN);
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -694,23 +694,23 @@ class FunctionExtended : public JSFuncti
     static const unsigned ARROW_NEWTARGET_SLOT = 0;
 
     static const unsigned METHOD_HOMEOBJECT_SLOT = 0;
 
     /*
      * All asm.js/wasm functions store their compiled module (either
      * WasmModuleObject or AsmJSModuleObject) in the first extended slot.
      */
-    static const unsigned ASM_MODULE_SLOT = 0;
+    static const unsigned WASM_MODULE_SLOT = 0;
 
     /*
      * wasm/asm.js exported functions store the index of the export in the
      * module's export vector in the second slot.
      */
-    static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
+    static const unsigned WASM_EXPORT_INDEX_SLOT = 1;
 
     static inline size_t offsetOfExtendedSlot(unsigned which) {
         MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
         return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
     }
     static inline size_t offsetOfArrowNewTargetSlot() {
         return offsetOfExtendedSlot(ARROW_NEWTARGET_SLOT);
     }
--- a/js/src/tests/js1_8_5/extensions/clone-transferables.js
+++ b/js/src/tests/js1_8_5/extensions/clone-transferables.js
@@ -1,21 +1,17 @@
 // |reftest| skip-if(!xulRuntime.shell)
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
 function test() {
-    // Note: -8 and -200 will trigger asm.js link failures because 8 and 200
-    // bytes are below the minimum allowed size, and the buffer will not
-    // actually be converted to an asm.js buffer.
     for (var size of [0, 8, 16, 200, 1000, 4096, -8, -200, -8192, -65536]) {
-        var buffer_ctor = (size < 0) ? AsmJSArrayBuffer : ArrayBuffer;
         size = Math.abs(size);
 
-        var old = new buffer_ctor(size);
+        var old = new ArrayBuffer(size);
         var copy = deserialize(serialize(old, [old]));
         assertEq(old.byteLength, 0);
         assertEq(copy.byteLength, size);
 
         var constructors = [ Int8Array,
                              Uint8Array,
                              Int16Array,
                              Uint16Array,
@@ -24,17 +20,17 @@ function test() {
                              Float32Array,
                              Float64Array,
                              Uint8ClampedArray,
                              DataView ];
 
         for (var ctor of constructors) {
             var dataview = (ctor === DataView);
 
-            var buf = new buffer_ctor(size);
+            var buf = new ArrayBuffer(size);
             var old_arr = new ctor(buf);
             assertEq(buf.byteLength, size);
             assertEq(buf, old_arr.buffer);
             if (!dataview)
                 assertEq(old_arr.length, size / old_arr.BYTES_PER_ELEMENT);
 
             var copy_arr = deserialize(serialize(old_arr, [ buf ]));
             assertEq(buf.byteLength, 0, "donor array buffer should be neutered");
@@ -47,46 +43,46 @@ function test() {
             buf = null;
             old_arr = null;
             gc(); // Tickle the ArrayBuffer -> view management
         }
 
         for (var ctor of constructors) {
             var dataview = (ctor === DataView);
 
-            var buf = new buffer_ctor(size);
+            var buf = new ArrayBuffer(size);
             var old_arr = new ctor(buf);
             var dv = new DataView(buf); // Second view
             var copy_arr = deserialize(serialize(old_arr, [ buf ]));
             assertEq(buf.byteLength, 0, "donor array buffer should be neutered");
             assertEq(old_arr.byteLength, 0, "donor typed array should be neutered");
             if (!dataview)
                 assertEq(old_arr.length, 0, "donor typed array should be neutered");
             assertEq(dv.byteLength, 0, "all views of donor array buffer should be neutered");
 
             buf = null;
             old_arr = null;
             gc(); // Tickle the ArrayBuffer -> view management
         }
 
         // Mutate the buffer during the clone operation. The modifications should be visible.
         if (size >= 4) {
-            old = new buffer_ctor(size);
+            old = new ArrayBuffer(size);
             var view = new Int32Array(old);
             view[0] = 1;
             var mutator = { get foo() { view[0] = 2; } };
             var copy = deserialize(serialize([ old, mutator ], [old]));
             var viewCopy = new Int32Array(copy[0]);
             assertEq(view.length, 0); // Neutered
             assertEq(viewCopy[0], 2);
         }
 
         // Neuter the buffer during the clone operation. Should throw an exception.
         if (size >= 4) {
-            old = new buffer_ctor(size);
+            old = new ArrayBuffer(size);
             var mutator = {
                 get foo() {
                     deserialize(serialize(old, [old]));
                 }
             };
             // The throw is not yet implemented, bug 919259.
             //var copy = deserialize(serialize([ old, mutator ], [old]));
         }
--- a/js/src/tests/js1_8_5/extensions/shell.js
+++ b/js/src/tests/js1_8_5/extensions/shell.js
@@ -11,22 +11,8 @@
 var workerDir = '';
 
 // explicitly turn on js185
 // XXX: The browser currently only supports up to version 1.8
 if (typeof version != 'undefined')
 {
   version(185);
 }
-
-
-// Note that AsmJS ArrayBuffers have a minimum size, currently 4096 bytes. If a
-// smaller size is given, a regular ArrayBuffer will be returned instead.
-function AsmJSArrayBuffer(size) {
-    var ab = new ArrayBuffer(size);
-    (new Function('global', 'foreign', 'buffer', '' +
-'        "use asm";' +
-'        var i32 = new global.Int32Array(buffer);' +
-'        function g() {};' +
-'        return g;' +
-''))(Function("return this")(),null,ab);
-    return ab;
-}
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -125,19 +125,16 @@ const Class ArrayBufferObject::class_ = 
 
 const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
     JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE),
     JS_FS_END
 };
 
 const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
     JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
-#ifdef NIGHTLY_BUILD
-    JS_FN("transfer", ArrayBufferObject::fun_transfer, 2, 0),
-#endif
     JS_FS_END
 };
 
 bool
 js::IsArrayBuffer(HandleValue v)
 {
     return v.isObject() && v.toObject().is<ArrayBufferObject>();
 }
@@ -228,233 +225,16 @@ bool
 ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(args.get(0).isObject() &&
                            JS_IsArrayBufferViewObject(&args.get(0).toObject()));
     return true;
 }
 
-#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-static void
-ReleaseAsmJSMappedData(void* base)
-{
-    MOZ_ASSERT(uintptr_t(base) % AsmJSPageSize == 0);
-#  ifdef XP_WIN
-    VirtualFree(base, 0, MEM_RELEASE);
-#  else
-    munmap(base, AsmJSMappedSize);
-#   if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
-    // Tell Valgrind/Memcheck to recommence reporting accesses in the
-    // previously-inaccessible region.
-    if (AsmJSMappedSize > 0) {
-        VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, AsmJSMappedSize);
-    }
-#   endif
-#  endif
-    MemProfiler::RemoveNative(base);
-}
-#else
-static void
-ReleaseAsmJSMappedData(void* base)
-{
-    MOZ_CRASH("asm.js only uses mapped buffers when using signal-handler OOB checking");
-}
-#endif
-
-#ifdef NIGHTLY_BUILD
-# if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-static bool
-TransferAsmJSMappedBuffer(JSContext* cx, const CallArgs& args,
-                          Handle<ArrayBufferObject*> oldBuffer, size_t newByteLength)
-{
-    size_t oldByteLength = oldBuffer->byteLength();
-    MOZ_ASSERT(oldByteLength % AsmJSPageSize == 0);
-    MOZ_ASSERT(newByteLength % AsmJSPageSize == 0);
-
-    ArrayBufferObject::BufferContents stolen =
-        ArrayBufferObject::stealContents(cx, oldBuffer, /* hasStealableContents = */ true);
-    if (!stolen)
-        return false;
-
-    MOZ_ASSERT(stolen.kind() == ArrayBufferObject::ASMJS_MAPPED);
-    uint8_t* data = stolen.data();
-
-    if (newByteLength > oldByteLength) {
-        void* diffStart = data + oldByteLength;
-        size_t diffLength = newByteLength - oldByteLength;
-#  ifdef XP_WIN
-        if (!VirtualAlloc(diffStart, diffLength, MEM_COMMIT, PAGE_READWRITE)) {
-            ReleaseAsmJSMappedData(data);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-#  else
-        // To avoid memset, use MAP_FIXED to clobber the newly-accessible pages
-        // with zero pages.
-        int flags = MAP_FIXED | MAP_PRIVATE | MAP_ANON;
-        if (mmap(diffStart, diffLength, PROT_READ | PROT_WRITE, flags, -1, 0) == MAP_FAILED) {
-            ReleaseAsmJSMappedData(data);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-#  endif
-        MemProfiler::SampleNative(diffStart, diffLength);
-    } else if (newByteLength < oldByteLength) {
-        void* diffStart = data + newByteLength;
-        size_t diffLength = oldByteLength - newByteLength;
-#  ifdef XP_WIN
-        if (!VirtualFree(diffStart, diffLength, MEM_DECOMMIT)) {
-            ReleaseAsmJSMappedData(data);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-#  else
-        if (madvise(diffStart, diffLength, MADV_DONTNEED) ||
-            mprotect(diffStart, diffLength, PROT_NONE))
-        {
-            ReleaseAsmJSMappedData(data);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-#  endif
-    }
-
-    ArrayBufferObject::BufferContents newContents =
-        ArrayBufferObject::BufferContents::create<ArrayBufferObject::ASMJS_MAPPED>(data);
-
-    RootedObject newBuffer(cx, ArrayBufferObject::create(cx, newByteLength, newContents));
-    if (!newBuffer) {
-        ReleaseAsmJSMappedData(data);
-        return false;
-    }
-
-    args.rval().setObject(*newBuffer);
-    return true;
-}
-# endif  // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-
-/*
- * Experimental implementation of ArrayBuffer.transfer:
- *   https://gist.github.com/andhow/95fb9e49996615764eff
- * which is currently in the early stages of proposal for ES7.
- */
-bool
-ArrayBufferObject::fun_transfer(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    HandleValue oldBufferArg = args.get(0);
-    HandleValue newByteLengthArg = args.get(1);
-
-    if (!oldBufferArg.isObject()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-        return false;
-    }
-
-    RootedObject oldBufferObj(cx, &oldBufferArg.toObject());
-    ESClassValue cls;
-    if (!GetBuiltinClass(cx, oldBufferObj, &cls))
-        return false;
-    if (cls != ESClass_ArrayBuffer) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-        return false;
-    }
-
-    // Beware: oldBuffer can point across compartment boundaries. ArrayBuffer
-    // contents are not compartment-specific so this is safe.
-    Rooted<ArrayBufferObject*> oldBuffer(cx);
-    if (oldBufferObj->is<ArrayBufferObject>()) {
-        oldBuffer = &oldBufferObj->as<ArrayBufferObject>();
-    } else {
-        JSObject* unwrapped = CheckedUnwrap(oldBufferObj);
-        if (!unwrapped || !unwrapped->is<ArrayBufferObject>()) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return false;
-        }
-        oldBuffer = &unwrapped->as<ArrayBufferObject>();
-    }
-
-    size_t oldByteLength = oldBuffer->byteLength();
-    size_t newByteLength;
-    if (newByteLengthArg.isUndefined()) {
-        newByteLength = oldByteLength;
-    } else {
-        int32_t i32;
-        if (!ToInt32(cx, newByteLengthArg, &i32))
-            return false;
-        if (i32 < 0) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
-            return false;
-        }
-        newByteLength = size_t(i32);
-    }
-
-    if (oldBuffer->isNeutered()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
-        return false;
-    }
-
-    UniquePtr<uint8_t, JS::FreePolicy> newData;
-    if (!newByteLength) {
-        if (!ArrayBufferObject::neuter(cx, oldBuffer, oldBuffer->contents()))
-            return false;
-    } else {
-# if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-        // With a 4gb mapped asm.js buffer, we can simply enable/disable access
-        // to the delta as long as the requested length is page-sized.
-        if (oldBuffer->isAsmJSMapped() && (newByteLength % AsmJSPageSize) == 0)
-            return TransferAsmJSMappedBuffer(cx, args, oldBuffer, newByteLength);
-# endif
-
-        // Since we try to realloc below, only allow stealing malloc'd buffers.
-        // If !hasMallocedContents, stealContents will malloc a copy which we
-        // can then realloc.
-        bool steal = oldBuffer->hasMallocedContents();
-        auto stolenContents = ArrayBufferObject::stealContents(cx, oldBuffer, steal);
-        if (!stolenContents)
-            return false;
-
-        UniquePtr<uint8_t, JS::FreePolicy> oldData(stolenContents.data());
-        if (newByteLength > oldByteLength) {
-            // In theory, realloc+memset(0) can be optimized to avoid touching
-            // any pages (by using OS page mapping tricks). However, in
-            // practice, we don't seem to get this optimization in Firefox with
-            // jemalloc so calloc+memcpy are faster.
-            newData.reset(cx->runtime()->pod_callocCanGC<uint8_t>(newByteLength));
-            if (newData) {
-                memcpy(newData.get(), oldData.get(), oldByteLength);
-            } else {
-                // Try malloc before giving up since it might be able to succed
-                // by resizing oldData in-place.
-                newData.reset(cx->pod_realloc(oldData.get(), oldByteLength, newByteLength));
-                if (!newData)
-                    return false;
-                oldData.release();
-                memset(newData.get() + oldByteLength, 0, newByteLength - oldByteLength);
-            }
-        } else if (newByteLength < oldByteLength) {
-            newData.reset(cx->pod_realloc(oldData.get(), oldByteLength, newByteLength));
-            if (!newData)
-                return false;
-            oldData.release();
-        } else {
-            newData = Move(oldData);
-        }
-    }
-
-    RootedObject newBuffer(cx, JS_NewArrayBufferWithContents(cx, newByteLength, newData.get()));
-    if (!newBuffer)
-        return false;
-    newData.release();
-
-    args.rval().setObject(*newBuffer);
-    return true;
-}
-#endif  // defined(NIGHTLY_BUILD)
-
 /*
  * new ArrayBuffer(byteLength)
  */
 bool
 ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -506,18 +286,20 @@ ArrayBufferObject::neuterView(JSContext*
     // Notify compiled jit code that the base pointer has moved.
     MarkObjectStateChange(cx, view);
 }
 
 /* static */ bool
 ArrayBufferObject::neuter(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                           BufferContents newContents)
 {
-    if (buffer->isAsmJS() && !OnDetachAsmJSArrayBuffer(cx, buffer))
+    if (buffer->isAsmJS()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
         return false;
+    }
 
     // When neutering buffers where we don't know all views, the new data must
     // match the old data. All missing views are typed objects, which do not
     // expect their data to ever change.
     MOZ_ASSERT_IF(buffer->forInlineTypedObject(),
                   newContents.data() == buffer->dataPointer());
 
     // When neutering a buffer with typed object views, any jitcode which
@@ -737,16 +519,43 @@ ArrayBufferObject::dataPointer() const
 }
 
 SharedMem<uint8_t*>
 ArrayBufferObject::dataPointerShared() const
 {
     return SharedMem<uint8_t*>::unshared(getSlot(DATA_SLOT).toPrivate());
 }
 
+#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+static void
+ReleaseAsmJSMappedData(void* base)
+{
+    MOZ_ASSERT(uintptr_t(base) % AsmJSPageSize == 0);
+#  ifdef XP_WIN
+    VirtualFree(base, 0, MEM_RELEASE);
+#  else
+    munmap(base, AsmJSMappedSize);
+#   if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
+    // Tell Valgrind/Memcheck to recommence reporting accesses in the
+    // previously-inaccessible region.
+    if (AsmJSMappedSize > 0) {
+        VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, AsmJSMappedSize);
+    }
+#   endif
+#  endif
+    MemProfiler::RemoveNative(base);
+}
+#else
+static void
+ReleaseAsmJSMappedData(void* base)
+{
+    MOZ_CRASH("asm.js only uses mapped buffers when using signal-handler OOB checking");
+}
+#endif
+
 void
 ArrayBufferObject::releaseData(FreeOp* fop)
 {
     MOZ_ASSERT(ownsData());
 
     switch (bufferKind()) {
       case PLAIN:
       case ASMJS_MALLOCED:
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -197,17 +197,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     canUseSignalHandlers_(false),
     defaultFreeOp_(thisFromCtor()),
     debuggerMutations(0),
     securityCallbacks(const_cast<JSSecurityCallbacks*>(&NullSecurityCallbacks)),
     DOMcallbacks(nullptr),
     destroyPrincipals(nullptr),
     readPrincipals(nullptr),
     errorReporter(nullptr),
-    linkedWasmModules(nullptr),
     propertyRemovals(0),
 #if !EXPOSE_INTL_API
     thousandsSeparator(0),
     decimalSeparator(0),
     numGrouping(0),
 #endif
     mathCache_(nullptr),
     activeCompilations_(0),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1185,19 +1185,16 @@ struct JSRuntime : public JS::shadow::Ru
     JSReadPrincipalsOp readPrincipals;
 
     /* Optional error reporter. */
     JSErrorReporter     errorReporter;
 
     /* AsmJSCache callbacks are runtime-wide. */
     JS::AsmJSCacheOps   asmJSCacheOps;
 
-    /* Head of the linked list of linked wasm modules. */
-    js::wasm::Module*   linkedWasmModules;
-
     /*
      * The propertyRemovals counter is incremented for every JSObject::clear,
      * and for each JSObject::remove method call that frees a slot in the given
      * object. See js_NativeGet and js_NativeSet in jsobj.cpp.
      */
     uint32_t            propertyRemovals;
 
 #if !EXPOSE_INTL_API