<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[211195] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/211195">211195</a></dd>
<dt>Author</dt> <dd>sbarati@apple.com</dd>
<dt>Date</dt> <dd>2017-01-25 18:38:41 -0800 (Wed, 25 Jan 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>WebAssembly JS API: coerce return values from imports
https://bugs.webkit.org/show_bug.cgi?id=165480
&lt;rdar://problem/29760318&gt;

Reviewed by Yusuke Suzuki.

JSTests:

* wasm/function-tests/function-import-return-value.js: Added.
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.const.tests.x.assert.eq):
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.const.tests.Math.fround):
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.let.type.of.Reflect.ownKeys):
(test.1):
(assert.truthy):
(assert.throws):

Source/JavaScriptCore:

This patch does proper coercion for all possible
JSValue return types from an imported function.

It also adds the spec-compliant code to throw an exception
when calling an import that has an i64 parameter or return
value.

* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::emitJumpIfException):
* jit/AssemblyHelpers.h:
* wasm/WasmB3IRGenerator.cpp:
* wasm/WasmBinding.cpp:
(JSC::Wasm::wasmToJs):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorejitAssemblyHelperscpp">trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitAssemblyHelpersh">trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h</a></li>
<li><a href="#trunkSourceJavaScriptCorewasmWasmB3IRGeneratorcpp">trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorewasmWasmBindingcpp">trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestswasmfunctiontestsfunctionimportreturnvaluejs">trunk/JSTests/wasm/function-tests/function-import-return-value.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (211194 => 211195)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/JSTests/ChangeLog        2017-01-26 02:38:41 UTC (rev 211195)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2017-01-25  Saam Barati  &lt;sbarati@apple.com&gt;
+
+        WebAssembly JS API: coerce return values from imports
+        https://bugs.webkit.org/show_bug.cgi?id=165480
+        &lt;rdar://problem/29760318&gt;
+
+        Reviewed by Yusuke Suzuki.
+
+        * wasm/function-tests/function-import-return-value.js: Added.
+        (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.const.tests.x.assert.eq):
+        (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.const.tests.Math.fround):
+        (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.let.type.of.Reflect.ownKeys):
+        (test.1):
+        (assert.truthy):
+        (assert.throws):
+
</ins><span class="cx"> 2017-01-25  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         jsc.cpp should have the $.agent stuff for testing SAB
</span></span></pre></div>
<a id="trunkJSTestswasmfunctiontestsfunctionimportreturnvaluejs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/wasm/function-tests/function-import-return-value.js (0 => 211195)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/function-tests/function-import-return-value.js                                (rev 0)
+++ trunk/JSTests/wasm/function-tests/function-import-return-value.js        2017-01-26 02:38:41 UTC (rev 211195)
</span><span class="lines">@@ -0,0 +1,247 @@
</span><ins>+import Builder from '../Builder.js'
+import * as assert from '../assert.js'
+
+{
+    let called = false;
+
+    const tests = {
+        i32: [
+            [20, (x) =&gt; assert.eq(x, 20)],
+            [20.888, (x) =&gt; assert.eq(x, 20.888 | 0)],
+            [Math.PI, (x) =&gt; assert.eq(x, Math.PI | 0)],
+            [{valueOf() { assert.truthy(!called); called = true; return 25; } }, (x) =&gt; { assert.truthy(called); assert.eq(x, 25); called = false; }],
+            [NaN, (x) =&gt; assert.eq(x, NaN | 0)],
+            [-0.0, (x) =&gt; assert.eq(1/x, Infinity)],
+            [undefined, (x) =&gt; assert.eq(x, undefined | 0)],
+            [null, (x) =&gt; assert.eq(x, null | 0)],
+            [Number.MAX_SAFE_INTEGER, (x) =&gt; assert.eq(x, Number.MAX_SAFE_INTEGER | 0)],
+            [2**32 - 1, (x) =&gt; assert.eq(x, (2**32 - 1) | 0)],
+            [2**32 - 1000, (x) =&gt; assert.eq(x, (2**32 - 1000) | 0)],
+            [-1000, (x) =&gt; assert.eq(x, -1000)],
+        ],
+        f32: [
+            [20, (x) =&gt; assert.eq(x, 20)],
+            [20.888, (x) =&gt; assert.eq(x, Math.fround(20.888))],
+            [Math.PI, (x) =&gt; assert.eq(x, Math.fround(Math.PI))],
+            [{valueOf() { assert.truthy(!called); called = true; return 25.82; } }, (x) =&gt; { assert.truthy(called); assert.eq(x, Math.fround(25.82)); called = false; }],
+            [NaN, (x) =&gt; assert.truthy(isNaN(x))],
+            [-0.0, (x) =&gt; assert.eq(1/x, -Infinity)],
+            [undefined, (x) =&gt; assert.truthy(isNaN(x))],
+            [null, (x) =&gt; assert.eq(x, 0)],
+            [Number.MAX_SAFE_INTEGER, (x) =&gt; assert.eq(x, Math.fround(Number.MAX_SAFE_INTEGER))],
+            [-1000, (x) =&gt; assert.eq(x, -1000)],
+        ],
+        f64: [
+            [20, (x) =&gt; assert.eq(x, 20)],
+            [2**24, (x) =&gt; assert.eq(x, 2**24)],
+            [2**52, (x) =&gt; assert.eq(x, 2**52)],
+            [20.8888888, (x) =&gt; assert.eq(x, 20.8888888)],
+            [Math.PI, (x) =&gt; assert.eq(x, Math.PI)],
+            [{valueOf() { assert.truthy(!called); called = true; return 25.82; } }, (x) =&gt; { assert.truthy(called); assert.eq(x, 25.82); called = false; }],
+            [NaN, (x) =&gt; assert.truthy(isNaN(x))],
+            [-0.0, (x) =&gt; assert.eq(1/x, -Infinity)],
+            [undefined, (x) =&gt; assert.truthy(isNaN(x))],
+            [null, (x) =&gt; assert.eq(x, 0)],
+            [Number.MAX_SAFE_INTEGER, (x) =&gt; assert.eq(x, Number.MAX_SAFE_INTEGER)],
+            [-1000, (x) =&gt; assert.eq(x, -1000)],
+        ]
+    };
+
+    for (let type of Reflect.ownKeys(tests)) {
+        const builder = new Builder()
+            .Type().End()
+            .Import()
+                .Function(&quot;imp&quot;, &quot;func&quot;, { params: [], ret: type})
+            .End()
+            .Function().End()
+            .Export()
+                .Function(&quot;foo&quot;)
+            .End()
+            .Code()
+                .Function(&quot;foo&quot;, {params: [], ret: type})
+                    .Call(0)
+                    .Return()
+                .End()
+            .End();
+
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+
+        for (let test of tests[type]) {
+            const func = () =&gt; {
+                return test[0];
+            };
+
+            const instance = new WebAssembly.Instance(module, {imp: {func}});
+            const ret = instance.exports.foo();
+            test[1](ret);
+        }
+    }
+}
+
+{
+    let types = [&quot;i32&quot;, &quot;f32&quot;, &quot;f64&quot;];
+    for (let type of types) {
+        const builder = new Builder()
+            .Type().End()
+            .Import()
+                .Function(&quot;imp&quot;, &quot;func&quot;, { params: [], ret: type})
+            .End()
+            .Function().End()
+            .Export()
+                .Function(&quot;foo&quot;)
+            .End()
+            .Code()
+                .Function(&quot;foo&quot;, {params: [], ret: type})
+                    .Call(0)
+                    .Unreachable()
+                .End()
+            .End();
+
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+        let error = null;
+        const func = () =&gt; {
+            return {
+                valueOf() {
+                    error = new Error;
+                    throw error;
+                }
+            };
+        };
+
+        const instance = new WebAssembly.Instance(module, {imp: {func}});
+        for (let i = 0; i &lt; 100; i++) {
+            let threw = false;
+            try {
+                instance.exports.foo();
+            } catch(e) {
+                assert.eq(e, error);
+                threw = true;
+                error = null;
+            }
+            assert.truthy(threw);
+        }
+    }
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Import()
+            .Function(&quot;imp&quot;, &quot;func&quot;, { params: [], ret: &quot;i64&quot;})
+        .End()
+        .Function().End()
+        .Export()
+            .Function(&quot;foo&quot;)
+        .End()
+        .Code()
+            .Function(&quot;foo&quot;, {params: [], ret: &quot;void&quot;})
+                .Call(0)
+                .Drop()
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const func = () =&gt; 20;
+    const instance = new WebAssembly.Instance(module, {imp: {func}});
+    for (let i = 0; i &lt; 100; i++) {
+        assert.throws(() =&gt; instance.exports.foo(), TypeError, &quot;i64 not allowed as return type or argument to an imported function&quot;);
+    }
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Import()
+            .Function(&quot;imp&quot;, &quot;func&quot;, { params: [&quot;i64&quot;], ret: &quot;void&quot;})
+        .End()
+        .Function().End()
+        .Export()
+            .Function(&quot;foo&quot;)
+        .End()
+        .Code()
+            .Function(&quot;foo&quot;, {params: [], ret: &quot;void&quot;})
+                 .I64Const(20)
+                .Call(0)
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const func = () =&gt; 20;
+    const instance = new WebAssembly.Instance(module, {imp: {func}});
+    for (let i = 0; i &lt; 100; i++) {
+        assert.throws(() =&gt; instance.exports.foo(), TypeError, &quot;i64 not allowed as return type or argument to an imported function&quot;);
+    }
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Import()
+            .Function(&quot;imp&quot;, &quot;func&quot;, { params: [&quot;i64&quot;], ret: &quot;void&quot;})
+        .End()
+        .Function().End()
+        .Export()
+            .Function(&quot;foo&quot;)
+        .End()
+        .Code()
+            .Function(&quot;foo&quot;, {params: [], ret: &quot;void&quot;})
+                 .I64Const(20)
+                .Call(0)
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    let called = false;
+    const func = () =&gt; {
+        called = true;
+    }
+    const instance = new WebAssembly.Instance(module, {imp: {func}});
+    for (let i = 0; i &lt; 100; i++) {
+        assert.throws(() =&gt; instance.exports.foo(), TypeError, &quot;i64 not allowed as return type or argument to an imported function&quot;);
+        assert.eq(called, false);
+    }
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Function().End()
+        .Export()
+            .Function(&quot;foo&quot;)
+        .End()
+        .Code()
+            .Function(&quot;foo&quot;, {params: [&quot;i64&quot;], ret: &quot;void&quot;})
+                 .I64Const(20)
+                .Call(0)
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    let called = false;
+    let value = {
+        valueOf() {
+            called = true;
+        }
+    };
+    const instance = new WebAssembly.Instance(module);
+    for (let i = 0; i &lt; 100; i++) {
+        assert.throws(() =&gt; instance.exports.foo(value), Error, &quot;WebAssembly function with an i64 argument can't be called from JavaScript&quot;);
+        assert.eq(called, false);
+    }
+}
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (211194 => 211195)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/JavaScriptCore/ChangeLog        2017-01-26 02:38:41 UTC (rev 211195)
</span><span class="lines">@@ -1,3 +1,25 @@
</span><ins>+2017-01-25  Saam Barati  &lt;sbarati@apple.com&gt;
+
+        WebAssembly JS API: coerce return values from imports
+        https://bugs.webkit.org/show_bug.cgi?id=165480
+        &lt;rdar://problem/29760318&gt;
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch does proper coercion for all possible
+        JSValue return types from an imported function.
+        
+        It also adds the spec-compliant code to throw an exception
+        when calling an import that has an i64 parameter or return
+        value.
+
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::emitJumpIfException):
+        * jit/AssemblyHelpers.h:
+        * wasm/WasmB3IRGenerator.cpp:
+        * wasm/WasmBinding.cpp:
+        (JSC::Wasm::wasmToJs):
+
</ins><span class="cx"> 2017-01-25  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         jsc.cpp should have the $.agent stuff for testing SAB
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitAssemblyHelperscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp (211194 => 211195)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp        2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp        2017-01-26 02:38:41 UTC (rev 211195)
</span><span class="lines">@@ -354,6 +354,11 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+AssemblyHelpers::Jump AssemblyHelpers::emitJumpIfException()
+{
+    return emitExceptionCheck(NormalExceptionCheck);
+}
+
</ins><span class="cx"> AssemblyHelpers::Jump AssemblyHelpers::emitExceptionCheck(ExceptionCheckKind kind, ExceptionJumpWidth width)
</span><span class="cx"> {
</span><span class="cx">     callExceptionFuzz();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitAssemblyHelpersh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h (211194 => 211195)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h        2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h        2017-01-26 02:38:41 UTC (rev 211195)
</span><span class="lines">@@ -1185,6 +1185,7 @@
</span><span class="cx">     JS_EXPORT_PRIVATE Jump emitExceptionCheck(
</span><span class="cx">         ExceptionCheckKind = NormalExceptionCheck, ExceptionJumpWidth = NormalJumpWidth);
</span><span class="cx">     JS_EXPORT_PRIVATE Jump emitNonPatchableExceptionCheck();
</span><ins>+    Jump emitJumpIfException();
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(SAMPLING_COUNTERS)
</span><span class="cx">     static void emitCount(MacroAssembler&amp; jit, AbstractSamplingCounter&amp; counter, int32_t increment = 1)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorewasmWasmB3IRGeneratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp (211194 => 211195)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp        2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp        2017-01-26 02:38:41 UTC (rev 211195)
</span><span class="lines">@@ -976,6 +976,7 @@
</span><span class="cx">             patchpoint-&gt;append(calleeCode, ValueRep::SomeRegister);
</span><span class="cx"> 
</span><span class="cx">             patchpoint-&gt;setGenerator([=] (CCallHelpers&amp; jit, const B3::StackmapGenerationParams&amp; params) {
</span><ins>+                AllowMacroScratchRegisterUsage allowScratch(jit);
</ins><span class="cx">                 jit.call(params[returnType == Void ? 0 : 1].gpr());
</span><span class="cx">             });
</span><span class="cx">         });
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorewasmWasmBindingcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp (211194 => 211195)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp        2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp        2017-01-26 02:38:41 UTC (rev 211195)
</span><span class="lines">@@ -28,15 +28,19 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(WEBASSEMBLY)
</span><span class="cx"> 
</span><del>-#include &quot;AssemblyHelpers.h&quot;
-#include &quot;JSCJSValueInlines.h&quot;
</del><ins>+#include &quot;CCallHelpers.h&quot;
+#include &quot;FrameTracers.h&quot;
+#include &quot;JITExceptions.h&quot;
+#include &quot;JSCInlines.h&quot;
</ins><span class="cx"> #include &quot;JSWebAssemblyInstance.h&quot;
</span><span class="cx"> #include &quot;LinkBuffer.h&quot;
</span><ins>+#include &quot;NativeErrorConstructor.h&quot;
</ins><span class="cx"> #include &quot;WasmCallingConvention.h&quot;
</span><ins>+#include &quot;WasmExceptionType.h&quot;
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC { namespace Wasm {
</span><span class="cx"> 
</span><del>-typedef AssemblyHelpers JIT;
</del><ins>+typedef CCallHelpers JIT;
</ins><span class="cx"> 
</span><span class="cx"> static void materializeImportJSCell(VM* vm, JIT&amp; jit, unsigned importIndex, GPRReg result)
</span><span class="cx"> {
</span><span class="lines">@@ -61,6 +65,55 @@
</span><span class="cx">     jit.store64(JIT::TrustedImm32(0), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::codeBlock * static_cast&lt;int&gt;(sizeof(Register)))); // FIXME Stop using 0 as codeBlocks. https://bugs.webkit.org/show_bug.cgi?id=165321
</span><span class="cx">     jit.storePtr(JIT::TrustedImmPtr(vm-&gt;webAssemblyToJSCallee.get()), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast&lt;int&gt;(sizeof(Register))));
</span><span class="cx"> 
</span><ins>+
+    {
+        bool hasBadI64Use = false;
+        hasBadI64Use |= signature-&gt;returnType() == I64;
+        for (unsigned argNum = 0; argNum &lt; argCount &amp;&amp; !hasBadI64Use; ++argNum) {
+            Type argType = signature-&gt;argument(argNum);
+            switch (argType) {
+            case Void:
+            case Func:
+            case Anyfunc:
+                RELEASE_ASSERT_NOT_REACHED();
+
+            case I64: {
+                hasBadI64Use = true;
+                break;
+            }
+
+            default:
+                break;
+            }
+        }
+
+        if (hasBadI64Use) {
+            jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer();
+            jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+            auto call = jit.call();
+            jit.jumpToExceptionHandler();
+
+            void (*throwBadI64)(ExecState*) = [] (ExecState* exec) -&gt; void {
+                VM* vm = &amp;exec-&gt;vm();
+                NativeCallFrameTracer tracer(vm, exec);
+
+                {
+                    auto throwScope = DECLARE_THROW_SCOPE(*vm);
+                    JSGlobalObject* globalObject = vm-&gt;topJSWebAssemblyInstance-&gt;globalObject();
+                    auto* error = ErrorInstance::create(exec, *vm, globalObject-&gt;typeErrorConstructor()-&gt;errorStructure(), ASCIILiteral(&quot;i64 not allowed as return type or argument to an imported function&quot;));
+                    throwException(exec, throwScope, error);
+                }
+
+                genericUnwind(vm, exec);
+                ASSERT(!!vm-&gt;callFrameForCatch);
+            };
+
+            LinkBuffer linkBuffer(*vm, jit, GLOBAL_THUNK_ID);
+            linkBuffer.link(call, throwBadI64);
+            return FINALIZE_CODE(linkBuffer, (&quot;WebAssembly-&gt;JavaScript invalid i64 use in import[%i]&quot;, importIndex));
+        }
+    }
+
</ins><span class="cx">     // Here we assume that the JS calling convention saves at least all the wasm callee saved. We therefore don't need to save and restore more registers since the wasm callee already took care of this.
</span><span class="cx">     RegisterSet missingCalleeSaves = wasmCC.m_calleeSaveRegisters;
</span><span class="cx">     missingCalleeSaves.exclude(jsCC.m_calleeSaveRegisters);
</span><span class="lines">@@ -74,26 +127,6 @@
</span><span class="cx">     const unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
</span><span class="cx">     jit.subPtr(MacroAssembler::TrustedImm32(stackOffset), MacroAssembler::stackPointerRegister);
</span><span class="cx">     JIT::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast&lt;ptrdiff_t&gt;(sizeof(CallerFrameAndPC)));
</span><del>-    
-    for (unsigned argNum = 0; argNum &lt; argCount; ++argNum) {
-        Type argType = signature-&gt;argument(argNum);
-        switch (argType) {
-        case Void:
-        case Func:
-        case Anyfunc:
-        case I64: {
-            // FIXME: Figure out the correct behavior here. I suspect we want such a stub to throw an exception immediately.
-            // if called. https://bugs.webkit.org/show_bug.cgi?id=165991
-            jit.breakpoint();
-            LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
-            return FINALIZE_CODE(patchBuffer, (&quot;WebAssembly import[%i] stub for signature %i&quot;, importIndex, signatureIndex));
-        }
-        case I32:
-        case F32:
-        case F64:
-            continue;
-        }
-    }
</del><span class="cx"> 
</span><span class="cx">     // FIXME make these loops which switch on Signature if there are many arguments on the stack. It'll otherwise be huge for huge signatures. https://bugs.webkit.org/show_bug.cgi?id=165547
</span><span class="cx">     
</span><span class="lines">@@ -220,10 +253,9 @@
</span><span class="cx"> 
</span><span class="cx">     materializeImportJSCell(vm, jit, importIndex, importJSCellGPRReg);
</span><span class="cx"> 
</span><del>-    uint64_t thisArgument = ValueUndefined; // FIXME what does the WebAssembly spec say this should be? https://bugs.webkit.org/show_bug.cgi?id=165471
</del><span class="cx">     jit.store64(importJSCellGPRReg, calleeFrame.withOffset(CallFrameSlot::callee * static_cast&lt;int&gt;(sizeof(Register))));
</span><span class="cx">     jit.store32(JIT::TrustedImm32(numberOfParameters), calleeFrame.withOffset(CallFrameSlot::argumentCount * static_cast&lt;int&gt;(sizeof(Register)) + PayloadOffset));
</span><del>-    jit.store64(JIT::TrustedImm64(thisArgument), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast&lt;int&gt;(sizeof(Register))));
</del><ins>+    jit.store64(JIT::TrustedImm64(ValueUndefined), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast&lt;int&gt;(sizeof(Register))));
</ins><span class="cx"> 
</span><span class="cx">     // FIXME Tail call if the wasm return type is void and no registers were spilled. https://bugs.webkit.org/show_bug.cgi?id=165488
</span><span class="cx"> 
</span><span class="lines">@@ -240,6 +272,8 @@
</span><span class="cx">     JIT::Call slowCall = jit.nearCall();
</span><span class="cx">     done.link(&amp;jit);
</span><span class="cx"> 
</span><ins>+    CCallHelpers::JumpList exceptionChecks;
+
</ins><span class="cx">     switch (signature-&gt;returnType()) {
</span><span class="cx">     case Void:
</span><span class="cx">         // Discard.
</span><span class="lines">@@ -249,53 +283,98 @@
</span><span class="cx">         // For the JavaScript embedding, imports with these types in their signature return are a WebAssembly.Module validation error.
</span><span class="cx">         RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx">         break;
</span><ins>+    case I64: {
+        RELEASE_ASSERT_NOT_REACHED(); // Handled above.
+    }
</ins><span class="cx">     case I32: {
</span><del>-        jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
-        JIT::Jump checkJSInt32 = jit.branch64(JIT::AboveOrEqual, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
-        jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
-        jit.truncateDoubleToInt32(FPRInfo::returnValueFPR, GPRInfo::returnValueGPR);
-        JIT::Jump checkJSNumber = jit.branchTest64(JIT::NonZero, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
-        jit.abortWithReason(AHIsNotJSNumber); // FIXME Coerce when the values aren't what we expect, instead of aborting. https://bugs.webkit.org/show_bug.cgi?id=165480
-        checkJSInt32.link(&amp;jit);
</del><ins>+        CCallHelpers::JumpList done;
+        CCallHelpers::JumpList slowPath;
+
+        slowPath.append(jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters));
+        slowPath.append(jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters));
</ins><span class="cx">         jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
</span><del>-        checkJSNumber.link(&amp;jit);
</del><ins>+        done.append(jit.jump());
+
+        slowPath.link(&amp;jit);
+        jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
+        auto call = jit.call();
+        exceptionChecks.append(jit.emitJumpIfException());
+
+        int32_t (*convertToI32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -&gt; int32_t { 
+            VM* vm = &amp;exec-&gt;vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            return v.toInt32(exec);
+        };
+        jit.addLinkTask([=] (LinkBuffer&amp; linkBuffer) {
+            linkBuffer.link(call, convertToI32);
+        });
+
+        done.link(&amp;jit);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><del>-    case I64: {
-        jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
-        JIT::Jump checkJSInt32 = jit.branch64(JIT::AboveOrEqual, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
-        jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
-        jit.truncateDoubleToInt64(FPRInfo::returnValueFPR, GPRInfo::returnValueGPR);
-        JIT::Jump checkJSNumber = jit.branchTest64(JIT::NonZero, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
-        jit.abortWithReason(AHIsNotJSNumber); // FIXME Coerce when the values aren't what we expect, instead of aborting. https://bugs.webkit.org/show_bug.cgi?id=165480
-        checkJSInt32.link(&amp;jit);
-        jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
-        checkJSNumber.link(&amp;jit);
-        break;
-    }
</del><span class="cx">     case F32: {
</span><ins>+        CCallHelpers::JumpList done;
+        auto notANumber = jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters);
+        auto isDouble = jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters);
+        // We're an int32
+        jit.signExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
+        jit.convertInt64ToFloat(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
+        done.append(jit.jump());
+
+        isDouble.link(&amp;jit);
</ins><span class="cx">         jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
</span><ins>+        jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
</ins><span class="cx">         jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
</span><span class="cx">         jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
</span><del>-        JIT::Jump checkJSInt32 = jit.branch64(JIT::AboveOrEqual, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
-        JIT::Jump checkJSNumber = jit.branchTest64(JIT::NonZero, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
-        jit.abortWithReason(AHIsNotJSNumber); // FIXME Coerce when the values aren't what we expect, instead of aborting. https://bugs.webkit.org/show_bug.cgi?id=165480
-        checkJSInt32.link(&amp;jit);
-        jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
-        jit.convertInt64ToFloat(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
-        checkJSNumber.link(&amp;jit);
</del><ins>+        done.append(jit.jump());
+
+        notANumber.link(&amp;jit);
+        jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
+        auto call = jit.call();
+        exceptionChecks.append(jit.emitJumpIfException());
+
+        float (*convertToF32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -&gt; float { 
+            VM* vm = &amp;exec-&gt;vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            return static_cast&lt;float&gt;(v.toNumber(exec));
+        };
+        jit.addLinkTask([=] (LinkBuffer&amp; linkBuffer) {
+            linkBuffer.link(call, convertToF32);
+        });
+
+        done.link(&amp;jit);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     case F64: {
</span><ins>+        CCallHelpers::JumpList done;
+        auto notANumber = jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters);
+        auto isDouble = jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters);
+        // We're an int32
+        jit.signExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
+        jit.convertInt64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
+        done.append(jit.jump());
+
+        isDouble.link(&amp;jit);
</ins><span class="cx">         jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
</span><ins>+        jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
</ins><span class="cx">         jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
</span><del>-        JIT::Jump checkJSInt32 = jit.branch64(JIT::AboveOrEqual, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
-        JIT::Jump checkJSNumber = jit.branchTest64(JIT::NonZero, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
-        jit.abortWithReason(AHIsNotJSNumber); // FIXME Coerce when the values aren't what we expect, instead of aborting. https://bugs.webkit.org/show_bug.cgi?id=165480
-        checkJSInt32.link(&amp;jit);
-        jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
-        jit.convertInt64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
-        checkJSNumber.link(&amp;jit);
</del><ins>+        done.append(jit.jump());
+
+        notANumber.link(&amp;jit);
+        jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
+        auto call = jit.call();
+        exceptionChecks.append(jit.emitJumpIfException());
+
+        double (*convertToF64)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -&gt; double { 
+            VM* vm = &amp;exec-&gt;vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            return v.toNumber(exec);
+        };
+        jit.addLinkTask([=] (LinkBuffer&amp; linkBuffer) {
+            linkBuffer.link(call, convertToF64);
+        });
+
+        done.link(&amp;jit);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     }
</span><span class="lines">@@ -303,6 +382,25 @@
</span><span class="cx">     jit.emitFunctionEpilogue();
</span><span class="cx">     jit.ret();
</span><span class="cx"> 
</span><ins>+    if (!exceptionChecks.empty()) {
+        exceptionChecks.link(&amp;jit);
+        jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer();
+        jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+        auto call = jit.call();
+        jit.jumpToExceptionHandler();
+
+        void (*doUnwinding)(ExecState*) = [] (ExecState* exec) -&gt; void {
+            VM* vm = &amp;exec-&gt;vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            genericUnwind(vm, exec);
+            ASSERT(!!vm-&gt;callFrameForCatch);
+        };
+
+        jit.addLinkTask([=] (LinkBuffer&amp; linkBuffer) {
+            linkBuffer.link(call, doUnwinding);
+        });
+    }
+
</ins><span class="cx">     LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
</span><span class="cx">     patchBuffer.link(slowCall, FunctionPtr(vm-&gt;getCTIStub(linkCallThunkGenerator).code().executableAddress()));
</span><span class="cx">     CodeLocationLabel callReturnLocation(patchBuffer.locationOfNearCall(slowCall));
</span><span class="lines">@@ -309,7 +407,11 @@
</span><span class="cx">     CodeLocationLabel hotPathBegin(patchBuffer.locationOf(targetToCheck));
</span><span class="cx">     CodeLocationNearCall hotPathOther = patchBuffer.locationOfNearCall(fastCall);
</span><span class="cx">     callLinkInfo-&gt;setCallLocations(callReturnLocation, hotPathBegin, hotPathOther);
</span><ins>+#if !defined(NDEBUG)
</ins><span class="cx">     String signatureDescription = SignatureInformation::get(vm, signatureIndex)-&gt;toString();
</span><ins>+#else
+    String signatureDescription;
+#endif
</ins><span class="cx">     return FINALIZE_CODE(patchBuffer, (&quot;WebAssembly-&gt;JavaScript import[%i] %s&quot;, importIndex, signatureDescription.ascii().data()));
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>