Title: [211195] trunk
Revision
211195
Author
[email protected]
Date
2017-01-25 18:38:41 -0800 (Wed, 25 Jan 2017)

Log Message

WebAssembly JS API: coerce return values from imports
https://bugs.webkit.org/show_bug.cgi?id=165480
<rdar://problem/29760318>

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/_javascript_Core:

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):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (211194 => 211195)


--- trunk/JSTests/ChangeLog	2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/JSTests/ChangeLog	2017-01-26 02:38:41 UTC (rev 211195)
@@ -1,3 +1,19 @@
+2017-01-25  Saam Barati  <[email protected]>
+
+        WebAssembly JS API: coerce return values from imports
+        https://bugs.webkit.org/show_bug.cgi?id=165480
+        <rdar://problem/29760318>
+
+        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):
+
 2017-01-25  Filip Pizlo  <[email protected]>
 
         jsc.cpp should have the $.agent stuff for testing SAB

Added: trunk/JSTests/wasm/function-tests/function-import-return-value.js (0 => 211195)


--- 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)
@@ -0,0 +1,247 @@
+import Builder from '../Builder.js'
+import * as assert from '../assert.js'
+
+{
+    let called = false;
+
+    const tests = {
+        i32: [
+            [20, (x) => assert.eq(x, 20)],
+            [20.888, (x) => assert.eq(x, 20.888 | 0)],
+            [Math.PI, (x) => assert.eq(x, Math.PI | 0)],
+            [{valueOf() { assert.truthy(!called); called = true; return 25; } }, (x) => { assert.truthy(called); assert.eq(x, 25); called = false; }],
+            [NaN, (x) => assert.eq(x, NaN | 0)],
+            [-0.0, (x) => assert.eq(1/x, Infinity)],
+            [undefined, (x) => assert.eq(x, undefined | 0)],
+            [null, (x) => assert.eq(x, null | 0)],
+            [Number.MAX_SAFE_INTEGER, (x) => assert.eq(x, Number.MAX_SAFE_INTEGER | 0)],
+            [2**32 - 1, (x) => assert.eq(x, (2**32 - 1) | 0)],
+            [2**32 - 1000, (x) => assert.eq(x, (2**32 - 1000) | 0)],
+            [-1000, (x) => assert.eq(x, -1000)],
+        ],
+        f32: [
+            [20, (x) => assert.eq(x, 20)],
+            [20.888, (x) => assert.eq(x, Math.fround(20.888))],
+            [Math.PI, (x) => assert.eq(x, Math.fround(Math.PI))],
+            [{valueOf() { assert.truthy(!called); called = true; return 25.82; } }, (x) => { assert.truthy(called); assert.eq(x, Math.fround(25.82)); called = false; }],
+            [NaN, (x) => assert.truthy(isNaN(x))],
+            [-0.0, (x) => assert.eq(1/x, -Infinity)],
+            [undefined, (x) => assert.truthy(isNaN(x))],
+            [null, (x) => assert.eq(x, 0)],
+            [Number.MAX_SAFE_INTEGER, (x) => assert.eq(x, Math.fround(Number.MAX_SAFE_INTEGER))],
+            [-1000, (x) => assert.eq(x, -1000)],
+        ],
+        f64: [
+            [20, (x) => assert.eq(x, 20)],
+            [2**24, (x) => assert.eq(x, 2**24)],
+            [2**52, (x) => assert.eq(x, 2**52)],
+            [20.8888888, (x) => assert.eq(x, 20.8888888)],
+            [Math.PI, (x) => assert.eq(x, Math.PI)],
+            [{valueOf() { assert.truthy(!called); called = true; return 25.82; } }, (x) => { assert.truthy(called); assert.eq(x, 25.82); called = false; }],
+            [NaN, (x) => assert.truthy(isNaN(x))],
+            [-0.0, (x) => assert.eq(1/x, -Infinity)],
+            [undefined, (x) => assert.truthy(isNaN(x))],
+            [null, (x) => assert.eq(x, 0)],
+            [Number.MAX_SAFE_INTEGER, (x) => assert.eq(x, Number.MAX_SAFE_INTEGER)],
+            [-1000, (x) => assert.eq(x, -1000)],
+        ]
+    };
+
+    for (let type of Reflect.ownKeys(tests)) {
+        const builder = new Builder()
+            .Type().End()
+            .Import()
+                .Function("imp", "func", { params: [], ret: type})
+            .End()
+            .Function().End()
+            .Export()
+                .Function("foo")
+            .End()
+            .Code()
+                .Function("foo", {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 = () => {
+                return test[0];
+            };
+
+            const instance = new WebAssembly.Instance(module, {imp: {func}});
+            const ret = instance.exports.foo();
+            test[1](ret);
+        }
+    }
+}
+
+{
+    let types = ["i32", "f32", "f64"];
+    for (let type of types) {
+        const builder = new Builder()
+            .Type().End()
+            .Import()
+                .Function("imp", "func", { params: [], ret: type})
+            .End()
+            .Function().End()
+            .Export()
+                .Function("foo")
+            .End()
+            .Code()
+                .Function("foo", {params: [], ret: type})
+                    .Call(0)
+                    .Unreachable()
+                .End()
+            .End();
+
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+        let error = null;
+        const func = () => {
+            return {
+                valueOf() {
+                    error = new Error;
+                    throw error;
+                }
+            };
+        };
+
+        const instance = new WebAssembly.Instance(module, {imp: {func}});
+        for (let i = 0; i < 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("imp", "func", { params: [], ret: "i64"})
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: [], ret: "void"})
+                .Call(0)
+                .Drop()
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const func = () => 20;
+    const instance = new WebAssembly.Instance(module, {imp: {func}});
+    for (let i = 0; i < 100; i++) {
+        assert.throws(() => instance.exports.foo(), TypeError, "i64 not allowed as return type or argument to an imported function");
+    }
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Import()
+            .Function("imp", "func", { params: ["i64"], ret: "void"})
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: [], ret: "void"})
+                 .I64Const(20)
+                .Call(0)
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    const func = () => 20;
+    const instance = new WebAssembly.Instance(module, {imp: {func}});
+    for (let i = 0; i < 100; i++) {
+        assert.throws(() => instance.exports.foo(), TypeError, "i64 not allowed as return type or argument to an imported function");
+    }
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Import()
+            .Function("imp", "func", { params: ["i64"], ret: "void"})
+        .End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: [], ret: "void"})
+                 .I64Const(20)
+                .Call(0)
+                .Return()
+            .End()
+        .End();
+
+
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    let called = false;
+    const func = () => {
+        called = true;
+    }
+    const instance = new WebAssembly.Instance(module, {imp: {func}});
+    for (let i = 0; i < 100; i++) {
+        assert.throws(() => instance.exports.foo(), TypeError, "i64 not allowed as return type or argument to an imported function");
+        assert.eq(called, false);
+    }
+}
+
+{
+    const builder = new Builder()
+        .Type().End()
+        .Function().End()
+        .Export()
+            .Function("foo")
+        .End()
+        .Code()
+            .Function("foo", {params: ["i64"], ret: "void"})
+                 .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 < 100; i++) {
+        assert.throws(() => instance.exports.foo(value), Error, "WebAssembly function with an i64 argument can't be called from _javascript_");
+        assert.eq(called, false);
+    }
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (211194 => 211195)


--- trunk/Source/_javascript_Core/ChangeLog	2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-01-26 02:38:41 UTC (rev 211195)
@@ -1,3 +1,25 @@
+2017-01-25  Saam Barati  <[email protected]>
+
+        WebAssembly JS API: coerce return values from imports
+        https://bugs.webkit.org/show_bug.cgi?id=165480
+        <rdar://problem/29760318>
+
+        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):
+
 2017-01-25  Filip Pizlo  <[email protected]>
 
         jsc.cpp should have the $.agent stuff for testing SAB

Modified: trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp (211194 => 211195)


--- trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp	2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/_javascript_Core/jit/AssemblyHelpers.cpp	2017-01-26 02:38:41 UTC (rev 211195)
@@ -354,6 +354,11 @@
     }
 }
 
+AssemblyHelpers::Jump AssemblyHelpers::emitJumpIfException()
+{
+    return emitExceptionCheck(NormalExceptionCheck);
+}
+
 AssemblyHelpers::Jump AssemblyHelpers::emitExceptionCheck(ExceptionCheckKind kind, ExceptionJumpWidth width)
 {
     callExceptionFuzz();

Modified: trunk/Source/_javascript_Core/jit/AssemblyHelpers.h (211194 => 211195)


--- trunk/Source/_javascript_Core/jit/AssemblyHelpers.h	2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/_javascript_Core/jit/AssemblyHelpers.h	2017-01-26 02:38:41 UTC (rev 211195)
@@ -1185,6 +1185,7 @@
     JS_EXPORT_PRIVATE Jump emitExceptionCheck(
         ExceptionCheckKind = NormalExceptionCheck, ExceptionJumpWidth = NormalJumpWidth);
     JS_EXPORT_PRIVATE Jump emitNonPatchableExceptionCheck();
+    Jump emitJumpIfException();
 
 #if ENABLE(SAMPLING_COUNTERS)
     static void emitCount(MacroAssembler& jit, AbstractSamplingCounter& counter, int32_t increment = 1)

Modified: trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp (211194 => 211195)


--- trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp	2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp	2017-01-26 02:38:41 UTC (rev 211195)
@@ -976,6 +976,7 @@
             patchpoint->append(calleeCode, ValueRep::SomeRegister);
 
             patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
+                AllowMacroScratchRegisterUsage allowScratch(jit);
                 jit.call(params[returnType == Void ? 0 : 1].gpr());
             });
         });

