diff options
Diffstat (limited to 'ext/ffi')
-rw-r--r-- | ext/ffi/Cargo.toml | 5 | ||||
-rw-r--r-- | ext/ffi/call.rs | 75 | ||||
-rw-r--r-- | ext/ffi/callback.rs | 47 | ||||
-rw-r--r-- | ext/ffi/dlfcn.rs | 55 | ||||
-rw-r--r-- | ext/ffi/ir.rs | 142 | ||||
-rw-r--r-- | ext/ffi/lib.rs | 18 | ||||
-rw-r--r-- | ext/ffi/repr.rs | 127 | ||||
-rw-r--r-- | ext/ffi/static.rs | 31 |
8 files changed, 319 insertions, 181 deletions
diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index 23a2aee1c..295e8be84 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_ffi" -version = "0.158.0" +version = "0.164.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -21,9 +21,12 @@ dynasmrt = "1.2.3" libffi = "=3.2.0" libffi-sys = "=2.3.0" log.workspace = true +num-bigint.workspace = true serde.workspace = true serde-value = "0.7" serde_json = "1.0" +thiserror.workspace = true +tokio.workspace = true [target.'cfg(windows)'.dependencies] winapi = { workspace = true, features = ["errhandlingapi", "minwindef", "ntdef", "winbase", "winnt"] } diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index 3572b9e81..bbff0ee48 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -7,23 +7,38 @@ use crate::symbol::NativeType; use crate::symbol::Symbol; use crate::FfiPermissions; use crate::ForeignFunction; -use deno_core::anyhow::anyhow; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::serde_json::Value; +use deno_core::serde_v8::BigInt as V8BigInt; use deno_core::serde_v8::ExternalPointer; use deno_core::unsync::spawn_blocking; use deno_core::v8; use deno_core::OpState; use deno_core::ResourceId; use libffi::middle::Arg; +use num_bigint::BigInt; use serde::Serialize; use std::cell::RefCell; use std::ffi::c_void; use std::future::Future; use std::rc::Rc; +#[derive(Debug, thiserror::Error)] +pub enum CallError { + #[error(transparent)] + IR(#[from] IRError), + #[error("Nonblocking FFI call failed: {0}")] + NonblockingCallFailure(#[source] tokio::task::JoinError), + #[error("Invalid FFI symbol name: '{0}'")] + InvalidSymbol(String), + #[error(transparent)] + Permission(#[from] deno_permissions::PermissionCheckError), + #[error(transparent)] + Resource(deno_core::error::AnyError), + #[error(transparent)] + Callback(#[from] super::CallbackError), +} + // SAFETY: Makes an FFI call unsafe fn ffi_call_rtype_struct( cif: &libffi::middle::Cif, @@ -45,7 +60,7 @@ pub(crate) fn ffi_call_sync<'scope>( args: v8::FunctionCallbackArguments, symbol: &Symbol, out_buffer: Option<OutBuffer>, -) -> Result<NativeValue, AnyError> +) -> Result<NativeValue, CallError> where 'scope: 'scope, { @@ -191,6 +206,7 @@ where #[serde(untagged)] pub enum FfiValue { Value(Value), + BigInt(V8BigInt), External(ExternalPointer), } @@ -201,7 +217,7 @@ fn ffi_call( parameter_types: &[NativeType], result_type: NativeType, out_buffer: Option<OutBuffer>, -) -> Result<FfiValue, AnyError> { +) -> FfiValue { let call_args: Vec<Arg> = call_args .iter() .enumerate() @@ -214,7 +230,7 @@ fn ffi_call( // SAFETY: types in the `Cif` match the actual calling convention and // types of symbol. unsafe { - Ok(match result_type { + match result_type { NativeType::Void => { cif.call::<()>(fun_ptr, &call_args); FfiValue::Value(Value::from(())) @@ -240,18 +256,18 @@ fn ffi_call( 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::U64 => FfiValue::BigInt(V8BigInt::from(BigInt::from( + cif.call::<u64>(fun_ptr, &call_args), + ))), + NativeType::I64 => FfiValue::BigInt(V8BigInt::from(BigInt::from( + cif.call::<i64>(fun_ptr, &call_args), + ))), + NativeType::USize => FfiValue::BigInt(V8BigInt::from(BigInt::from( + cif.call::<usize>(fun_ptr, &call_args), + ))), + NativeType::ISize => FfiValue::BigInt(V8BigInt::from(BigInt::from( + cif.call::<isize>(fun_ptr, &call_args), + ))), NativeType::F32 => { FfiValue::Value(Value::from(cif.call::<f32>(fun_ptr, &call_args))) } @@ -267,7 +283,7 @@ fn ffi_call( ffi_call_rtype_struct(cif, &fun_ptr, call_args, out_buffer.unwrap().0); FfiValue::Value(Value::Null) } - }) + } } } @@ -280,7 +296,7 @@ pub fn op_ffi_call_ptr_nonblocking<FP>( #[serde] def: ForeignFunction, parameters: v8::Local<v8::Array>, out_buffer: Option<v8::Local<v8::TypedArray>>, -) -> Result<impl Future<Output = Result<FfiValue, AnyError>>, AnyError> +) -> Result<impl Future<Output = Result<FfiValue, CallError>>, CallError> where FP: FfiPermissions + 'static, { @@ -309,7 +325,7 @@ where Ok(async move { let result = join_handle .await - .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; + .map_err(CallError::NonblockingCallFailure)?; // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. Ok(result) }) @@ -325,16 +341,17 @@ pub fn op_ffi_call_nonblocking( #[string] symbol: String, parameters: v8::Local<v8::Array>, out_buffer: Option<v8::Local<v8::TypedArray>>, -) -> Result<impl Future<Output = Result<FfiValue, AnyError>>, AnyError> { +) -> Result<impl Future<Output = Result<FfiValue, CallError>>, CallError> { let symbol = { let state = state.borrow(); - let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?; + let resource = state + .resource_table + .get::<DynamicLibraryResource>(rid) + .map_err(CallError::Resource)?; let symbols = &resource.symbols; *symbols .get(&symbol) - .ok_or_else(|| { - type_error(format!("Invalid FFI symbol name: '{symbol}'")) - })? + .ok_or_else(|| CallError::InvalidSymbol(symbol))? .clone() }; @@ -362,7 +379,7 @@ pub fn op_ffi_call_nonblocking( Ok(async move { let result = join_handle .await - .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; + .map_err(CallError::NonblockingCallFailure)?; // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. Ok(result) }) @@ -377,7 +394,7 @@ pub fn op_ffi_call_ptr<FP>( #[serde] def: ForeignFunction, parameters: v8::Local<v8::Array>, out_buffer: Option<v8::Local<v8::TypedArray>>, -) -> Result<FfiValue, AnyError> +) -> Result<FfiValue, CallError> where FP: FfiPermissions + 'static, { @@ -399,7 +416,7 @@ where &def.parameters, def.result.clone(), out_buffer_ptr, - )?; + ); // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. Ok(result) } diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index 6fa166f52..29583c800 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -3,7 +3,6 @@ use crate::symbol::NativeType; use crate::FfiPermissions; use crate::ForeignFunction; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; use deno_core::v8::TryCatch; @@ -34,6 +33,16 @@ thread_local! { static LOCAL_THREAD_ID: RefCell<u32> = const { RefCell::new(0) }; } +#[derive(Debug, thiserror::Error)] +pub enum CallbackError { + #[error(transparent)] + Resource(deno_core::error::AnyError), + #[error(transparent)] + Permission(#[from] deno_permissions::PermissionCheckError), + #[error(transparent)] + Other(deno_core::error::AnyError), +} + #[derive(Clone)] pub struct PtrSymbol { pub cif: libffi::middle::Cif, @@ -44,7 +53,7 @@ impl PtrSymbol { pub fn new( fn_ptr: *mut c_void, def: &ForeignFunction, - ) -> Result<Self, AnyError> { + ) -> Result<Self, CallbackError> { let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); let cif = libffi::middle::Cif::new( def @@ -52,8 +61,13 @@ impl PtrSymbol { .clone() .into_iter() .map(libffi::middle::Type::try_from) - .collect::<Result<Vec<_>, _>>()?, - def.result.clone().try_into()?, + .collect::<Result<Vec<_>, _>>() + .map_err(CallbackError::Other)?, + def + .result + .clone() + .try_into() + .map_err(CallbackError::Other)?, ); Ok(Self { cif, ptr }) @@ -522,10 +536,12 @@ unsafe fn do_ffi_callback( pub fn op_ffi_unsafe_callback_ref( state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId, -) -> Result<impl Future<Output = Result<(), AnyError>>, AnyError> { +) -> Result<impl Future<Output = ()>, CallbackError> { let state = state.borrow(); - let callback_resource = - state.resource_table.get::<UnsafeCallbackResource>(rid)?; + let callback_resource = state + .resource_table + .get::<UnsafeCallbackResource>(rid) + .map_err(CallbackError::Resource)?; Ok(async move { let info: &mut CallbackInfo = @@ -536,7 +552,6 @@ pub fn op_ffi_unsafe_callback_ref( .into_future() .or_cancel(callback_resource.cancel.clone()) .await; - Ok(()) }) } @@ -552,7 +567,7 @@ pub fn op_ffi_unsafe_callback_create<FP, 'scope>( scope: &mut v8::HandleScope<'scope>, #[serde] args: RegisterCallbackArgs, cb: v8::Local<v8::Function>, -) -> Result<v8::Local<'scope, v8::Value>, AnyError> +) -> Result<v8::Local<'scope, v8::Value>, CallbackError> where FP: FfiPermissions + 'static, { @@ -593,8 +608,10 @@ where .parameters .into_iter() .map(libffi::middle::Type::try_from) - .collect::<Result<Vec<_>, _>>()?, - libffi::middle::Type::try_from(args.result)?, + .collect::<Result<Vec<_>, _>>() + .map_err(CallbackError::Other)?, + libffi::middle::Type::try_from(args.result) + .map_err(CallbackError::Other)?, ); // SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists. @@ -624,14 +641,16 @@ pub fn op_ffi_unsafe_callback_close( state: &mut OpState, scope: &mut v8::HandleScope, #[smi] rid: ResourceId, -) -> Result<(), AnyError> { +) -> Result<(), CallbackError> { // SAFETY: This drops the closure and the callback info associated with it. // Any retained function pointers to the closure become dangling pointers. // It is up to the user to know that it is safe to call the `close()` on the // UnsafeCallback instance. unsafe { - let callback_resource = - state.resource_table.take::<UnsafeCallbackResource>(rid)?; + let callback_resource = state + .resource_table + .take::<UnsafeCallbackResource>(rid) + .map_err(CallbackError::Resource)?; let info = Box::from_raw(callback_resource.info); let _ = v8::Global::from_raw(scope, info.callback); let _ = v8::Global::from_raw(scope, info.context); diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs index 10199bf85..26d1b71e9 100644 --- a/ext/ffi/dlfcn.rs +++ b/ext/ffi/dlfcn.rs @@ -6,8 +6,6 @@ use crate::symbol::Symbol; use crate::turbocall; use crate::turbocall::Turbocall; use crate::FfiPermissions; -use deno_core::error::generic_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; use deno_core::GarbageCollected; @@ -17,10 +15,27 @@ use dlopen2::raw::Library; use serde::Deserialize; use serde_value::ValueDeserializer; use std::borrow::Cow; +use std::cell::RefCell; use std::collections::HashMap; use std::ffi::c_void; use std::rc::Rc; +#[derive(Debug, thiserror::Error)] +pub enum DlfcnError { + #[error("Failed to register symbol {symbol}: {error}")] + RegisterSymbol { + symbol: String, + #[source] + error: dlopen2::Error, + }, + #[error(transparent)] + Dlopen(#[from] dlopen2::Error), + #[error(transparent)] + Permission(#[from] deno_permissions::PermissionCheckError), + #[error(transparent)] + Other(deno_core::error::AnyError), +} + pub struct DynamicLibraryResource { lib: Library, pub symbols: HashMap<String, Box<Symbol>>, @@ -37,7 +52,7 @@ impl Resource for DynamicLibraryResource { } impl DynamicLibraryResource { - pub fn get_static(&self, symbol: String) -> Result<*mut c_void, AnyError> { + pub fn get_static(&self, symbol: String) -> Result<*mut c_void, DlfcnError> { // 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. @@ -45,9 +60,7 @@ impl DynamicLibraryResource { // SAFETY: The obtained T symbol is the size of a pointer. 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}" - ))), + Err(error) => Err(DlfcnError::RegisterSymbol { symbol, error }), }? } } @@ -114,14 +127,17 @@ pub struct FfiLoadArgs { #[op2] pub fn op_ffi_load<'scope, FP>( scope: &mut v8::HandleScope<'scope>, - state: &mut OpState, + state: Rc<RefCell<OpState>>, #[serde] args: FfiLoadArgs, -) -> Result<v8::Local<'scope, v8::Value>, AnyError> +) -> Result<v8::Local<'scope, v8::Value>, DlfcnError> where FP: FfiPermissions + 'static, { - let permissions = state.borrow_mut::<FP>(); - let path = permissions.check_partial_with_path(&args.path)?; + let path = { + let mut state = state.borrow_mut(); + let permissions = state.borrow_mut::<FP>(); + permissions.check_partial_with_path(&args.path)? + }; let lib = Library::open(&path).map_err(|e| { dlopen2::Error::OpeningLibraryError(std::io::Error::new( @@ -152,15 +168,16 @@ where // SAFETY: The obtained T symbol is the size of a pointer. match unsafe { resource.lib.symbol::<*const c_void>(symbol) } { Ok(value) => Ok(value), - Err(err) => if foreign_fn.optional { + Err(error) => if foreign_fn.optional { let null: v8::Local<v8::Value> = v8::null(scope).into(); let func_key = v8::String::new(scope, &symbol_key).unwrap(); obj.set(scope, func_key.into(), null); break 'register_symbol; } else { - Err(generic_error(format!( - "Failed to register symbol {symbol}: {err}" - ))) + Err(DlfcnError::RegisterSymbol { + symbol: symbol.to_owned(), + error, + }) }, }?; @@ -171,8 +188,13 @@ where .clone() .into_iter() .map(libffi::middle::Type::try_from) - .collect::<Result<Vec<_>, _>>()?, - foreign_fn.result.clone().try_into()?, + .collect::<Result<Vec<_>, _>>() + .map_err(DlfcnError::Other)?, + foreign_fn + .result + .clone() + .try_into() + .map_err(DlfcnError::Other)?, ); let func_key = v8::String::new(scope, &symbol_key).unwrap(); @@ -197,6 +219,7 @@ where } } + let mut state = state.borrow_mut(); let out = v8::Array::new(scope, 2); let rid = state.resource_table.add(resource); let rid_v8 = v8::Integer::new_from_unsigned(scope, rid); diff --git a/ext/ffi/ir.rs b/ext/ffi/ir.rs index ebf64945b..2e8084216 100644 --- a/ext/ffi/ir.rs +++ b/ext/ffi/ir.rs @@ -1,13 +1,55 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use crate::symbol::NativeType; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::v8; use libffi::middle::Arg; use std::ffi::c_void; use std::ptr; +#[derive(Debug, thiserror::Error)] +pub enum IRError { + #[error("Invalid FFI u8 type, expected boolean")] + InvalidU8ExpectedBoolean, + #[error("Invalid FFI u8 type, expected unsigned integer")] + InvalidU8ExpectedUnsignedInteger, + #[error("Invalid FFI i8 type, expected integer")] + InvalidI8, + #[error("Invalid FFI u16 type, expected unsigned integer")] + InvalidU16, + #[error("Invalid FFI i16 type, expected integer")] + InvalidI16, + #[error("Invalid FFI u32 type, expected unsigned integer")] + InvalidU32, + #[error("Invalid FFI i32 type, expected integer")] + InvalidI32, + #[error("Invalid FFI u64 type, expected unsigned integer")] + InvalidU64, + #[error("Invalid FFI i64 type, expected integer")] + InvalidI64, + #[error("Invalid FFI usize type, expected unsigned integer")] + InvalidUsize, + #[error("Invalid FFI isize type, expected integer")] + InvalidIsize, + #[error("Invalid FFI f32 type, expected number")] + InvalidF32, + #[error("Invalid FFI f64 type, expected number")] + InvalidF64, + #[error("Invalid FFI pointer type, expected null, or External")] + InvalidPointerType, + #[error( + "Invalid FFI buffer type, expected null, ArrayBuffer, or ArrayBufferView" + )] + InvalidBufferType, + #[error("Invalid FFI ArrayBufferView, expected data in the buffer")] + InvalidArrayBufferView, + #[error("Invalid FFI ArrayBuffer, expected data in buffer")] + InvalidArrayBuffer, + #[error("Invalid FFI struct type, expected ArrayBuffer, or ArrayBufferView")] + InvalidStructType, + #[error("Invalid FFI function type, expected null, or External")] + InvalidFunctionType, +} + pub struct OutBuffer(pub *mut u8); // SAFETY: OutBuffer is allocated by us in 00_ffi.js and is guaranteed to be @@ -126,9 +168,9 @@ unsafe impl Send for NativeValue {} #[inline] pub fn ffi_parse_bool_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let bool_value = v8::Local::<v8::Boolean>::try_from(arg) - .map_err(|_| type_error("Invalid FFI u8 type, expected boolean"))? + .map_err(|_| IRError::InvalidU8ExpectedBoolean)? .is_true(); Ok(NativeValue { bool_value }) } @@ -136,9 +178,9 @@ pub fn ffi_parse_bool_arg( #[inline] pub fn ffi_parse_u8_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let u8_value = v8::Local::<v8::Uint32>::try_from(arg) - .map_err(|_| type_error("Invalid FFI u8 type, expected unsigned integer"))? + .map_err(|_| IRError::InvalidU8ExpectedUnsignedInteger)? .value() as u8; Ok(NativeValue { u8_value }) } @@ -146,9 +188,9 @@ pub fn ffi_parse_u8_arg( #[inline] pub fn ffi_parse_i8_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let i8_value = v8::Local::<v8::Int32>::try_from(arg) - .map_err(|_| type_error("Invalid FFI i8 type, expected integer"))? + .map_err(|_| IRError::InvalidI8)? .value() as i8; Ok(NativeValue { i8_value }) } @@ -156,9 +198,9 @@ pub fn ffi_parse_i8_arg( #[inline] pub fn ffi_parse_u16_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let u16_value = v8::Local::<v8::Uint32>::try_from(arg) - .map_err(|_| type_error("Invalid FFI u16 type, expected unsigned integer"))? + .map_err(|_| IRError::InvalidU16)? .value() as u16; Ok(NativeValue { u16_value }) } @@ -166,9 +208,9 @@ pub fn ffi_parse_u16_arg( #[inline] pub fn ffi_parse_i16_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let i16_value = v8::Local::<v8::Int32>::try_from(arg) - .map_err(|_| type_error("Invalid FFI i16 type, expected integer"))? + .map_err(|_| IRError::InvalidI16)? .value() as i16; Ok(NativeValue { i16_value }) } @@ -176,9 +218,9 @@ pub fn ffi_parse_i16_arg( #[inline] pub fn ffi_parse_u32_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let u32_value = v8::Local::<v8::Uint32>::try_from(arg) - .map_err(|_| type_error("Invalid FFI u32 type, expected unsigned integer"))? + .map_err(|_| IRError::InvalidU32)? .value(); Ok(NativeValue { u32_value }) } @@ -186,9 +228,9 @@ pub fn ffi_parse_u32_arg( #[inline] pub fn ffi_parse_i32_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let i32_value = v8::Local::<v8::Int32>::try_from(arg) - .map_err(|_| type_error("Invalid FFI i32 type, expected integer"))? + .map_err(|_| IRError::InvalidI32)? .value(); Ok(NativeValue { i32_value }) } @@ -197,7 +239,7 @@ pub fn ffi_parse_i32_arg( pub fn ffi_parse_u64_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { // 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. @@ -207,9 +249,7 @@ pub fn ffi_parse_u64_arg( } 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", - )); + return Err(IRError::InvalidU64); }; Ok(NativeValue { u64_value }) } @@ -218,7 +258,7 @@ pub fn ffi_parse_u64_arg( pub fn ffi_parse_i64_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { // 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. @@ -228,7 +268,7 @@ pub fn ffi_parse_i64_arg( } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { value.integer_value(scope).unwrap() } else { - return Err(type_error("Invalid FFI i64 type, expected integer")); + return Err(IRError::InvalidI64); }; Ok(NativeValue { i64_value }) } @@ -237,7 +277,7 @@ pub fn ffi_parse_i64_arg( pub fn ffi_parse_usize_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { // 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. @@ -247,7 +287,7 @@ pub fn ffi_parse_usize_arg( } 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")); + return Err(IRError::InvalidUsize); }; Ok(NativeValue { usize_value }) } @@ -256,7 +296,7 @@ pub fn ffi_parse_usize_arg( pub fn ffi_parse_isize_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { // 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. @@ -266,7 +306,7 @@ pub fn ffi_parse_isize_arg( } 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")); + return Err(IRError::InvalidIsize); }; Ok(NativeValue { isize_value }) } @@ -274,9 +314,9 @@ pub fn ffi_parse_isize_arg( #[inline] pub fn ffi_parse_f32_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let f32_value = v8::Local::<v8::Number>::try_from(arg) - .map_err(|_| type_error("Invalid FFI f32 type, expected number"))? + .map_err(|_| IRError::InvalidF32)? .value() as f32; Ok(NativeValue { f32_value }) } @@ -284,9 +324,9 @@ pub fn ffi_parse_f32_arg( #[inline] pub fn ffi_parse_f64_arg( arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { let f64_value = v8::Local::<v8::Number>::try_from(arg) - .map_err(|_| type_error("Invalid FFI f64 type, expected number"))? + .map_err(|_| IRError::InvalidF64)? .value(); Ok(NativeValue { f64_value }) } @@ -295,15 +335,13 @@ pub fn ffi_parse_f64_arg( pub fn ffi_parse_pointer_arg( _scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { 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, or External", - )); + return Err(IRError::InvalidPointerType); }; Ok(NativeValue { pointer }) } @@ -312,7 +350,7 @@ pub fn ffi_parse_pointer_arg( pub fn ffi_parse_buffer_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { // Order of checking: // 1. ArrayBuffer: Fairly common and not supported by Fast API, optimise this case. // 2. ArrayBufferView: Common and supported by Fast API @@ -328,9 +366,7 @@ pub fn ffi_parse_buffer_arg( let byte_offset = value.byte_offset(); let pointer = value .buffer(scope) - .ok_or_else(|| { - type_error("Invalid FFI ArrayBufferView, expected data in the buffer") - })? + .ok_or(IRError::InvalidArrayBufferView)? .data(); if let Some(non_null) = pointer { // SAFETY: Pointer is non-null, and V8 guarantees that the byte_offset @@ -342,9 +378,7 @@ pub fn ffi_parse_buffer_arg( } else if arg.is_null() { ptr::null_mut() } else { - return Err(type_error( - "Invalid FFI buffer type, expected null, ArrayBuffer, or ArrayBufferView", - )); + return Err(IRError::InvalidBufferType); }; Ok(NativeValue { pointer }) } @@ -353,7 +387,7 @@ pub fn ffi_parse_buffer_arg( pub fn ffi_parse_struct_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { // Order of checking: // 1. ArrayBuffer: Fairly common and not supported by Fast API, optimise this case. // 2. ArrayBufferView: Common and supported by Fast API @@ -362,31 +396,23 @@ pub fn ffi_parse_struct_arg( if let Some(non_null) = value.data() { non_null.as_ptr() } else { - return Err(type_error( - "Invalid FFI ArrayBuffer, expected data in buffer", - )); + return Err(IRError::InvalidArrayBuffer); } } else if let Ok(value) = v8::Local::<v8::ArrayBufferView>::try_from(arg) { let byte_offset = value.byte_offset(); let pointer = value .buffer(scope) - .ok_or_else(|| { - type_error("Invalid FFI ArrayBufferView, expected data in the buffer") - })? + .ok_or(IRError::InvalidArrayBufferView)? .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) } } else { - return Err(type_error( - "Invalid FFI ArrayBufferView, expected data in buffer", - )); + return Err(IRError::InvalidArrayBufferView); } } else { - return Err(type_error( - "Invalid FFI struct type, expected ArrayBuffer, or ArrayBufferView", - )); + return Err(IRError::InvalidStructType); }; Ok(NativeValue { pointer }) } @@ -395,15 +421,13 @@ pub fn ffi_parse_struct_arg( pub fn ffi_parse_function_arg( _scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { +) -> Result<NativeValue, IRError> { 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, or External", - )); + return Err(IRError::InvalidFunctionType); }; Ok(NativeValue { pointer }) } @@ -412,7 +436,7 @@ pub fn ffi_parse_args<'scope>( scope: &mut v8::HandleScope<'scope>, args: v8::Local<v8::Array>, parameter_types: &[NativeType], -) -> Result<Vec<NativeValue>, AnyError> +) -> Result<Vec<NativeValue>, IRError> where 'scope: 'scope, { diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 77ec3c85e..73ec7757a 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,7 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::AnyError; - use std::mem::size_of; use std::os::raw::c_char; use std::os::raw::c_short; @@ -29,6 +27,14 @@ use repr::*; use symbol::NativeType; use symbol::Symbol; +pub use call::CallError; +pub use callback::CallbackError; +use deno_permissions::PermissionCheckError; +pub use dlfcn::DlfcnError; +pub use ir::IRError; +pub use r#static::StaticError; +pub use repr::ReprError; + #[cfg(not(target_pointer_width = "64"))] compile_error!("platform not supported"); @@ -41,17 +47,17 @@ const _: () = { pub const UNSTABLE_FEATURE_NAME: &str = "ffi"; pub trait FfiPermissions { - fn check_partial_no_path(&mut self) -> Result<(), AnyError>; + fn check_partial_no_path(&mut self) -> Result<(), PermissionCheckError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_partial_with_path( &mut self, path: &str, - ) -> Result<PathBuf, AnyError>; + ) -> Result<PathBuf, PermissionCheckError>; } impl FfiPermissions for deno_permissions::PermissionsContainer { #[inline(always)] - fn check_partial_no_path(&mut self) -> Result<(), AnyError> { + fn check_partial_no_path(&mut self) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_ffi_partial_no_path(self) } @@ -59,7 +65,7 @@ impl FfiPermissions for deno_permissions::PermissionsContainer { fn check_partial_with_path( &mut self, path: &str, - ) -> Result<PathBuf, AnyError> { + ) -> Result<PathBuf, PermissionCheckError> { deno_permissions::PermissionsContainer::check_ffi_partial_with_path( self, path, ) diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs index 315e6d53b..fd8a2c8e7 100644 --- a/ext/ffi/repr.rs +++ b/ext/ffi/repr.rs @@ -1,9 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use crate::FfiPermissions; -use deno_core::error::range_error; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; use deno_core::OpState; @@ -12,11 +9,51 @@ use std::ffi::c_void; use std::ffi::CStr; use std::ptr; +#[derive(Debug, thiserror::Error)] +pub enum ReprError { + #[error("Invalid pointer to offset, pointer is null")] + InvalidOffset, + #[error("Invalid ArrayBuffer pointer, pointer is null")] + InvalidArrayBuffer, + #[error("Destination length is smaller than source length")] + DestinationLengthTooShort, + #[error("Invalid CString pointer, pointer is null")] + InvalidCString, + #[error("Invalid CString pointer, string exceeds max length")] + CStringTooLong, + #[error("Invalid bool pointer, pointer is null")] + InvalidBool, + #[error("Invalid u8 pointer, pointer is null")] + InvalidU8, + #[error("Invalid i8 pointer, pointer is null")] + InvalidI8, + #[error("Invalid u16 pointer, pointer is null")] + InvalidU16, + #[error("Invalid i16 pointer, pointer is null")] + InvalidI16, + #[error("Invalid u32 pointer, pointer is null")] + InvalidU32, + #[error("Invalid i32 pointer, pointer is null")] + InvalidI32, + #[error("Invalid u64 pointer, pointer is null")] + InvalidU64, + #[error("Invalid i64 pointer, pointer is null")] + InvalidI64, + #[error("Invalid f32 pointer, pointer is null")] + InvalidF32, + #[error("Invalid f64 pointer, pointer is null")] + InvalidF64, + #[error("Invalid pointer pointer, pointer is null")] + InvalidPointer, + #[error(transparent)] + Permission(#[from] deno_permissions::PermissionCheckError), +} + #[op2(fast)] pub fn op_ffi_ptr_create<FP>( state: &mut OpState, #[bigint] ptr_number: usize, -) -> Result<*mut c_void, AnyError> +) -> Result<*mut c_void, ReprError> where FP: FfiPermissions + 'static, { @@ -31,7 +68,7 @@ pub fn op_ffi_ptr_equals<FP>( state: &mut OpState, a: *const c_void, b: *const c_void, -) -> Result<bool, AnyError> +) -> Result<bool, ReprError> where FP: FfiPermissions + 'static, { @@ -45,7 +82,7 @@ where pub fn op_ffi_ptr_of<FP>( state: &mut OpState, #[anybuffer] buf: *const u8, -) -> Result<*mut c_void, AnyError> +) -> Result<*mut c_void, ReprError> where FP: FfiPermissions + 'static, { @@ -59,7 +96,7 @@ where pub fn op_ffi_ptr_of_exact<FP>( state: &mut OpState, buf: v8::Local<v8::ArrayBufferView>, -) -> Result<*mut c_void, AnyError> +) -> Result<*mut c_void, ReprError> where FP: FfiPermissions + 'static, { @@ -80,7 +117,7 @@ pub fn op_ffi_ptr_offset<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<*mut c_void, AnyError> +) -> Result<*mut c_void, ReprError> where FP: FfiPermissions + 'static, { @@ -88,7 +125,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid pointer to offset, pointer is null")); + return Err(ReprError::InvalidOffset); } // TODO(mmastrac): Create a RawPointer that can safely do pointer math. @@ -110,7 +147,7 @@ unsafe extern "C" fn noop_deleter_callback( pub fn op_ffi_ptr_value<FP>( state: &mut OpState, ptr: *mut c_void, -) -> Result<usize, AnyError> +) -> Result<usize, ReprError> where FP: FfiPermissions + 'static, { @@ -127,7 +164,7 @@ pub fn op_ffi_get_buf<FP, 'scope>( ptr: *mut c_void, #[number] offset: isize, #[number] len: usize, -) -> Result<v8::Local<'scope, v8::ArrayBuffer>, AnyError> +) -> Result<v8::Local<'scope, v8::ArrayBuffer>, ReprError> where FP: FfiPermissions + 'static, { @@ -135,7 +172,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid ArrayBuffer pointer, pointer is null")); + return Err(ReprError::InvalidArrayBuffer); } // 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. @@ -144,7 +181,7 @@ where ptr.offset(offset), len, noop_deleter_callback, - std::ptr::null_mut(), + ptr::null_mut(), ) } .make_shared(); @@ -159,7 +196,7 @@ pub fn op_ffi_buf_copy_into<FP>( #[number] offset: isize, #[anybuffer] dst: &mut [u8], #[number] len: usize, -) -> Result<(), AnyError> +) -> Result<(), ReprError> where FP: FfiPermissions + 'static, { @@ -167,11 +204,9 @@ where permissions.check_partial_no_path()?; if src.is_null() { - Err(type_error("Invalid ArrayBuffer pointer, pointer is null")) + Err(ReprError::InvalidArrayBuffer) } else if dst.len() < len { - Err(range_error( - "Destination length is smaller than source length", - )) + Err(ReprError::DestinationLengthTooShort) } else { let src = src as *const c_void; @@ -190,7 +225,7 @@ pub fn op_ffi_cstr_read<FP, 'scope>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<v8::Local<'scope, v8::String>, AnyError> +) -> Result<v8::Local<'scope, v8::String>, ReprError> where FP: FfiPermissions + 'static, { @@ -198,16 +233,14 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid CString pointer, pointer is null")); + return Err(ReprError::InvalidCString); } 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::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal) - .ok_or_else(|| { - type_error("Invalid CString pointer, string exceeds max length") - })?; + .ok_or_else(|| ReprError::CStringTooLong)?; Ok(value) } @@ -216,7 +249,7 @@ pub fn op_ffi_read_bool<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<bool, AnyError> +) -> Result<bool, ReprError> where FP: FfiPermissions + 'static, { @@ -224,7 +257,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid bool pointer, pointer is null")); + return Err(ReprError::InvalidBool); } // SAFETY: ptr and offset are user provided. @@ -236,7 +269,7 @@ pub fn op_ffi_read_u8<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<u32, AnyError> +) -> Result<u32, ReprError> where FP: FfiPermissions + 'static, { @@ -244,7 +277,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid u8 pointer, pointer is null")); + return Err(ReprError::InvalidU8); } // SAFETY: ptr and offset are user provided. @@ -258,7 +291,7 @@ pub fn op_ffi_read_i8<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<i32, AnyError> +) -> Result<i32, ReprError> where FP: FfiPermissions + 'static, { @@ -266,7 +299,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid i8 pointer, pointer is null")); + return Err(ReprError::InvalidI8); } // SAFETY: ptr and offset are user provided. @@ -280,7 +313,7 @@ pub fn op_ffi_read_u16<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<u32, AnyError> +) -> Result<u32, ReprError> where FP: FfiPermissions + 'static, { @@ -288,7 +321,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid u16 pointer, pointer is null")); + return Err(ReprError::InvalidU16); } // SAFETY: ptr and offset are user provided. @@ -302,7 +335,7 @@ pub fn op_ffi_read_i16<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<i32, AnyError> +) -> Result<i32, ReprError> where FP: FfiPermissions + 'static, { @@ -310,7 +343,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid i16 pointer, pointer is null")); + return Err(ReprError::InvalidI16); } // SAFETY: ptr and offset are user provided. @@ -324,7 +357,7 @@ pub fn op_ffi_read_u32<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<u32, AnyError> +) -> Result<u32, ReprError> where FP: FfiPermissions + 'static, { @@ -332,7 +365,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid u32 pointer, pointer is null")); + return Err(ReprError::InvalidU32); } // SAFETY: ptr and offset are user provided. @@ -344,7 +377,7 @@ pub fn op_ffi_read_i32<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<i32, AnyError> +) -> Result<i32, ReprError> where FP: FfiPermissions + 'static, { @@ -352,7 +385,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid i32 pointer, pointer is null")); + return Err(ReprError::InvalidI32); } // SAFETY: ptr and offset are user provided. @@ -367,7 +400,7 @@ pub fn op_ffi_read_u64<FP>( // Note: The representation of 64-bit integers is function-wide. We cannot // choose to take this parameter as a number while returning a bigint. #[bigint] offset: isize, -) -> Result<u64, AnyError> +) -> Result<u64, ReprError> where FP: FfiPermissions + 'static, { @@ -375,7 +408,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid u64 pointer, pointer is null")); + return Err(ReprError::InvalidU64); } let value = @@ -393,7 +426,7 @@ pub fn op_ffi_read_i64<FP>( // Note: The representation of 64-bit integers is function-wide. We cannot // choose to take this parameter as a number while returning a bigint. #[bigint] offset: isize, -) -> Result<i64, AnyError> +) -> Result<i64, ReprError> where FP: FfiPermissions + 'static, { @@ -401,7 +434,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid i64 pointer, pointer is null")); + return Err(ReprError::InvalidI64); } let value = @@ -416,7 +449,7 @@ pub fn op_ffi_read_f32<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<f32, AnyError> +) -> Result<f32, ReprError> where FP: FfiPermissions + 'static, { @@ -424,7 +457,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid f32 pointer, pointer is null")); + return Err(ReprError::InvalidF32); } // SAFETY: ptr and offset are user provided. @@ -436,7 +469,7 @@ pub fn op_ffi_read_f64<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<f64, AnyError> +) -> Result<f64, ReprError> where FP: FfiPermissions + 'static, { @@ -444,7 +477,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid f64 pointer, pointer is null")); + return Err(ReprError::InvalidF64); } // SAFETY: ptr and offset are user provided. @@ -456,7 +489,7 @@ pub fn op_ffi_read_ptr<FP>( state: &mut OpState, ptr: *mut c_void, #[number] offset: isize, -) -> Result<*mut c_void, AnyError> +) -> Result<*mut c_void, ReprError> where FP: FfiPermissions + 'static, { @@ -464,7 +497,7 @@ where permissions.check_partial_no_path()?; if ptr.is_null() { - return Err(type_error("Invalid pointer pointer, pointer is null")); + return Err(ReprError::InvalidPointer); } // SAFETY: ptr and offset are user provided. diff --git a/ext/ffi/static.rs b/ext/ffi/static.rs index f08605754..61b405933 100644 --- a/ext/ffi/static.rs +++ b/ext/ffi/static.rs @@ -2,14 +2,24 @@ use crate::dlfcn::DynamicLibraryResource; use crate::symbol::NativeType; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; use deno_core::OpState; use deno_core::ResourceId; use std::ptr; +#[derive(Debug, thiserror::Error)] +pub enum StaticError { + #[error(transparent)] + Dlfcn(super::DlfcnError), + #[error("Invalid FFI static type 'void'")] + InvalidTypeVoid, + #[error("Invalid FFI static type 'struct'")] + InvalidTypeStruct, + #[error(transparent)] + Resource(deno_core::error::AnyError), +} + #[op2] pub fn op_ffi_get_static<'scope>( scope: &mut v8::HandleScope<'scope>, @@ -18,24 +28,27 @@ pub fn op_ffi_get_static<'scope>( #[string] name: String, #[serde] static_type: NativeType, optional: bool, -) -> Result<v8::Local<'scope, v8::Value>, AnyError> { - let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?; +) -> Result<v8::Local<'scope, v8::Value>, StaticError> { + let resource = state + .resource_table + .get::<DynamicLibraryResource>(rid) + .map_err(StaticError::Resource)?; let data_ptr = match resource.get_static(name) { - Ok(data_ptr) => Ok(data_ptr), + Ok(data_ptr) => data_ptr, Err(err) => { if optional { let null: v8::Local<v8::Value> = v8::null(scope).into(); return Ok(null); } else { - Err(err) + return Err(StaticError::Dlfcn(err)); } } - }?; + }; Ok(match static_type { NativeType::Void => { - return Err(type_error("Invalid FFI static type 'void'")); + return Err(StaticError::InvalidTypeVoid); } NativeType::Bool => { // SAFETY: ptr is user provided @@ -132,7 +145,7 @@ pub fn op_ffi_get_static<'scope>( external } NativeType::Struct(_) => { - return Err(type_error("Invalid FFI static type 'struct'")); + return Err(StaticError::InvalidTypeStruct); } }) } |