Bug 1308056: wasm: add syntax for inline import/export in functions; r=luke
☠☠ backed out by ced9e3a01e45 ☠ ☠
authorBenjamin Bouvier <benj@benj.me>
Fri, 07 Oct 2016 11:58:18 +0200
changeset 360366 c417fb206f6cb7fb6c4059f437f8f59f9c18b46e
parent 360365 7a39a87c6bb9f6cbe5cefc9d2e36f43710e1d57f
child 360367 913ccaec26d5f0f137f088482f6fd02d8e99b323
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1308056
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1308056: wasm: add syntax for inline import/export in functions; r=luke MozReview-Commit-ID: 7lrs8wG3Tn0
js/src/asmjs/WasmTextToBinary.cpp
js/src/jit-test/tests/wasm/table-gc.js
js/src/jit-test/tests/wasm/tables.js
js/src/jit-test/tests/wasm/text.js
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -2489,110 +2489,59 @@ ParseLocalOrParam(WasmParseContext& c, A
         return locals->append(AstName()) && ParseValueTypeList(c, localTypes);
 
     WasmToken token;
     return locals->append(c.ts.get().name()) &&
            c.ts.match(WasmToken::ValueType, &token, c.error) &&
            localTypes->append(token.valueType());
 }
 
-static AstFunc*
-ParseFunc(WasmParseContext& c, AstModule* module)
+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,
+                  AstRef ref = AstRef())
 {
-    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 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 (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.matchRef(sig, c.error))
+                return false;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
-                return nullptr;
+                return false;
         } 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 true;
 }
 
 static bool
-ParseFuncType(WasmParseContext& c, AstSig* sig)
+ParseFuncSig(WasmParseContext& c, AstSig* sig)
 {
     AstValTypeVector args(c.lifo);
     ExprType result = ExprType::Void;
 
     while (c.ts.getIf(WasmToken::OpenParen)) {
         WasmToken token = c.ts.get();
         switch (token.kind()) {
           case WasmToken::Param:
@@ -2610,28 +2559,143 @@ ParseFuncType(WasmParseContext& c, AstSi
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
     }
 
     *sig = AstSig(Move(args), result);
     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)
 {
     AstName name = c.ts.getIfName();
 
     if (!c.ts.match(WasmToken::OpenParen, c.error))
         return nullptr;
     if (!c.ts.match(WasmToken::Func, c.error))
         return nullptr;
 
     AstSig sig(c.lifo);
-    if (!ParseFuncType(c, &sig))
+    if (!ParseFuncSig(c, &sig))
         return nullptr;
 
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return nullptr;
 
     return new(c.lifo) AstSig(name, Move(sig));
 }
 
@@ -2804,51 +2868,38 @@ ParseImport(WasmParseContext& c, AstModu
             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();
 
-            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)) {
             if (!c.ts.matchRef(&sigRef, c.error))
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
         } else {
             c.ts.unget(openParen);
         }
     }
 
     if (sigRef.isInvalid()) {
         AstSig sig(c.lifo);
-        if (!ParseFuncType(c, &sig))
+        if (!ParseFuncSig(c, &sig))
             return nullptr;
 
         uint32_t sigIndex;
         if (!module->declare(Move(sig), &sigIndex))
             return nullptr;
         sigRef.setIndex(sigIndex);
     }
 
@@ -2926,35 +2977,16 @@ ParseExport(WasmParseContext& c)
         break;
     }
 
     c.ts.generateError(exportee, c.error);
     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)
 {
     AstName name = c.ts.getIfName();
 
     if (c.ts.getIf(WasmToken::OpenParen)) {
         // Either an import and we're done, or an export and continue.
         if (c.ts.getIf(WasmToken::Import)) {
             InlineImport names;
@@ -3134,18 +3166,17 @@ ParseModule(const char16_t* text, LifoAl
           }
           case WasmToken::Elem: {
             AstElemSegment* segment = ParseElemSegment(c);
             if (!segment || !module->append(segment))
                 return nullptr;
             break;
           }
           case WasmToken::Func: {
-            AstFunc* func = ParseFunc(c, module);
-            if (!func || !module->append(func))
+            if (!ParseFunc(c, module))
                 return nullptr;
             break;
           }
           default:
             c.ts.generateError(section, c.error);
             return nullptr;
         }
 
--- a/js/src/jit-test/tests/wasm/table-gc.js
+++ b/js/src/jit-test/tests/wasm/table-gc.js
@@ -4,17 +4,17 @@
 
 load(libdir + 'wasm.js');
 
 const Module = WebAssembly.Module;
 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
 // hold instances alive and instances hold imported tables alive. Nothing
 // should hold the export object alive.
 resetFinalizeCount();
 var i = wasmEvalText(`(module (table 2 anyfunc) (export "tbl" table) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`);
 var e = i.exports;
--- a/js/src/jit-test/tests/wasm/tables.js
+++ b/js/src/jit-test/tests/wasm/tables.js
@@ -31,17 +31,17 @@ var m = new Module(wasmTextToBinary(`
         (elem (get_global 0) $f0 $f0)
         ${callee(0)})
 `));
 var tbl = new Table({initial:50, element:"anyfunc"});
 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/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
 var call = wasmEvalText(`(module (table 10 anyfunc) (elem (i32.const 0)) ${callee(0)} ${caller})`).exports.call;
 assertErrorMessage(() => call(0), Error, /indirect call to null/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
@@ -150,19 +150,19 @@ assertErrorMessage(() => e3.call(2), Err
 
 // Call signatures are matched structurally:
 
 var call = wasmEvalText(`(module
     (type $v2i1 (func (result i32)))
     (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;
 assertEq(call(0), 0);
 assertEq(call(1), 1);
 assertErrorMessage(() => call(2), Error, /indirect call signature mismatch/);
 
 var call = wasmEvalText(`(module
--- a/js/src/jit-test/tests/wasm/text.js
+++ b/js/src/jit-test/tests/wasm/text.js
@@ -56,10 +56,41 @@ assertErrorMessage(() => wasmEvalText('(
 wasmEvalText('(module (table $t (import "mod" "field") 1 anyfunc))', {mod: {field: table}});
 
 assertErrorMessage(() => wasmEvalText('(module (table $t (export "mod") 1))'), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('(module (table $t (export "mod") anyfunc))'), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('(module (table $t (export "mod") anyfunc 1 2 3))'), SyntaxError, parsingError);
 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.