From b66823a3950f23da256f8c274ff260d2060eb647 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 7 Oct 2016 11:58:18 +0200 Subject: [PATCH] Bug 1308056: wasm: add syntax for inline import/export in functions; r=luke MozReview-Commit-ID: 7lrs8wG3Tn0 --HG-- extra : rebase_source : a9031389b6ee9da5236f3b8a9a0f85df86765ded --- js/src/asmjs/WasmTextToBinary.cpp | 291 ++++++++++++++----------- js/src/jit-test/tests/wasm/table-gc.js | 2 +- js/src/jit-test/tests/wasm/tables.js | 8 +- js/src/jit-test/tests/wasm/text.js | 31 +++ 4 files changed, 197 insertions(+), 135 deletions(-) diff --git a/js/src/asmjs/WasmTextToBinary.cpp b/js/src/asmjs/WasmTextToBinary.cpp index e663dd07933b..0abe683c08e0 100644 --- a/js/src/asmjs/WasmTextToBinary.cpp +++ b/js/src/asmjs/WasmTextToBinary.cpp @@ -2494,100 +2494,49 @@ ParseLocalOrParam(WasmParseContext& c, AstNameVector* locals, AstValTypeVector* localTypes->append(token.valueType()); } -static AstFunc* -ParseFunc(WasmParseContext& c, AstModule* module) +static bool +ParseInlineImport(WasmParseContext& c, InlineImport* import) { - AstValTypeVector vars(c.lifo); - AstValTypeVector args(c.lifo); - AstNameVector locals(c.lifo); - - AstName exportName = c.ts.getIfText(); - AstName funcName = c.ts.getIfName(); - - AstRef sig; - - WasmToken openParen; - if (exportName.empty() && c.ts.getIf(WasmToken::OpenParen, &openParen)) { - if (c.ts.getIf(WasmToken::Export)) { - WasmToken text; - if (!c.ts.match(WasmToken::Text, &text, c.error)) - return nullptr; - if (!c.ts.match(WasmToken::CloseParen, c.error)) - return nullptr; - exportName = text.text(); - } else { - c.ts.unget(openParen); - } - } - - if (!exportName.empty()) { - if (funcName.empty()) - funcName = exportName; - AstExport* exp = new(c.lifo) AstExport(exportName, DefinitionKind::Function, - AstRef(funcName, AstNoIndex)); - if (!exp || !module->append(exp)) - return nullptr; - } - - if (c.ts.getIf(WasmToken::OpenParen, &openParen)) { - if (c.ts.getIf(WasmToken::Type)) { - if (!c.ts.matchRef(&sig, c.error)) - return nullptr; - if (!c.ts.match(WasmToken::CloseParen, c.error)) - return nullptr; - } else { - c.ts.unget(openParen); - } - } - - AstExprVector body(c.lifo); - ExprType result = ExprType::Void; - - while (c.ts.getIf(WasmToken::OpenParen)) { - WasmToken token = c.ts.get(); - switch (token.kind()) { - case WasmToken::Local: - if (!ParseLocalOrParam(c, &locals, &vars)) - return nullptr; - break; - case WasmToken::Param: - if (!vars.empty()) { - c.ts.generateError(token, c.error); - return nullptr; - } - if (!ParseLocalOrParam(c, &locals, &args)) - return nullptr; - break; - case WasmToken::Result: - if (!ParseResult(c, &result)) - return nullptr; - break; - default: - c.ts.unget(token); - AstExpr* expr = ParseExprInsideParens(c); - if (!expr || !body.append(expr)) - return nullptr; - break; - } - if (!c.ts.match(WasmToken::CloseParen, c.error)) - return nullptr; - } - - if (!ParseExprList(c, &body, true)) - return nullptr; - - if (sig.isInvalid()) { - uint32_t sigIndex; - if (!module->declare(AstSig(Move(args), result), &sigIndex)) - return nullptr; - sig.setIndex(sigIndex); - } - - return new(c.lifo) AstFunc(funcName, sig, Move(vars), Move(locals), Move(body)); + return c.ts.match(WasmToken::Text, &import->module, c.error) && + c.ts.match(WasmToken::Text, &import->field, c.error); } static bool -ParseFuncType(WasmParseContext& c, AstSig* sig) +ParseInlineExport(WasmParseContext& c, DefinitionKind kind, AstModule* module, + AstRef ref = AstRef()) +{ + WasmToken name; + if (!c.ts.match(WasmToken::Text, &name, c.error)) + return false; + + AstExport* exp = nullptr; + if (!ref.isInvalid()) + exp = new(c.lifo) AstExport(name.text(), kind, ref); + else + exp = new(c.lifo) AstExport(name.text(), kind); + + return exp && module->append(exp); +} + +static bool +MaybeParseTypeUse(WasmParseContext& c, AstRef* sig) +{ + WasmToken openParen; + if (c.ts.getIf(WasmToken::OpenParen, &openParen)) { + if (c.ts.getIf(WasmToken::Type)) { + if (!c.ts.matchRef(sig, c.error)) + return false; + if (!c.ts.match(WasmToken::CloseParen, c.error)) + return false; + } else { + c.ts.unget(openParen); + } + } + return true; +} + +static bool +ParseFuncSig(WasmParseContext& c, AstSig* sig) { AstValTypeVector args(c.lifo); ExprType result = ExprType::Void; @@ -2615,6 +2564,121 @@ ParseFuncType(WasmParseContext& c, AstSig* sig) return true; } +static bool +ParseFuncType(WasmParseContext& c, AstRef* ref, AstModule* module) +{ + if (!MaybeParseTypeUse(c, ref)) + return false; + + if (ref->isInvalid()) { + AstSig sig(c.lifo); + if (!ParseFuncSig(c, &sig)) + return false; + uint32_t sigIndex; + if (!module->declare(Move(sig), &sigIndex)) + return false; + ref->setIndex(sigIndex); + } + + return true; +} + +static bool +ParseFunc(WasmParseContext& c, AstModule* module) +{ + AstValTypeVector vars(c.lifo); + AstValTypeVector args(c.lifo); + AstNameVector locals(c.lifo); + + AstName funcName = c.ts.getIfName(); + + // Inline imports and exports. + WasmToken openParen; + if (c.ts.getIf(WasmToken::OpenParen, &openParen)) { + if (c.ts.getIf(WasmToken::Import)) { + if (module->funcs().length()) { + c.ts.generateError(openParen, "import after function definition", c.error); + return false; + } + + InlineImport names; + if (!ParseInlineImport(c, &names)) + return false; + if (!c.ts.match(WasmToken::CloseParen, c.error)) + return false; + + AstRef sig; + if (!ParseFuncType(c, &sig, module)) + return false; + + auto* imp = new(c.lifo) AstImport(funcName, names.module.text(), names.field.text(), sig); + return imp && module->append(imp); + } + + if (c.ts.getIf(WasmToken::Export)) { + AstRef ref = funcName.empty() + ? AstRef(AstName(), module->funcImportNames().length() + module->funcs().length()) + : AstRef(funcName, AstNoIndex); + if (!ParseInlineExport(c, DefinitionKind::Function, module, ref)) + return false; + if (!c.ts.match(WasmToken::CloseParen, c.error)) + return false; + } else { + c.ts.unget(openParen); + } + } + + AstRef sigRef; + if (!MaybeParseTypeUse(c, &sigRef)) + return false; + + AstExprVector body(c.lifo); + + ExprType result = ExprType::Void; + while (c.ts.getIf(WasmToken::OpenParen)) { + WasmToken token = c.ts.get(); + switch (token.kind()) { + case WasmToken::Local: + if (!ParseLocalOrParam(c, &locals, &vars)) + return false; + break; + case WasmToken::Param: + if (!vars.empty()) { + c.ts.generateError(token, c.error); + return false; + } + if (!ParseLocalOrParam(c, &locals, &args)) + return false; + break; + case WasmToken::Result: + if (!ParseResult(c, &result)) + return false; + break; + default: + c.ts.unget(token); + AstExpr* expr = ParseExprInsideParens(c); + if (!expr || !body.append(expr)) + return false; + break; + } + if (!c.ts.match(WasmToken::CloseParen, c.error)) + return false; + } + + if (!ParseExprList(c, &body, true)) + return false; + + if (sigRef.isInvalid()) { + uint32_t sigIndex; + if (!module->declare(AstSig(Move(args), result), &sigIndex)) + return false; + sigRef.setIndex(sigIndex); + } + + auto* func = new(c.lifo) AstFunc(funcName, sigRef, Move(vars), Move(locals), Move(body)); + return func && module->append(func); +} + static AstSig* ParseTypeDef(WasmParseContext& c) { @@ -2626,7 +2690,7 @@ ParseTypeDef(WasmParseContext& c) return nullptr; AstSig sig(c.lifo); - if (!ParseFuncType(c, &sig)) + if (!ParseFuncSig(c, &sig)) return nullptr; if (!c.ts.match(WasmToken::CloseParen, c.error)) @@ -2809,26 +2873,13 @@ ParseImport(WasmParseContext& c, AstModule* module) if (c.ts.getIf(WasmToken::Func)) { AstName name = c.ts.getIfName(); - WasmToken token; - if (c.ts.getIf(WasmToken::Type, &token)) { - if (!c.ts.matchRef(&sigRef, c.error)) - return nullptr; - } else { - AstSig sig(c.lifo); - if (!ParseFuncType(c, &sig)) - return nullptr; - - uint32_t sigIndex; - if (!module->declare(Move(sig), &sigIndex)) - return nullptr; - - sigRef.setIndex(sigIndex); - } + 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); + return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(), sigRef); } if (c.ts.getIf(WasmToken::Type)) { @@ -2843,7 +2894,7 @@ ParseImport(WasmParseContext& c, AstModule* module) if (sigRef.isInvalid()) { AstSig sig(c.lifo); - if (!ParseFuncType(c, &sig)) + if (!ParseFuncSig(c, &sig)) return nullptr; uint32_t sigIndex; @@ -2930,25 +2981,6 @@ ParseExport(WasmParseContext& c) return nullptr; } -static bool -ParseInlineImport(WasmParseContext& c, InlineImport* import) -{ - return c.ts.match(WasmToken::Text, &import->module, c.error) && - c.ts.match(WasmToken::Text, &import->field, c.error); -} - -static bool -ParseInlineExport(WasmParseContext& c, DefinitionKind kind, AstModule* module) -{ - WasmToken name; - if (!c.ts.match(WasmToken::Text, &name, c.error)) - return false; - auto* exp = new(c.lifo) AstExport(name.text(), kind); - if (!exp || !module->append(exp)) - return false; - return true; -} - static bool ParseTable(WasmParseContext& c, WasmToken token, AstModule* module) { @@ -3139,8 +3171,7 @@ ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error) break; } case WasmToken::Func: { - AstFunc* func = ParseFunc(c, module); - if (!func || !module->append(func)) + if (!ParseFunc(c, module)) return nullptr; break; } diff --git a/js/src/jit-test/tests/wasm/table-gc.js b/js/src/jit-test/tests/wasm/table-gc.js index 654ef6d32e65..471504629736 100644 --- a/js/src/jit-test/tests/wasm/table-gc.js +++ b/js/src/jit-test/tests/wasm/table-gc.js @@ -9,7 +9,7 @@ const Instance = WebAssembly.Instance; const Table = WebAssembly.Table; var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)` -var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`; +var callee = i => `(func $f${i} (type $v2i) (i32.const ${i}))`; // A table should not hold exported functions alive and exported functions // should not hold their originating table alive. Live exported functions should diff --git a/js/src/jit-test/tests/wasm/tables.js b/js/src/jit-test/tests/wasm/tables.js index f425b5906d91..1e7872a0e123 100644 --- a/js/src/jit-test/tests/wasm/tables.js +++ b/js/src/jit-test/tests/wasm/tables.js @@ -36,7 +36,7 @@ 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)` -var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`; +var callee = i => `(func $f${i} (type $v2i) (i32.const ${i}))`; var call = wasmEvalText(`(module (table 10 anyfunc) ${callee(0)} ${caller})`).exports.call; assertErrorMessage(() => call(0), Error, /indirect call to null/); @@ -155,9 +155,9 @@ var call = wasmEvalText(`(module (type $v2i2 (func (result i32))) (type $i2v (func (param i32))) (table anyfunc (elem $a $b $c)) - (func $a (type $v2i1) (result i32) (i32.const 0)) - (func $b (type $v2i2) (result i32) (i32.const 1)) - (func $c (type $i2v) (param i32)) + (func $a (type $v2i1) (i32.const 0)) + (func $b (type $v2i2) (i32.const 1)) + (func $c (type $i2v)) (func $call (param i32) (result i32) (call_indirect $v2i1 (get_local 0))) (export "call" $call) )`).exports.call; diff --git a/js/src/jit-test/tests/wasm/text.js b/js/src/jit-test/tests/wasm/text.js index 2c7dec18d2ef..f3ca4a9aaf0a 100644 --- a/js/src/jit-test/tests/wasm/text.js +++ b/js/src/jit-test/tests/wasm/text.js @@ -61,5 +61,36 @@ assertErrorMessage(() => wasmEvalText('(module (table $t (export "mod") anyfunc assertEq(wasmEvalText('(module (table $t (export "tbl") anyfunc (elem)))').exports.tbl instanceof Table, true); assertEq(wasmEvalText('(module (func) (table $t (export "tbl") anyfunc (elem 0 0 0)))').exports.tbl instanceof Table, true); +// Functions. +assertErrorMessage(() => wasmEvalText('(module (func $t import))'), SyntaxError, parsingError); +assertErrorMessage(() => wasmEvalText('(module (func $t (import)))'), SyntaxError, parsingError); +assertErrorMessage(() => wasmEvalText('(module (func $t (import "mod")))'), SyntaxError, parsingError); +assertErrorMessage(() => wasmEvalText('(module (func $t (import "mod" "func" (local i32))))'), SyntaxError, parsingError); + +const func = i => 42 + i; +wasmEvalText('(module (func $t (import "mod" "func")))', { mod: {func} }); +wasmEvalText('(module (func $t (import "mod" "func")(param i32)))', { mod: {func} }); +wasmEvalText('(module (func $t (import "mod" "func")(result i32)))', { mod: {func} }); +wasmEvalText('(module (func $t (import "mod" "func")(param i32) (result i32)))', { mod: {func} }); +wasmEvalText('(module (func $t (import "mod" "func")(result i32) (param i32)))', { mod: {func} }); + +assertErrorMessage(() => wasmEvalText('(module (func $t (import "mod" "func") (type)))', { mod: {func} }), SyntaxError, parsingError); +wasmEvalText('(module (type $t (func)) (func $t (import "mod" "func") (type $t)))', { mod: {func} }); + +assertErrorMessage(() => wasmEvalText('(module (func $t (export))))'), SyntaxError, parsingError); +wasmEvalText('(module (func (export "f")))'); +wasmEvalText('(module (func $f (export "f")))'); +wasmEvalText('(module (func $f (export "f") (result i32) (param i32) (i32.add (get_local 0) (i32.const 42))))'); + +assertErrorMessage(() => wasmEvalText(` + (module + (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/); + // Note: the s-expression text format is temporary, this file is mostly just to // hold basic error smoke tests.