зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
97df04e212
Коммит
b66823a395
|
@ -2494,100 +2494,49 @@ ParseLocalOrParam(WasmParseContext& c, AstNameVector* locals, AstValTypeVector*
|
||||||
localTypes->append(token.valueType());
|
localTypes->append(token.valueType());
|
||||||
}
|
}
|
||||||
|
|
||||||
static AstFunc*
|
static bool
|
||||||
ParseFunc(WasmParseContext& c, AstModule* module)
|
ParseInlineImport(WasmParseContext& c, InlineImport* import)
|
||||||
{
|
{
|
||||||
AstValTypeVector vars(c.lifo);
|
return c.ts.match(WasmToken::Text, &import->module, c.error) &&
|
||||||
AstValTypeVector args(c.lifo);
|
c.ts.match(WasmToken::Text, &import->field, c.error);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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);
|
AstValTypeVector args(c.lifo);
|
||||||
ExprType result = ExprType::Void;
|
ExprType result = ExprType::Void;
|
||||||
|
@ -2615,6 +2564,121 @@ ParseFuncType(WasmParseContext& c, AstSig* sig)
|
||||||
return true;
|
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*
|
static AstSig*
|
||||||
ParseTypeDef(WasmParseContext& c)
|
ParseTypeDef(WasmParseContext& c)
|
||||||
{
|
{
|
||||||
|
@ -2626,7 +2690,7 @@ ParseTypeDef(WasmParseContext& c)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
AstSig sig(c.lifo);
|
AstSig sig(c.lifo);
|
||||||
if (!ParseFuncType(c, &sig))
|
if (!ParseFuncSig(c, &sig))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||||
|
@ -2809,26 +2873,13 @@ ParseImport(WasmParseContext& c, AstModule* module)
|
||||||
if (c.ts.getIf(WasmToken::Func)) {
|
if (c.ts.getIf(WasmToken::Func)) {
|
||||||
AstName name = c.ts.getIfName();
|
AstName name = c.ts.getIfName();
|
||||||
|
|
||||||
WasmToken token;
|
AstRef sigRef;
|
||||||
if (c.ts.getIf(WasmToken::Type, &token)) {
|
if (!ParseFuncType(c, &sigRef, module))
|
||||||
if (!c.ts.matchRef(&sigRef, c.error))
|
|
||||||
return nullptr;
|
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);
|
|
||||||
}
|
|
||||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
|
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(), sigRef);
|
||||||
sigRef);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.ts.getIf(WasmToken::Type)) {
|
if (c.ts.getIf(WasmToken::Type)) {
|
||||||
|
@ -2843,7 +2894,7 @@ ParseImport(WasmParseContext& c, AstModule* module)
|
||||||
|
|
||||||
if (sigRef.isInvalid()) {
|
if (sigRef.isInvalid()) {
|
||||||
AstSig sig(c.lifo);
|
AstSig sig(c.lifo);
|
||||||
if (!ParseFuncType(c, &sig))
|
if (!ParseFuncSig(c, &sig))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
uint32_t sigIndex;
|
uint32_t sigIndex;
|
||||||
|
@ -2930,25 +2981,6 @@ ParseExport(WasmParseContext& c)
|
||||||
return nullptr;
|
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
|
static bool
|
||||||
ParseTable(WasmParseContext& c, WasmToken token, AstModule* module)
|
ParseTable(WasmParseContext& c, WasmToken token, AstModule* module)
|
||||||
{
|
{
|
||||||
|
@ -3139,8 +3171,7 @@ ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WasmToken::Func: {
|
case WasmToken::Func: {
|
||||||
AstFunc* func = ParseFunc(c, module);
|
if (!ParseFunc(c, module))
|
||||||
if (!func || !module->append(func))
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ const Instance = WebAssembly.Instance;
|
||||||
const Table = WebAssembly.Table;
|
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 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
|
// A table should not hold exported functions alive and exported functions
|
||||||
// should not hold their originating table alive. Live exported functions should
|
// 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/);
|
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 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;
|
var call = wasmEvalText(`(module (table 10 anyfunc) ${callee(0)} ${caller})`).exports.call;
|
||||||
assertErrorMessage(() => call(0), Error, /indirect call to null/);
|
assertErrorMessage(() => call(0), Error, /indirect call to null/);
|
||||||
|
@ -155,9 +155,9 @@ var call = wasmEvalText(`(module
|
||||||
(type $v2i2 (func (result i32)))
|
(type $v2i2 (func (result i32)))
|
||||||
(type $i2v (func (param i32)))
|
(type $i2v (func (param i32)))
|
||||||
(table anyfunc (elem $a $b $c))
|
(table anyfunc (elem $a $b $c))
|
||||||
(func $a (type $v2i1) (result i32) (i32.const 0))
|
(func $a (type $v2i1) (i32.const 0))
|
||||||
(func $b (type $v2i2) (result i32) (i32.const 1))
|
(func $b (type $v2i2) (i32.const 1))
|
||||||
(func $c (type $i2v) (param i32))
|
(func $c (type $i2v))
|
||||||
(func $call (param i32) (result i32) (call_indirect $v2i1 (get_local 0)))
|
(func $call (param i32) (result i32) (call_indirect $v2i1 (get_local 0)))
|
||||||
(export "call" $call)
|
(export "call" $call)
|
||||||
)`).exports.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 (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);
|
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
|
// Note: the s-expression text format is temporary, this file is mostly just to
|
||||||
// hold basic error smoke tests.
|
// hold basic error smoke tests.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче