Bug 1308056: wasm: add syntax for inline import/export in globals; r=luke
☠☠ backed out by f9b61cc39e92 ☠ ☠
authorBenjamin Bouvier <benj@benj.me>
Fri, 07 Oct 2016 17:14:17 +0200
changeset 360367 913ccaec26d5f0f137f088482f6fd02d8e99b323
parent 360366 c417fb206f6cb7fb6c4059f437f8f59f9c18b46e
child 360368 27bec108f660f219747e0626cfa61a57cfce14f0
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1308056
milestone52.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 1308056: wasm: add syntax for inline import/export in globals; r=luke MozReview-Commit-ID: DLc7oCNkbpi
js/src/asmjs/WasmTextToBinary.cpp
js/src/jit-test/tests/wasm/globals.js
js/src/jit-test/tests/wasm/import-export.js
js/src/jit-test/tests/wasm/nan-semantics.js
js/src/jit-test/tests/wasm/tables.js
js/src/jit-test/tests/wasm/text.js
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -88,25 +88,25 @@ class WasmToken
         Error,
         Export,
         Float,
         Func,
         GetGlobal,
         GetLocal,
         Global,
         If,
-        Immutable,
         Import,
         Index,
         Memory,
         NegativeZero,
         Load,
         Local,
         Loop,
         Module,
+        Mutable,
         Name,
         Nop,
         Offset,
         OpenParen,
         Param,
         Result,
         Return,
         Segment,
@@ -291,17 +291,17 @@ class WasmToken
           case EndOfFile:
           case Equal:
           case End:
           case Error:
           case Export:
           case Float:
           case Func:
           case Global:
-          case Immutable:
+          case Mutable:
           case Import:
           case Index:
           case Memory:
           case NegativeZero:
           case Local:
           case Module:
           case Name:
           case Offset:
@@ -1372,18 +1372,16 @@ WasmTokenStream::next()
                 break;
               case 'x':
                 if (consume(u"xor"))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I64Xor, begin, cur_);
                 break;
             }
             break;
         }
-        if (consume(u"immutable"))
-            return WasmToken(WasmToken::Immutable, begin, cur_);
         if (consume(u"import"))
             return WasmToken(WasmToken::Import, begin, cur_);
         if (consume(u"infinity"))
             return WasmToken(WasmToken::Infinity, begin, cur_);
         if (consume(u"if"))
             return WasmToken(WasmToken::If, begin, cur_);
         break;
 
@@ -1394,16 +1392,18 @@ WasmTokenStream::next()
             return WasmToken(WasmToken::Loop, begin, cur_);
         break;
 
       case 'm':
         if (consume(u"module"))
             return WasmToken(WasmToken::Module, begin, cur_);
         if (consume(u"memory"))
             return WasmToken(WasmToken::Memory, begin, cur_);
+        if (consume(u"mut"))
+            return WasmToken(WasmToken::Mutable, begin, cur_);
         break;
 
       case 'n':
         if (consume(u"nan"))
             return nan(begin);
         if (consume(u"nop"))
             return WasmToken(WasmToken::NullaryOpcode, Expr::Nop, begin, cur_);
         break;
@@ -2793,25 +2793,28 @@ ParseStartFunc(WasmParseContext& c, Wasm
     }
 
     return true;
 }
 
 static bool
 ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, uint32_t* flags)
 {
-    if (!c.ts.match(WasmToken::ValueType, typeToken, c.error))
-        return false;
-
-    // Mutable by default.
-    *flags = 0x1;
-    if (c.ts.getIf(WasmToken::Immutable))
-        *flags = 0x0;
-
-    return true;
+    // Either (mut i32) or i32.
+    if (c.ts.getIf(WasmToken::OpenParen)) {
+        // Immutable by default.
+        *flags = c.ts.getIf(WasmToken::Mutable) ? 0x1 : 0x0;
+        if (!c.ts.match(WasmToken::ValueType, typeToken, c.error))
+            return false;
+        if (!c.ts.match(WasmToken::CloseParen, c.error))
+            return false;
+        return true;
+    }
+
+    return c.ts.match(WasmToken::ValueType, typeToken, c.error);
 }
 
 static bool
 ParseElemType(WasmParseContext& c)
 {
     // Only AnyFunc is allowed at the moment.
     return c.ts.match(WasmToken::AnyFunc, c.error);
 }
