diff options
author | Aapo Alasuutari <aapo.alasuutari@gmail.com> | 2022-07-24 13:41:11 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-24 16:11:11 +0530 |
commit | f8fee6cd21cce82d6c34e539d39da86df7b036f7 (patch) | |
tree | 8099527951e4b532934a00c25797758aeb496ab6 /ext | |
parent | e1cbd2364f536a1cef817961967e1738b89be734 (diff) |
feat(ext/ffi): Safe number pointers (#15173)
Diffstat (limited to 'ext')
-rw-r--r-- | ext/ffi/00_ffi.js | 38 | ||||
-rw-r--r-- | ext/ffi/jit_trampoline.rs | 27 | ||||
-rw-r--r-- | ext/ffi/lib.rs | 948 |
3 files changed, 602 insertions, 411 deletions
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(), ¶ms); + let recv = v8::undefined(scope); + let call_result = func.call(scope, recv.into(), ¶ms); 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, |