Bug 1342893: Add signatures for function imports and exports in wasmTestMode; r=luke
authorBenjamin Bouvier <benj@benj.me>
Mon, 27 Feb 2017 13:39:59 +0100
changeset 345132 8e98edd1c6d4a18239d043ec3df15449ab9e622e
parent 345086 c032cf7f54a06ef03254e450be74b5c54968a27b
child 345133 4a9b7fb94f2c102e956c9efd5ad26c61bd77590a
push id38073
push usercbook@mozilla.com
push dateTue, 28 Feb 2017 12:04:44 +0000
treeherderautoland@0d6ca3d14e5b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1342893
milestone54.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 1342893: Add signatures for function imports and exports in wasmTestMode; r=luke MozReview-Commit-ID: 7s7nqLAioHr
js/src/jit-test/tests/wasm/import-export-sigs.js
js/src/wasm/WasmJS.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/import-export-sigs.js
@@ -0,0 +1,88 @@
+// Tests that function imports and function exports descriptors have
+// signatures, in the test mode only, for fuzzers.
+
+var module = new WebAssembly.Module(wasmTextToBinary(`(module
+  (import $vv "env" "v_v")
+  (export "v_v" $vv)
+
+  (import $vi "env" "v_i" (param i32))
+  (export "v_i" $vi)
+
+  (import $vI "env" "v_I" (param i64))
+  (export "v_I" $vI)
+
+  (import $vf "env" "v_f" (param f32))
+  (export "v_f" $vf)
+
+  (import $mem "env" "memory" (memory 0))
+  (export "mem" (memory $mem))
+
+  (import $vd "env" "v_d" (param f64))
+  (export "v_d" $vd)
+
+  (import $vfd "env" "v_fd" (param f32) (param f64))
+  (export "v_fd" $vfd)
+
+  (import $vIfifd "env" "v_Ififd" (param i64) (param f32) (param i32) (param f32) (param f64))
+  (export "v_Ififd" $vIfifd)
+
+  (import $iv "env" "i_v" (result i32))
+  (export "i_v" $iv)
+
+  (import $Ii "env" "I_i" (result i64) (param i32))
+  (export "I_i" $Ii)
+
+  (import $table "env" "table" (table 0 anyfunc))
+  (export "table" (table $table))
+
+  (import $fd "env" "f_d" (result f32) (param f64))
+  (export "f_d" $fd)
+
+  (import $dffd "env" "d_ffd" (result f64) (param f32) (param f32) (param f64))
+  (export "d_ffd" $dffd)
+)`));
+
+for (let desc of WebAssembly.Module.imports(module)) {
+    assertEq(typeof desc.signature, 'undefined');
+}
+for (let desc of WebAssembly.Module.exports(module)) {
+    assertEq(typeof desc.signature, 'undefined');
+}
+
+setJitCompilerOption('wasm.test-mode', 1);
+
+function shortToType(s) {
+    switch (s) {
+        case 'v': return "";
+        case 'i': return "i32";
+        case 'I': return "i64";
+        case 'f': return "f32";
+        case 'd': return "f64";
+    }
+    throw new Error("unexpected shorthand type");
+}
+
+function expectedSignature(name) {
+    let [ret, args] = name.split('_');
+
+    args = args.split('').map(shortToType).join(', ');
+    ret = shortToType(ret);
+
+    return `(${args}) -> (${ret})`;
+}
+
+for (let desc of WebAssembly.Module.imports(module)) {
+    if (desc.kind !== 'function') {
+        assertEq(typeof desc.signature, 'undefined');
+    } else {
+        assertEq(desc.signature, expectedSignature(desc.name))
+    }
+}
+
+for (let desc of WebAssembly.Module.exports(module)) {
+    if (desc.kind !== 'function') {
+        assertEq(typeof desc.signature, 'undefined');
+    } else {
+        assertEq(desc.signature, expectedSignature(desc.name))
+    }
+}
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/RangedPtr.h"
 
 #include "jsprf.h"
 
 #include "builtin/Promise.h"
 #include "jit/JitOptions.h"
 #include "vm/Interpreter.h"
 #include "vm/String.h"
+#include "vm/StringBuffer.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
 #include "jsobjinlines.h"
 