@@ -2856,27 +2859,32 @@ ParseImport(WasmParseContext& c, AstModu
             if (!ParseTableSig(c, &table))
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
             return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
                                          DefinitionKind::Table, table);
         }
         if (c.ts.getIf(WasmToken::Global)) {
+            if (name.empty())
+                name = c.ts.getIfName();
+
             WasmToken typeToken;
             uint32_t flags = 0;
             if (!ParseGlobalType(c, &typeToken, &flags))
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
+
             return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
                                          AstGlobal(AstName(), typeToken.valueType(), flags));
         }
         if (c.ts.getIf(WasmToken::Func)) {
-            AstName name = c.ts.getIfName();
+            if (name.empty())
+                name = c.ts.getIfName();
 
             AstRef sigRef;
             if (!ParseFuncType(c, &sigRef, module))
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
 
             return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(), sigRef);
@@ -3079,31 +3087,68 @@ ParseElemSegment(WasmParseContext& c)
     while (c.ts.getIfRef(&elem)) {
         if (!elems.append(elem))
             return nullptr;
     }
 
     return new(c.lifo) AstElemSegment(offset, Move(elems));
 }
 
-static AstGlobal*
-ParseGlobal(WasmParseContext& c)
+static bool
+ParseGlobal(WasmParseContext& c, AstModule* module)
 {
     AstName name = c.ts.getIfName();
 
     WasmToken typeToken;
     uint32_t flags = 0;
+
+    WasmToken openParen;
+    if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
+        if (c.ts.getIf(WasmToken::Import)) {
+            if (module->globals().length()) {
+                c.ts.generateError(openParen, "import after global definition", c.error);
+                return false;
+            }
+
+            InlineImport names;
+            if (!ParseInlineImport(c, &names))
+                return false;
+            if (!c.ts.match(WasmToken::CloseParen, c.error))
+                return false;
+
+            if (!ParseGlobalType(c, &typeToken, &flags))
+                return false;
+
+            auto* imp = new(c.lifo) AstImport(name, names.module.text(), names.field.text(),
+                                              AstGlobal(AstName(), typeToken.valueType(), flags));
+            return imp && module->append(imp);
+        }
+
+        if (c.ts.getIf(WasmToken::Export)) {
+            AstRef ref = name.empty()
+                         ? AstRef(AstName(), module->globals().length())
+                         : AstRef(name, AstNoIndex);
+            if (!ParseInlineExport(c, DefinitionKind::Global, module, ref))
+                return false;
+            if (!c.ts.match(WasmToken::CloseParen, c.error))
+                return false;
+        } else {
+            c.ts.unget(openParen);
+        }
+    }
+
     if (!ParseGlobalType(c, &typeToken, &flags))
-        return nullptr;
+        return false;
 
     AstExpr* init = ParseExpr(c, true);
     if (!init)
