Bug 1452592 - Check mutability when importing globals. r=bbouvier
authorLars T Hansen <lhansen@mozilla.com>
Mon, 09 Apr 2018 17:09:28 +0200
changeset 466108 8a735eb98349815f732f712f4a67bcdbd0024b4b
parent 466107 7e2492c45ab41d26f131d64ebc9a3e10fae65e9f
child 466109 70ab5d0f6da37783913e39857edf488ccfc2ab7b
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1452592
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 1452592 - Check mutability when importing globals. r=bbouvier The mutability of the imported value must match the mutability of the import declaration. If an imported value is provided as a number constant and not a WebAssembly.Global, then it is implicitly immutable. Drive-by fix: We test for non-number imports before testing for i64.
js/src/jit-test/tests/wasm/globals.js
js/src/jit-test/tests/wasm/regress/bug1450800.js
js/src/js.msg
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmModule.cpp
--- a/js/src/jit-test/tests/wasm/globals.js
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -1,9 +1,9 @@
-const { Instance, Module } = WebAssembly;
+const { Instance, Module, LinkError } = WebAssembly;
 
 // Locally-defined globals
 assertErrorMessage(() => wasmEvalText(`(module (global))`), SyntaxError, /parsing/);
 assertErrorMessage(() => wasmEvalText(`(module (global i32))`), SyntaxError, /parsing/);
 assertErrorMessage(() => wasmEvalText(`(module (global (mut i32)))`), SyntaxError, /parsing/);
 
 // Initializer expressions.
 wasmFailValidateText(`(module (global i32 (f32.const 13.37)))`, /type mismatch/);
@@ -126,35 +126,35 @@ 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);
 
 // Can only import numbers (no implicit coercions).