Modified: trunk/Source/_javascript_Core/wasm/WasmBinding.cpp (211194 => 211195)


--- trunk/Source/_javascript_Core/wasm/WasmBinding.cpp	2017-01-26 02:34:30 UTC (rev 211194)
+++ trunk/Source/_javascript_Core/wasm/WasmBinding.cpp	2017-01-26 02:38:41 UTC (rev 211195)
@@ -28,15 +28,19 @@
 
 #if ENABLE(WEBASSEMBLY)
 
-#include "AssemblyHelpers.h"
-#include "JSCJSValueInlines.h"
+#include "CCallHelpers.h"
+#include "FrameTracers.h"
+#include "JITExceptions.h"
+#include "JSCInlines.h"
 #include "JSWebAssemblyInstance.h"
 #include "LinkBuffer.h"
+#include "NativeErrorConstructor.h"
 #include "WasmCallingConvention.h"
+#include "WasmExceptionType.h"
 
 namespace JSC { namespace Wasm {
 
-typedef AssemblyHelpers JIT;
+typedef CCallHelpers JIT;
 
 static void materializeImportJSCell(VM* vm, JIT& jit, unsigned importIndex, GPRReg result)
 {
@@ -61,6 +65,55 @@
     jit.store64(JIT::TrustedImm32(0), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::codeBlock * static_cast<int>(sizeof(Register)))); // FIXME Stop using 0 as codeBlocks. https://bugs.webkit.org/show_bug.cgi?id=165321
     jit.storePtr(JIT::TrustedImmPtr(vm->webAssemblyToJSCallee.get()), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
 
+
+    {
+        bool hasBadI64Use = false;
+        hasBadI64Use |= signature->returnType() == I64;
+        for (unsigned argNum = 0; argNum < argCount && !hasBadI64Use; ++argNum) {
+            Type argType = signature->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) -> void {
+                VM* vm = &exec->vm();
+                NativeCallFrameTracer tracer(vm, exec);
+
+                {
+                    auto throwScope = DECLARE_THROW_SCOPE(*vm);
+                    JSGlobalObject* globalObject = vm->topJSWebAssemblyInstance->globalObject();
+                    auto* error = ErrorInstance::create(exec, *vm, globalObject->typeErrorConstructor()->errorStructure(), ASCIILiteral("i64 not allowed as return type or argument to an imported function"));
+                    throwException(exec, throwScope, error);
+                }
+
+                genericUnwind(vm, exec);
+                ASSERT(!!vm->callFrameForCatch);
+            };
+
+            LinkBuffer linkBuffer(*vm, jit, GLOBAL_THUNK_ID);
+            linkBuffer.link(call, throwBadI64);
+            return FINALIZE_CODE(linkBuffer, ("WebAssembly->_javascript_ invalid i64 use in import[%i]", importIndex));
+        }
+    }
+
     // 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.
     RegisterSet missingCalleeSaves = wasmCC.m_calleeSaveRegisters;
     missingCalleeSaves.exclude(jsCC.m_calleeSaveRegisters);
@@ -74,26 +127,6 @@
     const unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
     jit.subPtr(MacroAssembler::TrustedImm32(stackOffset), MacroAssembler::stackPointerRegister);
     JIT::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
-    
-    for (unsigned argNum = 0; argNum < argCount; ++argNum) {
-        Type argType = signature->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, ("WebAssembly import[%i] stub for signature %i", importIndex, signatureIndex));
-        }
-        case I32:
-        case F32:
-        case F64:
-            continue;
-        }
-    }
 
     // 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
     
@@ -220,10 +253,9 @@
 
     materializeImportJSCell(vm, jit, importIndex, importJSCellGPRReg);
 