-        return nullptr;
-
-    return new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init));
+        return false;
+
+    auto* glob = new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init));
+    return glob && module->append(glob);
 }
 
 static AstModule*
 ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
 {
     WasmParseContext c(text, lifo, error);
 
     if (!c.ts.match(WasmToken::OpenParen, c.error))
@@ -3131,18 +3176,17 @@ ParseModule(const char16_t* text, LifoAl
             break;
           }
           case WasmToken::Memory: {
             if (!ParseMemory(c, section, module))
                 return nullptr;
             break;
           }
           case WasmToken::Global: {
-            AstGlobal* global = ParseGlobal(c);
-            if (!global || !module->append(global))
+            if (!ParseGlobal(c, module))
                 return nullptr;
             break;
           }
           case WasmToken::Data: {
             AstDataSegment* segment = ParseDataSegment(c);
             if (!segment || !module->append(segment))
                 return nullptr;
             break;
--- a/js/src/jit-test/tests/wasm/globals.js
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -1,32 +1,32 @@
 // |jit-test| test-also-wasm-baseline
 load(libdir + "wasm.js");
 
 const { Instance, Module } = WebAssembly;
 
 // Locally-defined globals
 assertErrorMessage(() => wasmEvalText(`(module (global))`), SyntaxError, /parsing/);
 assertErrorMessage(() => wasmEvalText(`(module (global i32))`), SyntaxError, /parsing/);
-assertErrorMessage(() => wasmEvalText(`(module (global i32 immutable))`), SyntaxError, /parsing/);
+assertErrorMessage(() => wasmEvalText(`(module (global (mut i32)))`), SyntaxError, /parsing/);
 
 // Initializer expressions.
 wasmFailValidateText(`(module (global i32 (f32.const 13.37)))`, /type mismatch/);
 wasmFailValidateText(`(module (global f64 (f32.const 13.37)))`, /type mismatch/);
 wasmFailValidateText(`(module (global i32 (i32.add (i32.const 13) (i32.const 37))))`, /failed to read end/);
 
 wasmFailValidateText(`(module (global i32 (get_global 0)))`, /out of range/);
-wasmFailValidateText(`(module (global i32 (get_global 1)) (global i32 immutable (i32.const 1)))`, /out of range/);
+wasmFailValidateText(`(module (global i32 (get_global 1)) (global i32 (i32.const 1)))`, /out of range/);
 
 // Test a well-defined global section.
 function testInner(type, initialValue, nextValue, coercion, assertFunc = assertEq)
 {
     var module = wasmEvalText(`(module
+        (global (mut ${type}) (${type}.const ${initialValue}))
         (global ${type} (${type}.const ${initialValue}))
-        (global ${type} immutable (${type}.const ${initialValue}))
 
         (func $get (result ${type}) (get_global 0))
         (func $set (param ${type}) (set_global 0 (get_local 0)))
 
         (func $get_cst (result ${type}) (get_global 1))
 
         (export "get" $get)
         (export "get_cst" $get_cst)
@@ -41,33 +41,33 @@ function testInner(type, initialValue, n
     assertFunc(module.get_cst(), coercion(initialValue));
 }
 
 testInner('i32', 13, 37, x => x|0);
 testInner('f32', 13.37, 0.1989, Math.fround);
 testInner('f64', 13.37, 0.1989, x => +x);
 
 // Semantic errors.
-wasmFailValidateText(`(module (global i32 (i32.const 1337)) (func (set_global 1 (i32.const 0))))`, /out of range/);
-wasmFailValidateText(`(module (global i32 immutable (i32.const 1337)) (func (set_global 0 (i32.const 0))))`, /can't write an immutable global/);
+wasmFailValidateText(`(module (global (mut i32) (i32.const 1337)) (func (set_global 1 (i32.const 0))))`, /out of range/);
+wasmFailValidateText(`(module (global i32 (i32.const 1337)) (func (set_global 0 (i32.const 0))))`, /can't write an immutable global/);
 
 // Big module with many variables: test that setting one doesn't overwrite the
 // other ones.
 function get_set(i, type) { return `
     (func $get_${i} (result ${type}) (get_global ${i}))
     (func $set_${i} (param ${type}) (set_global ${i} (get_local 0)))
 `
 }
 
 var module = wasmEvalText(`(module
-    (global i32 (i32.const 42))
-    (global i32 (i32.const 10))
-    (global f32 (f32.const 13.37))
-    (global f64 (f64.const 13.37))
-    (global i32 (i32.const -18))
+    (global (mut i32) (i32.const 42))
+    (global (mut i32) (i32.const 10))
+    (global (mut f32) (f32.const 13.37))
+    (global (mut f64) (f64.const 13.37))
+    (global (mut i32) (i32.const -18))
 
     ${get_set(0, 'i32')}
     ${get_set(1, 'i32')}
     ${get_set(2, 'f32')}
     ${get_set(3, 'f64')}
     ${get_set(4, 'i32')}
 
     (export "get0" $get_0) (export "set0" $set_0)
@@ -88,70 +88,70 @@ for (let i = 0; i < 5; i++) {
             continue;
         assertEq(module[`get${j}`](), values[j]);
     }
     assertEq(module[`set${i}`](values[i]), undefined);
     assertEq(module[`get${i}`](), values[i]);
 }
 
 // Initializer expressions can also be used in elem section initializers.
-wasmFailValidateText(`(module (import "globals" "a" (global f32 immutable)) (table 4 anyfunc) (elem (get_global 0) $f) (func $f))`, /type mismatch/);
+wasmFailValidateText(`(module (import "globals" "a" (global f32)) (table 4 anyfunc) (elem (get_global 0) $f) (func $f))`, /type mismatch/);
 
 module = wasmEvalText(`(module
-    (import "globals" "a" (global i32 immutable))
+    (import "globals" "a" (global i32))
     (table (export "tbl") 4 anyfunc)
     (elem (get_global 0) $f)
     (func $f)
     (export "f" $f)
 )`, {
     globals: {
         a: 1
     }
 }).exports;
 assertEq(module.f, module.tbl.get(1));
 
 // Import/export rules.
-wasmFailValidateText(`(module (import "globals" "x" (global i32)))`, /can't import.* mutable globals in the MVP/);
-wasmFailValidateText(`(module (global i32 (i32.const 42)) (export "" global 0))`, /can't .*export mutable globals in the MVP/);
+wasmFailValidateText(`(module (import "globals" "x" (global (mut i32))))`, /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/);
 
 // Import/export semantics.
 module = wasmEvalText(`(module
- (import $g "globals" "x" (global i32 immutable))
+ (import $g "globals" "x" (global i32))
  (func $get (result i32) (get_global $g))
  (export "getter" $get)
  (export "value" global 0)
 )`, { globals: {x: 42} }).exports;
 
 assertEq(module.getter(), 42);
 assertEq(module.value, 42);
 
 // Imported globals and locally defined globals use the same index space.
 module = wasmEvalText(`(module
- (import "globals" "x" (global i32 immutable))
- (global i32 immutable (i32.const 1337))
+ (import "globals" "x" (global i32))
+ (global i32 (i32.const 1337))
  (export "imported" global 0)
  (export "defined" global 1)
 )`, { globals: {x: 42} }).exports;
 
 assertEq(module.imported, 42);
 assertEq(module.defined, 1337);
 
 // Initializer expressions can reference an imported immutable global.
-wasmFailValidateText(`(module (global f32 immutable (f32.const 13.37)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
 wasmFailValidateText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
-wasmFailValidateText(`(module (global i32 (i32.const 0)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
+wasmFailValidateText(`(module (global (mut f32) (f32.const 13.37)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
+wasmFailValidateText(`(module (global (mut i32) (i32.const 0)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
 
-wasmFailValidateText(`(module (import "globals" "a" (global f32 immutable)) (global i32 (get_global 0)))`, /type mismatch/);
+wasmFailValidateText(`(module (import "globals" "a" (global f32)) (global i32 (get_global 0)))`, /type mismatch/);
 
 function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = assertEq) {
     var module = wasmEvalText(`(module
-        (import "globals" "a" (global ${type} immutable))
+        (import "globals" "a" (global ${type}))
 
+        (global (mut ${type}) (get_global 0))
         (global ${type} (get_global 0))
-        (global ${type} immutable (get_global 0))
 
         (func $get0 (result ${type}) (get_global 0))
 
         (func $get1 (result ${type}) (get_global 1))
         (func $set1 (param ${type}) (set_global 1 (get_local 0)))
 
         (func $get_cst (result ${type}) (get_global 2))
 
@@ -177,26 +177,26 @@ 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.
 {
-    wasmFailValidateText(`(module (import "globals" "x" (global i64 immutable)))`, /can't import.* an Int64 global/);
-    wasmFailValidateText(`(module (global i64 immutable (i64.const 42)) (export "" global 0))`, /can't .*export an Int64 global/);
+    wasmFailValidateText(`(module (import "globals" "x" (global i64)))`, /can't import.* an Int64 global/);
+    wasmFailValidateText(`(module (global i64 (i64.const 42)) (export "" global 0))`, /can't .*export an Int64 global/);
 
     setJitCompilerOption('wasm.test-mode', 1);
     testInner('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
     testInitExpr('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
 
     module = wasmEvalText(`(module
-     (import "globals" "x" (global i64 immutable))
-     (global i64 immutable (i64.const 0xFAFADADABABA))
+     (import "globals" "x" (global i64))
+     (global i64 (i64.const 0xFAFADADABABA))
      (export "imported" global 0)
      (export "defined" global 1)
     )`, { globals: {x: createI64('0x1234567887654321')} }).exports;
 
     assertEqI64(module.imported, createI64('0x1234567887654321'));
     assertEqI64(module.defined, createI64('0xFAFADADABABA'));
 
     setJitCompilerOption('wasm.test-mode', 0);
--- a/js/src/jit-test/tests/wasm/import-export.js
+++ b/js/src/jit-test/tests/wasm/import-export.js
@@ -361,35 +361,35 @@ assertEq(i8[2], 0x0);
 assertEq(i8[100], 0xc);
 assertEq(i8[101], 0xd);
 assertEq(i8[102], 0x0);
 
 // Data segments with imported offsets
 
 var m = new Module(wasmTextToBinary(`
     (module
-        (import "glob" "a" (global i32 immutable))
+        (import "glob" "a" (global i32))
         (memory 1)
         (data (get_global 0) "\\0a\\0b"))
 `));
 assertEq(new Instance(m, {glob:{a:0}}) instanceof Instance, true);
 assertEq(new Instance(m, {glob:{a:(64*1024 - 2)}}) instanceof Instance, true);
 assertErrorMessage(() => new Instance(m, {glob:{a:(64*1024 - 1)}}), RangeError, /data segment does not fit/);
 assertErrorMessage(() => new Instance(m, {glob:{a:64*1024}}), RangeError, /data segment does not fit/);
 
 // Errors during segment initialization do not have observable effects
 // and are checked against the actual memory/table length, not the declared
 // initial length.
 
 var m = new Module(wasmTextToBinary(`
     (module
         (import "a" "mem" (memory 1))
         (import "a" "tbl" (table 1 anyfunc))
-        (import $memOff "a" "memOff" (global i32 immutable))
-        (import $tblOff "a" "tblOff" (global i32 immutable))
+        (import $memOff "a" "memOff" (global i32))
+        (import $tblOff "a" "tblOff" (global i32))
         (func $f)
         (func $g)
         (data (i32.const 0) "\\01")
         (elem (i32.const 0) $f)
         (data (get_global $memOff) "\\02")
         (elem (get_global $tblOff) $g)
         (export "f" $f)
         (export "g" $g))
@@ -460,17 +460,17 @@ var i2 = new Instance(new Module(wasmTex
     (type $v2i (func (result i32)))
     (func $call (param i32) (result i32) (call_indirect $v2i (get_local 0)))
     (export "call" $call)
 )`)), {a:{b:i1.exports.f}});
 assertEq(i2.exports.call(0), 0x42);
 assertEq(i2.exports.call(1), 0x13);
 
 var m = new Module(wasmTextToBinary(`(module
-    (import $val "a" "val" (global i32 immutable))
+    (import $val "a" "val" (global i32))
     (import $next "a" "next" (result i32))
     (memory 1)
     (func $start (i32.store (i32.const 0) (get_global $val)))
     (start $start)
     (func $call (result i32)
         (i32.add
             (get_global $val)
             (i32.add
--- a/js/src/jit-test/tests/wasm/nan-semantics.js
+++ b/js/src/jit-test/tests/wasm/nan-semantics.js
@@ -76,27 +76,27 @@ assertEqNaN(f64_qnan, { nan_low: 0x0, na
 
 // An example where a signaling nan gets transformed into a quiet nan:
 // snan + 0.0 = qnan
 var nan = wasmEvalText(`(module (func (result f32) (f32.add ${f32_snan_code} (f32.const 0))) (export "" 0))`).exports[""]();
 assertEqNaN(nan, f32_qnan);
 
 // Globals.
 var m = wasmEvalText(`(module
-    (import "globals" "x" (global f32 immutable))
+    (import "globals" "x" (global f32))
     (func (result f32) (get_global 0))
     (export "global" global 0)
     (export "test" 0))
 `, { globals: { x: f32_snan } }).exports;
 
 assertEqNaN(m.test(), f32_snan);
 assertEqNaN(m.global, f32_snan);
 
 var m = wasmEvalText(`(module
-    (import "globals" "x" (global f64 immutable))
+    (import "globals" "x" (global f64))
     (func (result f64) (get_global 0))
     (export "global" global 0)
     (export "test" 0))
 `, { globals: { x: f64_snan } }).exports;
 
 assertEqNaN(m.test(), f64_snan);
 assertEqNaN(m.global, f64_snan);
 
--- a/js/src/jit-test/tests/wasm/tables.js
+++ b/js/src/jit-test/tests/wasm/tables.js
@@ -11,28 +11,28 @@ var callee = i => `(func $f${i} (result 
 wasmFailValidateText(`(module (elem (i32.const 0) $f0) ${callee(0)})`, /table index out of range/);
 wasmFailValidateText(`(module (table 10 anyfunc) (elem (i32.const 0) 0))`, /table element out of range/);
 wasmFailValidateText(`(module (table 10 anyfunc) (func) (elem (i32.const 0) 0 1))`, /table element out of range/);
 wasmFailValidateText(`(module (table 10 anyfunc) (func) (elem (f32.const 0) 0) ${callee(0)})`, /type mismatch/);
 
 wasmFailValidateText(`(module (table 10 anyfunc) (elem (i32.const 10) $f0) ${callee(0)})`, /element segment does not fit/);
 wasmFailValidateText(`(module (table 10 anyfunc) (elem (i32.const 8) $f0 $f0 $f0) ${callee(0)})`, /element segment does not fit/);
 
-assertErrorMessage(() => wasmEvalText(`(module (table 10 anyfunc) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:10}}), RangeError, /elem segment does not fit/);
-assertErrorMessage(() => wasmEvalText(`(module (table 10 anyfunc) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}), RangeError, /elem segment does not fit/);
+assertErrorMessage(() => wasmEvalText(`(module (table 10 anyfunc) (import "globals" "a" (global i32)) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:10}}), RangeError, /elem segment does not fit/);
+assertErrorMessage(() => wasmEvalText(`(module (table 10 anyfunc) (import "globals" "a" (global i32)) (elem (get_global 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}), RangeError, /elem segment does not fit/);
 
 assertEq(new Module(wasmTextToBinary(`(module (table 10 anyfunc) (elem (i32.const 1) $f0 $f0) (elem (i32.const 0) $f0) ${callee(0)})`)) instanceof Module, true);
 assertEq(new Module(wasmTextToBinary(`(module (table 10 anyfunc) (elem (i32.const 1) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`)) instanceof Module, true);
-wasmEvalText(`(module (table 10 anyfunc) (import "globals" "a" (global i32 immutable)) (elem (i32.const 1) $f0 $f0) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:0}});
-wasmEvalText(`(module (table 10 anyfunc) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`, {globals:{a:1}});
+wasmEvalText(`(module (table 10 anyfunc) (import "globals" "a" (global i32)) (elem (i32.const 1) $f0 $f0) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:0}});
+wasmEvalText(`(module (table 10 anyfunc) (import "globals" "a" (global i32)) (elem (get_global 0) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`, {globals:{a:1}});
 
 var m = new Module(wasmTextToBinary(`
     (module
         (import "globals" "table" (table 10 anyfunc))
-        (import "globals" "a" (global i32 immutable))
+        (import "globals" "a" (global i32))
         (elem (get_global 0) $f0 $f0)
         ${callee(0)})
 `));
 var tbl = new Table({initial:50, element:"anyfunc"});
 assertEq(new Instance(m, {globals:{a:20, table:tbl}}) instanceof Instance, true);
 assertErrorMessage(() => new Instance(m, {globals:{a:50, table:tbl}}), RangeError, /elem segment does not fit/);
 
 var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
--- a/js/src/jit-test/tests/wasm/text.js
+++ b/js/src/jit-test/tests/wasm/text.js
@@ -87,10 +87,30 @@ assertErrorMessage(() => wasmEvalText(`
         (type $tf (func (param i32) (result i32)))
         (func (import "mod" "a") (type $tf))
         (func (export "f1"))
         (func (import "mod" "b") (type $tf))
         (func (export "f2"))
     )
 `), SyntaxError, /import after function definition/);
 
+// Globals.
+assertErrorMessage(() => wasmEvalText('(module (global $t (export)))'), SyntaxError, parsingError);
+assertErrorMessage(() => wasmEvalText('(module (global $t (export "g")))'), SyntaxError, parsingError);
+assertErrorMessage(() => wasmEvalText('(module (global $t (export "g") i32))'), SyntaxError, parsingError);
+wasmEvalText('(module (global $t (export "g") i32 (i32.const 42)))');
+
+assertErrorMessage(() => wasmEvalText('(module (global $t (import) i32))'), SyntaxError, parsingError);
+assertErrorMessage(() => wasmEvalText('(module (global $t (import "mod") i32))'), SyntaxError, parsingError);
+assertErrorMessage(() => wasmEvalText('(module (global $t (import "mod" "field")))'), SyntaxError, parsingError);
+assertErrorMessage(() => wasmEvalText('(module (global $t (import "mod" "field")) i32 (i32.const 42))'), SyntaxError, parsingError);
+wasmEvalText('(module (global $t (import "mod" "field") i32))', { mod: {field: 42} });
+
+assertErrorMessage(() => wasmEvalText(`
+    (module
+        (global (import "mod" "a") i32)
+        (global (export "f1") i32 (i32.const 42))
+        (global (import "mod" "b") i32)
+    )
+`), SyntaxError, /import after global definition/);
+
 // Note: the s-expression text format is temporary, this file is mostly just to
 // hold basic error smoke tests.