-module = new WebAssembly.Module(wasmTextToBinary(`(module
+module = new Module(wasmTextToBinary(`(module
     (global (import "globs" "i32") i32)
     (global (import "globs" "f32") f32)
     (global (import "globs" "f64") f32)
 )`));
 
 const assertLinkFails = (m, imp, err) => {
-    assertErrorMessage(() => new WebAssembly.Instance(m, imp), WebAssembly.LinkError, err);
+    assertErrorMessage(() => new Instance(m, imp), LinkError, err);
 }
 
 var imp = {
     globs: {
         i32: 0,
         f32: Infinity,
         f64: NaN
     }
 };
 
-let i = new WebAssembly.Instance(module, imp);
+let i = new Instance(module, imp);
 
 for (let v of [
     null,
     {},
     "42",
     /not a number/,
     false,
     undefined,
@@ -247,61 +247,69 @@ function testInitExpr(type, initialValue
 
 testInitExpr('i32', 13, 37, x => x|0);
 testInitExpr('f32', 13.37, 0.1989, Math.fround);
 testInitExpr('f64', 13.37, 0.1989, x => +x);
 
 // Int64.
 
 // Import and export
+
+// The test for a Number value dominates the guard against int64.
+assertErrorMessage(() => wasmEvalText(`(module
+                                        (import "globals" "x" (global i64)))`,
+                                      {globals: {x:false}}),
+                   LinkError,
+                   /import object field 'x' is not a Number/);
+
+// The imported value is a Number, so the int64 guard should stop us
+assertErrorMessage(() => wasmEvalText(`(module
+                                        (import "globals" "x" (global i64)))`,
+                                      {globals: {x:42}}),
+                   LinkError,
+                   /cannot pass i64 to or from JS/);
+
 if (typeof WebAssembly.Global === "undefined") {
 
-    // Without WebAssembly.Global, i64 cannot be imported or exported
+    // Cannot export int64 at all.
 
-    module = new WebAssembly.Module(wasmTextToBinary(`(module (import "globals" "x" (global i64)))`));
-    assertErrorMessage(() => new WebAssembly.Instance(module, {globals: {x:42}}),
-                       WebAssembly.LinkError,
+    assertErrorMessage(() => wasmEvalText(`(module
+                                            (global i64 (i64.const 42))
+                                            (export "" global 0))`),
+                       LinkError,
                        /cannot pass i64 to or from JS/);
 
-    module = new WebAssembly.Module(wasmTextToBinary(`(module (global i64 (i64.const 42)) (export "" global 0))`));
-    assertErrorMessage(() => new WebAssembly.Instance(module), WebAssembly.LinkError, /cannot pass i64 to or from JS/);
-
-}
-else {
+} 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)))`)));
+    let i = wasmEvalText(`(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}});
+    let j = wasmEvalText(`(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/);
+    assertErrorMessage(() => i.exports.g.value, TypeError, /cannot pass i64 to or from JS/);
 
     // Mutability check comes before i64 check.
     assertErrorMessage(() => i.exports.g.value = 12, TypeError, /can't set value of immutable global/);
-    assertErrorMessage(() => i.exports.h.value = 12, WebAssembly.LinkError, /cannot pass i64 to or from JS/);
+    assertErrorMessage(() => i.exports.h.value = 12, TypeError, /cannot pass i64 to or from JS/);
 }
 
 // Test inner
 var initialValue = '0x123456789abcdef0';
 var nextValue = '0x531642753864975F';
 wasmAssert(`(module
     (global (mut i64) (i64.const ${initialValue}))
     (global i64 (i64.const ${initialValue}))
@@ -334,166 +342,140 @@ wasmAssert(`(module
     dv.setFloat32(0, module.nan32, true);
     assertEq(dv.getUint32(0, true), 0x7fc00000);
 }
 
 // WebAssembly.Global experiment
 
 if (typeof WebAssembly.Global === "function") {
 
+    const Global = WebAssembly.Global;
+
     // 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);
+    assertEq(new Global({type: "i32"}) instanceof Global, true);
+    assertEq(new Global({type: "f32"}) instanceof Global, true);
+    assertEq(new Global({type: "f64"}) instanceof Global, true);
 
     // These types should not work:
-    assertErrorMessage(() => new WebAssembly.Global({type: "i64"}),
-                       TypeError,
-                       /bad type for a WebAssembly.Global/);
-    assertErrorMessage(() => new WebAssembly.Global({}),
-                       TypeError,
-                       /bad type for a WebAssembly.Global/);
-    assertErrorMessage(() => new WebAssembly.Global({type: "fnord"}),
-                       TypeError,
-                       /bad type for a WebAssembly.Global/);
-    assertErrorMessage(() => new WebAssembly.Global(),
-                       TypeError,
-                       /WebAssembly.Global requires more than 0 arguments/);
+    assertErrorMessage(() => new Global({type: "i64"}),   TypeError, /bad type for a WebAssembly.Global/);
+    assertErrorMessage(() => new Global({}),              TypeError, /bad type for a WebAssembly.Global/);
+    assertErrorMessage(() => new Global({type: "fnord"}), TypeError, /bad type for a WebAssembly.Global/);
+    assertErrorMessage(() => new Global(),                TypeError, /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);
+    assertEq((new Global({type: "i32", value: 3.14})).value, 3);
+    assertEq((new Global({type: "f32", value: { valueOf: () => 33.5 }})).value, 33.5);
+    assertEq((new Global({type: "f64", value: "3.25"})).value, 3.25);
 
     // Nothing special about NaN, it coerces just fine
-    assertEq((new WebAssembly.Global({type: "i32", value: NaN})).value, 0);
+    assertEq((new Global({type: "i32", value: NaN})).value, 0);
 
     {
         // "value" is enumerable
-        let x = new WebAssembly.Global({type: "i32"});
+        let x = new 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);
+    assertEq("value" in Global.prototype, true);
 
     // Can't set the value of an immutable global
-    assertErrorMessage(() => (new WebAssembly.Global({type: "i32"})).value = 10,
+    assertErrorMessage(() => (new Global({type: "i32"})).value = 10,
                        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});
+        let g = new 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});
+        let g = new Global({type: "i32", value: 42});
 
         // valueOf
         assertEq(g - 5, 37);
 
         // @@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 Global instance:
+        let i = wasmEvalText(`(module (global (export "g") i32 (i32.const 42)))`);
 
         assertEq(typeof i.exports.g, "object");
-        assertEq(i.exports.g instanceof WebAssembly.Global, true);
+        assertEq(i.exports.g instanceof 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 }});
+        let j = wasmEvalText(`(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);
     }
 
-    // Identity of WebAssembly.Global objects (independent of mutablity).
+    // Identity of 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))`)));
+        let i = wasmEvalText(`(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}});
+        let j = wasmEvalText(`(module
+                               (import "" "a" (global i32))
+                               (export "x" global 0))`,
+                             { "": {a: i.exports.a}});
 
         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}});
+        let k = wasmEvalText(`(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);
     }
 
     // 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 = wasmEvalText(`(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 = wasmEvalText(`(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);
 
         // Set in i, observe everywhere
         i.exports.setter(42);
