summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAapo Alasuutari <aapo.alasuutari@gmail.com>2022-07-24 13:41:11 +0300
committerGitHub <noreply@github.com>2022-07-24 16:11:11 +0530
commitf8fee6cd21cce82d6c34e539d39da86df7b036f7 (patch)
tree8099527951e4b532934a00c25797758aeb496ab6
parente1cbd2364f536a1cef817961967e1738b89be734 (diff)
feat(ext/ffi): Safe number pointers (#15173)
-rw-r--r--cli/dts/lib.deno.unstable.d.ts27
-rw-r--r--ext/ffi/00_ffi.js38
-rw-r--r--ext/ffi/jit_trampoline.rs27
-rw-r--r--ext/ffi/lib.rs948
-rw-r--r--test_ffi/tests/bench.js41
-rw-r--r--test_ffi/tests/ffi_types.ts27
-rw-r--r--test_ffi/tests/integration_tests.rs34
-rw-r--r--test_ffi/tests/test.js14
8 files changed, 713 insertions, 443 deletions
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index 50749e1d6..037dbe2c1 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -366,9 +366,9 @@ declare namespace Deno {
type ToNativeTypeMap =
& Record<NativeNumberType, number>
- & Record<NativeBigIntType, bigint | number>
- & Record<NativePointerType, TypedArray | bigint | null>
- & Record<NativeFunctionType, bigint | null>;
+ & Record<NativeBigIntType, PointerValue>
+ & Record<NativePointerType, TypedArray | PointerValue | null>
+ & Record<NativeFunctionType, PointerValue | null>;
/** Type conversion for foreign symbol parameters and unsafe callback return types */
type ToNativeType<T extends NativeType = NativeType> = ToNativeTypeMap[T];
@@ -391,9 +391,9 @@ declare namespace Deno {
type FromNativeTypeMap =
& Record<NativeNumberType, number>
- & Record<NativeBigIntType, bigint>
- & Record<NativePointerType, bigint>
- & Record<NativeFunctionType, bigint>;
+ & Record<NativeBigIntType, PointerValue>
+ & Record<NativePointerType, PointerValue>
+ & Record<NativeFunctionType, PointerValue>;
/** Type conversion for foreign symbol return types and unsafe callback parameters */
type FromNativeType<T extends NativeType = NativeType> = FromNativeTypeMap[T];
@@ -481,6 +481,15 @@ declare namespace Deno {
| BigInt64Array
| BigUint64Array;
+ /**
+ * Pointer type depends on the architecture and actual pointer value.
+ *
+ * On a 32 bit system all pointer values are plain numbers. On a 64 bit
+ * system pointer values are represented as numbers if the value is below
+ * `Number.MAX_SAFE_INTEGER`.
+ */
+ export type PointerValue = number | bigint;
+
/** **UNSTABLE**: Unsafe and new API, beware!
*
* An unsafe pointer to a memory location for passing and returning pointers to and from the ffi
@@ -489,7 +498,7 @@ declare namespace Deno {
/**
* Return the direct memory pointer to the typed array in memory
*/
- static of(value: Deno.UnsafeCallback | TypedArray): bigint;
+ static of(value: Deno.UnsafeCallback | TypedArray): PointerValue;
}
/** **UNSTABLE**: Unsafe and new API, beware!
@@ -517,9 +526,9 @@ declare namespace Deno {
/** Gets a signed 32-bit integer at the specified byte offset from the pointer. */
getInt32(offset?: number): number;
/** Gets an unsigned 64-bit integer at the specified byte offset from the pointer. */
- getBigUint64(offset?: number): bigint;
+ getBigUint64(offset?: number): PointerValue;
/** Gets a signed 64-bit integer at the specified byte offset from the pointer. */
- getBigInt64(offset?: number): bigint;
+ getBigInt64(offset?: number): PointerValue;
/** Gets a signed 32-bit float at the specified byte offset from the pointer. */
getFloat32(offset?: number): number;
/** Gets a signed 64-bit float at the specified byte offset from the pointer. */
diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js
index ac8dda317..bc03dfb14 100644
--- a/ext/ffi/00_ffi.js
+++ b/ext/ffi/00_ffi.js
@@ -12,11 +12,19 @@
TypeError,
} = window.__bootstrap.primordials;
- function unpackU64([hi, lo]) {
+ function unpackU64(returnValue) {
+ if (typeof returnValue === "number") {
+ return returnValue;
+ }
+ const [hi, lo] = returnValue;
return BigInt(hi) << 32n | BigInt(lo);
}
- function unpackI64([hi, lo]) {
+ function unpackI64(returnValue) {
+ if (typeof returnValue === "number") {
+ return returnValue;
+ }
+ const [hi, lo] = returnValue;
const u64 = unpackU64([hi, lo]);
return u64 >> 63n ? u64 - 0x10000000000000000n : u64;
}
@@ -31,77 +39,77 @@
getUint8(offset = 0) {
return core.opSync(
"op_ffi_read_u8",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getInt8(offset = 0) {
return core.opSync(
"op_ffi_read_i8",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getUint16(offset = 0) {
return core.opSync(
"op_ffi_read_u16",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getInt16(offset = 0) {
return core.opSync(
"op_ffi_read_i16",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getUint32(offset = 0) {
return core.opSync(
"op_ffi_read_u32",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getInt32(offset = 0) {
return core.opSync(
"op_ffi_read_i32",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getBigUint64(offset = 0) {
return core.opSync(
"op_ffi_read_u64",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getBigInt64(offset = 0) {
return core.opSync(
- "op_ffi_read_u64",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ "op_ffi_read_i64",
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getFloat32(offset = 0) {
return core.opSync(
"op_ffi_read_f32",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getFloat64(offset = 0) {
return core.opSync(
"op_ffi_read_f64",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getCString(offset = 0) {
return core.opSync(
"op_ffi_cstr_read",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
@@ -116,7 +124,7 @@
copyInto(destination, offset = 0) {
core.opSync(
"op_ffi_buf_copy_into",
- offset ? this.pointer + BigInt(offset) : this.pointer,
+ offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
destination,
destination.byteLength,
);
diff --git a/ext/ffi/jit_trampoline.rs b/ext/ffi/jit_trampoline.rs
index 2fd078d60..12c9b2a7d 100644
--- a/ext/ffi/jit_trampoline.rs
+++ b/ext/ffi/jit_trampoline.rs
@@ -5,6 +5,9 @@ use crate::{tcc::Compiler, Symbol};
use std::ffi::c_void;
use std::ffi::CString;
use std::fmt::Write as _;
+use std::mem::size_of;
+
+const _: () = assert!(size_of::<fn()>() == size_of::<usize>());
pub(crate) struct Allocation {
pub addr: *mut c_void,
@@ -22,12 +25,14 @@ fn native_arg_to_c(ty: &NativeType) -> &'static str {
match ty {
NativeType::U8 | NativeType::U16 | NativeType::U32 => "uint32_t",
NativeType::I8 | NativeType::I16 | NativeType::I32 => "int32_t",
- NativeType::U64 | NativeType::USize => "uint64_t",
- NativeType::I64 | NativeType::ISize => "int64_t",
NativeType::Void => "void",
NativeType::F32 => "float",
NativeType::F64 => "double",
- _ => unimplemented!(),
+ NativeType::U64 => "uint64_t",
+ NativeType::I64 => "int64_t",
+ NativeType::ISize => "intptr_t",
+ NativeType::USize => "uintptr_t",
+ NativeType::Pointer | NativeType::Function => "void*",
}
}
@@ -42,9 +47,11 @@ fn native_to_c(ty: &NativeType) -> &'static str {
NativeType::Void => "void",
NativeType::F32 => "float",
NativeType::F64 => "double",
- NativeType::U64 | NativeType::USize => "uint64_t",
- NativeType::I64 | NativeType::ISize => "int64_t",
- _ => unimplemented!(),
+ NativeType::U64 => "uint64_t",
+ NativeType::I64 => "int64_t",
+ NativeType::ISize => "intptr_t",
+ NativeType::USize => "uintptr_t",
+ NativeType::Pointer | NativeType::Function => "void*",
}
}
@@ -180,16 +187,16 @@ mod tests {
assert_eq!(
codegen(vec![NativeType::ISize, NativeType::U64], NativeType::Void),
"#include <stdint.h>\n\n\
- extern void func(int64_t p0, uint64_t p1);\n\n\
- void func_trampoline(void* recv, int64_t p0, uint64_t p1) {\
+ extern void func(intptr_t p0, uint64_t p1);\n\n\
+ void func_trampoline(void* recv, intptr_t p0, uint64_t p1) {\
\n return func(p0, p1);\n\
}\n\n"
);
assert_eq!(
codegen(vec![NativeType::USize, NativeType::USize], NativeType::U32),
"#include <stdint.h>\n\n\
- extern uint32_t func(uint64_t p0, uint64_t p1);\n\n\
- uint32_t func_trampoline(void* recv, uint64_t p0, uint64_t p1) {\
+ extern uint32_t func(uintptr_t p0, uintptr_t p1);\n\n\
+ uint32_t func_trampoline(void* recv, uintptr_t p0, uintptr_t p1) {\
\n return func(p0, p1);\n\
}\n\n"
);
diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs
index 396affdb3..6d0315964 100644
--- a/ext/ffi/lib.rs
+++ b/ext/ffi/lib.rs
@@ -48,6 +48,9 @@ thread_local! {
static LOCAL_ISOLATE_POINTER: RefCell<*const v8::Isolate> = RefCell::new(ptr::null());
}
+const MAX_SAFE_INTEGER: isize = 9007199254740991;
+const MIN_SAFE_INTEGER: isize = -9007199254740991;
+
pub struct Unstable(pub bool);
fn check_unstable(state: &OpState, api_name: &str) {
@@ -93,7 +96,7 @@ struct PtrSymbol {
}
impl PtrSymbol {
- fn new(fn_ptr: u64, def: &ForeignFunction) -> Self {
+ fn new(fn_ptr: usize, def: &ForeignFunction) -> Self {
let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
let cif = libffi::middle::Cif::new(
def
@@ -175,6 +178,7 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
op_ffi_read_u32::decl::<P>(),
op_ffi_read_i32::decl::<P>(),
op_ffi_read_u64::decl::<P>(),
+ op_ffi_read_i64::decl::<P>(),
op_ffi_read_f32::decl::<P>(),
op_ffi_read_f64::decl::<P>(),
op_ffi_unsafe_callback_create::decl::<P>(),
@@ -323,21 +327,46 @@ impl NativeValue {
NativeType::U32 => Value::from(self.u32_value),
NativeType::I32 => Value::from(self.i32_value),
NativeType::U64 => {
- json!(U32x2::from(self.u64_value))
+ let value = self.u64_value;
+ if value > MAX_SAFE_INTEGER as u64 {
+ json!(U32x2::from(self.u64_value))
+ } else {
+ Value::from(value)
+ }
}
NativeType::I64 => {
- json!(U32x2::from(self.i64_value as u64))
+ let value = self.i64_value;
+ if value > MAX_SAFE_INTEGER as i64 || value < MIN_SAFE_INTEGER as i64 {
+ json!(U32x2::from(self.i64_value as u64))
+ } else {
+ Value::from(value)
+ }
}
NativeType::USize => {
- json!(U32x2::from(self.usize_value as u64))
+ let value = self.usize_value;
+ if value > MAX_SAFE_INTEGER as usize {
+ json!(U32x2::from(self.usize_value as u64))
+ } else {
+ Value::from(value)
+ }
}
NativeType::ISize => {
- json!(U32x2::from(self.isize_value as u64))
+ let value = self.isize_value;
+ if value > MAX_SAFE_INTEGER || value < MIN_SAFE_INTEGER {
+ json!(U32x2::from(self.isize_value as u64))
+ } else {
+ Value::from(value)
+ }
}
NativeType::F32 => Value::from(self.f32_value),
NativeType::F64 => Value::from(self.f64_value),
NativeType::Pointer | NativeType::Function => {
- json!(U32x2::from(self.pointer as u64))
+ let value = self.pointer as usize;
+ if value > MAX_SAFE_INTEGER as usize {
+ json!(U32x2::from(value as u64))
+ } else {
+ Value::from(value)
+ }
}
}
}
@@ -385,23 +414,44 @@ impl NativeValue {
local_value.into()
}
NativeType::U64 => {
+ let value = self.u64_value;
let local_value: v8::Local<v8::Value> =
- v8::BigInt::new_from_u64(scope, self.u64_value).into();
+ if value > MAX_SAFE_INTEGER as u64 {
+ v8::BigInt::new_from_u64(scope, value).into()
+ } else {
+ v8::Number::new(scope, value as f64).into()
+ };
local_value.into()
}
NativeType::I64 => {
+ let value = self.i64_value;
let local_value: v8::Local<v8::Value> =
- v8::BigInt::new_from_i64(scope, self.i64_value).into();
+ if value > MAX_SAFE_INTEGER as i64 || value < MIN_SAFE_INTEGER as i64
+ {
+ v8::BigInt::new_from_i64(scope, self.i64_value).into()
+ } else {
+ v8::Number::new(scope, value as f64).into()
+ };
local_value.into()
}
NativeType::USize => {
+ let value = self.usize_value;
let local_value: v8::Local<v8::Value> =
- v8::BigInt::new_from_u64(scope, self.usize_value as u64).into();
+ if value > MAX_SAFE_INTEGER as usize {
+ v8::BigInt::new_from_u64(scope, value as u64).into()
+ } else {
+ v8::Number::new(scope, value as f64).into()
+ };
local_value.into()
}
NativeType::ISize => {
+ let value = self.isize_value;
let local_value: v8::Local<v8::Value> =
- v8::BigInt::new_from_i64(scope, self.isize_value as i64).into();
+ if value > MAX_SAFE_INTEGER || value < MIN_SAFE_INTEGER {
+ v8::BigInt::new_from_i64(scope, self.isize_value as i64).into()
+ } else {
+ v8::Number::new(scope, value as f64).into()
+ };
local_value.into()
}
NativeType::F32 => {
@@ -415,8 +465,13 @@ impl NativeValue {
local_value.into()
}
NativeType::Pointer | NativeType::Function => {
+ let value = self.pointer as u64;
let local_value: v8::Local<v8::Value> =
- v8::BigInt::new_from_u64(scope, self.pointer as u64).into();
+ if value > MAX_SAFE_INTEGER as u64 {
+ v8::BigInt::new_from_u64(scope, value).into()
+ } else {
+ v8::Number::new(scope, value as f64).into()
+ };
local_value.into()
}
}
@@ -677,10 +732,11 @@ impl From<&NativeType> for fast_api::Type {
NativeType::F32 => fast_api::Type::Float32,
NativeType::F64 => fast_api::Type::Float64,
NativeType::Void => fast_api::Type::Void,
- NativeType::I64 | NativeType::ISize => fast_api::Type::Int64,
- NativeType::U64 | NativeType::USize => fast_api::Type::Uint64,
- NativeType::Function | NativeType::Pointer => {
- panic!("Cannot be fast api")
+ NativeType::I64 => fast_api::Type::Int64,
+ NativeType::U64 => fast_api::Type::Uint64,
+ NativeType::ISize => fast_api::Type::Int64,
+ NativeType::USize | NativeType::Function | NativeType::Pointer => {
+ fast_api::Type::Uint64
}
}
}
@@ -699,11 +755,6 @@ fn is_fast_api_rv(rv: NativeType) -> bool {
)
}
-#[cfg(not(target_os = "windows"))]
-fn is_fast_api_arg(rv: NativeType) -> bool {
- !matches!(rv, NativeType::Function | NativeType::Pointer)
-}
-
// Create a JavaScript function for synchronous FFI call to
// the given symbol.
fn make_sync_fn<'s>(
@@ -719,10 +770,7 @@ fn make_sync_fn<'s>(
#[cfg(not(target_os = "windows"))]
let mut fast_allocations: Option<*mut ()> = None;
#[cfg(not(target_os = "windows"))]
- if !sym.can_callback
- && !sym.parameter_types.iter().any(|t| !is_fast_api_arg(*t))
- && is_fast_api_rv(sym.result_type)
- {
+ if !sym.can_callback && is_fast_api_rv(sym.result_type) {
let ret = fast_api::Type::from(&sym.result_type);
let mut args = sym
@@ -792,6 +840,228 @@ fn make_sync_fn<'s>(
weak.to_local(scope).unwrap()
}
+#[inline]
+fn ffi_parse_u8_arg(
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ let u8_value = v8::Local::<v8::Uint32>::try_from(arg)
+ .map_err(|_| type_error("Invalid FFI u8 type, expected unsigned integer"))?
+ .value() as u8;
+ Ok(NativeValue { u8_value })
+}
+
+#[inline]
+fn ffi_parse_i8_arg(
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ let i8_value = v8::Local::<v8::Int32>::try_from(arg)
+ .map_err(|_| type_error("Invalid FFI i8 type, expected integer"))?
+ .value() as i8;
+ Ok(NativeValue { i8_value })
+}
+
+#[inline]
+fn ffi_parse_u16_arg(
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ let u16_value = v8::Local::<v8::Uint32>::try_from(arg)
+ .map_err(|_| type_error("Invalid FFI u16 type, expected unsigned integer"))?
+ .value() as u16;
+ Ok(NativeValue { u16_value })
+}
+
+#[inline]
+fn ffi_parse_i16_arg(
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ let i16_value = v8::Local::<v8::Int32>::try_from(arg)
+ .map_err(|_| type_error("Invalid FFI i16 type, expected integer"))?
+ .value() as i16;
+ Ok(NativeValue { i16_value })
+}
+
+#[inline]
+fn ffi_parse_u32_arg(
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ let u32_value = v8::Local::<v8::Uint32>::try_from(arg)
+ .map_err(|_| type_error("Invalid FFI u32 type, expected unsigned integer"))?
+ .value() as u32;
+ Ok(NativeValue { u32_value })
+}
+
+#[inline]
+fn ffi_parse_i32_arg(
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ let i32_value = v8::Local::<v8::Int32>::try_from(arg)
+ .map_err(|_| type_error("Invalid FFI i32 type, expected integer"))?
+ .value() as i32;
+ Ok(NativeValue { i32_value })
+}
+
+#[inline]
+fn ffi_parse_u64_arg(
+ scope: &mut v8::HandleScope,
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ // Order of checking:
+ // 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case.
+ // 2. Number: Common, supported by Fast API, so let that be the optimal case.
+ let u64_value: u64 = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg)
+ {
+ value.u64_value().0
+ } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
+ value.integer_value(scope).unwrap() as u64
+ } else {
+ return Err(type_error(
+ "Invalid FFI u64 type, expected unsigned integer",
+ ));
+ };
+ Ok(NativeValue { u64_value })
+}
+
+#[inline]
+fn ffi_parse_i64_arg(
+ scope: &mut v8::HandleScope,
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ // Order of checking:
+ // 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case.
+ // 2. Number: Common, supported by Fast API, so let that be the optimal case.
+ let i64_value: i64 = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg)
+ {
+ value.i64_value().0
+ } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
+ value.integer_value(scope).unwrap() as i64
+ } else {
+ return Err(type_error("Invalid FFI i64 type, expected integer"));
+ };
+ Ok(NativeValue { i64_value })
+}
+
+#[inline]
+fn ffi_parse_usize_arg(
+ scope: &mut v8::HandleScope,
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ // Order of checking:
+ // 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case.
+ // 2. Number: Common, supported by Fast API, so let that be the optimal case.
+ let usize_value: usize =
+ if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) {
+ value.u64_value().0 as usize
+ } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
+ value.integer_value(scope).unwrap() as usize
+ } else {
+ return Err(type_error("Invalid FFI usize type, expected integer"));
+ };
+ Ok(NativeValue { usize_value })
+}
+
+#[inline]
+fn ffi_parse_isize_arg(
+ scope: &mut v8::HandleScope,
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ // Order of checking:
+ // 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case.
+ // 2. Number: Common, supported by Fast API, so let that be the optimal case.
+ let isize_value: isize =
+ if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) {
+ value.i64_value().0 as isize
+ } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
+ value.integer_value(scope).unwrap() as isize
+ } else {
+ return Err(type_error("Invalid FFI isize type, expected integer"));
+ };
+ Ok(NativeValue { isize_value })
+}
+
+#[inline]
+fn ffi_parse_f32_arg(
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ let f32_value = v8::Local::<v8::Number>::try_from(arg)
+ .map_err(|_| type_error("Invalid FFI f32 type, expected number"))?
+ .value() as f32;
+ Ok(NativeValue { f32_value })
+}
+
+#[inline]
+fn ffi_parse_f64_arg(
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ let f64_value = v8::Local::<v8::Number>::try_from(arg)
+ .map_err(|_| type_error("Invalid FFI f64 type, expected number"))?
+ .value() as f64;
+ Ok(NativeValue { f64_value })
+}
+
+#[inline]
+fn ffi_parse_pointer_arg(
+ scope: &mut v8::HandleScope,
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ // Order of checking:
+ // 1. ArrayBufferView: Common and not supported by Fast API, optimise this case.
+ // 2. BigInt: Uncommon and not supported by Fast API, optimise this case as second.
+ // 3. Number: Common and supported by Fast API, optimise the common case third.
+ // 4. ArrayBuffer: Fairly common and not supported by Fast API.
+ // 5. Null: Very uncommon / can be represented by a 0.
+ let pointer = if let Ok(value) =
+ v8::Local::<v8::ArrayBufferView>::try_from(arg)
+ {
+ let byte_offset = value.byte_offset();
+ let backing_store = value
+ .buffer(scope)
+ .ok_or_else(|| {
+ type_error("Invalid FFI ArrayBufferView, expected data in the buffer")
+ })?
+ .get_backing_store();
+ if byte_offset > 0 {
+ &backing_store[byte_offset..] as *const _ as *const u8
+ } else {
+ &backing_store[..] as *const _ as *const u8
+ }
+ } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) {
+ value.u64_value().0 as usize as *const u8
+ } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
+ value.integer_value(scope).unwrap() as usize as *const u8
+ } else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(arg) {
+ let backing_store = value.get_backing_store();
+ &backing_store[..] as *const _ as *const u8
+ } else if arg.is_null() {
+ ptr::null()
+ } else {
+ return Err(type_error("Invalid FFI pointer type, expected null, integer, BigInt, ArrayBuffer, or ArrayBufferView"));
+ };
+ Ok(NativeValue { pointer })
+}
+
+#[inline]
+fn ffi_parse_function_arg(
+ scope: &mut v8::HandleScope,
+ arg: v8::Local<v8::Value>,
+) -> Result<NativeValue, AnyError> {
+ // Order of checking:
+ // 1. BigInt: Uncommon and not supported by Fast API, optimise this case.
+ // 2. Number: Common and supported by Fast API, optimise this case as second.
+ // 3. Null: Very uncommon / can be represented by a 0.
+ let pointer = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) {
+ value.u64_value().0 as usize as *const u8
+ } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
+ value.integer_value(scope).unwrap() as usize as *const u8
+ } else if arg.is_null() {
+ ptr::null()
+ } else {
+ return Err(type_error(
+ "Invalid FFI function type, expected null, integer, or BigInt",
+ ));
+ };
+ Ok(NativeValue { pointer })
+}
+
fn ffi_parse_args<'scope>(
scope: &mut v8::HandleScope<'scope>,
args: serde_v8::Value<'scope>,
@@ -812,155 +1082,50 @@ where
for (index, native_type) in parameter_types.iter().enumerate() {
let value = args.get_index(scope, index as u32).unwrap();
match native_type {
- NativeType::Void => {
- unreachable!();
- }
NativeType::U8 => {
- let value = value
- .uint32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI u8 type, expected number"))?
- as u8;
- ffi_args.push(NativeValue { u8_value: value });
+ ffi_args.push(ffi_parse_u8_arg(value)?);
}
NativeType::I8 => {
- let value = value
- .int32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI i8 type, expected number"))?
- as i8;
- ffi_args.push(NativeValue { i8_value: value });
+ ffi_args.push(ffi_parse_i8_arg(value)?);
}
NativeType::U16 => {
- let value = value
- .uint32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI u16 type, expected number"))?
- as u16;
- ffi_args.push(NativeValue { u16_value: value });
+ ffi_args.push(ffi_parse_u16_arg(value)?);
}
NativeType::I16 => {
- let value = value
- .int32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI i16 type, expected number"))?
- as i16;
- ffi_args.push(NativeValue { i16_value: value });
+ ffi_args.push(ffi_parse_i16_arg(value)?);
}
NativeType::U32 => {
- let value = value
- .uint32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI u32 type, expected number"))?
- as u32;
- ffi_args.push(NativeValue { u32_value: value });
+ ffi_args.push(ffi_parse_u32_arg(value)?);
}
NativeType::I32 => {
- let value = value
- .int32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI i32 type, expected number"))?
- as i32;
- ffi_args.push(NativeValue { i32_value: value });
+ ffi_args.push(ffi_parse_i32_arg(value)?);
}
NativeType::U64 => {
- let value: u64 =
- if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.u64_value().0
- } else {
- value.integer_value(scope).ok_or_else(|| {
- type_error("Invalid FFI u64 type, expected number")
- })? as u64
- };
- ffi_args.push(NativeValue { u64_value: value });
+ ffi_args.push(ffi_parse_u64_arg(scope, value)?);
}
NativeType::I64 => {
- let value: i64 =
- if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.i64_value().0
- } else {
- value.integer_value(scope).ok_or_else(|| {
- type_error("Invalid FFI i64 type, expected number")
- })? as i64
- };
- ffi_args.push(NativeValue { i64_value: value });
+ ffi_args.push(ffi_parse_i64_arg(scope, value)?);
}
NativeType::USize => {
- let value: usize =
- if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.u64_value().0 as usize
- } else {
- value.integer_value(scope).ok_or_else(|| {
- type_error("Invalid FFI usize type, expected number")
- })? as usize
- };
- ffi_args.push(NativeValue { usize_value: value });
+ ffi_args.push(ffi_parse_usize_arg(scope, value)?);
}
NativeType::ISize => {
- let value: isize =
- if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.i64_value().0 as isize
- } else {
- value.integer_value(scope).ok_or_else(|| {
- type_error("Invalid FFI isize type, expected number")
- })? as isize
- };
- ffi_args.push(NativeValue { isize_value: value });
+ ffi_args.push(ffi_parse_isize_arg(scope, value)?);
}
NativeType::F32 => {
- let value = value
- .number_value(scope)
- .ok_or_else(|| type_error("Invalid FFI f32 type, expected number"))?
- as f32;
- ffi_args.push(NativeValue { f32_value: value });
+ ffi_args.push(ffi_parse_f32_arg(value)?);
}
NativeType::F64 => {
- let value = value
- .number_value(scope)
- .ok_or_else(|| type_error("Invalid FFI f64 type, expected number"))?
- as f64;
- ffi_args.push(NativeValue { f64_value: value });
+ ffi_args.push(ffi_parse_f64_arg(value)?);
}
NativeType::Pointer => {
- if value.is_null() {
- let value: *const u8 = ptr::null();
- ffi_args.push(NativeValue { pointer: value })
- } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- let value = value.u64_value().0 as *const u8;
- ffi_args.push(NativeValue { pointer: value });
- } else if let Ok(value) =
- v8::Local::<v8::ArrayBufferView>::try_from(value)
- {
- let byte_offset = value.byte_offset();
- let backing_store = value
- .buffer(scope)
- .ok_or_else(|| {
- type_error(
- "Invalid FFI ArrayBufferView, expected data in the buffer",
- )
- })?
- .get_backing_store();
- let pointer = if byte_offset > 0 {
- &backing_store[byte_offset..] as *const _ as *const u8
- } else {
- &backing_store[..] as *const _ as *const u8
- };
- ffi_args.push(NativeValue { pointer });
- } else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value)
- {
- let backing_store = value.get_backing_store();
- let pointer = &backing_store[..] as *const _ as *const u8;
- ffi_args.push(NativeValue { pointer });
- } else {
- return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer, or ArrayBufferView"));
- }
+ ffi_args.push(ffi_parse_pointer_arg(scope, value)?);
}
NativeType::Function => {
- if value.is_null() {
- let value: *const u8 = ptr::null();
- ffi_args.push(NativeValue { pointer: value })
- } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- let value = value.u64_value().0 as *const u8;
- ffi_args.push(NativeValue { pointer: value });
- } else {
- return Err(type_error(
- "Invalid FFI function type, expected null, or BigInt",
- ));
- }
+ ffi_args.push(ffi_parse_function_arg(scope, value)?);
+ }
+ NativeType::Void => {
+ unreachable!();
}
}
}
@@ -990,169 +1155,50 @@ where
for (index, native_type) in parameter_types.iter().enumerate() {
let value = args.get(index as i32);
match native_type {
- NativeType::Void => {
- unreachable!();
- }
NativeType::U8 => {
- let value = value
- .uint32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI u8 type, expected number"))?
- as u8;
- ffi_args.push(NativeValue { u8_value: value });
+ ffi_args.push(ffi_parse_u8_arg(value)?);
}
NativeType::I8 => {
- let value = value
- .int32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI i8 type, expected number"))?
- as i8;
-
- ffi_args.push(NativeValue { i8_value: value });
+ ffi_args.push(ffi_parse_i8_arg(value)?);
}
NativeType::U16 => {
- let value = value
- .uint32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI u16 type, expected number"))?
- as u16;
-
- ffi_args.push(NativeValue { u16_value: value });
+ ffi_args.push(ffi_parse_u16_arg(value)?);
}
NativeType::I16 => {
- let value = value
- .int32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI i16 type, expected number"))?
- as i16;
-
- ffi_args.push(NativeValue { i16_value: value });
+ ffi_args.push(ffi_parse_i16_arg(value)?);
}
NativeType::U32 => {
- let value = value
- .uint32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI u32 type, expected number"))?
- as u32;
-
- ffi_args.push(NativeValue { u32_value: value });
+ ffi_args.push(ffi_parse_u32_arg(value)?);
}
NativeType::I32 => {
- let value = value
- .int32_value(scope)
- .ok_or_else(|| type_error("Invalid FFI i32 type, expected number"))?
- as i32;
-
- ffi_args.push(NativeValue { i32_value: value });
+ ffi_args.push(ffi_parse_i32_arg(value)?);
}
NativeType::U64 => {
- let value: u64 =
- if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.u64_value().0
- } else {
- value.integer_value(scope).ok_or_else(|| {
- type_error("Invalid FFI u64 type, expected number")
- })? as u64
- };
-
- ffi_args.push(NativeValue { u64_value: value });
+ ffi_args.push(ffi_parse_u64_arg(scope, value)?);
}
NativeType::I64 => {
- let value: i64 =
- if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.i64_value().0
- } else {
- value.integer_value(scope).ok_or_else(|| {
- type_error("Invalid FFI i64 type, expected number")
- })? as i64
- };
-
- ffi_args.push(NativeValue { i64_value: value });
+ ffi_args.push(ffi_parse_i64_arg(scope, value)?);
}
NativeType::USize => {
- let value: usize =
- if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.u64_value().0 as usize
- } else {
- value.integer_value(scope).ok_or_else(|| {
- type_error("Invalid FFI usize type, expected number")
- })? as usize
- };
-
- ffi_args.push(NativeValue { usize_value: value });
+ ffi_args.push(ffi_parse_usize_arg(scope, value)?);
}
NativeType::ISize => {
- let value: isize =
- if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.i64_value().0 as isize
- } else {
- value.integer_value(scope).ok_or_else(|| {
- type_error("Invalid FFI isize type, expected number")
- })? as isize
- };
-
- ffi_args.push(NativeValue { isize_value: value });
+ ffi_args.push(ffi_parse_isize_arg(scope, value)?);
}
NativeType::F32 => {
- let value = value
- .number_value(scope)
- .ok_or_else(|| type_error("Invalid FFI f32 type, expected number"))?
- as f32;
-
- ffi_args.push(NativeValue { f32_value: value });
+ ffi_args.push(ffi_parse_f32_arg(value)?);
}
NativeType::F64 => {
- let value = value
- .number_value(scope)
- .ok_or_else(|| type_error("Invalid FFI f64 type, expected number"))?
- as f64;
- ffi_args.push(NativeValue { f64_value: value });
+ ffi_args.push(ffi_parse_f64_arg(value)?);
}
NativeType::Pointer => {
- if value.is_null() {
- let value: *const u8 = ptr::null();
-
- ffi_args.push(NativeValue { pointer: value })
- } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- let value = value.u64_value().0 as *const u8;
-
- ffi_args.push(NativeValue { pointer: value });
- } else if let Ok(value) =
- v8::Local::<v8::ArrayBufferView>::try_from(value)
- {
- let byte_offset = value.byte_offset();
- let backing_store = value
- .buffer(scope)
- .ok_or_else(|| {
- type_error(
- "Invalid FFI ArrayBufferView, expected data in the buffer",
- )
- })?
- .get_backing_store();
- let pointer = if byte_offset > 0 {
- &backing_store[byte_offset..] as *const _ as *const u8
- } else {
- &backing_store[..] as *const _ as *const u8
- };
-
- ffi_args.push(NativeValue { pointer });
- } else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value)
- {
- let backing_store = value.get_backing_store();
- let pointer = &backing_store[..] as *const _ as *const u8;
-
- ffi_args.push(NativeValue { pointer });
- } else {
- return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer, or ArrayBufferView"));
- }
+ ffi_args.push(ffi_parse_pointer_arg(scope, value)?);
}
NativeType::Function => {
- if value.is_null() {
- let value: *const u8 = ptr::null();
- ffi_args.push(NativeValue { pointer: value })
- } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- let value = value.u64_value().0 as *const u8;
- ffi_args.push(NativeValue { pointer: value });
- } else {
- return Err(type_error(
- "Invalid FFI function type, expected null, or BigInt",
- ));
- }
+ ffi_args.push(ffi_parse_function_arg(scope, value)?);
+ }
+ NativeType::Void => {
+ unreachable!();
}
}
}
@@ -1373,8 +1419,8 @@ unsafe fn do_ffi_callback(
// it somehow cannot change the values that the loop sees, even if they both
// refer the same `let bool_value`.
let mut cb_scope = v8::CallbackScope::new(context);
- let mut scope = v8::HandleScope::new(&mut cb_scope);
- let func = callback.open(&mut scope);
+ let scope = &mut v8::HandleScope::new(&mut cb_scope);
+ let func = callback.open(scope);
let result = result as *mut c_void;
let repr: &[*mut ffi_type] =
std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize);
@@ -1386,45 +1432,62 @@ unsafe fn do_ffi_callback(
let value: v8::Local<v8::Value> = match (*(*repr)).type_ as _ {
FFI_TYPE_FLOAT => {
let value = *((*val) as *const f32);
- v8::Number::new(&mut scope, value as f64).into()
+ v8::Number::new(scope, value as f64).into()
}
FFI_TYPE_DOUBLE => {
let value = *((*val) as *const f64);
- v8::Number::new(&mut scope, value).into()
+ v8::Number::new(scope, value).into()
}
FFI_TYPE_SINT8 => {
let value = *((*val) as *const i8);
- v8::Integer::new(&mut scope, value as i32).into()
+ v8::Integer::new(scope, value as i32).into()
}
FFI_TYPE_UINT8 => {
let value = *((*val) as *const u8);
- v8::Integer::new_from_unsigned(&mut scope, value as u32).into()
+ v8::Integer::new_from_unsigned(scope, value as u32).into()
}
FFI_TYPE_SINT16 => {
let value = *((*val) as *const i16);
- v8::Integer::new(&mut scope, value as i32).into()
+ v8::Integer::new(scope, value as i32).into()
}
FFI_TYPE_UINT16 => {
let value = *((*val) as *const u16);
- v8::Integer::new_from_unsigned(&mut scope, value as u32).into()
+ v8::Integer::new_from_unsigned(scope, value as u32).into()
}
FFI_TYPE_INT | FFI_TYPE_SINT32 => {
let value = *((*val) as *const i32);
- v8::Integer::new(&mut scope, value).into()
+ v8::Integer::new(scope, value).into()
}
FFI_TYPE_UINT32 => {
let value = *((*val) as *const u32);
- v8::Integer::new_from_unsigned(&mut scope, value).into()
+ v8::Integer::new_from_unsigned(scope, value).into()
}
FFI_TYPE_SINT64 => {
let result = *((*val) as *const i64);
- v8::BigInt::new_from_i64(&mut scope, result).into()
+ if result > MAX_SAFE_INTEGER as i64 || result < MIN_SAFE_INTEGER as i64
+ {
+ v8::BigInt::new_from_i64(scope, result).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ }
}
- FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 => {
+ FFI_TYPE_UINT64 => {
let result = *((*val) as *const u64);
- v8::BigInt::new_from_u64(&mut scope, result).into()
+ if result > MAX_SAFE_INTEGER as u64 {
+ v8::BigInt::new_from_u64(scope, result).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ }
}
- FFI_TYPE_VOID => v8::undefined(&mut scope).into(),
+ FFI_TYPE_POINTER | FFI_TYPE_STRUCT => {
+ let result = *((*val) as *const u64);
+ if result > MAX_SAFE_INTEGER as u64 {
+ v8::BigInt::new_from_u64(scope, result).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ }
+ }
+ FFI_TYPE_VOID => v8::undefined(scope).into(),
_ => {
unreachable!()
}
@@ -1432,8 +1495,8 @@ unsafe fn do_ffi_callback(
params.push(value);
}
- let recv = v8::undefined(&mut scope);
- let call_result = func.call(&mut scope, recv.into(), &params);
+ let recv = v8::undefined(scope);
+ let call_result = func.call(scope, recv.into(), &params);
std::mem::forget(callback);
if call_result.is_none() {
@@ -1461,7 +1524,7 @@ unsafe fn do_ffi_callback(
}
FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64
| FFI_TYPE_SINT64 => {
- *(result as *mut u64) = 0;
+ *(result as *mut usize) = 0;
}
FFI_TYPE_VOID => {
// nop
@@ -1477,87 +1540,134 @@ unsafe fn do_ffi_callback(
match (*cif.rtype).type_ as _ {
FFI_TYPE_INT | FFI_TYPE_SINT32 => {
- *(result as *mut i32) = value
- .int32_value(&mut scope)
- .expect("Unable to deserialize result parameter.")
- as i32;
+ let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ value.value() as i32
+ } else {
+ // Fallthrough, probably UB.
+ value
+ .int32_value(scope)
+ .expect("Unable to deserialize result parameter.") as i32
+ };
+ *(result as *mut i32) = value;
}
FFI_TYPE_FLOAT => {
- *(result as *mut f32) = value
- .number_value(&mut scope)
- .expect("Unable to deserialize result parameter.")
- as f32;
+ let value = if let Ok(value) = v8::Local::<v8::Number>::try_from(value) {
+ value.value() as f32
+ } else {
+ // Fallthrough, probably UB.
+ value
+ .number_value(scope)
+ .expect("Unable to deserialize result parameter.") as f32
+ };
+ *(result as *mut f32) = value;
}
FFI_TYPE_DOUBLE => {
- *(result as *mut f64) = value
- .number_value(&mut scope)
- .expect("Unable to deserialize result parameter.");
+ let value = if let Ok(value) = v8::Local::<v8::Number>::try_from(value) {
+ value.value()
+ } else {
+ // Fallthrough, probably UB.
+ value
+ .number_value(scope)
+ .expect("Unable to deserialize result parameter.")
+ };
+ *(result as *mut f64) = value;
}
FFI_TYPE_POINTER | FFI_TYPE_STRUCT => {
- if let Ok(value) = v8::Local::<v8::ArrayBufferView>::try_from(value) {
+ let pointer = if let Ok(value) =
+ v8::Local::<v8::ArrayBufferView>::try_from(value)
+ {
let byte_offset = value.byte_offset();
let backing_store = value
- .buffer(&mut scope)
+ .buffer(scope)
.expect("Unable to deserialize result parameter.")
.get_backing_store();
- let pointer = if byte_offset > 0 {
+ if byte_offset > 0 {
&backing_store[byte_offset..] as *const _ as *const u8
} else {
&backing_store[..] as *const _ as *const u8
- };
- *(result as *mut *const u8) = pointer;
+ }
+ } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
+ value.u64_value().0 as usize as *const u8
} else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value) {
let backing_store = value.get_backing_store();
- let pointer = &backing_store[..] as *const _ as *const u8;
- *(result as *mut *const u8) = pointer;
- } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- *(result as *mut u64) = value.u64_value().0;
+ &backing_store[..] as *const _ as *const u8
+ } else if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ value.value() as usize as *const u8
} else if value.is_null() {
- *(result as *mut *const c_void) = ptr::null();
+ ptr::null()
} else {
// Fallthrough: Probably someone returned a number but this could
// also be eg. a string. This is essentially UB.
- *(result as *mut u64) = value
- .integer_value(&mut scope)
- .expect("Unable to deserialize result parameter.")
- as u64;
- }
+ value
+ .integer_value(scope)
+ .expect("Unable to deserialize result parameter.") as usize
+ as *const u8
+ };
+ *(result as *mut *const u8) = pointer;
}
FFI_TYPE_SINT8 => {
- *(result as *mut i8) = value
- .int32_value(&mut scope)
- .expect("Unable to deserialize result parameter.")
- as i8;
+ let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ value.value() as i8
+ } else {
+ // Fallthrough, essentially UB.
+ value
+ .int32_value(scope)
+ .expect("Unable to deserialize result parameter.") as i8
+ };
+ *(result as *mut i8) = value;
}
FFI_TYPE_UINT8 => {
- *(result as *mut u8) = value
- .uint32_value(&mut scope)
- .expect("Unable to deserialize result parameter.")
- as u8;
+ let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ value.value() as u8
+ } else {
+ // Fallthrough, essentially UB.
+ value
+ .uint32_value(scope)
+ .expect("Unable to deserialize result parameter.") as u8
+ };
+ *(result as *mut u8) = value;
}
FFI_TYPE_SINT16 => {
- *(result as *mut i16) = value
- .int32_value(&mut scope)
- .expect("Unable to deserialize result parameter.")
- as i16;
+ let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ value.value() as i16
+ } else {
+ // Fallthrough, essentially UB.
+ value
+ .int32_value(scope)
+ .expect("Unable to deserialize result parameter.") as i16
+ };
+ *(result as *mut i16) = value;
}
FFI_TYPE_UINT16 => {
- *(result as *mut u16) = value
- .uint32_value(&mut scope)
- .expect("Unable to deserialize result parameter.")
- as u16;
+ let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ value.value() as u16
+ } else {
+ // Fallthrough, essentially UB.
+ value
+ .uint32_value(scope)
+ .expect("Unable to deserialize result parameter.") as u16
+ };
+ *(result as *mut u16) = value;
}
FFI_TYPE_UINT32 => {
- *(result as *mut u32) = value
- .uint32_value(&mut scope)
- .expect("Unable to deserialize result parameter.");
+ let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ value.value() as u32
+ } else {
+ // Fallthrough, essentially UB.
+ value
+ .uint32_value(scope)
+ .expect("Unable to deserialize result parameter.")
+ };
+ *(result as *mut u32) = value;
}
FFI_TYPE_SINT64 => {
if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
*(result as *mut i64) = value.i64_value().0;
+ } else if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ *(result as *mut i64) = value.value();
} else {
*(result as *mut i64) = value
- .integer_value(&mut scope)
+ .integer_value(scope)
.expect("Unable to deserialize result parameter.")
as i64;
}
@@ -1565,9 +1675,11 @@ unsafe fn do_ffi_callback(
FFI_TYPE_UINT64 => {
if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
*(result as *mut u64) = value.u64_value().0;
+ } else if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
+ *(result as *mut u64) = value.value() as u64;
} else {
*(result as *mut u64) = value
- .integer_value(&mut scope)
+ .integer_value(scope)
.expect("Unable to deserialize result parameter.")
as u64;
}
@@ -1629,15 +1741,19 @@ where
);
let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, info);
- let ptr = *closure.code_ptr() as usize as u64;
+ let ptr = *closure.code_ptr() as usize;
let resource = UnsafeCallbackResource { closure, info };
let rid = state.resource_table.add(resource);
let rid_local = v8::Integer::new_from_unsigned(scope, rid);
- let ptr_local = v8::BigInt::new_from_u64(scope, ptr);
+ let ptr_local: v8::Local<v8::Value> = if ptr > MAX_SAFE_INTEGER as usize {
+ v8::BigInt::new_from_u64(scope, ptr as u64).into()
+ } else {
+ v8::Number::new(scope, ptr as f64).into()
+ };
let array = v8::Array::new(scope, 2);
array.set_index(scope, 0, rid_local.into());
- array.set_index(scope, 1, ptr_local.into());
+ array.set_index(scope, 1, ptr_local);
let array_value: v8::Local<v8::Value> = array.into();
Ok(array_value.into())
@@ -1647,7 +1763,7 @@ where
fn op_ffi_call_ptr<FP, 'scope>(
scope: &mut v8::HandleScope<'scope>,
state: Rc<RefCell<deno_core::OpState>>,
- pointer: u64,
+ pointer: usize,
def: ForeignFunction,
parameters: serde_v8::Value<'scope>,
) -> Result<serde_v8::Value<'scope>, AnyError>
@@ -1690,7 +1806,7 @@ fn op_ffi_unsafe_callback_ref(state: &mut deno_core::OpState, inc_dec: bool) {
fn op_ffi_call_ptr_nonblocking<'scope, FP>(
scope: &mut v8::HandleScope<'scope>,
state: Rc<RefCell<deno_core::OpState>>,
- pointer: u64,
+ pointer: usize,
def: ForeignFunction,
parameters: serde_v8::Value<'scope>,
) -> Result<impl Future<Output = Result<Value, AnyError>>, AnyError>
@@ -1781,30 +1897,46 @@ fn op_ffi_get_static<'scope>(
NativeType::U64 => {
// SAFETY: ptr is user provided
let result = unsafe { ptr::read_unaligned(data_ptr as *const u64) };
- let big_int: v8::Local<v8::Value> =
- v8::BigInt::new_from_u64(scope, result).into();
- big_int.into()
+ let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as u64 {
+ v8::BigInt::new_from_u64(scope, result).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ };
+ integer.into()
}
NativeType::I64 => {
// SAFETY: ptr is user provided
let result = unsafe { ptr::read_unaligned(data_ptr as *const i64) };
- let big_int: v8::Local<v8::Value> =
- v8::BigInt::new_from_i64(scope, result).into();
- big_int.into()
+ let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as i64
+ || result < MIN_SAFE_INTEGER as i64
+ {
+ v8::BigInt::new_from_i64(scope, result).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ };
+ integer.into()
}
NativeType::USize => {
// SAFETY: ptr is user provided
let result = unsafe { ptr::read_unaligned(data_ptr as *const usize) };
- let big_int: v8::Local<v8::Value> =
- v8::BigInt::new_from_u64(scope, result as u64).into();
- big_int.into()
+ let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as usize
+ {
+ v8::BigInt::new_from_u64(scope, result as u64).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ };
+ integer.into()
}
NativeType::ISize => {
// SAFETY: ptr is user provided
let result = unsafe { ptr::read_unaligned(data_ptr as *const isize) };
- let big_int: v8::Local<v8::Value> =
- v8::BigInt::new_from_i64(scope, result as i64).into();
- big_int.into()
+ let integer: v8::Local<v8::Value> =
+ if result > MAX_SAFE_INTEGER || result < MIN_SAFE_INTEGER {
+ v8::BigInt::new_from_i64(scope, result as i64).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ };
+ integer.into()
}
NativeType::F32 => {
// SAFETY: ptr is user provided
@@ -1821,9 +1953,12 @@ fn op_ffi_get_static<'scope>(
}
NativeType::Pointer | NativeType::Function => {
let result = data_ptr as u64;
- let big_int: v8::Local<v8::Value> =
- v8::BigInt::new_from_u64(scope, result).into();
- big_int.into()
+ let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as u64 {
+ v8::BigInt::new_from_u64(scope, result).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ };
+ integer.into()
}
})
}
@@ -1908,9 +2043,13 @@ where
));
};
- let big_int: v8::Local<v8::Value> =
- v8::BigInt::new_from_u64(scope, pointer as u64).into();
- Ok(big_int.into())
+ let integer: v8::Local<v8::Value> =
+ if pointer as usize > MAX_SAFE_INTEGER as usize {
+ v8::BigInt::new_from_u64(scope, pointer as u64).into()
+ } else {
+ v8::Number::new(scope, pointer as usize as f64).into()
+ };
+ Ok(integer.into())
}
unsafe extern "C" fn noop_deleter_callback(
@@ -1935,9 +2074,14 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let value = v8::Local::<v8::BigInt>::try_from(src.v8_value)
- .map_err(|_| type_error("Invalid FFI pointer value, expected BigInt"))?;
- let ptr = value.u64_value().0 as usize as *mut c_void;
+ let ptr = if let Ok(value) = v8::Local::<v8::Number>::try_from(src.v8_value) {
+ value.value() as usize as *mut c_void
+ } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(src.v8_value) {
+ value.u64_value().0 as usize as *mut c_void
+ } else {
+ return Err(type_error("Invalid FFI pointer value, expected BigInt"));
+ };
+
if std::ptr::eq(ptr, std::ptr::null()) {
return Err(type_error("Invalid FFI pointer value, got nullptr"));
}
@@ -1960,7 +2104,7 @@ where
#[op]
fn op_ffi_buf_copy_into<FP>(
state: &mut deno_core::OpState,
- src: u64,
+ src: usize,
mut dst: ZeroCopyBuf,
len: usize,
) -> Result<(), AnyError>
@@ -1989,7 +2133,7 @@ where
fn op_ffi_cstr_read<FP, 'scope>(
scope: &mut v8::HandleScope<'scope>,
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<serde_v8::Value<'scope>, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2013,7 +2157,7 @@ where
#[op]
fn op_ffi_read_u8<FP>(
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<u8, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2030,7 +2174,7 @@ where
#[op]
fn op_ffi_read_i8<FP>(
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<i8, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2047,7 +2191,7 @@ where
#[op]
fn op_ffi_read_u16<FP>(
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<u16, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2064,7 +2208,7 @@ where
#[op]
fn op_ffi_read_i16<FP>(
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<i16, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2081,7 +2225,7 @@ where
#[op]
fn op_ffi_read_u32<FP>(
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<u32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2098,7 +2242,7 @@ where
#[op]
fn op_ffi_read_i32<FP>(
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<i32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2116,7 +2260,7 @@ where
fn op_ffi_read_u64<FP, 'scope>(
scope: &mut v8::HandleScope<'scope>,
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<serde_v8::Value<'scope>, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2130,15 +2274,47 @@ where
// SAFETY: ptr is user provided.
let result = unsafe { ptr::read_unaligned(ptr as *const u64) };
- let big_int: v8::Local<v8::Value> =
- v8::BigInt::new_from_u64(scope, result).into();
- Ok(big_int.into())
+ let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as u64 {
+ v8::BigInt::new_from_u64(scope, result).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ };
+
+ Ok(integer.into())
+}
+
+#[op(v8)]
+fn op_ffi_read_i64<FP, 'scope>(
+ scope: &mut v8::HandleScope<'scope>,
+ state: &mut deno_core::OpState,
+ ptr: usize,
+) -> Result<serde_v8::Value<'scope>, AnyError>
+where
+ FP: FfiPermissions + 'static,
+ 'scope: 'scope,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getBigUint64");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ // SAFETY: ptr is user provided.
+ let result = unsafe { ptr::read_unaligned(ptr as *const i64) };
+
+ let integer: v8::Local<v8::Value> =
+ if result > MAX_SAFE_INTEGER as i64 || result < MIN_SAFE_INTEGER as i64 {
+ v8::BigInt::new_from_i64(scope, result).into()
+ } else {
+ v8::Number::new(scope, result as f64).into()
+ };
+
+ Ok(integer.into())
}
#[op]
fn op_ffi_read_f32<FP>(
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<f32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -2155,7 +2331,7 @@ where
#[op]
fn op_ffi_read_f64<FP>(
state: &mut deno_core::OpState,
- ptr: u64,
+ ptr: usize,
) -> Result<f64, AnyError>
where
FP: FfiPermissions + 'static,
diff --git a/test_ffi/tests/bench.js b/test_ffi/tests/bench.js
index 165b395e2..8885c7d7e 100644
--- a/test_ffi/tests/bench.js
+++ b/test_ffi/tests/bench.js
@@ -305,6 +305,11 @@ Deno.bench("nop_buffer()", () => {
nop_buffer(buffer);
});
+const buffer_ptr = Deno.UnsafePointer.of(buffer);
+Deno.bench("nop_buffer() number", () => {
+ nop_buffer(buffer_ptr);
+});
+
const { return_u8 } = dylib.symbols;
Deno.bench("return_u8()", () => {
return_u8();
@@ -442,6 +447,10 @@ Deno.bench("nop_buffer_nonblocking()", async () => {
await nop_buffer_nonblocking(buffer);
});
+Deno.bench("nop_buffer_nonblocking() number", async () => {
+ await nop_buffer_nonblocking(buffer_ptr);
+});
+
const { return_u8_nonblocking } = dylib.symbols;
Deno.bench("return_u8_nonblocking()", async () => {
await return_u8_nonblocking();
@@ -540,6 +549,38 @@ Deno.bench("nop_many_parameters()", () => {
);
});
+const buffer2_ptr = Deno.UnsafePointer.of(buffer2);
+Deno.bench("nop_many_parameters() number", () => {
+ nop_many_parameters(
+ 135,
+ 47,
+ 356,
+ -236,
+ 7457,
+ -1356,
+ 16471468,
+ -1334748136,
+ 132658769535,
+ -42745856824,
+ 13567.26437,
+ 7.686234e-3,
+ buffer_ptr,
+ 64,
+ -42,
+ 83,
+ -136,
+ 3657,
+ -2376,
+ 3277918,
+ -474628146,
+ 344657895,
+ -2436732,
+ 135.26437e3,
+ 264.3576468623546834,
+ buffer2_ptr,
+ );
+});
+
const { nop_many_parameters_nonblocking } = dylib.symbols;
Deno.bench("nop_many_parameters_nonblocking()", () => {
nop_many_parameters_nonblocking(
diff --git a/test_ffi/tests/ffi_types.ts b/test_ffi/tests/ffi_types.ts
index 172c429e8..d4a90552f 100644
--- a/test_ffi/tests/ffi_types.ts
+++ b/test_ffi/tests/ffi_types.ts
@@ -131,7 +131,7 @@ remote.symbols.method14(null);
remote.symbols.method14(0);
// @ts-expect-error: Invalid argument
-remote.symbols.method15(0);
+remote.symbols.method15("foo");
remote.symbols.method15(new Uint16Array(1));
remote.symbols.method15(0n);
@@ -245,16 +245,16 @@ remote.symbols.method20(unsafe_callback_right1.pointer);
// @ts-expect-error: Invalid member type
const static1_wrong: null = remote.symbols.static1;
-const static1_right: bigint = remote.symbols.static1;
+const static1_right: Deno.PointerValue = 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: bigint = remote.symbols.static3;
+const static3_right: Deno.PointerValue = remote.symbols.static3;
// @ts-expect-error: Invalid member type
const static4_wrong: null = remote.symbols.static4;
-const static4_right: bigint = remote.symbols.static4;
+const static4_right: Deno.PointerValue = remote.symbols.static4;
// @ts-expect-error: Invalid member type
const static5_wrong: null = remote.symbols.static5;
const static5_right: number = remote.symbols.static5;
@@ -266,7 +266,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: bigint = remote.symbols.static8;
+const static8_right: Deno.PointerValue = remote.symbols.static8;
// @ts-expect-error: Invalid member type
const static9_wrong: null = remote.symbols.static9;
const static9_right: number = remote.symbols.static9;
@@ -278,7 +278,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: bigint = remote.symbols.static12;
+const static12_right: Deno.PointerValue = remote.symbols.static12;
// @ts-expect-error: Invalid member type
const static13_wrong: null = remote.symbols.static13;
const static13_right: number = remote.symbols.static13;
@@ -331,7 +331,10 @@ type __Tests__ = [
higher_order_params: AssertEqual<
{
symbols: {
- pushBuf: (ptr: bigint | TypedArray | null, func: bigint | null) => void;
+ pushBuf: (
+ ptr: number | bigint | TypedArray | null,
+ func: number | bigint | null,
+ ) => void;
};
close(): void;
},
@@ -343,9 +346,9 @@ type __Tests__ = [
{
symbols: {
pushBuf: (
- ptr: bigint | TypedArray | null,
- func: bigint | null,
- ) => bigint;
+ ptr: number | bigint | TypedArray | null,
+ func: number | bigint | null,
+ ) => number | bigint;
};
close(): void;
},
@@ -356,7 +359,9 @@ type __Tests__ = [
non_exact_params: AssertEqual<
{
symbols: {
- foo: (...args: (number | bigint | TypedArray | null)[]) => bigint;
+ foo: (
+ ...args: (number | bigint | TypedArray | null)[]
+ ) => number | bigint;
};
close(): void;
},
diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs
index 4a910a836..079293550 100644
--- a/test_ffi/tests/integration_tests.rs
+++ b/test_ffi/tests/integration_tests.rs
@@ -66,17 +66,29 @@ fn basic() {
5\n\
5\n\
579\n\
- 8589934590n\n\
- -8589934590n\n\
- 8589934590n\n\
- -8589934590n\n\
+ 8589934590\n\
+ -8589934590\n\
+ 8589934590\n\
+ -8589934590\n\
+ 9007199254740992n\n\
+ 9007199254740992n\n\
+ -9007199254740992n\n\
+ 9007199254740992n\n\
+ 9007199254740992n\n\
+ -9007199254740992n\n\
579.9119873046875\n\
579.912\n\
579\n\
- 8589934590n\n\
- -8589934590n\n\
- 8589934590n\n\
- -8589934590n\n\
+ 8589934590\n\
+ -8589934590\n\
+ 8589934590\n\
+ -8589934590\n\
+ 9007199254740992n\n\
+ 9007199254740992n\n\
+ -9007199254740992n\n\
+ 9007199254740992n\n\
+ 9007199254740992n\n\
+ -9007199254740992n\n\
579.9119873046875\n\
579.912\n\
After sleep_blocking\n\
@@ -86,7 +98,7 @@ fn basic() {
After\n\
true\n\
logCallback\n\
- 1 -1 2 -2 3 -3 4n -4n 0.5 -0.5 1 2 3 4 5 6 7 8\n\
+ 1 -1 2 -2 3 -3 4 -4 0.5 -0.5 1 2 3 4 5 6 7 8\n\
u8: 8\n\
buf: [1, 2, 3, 4, 5, 6, 7, 8]\n\
logCallback\n\
@@ -98,7 +110,7 @@ fn basic() {
Thread safe call counter: 1\n\
u8: 8\n\
Static u32: 42\n\
- Static i64: -1242464576485n\n\
+ Static i64: -1242464576485\n\
Static ptr: true\n\
Static ptr value: 42\n\
arrayBuffer.byteLength: 4\n\
@@ -116,7 +128,7 @@ fn symbol_types() {
build();
let output = deno_cmd()
- .arg("cache")
+ .arg("check")
.arg("--unstable")
.arg("--quiet")
.arg("tests/ffi_types.ts")
diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js
index 516182f6f..e27a09d4f 100644
--- a/test_ffi/tests/test.js
+++ b/test_ffi/tests/test.js
@@ -265,6 +265,12 @@ 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_u64(Number.MAX_SAFE_INTEGER, 1));
+console.log(dylib.symbols.add_i64(Number.MAX_SAFE_INTEGER, 1));
+console.log(dylib.symbols.add_i64(Number.MIN_SAFE_INTEGER, -1));
+console.log(dylib.symbols.add_usize(Number.MAX_SAFE_INTEGER, 1));
+console.log(dylib.symbols.add_isize(Number.MAX_SAFE_INTEGER, 1));
+console.log(dylib.symbols.add_isize(Number.MIN_SAFE_INTEGER, -1));
console.log(dylib.symbols.add_f32(123.123, 456.789));
console.log(dylib.symbols.add_f64(123.123, 456.789));
@@ -280,6 +286,12 @@ console.log(
console.log(
await dylib.symbols.add_isize_nonblocking(-0xffffffffn, -0xffffffffn),
);
+console.log(await dylib.symbols.add_u64_nonblocking(Number.MAX_SAFE_INTEGER, 1));
+console.log(await dylib.symbols.add_i64_nonblocking(Number.MAX_SAFE_INTEGER, 1));
+console.log(await dylib.symbols.add_i64_nonblocking(Number.MIN_SAFE_INTEGER, -1));
+console.log(await dylib.symbols.add_usize_nonblocking(Number.MAX_SAFE_INTEGER, 1));
+console.log(await dylib.symbols.add_isize_nonblocking(Number.MAX_SAFE_INTEGER, 1));
+console.log(await dylib.symbols.add_isize_nonblocking(Number.MIN_SAFE_INTEGER, -1));
console.log(await dylib.symbols.add_f32_nonblocking(123.123, 456.789));
console.log(await dylib.symbols.add_f64_nonblocking(123.123, 456.789));
@@ -439,7 +451,7 @@ console.log("Static u32:", dylib.symbols.static_u32);
console.log("Static i64:", dylib.symbols.static_i64);
console.log(
"Static ptr:",
- typeof dylib.symbols.static_ptr === "bigint",
+ typeof dylib.symbols.static_ptr === "number",
);
const view = new Deno.UnsafePointerView(dylib.symbols.static_ptr);
console.log("Static ptr value:", view.getUint32());