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 374150 8e98edd1c6d4a18239d043ec3df15449ab9e622e
parent 374099 c032cf7f54a06ef03254e450be74b5c54968a27b
child 374151 4a9b7fb94f2c102e956c9efd5ad26c61bd77590a
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1342893
milestone54.0a1
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());