diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/ffi/00_ffi.js | 34 | ||||
-rw-r--r-- | ext/ffi/call.rs | 140 | ||||
-rw-r--r-- | ext/ffi/callback.rs | 70 | ||||
-rw-r--r-- | ext/ffi/dlfcn.rs | 26 | ||||
-rw-r--r-- | ext/ffi/ir.rs | 68 | ||||
-rw-r--r-- | ext/ffi/lib.rs | 5 | ||||
-rw-r--r-- | ext/ffi/repr.rs | 249 | ||||
-rw-r--r-- | ext/ffi/static.rs | 11 | ||||
-rw-r--r-- | ext/ffi/turbocall.rs | 14 |
9 files changed, 339 insertions, 278 deletions
diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 6864fd638..24a0dc913 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -118,6 +118,13 @@ class UnsafePointerView { ); } + getPointer(offset = 0) { + return ops.op_ffi_read_ptr( + this.pointer, + offset, + ); + } + getCString(offset = 0) { return ops.op_ffi_cstr_read( this.pointer, @@ -170,11 +177,33 @@ class UnsafePointerView { const OUT_BUFFER = new Uint32Array(2); const OUT_BUFFER_64 = new BigInt64Array(OUT_BUFFER.buffer); class UnsafePointer { + static create(value) { + return ops.op_ffi_ptr_create(value); + } + + static equals(a, b) { + if (a === null || b === null) { + return a === b; + } + return ops.op_ffi_ptr_equals(a, b); + } + static of(value) { if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) { return value.pointer; } - ops.op_ffi_ptr_of(value, OUT_BUFFER); + return ops.op_ffi_ptr_of(value); + } + + static offset(value, offset) { + return ops.op_ffi_ptr_offset(value, offset); + } + + static value(value) { + if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) { + value = value.pointer; + } + ops.op_ffi_ptr_value(value, OUT_BUFFER); const result = OUT_BUFFER[0] + 2 ** 32 * OUT_BUFFER[1]; if (NumberIsSafeInteger(result)) { return result; @@ -240,8 +269,7 @@ class UnsafeFnPointer { } function isReturnedAsBigInt(type) { - return type === "buffer" || type === "pointer" || type === "function" || - type === "u64" || type === "i64" || + return type === "u64" || type === "i64" || type === "usize" || type === "isize"; } diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index 731460af9..8a9b393c3 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -14,9 +14,11 @@ use deno_core::error::AnyError; use deno_core::op; use deno_core::serde_json::Value; use deno_core::serde_v8; +use deno_core::serde_v8::ExternalPointer; use deno_core::v8; use deno_core::ResourceId; use libffi::middle::Arg; +use serde::Serialize; use std::cell::RefCell; use std::ffi::c_void; use std::future::Future; @@ -28,14 +30,13 @@ unsafe fn ffi_call_rtype_struct( fn_ptr: &libffi::middle::CodePtr, call_args: Vec<Arg>, out_buffer: *mut u8, -) -> NativeValue { +) { libffi::raw::ffi_call( cif.as_raw_ptr(), Some(*fn_ptr.as_safe_fun()), out_buffer as *mut c_void, call_args.as_ptr() as *mut *mut c_void, ); - NativeValue { void_value: () } } // A one-off synchronous FFI call. @@ -174,16 +175,25 @@ where pointer: cif.call::<*mut c_void>(*fun_ptr, &call_args), } } - NativeType::Struct(_) => ffi_call_rtype_struct( - &symbol.cif, - &symbol.ptr, - call_args, - out_buffer.unwrap().0, - ), + NativeType::Struct(_) => NativeValue { + void_value: ffi_call_rtype_struct( + &symbol.cif, + &symbol.ptr, + call_args, + out_buffer.unwrap().0, + ), + }, }) } } +#[derive(Serialize)] +#[serde(untagged)] +pub enum FfiValue { + Value(Value), + External(ExternalPointer), +} + fn ffi_call( call_args: Vec<NativeValue>, cif: &libffi::middle::Cif, @@ -191,7 +201,7 @@ fn ffi_call( parameter_types: &[NativeType], result_type: NativeType, out_buffer: Option<OutBuffer>, -) -> Result<NativeValue, AnyError> { +) -> Result<FfiValue, AnyError> { let call_args: Vec<Arg> = call_args .iter() .enumerate() @@ -205,55 +215,57 @@ fn ffi_call( // types of symbol. unsafe { Ok(match result_type { - NativeType::Void => NativeValue { - void_value: cif.call::<()>(fun_ptr, &call_args), - }, - NativeType::Bool => NativeValue { - bool_value: cif.call::<bool>(fun_ptr, &call_args), - }, - NativeType::U8 => NativeValue { - u8_value: cif.call::<u8>(fun_ptr, &call_args), - }, - NativeType::I8 => NativeValue { - i8_value: cif.call::<i8>(fun_ptr, &call_args), - }, - NativeType::U16 => NativeValue { - u16_value: cif.call::<u16>(fun_ptr, &call_args), - }, - NativeType::I16 => NativeValue { - i16_value: cif.call::<i16>(fun_ptr, &call_args), - }, - NativeType::U32 => NativeValue { - u32_value: cif.call::<u32>(fun_ptr, &call_args), - }, - NativeType::I32 => NativeValue { - i32_value: cif.call::<i32>(fun_ptr, &call_args), - }, - NativeType::U64 => NativeValue { - u64_value: cif.call::<u64>(fun_ptr, &call_args), - }, - NativeType::I64 => NativeValue { - i64_value: cif.call::<i64>(fun_ptr, &call_args), - }, - NativeType::USize => NativeValue { - usize_value: cif.call::<usize>(fun_ptr, &call_args), - }, - NativeType::ISize => NativeValue { - isize_value: cif.call::<isize>(fun_ptr, &call_args), - }, - NativeType::F32 => NativeValue { - f32_value: cif.call::<f32>(fun_ptr, &call_args), - }, - NativeType::F64 => NativeValue { - f64_value: cif.call::<f64>(fun_ptr, &call_args), - }, + NativeType::Void => { + cif.call::<()>(fun_ptr, &call_args); + FfiValue::Value(Value::from(())) + } + NativeType::Bool => { + FfiValue::Value(Value::from(cif.call::<bool>(fun_ptr, &call_args))) + } + NativeType::U8 => { + FfiValue::Value(Value::from(cif.call::<u8>(fun_ptr, &call_args))) + } + NativeType::I8 => { + FfiValue::Value(Value::from(cif.call::<i8>(fun_ptr, &call_args))) + } + NativeType::U16 => { + FfiValue::Value(Value::from(cif.call::<u16>(fun_ptr, &call_args))) + } + NativeType::I16 => { + FfiValue::Value(Value::from(cif.call::<i16>(fun_ptr, &call_args))) + } + NativeType::U32 => { + FfiValue::Value(Value::from(cif.call::<u32>(fun_ptr, &call_args))) + } + NativeType::I32 => { + FfiValue::Value(Value::from(cif.call::<i32>(fun_ptr, &call_args))) + } + NativeType::U64 => { + FfiValue::Value(Value::from(cif.call::<u64>(fun_ptr, &call_args))) + } + NativeType::I64 => { + FfiValue::Value(Value::from(cif.call::<i64>(fun_ptr, &call_args))) + } + NativeType::USize => { + FfiValue::Value(Value::from(cif.call::<usize>(fun_ptr, &call_args))) + } + NativeType::ISize => { + FfiValue::Value(Value::from(cif.call::<isize>(fun_ptr, &call_args))) + } + NativeType::F32 => { + FfiValue::Value(Value::from(cif.call::<f32>(fun_ptr, &call_args))) + } + NativeType::F64 => { + FfiValue::Value(Value::from(cif.call::<f64>(fun_ptr, &call_args))) + } NativeType::Pointer | NativeType::Function | NativeType::Buffer => { - NativeValue { - pointer: cif.call::<*mut c_void>(fun_ptr, &call_args), - } + FfiValue::External(ExternalPointer::from( + cif.call::<*mut c_void>(fun_ptr, &call_args), + )) } NativeType::Struct(_) => { - ffi_call_rtype_struct(cif, &fun_ptr, call_args, out_buffer.unwrap().0) + ffi_call_rtype_struct(cif, &fun_ptr, call_args, out_buffer.unwrap().0); + FfiValue::Value(Value::Null) } }) } @@ -263,11 +275,11 @@ fn ffi_call( pub fn op_ffi_call_ptr_nonblocking<'scope, FP>( scope: &mut v8::HandleScope<'scope>, state: Rc<RefCell<deno_core::OpState>>, - pointer: usize, + pointer: *mut c_void, def: ForeignFunction, parameters: serde_v8::Value<'scope>, out_buffer: Option<serde_v8::Value<'scope>>, -) -> Result<impl Future<Output = Result<Value, AnyError>>, AnyError> +) -> Result<impl Future<Output = Result<FfiValue, AnyError>>, AnyError> where FP: FfiPermissions + 'static, { @@ -280,7 +292,6 @@ where let symbol = PtrSymbol::new(pointer, &def)?; let call_args = ffi_parse_args(scope, parameters, &def.parameters)?; - let def_result = def.result.clone(); let out_buffer = out_buffer .map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap()); @@ -303,7 +314,7 @@ where .await .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. - Ok(unsafe { result.to_value(def_result) }) + Ok(result) }) } @@ -316,7 +327,8 @@ pub fn op_ffi_call_nonblocking<'scope>( symbol: String, parameters: serde_v8::Value<'scope>, out_buffer: Option<serde_v8::Value<'scope>>, -) -> Result<impl Future<Output = Result<Value, AnyError>> + 'static, AnyError> { +) -> Result<impl Future<Output = Result<FfiValue, AnyError>> + 'static, AnyError> +{ let symbol = { let state = state.borrow(); let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?; @@ -332,7 +344,6 @@ pub fn op_ffi_call_nonblocking<'scope>( .map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap()); let out_buffer_ptr = out_buffer_as_ptr(scope, out_buffer); - let result_type = symbol.result_type.clone(); let join_handle = tokio::task::spawn_blocking(move || { let Symbol { cif, @@ -356,7 +367,7 @@ pub fn op_ffi_call_nonblocking<'scope>( .await .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. - Ok(unsafe { result.to_value(result_type) }) + Ok(result) }) } @@ -364,11 +375,11 @@ pub fn op_ffi_call_nonblocking<'scope>( pub fn op_ffi_call_ptr<FP, 'scope>( scope: &mut v8::HandleScope<'scope>, state: Rc<RefCell<deno_core::OpState>>, - pointer: usize, + pointer: *mut c_void, def: ForeignFunction, parameters: serde_v8::Value<'scope>, out_buffer: Option<serde_v8::Value<'scope>>, -) -> Result<serde_v8::Value<'scope>, AnyError> +) -> Result<FfiValue, AnyError> where FP: FfiPermissions + 'static, { @@ -395,6 +406,5 @@ where out_buffer_ptr, )?; // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. - let result = unsafe { result.to_v8(scope, def.result) }; Ok(result) } diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index 1558d950e..d608c5432 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -40,7 +40,10 @@ pub struct PtrSymbol { } impl PtrSymbol { - pub fn new(fn_ptr: usize, def: &ForeignFunction) -> Result<Self, AnyError> { + pub fn new( + fn_ptr: *mut c_void, + def: &ForeignFunction, + ) -> Result<Self, AnyError> { let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); let cif = libffi::middle::Cif::new( def @@ -236,11 +239,11 @@ unsafe fn do_ffi_callback( } } NativeType::Pointer | NativeType::Buffer | NativeType::Function => { - let result = *((*val) as *const usize); - if result > MAX_SAFE_INTEGER as usize { - v8::BigInt::new_from_u64(scope, result as u64).into() + let result = *((*val) as *const *mut c_void); + if result.is_null() { + v8::null(scope).into() } else { - v8::Number::new(scope, result as f64).into() + v8::External::new(scope, result).into() } } NativeType::Struct(_) => { @@ -353,34 +356,43 @@ unsafe fn do_ffi_callback( }; *(result as *mut f64) = value; } - NativeType::Pointer | NativeType::Buffer | NativeType::Function => { - let pointer = if let Ok(value) = + NativeType::Buffer => { + let pointer: *mut u8 = if let Ok(value) = v8::Local::<v8::ArrayBufferView>::try_from(value) { let byte_offset = value.byte_offset(); - let backing_store = value + let pointer = value .buffer(scope) .expect("Unable to deserialize result parameter.") - .get_backing_store(); - &backing_store[byte_offset..] as *const _ as *const u8 - } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) { - value.u64_value().0 as usize as *const u8 + .data(); + if let Some(non_null) = pointer { + // SAFETY: Pointer is non-null, and V8 guarantees that the byte_offset + // is within the buffer backing store. + unsafe { non_null.as_ptr().add(byte_offset) as *mut u8 } + } else { + ptr::null_mut() + } } else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value) { - let backing_store = value.get_backing_store(); - &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() { - ptr::null() + let pointer = value.data(); + if let Some(non_null) = pointer { + non_null.as_ptr() as *mut u8 + } else { + ptr::null_mut() + } } else { - // Fallthrough: Probably someone returned a number but this could - // also be eg. a string. This is essentially UB. - value - .integer_value(scope) - .expect("Unable to deserialize result parameter.") as usize - as *const u8 + ptr::null_mut() }; - *(result as *mut *const u8) = pointer; + *(result as *mut *mut u8) = pointer; + } + NativeType::Pointer | NativeType::Function => { + let pointer: *mut c_void = + if let Ok(external) = v8::Local::<v8::External>::try_from(value) { + external.value() + } else { + // TODO(@aapoalas): Start throwing errors into JS about invalid callback return values. + ptr::null_mut() + }; + *(result as *mut *mut c_void) = pointer; } NativeType::I8 => { let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { @@ -591,7 +603,7 @@ where let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, unsafe { info.as_ref().unwrap() }); - let ptr = *closure.code_ptr() as usize; + let ptr = *closure.code_ptr() as *mut c_void; let resource = UnsafeCallbackResource { cancel: CancelHandle::new_rc(), closure, @@ -600,11 +612,7 @@ where let rid = state.resource_table.add(resource); let rid_local = v8::Integer::new_from_unsigned(scope, rid); - 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 ptr_local: v8::Local<v8::Value> = v8::External::new(scope, ptr).into(); let array = v8::Array::new(scope, 2); array.set_index(scope, 0, rid_local.into()); array.set_index(scope, 1, ptr_local); diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs index c2b123246..cb5009de7 100644 --- a/ext/ffi/dlfcn.rs +++ b/ext/ffi/dlfcn.rs @@ -38,13 +38,13 @@ impl Resource for DynamicLibraryResource { } impl DynamicLibraryResource { - pub fn get_static(&self, symbol: String) -> Result<*const c_void, AnyError> { + pub fn get_static(&self, symbol: String) -> Result<*mut c_void, AnyError> { // By default, Err returned by this function does not tell // which symbol wasn't exported. So we'll modify the error // message to include the name of symbol. // // SAFETY: The obtained T symbol is the size of a pointer. - match unsafe { self.lib.symbol::<*const c_void>(&symbol) } { + match unsafe { self.lib.symbol::<*mut c_void>(&symbol) } { Ok(value) => Ok(Ok(value)), Err(err) => Err(generic_error(format!( "Failed to register symbol {symbol}: {err}" @@ -56,13 +56,7 @@ impl DynamicLibraryResource { pub fn needs_unwrap(rv: &NativeType) -> bool { matches!( rv, - NativeType::Function - | NativeType::Pointer - | NativeType::Buffer - | NativeType::I64 - | NativeType::ISize - | NativeType::U64 - | NativeType::USize + NativeType::I64 | NativeType::ISize | NativeType::U64 | NativeType::USize ) } @@ -257,26 +251,20 @@ fn make_sync_fn<'s>( match needs_unwrap { Some(v) => { let view: v8::Local<v8::ArrayBufferView> = v.try_into().unwrap(); - let backing_store = - view.buffer(scope).unwrap().get_backing_store(); + let pointer = + view.buffer(scope).unwrap().data().unwrap().as_ptr() as *mut u8; if is_i64(&symbol.result_type) { // SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>, // it points to a fixed continuous slice of bytes on the heap. - let bs = unsafe { - &mut *(&backing_store[..] as *const _ as *mut [u8] - as *mut i64) - }; + let bs = unsafe { &mut *(pointer as *mut i64) }; // SAFETY: We already checked that type == I64 let value = unsafe { result.i64_value }; *bs = value; } else { // SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>, // it points to a fixed continuous slice of bytes on the heap. - let bs = unsafe { - &mut *(&backing_store[..] as *const _ as *mut [u8] - as *mut u64) - }; + let bs = unsafe { &mut *(pointer as *mut u64) }; // SAFETY: We checked that type == U64 let value = unsafe { result.u64_value }; *bs = value; diff --git a/ext/ffi/ir.rs b/ext/ffi/ir.rs index 80f727cd2..8ca96a280 100644 --- a/ext/ffi/ir.rs +++ b/ext/ffi/ir.rs @@ -5,7 +5,6 @@ use crate::MAX_SAFE_INTEGER; use crate::MIN_SAFE_INTEGER; use deno_core::error::type_error; use deno_core::error::AnyError; -use deno_core::serde_json::Value; use deno_core::serde_v8; use deno_core::v8; use libffi::middle::Arg; @@ -81,33 +80,6 @@ impl NativeValue { } // SAFETY: native_type must correspond to the type of value represented by the union field - pub unsafe fn to_value(&self, native_type: NativeType) -> Value { - match native_type { - NativeType::Void => Value::Null, - NativeType::Bool => Value::from(self.bool_value), - NativeType::U8 => Value::from(self.u8_value), - NativeType::I8 => Value::from(self.i8_value), - NativeType::U16 => Value::from(self.u16_value), - NativeType::I16 => Value::from(self.i16_value), - NativeType::U32 => Value::from(self.u32_value), - NativeType::I32 => Value::from(self.i32_value), - NativeType::U64 => Value::from(self.u64_value), - NativeType::I64 => Value::from(self.i64_value), - NativeType::USize => Value::from(self.usize_value), - NativeType::ISize => Value::from(self.isize_value), - NativeType::F32 => Value::from(self.f32_value), - NativeType::F64 => Value::from(self.f64_value), - NativeType::Pointer | NativeType::Function | NativeType::Buffer => { - Value::from(self.pointer as usize) - } - NativeType::Struct(_) => { - // Return value is written to out_buffer - Value::Null - } - } - } - - // SAFETY: native_type must correspond to the type of value represented by the union field #[inline] pub unsafe fn to_v8<'scope>( &self, @@ -206,13 +178,11 @@ impl NativeValue { local_value.into() } NativeType::Pointer | NativeType::Buffer | NativeType::Function => { - let value = self.pointer as u64; - let local_value: v8::Local<v8::Value> = - if value > MAX_SAFE_INTEGER as u64 { - v8::BigInt::new_from_u64(scope, value).into() - } else { - v8::Number::new(scope, value as f64).into() - }; + let local_value: v8::Local<v8::Value> = if self.pointer.is_null() { + v8::null(scope).into() + } else { + v8::External::new(scope, self.pointer).into() + }; local_value.into() } NativeType::Struct(_) => { @@ -396,22 +366,16 @@ pub fn ffi_parse_f64_arg( #[inline] pub fn ffi_parse_pointer_arg( - scope: &mut v8::HandleScope, + _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. - // 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 *mut c_void - } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { - value.integer_value(scope).unwrap() as usize as *mut c_void + let pointer = if let Ok(value) = v8::Local::<v8::External>::try_from(arg) { + value.value() } else if arg.is_null() { ptr::null_mut() } else { return Err(type_error( - "Invalid FFI pointer type, expected null, integer or BigInt", + "Invalid FFI pointer type, expected null, or External", )); }; Ok(NativeValue { pointer }) @@ -502,22 +466,16 @@ pub fn ffi_parse_struct_arg( #[inline] pub fn ffi_parse_function_arg( - scope: &mut v8::HandleScope, + _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 *mut c_void - } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { - value.integer_value(scope).unwrap() as usize as *mut c_void + let pointer = if let Ok(value) = v8::Local::<v8::External>::try_from(arg) { + value.value() } else if arg.is_null() { ptr::null_mut() } else { return Err(type_error( - "Invalid FFI function type, expected null, integer, or BigInt", + "Invalid FFI function type, expected null, or External", )); }; Ok(NativeValue { pointer }) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index a14a28a83..d49b66274 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -91,7 +91,11 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension { op_ffi_call_nonblocking::decl(), op_ffi_call_ptr::decl::<P>(), op_ffi_call_ptr_nonblocking::decl::<P>(), + op_ffi_ptr_create::decl::<P>(), + op_ffi_ptr_equals::decl::<P>(), op_ffi_ptr_of::decl::<P>(), + op_ffi_ptr_offset::decl::<P>(), + op_ffi_ptr_value::decl::<P>(), op_ffi_get_buf::decl::<P>(), op_ffi_buf_copy_into::decl::<P>(), op_ffi_cstr_read::decl::<P>(), @@ -106,6 +110,7 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension { op_ffi_read_i64::decl::<P>(), op_ffi_read_f32::decl::<P>(), op_ffi_read_f64::decl::<P>(), + op_ffi_read_ptr::decl::<P>(), op_ffi_unsafe_callback_create::decl::<P>(), op_ffi_unsafe_callback_ref::decl(), op_ffi_unsafe_callback_unref::decl(), diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs index c21b2b0f1..4372b0f1b 100644 --- a/ext/ffi/repr.rs +++ b/ext/ffi/repr.rs @@ -14,15 +14,89 @@ use std::ffi::CStr; use std::ptr; #[op(fast)] +fn op_ffi_ptr_create<FP>( + state: &mut deno_core::OpState, + ptr_number: usize, +) -> Result<*mut c_void, AnyError> +where + FP: FfiPermissions + 'static, +{ + check_unstable(state, "Deno.UnsafePointer#create"); + let permissions = state.borrow_mut::<FP>(); + permissions.check(None)?; + + Ok(ptr_number as *mut c_void) +} + +#[op(fast)] +pub fn op_ffi_ptr_equals<FP>( + state: &mut deno_core::OpState, + a: *const c_void, + b: *const c_void, +) -> Result<bool, AnyError> +where + FP: FfiPermissions + 'static, +{ + check_unstable(state, "Deno.UnsafePointer#equals"); + let permissions = state.borrow_mut::<FP>(); + permissions.check(None)?; + + Ok(a == b) +} + +#[op(fast)] pub fn op_ffi_ptr_of<FP>( state: &mut deno_core::OpState, buf: *const u8, +) -> Result<*mut c_void, AnyError> +where + FP: FfiPermissions + 'static, +{ + check_unstable(state, "Deno.UnsafePointer#of"); + let permissions = state.borrow_mut::<FP>(); + permissions.check(None)?; + + Ok(buf as *mut c_void) +} + +#[op(fast)] +fn op_ffi_ptr_offset<FP>( + state: &mut deno_core::OpState, + ptr: *mut c_void, + offset: isize, +) -> Result<*mut c_void, AnyError> +where + FP: FfiPermissions + 'static, +{ + check_unstable(state, "Deno.UnsafePointer#offset"); + let permissions = state.borrow_mut::<FP>(); + permissions.check(None)?; + + if ptr.is_null() { + return Err(type_error("Invalid pointer to offset, pointer is null")); + } + + // SAFETY: Pointer and offset are user provided. + Ok(unsafe { ptr.offset(offset) }) +} + +unsafe extern "C" fn noop_deleter_callback( + _data: *mut c_void, + _byte_length: usize, + _deleter_data: *mut c_void, +) { +} + +#[op(fast)] +fn op_ffi_ptr_value<FP>( + state: &mut deno_core::OpState, + ptr: *mut c_void, out: &mut [u32], ) -> Result<(), AnyError> where FP: FfiPermissions + 'static, { - check_unstable(state, "Deno.UnsafePointer#of"); + check_unstable(state, "Deno.UnsafePointer#value"); let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; @@ -35,47 +109,35 @@ where // SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned. let out = unsafe { &mut *outptr }; - *out = buf as usize; + *out = ptr as usize; Ok(()) } -unsafe extern "C" fn noop_deleter_callback( - _data: *mut c_void, - _byte_length: usize, - _deleter_data: *mut c_void, -) { -} - #[op(v8)] pub fn op_ffi_get_buf<FP, 'scope>( scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, len: usize, ) -> Result<serde_v8::Value<'scope>, AnyError> where FP: FfiPermissions + 'static, { - check_unstable(state, "Deno.UnsafePointerView#arrayBuffer"); + check_unstable(state, "Deno.UnsafePointerView#getArrayBuffer"); let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *mut c_void; - if ptr.is_null() { - return Err(type_error("Invalid FFI pointer value, got nullptr")); + return Err(type_error("Invalid ArrayBuffer pointer, pointer is null")); } - // SAFETY: Offset is user defined. - let ptr = unsafe { ptr.add(offset) }; - - // SAFETY: Trust the user to have provided a real pointer, and a valid matching size to it. Since this is a foreign pointer, we should not do any deletion. + // SAFETY: Trust the user to have provided a real pointer, offset, and a valid matching size to it. Since this is a foreign pointer, we should not do any deletion. let backing_store = unsafe { v8::ArrayBuffer::new_backing_store_from_ptr( - ptr, + ptr.offset(offset), len, noop_deleter_callback, std::ptr::null_mut(), @@ -90,8 +152,8 @@ where #[op(fast)] pub fn op_ffi_buf_copy_into<FP>( state: &mut deno_core::OpState, - src: usize, - offset: usize, + src: *mut c_void, + offset: isize, dst: &mut [u8], len: usize, ) -> Result<(), AnyError> @@ -103,19 +165,20 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - if dst.len() < len { + if src.is_null() { + Err(type_error("Invalid ArrayBuffer pointer, pointer is null")) + } else if dst.len() < len { Err(range_error( "Destination length is smaller than source length", )) } else { let src = src as *const c_void; - // SAFETY: Offset is user defined. - let src = unsafe { src.add(offset) as *const u8 }; - - // SAFETY: src is user defined. + // SAFETY: src and offset are user defined. // dest is properly aligned and is valid for writes of len * size_of::<T>() bytes. - unsafe { ptr::copy::<u8>(src, dst.as_mut_ptr(), len) }; + unsafe { + ptr::copy::<u8>(src.offset(offset) as *const u8, dst.as_mut_ptr(), len) + }; Ok(()) } } @@ -124,8 +187,8 @@ where pub fn op_ffi_cstr_read<FP, 'scope>( scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<serde_v8::Value<'scope>, AnyError> where FP: FfiPermissions + 'static, @@ -135,17 +198,13 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid CString pointer, pointer is null")); } - // SAFETY: Offset is user defined. - let ptr = unsafe { ptr.add(offset) }; - - // SAFETY: Pointer is user provided. - let cstr = unsafe { CStr::from_ptr(ptr as *const c_char) }.to_bytes(); + let cstr = + // SAFETY: Pointer and offset are user provided. + unsafe { CStr::from_ptr(ptr.offset(offset) as *const c_char) }.to_bytes(); let value: v8::Local<v8::Value> = v8::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal) .ok_or_else(|| { @@ -158,8 +217,8 @@ where #[op(fast)] pub fn op_ffi_read_bool<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<bool, AnyError> where FP: FfiPermissions + 'static, @@ -169,21 +228,19 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid bool pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::<bool>(ptr.add(offset) as *const bool) }) + Ok(unsafe { ptr::read_unaligned::<bool>(ptr.offset(offset) as *const bool) }) } #[op(fast)] pub fn op_ffi_read_u8<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<u32, AnyError> where FP: FfiPermissions + 'static, @@ -193,21 +250,21 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid u8 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::<u8>(ptr.add(offset) as *const u8) as u32 }) + Ok(unsafe { + ptr::read_unaligned::<u8>(ptr.offset(offset) as *const u8) as u32 + }) } #[op(fast)] pub fn op_ffi_read_i8<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<i32, AnyError> where FP: FfiPermissions + 'static, @@ -217,21 +274,21 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid i8 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::<i8>(ptr.add(offset) as *const i8) as i32 }) + Ok(unsafe { + ptr::read_unaligned::<i8>(ptr.offset(offset) as *const i8) as i32 + }) } #[op(fast)] pub fn op_ffi_read_u16<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<u32, AnyError> where FP: FfiPermissions + 'static, @@ -241,23 +298,21 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid u16 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { - ptr::read_unaligned::<u16>(ptr.add(offset) as *const u16) as u32 + ptr::read_unaligned::<u16>(ptr.offset(offset) as *const u16) as u32 }) } #[op(fast)] pub fn op_ffi_read_i16<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<i32, AnyError> where FP: FfiPermissions + 'static, @@ -267,23 +322,21 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid i16 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. Ok(unsafe { - ptr::read_unaligned::<i16>(ptr.add(offset) as *const i16) as i32 + ptr::read_unaligned::<i16>(ptr.offset(offset) as *const i16) as i32 }) } #[op(fast)] pub fn op_ffi_read_u32<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<u32, AnyError> where FP: FfiPermissions + 'static, @@ -293,21 +346,19 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid u32 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::<u32>(ptr.add(offset) as *const u32) }) + Ok(unsafe { ptr::read_unaligned::<u32>(ptr.offset(offset) as *const u32) }) } #[op(fast)] pub fn op_ffi_read_i32<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<i32, AnyError> where FP: FfiPermissions + 'static, @@ -317,21 +368,19 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid i32 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::<i32>(ptr.add(offset) as *const i32) }) + Ok(unsafe { ptr::read_unaligned::<i32>(ptr.offset(offset) as *const i32) }) } #[op] pub fn op_ffi_read_u64<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, out: &mut [u32], ) -> Result<(), AnyError> where @@ -349,15 +398,13 @@ where ); assert_eq!((outptr as usize % std::mem::size_of::<u64>()), 0); - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid u64 pointer, pointer is null")); } let value = // SAFETY: ptr and offset are user provided. - unsafe { ptr::read_unaligned::<u64>(ptr.add(offset) as *const u64) }; + unsafe { ptr::read_unaligned::<u64>(ptr.offset(offset) as *const u64) }; // SAFETY: Length and alignment of out slice were asserted to be correct. unsafe { *outptr = value }; @@ -367,8 +414,8 @@ where #[op(fast)] pub fn op_ffi_read_i64<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, out: &mut [u32], ) -> Result<(), AnyError> where @@ -386,15 +433,13 @@ where ); assert_eq!((outptr as usize % std::mem::size_of::<i64>()), 0); - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid i64 pointer, pointer is null")); } let value = // SAFETY: ptr and offset are user provided. - unsafe { ptr::read_unaligned::<i64>(ptr.add(offset) as *const i64) }; + unsafe { ptr::read_unaligned::<i64>(ptr.offset(offset) as *const i64) }; // SAFETY: Length and alignment of out slice were asserted to be correct. unsafe { *outptr = value }; Ok(()) @@ -403,8 +448,8 @@ where #[op(fast)] pub fn op_ffi_read_f32<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<f32, AnyError> where FP: FfiPermissions + 'static, @@ -414,21 +459,19 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid f32 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::<f32>(ptr.add(offset) as *const f32) }) + Ok(unsafe { ptr::read_unaligned::<f32>(ptr.offset(offset) as *const f32) }) } #[op(fast)] pub fn op_ffi_read_f64<FP>( state: &mut deno_core::OpState, - ptr: usize, - offset: usize, + ptr: *mut c_void, + offset: isize, ) -> Result<f64, AnyError> where FP: FfiPermissions + 'static, @@ -438,12 +481,34 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = ptr as *const c_void; - if ptr.is_null() { return Err(type_error("Invalid f64 pointer, pointer is null")); } // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::<f64>(ptr.add(offset) as *const f64) }) + Ok(unsafe { ptr::read_unaligned::<f64>(ptr.offset(offset) as *const f64) }) +} + +#[op(fast)] +pub fn op_ffi_read_ptr<FP>( + state: &mut deno_core::OpState, + ptr: *mut c_void, + offset: isize, +) -> Result<*mut c_void, AnyError> +where + FP: FfiPermissions + 'static, +{ + check_unstable(state, "Deno.UnsafePointerView#getPointer"); + + let permissions = state.borrow_mut::<FP>(); + permissions.check(None)?; + + if ptr.is_null() { + return Err(type_error("Invalid pointer pointer, pointer is null")); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::<*mut c_void>(ptr.offset(offset) as *const *mut c_void) + }) } diff --git a/ext/ffi/static.rs b/ext/ffi/static.rs index 9ea0d616d..2f32f03fd 100644 --- a/ext/ffi/static.rs +++ b/ext/ffi/static.rs @@ -10,6 +10,7 @@ use deno_core::op; use deno_core::serde_v8; use deno_core::v8; use deno_core::ResourceId; +use std::ffi::c_void; use std::ptr; #[op(v8)] @@ -134,13 +135,9 @@ pub fn op_ffi_get_static<'scope>( number.into() } NativeType::Pointer | NativeType::Function | NativeType::Buffer => { - let result = data_ptr as u64; - 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() + let external: v8::Local<v8::Value> = + v8::External::new(scope, data_ptr as *mut c_void).into(); + external.into() } NativeType::Struct(_) => { return Err(type_error("Invalid FFI static type 'struct'")); diff --git a/ext/ffi/turbocall.rs b/ext/ffi/turbocall.rs index 1eb1655e1..3d01f00f5 100644 --- a/ext/ffi/turbocall.rs +++ b/ext/ffi/turbocall.rs @@ -48,6 +48,9 @@ pub(crate) fn make_template(sym: &Symbol, trampoline: &Trampoline) -> Template { let ret = if needs_unwrap(&sym.result_type) { params.push(fast_api::Type::TypedArray(fast_api::CType::Int32)); fast_api::Type::Void + } else if sym.result_type == NativeType::Buffer { + // Buffer can be used as a return type and converts differently than in parameters. + fast_api::Type::Pointer } else { fast_api::Type::from(&sym.result_type) }; @@ -71,9 +74,9 @@ impl Trampoline { } pub(crate) struct Template { - args: Box<[fast_api::Type]>, - ret: fast_api::CType, - symbol_ptr: *const c_void, + pub args: Box<[fast_api::Type]>, + pub ret: fast_api::CType, + pub symbol_ptr: *const c_void, } impl fast_api::FastFunction for Template { @@ -106,9 +109,8 @@ impl From<&NativeType> for fast_api::Type { NativeType::I64 => fast_api::Type::Int64, NativeType::U64 => fast_api::Type::Uint64, NativeType::ISize => fast_api::Type::Int64, - NativeType::USize | NativeType::Pointer | NativeType::Function => { - fast_api::Type::Uint64 - } + NativeType::USize => fast_api::Type::Uint64, + NativeType::Pointer | NativeType::Function => fast_api::Type::Pointer, NativeType::Buffer => fast_api::Type::TypedArray(fast_api::CType::Uint8), NativeType::Struct(_) => { fast_api::Type::TypedArray(fast_api::CType::Uint8) |