@@ -549,18 +550,19 @@ GetModuleArg(JSContext* cx, CallArgs arg
     return true;
 }
 
 struct KindNames
 {
     RootedPropertyName kind;
     RootedPropertyName table;
     RootedPropertyName memory;
+    RootedPropertyName signature;
 
-    explicit KindNames(JSContext* cx) : kind(cx), table(cx), memory(cx) {}
+    explicit KindNames(JSContext* cx) : kind(cx), table(cx), memory(cx), signature(cx) {}
 };
 
 static bool
 InitKindNames(JSContext* cx, KindNames* names)
 {
     JSAtom* kind = Atomize(cx, "kind", strlen("kind"));
     if (!kind)
         return false;
@@ -571,16 +573,21 @@ InitKindNames(JSContext* cx, KindNames* 
         return false;
     names->table = table->asPropertyName();
 
     JSAtom* memory = Atomize(cx, "memory", strlen("memory"));
     if (!memory)
         return false;
     names->memory = memory->asPropertyName();
 
+    JSAtom* signature = Atomize(cx, "signature", strlen("signature"));
+    if (!signature)
+        return false;
+    names->signature = signature->asPropertyName();
+
     return true;
 }
 
 static JSString*
 KindToString(JSContext* cx, const KindNames& names, DefinitionKind kind)
 {
     switch (kind) {
       case DefinitionKind::Function:
@@ -592,16 +599,50 @@ KindToString(JSContext* cx, const KindNa
       case DefinitionKind::Global:
         return cx->names().global;
     }
 
     MOZ_CRASH("invalid kind");
 }
 
 static JSString*
+SigToString(JSContext* cx, const Sig& sig)
+{
+    StringBuffer buf(cx);
+    if (!buf.append('('))
+        return nullptr;
+
+    bool first = true;
+    for (ValType arg : sig.args()) {
+        if (!first && !buf.append(", ", strlen(", ")))
+            return nullptr;
+
+        const char* argStr = ToCString(arg);
+        if (!buf.append(argStr, strlen(argStr)))
+            return nullptr;
+
+        first = false;
+    }
+
+    if (!buf.append(") -> (", strlen(") -> (")))
+        return nullptr;
+
+    if (sig.ret() != ExprType::Void) {
+        const char* retStr = ToCString(sig.ret());
+        if (!buf.append(retStr, strlen(retStr)))
+            return nullptr;
+    }
+
+    if (!buf.append(')'))
+        return nullptr;
+
+    return buf.finishString();
+}
+
+static JSString*
 UTF8CharsToString(JSContext* cx, const char* chars)
 {
     return NewStringCopyUTF8Z<CanGC>(cx, JS::ConstUTF8CharsZ(chars, strlen(chars)));
 }
 
 /* static */ bool
 WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -614,16 +655,17 @@ WasmModuleObject::imports(JSContext* cx,
     KindNames names(cx);
     if (!InitKindNames(cx, &names))
         return false;
 
     AutoValueVector elems(cx);
     if (!elems.reserve(module->imports().length()))
         return false;
 
+    size_t numFuncImport = 0;
     for (const Import& import : module->imports()) {
         Rooted<IdValueVector> props(cx, IdValueVector(cx));
         if (!props.reserve(3))
             return false;
 
         JSString* moduleStr = UTF8CharsToString(cx, import.module.get());
         if (!moduleStr)
             return false;
@@ -634,16 +676,24 @@ WasmModuleObject::imports(JSContext* cx,
             return false;
         props.infallibleAppend(IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
 
         JSString* kindStr = KindToString(cx, names, import.kind);
         if (!kindStr)
             return false;
         props.infallibleAppend(IdValuePair(NameToId(names.kind), StringValue(kindStr)));
 
+        if (JitOptions.wasmTestMode && import.kind == DefinitionKind::Function) {
+            JSString* sigStr = SigToString(cx, module->metadata().funcImports[numFuncImport++].sig());
+            if (!sigStr)
+                return false;
+            if (!props.append(IdValuePair(NameToId(names.signature), StringValue(sigStr))))
+                return false;
+        }
+
         JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(), props.length(), GenericObject);
         if (!obj)
             return false;
 
         elems.infallibleAppend(ObjectValue(*obj));
     }
 
     JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
@@ -666,31 +716,40 @@ WasmModuleObject::exports(JSContext* cx,
     KindNames names(cx);
     if (!InitKindNames(cx, &names))
         return false;
 
     AutoValueVector elems(cx);
     if (!elems.reserve(module->exports().length()))
         return false;
 
+    size_t numFuncExport = 0;
     for (const Export& exp : module->exports()) {
         Rooted<IdValueVector> props(cx, IdValueVector(cx));
         if (!props.reserve(2))
             return false;
 
         JSString* nameStr = UTF8CharsToString(cx, exp.fieldName());
         if (!nameStr)
             return false;
         props.infallibleAppend(IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
 
         JSString* kindStr = KindToString(cx, names, exp.kind());
         if (!kindStr)
             return false;
         props.infallibleAppend(IdValuePair(NameToId(names.kind), StringValue(kindStr)));
 
+        if (JitOptions.wasmTestMode && exp.kind() == DefinitionKind::Function) {
+            JSString* sigStr = SigToString(cx, module->metadata().funcExports[numFuncExport++].sig());
+            if (!sigStr)
+                return false;
+            if (!props.append(IdValuePair(NameToId(names.signature), StringValue(sigStr))))
+                return false;
+        }
+
         JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(), props.length(), GenericObject);
         if (!obj)
             return false;
 
         elems.infallibleAppend(ObjectValue(*obj));
     }
 
     JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());