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()));
}