Bug 1487475 - Baldr: save custom sections separately of bytecode (r=yury)
authorLuke Wagner <luke@mozilla.com>
Tue, 18 Sep 2018 20:20:22 -0500
changeset 492924 1185e88c0067afd38237b65bfcd353d00a04162a
parent 492923 6493ea25b80c14f42598b3d5e74de46a79c156ee
child 492925 e03b6055ab323548f101d930b53830e0524539e0
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyury
bugs1487475
milestone64.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 1487475 - Baldr: save custom sections separately of bytecode (r=yury) Currently, custom sections and names are stored as offsets into the bytecode which is stored in the wasm::Module. This patch removes this last dependency on bytecode in the wasm::Module by instead storing custom section payloads directly. Names are still stored as offsets, but offsets into the name custom section, not the bytecode. While the wasm::Module necessarily keeps all custom sections alive, wasm::Code only needs the names custom section for the purpose of producing names. Thus, custom section payloads are ref-counted so that wasm::Code can hold a special reference to the names custom section.
js/src/jit-test/tests/debug/wasm-binary-sources.js
js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
js/src/jsapi.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/MemoryMetrics.cpp
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmBuiltins.cpp
js/src/wasm/WasmCode.cpp
js/src/wasm/WasmCode.h
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmCompile.h
js/src/wasm/WasmDebug.cpp
js/src/wasm/WasmDebug.h
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmInstance.h
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmJS.h
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmModule.h
js/src/wasm/WasmRealm.cpp
js/src/wasm/WasmTypes.cpp
js/src/wasm/WasmTypes.h
js/src/wasm/WasmValidate.cpp
js/src/wasm/WasmValidate.h
--- a/js/src/jit-test/tests/debug/wasm-binary-sources.js
+++ b/js/src/jit-test/tests/debug/wasm-binary-sources.js
@@ -27,11 +27,11 @@ assertThrowsInstanceOf(() => source.bina
 dbg.allowWasmBinarySource = true;
 
 g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
 assertEq(s.format, "wasm");
 
 var source2 = s.source;
 
 // The text is predefined if wasm binary sources are enabled.
-assertEq(source2.text, '[wasm]');
+assertEq(source2.text, '[debugger missing wasm binary-to-text conversion]');
 // The binary contains Uint8Array which is equal to wasm bytecode;
 arraysEqual(source2.binary, wasmTextToBinary('(module (func) (export "" 0))'));
--- a/js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
+++ b/js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
@@ -61,14 +61,14 @@ assertEq(gotScript.source.sourceMapURL, 
 // The sourceMappingURL section is present
 g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(toWasm('(module (func) (export "a" 0))', 'http://example.org/test')));`);
 assertEq(gotScript.format, "wasm");
 assertEq(gotScript.source.sourceMapURL, 'http://example.org/test');
 
 // The sourceMapURL is read-only for wasm
 assertThrowsInstanceOf(() => gotScript.source.sourceMapURL = 'foo', Error);
 
-// The sourceMappingURL section is present, but is not available when wasm
+// The sourceMappingURL section is present, and is still available when wasm
 // binary source is disabled.
 dbg.allowWasmBinarySource = false;
 g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(toWasm('(module (func) (export "a" 0))', 'http://example.org/test2')));`);
 assertEq(gotScript.format, "wasm");
-assertEq(gotScript.source.sourceMapURL, null);
+assertEq(gotScript.source.sourceMapURL, 'http://example.org/test2');
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -7002,17 +7002,17 @@ JS::IsWasmModuleObject(HandleObject obj)
     }
     return unwrapped->is<WasmModuleObject>();
 }
 
 JS_PUBLIC_API(RefPtr<JS::WasmModule>)
 JS::GetWasmModule(HandleObject obj)
 {
     MOZ_ASSERT(JS::IsWasmModuleObject(obj));
-    return &CheckedUnwrap(obj)->as<WasmModuleObject>().module();
+    return const_cast<wasm::Module*>(&CheckedUnwrap(obj)->as<WasmModuleObject>().module());
 }
 
 JS_PUBLIC_API(RefPtr<JS::WasmModule>)
 JS::DeserializeWasmModule(PRFileDesc* bytecode, UniqueChars filename, unsigned line)
 {
     return wasm::DeserializeModule(bytecode, std::move(filename), line);
 }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6574,17 +6574,17 @@ enum class MailboxTag {
 
 struct SharedObjectMailbox
 {
     union Value {
         struct {
             SharedArrayRawBuffer* buffer;
             uint32_t              length;
         } sarb;
-        wasm::Module*             module;
+        const wasm::Module*       module;
     };
 
     SharedObjectMailbox() : tag(MailboxTag::Empty) {}
 
     MailboxTag tag;
     Value      val;
 };
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2780,17 +2780,17 @@ UpdateExecutionObservabilityOfScriptsInZ
     // Iterate through all wasm instances to find ones that need to be updated.
     for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
         for (wasm::Instance* instance : r->wasm.instances()) {
             if (!instance->debugEnabled()) {
                 continue;
             }
 
             bool enableTrap = observing == Debugger::IsObserving::Observing;
-            instance->ensureEnterFrameTrapsState(cx, enableTrap);
+            instance->debug().ensureEnterFrameTrapsState(cx, enableTrap);
         }
     }
 
     return true;
 }
 
 /* static */ bool
 Debugger::updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
@@ -5926,22 +5926,23 @@ struct DebuggerScriptGetLineCountMatcher
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
-        uint32_t result;
-        if (!wasmInstance->instance().debug().totalSourceLines(cx_, &result)) {
-            return false;
-        }
-        totalLines = double(result);
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        if (instance.debugEnabled()) {
+            totalLines = double(instance.debug().totalSourceLines());
+        } else {
+            totalLines = 0;
+        }
         return true;
     }
 };
 
 static bool
 DebuggerScript_getLineCount(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get lineCount)", args, obj, referent);
@@ -6385,25 +6386,26 @@ class DebuggerScriptGetOffsetLocationMat
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> instance) {
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        if (!instance.debugEnabled()) {
+            JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
+            return false;
+        }
+
         size_t lineno;
         size_t column;
-        bool found;
-        if (!instance->instance().debug().getOffsetLocation(cx_, offset_, &found, &lineno, &column)) {
-            return false;
-        }
-
-        if (!found) {
+        if (!instance.debug().getOffsetLocation(offset_, &lineno, &column)) {
             JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
             return false;
         }
 
         result_.set(NewBuiltinClassInstance<PlainObject>(cx_));
         if (!result_) {
             return false;
         }
@@ -6691,19 +6693,21 @@ class DebuggerScriptGetAllColumnOffsetsM
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> instance) {
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+
         Vector<wasm::ExprLoc> offsets(cx_);
-        if (!instance->instance().debug().getAllColumnOffsets(cx_, &offsets)) {
+        if (instance.debugEnabled() && !instance.debug().getAllColumnOffsets(cx_, &offsets)) {
             return false;
         }
 
         result_.set(NewDenseEmptyArray(cx_));
         if (!result_) {
             return false;
         }
 
@@ -6780,19 +6784,21 @@ class DebuggerScriptGetLineOffsetsMatche
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> instance) {
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+
         Vector<uint32_t> offsets(cx_);
-        if (!instance->instance().debug().getLineOffsets(cx_, lineno_, &offsets)) {
+        if (instance.debugEnabled() && !instance.debug().getLineOffsets(cx_, lineno_, &offsets)) {
             return false;
         }
 
         result_.set(NewDenseEmptyArray(cx_));
         if (!result_) {
             return false;
         }
 
@@ -7103,17 +7109,17 @@ struct DebuggerScriptSetBreakpointMatche
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
     ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
         wasm::Instance& instance = wasmInstance->instance();
-        if (!instance.debug().hasBreakpointTrapAtOffset(offset_)) {
+        if (!instance.debugEnabled() || !instance.debug().hasBreakpointTrapAtOffset(offset_)) {
             JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
             return false;
         }
         WasmBreakpointSite* site = instance.debug().getOrCreateBreakpointSite(cx_, offset_);
         if (!site) {
             return false;
         }
         site->inc(cx_->runtime()->defaultFreeOp());
@@ -7211,18 +7217,22 @@ class DebuggerScriptClearBreakpointMatch
     }
     ReturnType match(Handle<LazyScript*> lazyScript) {
         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
         if (!script) {
             return false;
         }
         return match(script);
     }
-    ReturnType match(Handle<WasmInstanceObject*> instance) {
-        return instance->instance().debug().clearBreakpointsIn(cx_, instance, dbg_, handler_);
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        if (!instance.debugEnabled()) {
+            return true;
+        }
+        return instance.debug().clearBreakpointsIn(cx_, instanceObj, dbg_, handler_);
     }
 };
 
 
 static bool
 DebuggerScript_clearBreakpoint(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "clearBreakpoint", args, obj, referent);
@@ -7648,23 +7658,25 @@ class DebuggerSourceGetTextMatcher
 
         if (ss->isFunctionBody()) {
             return ss->functionBodyString(cx_);
         }
 
         return ss->substring(cx_, 0, ss->length());
     }
 
-    ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
-        if (wasmInstance->instance().debug().maybeBytecode() &&
-            wasmInstance->instance().debug().binarySource())
-        {
-            return NewStringCopyZ<CanGC>(cx_, "[wasm]");
-        }
-        return wasmInstance->instance().debug().createText(cx_);
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        const char* msg;
+        if (!instance.debugEnabled()) {
+            msg = "Restart with developer tools open to view WebAssembly source.";
+        } else {
+            msg = "[debugger missing wasm binary-to-text conversion]";
+        }
+        return NewStringCopyZ<CanGC>(cx_, msg);
     }
 };
 
 static bool
 DebuggerSource_getText(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get text)", args, obj, referent);
     Value textv = obj->getReservedSlot(JSSLOT_DEBUGSOURCE_TEXT);
@@ -7691,32 +7703,32 @@ DebuggerSource_getBinary(JSContext* cx, 
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get binary)", args, obj, referent);
 
     if (!referent.is<WasmInstanceObject*>()) {
         ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, args.thisv(), nullptr,
                          "a wasm source");
         return false;
     }
 
-    RootedWasmInstanceObject wasmInstance(cx, referent.as<WasmInstanceObject*>());
-    if (!wasmInstance->instance().debug().binarySource()) {
+    RootedWasmInstanceObject instanceObj(cx, referent.as<WasmInstanceObject*>());
+    wasm::Instance& instance = instanceObj->instance();
+
+    if (!instance.debugEnabled() || !instance.debug().binarySource()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_DEBUG_NO_BINARY_SOURCE);
         return false;
     }
 
-    auto bytecode = wasmInstance->instance().debug().maybeBytecode();
-    size_t arrLength = bytecode ? bytecode->length() : 0;
-    RootedObject arr(cx, JS_NewUint8Array(cx, arrLength));
+    const wasm::Bytes& bytecode = instance.debug().bytecode();
+    RootedObject arr(cx, JS_NewUint8Array(cx, bytecode.length()));
     if (!arr) {
         return false;
     }
-    if (bytecode) {
-        memcpy(arr->as<TypedArrayObject>().viewDataUnshared(), bytecode->begin(), arrLength);
-    }
+
+    memcpy(arr->as<TypedArrayObject>().viewDataUnshared(), bytecode.begin(), bytecode.length());
 
     args.rval().setObject(*arr);
     return true;
 }
 
 class DebuggerSourceGetURLMatcher
 {
     JSContext* cx_;
@@ -7730,28 +7742,18 @@ class DebuggerSourceGetURLMatcher
         ScriptSource* ss = sourceObject->source();
         MOZ_ASSERT(ss);
         if (ss->filename()) {
             JSString* str = NewStringCopyZ<CanGC>(cx_, ss->filename());
             return Some(str);
         }
         return Nothing();
     }
-    ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
-        if (wasmInstance->instance().metadata().filenameIsURL) {
-            JSString* str = NewStringCopyZ<CanGC>(cx_, wasmInstance->instance().metadata().filename.get());
-            if (!str) {
-                return Nothing();
-            }
-            return Some(str);
-        }
-        if (JSString* str = wasmInstance->instance().debug().debugDisplayURL(cx_)) {
-            return Some(str);
-        }
-        return Nothing();
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        return Some(instanceObj->instance().createDisplayURL(cx_));
     }
 };
 
 static bool
 DebuggerSource_getURL(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, referent);
 
@@ -8004,27 +8006,28 @@ class DebuggerSourceGetSourceMapURLMatch
         }
         JSString* str = JS_NewUCStringCopyZ(cx_, ss->sourceMapURL());
         if (!str) {
             return false;
         }
         result_.set(str);
         return true;
     }
