From cff747d16b0ce2340bd5b97dcc5f86397e57d140 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 7 Oct 2016 17:14:17 +0200 Subject: [PATCH] Bug 1308056: wasm: add syntax for inline import/export in globals; r=luke MozReview-Commit-ID: DLc7oCNkbpi --HG-- extra : rebase_source : 2a9472e84eaf7363c9c9bb3c5a8e1b03a55d33ae --- js/src/asmjs/WasmTextToBinary.cpp | 84 ++++++++++++++++----- js/src/jit-test/tests/wasm/globals.js | 52 ++++++------- js/src/jit-test/tests/wasm/import-export.js | 8 +- js/src/jit-test/tests/wasm/nan-semantics.js | 4 +- js/src/jit-test/tests/wasm/tables.js | 10 +-- js/src/jit-test/tests/wasm/text.js | 20 +++++ 6 files changed, 121 insertions(+), 57 deletions(-) diff --git a/js/src/asmjs/WasmTextToBinary.cpp b/js/src/asmjs/WasmTextToBinary.cpp index 0abe683c08e0..01a3be43dad4 100644 --- a/js/src/asmjs/WasmTextToBinary.cpp +++ b/js/src/asmjs/WasmTextToBinary.cpp @@ -93,7 +93,6 @@ class WasmToken GetLocal, Global, If, - Immutable, Import, Index, Memory, @@ -102,6 +101,7 @@ class WasmToken Local, Loop, Module, + Mutable, Name, Nop, Offset, @@ -296,7 +296,7 @@ class WasmToken case Float: case Func: case Global: - case Immutable: + case Mutable: case Import: case Index: case Memory: @@ -1377,8 +1377,6 @@ WasmTokenStream::next() } 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")) @@ -1399,6 +1397,8 @@ WasmTokenStream::next() 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': @@ -2798,15 +2798,18 @@ ParseStartFunc(WasmParseContext& c, WasmToken token, AstModule* module) static bool ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, uint32_t* flags) { - if (!c.ts.match(WasmToken::ValueType, typeToken, c.error)) - return false; + // 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; + } - // Mutable by default. - *flags = 0x1; - if (c.ts.getIf(WasmToken::Immutable)) - *flags = 0x0; - - return true; + return c.ts.match(WasmToken::ValueType, typeToken, c.error); } static bool @@ -2861,17 +2864,22 @@ ParseImport(WasmParseContext& c, AstModule* module) 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)) @@ -3084,21 +3092,58 @@ ParseElemSegment(WasmParseContext& c) 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 false; - return new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init)); + auto* glob = new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init)); + return glob && module->append(glob); } static AstModule* @@ -3136,8 +3181,7 @@ ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error) break; } case WasmToken::Global: { - AstGlobal* global = ParseGlobal(c); - if (!global || !module->append(global)) + if (!ParseGlobal(c, module)) return nullptr; break; } diff --git a/js/src/jit-test/tests/wasm/globals.js b/js/src/jit-test/tests/wasm/globals.js index 2f8c5b50d98e..6e3682479c86 100644 --- a/js/src/jit-test/tests/wasm/globals.js +++ b/js/src/jit-test/tests/wasm/globals.js @@ -6,7 +6,7 @@ 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/); @@ -14,14 +14,14 @@ 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))) @@ -46,8 +46,8 @@ 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. @@ -58,11 +58,11 @@ function get_set(i, type) { return ` } 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')} @@ -93,10 +93,10 @@ for (let i = 0; i < 5; 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) @@ -109,12 +109,12 @@ module = wasmEvalText(`(module 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) @@ -125,8 +125,8 @@ 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; @@ -135,18 +135,18 @@ 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)) @@ -182,16 +182,16 @@ 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; diff --git a/js/src/jit-test/tests/wasm/import-export.js b/js/src/jit-test/tests/wasm/import-export.js index e97e1fa95dce..1af00098a468 100644 --- a/js/src/jit-test/tests/wasm/import-export.js +++ b/js/src/jit-test/tests/wasm/import-export.js @@ -366,7 +366,7 @@ assertEq(i8[102], 0x0); 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")) `)); @@ -383,8 +383,8 @@ 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") @@ -465,7 +465,7 @@ 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))) diff --git a/js/src/jit-test/tests/wasm/nan-semantics.js b/js/src/jit-test/tests/wasm/nan-semantics.js index 9475e3fecea1..ec3239d43761 100644 --- a/js/src/jit-test/tests/wasm/nan-semantics.js +++ b/js/src/jit-test/tests/wasm/nan-semantics.js @@ -81,7 +81,7 @@ 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)) @@ -91,7 +91,7 @@ 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)) diff --git a/js/src/jit-test/tests/wasm/tables.js b/js/src/jit-test/tests/wasm/tables.js index 1e7872a0e123..9a9675c5f712 100644 --- a/js/src/jit-test/tests/wasm/tables.js +++ b/js/src/jit-test/tests/wasm/tables.js @@ -16,18 +16,18 @@ wasmFailValidateText(`(module (table 10 anyfunc) (func) (elem (f32.const 0) 0) $ 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)}) `)); diff --git a/js/src/jit-test/tests/wasm/text.js b/js/src/jit-test/tests/wasm/text.js index f3ca4a9aaf0a..8440a2f7395f 100644 --- a/js/src/jit-test/tests/wasm/text.js +++ b/js/src/jit-test/tests/wasm/text.js @@ -92,5 +92,25 @@ assertErrorMessage(() => wasmEvalText(` ) `), 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.