@@ -512,24 +494,53 @@ if (typeof WebAssembly.Global === "funct
         // 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);
     }
 
-    // Providing a primitive value to an imported global should internally
-    // promote it to a Webassembly.Global object.
+    // Mutability of import declaration and imported value have to match
+    {
+        const mutErr = /imported global mutability mismatch/;
+        const i64Err = /cannot pass i64 to or from JS/;
+
+        let m1 = new Module(wasmTextToBinary(`(module
+                                               (import "m" "g" (global i32)))`));
+
+        // Mutable Global matched to immutable import
+        let gm = new Global({type: "i32", value: 42, mutable: true});
+        assertErrorMessage(() => new Instance(m1, {m: {g: gm}}),
+                           LinkError,
+                           mutErr);
+
+        let m2 = new Module(wasmTextToBinary(`(module
+                                               (import "m" "g" (global (mut i32))))`));
 
-    assertEq(wasmEvalText(`(module
-        (global (import "a" "b") (mut i32))
-        (func (export "get") (result i32) get_global 0)
-    )`, { a: { b: 42 } }).exports.get(), 42);
+        // Immutable Global matched to mutable import
+        let gi = new Global({type: "i32", value: 42, mutable: false});
+        assertErrorMessage(() => new Instance(m2, {m: {g: gi}}),
+                           LinkError,
+                           mutErr);
+
+        // Constant value is the same as immutable Global
+        assertErrorMessage(() => new Instance(m2, {m: {g: 42}}),
+                           LinkError,
+                           mutErr);
+
+        let m3 = new Module(wasmTextToBinary(`(module
+                                               (import "m" "g" (global (mut i64))))`));
+
+        // Check against i64 import before matching mutability
+        assertErrorMessage(() => new Instance(m3, {m: {g: 42}}),
+                           LinkError,
+                           i64Err);
+    }
 
     // TEST THIS LAST
 
     // "value" is deletable
-    assertEq(delete WebAssembly.Global.prototype.value, true);
-    assertEq("value" in WebAssembly.Global.prototype, false);
+    assertEq(delete Global.prototype.value, true);
+    assertEq("value" in Global.prototype, false);
 
     // ADD NO MORE TESTS HERE!
 }