-    ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
-        // sourceMapURL is not available if debugger was not in
-        // allowWasmBinarySource mode.
-        if (!wasmInstance->instance().debug().binarySource()) {
+    ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
+        wasm::Instance& instance = instanceObj->instance();
+        if (!instance.debugEnabled()) {
             result_.set(nullptr);
             return true;
         }
+
         RootedString str(cx_);
-        if (!wasmInstance->instance().debug().getSourceMappingURL(cx_, &str)) {
-            return false;
-        }
+        if (!instance.debug().getSourceMappingURL(cx_, &str)) {
+            return false;
+        }
+
         result_.set(str);
         return true;
     }
 };
 
 static bool
 DebuggerSource_getSourceMapURL(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -463,17 +463,17 @@ StatsCellCallback(JSRuntime* rt, void* d
         JS::ClassInfo info;        // This zeroes all the sizes.
         info.objectsGCHeap += thingSize;
 
         obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
 
         // These classes require special handling due to shared resources which
         // we must be careful not to report twice.
         if (obj->is<WasmModuleObject>()) {
-            wasm::Module& module = obj->as<WasmModuleObject>().module();
+            const wasm::Module& module = obj->as<WasmModuleObject>().module();
             if (ScriptSource* ss = module.metadata().maybeScriptSource()) {
                 CollectScriptSourceStats<granularity>(closure, ss);
             }
             module.addSizeOfMisc(rtStats->mallocSizeOf_,
                                  &closure->wasmSeenMetadata,
                                  &closure->wasmSeenBytes,
                                  &closure->wasmSeenCode,
                                  &info.objectsNonHeapCodeWasm,
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -362,19 +362,17 @@ struct js::AsmJSMetadata : Metadata, Asm
         return scriptSource.get()->mutedErrors();
     }
     const char16_t* displayURL() const override {
         return scriptSource.get()->hasDisplayURL() ? scriptSource.get()->displayURL() : nullptr;
     }
     ScriptSource* maybeScriptSource() const override {
         return scriptSource.get();
     }
-    bool getFuncName(NameContext ctx, const Bytes* maybeBytecode, uint32_t funcIndex,
-                     UTF8Bytes* name) const override
-    {
+    bool getFuncName(NameContext ctx, uint32_t funcIndex, UTF8Bytes* name) const override {
         const char* p = asmJSFuncNames[funcIndex].get();
         if (!p) {
             return true;
         }
         return name->append(p, strlen(p));
     }
 
     AsmJSMetadataCacheablePod& pod() { return *this; }
@@ -2399,17 +2397,17 @@ IsLiteralInt(ModuleValidator& m, ParseNo
     return IsNumericLiteral(m, pn) &&
            IsLiteralInt(ExtractNumericLiteral(m, pn), u32);
 }
 
 /*****************************************************************************/
 
 namespace {
 
-typedef Vector<PropertyName*, 4, SystemAllocPolicy> NameVector;
+typedef Vector<PropertyName*, 4, SystemAllocPolicy> LabelVector;
 
 // Encapsulates the building of an asm bytecode function from an asm.js function
 // source code, packing the asm.js code into the asm bytecode form that can
 // be decoded and compiled with a FunctionCompiler.
 class MOZ_STACK_CLASS FunctionValidator
 {
   public:
     struct Local
@@ -2529,29 +2527,29 @@ class MOZ_STACK_CLASS FunctionValidator
                encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
                breakableStack_.append(blockDepth_++);
     }
     bool popBreakableBlock() {
         MOZ_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
         return encoder().writeOp(Op::End);
     }
 
-    bool pushUnbreakableBlock(const NameVector* labels = nullptr) {
+    bool pushUnbreakableBlock(const LabelVector* labels = nullptr) {
         if (labels) {
             for (PropertyName* label : *labels) {
                 if (!breakLabels_.putNew(label, blockDepth_)) {
                     return false;
                 }
             }
         }
         blockDepth_++;
         return encoder().writeOp(Op::Block) &&
                encoder().writeFixedU8(uint8_t(ExprType::Void));
     }
-    bool popUnbreakableBlock(const NameVector* labels = nullptr) {
+    bool popUnbreakableBlock(const LabelVector* labels = nullptr) {
         if (labels) {
             for (PropertyName* label : *labels) {
                 removeLabel(label, &breakLabels_);
             }
         }
         --blockDepth_;
         return encoder().writeOp(Op::End);
     }
@@ -2617,30 +2615,30 @@ class MOZ_STACK_CLASS FunctionValidator
     }
     bool writeUnlabeledBreakOrContinue(bool isBreak) {
         return writeBr(isBreak? breakableStack_.back() : continuableStack_.back());
     }
     bool writeContinue() {
         return writeBr(continuableStack_.back());
     }
 
-    bool addLabels(const NameVector& labels, uint32_t relativeBreakDepth,
+    bool addLabels(const LabelVector& labels, uint32_t relativeBreakDepth,
                    uint32_t relativeContinueDepth)
     {
         for (PropertyName* label : labels) {
             if (!breakLabels_.putNew(label, blockDepth_ + relativeBreakDepth)) {
                 return false;
             }
             if (!continueLabels_.putNew(label, blockDepth_ + relativeContinueDepth)) {
                 return false;
             }
         }
         return true;
     }
-    void removeLabels(const NameVector& labels) {
+    void removeLabels(const LabelVector& labels) {
         for (PropertyName* label : labels) {
             removeLabel(label, &breakLabels_);
             removeLabel(label, &continueLabels_);
         }
     }
     bool writeLabeledBreakOrContinue(PropertyName* label, bool isBreak) {
         LabelMap& map = isBreak ? breakLabels_ : continueLabels_;
         if (LabelMap::Ptr p = map.lookup(label)) {
@@ -5166,17 +5164,17 @@ CheckLoopConditionOnEntry(FunctionValida
     if (!f.writeBreakIf()) {
         return false;
     }
 
     return true;
 }
 
 static bool
-CheckWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
+CheckWhile(FunctionValidator& f, ParseNode* whileStmt, const LabelVector* labels = nullptr)
 {
     MOZ_ASSERT(whileStmt->isKind(ParseNodeKind::While));
     ParseNode* cond = BinaryLeft(whileStmt);
     ParseNode* body = BinaryRight(whileStmt);
 
     // A while loop `while(#cond) #body` is equivalent to:
     // (block $after_loop
     //    (loop $top
@@ -5208,17 +5206,17 @@ CheckWhile(FunctionValidator& f, ParseNo
     }
     if (labels) {
         f.removeLabels(*labels);
     }
     return true;
 }
 
 static bool
-CheckFor(FunctionValidator& f, ParseNode* forStmt, const NameVector* labels = nullptr)
+CheckFor(FunctionValidator& f, ParseNode* forStmt, const LabelVector* labels = nullptr)
 {
     MOZ_ASSERT(forStmt->isKind(ParseNodeKind::For));
     ParseNode* forHead = BinaryLeft(forStmt);
     ParseNode* body = BinaryRight(forStmt);
 
     if (!forHead->isKind(ParseNodeKind::ForHead)) {
         return f.fail(forHead, "unsupported for-loop statement");
     }
@@ -5294,17 +5292,17 @@ CheckFor(FunctionValidator& f, ParseNode
     if (labels) {
         f.removeLabels(*labels);
     }
 
     return true;
 }
 
 static bool
-CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
+CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt, const LabelVector* labels = nullptr)
 {
     MOZ_ASSERT(whileStmt->isKind(ParseNodeKind::DoWhile));
     ParseNode* body = BinaryLeft(whileStmt);
     ParseNode* cond = BinaryRight(whileStmt);
 
     // A do-while loop `do { #body } while (#cond)` is equivalent to:
     // (block $after_loop           // depth X
     //   (loop $top                 // depth X+1
@@ -5351,24 +5349,24 @@ CheckDoWhile(FunctionValidator& f, Parse
         return false;
     }
     if (labels) {
         f.removeLabels(*labels);
     }
     return true;
 }
 
-static bool CheckStatementList(FunctionValidator& f, ParseNode*, const NameVector* = nullptr);
+static bool CheckStatementList(FunctionValidator& f, ParseNode*, const LabelVector* = nullptr);
 
 static bool
 CheckLabel(FunctionValidator& f, ParseNode* labeledStmt)
 {
     MOZ_ASSERT(labeledStmt->isKind(ParseNodeKind::Label));
 
-    NameVector labels;
+    LabelVector labels;
     ParseNode* innermost = labeledStmt;
     do {
         if (!labels.append(LabeledStatementLabel(innermost))) {
             return false;
         }
         innermost = LabeledStatementStatement(innermost);
     } while (innermost->getKind() == ParseNodeKind::Label);
 
@@ -5746,17 +5744,17 @@ CheckReturn(FunctionValidator& f, ParseN
     if (!f.encoder().writeOp(Op::Return)) {
         return false;
     }
 
     return true;
 }
 
 static bool
-CheckStatementList(FunctionValidator& f, ParseNode* stmtList, const NameVector* labels /*= nullptr */)
+CheckStatementList(FunctionValidator& f, ParseNode* stmtList, const LabelVector* labels /*= nullptr */)
 {
     MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
 
     if (!f.pushUnbreakableBlock(labels)) {
         return false;
     }
 
     for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) {
@@ -6625,17 +6623,17 @@ GetImports(JSContext* cx, const AsmJSMet
             return false;
         }
     }
 
     return true;
 }
 
 static bool
-TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
+TryInstantiate(JSContext* cx, CallArgs args, const Module& module, const AsmJSMetadata& metadata,
                MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
 {
     HandleValue globalVal = args.get(0);
     HandleValue importVal = args.get(1);
     HandleValue bufferVal = args.get(2);
 
     RootedArrayBufferObjectMaybeShared buffer(cx);
     RootedWasmMemoryObject memory(cx);
@@ -6729,32 +6727,32 @@ HandleInstantiationFailure(JSContext* cx
         return false;
     }
 
     // Call the function we just recompiled.
     args.setCallee(ObjectValue(*fun));
     return InternalCallOrConstruct(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
 }
 
-static Module&
+static const Module&
 AsmJSModuleFunctionToModule(JSFunction* fun)
 {
     MOZ_ASSERT(IsAsmJSModule(fun));
     const Value& v = fun->getExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT);
     return v.toObject().as<WasmModuleObject>().module();
 }
 
 // Implements the semantics of an asm.js module function that has been successfully validated.
 bool
 js::InstantiateAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSFunction* callee = &args.callee().as<JSFunction>();
-    Module& module = AsmJSModuleFunctionToModule(callee);
+    const Module& module = AsmJSModuleFunctionToModule(callee);
     const AsmJSMetadata& metadata = module.metadata().asAsmJS();
 
     RootedWasmInstanceObject instanceObj(cx);
     RootedObject exportObj(cx);
     if (!TryInstantiate(cx, args, module, metadata, &instanceObj, &exportObj)) {
         // Link-time validation checks failed, so reparse the entire asm.js
         // module from scratch to get normal interpreted bytecode which we can
         // simply Invoke. Very slow.
@@ -7146,17 +7144,18 @@ struct ScopedCacheEntryOpenedForRead
             cx->asmJSCacheOps().closeEntryForRead(serializedSize, memory, handle);
         }
     }
 };
 
 } // unnamed namespace
 
 static JS::AsmJSCacheResult
-StoreAsmJSModuleInCache(AsmJSParser& parser, Module& module, const LinkData& linkData, JSContext* cx)
+StoreAsmJSModuleInCache(AsmJSParser& parser, const Module& module, const LinkData& linkData,
+                        JSContext* cx)
 {
     ModuleCharsForStore moduleChars;
     if (!moduleChars.init(parser)) {
         return JS::AsmJSCache_InternalError;
     }
 
     MOZ_RELEASE_ASSERT(module.bytecode().length() == 0);
 
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -84,17 +84,17 @@ WasmHandleDebugTrap()
     const CallSite* site = code.lookupCallSite(fp->returnAddress);
     MOZ_ASSERT(site);
 
     // Advance to the actual trapping frame.
     fp = fp->callerFP;
     DebugFrame* debugFrame = DebugFrame::from(fp);
 
     if (site->kind() == CallSite::EnterFrame) {
-        if (!instance->enterFrameTrapsEnabled()) {
+        if (!instance->debug().enterFrameTrapsEnabled()) {
             return true;
         }
         debugFrame->setIsDebuggee();
         debugFrame->observe(cx);
         ResumeMode mode = Debugger::onEnterFrame(cx, debugFrame);
         if (mode == ResumeMode::Return) {
             // Ignoring forced return (ResumeMode::Return) -- changing code execution
             // order is not yet implemented in the wasm baseline.
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -276,17 +276,17 @@ StaticallyUnlink(uint8_t* base, const Li
 static bool
 AppendToString(const char* str, UTF8Bytes* bytes)
 {
     return bytes->append(str, strlen(str)) && bytes->append('\0');
 }
 #endif
 
 static void
-SendCodeRangesToProfiler(const ModuleSegment& ms, const Bytes& bytecode, const Metadata& metadata,
+SendCodeRangesToProfiler(const ModuleSegment& ms, const Metadata& metadata,
                          const CodeRangeVector& codeRanges)
 {
     bool enabled = false;
 #ifdef JS_ION_PERF
     enabled |= PerfFuncEnabled();
 #endif
 #ifdef MOZ_VTUNE
     enabled |= vtune::IsProfilingActive();
@@ -299,17 +299,17 @@ SendCodeRangesToProfiler(const ModuleSeg
         if (!codeRange.hasFuncIndex()) {
             continue;
         }
 
         uintptr_t start = uintptr_t(ms.base() + codeRange.begin());
         uintptr_t size = codeRange.end() - codeRange.begin();
 
         UTF8Bytes name;
-        if (!metadata.getFuncNameStandalone(&bytecode, codeRange.funcIndex(), &name)) {
+        if (!metadata.getFuncNameStandalone(codeRange.funcIndex(), &name)) {
             return;
         }
 
         // Avoid "unused" warnings
         (void)start;
         (void)size;
 
 #ifdef JS_ION_PERF
@@ -399,33 +399,32 @@ ModuleSegment::create(Tier tier, const B
 
     memcpy(codeBytes.get(), unlinkedBytes.begin(), codeLength);
 
     return js::MakeUnique<ModuleSegment>(tier, std::move(codeBytes), codeLength, linkData);
 }
 
 bool
 ModuleSegment::initialize(const CodeTier& codeTier,
-                          const ShareableBytes& bytecode,
                           const LinkData& linkData,
                           const Metadata& metadata,
                           const MetadataTier& metadataTier)
 {
     if (!StaticallyLink(*this, linkData)) {
         return false;
     }
 
     ExecutableAllocator::cacheFlush(base(), RoundupCodeLength(length()));
 
     // Reprotect the whole region to avoid having separate RW and RX mappings.
     if (!ExecutableAllocator::makeExecutable(base(), RoundupCodeLength(length()))) {
         return false;
     }
 
-    SendCodeRangesToProfiler(*this, bytecode.bytes, metadata, metadataTier.codeRanges);
+    SendCodeRangesToProfiler(*this, metadata, metadataTier.codeRanges);
 
     // See comments in CodeSegment::initialize() for why this must be last.
     return CodeSegment::initialize(codeTier);
 }
 
 size_t
 ModuleSegment::serializedSize() const
 {
@@ -971,66 +970,65 @@ MetadataTier::clone(const MetadataTier& 
 
 size_t
 Metadata::serializedSize() const
 {
     return sizeof(pod()) +
            SerializedVectorSize(funcTypeIds) +
            SerializedPodVectorSize(globals) +
            SerializedPodVectorSize(tables) +
+           sizeof(moduleName) +
            SerializedPodVectorSize(funcNames) +
-           SerializedPodVectorSize(customSections) +
            filename.serializedSize() +
            sourceMapURL.serializedSize();
 }
 
-size_t
-Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
-{
-    return SizeOfVectorExcludingThis(funcTypeIds, mallocSizeOf) +
-           globals.sizeOfExcludingThis(mallocSizeOf) +
-           tables.sizeOfExcludingThis(mallocSizeOf) +
-           funcNames.sizeOfExcludingThis(mallocSizeOf) +
-           customSections.sizeOfExcludingThis(mallocSizeOf) +
-           filename.sizeOfExcludingThis(mallocSizeOf) +
-           sourceMapURL.sizeOfExcludingThis(mallocSizeOf);
-}
-
 uint8_t*
 Metadata::serialize(uint8_t* cursor) const
 {
     MOZ_ASSERT(!debugEnabled && debugFuncArgTypes.empty() && debugFuncReturnTypes.empty());
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializeVector(cursor, funcTypeIds);
     cursor = SerializePodVector(cursor, globals);
     cursor = SerializePodVector(cursor, tables);
+    cursor = WriteBytes(cursor, &moduleName, sizeof(moduleName));
     cursor = SerializePodVector(cursor, funcNames);
-    cursor = SerializePodVector(cursor, customSections);
     cursor = filename.serialize(cursor);
     cursor = sourceMapURL.serialize(cursor);
     return cursor;
 }
 
 /* static */ const uint8_t*
 Metadata::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
     (cursor = DeserializeVector(cursor, &funcTypeIds)) &&
     (cursor = DeserializePodVector(cursor, &globals)) &&
     (cursor = DeserializePodVector(cursor, &tables)) &&
+    (cursor = ReadBytes(cursor, &moduleName, sizeof(moduleName))) &&
     (cursor = DeserializePodVector(cursor, &funcNames)) &&
-    (cursor = DeserializePodVector(cursor, &customSections)) &&
     (cursor = filename.deserialize(cursor)) &&
     (cursor = sourceMapURL.deserialize(cursor));
     debugEnabled = false;
     debugFuncArgTypes.clear();
     debugFuncReturnTypes.clear();
     return cursor;
 }
 
+size_t
+Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return SizeOfVectorExcludingThis(funcTypeIds, mallocSizeOf) +
+           globals.sizeOfExcludingThis(mallocSizeOf) +
+           tables.sizeOfExcludingThis(mallocSizeOf) +
+           funcNames.sizeOfExcludingThis(mallocSizeOf) +
+           filename.sizeOfExcludingThis(mallocSizeOf) +
+           sourceMapURL.sizeOfExcludingThis(mallocSizeOf);
+}
+
 struct ProjectFuncIndex
 {
     const FuncExportVector& funcExports;
     explicit ProjectFuncIndex(const FuncExportVector& funcExports)
       : funcExports(funcExports)
     {}
     uint32_t operator[](size_t index) const {
         return funcExports[index].funcIndex();
@@ -1052,21 +1050,21 @@ MetadataTier::lookupFuncExport(uint32_t 
 
 const FuncExport&
 MetadataTier::lookupFuncExport(uint32_t funcIndex, size_t* funcExportIndex) const
 {
     return const_cast<MetadataTier*>(this)->lookupFuncExport(funcIndex, funcExportIndex);
 }
 
 static bool
-AppendNameInBytecode(const Bytes* maybeBytecode, const NameInBytecode& name, UTF8Bytes* bytes)
+AppendName(const Bytes& namePayload, const Name& name, UTF8Bytes* bytes)
 {
-    MOZ_RELEASE_ASSERT(name.offset <= maybeBytecode->length());
-    MOZ_RELEASE_ASSERT(name.length <= maybeBytecode->length() - name.offset);
-    return bytes->append((const char*)maybeBytecode->begin() + name.offset, name.length);
+    MOZ_RELEASE_ASSERT(name.offsetInNamePayload <= namePayload.length());
+    MOZ_RELEASE_ASSERT(name.length <= namePayload.length() - name.offsetInNamePayload);
+    return bytes->append((const char*)namePayload.begin() + name.offsetInNamePayload, name.length);
 }
 
 static bool
 AppendFunctionIndexName(uint32_t funcIndex, UTF8Bytes* bytes)
 {
     const char beforeFuncIndex[] = "wasm-function[";
     const char afterFuncIndex[] = "]";
 
@@ -1075,30 +1073,29 @@ AppendFunctionIndexName(uint32_t funcInd
     MOZ_ASSERT(funcIndexStr);
 
     return bytes->append(beforeFuncIndex, strlen(beforeFuncIndex)) &&
            bytes->append(funcIndexStr, strlen(funcIndexStr)) &&
            bytes->append(afterFuncIndex, strlen(afterFuncIndex));
 }
 
 bool
-Metadata::getFuncName(NameContext ctx, const Bytes* maybeBytecode, uint32_t funcIndex,
-                      UTF8Bytes* name) const
+Metadata::getFuncName(NameContext ctx, uint32_t funcIndex, UTF8Bytes* name) const
 {
     if (moduleName && moduleName->length != 0) {
-        if (!AppendNameInBytecode(maybeBytecode, *moduleName, name)) {
+        if (!AppendName(namePayload->bytes, *moduleName, name)) {
             return false;
         }
         if (!name->append('.')) {
             return false;
         }
     }
 
     if (funcIndex < funcNames.length() && funcNames[funcIndex].length != 0) {
-        return AppendNameInBytecode(maybeBytecode, funcNames[funcIndex], name);
+        return AppendName(namePayload->bytes, funcNames[funcIndex], name);
     }
 
     if (ctx == NameContext::BeforeLocation) {
         return true;
     }
 
     return AppendFunctionIndexName(funcIndex, name);
 }
@@ -1217,36 +1214,35 @@ JumpTables::init(CompileMode mode, const
 Code::Code(UniqueCodeTier tier1, const Metadata& metadata, JumpTables&& maybeJumpTables)
   : tier1_(std::move(tier1)),
     metadata_(&metadata),
     profilingLabels_(mutexid::WasmCodeProfilingLabels, CacheableCharsVector()),
     jumpTables_(std::move(maybeJumpTables))
 {}
 
 bool
-Code::initialize(const ShareableBytes& bytecode, const LinkData& linkData)
+Code::initialize(const LinkData& linkData)
 {
     MOZ_ASSERT(!initialized());
 
-    if (!tier1_->initialize(*this, bytecode, linkData, *metadata_)) {
+    if (!tier1_->initialize(*this, linkData, *metadata_)) {
         return false;
     }
 
     MOZ_ASSERT(initialized());
     return true;
 }
 
 bool
-Code::setTier2(UniqueCodeTier tier2, const ShareableBytes& bytecode,
-               const LinkData& linkData) const
+Code::setTier2(UniqueCodeTier tier2, const LinkData& linkData) const
 {
     MOZ_RELEASE_ASSERT(!hasTier2());
     MOZ_RELEASE_ASSERT(tier2->tier() == Tier::Ion && tier1_->tier() == Tier::Baseline);
 
-    if (!tier2->initialize(*this, bytecode, linkData, *metadata_)) {
+    if (!tier2->initialize(*this, linkData, *metadata_)) {
         return false;
     }
 
     tier2_ = std::move(tier2);
 
     return true;
 }
 
@@ -1410,17 +1406,17 @@ Code::lookupTrap(void* pc, Trap* trapOut
     return false;
 }
 
 // When enabled, generate profiling labels for every name in funcNames_ that is
 // the name of some Function CodeRange. This involves malloc() so do it now
 // since, once we start sampling, we'll be in a signal-handing context where we
 // cannot malloc.
 void
-Code::ensureProfilingLabels(const Bytes* maybeBytecode, bool profilingEnabled) const
+Code::ensureProfilingLabels(bool profilingEnabled) const
 {
     auto labels = profilingLabels_.lock();
 
     if (!profilingEnabled) {
         labels->clear();
         return;
     }
 
@@ -1436,17 +1432,17 @@ Code::ensureProfilingLabels(const Bytes*
             continue;
         }
 
         ToCStringBuf cbuf;
         const char* bytecodeStr = NumberToCString(nullptr, &cbuf, codeRange.funcLineOrBytecode());
         MOZ_ASSERT(bytecodeStr);
 
         UTF8Bytes name;
-        if (!metadata().getFuncNameStandalone(maybeBytecode, codeRange.funcIndex(), &name)) {
+        if (!metadata().getFuncNameStandalone(codeRange.funcIndex(), &name)) {
             return;
         }
         if (!name.append(" (", 2)) {
             return;
         }
 
         if (const char* filename = metadata().filename.get()) {
             if (!name.append(filename, strlen(filename))) {
@@ -1512,27 +1508,26 @@ Code::addSizeOfMiscIfNotSeen(MallocSizeO
 
     for (auto t : tiers()) {
         codeTier(t).addSizeOfMisc(mallocSizeOf, code, data);
     }
 }
 
 bool
 CodeTier::initialize(const Code& code,
-                     const ShareableBytes& bytecode,
                      const LinkData& linkData,
                      const Metadata& metadata)
 {
     MOZ_ASSERT(!initialized());
     code_ = &code;
 
     MOZ_ASSERT(lazyStubs_.lock()->empty());
 
     // See comments in CodeSegment::initialize() for why this must be last.
-    if (!segment_->initialize(*this, bytecode, linkData, metadata, *metadata_)) {
+    if (!segment_->initialize(*this, linkData, metadata, *metadata_)) {
         return false;
     }
 
     MOZ_ASSERT(initialized());
     return true;
 }
 
 size_t
@@ -1549,17 +1544,16 @@ Code::serialize(uint8_t* cursor, const L
 
     cursor = metadata().serialize(cursor);
     cursor = codeTier(Tier::Serialized).serialize(cursor, linkData);
     return cursor;
 }
 
 /* static */ const uint8_t*
 Code::deserialize(const uint8_t* cursor,
-                  const ShareableBytes& bytecode,
                   const LinkData& linkData,
                   Metadata& metadata,
                   SharedCode* out)
 {
     cursor = metadata.deserialize(cursor);
     if (!cursor) {
         return nullptr;
     }
@@ -1571,15 +1565,15 @@ Code::deserialize(const uint8_t* cursor,
     }
 
     JumpTables jumpTables;
     if (!jumpTables.init(CompileMode::Once, codeTier->segment(), codeTier->metadata().codeRanges)) {
         return nullptr;
     }
 
     MutableCode code = js_new<Code>(std::move(codeTier), metadata, std::move(jumpTables));
-    if (!code || !code->initialize(bytecode, linkData)) {
+    if (!code || !code->initialize(linkData)) {
         return nullptr;
     }
 
     *out = code;
     return cursor;
 }
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -173,17 +173,16 @@ class ModuleSegment : public CodeSegment
     static UniqueModuleSegment create(Tier tier,
                                       jit::MacroAssembler& masm,
                                       const LinkData& linkData);
     static UniqueModuleSegment create(Tier tier,
                                       const Bytes& unlinkedBytes,
                                       const LinkData& linkData);
 
     bool initialize(const CodeTier& codeTier,
-                    const ShareableBytes& bytecode,
                     const LinkData& linkData,
                     const Metadata& metadata,
                     const MetadataTier& metadataTier);
 
     Tier tier() const { return tier_; }
 
     // Pointers to stubs to which PC is redirected from the signal-handler.
 
@@ -322,54 +321,16 @@ typedef Vector<FuncImport, 0, SystemAllo
 
 enum class MemoryUsage
 {
     None = false,
     Unshared = 1,
     Shared = 2
 };
 
-// NameInBytecode represents a name that is embedded in the wasm bytecode.
-// The presence of NameInBytecode implies that bytecode has been kept.
-
-struct NameInBytecode
-{
-    uint32_t offset;
-    uint32_t length;
-
-    NameInBytecode()
-      : offset(UINT32_MAX), length(0)
-    {}
-    NameInBytecode(uint32_t offset, uint32_t length)
-      : offset(offset), length(length)
-    {}
-};
-
-typedef Vector<NameInBytecode, 0, SystemAllocPolicy> NameInBytecodeVector;
-
-// CustomSection represents a custom section in the bytecode which can be
-// extracted via Module.customSections. The (offset, length) pair does not
-// include the custom section name.
-
-struct CustomSection
-{
-    NameInBytecode name;
-    uint32_t offset;
-    uint32_t length;
-
-    CustomSection() = default;
-    CustomSection(NameInBytecode name, uint32_t offset, uint32_t length)
-      : name(name), offset(offset), length(length)
-    {}
-};
-
-typedef Vector<CustomSection, 0, SystemAllocPolicy> CustomSectionVector;
-typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncArgTypesVector;
-typedef Vector<ExprType, 0, SystemAllocPolicy> FuncReturnTypesVector;
-
 // Metadata holds all the data that is needed to describe compiled wasm code
 // at runtime (as opposed to data that is only used to statically link or
 // instantiate a module).
 //
 // Metadata is built incrementally by ModuleGenerator and then shared immutably
 // between modules.
 //
 // The Metadata structure is split into tier-invariant and tier-variant parts;
@@ -381,41 +342,48 @@ struct MetadataCacheablePod
 {
     ModuleKind            kind;
     MemoryUsage           memoryUsage;
     HasGcTypes            temporaryGcTypesConfigured;
     uint32_t              minMemoryLength;
     uint32_t              globalDataLength;
     Maybe<uint32_t>       maxMemoryLength;
     Maybe<uint32_t>       startFuncIndex;
-    Maybe<NameInBytecode> moduleName;
+    Maybe<uint32_t>       nameCustomSectionIndex;
     bool                  filenameIsURL;
 
     explicit MetadataCacheablePod(ModuleKind kind)
       : kind(kind),
         memoryUsage(MemoryUsage::None),
         temporaryGcTypesConfigured(HasGcTypes::False),
         minMemoryLength(0),
         globalDataLength(0),
         filenameIsURL(false)
     {}
 };
 
 typedef uint8_t ModuleHash[8];
+typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncArgTypesVector;
+typedef Vector<ExprType, 0, SystemAllocPolicy> FuncReturnTypesVector;
 
 struct Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod
 {
     FuncTypeWithIdVector  funcTypeIds;
     GlobalDescVector      globals;
     TableDescVector       tables;
-    NameInBytecodeVector  funcNames;
-    CustomSectionVector   customSections;
     CacheableChars        filename;
     CacheableChars        sourceMapURL;
 
+    // namePayload points at the name section's CustomSection::payload so that
+    // the Names (which are use payload-relative offsets) can be used
+    // independently of the Module without duplicating the name section.
+    SharedBytes           namePayload;
+    Maybe<Name>           moduleName;
+    NameVector            funcNames;
+
     // Debug-enabled code is not serialized.
     bool                  debugEnabled;
     FuncArgTypesVector    debugFuncArgTypes;
     FuncReturnTypesVector debugFuncReturnTypes;
     ModuleHash            debugHash;
 
     explicit Metadata(ModuleKind kind = ModuleKind::Wasm)
       : MetadataCacheablePod(kind),
@@ -454,24 +422,23 @@ struct Metadata : public ShareableBase<M
 
     // The Developer-Facing Display Conventions section of the WebAssembly Web
     // API spec defines two cases for displaying a wasm function name:
     //  1. the function name stands alone
     //  2. the function name precedes the location
 
     enum NameContext { Standalone, BeforeLocation };
 
-    virtual bool getFuncName(NameContext ctx, const Bytes* maybeBytecode, uint32_t funcIndex,
-                             UTF8Bytes* name) const;
+    virtual bool getFuncName(NameContext ctx, uint32_t funcIndex, UTF8Bytes* name) const;
 
-    bool getFuncNameStandalone(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const {
-        return getFuncName(NameContext::Standalone, maybeBytecode, funcIndex, name);
+    bool getFuncNameStandalone(uint32_t funcIndex, UTF8Bytes* name) const {
+        return getFuncName(NameContext::Standalone, funcIndex, name);
     }
-    bool getFuncNameBeforeLocation(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const {
-        return getFuncName(NameContext::BeforeLocation, maybeBytecode, funcIndex, name);
+    bool getFuncNameBeforeLocation(uint32_t funcIndex, UTF8Bytes* name) const {
+        return getFuncName(NameContext::BeforeLocation, funcIndex, name);
     }
 
     WASM_DECLARE_SERIALIZABLE_VIRTUAL(Metadata);
 };
 
 typedef RefPtr<Metadata> MutableMetadata;
 typedef RefPtr<const Metadata> SharedMetadata;
 
@@ -629,21 +596,17 @@ class CodeTier
     CodeTier(UniqueMetadataTier metadata, UniqueModuleSegment segment)
       : code_(nullptr),
         metadata_(std::move(metadata)),
         segment_(std::move(segment)),
         lazyStubs_(mutexForTier(segment_->tier()))
     {}
 
     bool initialized() const { return !!code_ && segment_->initialized(); }
-
-    bool initialize(const Code& code,
-                    const ShareableBytes& bytecode,
-                    const LinkData& linkData,
-                    const Metadata& metadata);
+    bool initialize(const Code& code, const LinkData& linkData, const Metadata& metadata);
 
     Tier tier() const { return segment_->tier(); }
     const ExclusiveData<LazyStubTier>& lazyStubs() const { return lazyStubs_; }
     const MetadataTier& metadata() const { return *metadata_.get(); }
     const ModuleSegment& segment() const { return *segment_.get(); }
     const Code& code() const { MOZ_ASSERT(initialized()); return *code_; }
 
     const CodeRange* lookupRange(const void* pc) const;
@@ -725,27 +688,26 @@ class Code : public ShareableBase<Code>
     JumpTables                          jumpTables_;
 
   public:
     Code(UniqueCodeTier tier1,
          const Metadata& metadata,
          JumpTables&& maybeJumpTables);
     bool initialized() const { return tier1_->initialized(); }
 
-    bool initialize(const ShareableBytes& bytecode, const LinkData& linkData);
+    bool initialize(const LinkData& linkData);
 
     void setTieringEntry(size_t i, void* target) const { jumpTables_.setTieringEntry(i, target); }
     void** tieringJumpTable() const { return jumpTables_.tiering(); }
 
     void setJitEntry(size_t i, void* target) const { jumpTables_.setJitEntry(i, target); }
     void** getAddressOfJitEntry(size_t i) const { return jumpTables_.getAddressOfJitEntry(i); }
     uint32_t getFuncIndex(JSFunction* fun) const;
 
-    bool setTier2(UniqueCodeTier tier2, const ShareableBytes& bytecode,
-                  const LinkData& linkData) const;
+    bool setTier2(UniqueCodeTier tier2, const LinkData& linkData) const;
     void commitTier2() const;
 
     bool hasTier2() const { return hasTier2_; }
     Tiers tiers() const;
     bool hasTier(Tier t) const;
 
     Tier stableTier() const;    // This is stable during a run
     Tier bestTier() const;      // This may transition from Baseline -> Ion at any time
@@ -765,17 +727,17 @@ class Code : public ShareableBase<Code>
     const CallSite* lookupCallSite(void* returnAddress) const;
     const CodeRange* lookupFuncRange(void* pc) const;
     bool containsCodePC(const void* pc) const;
     bool lookupTrap(void* pc, Trap* trap, BytecodeOffset* bytecode) const;
 
     // To save memory, profilingLabels_ are generated lazily when profiling mode
     // is enabled.
 
-    void ensureProfilingLabels(const Bytes* maybeBytecode, bool profilingEnabled) const;
+    void ensureProfilingLabels(bool profilingEnabled) const;
     const char* profilingLabel(uint32_t funcIndex) const;
 
     // about:memory reporting:
 
     void addSizeOfMiscIfNotSeen(MallocSizeOf mallocSizeOf,
                                 Metadata::SeenSet* seenMetadata,
                                 Code::SeenSet* seenCode,
                                 size_t* code,
@@ -783,17 +745,16 @@ class Code : public ShareableBase<Code>
 
     // A Code object is serialized as the length and bytes of the machine code
     // after statically unlinking it; the Code is then later recreated from the
     // machine code and other parts.
 
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const;
     static const uint8_t* deserialize(const uint8_t* cursor,
-                                      const ShareableBytes& bytecode,
                                       const LinkData& linkData,
                                       Metadata& metadata,
                                       SharedCode* code);
 };
 
 } // namespace wasm
 } // namespace js
 
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -538,17 +538,17 @@ wasm::CompileBuffer(const CompileArgs& a
     if (!DecodeModuleTail(d, &env, mg.deferredValidationState())) {
         return nullptr;
     }
 
     return mg.finishModule(bytecode);
 }
 
 void
-wasm::CompileTier2(const CompileArgs& args, Module& module, Atomic<bool>* cancelled)
+wasm::CompileTier2(const CompileArgs& args, const Module& module, Atomic<bool>* cancelled)
 {
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
     UniqueChars error;
     Decoder d(module.bytecode().bytes, 0, &error);
 
     HasGcTypes gcTypesConfigured = HasGcTypes::False; // No Ion support yet
     CompilerEnvironment compilerEnv(CompileMode::Tier2, Tier::Ion, DebugEnabled::False,
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -87,17 +87,17 @@ SharedModule
 CompileBuffer(const CompileArgs& args,
               const ShareableBytes& bytecode,
               UniqueChars* error,
               UniqueCharsVector* warnings);
 
 // Attempt to compile the second tier of the given wasm::Module.
 
 void
-CompileTier2(const CompileArgs& args, Module& module, Atomic<bool>* cancelled);
+CompileTier2(const CompileArgs& args, const Module& module, Atomic<bool>* cancelled);
 
 // Compile the given WebAssembly module which has been broken into three
 // partitions:
 //  - envBytes contains a complete ModuleEnvironment that has already been
 //    copied in from the stream.
 //  - codeBytes is pre-sized to hold the complete code section when the stream
 //    completes.
 //  - The range [codeBytes.begin(), codeStreamEnd) contains the bytes currently
--- a/js/src/wasm/WasmDebug.cpp
+++ b/js/src/wasm/WasmDebug.cpp
@@ -19,163 +19,109 @@
 #include "wasm/WasmDebug.h"
 
 #include "mozilla/BinarySearch.h"
 
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/MacroAssembler.h"
-#include "util/StringBuffer.h"
-#include "util/Text.h"
 #include "vm/Debugger.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::BinarySearchIf;
 
-DebugState::DebugState(SharedCode code,
-                       const ShareableBytes* maybeBytecode,
-                       bool binarySource)
-  : code_(std::move(code)),
-    maybeBytecode_(maybeBytecode),
+DebugState::DebugState(const Code& code, const Module& module, bool binarySource)
+  : code_(&code),
+    module_(&module),
     binarySource_(binarySource),
+    enterFrameTrapsEnabled_(false),
     enterAndLeaveFrameTrapsCounter_(0)
 {
-    MOZ_ASSERT_IF(debugEnabled(), maybeBytecode);
+    MOZ_ASSERT(code.metadata().debugEnabled);
 }
 
-const char enabledMessage[] =
-    "Restart with developer tools open to view WebAssembly source.";
-
-const char noBinarySource[] =
-    "Configure the debugger to display WebAssembly bytecode.";
-
-const char notGeneratedMessage[] =
-    "WebAssembly text generation was disabled.";
-
 static const uint32_t DefaultBinarySourceColumnNumber = 1;
 
 static const CallSite*
 SlowCallSiteSearchByOffset(const MetadataTier& metadata, uint32_t offset)
 {
     for (const CallSite& callSite : metadata.callSites) {
         if (callSite.lineOrBytecode() == offset && callSite.kind() == CallSiteDesc::Breakpoint) {
             return &callSite;
         }
     }
     return nullptr;
 }
 
-JSString*
-DebugState::createText(JSContext* cx)
-{
-    StringBuffer buffer(cx);
-    if (!maybeBytecode_) {
-        if (!buffer.append(enabledMessage)) {
-            return nullptr;
-        }
-    } else if (binarySource_) {
-        if (!buffer.append(notGeneratedMessage)) {
-            return nullptr;
-        }
-    } else {
-        if (!buffer.append(noBinarySource)) {
-            return nullptr;
-        }
-    }
-    return buffer.finishString();
-}
-
 bool
 DebugState::getLineOffsets(JSContext* cx, size_t lineno, Vector<uint32_t>* offsets)
 {
-    if (!debugEnabled()) {
-        return true;
-    }
     if (!binarySource_) {
         return true;
     }
     const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno);
     if (callsite && !offsets->append(lineno)) {
         return false;
     }
     return true;
 }
 
 bool
 DebugState::getAllColumnOffsets(JSContext* cx, Vector<ExprLoc>* offsets)
 {
-    if (!metadata().debugEnabled) {
-        return true;
-    }
     if (!binarySource_) {
         return true;
     }
     for (const CallSite& callSite : metadata(Tier::Debug).callSites) {
         if (callSite.kind() != CallSite::Breakpoint) {
             continue;
         }
         uint32_t offset = callSite.lineOrBytecode();
         if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset)) {
             return false;
         }
     }
     return true;
 }
 
 bool
-DebugState::getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_t* lineno, size_t* column)
+DebugState::getOffsetLocation(uint32_t offset, size_t* lineno, size_t* column)
 {
-    *found = false;
-    if (!debugEnabled()) {
-        return true;
-    }
     if (!binarySource_) {
-        return true;
+        return false;
     }
     if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset)) {
-        return true; // offset was not found
+        return false;
     }
-    *found = true;
     *lineno = offset;
     *column = DefaultBinarySourceColumnNumber;
     return true;
 }
 
-bool
-DebugState::totalSourceLines(JSContext* cx, uint32_t* count)
+uint32_t
+DebugState::totalSourceLines()
 {
-    *count = 0;
-    if (!debugEnabled()) {
-        return true;
-    }
-    if (!binarySource_) {
-        return true;
-    }
-    if (maybeBytecode_) {
-        *count = maybeBytecode_->length();
-    }
-    return true;
+    return binarySource_ ? bytecode().length() : 0;
 }
 
 bool
 DebugState::stepModeEnabled(uint32_t funcIndex) const
 {
     return stepModeCounters_.lookup(funcIndex).found();
 }
 
 bool
 DebugState::incrementStepModeCount(JSContext* cx, uint32_t funcIndex)
 {
-    MOZ_ASSERT(debugEnabled());
     const CodeRange& codeRange = codeRanges(Tier::Debug)[funcToCodeRangeIndex(funcIndex)];
     MOZ_ASSERT(codeRange.isFunction());
 
     StepModeCounters::AddPtr p = stepModeCounters_.lookupForAdd(funcIndex);
     if (p) {
         MOZ_ASSERT(p->value() > 0);
         p->value()++;
         return true;
@@ -199,17 +145,16 @@ DebugState::incrementStepModeCount(JSCon
         }
     }
     return true;
 }
 
 bool
 DebugState::decrementStepModeCount(FreeOp* fop, uint32_t funcIndex)
 {
-    MOZ_ASSERT(debugEnabled());
     const CodeRange& codeRange = codeRanges(Tier::Debug)[funcToCodeRangeIndex(funcIndex)];
     MOZ_ASSERT(codeRange.isFunction());
 
     MOZ_ASSERT(!stepModeCounters_.empty());
     StepModeCounters::Ptr p = stepModeCounters_.lookup(funcIndex);
     MOZ_ASSERT(p);
     if (--p->value()) {
         return true;
@@ -232,26 +177,22 @@ DebugState::decrementStepModeCount(FreeO
         }
     }
     return true;
 }
 
 bool
 DebugState::hasBreakpointTrapAtOffset(uint32_t offset)
 {
-    if (!debugEnabled()) {
-        return false;
-    }
     return SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset);
 }
 
 void
 DebugState::toggleBreakpointTrap(JSRuntime* rt, uint32_t offset, bool enabled)
 {
-    MOZ_ASSERT(debugEnabled());
     const CallSite* callSite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset);
     if (!callSite) {
         return;
     }
     size_t debugTrapOffset = callSite->returnAddressOffset();
 
     const ModuleSegment& codeSegment = code_->segment(Tier::Debug);
     const CodeRange* codeRange = code_->lookupFuncRange(codeSegment.base() + debugTrapOffset);
@@ -359,17 +300,16 @@ DebugState::toggleDebugTrap(uint32_t off
     } else {
         MacroAssembler::patchCallToNop(trap);
     }
 }
 
 void
 DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled)
 {
-    MOZ_ASSERT(debugEnabled());
     MOZ_ASSERT_IF(!enabled, enterAndLeaveFrameTrapsCounter_ > 0);
 
     bool wasEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
     if (enabled) {
         ++enterAndLeaveFrameTrapsCounter_;
     } else {
         --enterAndLeaveFrameTrapsCounter_;
     }
@@ -385,41 +325,49 @@ DebugState::adjustEnterAndLeaveFrameTrap
     for (const CallSite& callSite : callSites(Tier::Debug)) {
         if (callSite.kind() != CallSite::EnterFrame && callSite.kind() != CallSite::LeaveFrame) {
             continue;
         }
         toggleDebugTrap(callSite.returnAddressOffset(), stillEnabled);
     }
 }
 
+void
+DebugState::ensureEnterFrameTrapsState(JSContext* cx, bool enabled)
+{
+    if (enterFrameTrapsEnabled_ == enabled) {
+        return;
+    }
+
+    adjustEnterAndLeaveFrameTrapsState(cx, enabled);
+
+    enterFrameTrapsEnabled_ = enabled;
+}
+
 bool
 DebugState::debugGetLocalTypes(uint32_t funcIndex, ValTypeVector* locals, size_t* argsLength)
 {
-    MOZ_ASSERT(debugEnabled());
-
     const ValTypeVector& args = metadata().debugFuncArgTypes[funcIndex];
     *argsLength = args.length();
     if (!locals->appendAll(args)) {
         return false;
     }
 
     // Decode local var types from wasm binary function body.
     const CodeRange& range = codeRanges(Tier::Debug)[funcToCodeRangeIndex(funcIndex)];
     // In wasm, the Code points to the function start via funcLineOrBytecode.
-    MOZ_ASSERT(!metadata().isAsmJS() && maybeBytecode_);
     size_t offsetInModule = range.funcLineOrBytecode();
-    Decoder d(maybeBytecode_->begin() + offsetInModule,  maybeBytecode_->end(),
-              offsetInModule, /* error = */ nullptr);
+    Decoder d(bytecode().begin() + offsetInModule,  bytecode().end(), offsetInModule,
+              /* error = */ nullptr);
     return DecodeValidatedLocalEntries(d, locals);
 }
 
 ExprType
 DebugState::debugGetResultType(uint32_t funcIndex)
 {
-    MOZ_ASSERT(debugEnabled());
     return metadata().debugFuncReturnTypes[funcIndex];
 }
 
 bool
 DebugState::getGlobal(Instance& instance, uint32_t globalIndex, MutableHandleValue vp)
 {
     const GlobalDesc& global = metadata().globals[globalIndex];
 
@@ -470,87 +418,31 @@ DebugState::getGlobal(Instance& instance
       }
       default:
         MOZ_CRASH("Global variable type");
         break;
     }
     return true;
 }
 
-
-JSString*
-DebugState::debugDisplayURL(JSContext* cx) const
-{
-    // Build wasm module URL from following parts:
-    // - "wasm:" as protocol;
-    // - URI encoded filename from metadata (if can be encoded), plus ":";
-    // - 64-bit hash of the module bytes (as hex dump).
-
-    js::StringBuffer result(cx);
-    if (!result.append("wasm:")) {
-        return nullptr;
-    }
-
-    if (const char* filename = metadata().filename.get()) {
-        // EncodeURI returns false due to invalid chars or OOM -- fail only
-        // during OOM.
-        JSString* filenamePrefix = EncodeURI(cx, filename, strlen(filename));
-        if (!filenamePrefix) {
-            if (cx->isThrowingOutOfMemory()) {
-                return nullptr;
-            }
-
-            MOZ_ASSERT(!cx->isThrowingOverRecursed());
-            cx->clearPendingException(); // ignore invalid URI
-        } else if (!result.append(filenamePrefix)) {
-            return nullptr;
-        }
-    }
-
-    if (metadata().debugEnabled) {
-        if (!result.append(":")) {
-            return nullptr;
-        }
-
-        const ModuleHash& hash = metadata().debugHash;
-        for (size_t i = 0; i < sizeof(ModuleHash); i++) {
-            char digit1 = hash[i] / 16, digit2 = hash[i] % 16;
-            if (!result.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10))) {
-                return nullptr;
-            }
-            if (!result.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10))) {
-                return nullptr;
-            }
-        }
-    }
-
-    return result.finishString();
-}
-
 bool
 DebugState::getSourceMappingURL(JSContext* cx, MutableHandleString result) const
 {
     result.set(nullptr);
-    if (!maybeBytecode_) {
-        return true;
-    }
 
-    for (const CustomSection& customSection : metadata().customSections) {
-        const NameInBytecode& sectionName = customSection.name;
-        if (strlen(SourceMappingURLSectionName) != sectionName.length ||
-            memcmp(SourceMappingURLSectionName, maybeBytecode_->begin() + sectionName.offset,
-                   sectionName.length) != 0)
+    for (const CustomSection& customSection : module_->customSections()) {
+        const Bytes& sectionName = customSection.name;
+        if (strlen(SourceMappingURLSectionName) != sectionName.length() ||
+            memcmp(SourceMappingURLSectionName, sectionName.begin(), sectionName.length()) != 0)
         {
             continue;
         }
 
         // Parse found "SourceMappingURL" custom section.
-        Decoder d(maybeBytecode_->begin() + customSection.offset,
-                  maybeBytecode_->begin() + customSection.offset + customSection.length,
-                  customSection.offset,
+        Decoder d(customSection.payload->begin(), customSection.payload->end(), 0,
                   /* error = */ nullptr);
         uint32_t nchars;
         if (!d.readVarU32(&nchars)) {
             return true; // ignoring invalid section data
         }
         const uint8_t* chars;
         if (!d.readBytes(nchars, &chars) || d.currentPosition() != d.end()) {
             return true; // ignoring invalid section data
@@ -582,12 +474,10 @@ void
 DebugState::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                           Metadata::SeenSet* seenMetadata,
                           ShareableBytes::SeenSet* seenBytes,
                           Code::SeenSet* seenCode,
                           size_t* code,
                           size_t* data) const
 {
     code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
-    if (maybeBytecode_) {
-        *data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
-    }
+    module_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data);
 }
--- a/js/src/wasm/WasmDebug.h
+++ b/js/src/wasm/WasmDebug.h
@@ -15,17 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_debug_h
 #define wasm_debug_h
 
 #include "js/HashTable.h"
-#include "wasm/WasmCode.h"
+#include "wasm/WasmModule.h"
 #include "wasm/WasmTypes.h"
 
 namespace js {
 
 class Debugger;
 class WasmBreakpoint;
 class WasmBreakpointSite;
 class WasmInstanceObject;
@@ -50,48 +50,48 @@ struct ExprLoc
 
 typedef HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> StepModeCounters;
 typedef HashMap<uint32_t, WasmBreakpointSite*, DefaultHasher<uint32_t>, SystemAllocPolicy>
     WasmBreakpointSiteMap;
 
 class DebugState
 {
     const SharedCode         code_;
-    const SharedBytes        maybeBytecode_;
+    const SharedModule       module_;
     bool                     binarySource_;
 
     // State maintained when debugging is enabled. In this case, the Code is
     // not actually shared, but is referenced uniquely by the instance that is
     // being debugged.
 
+    bool                     enterFrameTrapsEnabled_;
     uint32_t                 enterAndLeaveFrameTrapsCounter_;
     WasmBreakpointSiteMap    breakpointSites_;
     StepModeCounters         stepModeCounters_;
 
     void toggleDebugTrap(uint32_t offset, bool enabled);
 
   public:
-    DebugState(SharedCode code,
-               const ShareableBytes* maybeBytecode,
-               bool binarySource);
+    DebugState(const Code& code, const Module& module, bool binarySource);
 
-    const Bytes* maybeBytecode() const { return maybeBytecode_ ? &maybeBytecode_->bytes : nullptr; }
+    const Bytes& bytecode() const { return module_->bytecode().bytes; }
     bool binarySource() const { return binarySource_; }
 
-    JSString* createText(JSContext* cx);
     bool getLineOffsets(JSContext* cx, size_t lineno, Vector<uint32_t>* offsets);
     bool getAllColumnOffsets(JSContext* cx, Vector<ExprLoc>* offsets);
-    bool getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_t* lineno, size_t* column);
-    bool totalSourceLines(JSContext* cx, uint32_t* count);
+    bool getOffsetLocation(uint32_t offset, size_t* lineno, size_t* column);
+    uint32_t totalSourceLines();
 
     // The Code can track enter/leave frame events. Any such event triggers
     // debug trap. The enter/leave frame events enabled or disabled across
     // all functions.
 
     void adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled);
+    void ensureEnterFrameTrapsState(JSContext* cx, bool enabled);
+    bool enterFrameTrapsEnabled() const { return enterFrameTrapsEnabled_; }
 
     // When the Code is debugEnabled, individual breakpoints can be enabled or
     // disabled at instruction offsets.
 
     bool hasBreakpointTrapAtOffset(uint32_t offset);
     void toggleBreakpointTrap(JSRuntime* rt, uint32_t offset, bool enabled);
     WasmBreakpointSite* getOrCreateBreakpointSite(JSContext* cx, uint32_t offset);
     bool hasBreakpointSite(uint32_t offset);
@@ -108,24 +108,22 @@ class DebugState
     // Stack inspection helpers.
 
     bool debugGetLocalTypes(uint32_t funcIndex, ValTypeVector* locals, size_t* argsLength);
     ExprType debugGetResultType(uint32_t funcIndex);
     bool getGlobal(Instance& instance, uint32_t globalIndex, MutableHandleValue vp);
 
     // Debug URL helpers.
 
-    JSString* debugDisplayURL(JSContext* cx) const;
     bool getSourceMappingURL(JSContext* cx, MutableHandleString result) const;
 
     // Accessors for commonly used elements of linked structures.
 
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const Metadata& metadata() const { return code_->metadata(); }
-    bool debugEnabled() const { return metadata().debugEnabled; }
     const CodeRangeVector& codeRanges(Tier t) const { return metadata(t).codeRanges; }
     const CallSiteVector& callSites(Tier t) const { return metadata(t).callSites; }
 
     uint32_t funcToCodeRangeIndex(uint32_t funcIndex) const {
         return metadata(Tier::Debug).funcToCodeRange[funcIndex];
     }
 
     // about:memory reporting:
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -980,21 +980,21 @@ ModuleGenerator::finishMetadata(const Sh
 
     // Copy over data from the ModuleEnvironment.
 
     metadata_->memoryUsage = env_->memoryUsage;
     metadata_->temporaryGcTypesConfigured = env_->gcTypesConfigured;
     metadata_->minMemoryLength = env_->minMemoryLength;
     metadata_->maxMemoryLength = env_->maxMemoryLength;
     metadata_->startFuncIndex = env_->startFuncIndex;
-    metadata_->moduleName = env_->moduleName;
     metadata_->tables = std::move(env_->tables);
     metadata_->globals = std::move(env_->globals);
+    metadata_->nameCustomSectionIndex = env_->nameCustomSectionIndex;
+    metadata_->moduleName = env_->moduleName;
     metadata_->funcNames = std::move(env_->funcNames);
-    metadata_->customSections = std::move(env_->customSections);
 
     // Copy over additional debug information.
 
     if (env_->debugEnabled()) {
         metadata_->debugEnabled = true;
 
         const size_t numFuncTypes = env_->funcTypes.length();
         if (!metadata_->debugFuncArgTypes.resize(numFuncTypes)) {
@@ -1037,67 +1037,101 @@ ModuleGenerator::finishModule(const Shar
         return nullptr;
     }
 
     if (!finishMetadata(bytecode)) {
         return nullptr;
     }
 
     MutableCode code = js_new<Code>(std::move(codeTier), *metadata_, std::move(jumpTables));
-    if (!code || !code->initialize(bytecode, *linkData_)) {
+    if (!code || !code->initialize(*linkData_)) {
         return nullptr;
     }
 
     StructTypeVector structTypes;
     for (TypeDef& td : env_->types) {
         if (td.isStructType() && !structTypes.append(std::move(td.structType()))) {
             return nullptr;
         }
     }
 
+    // Copy over data from the Bytecode, which is going away at the end of
+    // compilation.
+
     DataSegmentVector dataSegments;
     if (!dataSegments.reserve(env_->dataSegments.length())) {
         return nullptr;
     }
     for (const DataSegmentEnv& srcSeg : env_->dataSegments) {
         MutableDataSegment dstSeg = js_new<DataSegment>(srcSeg);
         if (!dstSeg) {
             return nullptr;
         }
         if (!dstSeg->bytes.append(bytecode.begin() + srcSeg.bytecodeOffset, srcSeg.length)) {
             return nullptr;
         }
         dataSegments.infallibleAppend(std::move(dstSeg));
     }
 
+    CustomSectionVector customSections;
+    if (!customSections.reserve(env_->customSections.length())) {
+        return nullptr;
+    }
+    for (const CustomSectionEnv& srcSec : env_->customSections) {
+        CustomSection sec;
+        if (!sec.name.append(bytecode.begin() + srcSec.nameOffset, srcSec.nameLength)) {
+            return nullptr;
+        }
+        MutableBytes payload = js_new<ShareableBytes>();
+        if (!payload) {
+            return nullptr;
+        }
+        if (!payload->append(bytecode.begin() + srcSec.payloadOffset, srcSec.payloadLength)) {
+            return nullptr;
+        }
+        sec.payload = std::move(payload);
+        customSections.infallibleAppend(std::move(sec));
+    }
+
+    if (env_->nameCustomSectionIndex) {
+        metadata_->namePayload = customSections[*env_->nameCustomSectionIndex].payload;
+    }
+
+    // See Module debugCodeClaimed_ comments for why we need to make a separate
+    // debug copy.
+
     UniqueBytes debugUnlinkedCode;
     UniqueLinkData debugLinkData;
     if (env_->debugEnabled()) {
         MOZ_ASSERT(mode() == CompileMode::Once);
         MOZ_ASSERT(tier() == Tier::Debug);
 
         debugUnlinkedCode = js::MakeUnique<Bytes>();
         if (!debugUnlinkedCode || !debugUnlinkedCode->resize(masm_.bytesNeeded())) {
             return nullptr;
         }
 
         masm_.executableCopy(debugUnlinkedCode->begin(), /* flushICache = */ false);
 
         debugLinkData = std::move(linkData_);
     }
 
-    SharedModule module(js_new<Module>(*code,
-                                       std::move(env_->imports),
-                                       std::move(env_->exports),
-                                       std::move(structTypes),
-                                       std::move(dataSegments),
-                                       std::move(env_->elemSegments),
-                                       bytecode,
-                                       std::move(debugUnlinkedCode),
-                                       std::move(debugLinkData)));
+    // All the components are finished, so create the complete Module and start
+    // tier-2 compilation if requested.
+
+    MutableModule module = js_new<Module>(*code,
+                                          std::move(env_->imports),
+                                          std::move(env_->exports),
+                                          std::move(structTypes),
+                                          std::move(dataSegments),
+                                          std::move(env_->elemSegments),
+                                          std::move(customSections),
+                                          bytecode,
+                                          std::move(debugUnlinkedCode),
+                                          std::move(debugLinkData));
     if (!module) {
         return nullptr;
     }
 
     if (mode() == CompileMode::Tier1) {
         module->startTier2(*compileArgs_);
     }
 
@@ -1106,17 +1140,17 @@ ModuleGenerator::finishModule(const Shar
         MOZ_ASSERT(!env_->debugEnabled());
         *linkData = std::move(linkData_);
     }
 
     return module;
 }
 
 bool
-ModuleGenerator::finishTier2(Module& module)
+ModuleGenerator::finishTier2(const Module& module)
 {
     MOZ_ASSERT(mode() == CompileMode::Tier2);
     MOZ_ASSERT(tier() == Tier::Ion);
     MOZ_ASSERT(!env_->debugEnabled());
 
     if (cancelled_ && *cancelled_) {
         return false;
     }
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -227,17 +227,17 @@ class MOZ_STACK_CLASS ModuleGenerator
 
     MOZ_MUST_USE bool finishFuncDefs();
 
     // If env->mode is Once or Tier1, finishModule() must be called to generate
     // a new Module. Otherwise, if env->mode is Tier2, finishTier2() must be
     // called to augment the given Module with tier 2 code.
 
     SharedModule finishModule(const ShareableBytes& bytecode, UniqueLinkData* linkData = nullptr);
-    MOZ_MUST_USE bool finishTier2(Module& module);
+    MOZ_MUST_USE bool finishTier2(const Module& module);
 
     ExclusiveDeferredValidationState& deferredValidationState() {
         return deferredValidationState_;
     }
 };
 
 } // namespace wasm
 } // namespace js
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -18,16 +18,18 @@
 
 #include "wasm/WasmInstance.h"
 
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
 #include "jit/JitRealm.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "wasm/WasmBuiltins.h"
 #include "wasm/WasmModule.h"
 
 #include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
@@ -692,32 +694,33 @@ Instance::postBarrier(Instance* instance
     MOZ_ASSERT(location);
     TlsContext.get()->runtime()->gc.storeBuffer().putCell(location);
 }
 #endif // ENABLE_WASM_GC
 
 Instance::Instance(JSContext* cx,
                    Handle<WasmInstanceObject*> object,
                    SharedCode code,
-                   UniqueDebugState debug,
                    UniqueTlsData tlsDataIn,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
                    Handle<FunctionVector> funcImports,
                    HandleValVector globalImportValues,
-                   const WasmGlobalObjectVector& globalObjs)
+                   const WasmGlobalObjectVector& globalObjs,
+                   UniqueDebugState maybeDebug)
   : realm_(cx->realm()),
     object_(object),
     code_(code),
-    debug_(std::move(debug)),
     tlsData_(std::move(tlsDataIn)),
     memory_(memory),
     tables_(std::move(tables)),
-    enterFrameTrapsEnabled_(false)
+    maybeDebug_(std::move(maybeDebug))
 {
+    MOZ_ASSERT(!!maybeDebug_ == metadata().debugEnabled);
+
 #ifdef DEBUG
     for (auto t : code_->tiers()) {
         MOZ_ASSERT(funcImports.length() == metadata(t).funcImports.length());
     }
 #endif
     MOZ_ASSERT(tables_.length() == metadata().tables.length());
 
     tlsData()->memoryBase = memory ? memory->buffer().dataPointerEither().unwrap() : nullptr;
@@ -1140,27 +1143,27 @@ Instance::callExport(JSContext* cx, uint
 }
 
 JSAtom*
 Instance::getFuncDisplayAtom(JSContext* cx, uint32_t funcIndex) const
 {
     // The "display name" of a function is primarily shown in Error.stack which
     // also includes location, so use getFuncNameBeforeLocation.
     UTF8Bytes name;
-    if (!metadata().getFuncNameBeforeLocation(debug_->maybeBytecode(), funcIndex, &name)) {
+    if (!metadata().getFuncNameBeforeLocation(funcIndex, &name)) {
         return nullptr;
     }
 
     return AtomizeUTF8Chars(cx, name.begin(), name.length());
 }
 
 void
 Instance::ensureProfilingLabels(bool profilingEnabled) const
 {
-    return code_->ensureProfilingLabels(debug_->maybeBytecode(), profilingEnabled);
+    return code_->ensureProfilingLabels(profilingEnabled);
 }
 
 void
 Instance::onMovingGrowMemory(uint8_t* prevMemoryBase)
 {
     MOZ_ASSERT(!isAsmJS());
     MOZ_ASSERT(!memory_->isShared());
 
@@ -1186,25 +1189,73 @@ Instance::deoptimizeImportExit(uint32_t 
 {
     Tier t = code().bestTier();
     const FuncImport& fi = metadata(t).funcImports[funcImportIndex];
     FuncImportTls& import = funcImportTls(fi);
     import.code = codeBase(t) + fi.interpExitCodeOffset();
     import.baselineScript = nullptr;
 }
 
-void
-Instance::ensureEnterFrameTrapsState(JSContext* cx, bool enabled)
+JSString*
+Instance::createDisplayURL(JSContext* cx)
 {
-    if (enterFrameTrapsEnabled_ == enabled) {
-        return;
+    // In the best case, we simply have a URL, from a streaming compilation of a
+    // fetched Response.
+
+    if (metadata().filenameIsURL) {
+        return NewStringCopyZ<CanGC>(cx, metadata().filename.get());
+    }
+
+    // Otherwise, build wasm module URL from following parts:
+    // - "wasm:" as protocol;
+    // - URI encoded filename from metadata (if can be encoded), plus ":";
+    // - 64-bit hash of the module bytes (as hex dump).
+
+    StringBuffer result(cx);
+    if (!result.append("wasm:")) {
+        return nullptr;
     }
 
-    debug_->adjustEnterAndLeaveFrameTrapsState(cx, enabled);
-    enterFrameTrapsEnabled_ = enabled;
+    if (const char* filename = metadata().filename.get()) {
+        // EncodeURI returns false due to invalid chars or OOM -- fail only
+        // during OOM.
+        JSString* filenamePrefix = EncodeURI(cx, filename, strlen(filename));
+        if (!filenamePrefix) {
+            if (cx->isThrowingOutOfMemory()) {
+                return nullptr;
+            }
+
+            MOZ_ASSERT(!cx->isThrowingOverRecursed());
+            cx->clearPendingException();
+            return nullptr;
+        }
+
+        if (!result.append(filenamePrefix)) {
+            return nullptr;
+        }
+    }
+
+    if (metadata().debugEnabled) {
+        if (!result.append(":")) {
+            return nullptr;
+        }
+
+        const ModuleHash& hash = metadata().debugHash;
+        for (size_t i = 0; i < sizeof(ModuleHash); i++) {
+            char digit1 = hash[i] / 16, digit2 = hash[i] % 16;
+            if (!result.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10))) {
+                return nullptr;
+            }
+            if (!result.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10))) {
+                return nullptr;
+            }
+        }
+    }
+
+    return result.finishString();
 }
 
 void
 Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                         Metadata::SeenSet* seenMetadata,
                         ShareableBytes::SeenSet* seenBytes,
                         Code::SeenSet* seenCode,
                         Table::SeenSet* seenTables,
@@ -1212,11 +1263,14 @@ Instance::addSizeOfMisc(MallocSizeOf mal
                         size_t* data) const
 {
     *data += mallocSizeOf(this);
     *data += mallocSizeOf(tlsData_.get());
     for (const SharedTable& table : tables_) {
          *data += table->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenTables);
     }
 
-    debug_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data);
+    if (maybeDebug_) {
+        maybeDebug_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data);
+    }
+
     code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
 }
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -46,23 +46,22 @@ class Instance
     JS::Realm* const                realm_;
     ReadBarrieredWasmInstanceObject object_;
     jit::TrampolinePtr              jsJitArgsRectifier_;
     jit::TrampolinePtr              jsJitExceptionHandler_;
 #ifdef ENABLE_WASM_GC
     jit::TrampolinePtr              preBarrierCode_;
 #endif
     const SharedCode                code_;
-    const UniqueDebugState          debug_;
     const UniqueTlsData             tlsData_;
     GCPtrWasmMemoryObject           memory_;
-    SharedTableVector               tables_;
+    const SharedTableVector         tables_;
     DataSegmentVector               passiveDataSegments_;
     ElemSegmentVector               passiveElemSegments_;
-    bool                            enterFrameTrapsEnabled_;
+    const UniqueDebugState          maybeDebug_;
 
     // Internal helpers:
     const void** addressOfFuncTypeId(const FuncTypeIdDesc& funcTypeId) const;
     FuncImportTls& funcImportTls(const FuncImport& fi);
     TableTls& tableTls(const TableDesc& td) const;
 
     // Only WasmInstanceObject can call the private trace function.
     friend class js::WasmInstanceObject;
@@ -70,34 +69,34 @@ class Instance
 
     bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
                     MutableHandleValue rval);
 
   public:
     Instance(JSContext* cx,
              HandleWasmInstanceObject object,
              SharedCode code,
-             UniqueDebugState debug,
              UniqueTlsData tlsData,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports,
              HandleValVector globalImportValues,
-             const WasmGlobalObjectVector& globalObjs);
+             const WasmGlobalObjectVector& globalObjs,
+             UniqueDebugState maybeDebug);
     ~Instance();
     bool init(JSContext* cx,
               const DataSegmentVector& dataSegments,
               const ElemSegmentVector& elemSegments);
     void trace(JSTracer* trc);
 
     JS::Realm* realm() const { return realm_; }
     const Code& code() const { return *code_; }
     const CodeTier& code(Tier t) const { return code_->codeTier(t); }
-    DebugState& debug() { return *debug_; }
-    const DebugState& debug() const { return *debug_; }
+    bool debugEnabled() const { return !!maybeDebug_; }
+    DebugState& debug() { return *maybeDebug_; }
     const ModuleSegment& moduleSegment(Tier t) const { return code_->segment(t); }
     TlsData* tlsData() const { return tlsData_.get(); }
     uint8_t* globalData() const { return (uint8_t*)&tlsData_->globalArea; }
     uint8_t* codeBase(Tier t) const { return code_->segment(t).base(); }
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const Metadata& metadata() const { return code_->metadata(); }
     bool isAsmJS() const { return metadata().isAsmJS(); }
     const SharedTableVector& tables() const { return tables_; }
@@ -153,21 +152,19 @@ class Instance
     void onMovingGrowMemory(uint8_t* prevMemoryBase);
     void onMovingGrowTable();
 
     // Called to apply a single ElemSegment at a given offset, assuming
     // that all bounds validation has already been performed.
 
     void initElems(const ElemSegment& seg, uint32_t dstOffset, uint32_t srcOffset, uint32_t len);
 
-    // Debug support:
+    // Debugger support:
 
-    bool debugEnabled() const { return metadata().debugEnabled; }
-    bool enterFrameTrapsEnabled() const { return enterFrameTrapsEnabled_; }
-    void ensureEnterFrameTrapsState(JSContext* cx, bool enabled);
+    JSString* createDisplayURL(JSContext* cx);
 
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
                        Code::SeenSet* seenCode,
                        Table::SeenSet* seenTables,
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -593,29 +593,29 @@ const JSFunctionSpec WasmModuleObject::s
 
 /* static */ void
 WasmModuleObject::finalize(FreeOp* fop, JSObject* obj)
 {
     obj->as<WasmModuleObject>().module().Release();
 }
 
 static bool
-IsModuleObject(JSObject* obj, Module** module)
+IsModuleObject(JSObject* obj, const Module** module)
 {
     JSObject* unwrapped = CheckedUnwrap(obj);
     if (!unwrapped || !unwrapped->is<WasmModuleObject>()) {
         return false;
     }
 
     *module = &unwrapped->as<WasmModuleObject>().module();
     return true;
 }
 
 static bool
-GetModuleArg(JSContext* cx, CallArgs args, const char* name, Module** module)
+GetModuleArg(JSContext* cx, CallArgs args, const char* name, const Module** module)
 {
     if (!args.requireAtLeast(cx, name, 1)) {
         return false;
     }
 
     if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MOD_ARG);
         return false;
@@ -727,17 +727,17 @@ UTF8CharsToString(JSContext* cx, const c
     return NewStringCopyUTF8Z<CanGC>(cx, JS::ConstUTF8CharsZ(chars, strlen(chars)));
 }
 
 /* static */ bool
 WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    Module* module;
+    const Module* module;
     if (!GetModuleArg(cx, args, "WebAssembly.Module.imports", &module)) {
         return false;
     }
 
     KindNames names(cx);
     if (!InitKindNames(cx, &names)) {
         return false;
     }
@@ -801,17 +801,17 @@ WasmModuleObject::imports(JSContext* cx,
     return true;
 }
 
 /* static */ bool
 WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    Module* module;
+    const Module* module;
     if (!GetModuleArg(cx, args, "WebAssembly.Module.exports", &module)) {
         return false;
     }
 
     KindNames names(cx);
     if (!InitKindNames(cx, &names)) {
         return false;
     }
@@ -869,17 +869,17 @@ WasmModuleObject::exports(JSContext* cx,
     return true;
 }
 
 /* static */ bool
 WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    Module* module;
+    const Module* module;
     if (!GetModuleArg(cx, args, "WebAssembly.Module.customSections", &module)) {
         return false;
     }
 
     Vector<char, 8> name(cx);
     {
         RootedString str(cx, ToString(cx, args.get(1)));
         if (!str) {
@@ -893,58 +893,56 @@ WasmModuleObject::customSections(JSConte
 
         if (!name.initLengthUninitialized(JS::GetDeflatedUTF8StringLength(flat))) {
             return false;
         }
 
         JS::DeflateStringToUTF8Buffer(flat, RangedPtr<char>(name.begin(), name.length()));
     }
 
-    const uint8_t* bytecode = module->bytecode().begin();
-
     AutoValueVector elems(cx);
     RootedArrayBufferObject buf(cx);
-    for (const CustomSection& sec : module->metadata().customSections) {
-        if (name.length() != sec.name.length) {
+    for (const CustomSection& cs : module->customSections()) {
+        if (name.length() != cs.name.length()) {
             continue;
         }
-        if (memcmp(name.begin(), bytecode + sec.name.offset, name.length())) {
+        if (memcmp(name.begin(), cs.name.begin(), name.length())) {
             continue;
         }
 
-        buf = ArrayBufferObject::create(cx, sec.length);
+        buf = ArrayBufferObject::create(cx, cs.payload->length());
         if (!buf) {
             return false;
         }
 
-        memcpy(buf->dataPointer(), bytecode + sec.offset, sec.length);
+        memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length());
         if (!elems.append(ObjectValue(*buf))) {
             return false;
         }
     }
 
     JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
     if (!arr) {
         return false;
     }
 
     args.rval().setObject(*arr);
     return true;
 }
 
 /* static */ WasmModuleObject*
-WasmModuleObject::create(JSContext* cx, Module& module, HandleObject proto)
+WasmModuleObject::create(JSContext* cx, const Module& module, HandleObject proto)
 {
     AutoSetNewObjectMetadata metadata(cx);
     auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
     if (!obj) {
         return nullptr;
     }
 
-    obj->initReservedSlot(MODULE_SLOT, PrivateValue(&module));
+    obj->initReservedSlot(MODULE_SLOT, PrivateValue(const_cast<Module*>(&module)));
     module.AddRef();
     // We account for the first tier here; the second tier, if different, will be
     // accounted for separately when it's been compiled.
     cx->zone()->updateJitCodeMallocBytes(module.codeLength(module.code().stableTier()));
     return obj;
 }
 
 static bool
@@ -1054,21 +1052,21 @@ WasmModuleObject::construct(JSContext* c
     if (!moduleObj) {
         return false;
     }
 
     callArgs.rval().setObject(*moduleObj);
     return true;
 }
 
-Module&
+const Module&
 WasmModuleObject::module() const
 {
     MOZ_ASSERT(is<WasmModuleObject>());
-    return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate();
+    return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate();
 }
 
 // ============================================================================
 // WebAssembly.Instance class and methods
 
 const ClassOps WasmInstanceObject::classOps_ =
 {
     nullptr, /* addProperty */
@@ -1154,25 +1152,25 @@ WasmInstanceObject::trace(JSTracer* trc,
     }
 }
 
 /* static */ WasmInstanceObject*
 WasmInstanceObject::create(JSContext* cx,
                            SharedCode code,
                            const DataSegmentVector& dataSegments,
                            const ElemSegmentVector& elemSegments,
-                           UniqueDebugState debug,
                            UniqueTlsData tlsData,
                            HandleWasmMemoryObject memory,
                            SharedTableVector&& tables,
                            Handle<FunctionVector> funcImports,
                            const GlobalDescVector& globals,
                            HandleValVector globalImportValues,
                            const WasmGlobalObjectVector& globalObjs,
-                           HandleObject proto)
+                           HandleObject proto,
+                           UniqueDebugState maybeDebug)
 {
     UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>();
     if (!exports) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     UniquePtr<ScopeMap> scopes = js::MakeUnique<ScopeMap>(cx->zone());
@@ -1222,23 +1220,23 @@ WasmInstanceObject::create(JSContext* cx
     // The INSTANCE_SLOT may not be initialized if Instance allocation fails,
     // leading to an observable "newborn" state in tracing/finalization.
     MOZ_ASSERT(obj->isNewborn());
 
     // Root the Instance via WasmInstanceObject before any possible GC.
     auto* instance = cx->new_<Instance>(cx,
                                         obj,
                                         code,
-                                        std::move(debug),
                                         std::move(tlsData),
                                         memory,
                                         std::move(tables),
                                         funcImports,
                                         globalImportValues,
-                                        globalObjs);
+                                        globalObjs,
+                                        std::move(maybeDebug));
     if (!instance) {
         return nullptr;
     }
 
     obj->initReservedSlot(INSTANCE_SLOT, PrivateValue(instance));
     MOZ_ASSERT(!obj->isNewborn());
 
     if (!instance->init(cx, dataSegments, elemSegments)) {
@@ -1295,17 +1293,17 @@ WasmInstanceObject::construct(JSContext*
     if (!ThrowIfNotConstructing(cx, args, "Instance")) {
         return false;
     }
 
     if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1)) {
         return false;
     }
 
-    Module* module;
+    const Module* module;
     if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MOD_ARG);
         return false;
     }
 
     RootedObject importObj(cx);
     if (!GetImportArg(cx, args, &importObj)) {
         return false;
@@ -2681,17 +2679,17 @@ Reject(JSContext* cx, const CompileArgs&
         return false;
     }
 
     RootedValue rejectionValue(cx, ObjectValue(*errorObj));
     return PromiseObject::reject(cx, promise, rejectionValue);
 }
 
 static bool
-Resolve(JSContext* cx, Module& module, Handle<PromiseObject*> promise, bool instantiate,
+Resolve(JSContext* cx, const Module& module, Handle<PromiseObject*> promise, bool instantiate,
         HandleObject importObj, const UniqueCharsVector& warnings)
 {
     if (!ReportCompileWarnings(cx, warnings)) {
         return false;
     }
 
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
     RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
@@ -2874,17 +2872,17 @@ WebAssembly_instantiate(JSContext* cx, u
     CallArgs callArgs = CallArgsFromVp(argc, vp);
 
     RootedObject firstArg(cx);
     RootedObject importObj(cx);
     if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj)) {
         return RejectWithPendingException(cx, promise, callArgs);
     }
 
-    Module* module;
+    const Module* module;
     if (IsModuleObject(firstArg, &module)) {
         RootedWasmInstanceObject instanceObj(cx);
         if (!Instantiate(cx, *module, importObj, &instanceObj)) {
             return RejectWithPendingException(cx, promise, callArgs);
         }
 
         RootedValue resolutionValue(cx, ObjectValue(*instanceObj));
         if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -102,19 +102,19 @@ class WasmModuleObject : public NativeOb
     static const unsigned RESERVED_SLOTS = 1;
     static const Class class_;
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec static_methods[];
     static bool construct(JSContext*, unsigned, Value*);
 
     static WasmModuleObject* create(JSContext* cx,
-                                    wasm::Module& module,
+                                    const wasm::Module& module,
                                     HandleObject proto = nullptr);
-    wasm::Module& module() const;
+    const wasm::Module& module() const;
 };
 
 // The class of WebAssembly.Global.  This wraps a storage location, and there is
 // a per-agent one-to-one relationship between the WasmGlobalObject and the
 // storage location (the Cell) it wraps: if a module re-exports an imported
 // global, the imported and exported WasmGlobalObjects are the same, and if a
 // module exports a global twice, the two exported WasmGlobalObjects are the
 // same.
@@ -211,25 +211,25 @@ class WasmInstanceObject : public Native
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec static_methods[];
     static bool construct(JSContext*, unsigned, Value*);
 
     static WasmInstanceObject* create(JSContext* cx,
                                       RefPtr<const wasm::Code> code,
                                       const wasm::DataSegmentVector& dataSegments,
                                       const wasm::ElemSegmentVector& elemSegments,
-                                      UniquePtr<wasm::DebugState> debug,
                                       wasm::UniqueTlsData tlsData,
                                       HandleWasmMemoryObject memory,
                                       Vector<RefPtr<wasm::Table>, 0, SystemAllocPolicy>&& tables,
                                       Handle<FunctionVector> funcImports,
                                       const wasm::GlobalDescVector& globals,
                                       wasm::HandleValVector globalImportValues,
                                       const WasmGlobalObjectVector& globalObjs,
-                                      HandleObject proto);
+                                      HandleObject proto,
+                                      UniquePtr<wasm::DebugState> maybeDebug);
     void initExportsObj(JSObject& exportsObj);
 
     wasm::Instance& instance() const;
     JSObject& exportsObj() const;
 
     static bool getExportedFunction(JSContext* cx,
                                     HandleWasmInstanceObject instanceObj,
                                     uint32_t funcIndex,
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -76,24 +76,24 @@ Module::startTier2(const CompileArgs& ar
     // This flag will be cleared asynchronously by ~Tier2GeneratorTaskImpl()
     // on success or failure.
     testingTier2Active_ = true;
 
     StartOffThreadWasmTier2Generator(std::move(task));
 }
 
 bool
-Module::finishTier2(const LinkData& linkData2, UniqueCodeTier code2)
+Module::finishTier2(const LinkData& linkData2, UniqueCodeTier code2) const
 {
     MOZ_ASSERT(code().bestTier() == Tier::Baseline && code2->tier() == Tier::Ion);
 
     // Install the data in the data structures. They will not be visible
     // until commitTier2().
 
-    if (!code().setTier2(std::move(code2), *bytecode_, linkData2)) {
+    if (!code().setTier2(std::move(code2), linkData2)) {
         return false;
     }
 
     // Before we can make tier-2 live, we need to compile tier2 versions of any
     // extant tier1 lazy stubs (otherwise, tiering would break the assumption
     // that any extant exported wasm function has had a lazy entry stub already
     // compiled for it).
     {
@@ -168,16 +168,17 @@ Module::testingBlockOnTier2Complete() co
 Module::serializedSize(const LinkData& linkData) const
 {
     return linkData.serializedSize() +
            SerializedVectorSize(imports_) +
            SerializedVectorSize(exports_) +
            SerializedVectorSize(structTypes_) +
            SerializedVectorSize(dataSegments_) +
            SerializedVectorSize(elemSegments_) +
+           SerializedVectorSize(customSections_) +
            code_->serializedSize();
 }
 
 /* virtual */ void
 Module::serialize(const LinkData& linkData, uint8_t* begin, size_t size) const
 {
     MOZ_RELEASE_ASSERT(!testingTier2Active_);
     MOZ_RELEASE_ASSERT(!metadata().debugEnabled);
@@ -185,21 +186,22 @@ Module::serialize(const LinkData& linkDa
 
     uint8_t* cursor = begin;
     cursor = linkData.serialize(cursor);
     cursor = SerializeVector(cursor, imports_);
     cursor = SerializeVector(cursor, exports_);
     cursor = SerializeVector(cursor, structTypes_);
     cursor = SerializeVector(cursor, dataSegments_);
     cursor = SerializeVector(cursor, elemSegments_);
+    cursor = SerializeVector(cursor, customSections_);
     cursor = code_->serialize(cursor, linkData);
     MOZ_RELEASE_ASSERT(cursor == begin + size);
 }
 
-/* static */ SharedModule
+/* static */ MutableModule
 Module::deserialize(const uint8_t* begin, size_t size, Metadata* maybeMetadata)
 {
     MutableMetadata metadata(maybeMetadata);
     if (!metadata) {
         metadata = js_new<Metadata>();
         if (!metadata) {
             return nullptr;
         }
@@ -245,31 +247,45 @@ Module::deserialize(const uint8_t* begin
     }
 
     ElemSegmentVector elemSegments;
     cursor = DeserializeVector(cursor, &elemSegments);
     if (!cursor) {
         return nullptr;
     }
 
+    CustomSectionVector customSections;
+    cursor = DeserializeVector(cursor, &customSections);
+    if (!cursor) {
+        return nullptr;
+    }
+
+    if (metadata->nameCustomSectionIndex) {
+        metadata->namePayload = customSections[*metadata->nameCustomSectionIndex].payload;
+    } else {
+        MOZ_RELEASE_ASSERT(!metadata->moduleName);
+        MOZ_RELEASE_ASSERT(metadata->funcNames.empty());
+    }
+
     SharedCode code;
-    cursor = Code::deserialize(cursor, *bytecode, linkData, *metadata, &code);
+    cursor = Code::deserialize(cursor, linkData, *metadata, &code);
     if (!cursor) {
         return nullptr;
     }
 
     MOZ_RELEASE_ASSERT(cursor == begin + size);
     MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS());
 
     return js_new<Module>(*code,
                           std::move(imports),
                           std::move(exports),
                           std::move(structTypes),
                           std::move(dataSegments),
                           std::move(elemSegments),
+                          std::move(customSections),
                           *bytecode);
 }
 
 /* virtual */ JSObject*
 Module::createObject(JSContext* cx)
 {
     if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
         return nullptr;
@@ -304,17 +320,17 @@ MapFile(PRFileDesc* file, PRFileInfo* in
     // PRFileMap objects do not need to be kept alive after the memory has been
     // mapped, so unconditionally close the PRFileMap, regardless of whether
     // PR_MemMap succeeds.
     uint8_t* memory = (uint8_t*)PR_MemMap(map, 0, info->size);
     PR_CloseFileMap(map);
     return UniqueMapping(memory, MemUnmap(info->size));
 }
 
-SharedModule
+RefPtr<JS::WasmModule>
 wasm::DeserializeModule(PRFileDesc* bytecodeFile, UniqueChars filename, unsigned line)
 {
     PRFileInfo bytecodeInfo;
     UniqueMapping bytecodeMapping = MapFile(bytecodeFile, &bytecodeInfo);
     if (!bytecodeMapping) {
         return nullptr;
     }
 
@@ -344,17 +360,23 @@ wasm::DeserializeModule(PRFileDesc* byte
     // (We would prefer to store this value with the Assumptions when
     // serializing, and for the caller of the deserialization machinery to
     // provide the value from the originating context.)
 
     args->sharedMemoryEnabled = true;
 
     UniqueChars error;
     UniqueCharsVector warnings;
-    return CompileBuffer(*args, *bytecode, &error, &warnings);
+    SharedModule module = CompileBuffer(*args, *bytecode, &error, &warnings);
+    if (!module) {
+        return nullptr;
+    }
+
+    // The public interface is effectively const.
+    return RefPtr<JS::WasmModule>(const_cast<Module*>(module.get()));
 }
 
 /* virtual */ void
 Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                       Metadata::SeenSet* seenMetadata,
                       ShareableBytes::SeenSet* seenBytes,
                       Code::SeenSet* seenCode,
                       size_t* code,
@@ -362,16 +384,17 @@ Module::addSizeOfMisc(MallocSizeOf mallo
 {
     code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
     *data += mallocSizeOf(this) +
              SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
              SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
              SizeOfVectorExcludingThis(structTypes_, mallocSizeOf) +
              SizeOfVectorExcludingThis(dataSegments_, mallocSizeOf) +
              SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
+             SizeOfVectorExcludingThis(customSections_, mallocSizeOf) +
              bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
 
     if (debugUnlinkedCode_) {
         *data += debugUnlinkedCode_->sizeOfExcludingThis(mallocSizeOf);
     }
 }
 
 // Extracting machine code as JS object. The result has the "code" property, as
@@ -880,17 +903,17 @@ Module::getDebugEnabledCode() const
     }
 
     JumpTables jumpTables;
     if (!jumpTables.init(CompileMode::Once, codeTier->segment(), metadata(tier).codeRanges)) {
         return nullptr;
     }
 
     MutableCode debugCode = js_new<Code>(std::move(codeTier), metadata(), std::move(jumpTables));
-    if (!debugCode || !debugCode->initialize(*bytecode_, *debugLinkData_)) {
+    if (!debugCode || !debugCode->initialize(*debugLinkData_)) {
         return nullptr;
     }
 
     return debugCode;
 }
 
 static bool
 GetFunctionExport(JSContext* cx,
@@ -1016,66 +1039,47 @@ Module::instantiate(JSContext* cx,
     }
 
     UniqueTlsData tlsData = CreateTlsData(metadata().globalDataLength);
     if (!tlsData) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    // Debugging mutates code (for traps, stepping, etc) and thus may need to
-    // clone the code on each instantiation.
-
-    SharedCode code(code_);
+    SharedCode code;
+    UniqueDebugState maybeDebug;
     if (metadata().debugEnabled) {
         code = getDebugEnabledCode();
         if (!code) {
             ReportOutOfMemory(cx);
             return false;
         }
-    }
 
-    // To support viewing the source of an instance (Instance::createText), the
-    // instance must hold onto a ref of the bytecode (keeping it alive). This
-    // wastes memory for most users, so we try to only save the source when a
-    // developer actually cares: when the realm is debuggable (which is true
-    // when the web console is open), has code compiled with debug flag
-    // enabled or a names section is present (since this going to be stripped
-    // for non-developer builds).
-
-    const ShareableBytes* maybeBytecode = nullptr;
-    if (cx->realm()->isDebuggee() || metadata().debugEnabled ||
-        !metadata().funcNames.empty() || !!metadata().moduleName)
-    {
-        maybeBytecode = bytecode_.get();
-    }
-
-    // The debug object must be present even when debugging is not enabled: It
-    // provides the lazily created source text for the program, even if that
-    // text is a placeholder message when debugging is not enabled.
-
-    bool binarySource = cx->realm()->debuggerObservesBinarySource();
-    auto debug = cx->make_unique<DebugState>(code, maybeBytecode, binarySource);
-    if (!debug) {
-        return false;
+        bool binarySource = cx->realm()->debuggerObservesBinarySource();
+        maybeDebug = cx->make_unique<DebugState>(*code, *this, binarySource);
+        if (!maybeDebug) {
+            return false;
+        }
+    } else {
+        code = code_;
     }
 
     instance.set(WasmInstanceObject::create(cx,
                                             code,
                                             dataSegments_,
                                             elemSegments_,
-                                            std::move(debug),
                                             std::move(tlsData),
                                             memory,
                                             std::move(tables),
                                             funcImports,
                                             metadata().globals,
                                             globalImportValues,
                                             globalObjs,
-                                            instanceProto));
+                                            instanceProto,
+                                            std::move(maybeDebug)));
     if (!instance) {
         return false;
     }
 
     if (!CreateExportObject(cx, instance, funcImports, table, memory, globalObjs, exports_)) {
         return false;
     }
 
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -43,23 +43,24 @@ struct CompileArgs;
 // ModuleSegment) can be shared between instances, provided none of those
 // instances are being debugged. If patchable code is needed then each instance
 // must have its own Code. Module eagerly creates a new Code and gives it to the
 // first instance; it then instantiates new Code objects from a copy of the
 // unlinked code that it keeps around for that purpose.
 
 class Module : public JS::WasmModule
 {
-    const SharedCode        code_;
-    const ImportVector      imports_;
-    const ExportVector      exports_;
-    const StructTypeVector  structTypes_;
-    const DataSegmentVector dataSegments_;
-    const ElemSegmentVector elemSegments_;
-    const SharedBytes       bytecode_;
+    const SharedCode          code_;
+    const ImportVector        imports_;
+    const ExportVector        exports_;
+    const StructTypeVector    structTypes_;
+    const DataSegmentVector   dataSegments_;
+    const ElemSegmentVector   elemSegments_;
+    const CustomSectionVector customSections_;
+    const SharedBytes         bytecode_;
 
     // These fields are only meaningful when code_->metadata().debugEnabled.
     // `debugCodeClaimed_` is set to false initially and then to true when
     // `code_` is already being used for an instance and can't be shared because
     // it may be patched by the debugger. Subsequent instances must then create
     // copies by linking the `debugUnlinkedCode_` using `debugLinkData_`.
     // This could all be removed if debugging didn't need to perform
     // per-instance code patching.
@@ -91,41 +92,44 @@ class Module : public JS::WasmModule
 
   public:
     Module(const Code& code,
            ImportVector&& imports,
            ExportVector&& exports,
            StructTypeVector&& structTypes,
            DataSegmentVector&& dataSegments,
            ElemSegmentVector&& elemSegments,
+           CustomSectionVector&& customSections,
            const ShareableBytes& bytecode,
            UniqueConstBytes debugUnlinkedCode = nullptr,
            UniqueLinkData debugLinkData = nullptr)
       : code_(&code),
         imports_(std::move(imports)),
         exports_(std::move(exports)),
         structTypes_(std::move(structTypes)),
         dataSegments_(std::move(dataSegments)),
         elemSegments_(std::move(elemSegments)),
+        customSections_(std::move(customSections)),
         bytecode_(&bytecode),
         debugCodeClaimed_(false),
         debugUnlinkedCode_(std::move(debugUnlinkedCode)),
         debugLinkData_(std::move(debugLinkData)),
         testingTier2Active_(false)
     {
         MOZ_ASSERT_IF(metadata().debugEnabled, debugUnlinkedCode_ && debugLinkData_);
     }
     ~Module() override { /* Note: can be called on any thread */ }
 
     const Code& code() const { return *code_; }
     const ModuleSegment& moduleSegment(Tier t) const { return code_->segment(t); }
     const Metadata& metadata() const { return code_->metadata(); }
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const ImportVector& imports() const { return imports_; }
     const ExportVector& exports() const { return exports_; }
+    const CustomSectionVector& customSections() const { return customSections_; }
     const ShareableBytes& bytecode() const { return *bytecode_; }
     uint32_t codeLength(Tier t) const { return code_->segment(t).length(); }
 
     // Instantiate this module with the given imports:
 
     bool instantiate(JSContext* cx,
                      Handle<FunctionVector> funcImports,
                      HandleWasmTableObject tableImport,
@@ -136,17 +140,17 @@ class Module : public JS::WasmModule
                      MutableHandleWasmInstanceObject instanceObj) const;
 
     // Tier-2 compilation may be initiated after the Module is constructed at
     // most once. When tier-2 compilation completes, ModuleGenerator calls
     // finishTier2() from a helper thread, passing tier-variant data which will
     // be installed and made visible.
 
     void startTier2(const CompileArgs& args);
-    bool finishTier2(const LinkData& linkData2, UniqueCodeTier code2);
+    bool finishTier2(const LinkData& linkData2, UniqueCodeTier code2) const;
 
     void testingBlockOnTier2Complete() const;
     bool testingTier2Active() const { return testingTier2Active_; }
 
     // Currently dead, but will be ressurrected with shell tests (bug 1330661)
     // and HTTP cache integration.
 
     size_t serializedSize(const LinkData& linkData) const;
@@ -166,19 +170,20 @@ class Module : public JS::WasmModule
                        Code::SeenSet* seenCode,
                        size_t* code, size_t* data) const;
 
     // Generated code analysis support:
 
     bool extractCode(JSContext* cx, Tier tier, MutableHandleValue vp) const;
 };
 
-typedef RefPtr<Module> SharedModule;
+typedef RefPtr<Module> MutableModule;
+typedef RefPtr<const Module> SharedModule;
 
 // JS API implementations:
 
-SharedModule
+RefPtr<JS::WasmModule>
 DeserializeModule(PRFileDesc* bytecode, UniqueChars filename, unsigned line);
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_module_h
--- a/js/src/wasm/WasmRealm.cpp
+++ b/js/src/wasm/WasmRealm.cpp
@@ -68,17 +68,17 @@ wasm::Realm::registerInstance(JSContext*
     MOZ_ASSERT(runtime_ == cx->runtime());
 
     Instance& instance = instanceObj->instance();
     MOZ_ASSERT(this == &instance.realm()->wasm);
 
     instance.ensureProfilingLabels(cx->runtime()->geckoProfiler().enabled());
 
     if (instance.debugEnabled() && instance.realm()->debuggerObservesAllExecution()) {
-        instance.ensureEnterFrameTrapsState(cx, true);
+        instance.debug().ensureEnterFrameTrapsState(cx, true);
     }
 
     {
         if (!instances_.reserve(instances_.length() + 1)) {
             return false;
         }
 
         auto runtimeInstances = cx->runtime()->wasmInstances.lock();
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -512,16 +512,60 @@ DataSegment::deserialize(const uint8_t* 
 }
 
 size_t
 DataSegment::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return bytes.sizeOfExcludingThis(mallocSizeOf);
 }
 
+size_t
+CustomSection::serializedSize() const
+{
+    return SerializedPodVectorSize(name) +
+           SerializedPodVectorSize(payload->bytes);
+}
+
+uint8_t*
+CustomSection::serialize(uint8_t* cursor) const
+{
+    cursor = SerializePodVector(cursor, name);
+    cursor = SerializePodVector(cursor, payload->bytes);
+    return cursor;
+}
+
+const uint8_t*
+CustomSection::deserialize(const uint8_t* cursor)
+{
+    cursor = DeserializePodVector(cursor, &name);
+    if (!cursor) {
+        return nullptr;
+    }
+
+    Bytes bytes;
+    cursor = DeserializePodVector(cursor, &bytes);
+    if (!cursor) {
+        return nullptr;
+    }
+    payload = js_new<ShareableBytes>(std::move(bytes));
+    if (!payload) {
+        return nullptr;
+    }
+
+    return cursor;
+}
+
+size_t
+CustomSection::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return name.sizeOfExcludingThis(mallocSizeOf) +
+           sizeof(*payload) +
+           payload->sizeOfExcludingThis(mallocSizeOf);
+}
+
 //  Heap length on ARM should fit in an ARM immediate. We approximate the set
 //  of valid ARM immediates with the predicate:
 //    2^n for n in [16, 24)
 //  or
 //    2^24 * n for n >= 1.
 bool
 wasm::IsValidARMImmediate(uint32_t i)
 {
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1174,16 +1174,58 @@ struct DataSegment : AtomicRefCounted<Da
 
     WASM_DECLARE_SERIALIZABLE(DataSegment)
 };
 
 typedef RefPtr<DataSegment> MutableDataSegment;
 typedef SerializableRefPtr<const DataSegment> SharedDataSegment;
 typedef Vector<SharedDataSegment, 0, SystemAllocPolicy> DataSegmentVector;
 
+// The CustomSection(Env) structs are like DataSegment(Env): CustomSectionEnv is
+// stored in the ModuleEnvironment and CustomSection holds a copy of the payload
+// and is stored in the wasm::Module.
+
+struct CustomSectionEnv
+{
+    uint32_t nameOffset;
+    uint32_t nameLength;
+    uint32_t payloadOffset;
+    uint32_t payloadLength;
+};
+
+typedef Vector<CustomSectionEnv, 0, SystemAllocPolicy> CustomSectionEnvVector;
+
+struct CustomSection
+{
+    Bytes name;
+    SharedBytes payload;
+
+    WASM_DECLARE_SERIALIZABLE(CustomSection)
+};
+
+typedef Vector<CustomSection, 0, SystemAllocPolicy> CustomSectionVector;
+
+// A Name represents a string of utf8 chars embedded within the name custom
+// section. The offset of a name is expressed relative to the beginning of the
+// name section's payload so that Names can stored in wasm::Code, which only
+// holds the name section's bytes, not the whole bytecode.
+
+struct Name
+{
+    // All fields are treated as cacheable POD:
+    uint32_t offsetInNamePayload;
+    uint32_t length;
+
+    Name()
+      : offsetInNamePayload(UINT32_MAX), length(0)
+    {}
+};
+
+typedef Vector<Name, 0, SystemAllocPolicy> NameVector;
+
 // FuncTypeIdDesc describes a function type that can be used by call_indirect
 // and table-entry prologues to structurally compare whether the caller and
 // callee's signatures *structurally* match. To handle the general case, a
 // FuncType is allocated and stored in a process-wide hash table, so that
 // pointer equality implies structural equality. As an optimization for the 99%
 // case where the FuncType has a small number of parameters, the FuncType is
 // bit-packed into a uint32 immediate value so that integer equality implies
 // structural equality. Both cases can be handled with a single comparison by
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -199,39 +199,42 @@ Decoder::startCustomSection(const char* 
         if (!*range) {
             goto rewind;
         }
 
         if (bytesRemain() < (*range)->size) {
             goto fail;
         }
 
-        NameInBytecode name;
-        if (!readVarU32(&name.length) || name.length > bytesRemain()) {
+        CustomSectionEnv sec;
+        if (!readVarU32(&sec.nameLength) || sec.nameLength > bytesRemain()) {
             goto fail;
         }
 
-        name.offset = currentOffset();
-        uint32_t payloadOffset = name.offset + name.length;
+        sec.nameOffset = currentOffset();
+        sec.payloadOffset = sec.nameOffset + sec.nameLength;
+
         uint32_t payloadEnd = (*range)->start + (*range)->size;
-        if (payloadOffset > payloadEnd) {
+        if (sec.payloadOffset > payloadEnd) {
             goto fail;
         }
 
+        sec.payloadLength = payloadEnd - sec.payloadOffset;
+
         // Now that we have a valid custom section, record its offsets in the
         // metadata which can be queried by the user via Module.customSections.
         // Note: after an entry is appended, it may be popped if this loop or
         // the loop in startSection needs to rewind.
-        if (!env->customSections.emplaceBack(name, payloadOffset, payloadEnd - payloadOffset)) {
+        if (!env->customSections.append(sec)) {
             return false;
         }
 
         // If this is the expected custom section, we're done.
-        if (!expected || (expectedLength == name.length && !memcmp(cur_, expected, name.length))) {
-            cur_ += name.length;
+        if (!expected || (expectedLength == sec.nameLength && !memcmp(cur_, expected, sec.nameLength))) {
+            cur_ += sec.nameLength;
             return true;
         }
 
         // Otherwise, blindly skip the custom section and keep looking.
         skipAndFinishCustomSection(**range);
         range->reset();
     }
     MOZ_CRASH("unreachable");
@@ -2500,93 +2503,90 @@ DecodeDataSection(Decoder& d, ModuleEnvi
             return false;
         }
     }
 
     return d.finishSection(*range, "data");
 }
 
 static bool
-DecodeModuleNameSubsection(Decoder& d, ModuleEnvironment* env)
+DecodeModuleNameSubsection(Decoder& d, const CustomSectionEnv& nameSection, ModuleEnvironment* env)
 {
     Maybe<uint32_t> endOffset;
     if (!d.startNameSubsection(NameType::Module, &endOffset)) {
         return false;
     }
     if (!endOffset) {
         return true;
     }
 
-    // Don't use NameInBytecode for module name; instead store a copy of the
-    // string. This way supplying a module name doesn't need to save the whole
-    // bytecode. While function names are likely to be stripped in practice,
-    // module names aren't necessarily.
-
-    uint32_t nameLength;
-    if (!d.readVarU32(&nameLength)) {
+    Name moduleName;
+    if (!d.readVarU32(&moduleName.length)) {
         return d.fail("failed to read module name length");
     }
 
-    NameInBytecode moduleName(d.currentOffset(), nameLength);
+    MOZ_ASSERT(d.currentOffset() >= nameSection.payloadOffset);
+    moduleName.offsetInNamePayload = d.currentOffset() - nameSection.payloadOffset;
 
     const uint8_t* bytes;
-    if (!d.readBytes(nameLength, &bytes)) {
+    if (!d.readBytes(moduleName.length, &bytes)) {
         return d.fail("failed to read module name bytes");
     }
 
     env->moduleName.emplace(moduleName);
 
     return d.finishNameSubsection(*endOffset);
 }
 
 static bool
-DecodeFunctionNameSubsection(Decoder& d, ModuleEnvironment* env)
+DecodeFunctionNameSubsection(Decoder& d, const CustomSectionEnv& nameSection, ModuleEnvironment* env)
 {
     Maybe<uint32_t> endOffset;
     if (!d.startNameSubsection(NameType::Function, &endOffset)) {
         return false;
     }
     if (!endOffset) {
         return true;
     }
 
     uint32_t nameCount = 0;
     if (!d.readVarU32(&nameCount) || nameCount > MaxFuncs) {
         return d.fail("bad function name count");
     }
 
-    NameInBytecodeVector funcNames;
+    NameVector funcNames;
 
     for (uint32_t i = 0; i < nameCount; ++i) {
         uint32_t funcIndex = 0;
         if (!d.readVarU32(&funcIndex)) {
             return d.fail("unable to read function index");
         }
 
         // Names must refer to real functions and be given in ascending order.
         if (funcIndex >= env->numFuncs() || funcIndex < funcNames.length()) {
             return d.fail("invalid function index");
         }
 
-        uint32_t nameLength = 0;
-        if (!d.readVarU32(&nameLength) || nameLength > MaxStringLength) {
+        Name funcName;
+        if (!d.readVarU32(&funcName.length) || funcName.length > MaxStringLength) {
             return d.fail("unable to read function name length");
         }
 
-        if (!nameLength) {
+        if (!funcName.length) {
             continue;
         }
 
         if (!funcNames.resize(funcIndex + 1)) {
             return false;
         }
 
-        NameInBytecode funcName(d.currentOffset(), nameLength);
-
-        if (!d.readBytes(nameLength)) {
+        MOZ_ASSERT(d.currentOffset() >= nameSection.payloadOffset);
+        funcName.offsetInNamePayload = d.currentOffset() - nameSection.payloadOffset;
+
+        if (!d.readBytes(funcName.length)) {
             return d.fail("unable to read function name bytes");
         }
 
         funcNames[funcIndex] = funcName;
     }
 
     if (!d.finishNameSubsection(*endOffset)) {
         return false;
@@ -2604,23 +2604,26 @@ DecodeNameSection(Decoder& d, ModuleEnvi
     MaybeSectionRange range;
     if (!d.startCustomSection(NameSectionName, env, &range)) {
         return false;
     }
     if (!range) {
         return true;
     }
 
+    env->nameCustomSectionIndex = Some(env->customSections.length() - 1);
+    const CustomSectionEnv& nameSection = env->customSections.back();
+
     // Once started, custom sections do not report validation errors.
 
-    if (!DecodeModuleNameSubsection(d, env)) {
+    if (!DecodeModuleNameSubsection(d, nameSection, env)) {
         goto finish;
     }
 
-    if (!DecodeFunctionNameSubsection(d, env)) {
+    if (!DecodeFunctionNameSubsection(d, nameSection, env)) {
         goto finish;
     }
 
     while (d.currentOffset() < range->end()) {
         if (!d.skipNameSubsection()) {
             goto finish;
         }
     }
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -174,19 +174,20 @@ struct ModuleEnvironment
     ImportVector              imports;
     ExportVector              exports;
     Maybe<uint32_t>           startFuncIndex;
     ElemSegmentVector         elemSegments;
     MaybeSectionRange         codeSection;
 
     // Fields decoded as part of the wasm module tail:
     DataSegmentEnvVector      dataSegments;
-    Maybe<NameInBytecode>     moduleName;
-    NameInBytecodeVector      funcNames;
-    CustomSectionVector       customSections;
+    CustomSectionEnvVector    customSections;
+    Maybe<uint32_t>           nameCustomSectionIndex;
+    Maybe<Name>               moduleName;
+    NameVector                funcNames;
 
     explicit ModuleEnvironment(HasGcTypes gcTypesConfigured,
                                CompilerEnvironment* compilerEnv,
                                Shareable sharedMemoryEnabled,
                                ModuleKind kind = ModuleKind::Wasm)
       : kind(kind),
         sharedMemoryEnabled(sharedMemoryEnabled),
         gcTypesConfigured(gcTypesConfigured),
@@ -431,17 +432,17 @@ class Encoder
     // of bytes. When used for strings, bytes are to be interpreted as utf8.
 
     MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
         return writeVarU32(numBytes) &&
                bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
     }
 
     // A "section" is a contiguous range of bytes that stores its own size so
-    // that it may be trivially skipped without examining the contents. Sections
+    // that it may be trivially skipped without examining the payload. Sections
     // require backpatching since the size of the section is only known at the
     // end while the size's varU32 must be stored at the beginning. Immediately
     // after the section length is the string id of the section.
 
     MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
         MOZ_ASSERT(uint32_t(id) < 128);
         return writeVarU32(uint32_t(id)) &&
                writePatchableVarU32(offset);