Bug 1308056: wasm: add syntax for inline import/export in globals; r=luke

MozReview-Commit-ID: DLc7oCNkbpi

--HG--
extra : rebase_source : d531b8af2b465d68301590e392bed712396ccd31
This commit is contained in:
Benjamin Bouvier 2016-10-07 17:14:17 +02:00
Родитель b66823a395
Коммит 66fccf7fcf
6 изменённых файлов: 121 добавлений и 57 удалений

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;

Просмотреть файл

@ -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)))

Просмотреть файл

@ -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))

Просмотреть файл

@ -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)})
`));

Просмотреть файл

@ -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.