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

MozReview-Commit-ID: 7lrs8wG3Tn0

--HG--
extra : rebase_source : a9031389b6ee9da5236f3b8a9a0f85df86765ded
This commit is contained in:
Benjamin Bouvier 2016-10-07 11:58:18 +02:00
Родитель 97df04e212
Коммит b66823a395
4 изменённых файлов: 197 добавлений и 135 удалений

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

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

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

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

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

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

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

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