Bug 1449213: Create WebAssembly.Global objects for imported globals that received a primitive; r=luke
authorBenjamin Bouvier <benj@benj.me>
Tue, 27 Mar 2018 18:07:06 +0200
changeset 410617 b78ea9a4d2f3400bfdffe9455276840081920618
parent 410616 7b24c2041026864c2ffbc1e0a7c982f1397b1b51
child 410618 a9cdb1a6783e3c102baa77d25e20a0c92b26e865
push id101541
push userbbouvier@mozilla.com
push dateThu, 29 Mar 2018 16:07:49 +0000
treeherdermozilla-inbound@b78ea9a4d2f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1449213
milestone61.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 1449213: Create WebAssembly.Global objects for imported globals that received a primitive; r=luke
js/src/jit-test/tests/wasm/globals.js
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmModule.h
js/src/wasm/WasmTypes.h
--- a/js/src/jit-test/tests/wasm/globals.js
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -104,19 +104,19 @@ module = wasmEvalText(`(module
         a: 1
     }
 }).exports;
 assertEq(module.f, module.tbl.get(1));
 
 // Import/export rules.
 if (typeof WebAssembly.Global === "undefined") {
     wasmFailValidateText(`(module (import "globals" "x" (global (mut i32))))`,
-			 /can't import.* mutable globals in the MVP/);
+             /can't import.* mutable globals in the MVP/);
     wasmFailValidateText(`(module (global (mut i32) (i32.const 42)) (export "" global 0))`,
-			 /can't .*export mutable globals in the MVP/);
+             /can't .*export mutable globals in the MVP/);
 }
 
 // Import/export semantics.
 module = wasmEvalText(`(module
  (import $g "globals" "x" (global i32))
  (func $get (result i32) (get_global $g))
  (export "getter" $get)
  (export "value" global 0)
@@ -125,16 +125,21 @@ module = wasmEvalText(`(module
 assertEq(module.getter(), 42);
 // Adapt to ongoing experiment with WebAssembly.Global.
 // assertEq() will not trigger @@toPrimitive, so we must have a cast here.
 if (typeof WebAssembly.Global === "function")
     assertEq(Number(module.value), 42);
 else
     assertEq(module.value, 42);
 
+assertEq(wasmEvalText(`(module
+    (global (import "a" "b") (mut i32))
+    (func (export "get") (result i32) get_global 0)
+)`, { a: { b: 42 } }).exports.get(), 42);
+
 // Can only import numbers (no implicit coercions).
 module = new WebAssembly.Module(wasmTextToBinary(`(module
     (global (import "globs" "i32") i32)
     (global (import "globs" "f32") f32)
     (global (import "globs" "f64") f32)
 )`));
 
 const assertLinkFails = (m, imp, err) => {
@@ -224,28 +229,28 @@ function testInitExpr(type, initialValue
             a: coercion(initialValue)
         }
     }).exports;
 
     assertFunc(module.get0(), coercion(initialValue));
     assertFunc(module.get1(), coercion(initialValue));
     // See comment earlier about WebAssembly.Global
     if (typeof WebAssembly.Global === "function")
-	assertFunc(Number(module.global_imm), coercion(initialValue));
+        assertFunc(Number(module.global_imm), coercion(initialValue));
     else
-	assertFunc(module.global_imm, coercion(initialValue));
+        assertFunc(module.global_imm, coercion(initialValue));
 
     assertEq(module.set1(coercion(nextValue)), undefined);
     assertFunc(module.get1(), coercion(nextValue));
     assertFunc(module.get0(), coercion(initialValue));
     // See comment earlier about WebAssembly.Global
     if (typeof WebAssembly.Global === "function")
-	assertFunc(Number(module.global_imm), coercion(initialValue));
+        assertFunc(Number(module.global_imm), coercion(initialValue));
     else
-	assertFunc(module.global_imm, coercion(initialValue));
+        assertFunc(module.global_imm, coercion(initialValue));
 
     assertFunc(module.get_cst(), coercion(initialValue));
 }
 
 testInitExpr('i32', 13, 37, x => x|0);
 testInitExpr('f32', 13.37, 0.1989, Math.fround);
 testInitExpr('f64', 13.37, 0.1989, x => +x);
 
@@ -268,28 +273,28 @@ if (typeof WebAssembly.Global === "undef
 else {
 
     // We can import and export i64 globals as cells.  They cannot be created
     // from JS because there's no way to specify a non-zero initial value; that
     // restriction is tested later.  But we can export one from a module and
     // import it into another.
 
     let i = new WebAssembly.Instance(
-	new WebAssembly.Module(
-	    wasmTextToBinary(`(module
-			       (global (export "g") i64 (i64.const 37))
-			       (global (export "h") (mut i64) (i64.const 37)))`)));
+        new WebAssembly.Module(
+            wasmTextToBinary(`(module
+                (global (export "g") i64 (i64.const 37))
+                (global (export "h") (mut i64) (i64.const 37)))`)));
 
     let j = new WebAssembly.Instance(
-	new WebAssembly.Module(
-	    wasmTextToBinary(`(module
-			       (import "globals" "g" (global i64))
-			       (func (export "f") (result i32)
-				(i64.eq (get_global 0) (i64.const 37))))`)),
-	{globals: {g: i.exports.g}});
+        new WebAssembly.Module(
+            wasmTextToBinary(`(module
+                (import "globals" "g" (global i64))
+                (func (export "f") (result i32)
+                    (i64.eq (get_global 0) (i64.const 37))))`)),
+        {globals: {g: i.exports.g}});
 
     assertEq(j.exports.f(), 1);
 
     // We cannot read or write i64 global values from JS.
 
     let g = i.exports.g;
 
     assertErrorMessage(() => i.exports.g.value, WebAssembly.LinkError, /cannot pass i64 to or from JS/);
@@ -341,185 +346,185 @@ if (typeof WebAssembly.Global === "funct
 
     // These types should work:
     assertEq(new WebAssembly.Global({type: "i32"}) instanceof WebAssembly.Global, true);
     assertEq(new WebAssembly.Global({type: "f32"}) instanceof WebAssembly.Global, true);
     assertEq(new WebAssembly.Global({type: "f64"}) instanceof WebAssembly.Global, true);
 
     // These types should not work:
     assertErrorMessage(() => new WebAssembly.Global({type: "i64"}),
-		       TypeError,
-		       /bad type for a WebAssembly.Global/);
+                       TypeError,
+                       /bad type for a WebAssembly.Global/);
     assertErrorMessage(() => new WebAssembly.Global({}),
-		       TypeError,
-		       /bad type for a WebAssembly.Global/);
+                       TypeError,
+                       /bad type for a WebAssembly.Global/);
     assertErrorMessage(() => new WebAssembly.Global({type: "fnord"}),
-		       TypeError,
-		       /bad type for a WebAssembly.Global/);
+                       TypeError,
+                       /bad type for a WebAssembly.Global/);
     assertErrorMessage(() => new WebAssembly.Global(),
-		       TypeError,
-		       /WebAssembly.Global requires more than 0 arguments/);
+                       TypeError,
+                       /WebAssembly.Global requires more than 0 arguments/);
 
     // Coercion of init value; ".value" accessor
     assertEq((new WebAssembly.Global({type: "i32", value: 3.14})).value, 3);
     assertEq((new WebAssembly.Global({type: "f32", value: { valueOf: () => 33.5 }})).value, 33.5);
 
     // Nothing special about NaN, it coerces just fine
     assertEq((new WebAssembly.Global({type: "i32", value: NaN})).value, 0);
 
     {
-	// "value" is enumerable
-	let x = new WebAssembly.Global({type: "i32"});
-	let s = "";
-	for ( let i in x )
-	    s = s + i + ",";
-	assertEq(s, "value,");
+        // "value" is enumerable
+        let x = new WebAssembly.Global({type: "i32"});
+        let s = "";
+        for ( let i in x )
+            s = s + i + ",";
+        assertEq(s, "value,");
     }
 
     // "value" is defined on the prototype, not on the object
     assertEq("value" in WebAssembly.Global.prototype, true);
 
     // Can't set the value of an immutable global
     assertErrorMessage(() => (new WebAssembly.Global({type: "i32"})).value = 10,
-		       TypeError,
-		       /can't set value of immutable global/);
+                       TypeError,
+                       /can't set value of immutable global/);
 
     {
-	// Can set the value of a mutable global
-	let g = new WebAssembly.Global({type: "i32", mutable: true, value: 37});
-	g.value = 10;
-	assertEq(g.value, 10);
+        // Can set the value of a mutable global
+        let g = new WebAssembly.Global({type: "i32", mutable: true, value: 37});
+        g.value = 10;
+        assertEq(g.value, 10);
     }
 
     {
-	// Misc internal conversions
-	let g = new WebAssembly.Global({type: "i32", value: 42});
+        // Misc internal conversions
+        let g = new WebAssembly.Global({type: "i32", value: 42});
 
-	// valueOf
-	assertEq(g - 5, 37);
+        // valueOf
+        assertEq(g - 5, 37);
 
-	// @@toStringTag
-	assertEq(g.toString(), "[object WebAssembly.Global]");
+        // @@toStringTag
+        assertEq(g.toString(), "[object WebAssembly.Global]");
     }
 
     {
-	// An exported global should appear as a WebAssembly.Global instance:
-	let i =
-	    new WebAssembly.Instance(
-		new WebAssembly.Module(
-		    wasmTextToBinary(`(module (global (export "g") i32 (i32.const 42)))`)));
+        // An exported global should appear as a WebAssembly.Global instance:
+        let i =
+            new WebAssembly.Instance(
+                new WebAssembly.Module(
+                    wasmTextToBinary(`(module (global (export "g") i32 (i32.const 42)))`)));
 
-	assertEq(typeof i.exports.g, "object");
-	assertEq(i.exports.g instanceof WebAssembly.Global, true);
+        assertEq(typeof i.exports.g, "object");
+        assertEq(i.exports.g instanceof WebAssembly.Global, true);
 
-	// An exported global can be imported into another instance even if
-	// it is an object:
-	let j =
-	    new WebAssembly.Instance(
-		new WebAssembly.Module(
-		    wasmTextToBinary(`(module
-				       (global (import "" "g") i32)
-				       (func (export "f") (result i32)
-					(get_global 0)))`)),
-		{ "": { "g": i.exports.g }});
+        // An exported global can be imported into another instance even if
+        // it is an object:
+        let j =
+            new WebAssembly.Instance(
+                new WebAssembly.Module(
+                    wasmTextToBinary(`(module
+                        (global (import "" "g") i32)
+                        (func (export "f") (result i32)
+                            (get_global 0)))`)),
+                { "": { "g": i.exports.g }});
 
-	// And when it is then accessed it has the right value:
-	assertEq(j.exports.f(), 42);
+        // And when it is then accessed it has the right value:
+        assertEq(j.exports.f(), 42);
     }
 
     // Identity of WebAssembly.Global objects (independent of mutablity).
     {
-	// When a global is exported twice, the two objects are the same.
-	let i =
-	    new WebAssembly.Instance(
-		new WebAssembly.Module(
-		    wasmTextToBinary(`(module
-				       (global i32 (i32.const 0))
-				       (export "a" global 0)
-				       (export "b" global 0))`)));
-	assertEq(i.exports.a, i.exports.b);
+        // When a global is exported twice, the two objects are the same.
+        let i =
+            new WebAssembly.Instance(
+                new WebAssembly.Module(
+                    wasmTextToBinary(`(module
+                        (global i32 (i32.const 0))
+                        (export "a" global 0)
+                        (export "b" global 0))`)));
+        assertEq(i.exports.a, i.exports.b);
 
-	// When a global is imported and then exported, the exported object is
-	// the same as the imported object.
-	let j =
-	    new WebAssembly.Instance(
-		new WebAssembly.Module(
-		    wasmTextToBinary(`(module
-				       (import "" "a" (global i32))
-				       (export "x" global 0))`)),
-		{ "": {a: i.exports.a}});
+        // When a global is imported and then exported, the exported object is
+        // the same as the imported object.
+        let j =
+            new WebAssembly.Instance(
+                new WebAssembly.Module(
+                    wasmTextToBinary(`(module
+                        (import "" "a" (global i32))
+                        (export "x" global 0))`)),
+                { "": {a: i.exports.a}});
 
-	assertEq(i.exports.a, j.exports.x);
+        assertEq(i.exports.a, j.exports.x);
 
-	// When a global is imported twice (ie aliased) and then exported twice,
-	// the exported objects are the same, and are also the same as the
-	// imported object.
-	let k =
-	    new WebAssembly.Instance(
-		new WebAssembly.Module(
-		    wasmTextToBinary(`(module
-				       (import "" "a" (global i32))
-				       (import "" "b" (global i32))
-				       (export "x" global 0)
-				       (export "y" global 1))`)),
-		{ "": {a: i.exports.a,
-		       b: i.exports.a}});
+        // When a global is imported twice (ie aliased) and then exported twice,
+        // the exported objects are the same, and are also the same as the
+        // imported object.
+        let k =
+            new WebAssembly.Instance(
+                new WebAssembly.Module(
+                    wasmTextToBinary(`(module
+                        (import "" "a" (global i32))
+                        (import "" "b" (global i32))
+                        (export "x" global 0)
+                        (export "y" global 1))`)),
+                { "": {a: i.exports.a,
+                    b: i.exports.a}});
 
-	assertEq(i.exports.a, k.exports.x);
-	assertEq(k.exports.x, k.exports.y);
+        assertEq(i.exports.a, k.exports.x);
+        assertEq(k.exports.x, k.exports.y);
     }
 
     // Mutability
     {
-	let i =
-	    new WebAssembly.Instance(
-		new WebAssembly.Module(
-		    wasmTextToBinary(`(module
-				       (global (export "g") (mut i32) (i32.const 37))
-				       (func (export "getter") (result i32)
-					(get_global 0))
-				       (func (export "setter") (param i32)
-					(set_global 0 (get_local 0))))`)));
+        let i =
+            new WebAssembly.Instance(
+                new WebAssembly.Module(
+                    wasmTextToBinary(`(module
+                        (global (export "g") (mut i32) (i32.const 37))
+                        (func (export "getter") (result i32)
+                            (get_global 0))
+                        (func (export "setter") (param i32)
+                            (set_global 0 (get_local 0))))`)));
 
-	let j =
-	    new WebAssembly.Instance(
-		new WebAssembly.Module(
-		    wasmTextToBinary(`(module
-				       (import "" "g" (global (mut i32)))
-				       (func (export "getter") (result i32)
-					(get_global 0))
-				       (func (export "setter") (param i32)
-					(set_global 0 (get_local 0))))`)),
-		{"": {g: i.exports.g}});
+        let j =
+            new WebAssembly.Instance(
+                new WebAssembly.Module(
+                    wasmTextToBinary(`(module
+                        (import "" "g" (global (mut i32)))
+                        (func (export "getter") (result i32)
+                            (get_global 0))
+                        (func (export "setter") (param i32)
+                            (set_global 0 (get_local 0))))`)),
+                {"": {g: i.exports.g}});
 
-	// Initial values
-	assertEq(i.exports.g.value, 37);
-	assertEq(i.exports.getter(), 37);
-	assertEq(j.exports.getter(), 37);
+        // Initial values
+        assertEq(i.exports.g.value, 37);
+        assertEq(i.exports.getter(), 37);
+        assertEq(j.exports.getter(), 37);
 
-	// Set in i, observe everywhere
-	i.exports.setter(42);
+        // Set in i, observe everywhere
+        i.exports.setter(42);
 
-	assertEq(i.exports.g.value, 42);
-	assertEq(i.exports.getter(), 42);
-	assertEq(j.exports.getter(), 42);
+        assertEq(i.exports.g.value, 42);
+        assertEq(i.exports.getter(), 42);
+        assertEq(j.exports.getter(), 42);
 
-	// Set in j, observe everywhere
-	j.exports.setter(78);
+        // Set in j, observe everywhere
+        j.exports.setter(78);
 
-    	assertEq(i.exports.g.value, 78);
-	assertEq(i.exports.getter(), 78);
-	assertEq(j.exports.getter(), 78);
+        assertEq(i.exports.g.value, 78);
+        assertEq(i.exports.getter(), 78);
+        assertEq(j.exports.getter(), 78);
 
-	// Set on global object, observe everywhere
-	i.exports.g.value = 197;
+        // Set on global object, observe everywhere
+        i.exports.g.value = 197;
 
-    	assertEq(i.exports.g.value, 197);
-	assertEq(i.exports.getter(), 197);
-	assertEq(j.exports.getter(), 197);
+        assertEq(i.exports.g.value, 197);
+        assertEq(i.exports.getter(), 197);
+        assertEq(j.exports.getter(), 197);
     }
 
     // TEST THIS LAST
 
     // "value" is deletable
     assertEq(delete WebAssembly.Global.prototype.value, true);
     assertEq("value" in WebAssembly.Global.prototype, false);
 
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -450,20 +450,21 @@ Instance::Instance(JSContext* cx,
 
         // Constants are baked into the code, never stored in the global area.
         if (global.isConstant())
             continue;
 
         uint8_t* globalAddr = globalData() + global.offset();
         switch (global.kind()) {
           case GlobalKind::Import: {
+            size_t imported = global.importIndex();
             if (global.isIndirect())
-                *(void**)globalAddr = globalObjs[global.importIndex()]->cell();
+                *(void**)globalAddr = globalObjs[imported]->cell();
             else
-                globalImportValues[global.importIndex()].writePayload(globalAddr);
+                globalImportValues[imported].writePayload(globalAddr);
             break;
           }
           case GlobalKind::Variable: {
             const InitExpr& init = global.initExpr();
             switch (init.kind()) {
               case InitExpr::Kind::Constant: {
                 if (global.isIndirect())
                     *(void**)globalAddr = globalObjs[i]->cell();
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -244,17 +244,17 @@ GetImports(JSContext* cx,
             Val val;
             const uint32_t index = globalIndex++;
             const GlobalDesc& global = globals[index];
             MOZ_ASSERT(global.importIndex() == index);
 
 #if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
             if (v.isObject() && v.toObject().is<WasmGlobalObject>()) {
                 RootedWasmGlobalObject obj(cx, &v.toObject().as<WasmGlobalObject>());
-                if (globalObjs.length() <= index && !globalObjs.resize(index+1)) {
+                if (globalObjs.length() <= index && !globalObjs.resize(index + 1)) {
                     ReportOutOfMemory(cx);
                     return false;
                 }
                 globalObjs[index] = obj;
                 val = obj->val();
             } else
 #endif
             {
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -1013,50 +1013,79 @@ ExtractGlobalValue(const ValVector& glob
       }
       case GlobalKind::Constant: {
         return global.constantValue();
       }
     }
     MOZ_CRASH("Not a global value");
 }
 
+#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
+static bool
+EnsureGlobalObject(JSContext* cx, const ValVector& globalImportValues, size_t globalIndex,
+                   const GlobalDesc& global, WasmGlobalObjectVector& globalObjs)
+{
+    if (globalIndex < globalObjs.length() && globalObjs[globalIndex])
+        return true;
+
+    Val val = ExtractGlobalValue(globalImportValues, globalIndex, global);
+    RootedWasmGlobalObject go(cx, WasmGlobalObject::create(cx, val, global.isMutable()));
+    if (!go)
+        return false;
+
+    if (globalObjs.length() <= globalIndex && !globalObjs.resize(globalIndex + 1)) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    globalObjs[globalIndex] = go;
+    return true;
+}
+#endif
+
 bool
-Module::instantiateGlobalExports(JSContext* cx,
-                                 const ValVector& globalImportValues,
-                                 WasmGlobalObjectVector& globalObjs) const
+Module::instantiateGlobals(JSContext* cx, const ValVector& globalImportValues,
+                           WasmGlobalObjectVector& globalObjs) const
 {
 #if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
-    // If there are exported globals that aren't in the globalObjs because they
+    // If there are exported globals that aren't in globalObjs because they
     // originate in this module or because they were immutable imports that came
-    // in as values (not cells) then we must create cells in the globalObjs for
+    // in as primitive values then we must create cells in the globalObjs for
     // them here, as WasmInstanceObject::create() and CreateExportObject() will
     // need the cells to exist.
 
     const GlobalDescVector& globals = metadata().globals;
 
     for (const Export& exp : exports_) {
-        if (exp.kind() == DefinitionKind::Global) {
-            unsigned globalIndex = exp.globalIndex();
+        if (exp.kind() != DefinitionKind::Global)
+            continue;
+        unsigned globalIndex = exp.globalIndex();
+        const GlobalDesc& global = globals[globalIndex];
+        if (!EnsureGlobalObject(cx, globalImportValues, globalIndex, global, globalObjs))
+            return false;
+    }
 
-            if (globalIndex >= globalObjs.length() || !globalObjs[globalIndex]) {
-                const GlobalDesc& global = globals[globalIndex];
+    // Imported globals may also have received only a primitive value, thus
+    // they may need their own Global object, because the compiled code assumed
+    // they were indirect.
 
-                Val val = ExtractGlobalValue(globalImportValues, globalIndex, global);
-                RootedWasmGlobalObject go(cx, WasmGlobalObject::create(cx, val,
-                                                                       global.isMutable()));
-                if (!go)
-                    return false;
-                if (globalObjs.length() <= globalIndex && !globalObjs.resize(globalIndex+1)) {
-                    ReportOutOfMemory(cx);
-                    return false;
-                }
-                globalObjs[globalIndex] = go;
-            }
-        }
+    size_t numGlobalImports = 0;
+    for (const Import& import : imports_) {
+        if (import.kind != DefinitionKind::Global)
+            continue;
+        size_t globalIndex = numGlobalImports++;
+        const GlobalDesc& global = globals[globalIndex];
+        MOZ_ASSERT(global.importIndex() == globalIndex);
+        if (!global.isIndirect())
+            continue;
+        if (!EnsureGlobalObject(cx, globalImportValues, globalIndex, global, globalObjs))
+            return false;
     }
+    MOZ_ASSERT_IF(!metadata().isAsmJS(),
+                  numGlobalImports == globals.length() || !globals[numGlobalImports].isImport());
 #endif
     return true;
 }
 
 static bool
 GetFunctionExport(JSContext* cx,
                   HandleWasmInstanceObject instanceObj,
                   Handle<FunctionVector> funcImports,
@@ -1191,17 +1220,17 @@ Module::instantiate(JSContext* cx,
     if (!instantiateMemory(cx, &memory))
         return false;
 
     RootedWasmTableObject table(cx, tableImport);
     SharedTableVector tables;
     if (!instantiateTable(cx, &table, &tables))
         return false;
 
-    if (!instantiateGlobalExports(cx, globalImportValues, globalObjs))
+    if (!instantiateGlobals(cx, globalImportValues, globalObjs))
         return false;
 
     UniqueTlsData tlsData = CreateTlsData(metadata().globalDataLength);
     if (!tlsData) {
         ReportOutOfMemory(cx);
         return false;
     }
 
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -144,19 +144,18 @@ class Module : public JS::WasmModule
 
     mutable Atomic<bool>    codeIsBusy_;
 
     bool instantiateFunctions(JSContext* cx, Handle<FunctionVector> funcImports) const;
     bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
     bool instantiateTable(JSContext* cx,
                           MutableHandleWasmTableObject table,
                           SharedTableVector* tables) const;
-    bool instantiateGlobalExports(JSContext* cx,
-                                  const ValVector& globalImportValues,
-                                  WasmGlobalObjectVector& globalObjs) const;
+    bool instantiateGlobals(JSContext* cx, const ValVector& globalImportValues,
+                            WasmGlobalObjectVector& globalObjs) const;
     bool initSegments(JSContext* cx,
                       HandleWasmInstanceObject instance,
                       Handle<FunctionVector> funcImports,
                       HandleWasmMemoryObject memory,
                       const ValVector& globalImportValues) const;
 
     class Tier2GeneratorTaskImpl;
     void notifyCompilationListeners();
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -801,17 +801,21 @@ class GlobalDesc
     uint32_t importIndex() const { MOZ_ASSERT(isImport()); return u.var.val.import.index_; }
 
     // If isIndirect() is true then storage for the value is not in the
     // instance's global area, but in a WasmGlobalObject::Cell hanging off a
     // WasmGlobalObject; the global area contains a pointer to the Cell.
     //
     // We don't want to indirect unless we must, so only mutable, exposed
     // globals are indirected - in all other cases we copy values into and out
-    // of them module.
+    // of their module.
+    //
+    // Note that isIndirect() isn't equivalent to getting a WasmGlobalObject:
+    // an immutable exported global will still get an object, but will not be
+    // indirect.
     bool isIndirect() const { return isMutable() && isWasm() && (isImport() || isExport()); }
 
     ValType type() const {
         switch (kind_) {
           case GlobalKind::Import:   return u.var.val.import.type_;
           case GlobalKind::Variable: return u.var.val.initial_.type();
           case GlobalKind::Constant: return u.cst_.type();
         }