-    uint64_t thisArgument = ValueUndefined; // FIXME what does the WebAssembly spec say this should be? https://bugs.webkit.org/show_bug.cgi?id=165471
     jit.store64(importJSCellGPRReg, calleeFrame.withOffset(CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
     jit.store32(JIT::TrustedImm32(numberOfParameters), calleeFrame.withOffset(CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset));
-    jit.store64(JIT::TrustedImm64(thisArgument), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast<int>(sizeof(Register))));
+    jit.store64(JIT::TrustedImm64(ValueUndefined), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast<int>(sizeof(Register))));
 
     // FIXME Tail call if the wasm return type is void and no registers were spilled. https://bugs.webkit.org/show_bug.cgi?id=165488
 
@@ -240,6 +272,8 @@
     JIT::Call slowCall = jit.nearCall();
     done.link(&jit);
 
+    CCallHelpers::JumpList exceptionChecks;
+
     switch (signature->returnType()) {
     case Void:
         // Discard.
@@ -249,53 +283,98 @@
         // For the _javascript_ embedding, imports with these types in their signature return are a WebAssembly.Module validation error.
         RELEASE_ASSERT_NOT_REACHED();
         break;
+    case I64: {
+        RELEASE_ASSERT_NOT_REACHED(); // Handled above.
+    }
     case I32: {
-        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(&jit);
+        CCallHelpers::JumpList done;
+        CCallHelpers::JumpList slowPath;
+
+        slowPath.append(jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters));
+        slowPath.append(jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters));
         jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
-        checkJSNumber.link(&jit);
+        done.append(jit.jump());
+
+        slowPath.link(&jit);
+        jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
+        auto call = jit.call();
+        exceptionChecks.append(jit.emitJumpIfException());
+
+        int32_t (*convertToI32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> int32_t { 
+            VM* vm = &exec->vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            return v.toInt32(exec);
+        };
+        jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+            linkBuffer.link(call, convertToI32);
+        });
+
+        done.link(&jit);
         break;
     }
-    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(&jit);
-        jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
-        checkJSNumber.link(&jit);
-        break;
-    }
     case F32: {
+        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(&jit);
         jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
+        jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
         jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
         jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
-        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(&jit);
-        jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
-        jit.convertInt64ToFloat(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
-        checkJSNumber.link(&jit);
+        done.append(jit.jump());
+
+        notANumber.link(&jit);
+        jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
+        auto call = jit.call();
+        exceptionChecks.append(jit.emitJumpIfException());
+
+        float (*convertToF32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> float { 
+            VM* vm = &exec->vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            return static_cast<float>(v.toNumber(exec));
+        };
+        jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+            linkBuffer.link(call, convertToF32);
+        });
+
+        done.link(&jit);
         break;
     }
     case F64: {
+        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(&jit);
         jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
+        jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
         jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
-        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(&jit);
-        jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
-        jit.convertInt64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
-        checkJSNumber.link(&jit);
+        done.append(jit.jump());
+
+        notANumber.link(&jit);
+        jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
+        auto call = jit.call();
+        exceptionChecks.append(jit.emitJumpIfException());
+
+        double (*convertToF64)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> double { 
+            VM* vm = &exec->vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            return v.toNumber(exec);
+        };
+        jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+            linkBuffer.link(call, convertToF64);
+        });
+
+        done.link(&jit);
         break;
     }
     }
@@ -303,6 +382,25 @@
     jit.emitFunctionEpilogue();
     jit.ret();
 
+    if (!exceptionChecks.empty()) {
+        exceptionChecks.link(&jit);
+        jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer();
+        jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+        auto call = jit.call();
+        jit.jumpToExceptionHandler();
+
+        void (*doUnwinding)(ExecState*) = [] (ExecState* exec) -> void {
+            VM* vm = &exec->vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            genericUnwind(vm, exec);
+            ASSERT(!!vm->callFrameForCatch);
+        };
+
+        jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+            linkBuffer.link(call, doUnwinding);
+        });
+    }
+
     LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
     patchBuffer.link(slowCall, FunctionPtr(vm->getCTIStub(linkCallThunkGenerator).code().executableAddress()));
     CodeLocationLabel callReturnLocation(patchBuffer.locationOfNearCall(slowCall));
@@ -309,7 +407,11 @@
     CodeLocationLabel hotPathBegin(patchBuffer.locationOf(targetToCheck));
     CodeLocationNearCall hotPathOther = patchBuffer.locationOfNearCall(fastCall);
     callLinkInfo->setCallLocations(callReturnLocation, hotPathBegin, hotPathOther);
+#if !defined(NDEBUG)
     String signatureDescription = SignatureInformation::get(vm, signatureIndex)->toString();
+#else
+    String signatureDescription;
+#endif
     return FINALIZE_CODE(patchBuffer, ("WebAssembly->_javascript_ import[%i] %s", importIndex, signatureDescription.ascii().data()));
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to