1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 08:33:43 -05:00

feat(ext/ffi): support passing and returning bigints (#14523)

This commit is contained in:
Elias Sjögreen 2022-06-08 13:13:10 +02:00 committed by GitHub
parent 2769d60250
commit 8113fac939
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 120 additions and 54 deletions

View file

@ -372,17 +372,25 @@ declare namespace Deno {
}
/** All possible number types interfacing with foreign functions */
type StaticNativeNumberType = Exclude<NativeType, "void" | "pointer">;
type StaticNativeNumberType = Exclude<
NativeType,
"void" | "pointer" | StaticNativeBigIntType
>;
/** All possible bigint types interfacing with foreign functions */
type StaticNativeBigIntType = "u64" | "i64" | "usize" | "isize";
/** Infers a foreign function return type */
type StaticForeignFunctionResult<T extends NativeType> = T extends "void"
? void
: T extends StaticNativeBigIntType ? bigint
: T extends StaticNativeNumberType ? number
: T extends "pointer" ? UnsafePointer
: never;
type StaticForeignFunctionParameter<T> = T extends "void" ? void
: T extends StaticNativeNumberType ? number
: T extends StaticNativeNumberType | StaticNativeBigIntType
? number | bigint
: T extends "pointer" ? Deno.UnsafePointer | Deno.TypedArray | null
: unknown;

View file

@ -14,12 +14,12 @@
TypeError,
} = window.__bootstrap.primordials;
function unpackU64([hi, lo]) {
return BigInt(hi) << 32n | BigInt(lo);
function pack64(value) {
return [Number(value >> 32n) >>> 0, Number(value & 0xFFFFFFFFn)];
}
function packU64(value) {
return [Number(value >> 32n), Number(value & 0xFFFFFFFFn)];
function unpackU64([hi, lo]) {
return BigInt(hi) << 32n | BigInt(lo);
}
function unpackI64([hi, lo]) {
@ -37,77 +37,77 @@
getUint8(offset = 0) {
return core.opSync(
"op_ffi_read_u8",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
getInt8(offset = 0) {
return core.opSync(
"op_ffi_read_i8",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
getUint16(offset = 0) {
return core.opSync(
"op_ffi_read_u16",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
getInt16(offset = 0) {
return core.opSync(
"op_ffi_read_i16",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
getUint32(offset = 0) {
return core.opSync(
"op_ffi_read_u32",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
getInt32(offset = 0) {
return core.opSync(
"op_ffi_read_i32",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
getBigUint64(offset = 0) {
return unpackU64(core.opSync(
"op_ffi_read_u64",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
));
}
getBigInt64(offset = 0) {
return unpackI64(core.opSync(
"op_ffi_read_u64",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
));
}
getFloat32(offset = 0) {
return core.opSync(
"op_ffi_read_f32",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
getFloat64(offset = 0) {
return core.opSync(
"op_ffi_read_f64",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
getCString(offset = 0) {
return core.opSync(
"op_ffi_cstr_read",
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
);
}
@ -119,7 +119,7 @@
copyInto(destination, offset = 0) {
core.opSync("op_ffi_buf_copy_into", [
packU64(this.pointer.value + BigInt(offset)),
pack64(this.pointer.value + BigInt(offset)),
destination,
destination.byteLength,
]);
@ -161,7 +161,7 @@
parameters.push(buffers.length);
buffers.push(arg);
} else if (ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg)) {
parameters.push(packU64(arg.value));
parameters.push(pack64(arg.value));
buffers.push(undefined);
} else if (arg === null) {
parameters.push(null);
@ -171,6 +171,14 @@
"Invalid ffi arg value, expected TypedArray, UnsafePointer or null",
);
}
} else if (typeof arg === "bigint") {
if (arg > 0xffffffffffffffffn) {
throw new TypeError(
"Invalid ffi arg value, it needs to be less than 0xffffffffffffffff",
);
}
parameters.push(pack64(arg));
} else {
parameters.push(arg);
}
@ -179,6 +187,23 @@
return { parameters, buffers };
}
function unpackResult(type, result) {
switch (type) {
case "pointer":
return new UnsafePointer(unpackU64(result));
case "u64":
return unpackU64(result);
case "i64":
return unpackI64(result);
case "usize":
return unpackU64(result);
case "isize":
return unpackI64(result);
default:
return result;
}
}
class UnsafeFnPointer {
pointer;
definition;
@ -195,7 +220,7 @@
);
if (this.definition.nonblocking) {
const promise = core.opAsync("op_ffi_call_ptr_nonblocking", {
pointer: packU64(this.pointer.value),
pointer: pack64(this.pointer.value),
def: this.definition,
parameters,
buffers,
@ -208,7 +233,7 @@
return promise;
} else {
const result = core.opSync("op_ffi_call_ptr", {
pointer: packU64(this.pointer.value),
pointer: pack64(this.pointer.value),
def: this.definition,
parameters,
buffers,
@ -268,8 +293,10 @@
);
continue;
}
const isNonBlocking = symbols[symbol].nonblocking;
const types = symbols[symbol].parameters;
const resultType = symbols[symbol].result;
const fn = (...args) => {
const { parameters, buffers } = prepareArgs(types, args);
@ -282,10 +309,8 @@
buffers,
});
if (symbols[symbol].result === "pointer") {
return promise.then((value) =>
new UnsafePointer(unpackU64(value))
);
if (resultType === "pointer") {
return promise.then((result) => unpackResult(resultType, result));
}
return promise;
@ -297,11 +322,7 @@
buffers,
});
if (symbols[symbol].result === "pointer") {
return new UnsafePointer(unpackU64(result));
}
return result;
return unpackResult(resultType, result);
}
};

View file

@ -311,6 +311,11 @@ impl NativeValue {
}
fn value_as_uint<T: TryFrom<u64>>(value: Value) -> Result<T, AnyError> {
if value.is_array() {
let value = U32x2::try_from(value)?;
return T::try_from(u64::from(value)).map_err(|_| type_error(format!("Found U32x2 FFI argument but it could not be converted to an unsigned integer, got {:?}", value)));
}
match value.as_u64().and_then(|v| T::try_from(v).ok()) {
Some(value) => Ok(value),
None => Err(type_error(format!(
@ -321,6 +326,11 @@ fn value_as_uint<T: TryFrom<u64>>(value: Value) -> Result<T, AnyError> {
}
fn value_as_int<T: TryFrom<i64>>(value: Value) -> Result<T, AnyError> {
if value.is_array() {
let value = U32x2::try_from(value)?;
return T::try_from(u64::from(value) as i64).map_err(|_| type_error(format!("Found U32x2 FFI argument but it could not be converted to a signed integer, got {:?}", value)));
}
match value.as_i64().and_then(|v| T::try_from(v).ok()) {
Some(value) => Ok(value),
None => Err(type_error(format!(
@ -359,6 +369,25 @@ impl From<U32x2> for u64 {
}
}
impl TryFrom<Value> for U32x2 {
type Error = AnyError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
if let Some(value) = value.as_array() {
if let Some(hi) = value[0].as_u64() {
if let Some(lo) = value[1].as_u64() {
return Ok(U32x2(hi as u32, lo as u32));
}
}
}
Err(type_error(format!(
"Expected FFI argument to be a signed integer, but got {:?}",
value
)))
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct ForeignFunction {
@ -627,16 +656,24 @@ fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result<Value, AnyError> {
json!(unsafe { symbol.cif.call::<i32>(symbol.ptr, &call_args) })
}
NativeType::U64 => {
json!(unsafe { symbol.cif.call::<u64>(symbol.ptr, &call_args) })
json!(U32x2::from(unsafe {
symbol.cif.call::<u64>(symbol.ptr, &call_args)
}))
}
NativeType::I64 => {
json!(unsafe { symbol.cif.call::<i64>(symbol.ptr, &call_args) })
json!(U32x2::from(unsafe {
symbol.cif.call::<i64>(symbol.ptr, &call_args)
} as u64))
}
NativeType::USize => {
json!(unsafe { symbol.cif.call::<usize>(symbol.ptr, &call_args) })
json!(U32x2::from(unsafe {
symbol.cif.call::<usize>(symbol.ptr, &call_args)
} as u64))
}
NativeType::ISize => {
json!(unsafe { symbol.cif.call::<isize>(symbol.ptr, &call_args) })
json!(U32x2::from(unsafe {
symbol.cif.call::<isize>(symbol.ptr, &call_args)
} as u64))
}
NativeType::F32 => {
json!(unsafe { symbol.cif.call::<f32>(symbol.ptr, &call_args) })

View file

@ -45,7 +45,7 @@ const remote = Deno.dlopen(
remote.symbols.method1(0);
// @ts-expect-error: Invalid return type
<number> remote.symbols.method1(0, 0);
<void> remote.symbols.method1(0, 0);
<void> remote.symbols.method1(0n, 0n);
// @ts-expect-error: Invalid argument
remote.symbols.method2(null);
@ -53,11 +53,11 @@ remote.symbols.method2(void 0);
// @ts-expect-error: Invalid argument
remote.symbols.method3(null);
remote.symbols.method3(0);
remote.symbols.method3(0n);
// @ts-expect-error: Invalid argument
remote.symbols.method4(null);
remote.symbols.method4(0);
remote.symbols.method4(0n);
// @ts-expect-error: Invalid argument
remote.symbols.method5(null);
@ -73,7 +73,7 @@ remote.symbols.method7(0);
// @ts-expect-error: Invalid argument
remote.symbols.method8(null);
remote.symbols.method8(0);
remote.symbols.method8(0n);
// @ts-expect-error: Invalid argument
remote.symbols.method9(null);
@ -89,7 +89,7 @@ remote.symbols.method11(0);
// @ts-expect-error: Invalid argument
remote.symbols.method12(null);
remote.symbols.method12(0);
remote.symbols.method12(0n);
// @ts-expect-error: Invalid argument
remote.symbols.method13(null);
@ -107,12 +107,12 @@ remote.symbols.method15({} as Deno.UnsafePointer);
const result = remote.symbols.method16();
// @ts-expect-error: Invalid argument
let r_0: string = result;
let r_1: number = result;
let r_1: number | bigint = result;
const result2 = remote.symbols.method17();
// @ts-expect-error: Invalid argument
result2.then((_0: string) => {});
result2.then((_1: number) => {});
result2.then((_1: number | bigint) => {});
const result3 = remote.symbols.method18();
// @ts-expect-error: Invalid argument
@ -138,16 +138,16 @@ fnptr.call(0, null);
// @ts-expect-error: Invalid member type
const static1_wrong: null = remote.symbols.static1;
const static1_right: number = remote.symbols.static1;
const static1_right: bigint = remote.symbols.static1;
// @ts-expect-error: Invalid member type
const static2_wrong: null = remote.symbols.static2;
const static2_right: Deno.UnsafePointer = remote.symbols.static2;
// @ts-expect-error: Invalid member type
const static3_wrong: null = remote.symbols.static3;
const static3_right: number = remote.symbols.static3;
const static3_right: bigint = remote.symbols.static3;
// @ts-expect-error: Invalid member type
const static4_wrong: null = remote.symbols.static4;
const static4_right: number = remote.symbols.static4;
const static4_right: bigint = remote.symbols.static4;
// @ts-expect-error: Invalid member type
const static5_wrong: null = remote.symbols.static5;
const static5_right: number = remote.symbols.static5;
@ -159,7 +159,7 @@ const static7_wrong: null = remote.symbols.static7;
const static7_right: number = remote.symbols.static7;
// @ts-expect-error: Invalid member type
const static8_wrong: null = remote.symbols.static8;
const static8_right: number = remote.symbols.static8;
const static8_right: bigint = remote.symbols.static8;
// @ts-expect-error: Invalid member type
const static9_wrong: null = remote.symbols.static9;
const static9_right: number = remote.symbols.static9;
@ -171,7 +171,7 @@ const static11_wrong: null = remote.symbols.static11;
const static11_right: number = remote.symbols.static11;
// @ts-expect-error: Invalid member type
const static12_wrong: null = remote.symbols.static12;
const static12_right: number = remote.symbols.static12;
const static12_right: bigint = remote.symbols.static12;
// @ts-expect-error: Invalid member type
const static13_wrong: null = remote.symbols.static13;
const static13_right: number = remote.symbols.static13;

View file

@ -59,10 +59,10 @@ fn basic() {
true\n\
579\n\
579\n\
579\n\
579\n\
579\n\
579\n\
8589934590n\n\
-8589934590n\n\
8589934590n\n\
-8589934590n\n\
579.9119873046875\n\
579.912\n\
After sleep_blocking\n\

View file

@ -145,10 +145,10 @@ assertThrows(
"Expected FFI argument to be an unsigned integer, but got Null",
);
console.log(dylib.symbols.add_i32(123, 456));
console.log(dylib.symbols.add_u64(123, 456));
console.log(dylib.symbols.add_i64(123, 456));
console.log(dylib.symbols.add_usize(123, 456));
console.log(dylib.symbols.add_isize(123, 456));
console.log(dylib.symbols.add_u64(0xffffffffn, 0xffffffffn));
console.log(dylib.symbols.add_i64(-0xffffffffn, -0xffffffffn));
console.log(dylib.symbols.add_usize(0xffffffffn, 0xffffffffn));
console.log(dylib.symbols.add_isize(-0xffffffffn, -0xffffffffn));
console.log(dylib.symbols.add_f32(123.123, 456.789));
console.log(dylib.symbols.add_f64(123.123, 456.789));