--- a/js/src/jit-test/tests/wasm/regress/bug1450800.js
+++ b/js/src/jit-test/tests/wasm/regress/bug1450800.js
@@ -3,19 +3,21 @@ if (!this.gczeal || !WebAssembly.Global)
 
 gczeal(9, 10);
 function wasmEvalText(str, imports) {
     let binary = wasmTextToBinary(str);
     m = new WebAssembly.Module(binary);
     return new WebAssembly.Instance(m, imports);
 }
 assertEq(wasmEvalText(`(module
-    (global (import "a" "b") (mut i32))
-    (func (export "get") (result i32) get_global 0)
-)`, {  a: { b: 42 }}).exports.get(), 42);
+                        (global (import "a" "b") i32)
+                        (export "g" (global 0))
+                        (func (export "get") (result i32) get_global 0))`,
+                      { a: { b: 42 }}).exports.get(),
+         42);
 for (let v of []) {}
 function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = assertEq) {
     var module = wasmEvalText(`(module
         (import "globals" "a" (global ${type}))
         (global $glob_imm ${type} (get_global 0))
         (export "global_imm" (global $glob_imm))
     )`, {
         globals: {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -363,16 +363,17 @@ MSG_DEF(JSMSG_WASM_BAD_IMPORT_TYPE,    2
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG,     2, JSEXN_WASMLINKERROR, "imported function '{0}.{1}' signature mismatch")
 MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE,       1, JSEXN_WASMLINKERROR, "imported {0} with incompatible size")
 MSG_DEF(JSMSG_WASM_BAD_IMP_MAX,        1, JSEXN_WASMLINKERROR, "imported {0} with incompatible maximum size")
 MSG_DEF(JSMSG_WASM_IMP_SHARED_REQD,    0, JSEXN_WASMLINKERROR, "imported unshared memory but shared required")
 MSG_DEF(JSMSG_WASM_IMP_SHARED_BANNED,  0, JSEXN_WASMLINKERROR, "imported shared memory but unshared required")
 MSG_DEF(JSMSG_WASM_BAD_FIT,            2, JSEXN_WASMLINKERROR, "{0} segment does not fit in {1}")
 MSG_DEF(JSMSG_WASM_BAD_I64_LINK,       0, JSEXN_WASMLINKERROR, "cannot pass i64 to or from JS")
 MSG_DEF(JSMSG_WASM_NO_SHMEM_LINK,      0, JSEXN_WASMLINKERROR, "shared memory is disabled")
+MSG_DEF(JSMSG_WASM_BAD_MUT_LINK,       0, JSEXN_WASMLINKERROR, "imported global mutability mismatch")
 MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL,   0, JSEXN_WASMRUNTIMEERROR, "indirect call to null")
 MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG,   0, JSEXN_WASMRUNTIMEERROR, "indirect call signature mismatch")
 MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_WASMRUNTIMEERROR, "unreachable executed")
 MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW,   0, JSEXN_WASMRUNTIMEERROR, "integer overflow")
 MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_WASMRUNTIMEERROR, "invalid conversion to integer")
 MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_WASMRUNTIMEERROR, "integer divide by zero")
 MSG_DEF(JSMSG_WASM_OUT_OF_BOUNDS,      0, JSEXN_WASMRUNTIMEERROR, "index out of bounds")
 MSG_DEF(JSMSG_WASM_UNALIGNED_ACCESS,   0, JSEXN_WASMRUNTIMEERROR, "unaligned memory access")
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -244,35 +244,47 @@ 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 (obj->isMutable() != global.isMutable()) {
+                    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MUT_LINK);
+                    return false;
+                }
+
                 if (globalObjs.length() <= index && !globalObjs.resize(index + 1)) {
                     ReportOutOfMemory(cx);
                     return false;
                 }
                 globalObjs[index] = obj;
                 val = obj->val();
             } else
 #endif
-            {
+            if (v.isNumber()) {
                 if (global.type() == ValType::I64) {
                     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
                     return false;
                 }
 
-                if (!v.isNumber())
-                    return ThrowBadImportType(cx, import.field.get(), "Number");
+#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
+                if (global.isMutable()) {
+                    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MUT_LINK);
+                    return false;
+                }
+#endif
 
                 if (!ToWebAssemblyValue(cx, global.type(), v, &val))
                     return false;
+            } else {
+                return ThrowBadImportType(cx, import.field.get(), "Number");
             }
 
             if (!globalImportValues->append(val))
                 return false;
         }
     }
 
     MOZ_ASSERT(globalIndex == globals.length() || !globals[globalIndex].isImport());
@@ -2173,17 +2185,17 @@ WasmGlobalObject::valueGetterImpl(JSCont
 {
     switch (args.thisv().toObject().as<WasmGlobalObject>().type()) {
       case ValType::I32:
       case ValType::F32:
       case ValType::F64:
         args.rval().set(args.thisv().toObject().as<WasmGlobalObject>().value());
         return true;
       case ValType::I64:
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_TYPE);
         return false;
       default:
         MOZ_CRASH();
     }
 }
 
 /* static */ bool
 WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp)
@@ -2197,17 +2209,17 @@ WasmGlobalObject::valueSetterImpl(JSCont
 {
     RootedWasmGlobalObject global(cx, &args.thisv().toObject().as<WasmGlobalObject>());
     if (!global->isMutable()) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_GLOBAL_IMMUTABLE);
         return false;
     }
 
     if (global->type() == ValType::I64) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_TYPE);
         return false;
     }
 
     Val val;
     if (!ToWebAssemblyValue(cx, global->type(), args.get(0), &val))
         return false;
 
     Cell* cell = global->cell();
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -1058,34 +1058,34 @@ Module::instantiateGlobals(JSContext* cx
         if (exp.kind() != DefinitionKind::Global)
             continue;
         unsigned globalIndex = exp.globalIndex();
         const GlobalDesc& global = globals[globalIndex];
         if (!EnsureGlobalObject(cx, globalImportValues, globalIndex, global, globalObjs))
             return false;
     }
 
-    // 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.
+    // Imported globals that are not re-exported may also have received only a
+    // primitive value; these globals are always immutable.  Assert that we do
+    // not need to create any additional Global objects for such imports.
 
+# ifdef DEBUG
     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(global.isIndirect(),
+                      globalIndex < globalObjs.length() || globalObjs[globalIndex]);
     }
     MOZ_ASSERT_IF(!metadata().isAsmJS(),
                   numGlobalImports == globals.length() || !globals[numGlobalImports].isImport());
+# endif
 #endif
     return true;
 }
 
 static bool
 GetFunctionExport(JSContext* cx,
                   HandleWasmInstanceObject instanceObj,
                   Handle<FunctionVector> funcImports,