mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
[bun:ffi] Support double and float
This commit is contained in:
@@ -100,6 +100,7 @@ typedef union EncodedJSValue {
|
||||
#endif
|
||||
|
||||
void* asPtr;
|
||||
double asDouble;
|
||||
} EncodedJSValue;
|
||||
|
||||
EncodedJSValue ValueUndefined = { TagValueUndefined };
|
||||
@@ -116,6 +117,7 @@ JSContext cachedJSContext;
|
||||
void* cachedCallbackFunction;
|
||||
#endif
|
||||
|
||||
|
||||
static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inline__));
|
||||
static EncodedJSValue DOUBLE_TO_JSVALUE(double val) __attribute__((__always_inline__));
|
||||
static EncodedJSValue FLOAT_TO_JSVALUE(float val) __attribute__((__always_inline__));
|
||||
@@ -149,19 +151,16 @@ static EncodedJSValue INT32_TO_JSVALUE(int32_t val) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static EncodedJSValue DOUBLE_TO_JSVALUE(double val) {
|
||||
EncodedJSValue res;
|
||||
#ifdef USES_FLOAT
|
||||
res.asInt64 = trunc(val) == val ? val : val - DoubleEncodeOffset;
|
||||
#else
|
||||
// should never get here
|
||||
res.asInt64 = 0xa;
|
||||
#endif
|
||||
EncodedJSValue res;
|
||||
res.asDouble = val;
|
||||
res.asInt64 += DoubleEncodeOffset;
|
||||
return res;
|
||||
}
|
||||
|
||||
static EncodedJSValue FLOAT_TO_JSVALUE(float val) {
|
||||
return DOUBLE_TO_JSVALUE(val);
|
||||
return DOUBLE_TO_JSVALUE((double)val);
|
||||
}
|
||||
|
||||
static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) {
|
||||
@@ -172,7 +171,8 @@ static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) {
|
||||
|
||||
|
||||
static double JSVALUE_TO_DOUBLE(EncodedJSValue val) {
|
||||
return val.asInt64 + DoubleEncodeOffset;
|
||||
val.asInt64 -= DoubleEncodeOffset;
|
||||
return val.asDouble;
|
||||
}
|
||||
|
||||
static float JSVALUE_TO_FLOAT(EncodedJSValue val) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#define HAS_ARGUMENTS
|
||||
#define USES_FLOAT 1
|
||||
// This file is part of Bun!
|
||||
// You can find the original source:
|
||||
// https://github.com/Jarred-Sumner/bun/blob/main/src/javascript/jsc/api/FFI.h#L2
|
||||
@@ -100,6 +100,7 @@ typedef union EncodedJSValue {
|
||||
#endif
|
||||
|
||||
void* asPtr;
|
||||
double asDouble;
|
||||
} EncodedJSValue;
|
||||
|
||||
EncodedJSValue ValueUndefined = { TagValueUndefined };
|
||||
@@ -116,6 +117,7 @@ JSContext cachedJSContext;
|
||||
void* cachedCallbackFunction;
|
||||
#endif
|
||||
|
||||
|
||||
static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inline__));
|
||||
static EncodedJSValue DOUBLE_TO_JSVALUE(double val) __attribute__((__always_inline__));
|
||||
static EncodedJSValue FLOAT_TO_JSVALUE(float val) __attribute__((__always_inline__));
|
||||
@@ -149,19 +151,16 @@ static EncodedJSValue INT32_TO_JSVALUE(int32_t val) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static EncodedJSValue DOUBLE_TO_JSVALUE(double val) {
|
||||
EncodedJSValue res;
|
||||
#ifdef USES_FLOAT
|
||||
res.asInt64 = trunc(val) == val ? val : val - DoubleEncodeOffset;
|
||||
#else
|
||||
// should never get here
|
||||
res.asInt64 = 0xa;
|
||||
#endif
|
||||
EncodedJSValue res;
|
||||
res.asDouble = val;
|
||||
res.asInt64 += DoubleEncodeOffset;
|
||||
return res;
|
||||
}
|
||||
|
||||
static EncodedJSValue FLOAT_TO_JSVALUE(float val) {
|
||||
return DOUBLE_TO_JSVALUE(val);
|
||||
return DOUBLE_TO_JSVALUE((double)val);
|
||||
}
|
||||
|
||||
static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) {
|
||||
@@ -172,7 +171,8 @@ static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) {
|
||||
|
||||
|
||||
static double JSVALUE_TO_DOUBLE(EncodedJSValue val) {
|
||||
return val.asInt64 + DoubleEncodeOffset;
|
||||
val.asInt64 -= DoubleEncodeOffset;
|
||||
return val.asDouble;
|
||||
}
|
||||
|
||||
static float JSVALUE_TO_FLOAT(EncodedJSValue val) {
|
||||
@@ -193,19 +193,14 @@ void* JSFunctionCall(void* globalObject, void* callFrame);
|
||||
|
||||
// --- Generated Code ---
|
||||
/* --- The Function To Call */
|
||||
void callback(void* arg0);
|
||||
float not_a_callback();
|
||||
|
||||
/* ---- Your Wrapper Function ---- */
|
||||
void* JSFunctionCall(void* globalObject, void* callFrame) {
|
||||
#ifdef HAS_ARGUMENTS
|
||||
LOAD_ARGUMENTS_FROM_CALL_FRAME;
|
||||
#endif
|
||||
#ifdef INJECT_BEFORE
|
||||
//Bun_FFI_PointerOffsetToArgumentsList: 6
|
||||
//Bun_FFI_PointerOffsetToArgumentsCount: 0
|
||||
#endif
|
||||
callback( JSVALUE_TO_PTR(arg(0)));
|
||||
|
||||
return ValueUndefined.asPtr;
|
||||
float return_value = not_a_callback();
|
||||
return FLOAT_TO_JSVALUE(return_value).asPtr;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ it("ffi print", async () => {
|
||||
import.meta.dir + "/ffi.test.fixture.receiver.c",
|
||||
viewSource(
|
||||
{
|
||||
callback: {
|
||||
return_type: 13,
|
||||
args: ["ptr"],
|
||||
not_a_callback: {
|
||||
return_type: "float",
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
false
|
||||
@@ -71,14 +71,14 @@ it("ffi run", () => {
|
||||
return_type: "char",
|
||||
args: [],
|
||||
},
|
||||
// returns_42_float: {
|
||||
// return_type: "float",
|
||||
// args: [],
|
||||
// },
|
||||
// returns_42_double: {
|
||||
// return_type: "double",
|
||||
// args: [],
|
||||
// },
|
||||
returns_42_float: {
|
||||
return_type: "float",
|
||||
args: [],
|
||||
},
|
||||
returns_42_double: {
|
||||
return_type: "double",
|
||||
args: [],
|
||||
},
|
||||
returns_42_uint8_t: {
|
||||
return_type: "uint8_t",
|
||||
args: [],
|
||||
@@ -116,18 +116,18 @@ it("ffi run", () => {
|
||||
return_type: "char",
|
||||
args: ["char"],
|
||||
},
|
||||
// identity_float: {
|
||||
// return_type: "float",
|
||||
// args: ["float"],
|
||||
// },
|
||||
identity_float: {
|
||||
return_type: "float",
|
||||
args: ["float"],
|
||||
},
|
||||
identity_bool: {
|
||||
return_type: "bool",
|
||||
args: ["bool"],
|
||||
},
|
||||
// identity_double: {
|
||||
// return_type: "double",
|
||||
// args: ["double"],
|
||||
// },
|
||||
identity_double: {
|
||||
return_type: "double",
|
||||
args: ["double"],
|
||||
},
|
||||
identity_int8_t: {
|
||||
return_type: "int8_t",
|
||||
args: ["int8_t"],
|
||||
@@ -340,8 +340,10 @@ it("ffi run", () => {
|
||||
expect(returns_true()).toBe(true);
|
||||
expect(returns_false()).toBe(false);
|
||||
expect(returns_42_char()).toBe(42);
|
||||
// expect(returns_42_float()).toBe(42);
|
||||
// expect(returns_42_double()).toBe(42);
|
||||
|
||||
expect(Math.fround(returns_42_float())).toBe(Math.fround(42.41999804973602));
|
||||
|
||||
expect(returns_42_double()).toBe(42.42);
|
||||
expect(returns_42_uint8_t()).toBe(42);
|
||||
expect(returns_neg_42_int8_t()).toBe(-42);
|
||||
expect(returns_42_uint16_t()).toBe(42);
|
||||
@@ -352,20 +354,20 @@ it("ffi run", () => {
|
||||
expect(identity_int32_t(10)).toBe(10);
|
||||
// expect(returns_neg_42_int64_t()).toBe(-42);
|
||||
expect(identity_char(10)).toBe(10);
|
||||
// expect(identity_float(10.1)).toBe(10.1);
|
||||
expect(identity_float(10.199999809265137)).toBe(10.199999809265137);
|
||||
expect(identity_bool(true)).toBe(true);
|
||||
expect(identity_bool(false)).toBe(false);
|
||||
// expect(identity_double(10.1)).toBe(10.1);
|
||||
expect(identity_double(10.100000000000364)).toBe(10.100000000000364);
|
||||
expect(identity_int8_t(10)).toBe(10);
|
||||
expect(identity_int16_t(10)).toBe(10);
|
||||
|
||||
console.log("here");
|
||||
// expect(identity_int64_t(10)).toBe(10);
|
||||
expect(identity_uint8_t(10)).toBe(10);
|
||||
expect(identity_uint16_t(10)).toBe(10);
|
||||
expect(identity_uint32_t(10)).toBe(10);
|
||||
expect(add_char(1, 1)).toBe(2);
|
||||
// expect(add_float(1.1, 1.1)).toBe(2.2);
|
||||
// expect(add_double(1.1, 1.1)).toBe(2.2);
|
||||
expect(add_float(2.4, 2.8)).toBe(Math.fround(5.2));
|
||||
expect(add_double(4.2, 0.1)).toBe(4.3);
|
||||
expect(add_int8_t(1, 1)).toBe(2);
|
||||
expect(add_int16_t(1, 1)).toBe(2);
|
||||
expect(add_int32_t(1, 1)).toBe(2);
|
||||
|
||||
@@ -99,6 +99,7 @@ typedef union EncodedJSValue {
|
||||
#endif
|
||||
|
||||
void* asPtr;
|
||||
double asDouble;
|
||||
} EncodedJSValue;
|
||||
|
||||
EncodedJSValue ValueUndefined = { TagValueUndefined };
|
||||
@@ -115,6 +116,7 @@ JSContext cachedJSContext;
|
||||
void* cachedCallbackFunction;
|
||||
#endif
|
||||
|
||||
|
||||
static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inline__));
|
||||
static EncodedJSValue DOUBLE_TO_JSVALUE(double val) __attribute__((__always_inline__));
|
||||
static EncodedJSValue FLOAT_TO_JSVALUE(float val) __attribute__((__always_inline__));
|
||||
@@ -148,19 +150,16 @@ static EncodedJSValue INT32_TO_JSVALUE(int32_t val) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static EncodedJSValue DOUBLE_TO_JSVALUE(double val) {
|
||||
EncodedJSValue res;
|
||||
#ifdef USES_FLOAT
|
||||
res.asInt64 = trunc(val) == val ? val : val - DoubleEncodeOffset;
|
||||
#else
|
||||
// should never get here
|
||||
res.asInt64 = 0xa;
|
||||
#endif
|
||||
EncodedJSValue res;
|
||||
res.asDouble = val;
|
||||
res.asInt64 += DoubleEncodeOffset;
|
||||
return res;
|
||||
}
|
||||
|
||||
static EncodedJSValue FLOAT_TO_JSVALUE(float val) {
|
||||
return DOUBLE_TO_JSVALUE(val);
|
||||
return DOUBLE_TO_JSVALUE((double)val);
|
||||
}
|
||||
|
||||
static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) {
|
||||
@@ -171,7 +170,8 @@ static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) {
|
||||
|
||||
|
||||
static double JSVALUE_TO_DOUBLE(EncodedJSValue val) {
|
||||
return val.asInt64 + DoubleEncodeOffset;
|
||||
val.asInt64 -= DoubleEncodeOffset;
|
||||
return val.asDouble;
|
||||
}
|
||||
|
||||
static float JSVALUE_TO_FLOAT(EncodedJSValue val) {
|
||||
|
||||
@@ -1477,7 +1477,7 @@ pub fn nanoseconds(
|
||||
_: JSC.C.ExceptionRef,
|
||||
) JSC.C.JSValueRef {
|
||||
const ns = JSC.VirtualMachine.vm.origin_timer.read();
|
||||
JSC.JSValue.jsNumberFromUint64(ns).asObjectRef();
|
||||
return JSC.JSValue.jsNumberFromUint64(ns).asObjectRef();
|
||||
}
|
||||
|
||||
pub fn serve(
|
||||
|
||||
@@ -740,7 +740,6 @@ pub const FFI = struct {
|
||||
}
|
||||
|
||||
_ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY);
|
||||
CompilerRT.inject(state);
|
||||
|
||||
const compilation_result = TCC.tcc_compile_string(
|
||||
state,
|
||||
@@ -763,6 +762,7 @@ pub const FFI = struct {
|
||||
_ = TCC.tcc_add_symbol(state, "bun_call", JSC.C.JSObjectCallAsFunction);
|
||||
_ = TCC.tcc_add_symbol(state, "cachedJSContext", js_context);
|
||||
_ = TCC.tcc_add_symbol(state, "cachedCallbackFunction", js_function);
|
||||
CompilerRT.inject(state);
|
||||
|
||||
var relocation_size = TCC.tcc_relocate(state, null);
|
||||
if (relocation_size == 0) return;
|
||||
|
||||
@@ -34,7 +34,10 @@ using FFIFunction = JSC::EncodedJSValue (*)(JSC::JSGlobalObject* globalObject, J
|
||||
*
|
||||
* It was about 20% faster than using the JavaScriptCore C API for functions with 1 argument
|
||||
*
|
||||
* Note: there is no wrapper function here
|
||||
* There is no wrapper function. It does zero bounds checking on the arguments.
|
||||
* It does not check for exceptions. It does not check for return value.
|
||||
* It is the caller's responsibility to not buffer overflow the arguments
|
||||
* For all those reasons, this shouldn't be used directly.
|
||||
*/
|
||||
class JSFFIFunction final : public JSC::JSFunction {
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user