diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2022-12-12 06:14:20 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-12 14:14:20 +0000 |
commit | a2db70a8d0820722695e9094c8dbc888bde1ffa3 (patch) | |
tree | 6885e2aee424ab0264a8b01df9b0bafbcfe85792 /ext/ffi/lib.rs | |
parent | 890b0653104388bfef60ca7ebd0af758caf5f0be (diff) |
refactor(ext/ffi): split into multiple parts (#16950)
- [x] `dlfcn.rs` - `dlopen()`-related code.
- [x] `turbocall.rs` - Call trampoline JIT compiler.
- [x] `repr.rs` - Pointer representation. Home of the UnsafePointerView
ops.
- [x] `symbol.rs` - Function symbol related code.
- [x] `callback.rs` - Home of `Deno.UnsafeCallback` ops.
- [x] `ir.rs` - Intermediate representation for values. Home of the
`NativeValue` type.
- [x] `call.rs` - Generic call ops. Home to everything related to
calling FFI symbols.
- [x] `static.rs` - static symbol support
I find easier to work with this setup, I eventually want to expand
TurboCall to unroll type conversion loop in generic calls, generate code
for individual symbols (lazy function pointers), etc.
Diffstat (limited to 'ext/ffi/lib.rs')
-rw-r--r-- | ext/ffi/lib.rs | 2429 |
1 files changed, 28 insertions, 2401 deletions
diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 7e7756c93..f3a906505 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,47 +1,41 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use core::ptr::NonNull; -use deno_core::anyhow::anyhow; -use deno_core::error::generic_error; -use deno_core::error::range_error; -use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::futures::channel::mpsc; -use deno_core::futures::Future; use deno_core::include_js_files; -use deno_core::op; -use deno_core::serde_json::Value; -use deno_core::serde_v8; use deno_core::v8; -use deno_core::CancelFuture; -use deno_core::CancelHandle; use deno_core::Extension; use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use dlopen::raw::Library; -use libffi::middle::Arg; -use libffi::middle::Cif; -use serde::Deserialize; -use std::borrow::Cow; + use std::cell::RefCell; -use std::collections::HashMap; -use std::ffi::c_void; -use std::ffi::CStr; -use std::future::IntoFuture; use std::mem::size_of; use std::os::raw::c_char; use std::os::raw::c_short; use std::path::Path; -use std::path::PathBuf; -use std::pin::Pin; use std::ptr; use std::rc::Rc; -use std::sync::mpsc::sync_channel; -use std::task::Poll; -use std::task::Waker; -mod fast_call; +mod call; +mod callback; +mod dlfcn; +mod ir; +mod repr; +mod r#static; +mod symbol; +mod turbocall; + +use call::op_ffi_call_nonblocking; +use call::op_ffi_call_ptr; +use call::op_ffi_call_ptr_nonblocking; +use callback::op_ffi_unsafe_callback_create; +use callback::op_ffi_unsafe_callback_ref; +use callback::op_ffi_unsafe_callback_unref; +use dlfcn::op_ffi_load; +use dlfcn::ForeignFunction; +use r#static::op_ffi_get_static; +use repr::*; +use symbol::NativeType; +use symbol::Symbol; #[cfg(not(target_pointer_width = "64"))] compile_error!("platform not supported"); @@ -56,8 +50,8 @@ 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(crate) const MAX_SAFE_INTEGER: isize = 9007199254740991; +pub(crate) const MIN_SAFE_INTEGER: isize = -9007199254740991; pub struct Unstable(pub bool); @@ -82,86 +76,11 @@ pub trait FfiPermissions { fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError>; } -#[derive(Clone)] -struct Symbol { - cif: libffi::middle::Cif, - ptr: libffi::middle::CodePtr, - parameter_types: Vec<NativeType>, - result_type: NativeType, - can_callback: bool, -} - -#[allow(clippy::non_send_fields_in_send_ty)] -// SAFETY: unsafe trait must have unsafe implementation -unsafe impl Send for Symbol {} -// SAFETY: unsafe trait must have unsafe implementation -unsafe impl Sync for Symbol {} - -#[derive(Clone)] -struct PtrSymbol { - cif: libffi::middle::Cif, - ptr: libffi::middle::CodePtr, -} - -impl PtrSymbol { - 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 - .parameters - .clone() - .into_iter() - .map(libffi::middle::Type::from), - def.result.into(), - ); - - Self { cif, ptr } - } -} - -#[allow(clippy::non_send_fields_in_send_ty)] -// SAFETY: unsafe trait must have unsafe implementation -unsafe impl Send for PtrSymbol {} -// SAFETY: unsafe trait must have unsafe implementation -unsafe impl Sync for PtrSymbol {} - -struct DynamicLibraryResource { - lib: Library, - symbols: HashMap<String, Box<Symbol>>, -} - -impl Resource for DynamicLibraryResource { - fn name(&self) -> Cow<str> { - "dynamicLibrary".into() - } +pub(crate) type PendingFfiAsyncWork = Box<dyn FnOnce()>; - fn close(self: Rc<Self>) { - drop(self) - } -} - -impl DynamicLibraryResource { - fn get_static(&self, symbol: String) -> Result<*const 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) } { - Ok(value) => Ok(Ok(value)), - Err(err) => Err(generic_error(format!( - "Failed to register symbol {}: {}", - symbol, err - ))), - }? - } -} - -type PendingFfiAsyncWork = Box<dyn FnOnce()>; - -struct FfiState { - async_work_sender: mpsc::UnboundedSender<PendingFfiAsyncWork>, - async_work_receiver: mpsc::UnboundedReceiver<PendingFfiAsyncWork>, +pub(crate) struct FfiState { + pub(crate) async_work_sender: mpsc::UnboundedSender<PendingFfiAsyncWork>, + pub(crate) async_work_receiver: mpsc::UnboundedReceiver<PendingFfiAsyncWork>, } pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension { @@ -237,2295 +156,3 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension { }) .build() } - -/// Defines the accepted types that can be used as -/// parameters and return values in FFI. -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "lowercase")] -enum NativeType { - Void, - Bool, - U8, - I8, - U16, - I16, - U32, - I32, - U64, - I64, - USize, - ISize, - F32, - F64, - Pointer, - Buffer, - Function, -} - -impl From<NativeType> for libffi::middle::Type { - fn from(native_type: NativeType) -> Self { - match native_type { - NativeType::Void => libffi::middle::Type::void(), - NativeType::U8 | NativeType::Bool => libffi::middle::Type::u8(), - NativeType::I8 => libffi::middle::Type::i8(), - NativeType::U16 => libffi::middle::Type::u16(), - NativeType::I16 => libffi::middle::Type::i16(), - NativeType::U32 => libffi::middle::Type::u32(), - NativeType::I32 => libffi::middle::Type::i32(), - NativeType::U64 => libffi::middle::Type::u64(), - NativeType::I64 => libffi::middle::Type::i64(), - NativeType::USize => libffi::middle::Type::usize(), - NativeType::ISize => libffi::middle::Type::isize(), - NativeType::F32 => libffi::middle::Type::f32(), - NativeType::F64 => libffi::middle::Type::f64(), - NativeType::Pointer | NativeType::Buffer | NativeType::Function => { - libffi::middle::Type::pointer() - } - } - } -} - -/// Intermediate format for easy translation from NativeType + V8 value -/// to libffi argument types. -#[repr(C)] -union NativeValue { - void_value: (), - bool_value: bool, - u8_value: u8, - i8_value: i8, - u16_value: u16, - i16_value: i16, - u32_value: u32, - i32_value: i32, - u64_value: u64, - i64_value: i64, - usize_value: usize, - isize_value: isize, - f32_value: f32, - f64_value: f64, - pointer: *mut c_void, -} - -impl NativeValue { - unsafe fn as_arg(&self, native_type: NativeType) -> Arg { - match native_type { - NativeType::Void => unreachable!(), - NativeType::Bool => Arg::new(&self.bool_value), - NativeType::U8 => Arg::new(&self.u8_value), - NativeType::I8 => Arg::new(&self.i8_value), - NativeType::U16 => Arg::new(&self.u16_value), - NativeType::I16 => Arg::new(&self.i16_value), - NativeType::U32 => Arg::new(&self.u32_value), - NativeType::I32 => Arg::new(&self.i32_value), - NativeType::U64 => Arg::new(&self.u64_value), - NativeType::I64 => Arg::new(&self.i64_value), - NativeType::USize => Arg::new(&self.usize_value), - NativeType::ISize => Arg::new(&self.isize_value), - NativeType::F32 => Arg::new(&self.f32_value), - NativeType::F64 => Arg::new(&self.f64_value), - NativeType::Pointer | NativeType::Buffer | NativeType::Function => { - Arg::new(&self.pointer) - } - } - } - - // SAFETY: native_type must correspond to the type of value represented by the union field - 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) - } - } - } - - // SAFETY: native_type must correspond to the type of value represented by the union field - #[inline] - unsafe fn to_v8<'scope>( - &self, - scope: &mut v8::HandleScope<'scope>, - native_type: NativeType, - ) -> serde_v8::Value<'scope> { - match native_type { - NativeType::Void => { - let local_value: v8::Local<v8::Value> = v8::undefined(scope).into(); - local_value.into() - } - NativeType::Bool => { - let local_value: v8::Local<v8::Value> = - v8::Boolean::new(scope, self.bool_value).into(); - local_value.into() - } - NativeType::U8 => { - let local_value: v8::Local<v8::Value> = - v8::Integer::new_from_unsigned(scope, self.u8_value as u32).into(); - local_value.into() - } - NativeType::I8 => { - let local_value: v8::Local<v8::Value> = - v8::Integer::new(scope, self.i8_value as i32).into(); - local_value.into() - } - NativeType::U16 => { - let local_value: v8::Local<v8::Value> = - v8::Integer::new_from_unsigned(scope, self.u16_value as u32).into(); - local_value.into() - } - NativeType::I16 => { - let local_value: v8::Local<v8::Value> = - v8::Integer::new(scope, self.i16_value as i32).into(); - local_value.into() - } - NativeType::U32 => { - let local_value: v8::Local<v8::Value> = - v8::Integer::new_from_unsigned(scope, self.u32_value).into(); - local_value.into() - } - NativeType::I32 => { - let local_value: v8::Local<v8::Value> = - v8::Integer::new(scope, self.i32_value).into(); - local_value.into() - } - NativeType::U64 => { - let value = self.u64_value; - 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() - }; - local_value.into() - } - NativeType::I64 => { - let value = self.i64_value; - let local_value: v8::Local<v8::Value> = - 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> = - 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> = - if !(MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&value) { - 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 => { - let local_value: v8::Local<v8::Value> = - v8::Number::new(scope, self.f32_value as f64).into(); - local_value.into() - } - NativeType::F64 => { - let local_value: v8::Local<v8::Value> = - v8::Number::new(scope, self.f64_value).into(); - 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() - }; - local_value.into() - } - } - } -} - -// SAFETY: unsafe trait must have unsafe implementation -unsafe impl Send for NativeValue {} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -struct ForeignFunction { - name: Option<String>, - parameters: Vec<NativeType>, - result: NativeType, - #[serde(rename = "nonblocking")] - non_blocking: Option<bool>, - #[serde(rename = "callback")] - #[serde(default = "default_callback")] - callback: bool, -} - -fn default_callback() -> bool { - false -} - -// ForeignStatic's name and type fields are read and used by -// serde_v8 to determine which variant a ForeignSymbol is. -// They are not used beyond that and are thus marked with underscores. -#[derive(Deserialize, Debug)] -struct ForeignStatic { - #[serde(rename(deserialize = "name"))] - _name: Option<String>, - #[serde(rename(deserialize = "type"))] - _type: String, -} - -#[derive(Deserialize, Debug)] -#[serde(untagged)] -enum ForeignSymbol { - ForeignFunction(ForeignFunction), - ForeignStatic(ForeignStatic), -} - -#[derive(Deserialize, Debug)] -struct FfiLoadArgs { - path: String, - symbols: HashMap<String, ForeignSymbol>, -} - -// `path` is only used on Windows. -#[allow(unused_variables)] -pub(crate) fn format_error(e: dlopen::Error, path: String) -> String { - match e { - #[cfg(target_os = "windows")] - // This calls FormatMessageW with library path - // as replacement for the insert sequences. - // Unlike libstd which passes the FORMAT_MESSAGE_IGNORE_INSERTS - // flag without any arguments. - // - // https://github.com/denoland/deno/issues/11632 - dlopen::Error::OpeningLibraryError(e) => { - use std::ffi::OsStr; - use std::os::windows::ffi::OsStrExt; - use winapi::shared::minwindef::DWORD; - use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER; - use winapi::um::errhandlingapi::GetLastError; - use winapi::um::winbase::FormatMessageW; - use winapi::um::winbase::FORMAT_MESSAGE_ARGUMENT_ARRAY; - use winapi::um::winbase::FORMAT_MESSAGE_FROM_SYSTEM; - use winapi::um::winnt::LANG_SYSTEM_DEFAULT; - use winapi::um::winnt::MAKELANGID; - use winapi::um::winnt::SUBLANG_SYS_DEFAULT; - - let err_num = match e.raw_os_error() { - Some(err_num) => err_num, - // This should never hit unless dlopen changes its error type. - None => return e.to_string(), - }; - - // Language ID (0x0800) - let lang_id = - MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) as DWORD; - - let mut buf = vec![0; 500]; - - let path = OsStr::new(&path) - .encode_wide() - .chain(Some(0).into_iter()) - .collect::<Vec<_>>(); - - let arguments = [path.as_ptr()]; - - loop { - // SAFETY: - // winapi call to format the error message - let length = unsafe { - FormatMessageW( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, - std::ptr::null_mut(), - err_num as DWORD, - lang_id as DWORD, - buf.as_mut_ptr(), - buf.len() as DWORD, - arguments.as_ptr() as _, - ) - }; - - if length == 0 { - // SAFETY: - // winapi call to get the last error message - let err_num = unsafe { GetLastError() }; - if err_num == ERROR_INSUFFICIENT_BUFFER { - buf.resize(buf.len() * 2, 0); - continue; - } - - // Something went wrong, just return the original error. - return e.to_string(); - } - - let msg = String::from_utf16_lossy(&buf[..length as usize]); - return msg; - } - } - _ => e.to_string(), - } -} - -#[op(v8)] -fn op_ffi_load<FP, 'scope>( - scope: &mut v8::HandleScope<'scope>, - state: &mut deno_core::OpState, - args: FfiLoadArgs, -) -> Result<(ResourceId, serde_v8::Value<'scope>), AnyError> -where - FP: FfiPermissions + 'static, -{ - let path = args.path; - - check_unstable(state, "Deno.dlopen"); - let permissions = state.borrow_mut::<FP>(); - permissions.check(Some(&PathBuf::from(&path)))?; - - let lib = Library::open(&path).map_err(|e| { - dlopen::Error::OpeningLibraryError(std::io::Error::new( - std::io::ErrorKind::Other, - format_error(e, path), - )) - })?; - let mut resource = DynamicLibraryResource { - lib, - symbols: HashMap::new(), - }; - let obj = v8::Object::new(scope); - - for (symbol_key, foreign_symbol) in args.symbols { - match foreign_symbol { - ForeignSymbol::ForeignStatic(_) => { - // No-op: Statics will be handled separately and are not part of the Rust-side resource. - } - ForeignSymbol::ForeignFunction(foreign_fn) => { - let symbol = match &foreign_fn.name { - Some(symbol) => symbol, - None => &symbol_key, - }; - // 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. - let fn_ptr = - // 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) => Err(generic_error(format!( - "Failed to register symbol {}: {}", - symbol, err - ))), - }?; - let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); - let cif = libffi::middle::Cif::new( - foreign_fn - .parameters - .clone() - .into_iter() - .map(libffi::middle::Type::from), - foreign_fn.result.into(), - ); - - let func_key = v8::String::new(scope, &symbol_key).unwrap(); - let sym = Box::new(Symbol { - cif, - ptr, - parameter_types: foreign_fn.parameters, - result_type: foreign_fn.result, - can_callback: foreign_fn.callback, - }); - - resource.symbols.insert(symbol_key, sym.clone()); - match foreign_fn.non_blocking { - // Generate functions for synchronous calls. - Some(false) | None => { - let function = make_sync_fn(scope, sym); - obj.set(scope, func_key.into(), function.into()); - } - // This optimization is not yet supported for non-blocking calls. - _ => {} - }; - } - } - } - - let rid = state.resource_table.add(resource); - Ok(( - rid, - serde_v8::Value { - v8_value: obj.into(), - }, - )) -} - -fn needs_unwrap(rv: NativeType) -> bool { - matches!( - rv, - NativeType::Function - | NativeType::Pointer - | NativeType::Buffer - | NativeType::I64 - | NativeType::ISize - | NativeType::U64 - | NativeType::USize - ) -} - -fn is_i64(rv: NativeType) -> bool { - matches!(rv, NativeType::I64 | NativeType::ISize) -} - -// Create a JavaScript function for synchronous FFI call to -// the given symbol. -fn make_sync_fn<'s>( - scope: &mut v8::HandleScope<'s>, - sym: Box<Symbol>, -) -> v8::Local<'s, v8::Function> { - let sym = Box::leak(sym); - let builder = v8::FunctionTemplate::builder( - |scope: &mut v8::HandleScope, - args: v8::FunctionCallbackArguments, - mut rv: v8::ReturnValue| { - let external: v8::Local<v8::External> = args.data().try_into().unwrap(); - // SAFETY: The pointer will not be deallocated until the function is - // garbage collected. - let symbol = unsafe { &*(external.value() as *const Symbol) }; - let needs_unwrap = match needs_unwrap(symbol.result_type) { - true => Some(args.get(symbol.parameter_types.len() as i32)), - false => None, - }; - match ffi_call_sync(scope, args, symbol) { - Ok(result) => { - 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(); - - 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) - }; - // 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) - }; - // SAFETY: We checked that type == U64 - let value = unsafe { result.u64_value }; - *bs = value; - } - } - None => { - // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. - let result = unsafe { result.to_v8(scope, symbol.result_type) }; - rv.set(result.v8_value); - } - } - } - Err(err) => { - deno_core::_ops::throw_type_error(scope, err.to_string()); - } - }; - }, - ) - .data(v8::External::new(scope, sym as *mut Symbol as *mut _).into()); - - let mut fast_call_alloc = None; - - let func = if fast_call::is_compatible(sym) { - let trampoline = fast_call::compile_trampoline(sym); - let func = builder.build_fast( - scope, - &fast_call::make_template(sym, &trampoline), - None, - ); - fast_call_alloc = Some(Box::into_raw(Box::new(trampoline))); - func - } else { - builder.build(scope) - }; - let func = func.get_function(scope).unwrap(); - - let weak = v8::Weak::with_finalizer( - scope, - func, - Box::new(move |_| { - // SAFETY: This is never called twice. pointer obtained - // from Box::into_raw, hence, satisfies memory layout requirements. - let _ = unsafe { Box::from_raw(sym) }; - if let Some(fast_call_ptr) = fast_call_alloc { - // fast-call compiled trampoline is unmapped when the MMAP handle is dropped - // SAFETY: This is never called twice. pointer obtained - // from Box::into_raw, hence, satisfies memory layout requirements. - let _ = unsafe { Box::from_raw(fast_call_ptr) }; - } - }), - ); - - weak.to_local(scope).unwrap() -} - -#[inline] -fn ffi_parse_bool_arg( - arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { - let bool_value = v8::Local::<v8::Boolean>::try_from(arg) - .map_err(|_| type_error("Invalid FFI u8 type, expected boolean"))? - .is_true(); - Ok(NativeValue { bool_value }) -} - -#[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. 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 - } else if arg.is_null() { - ptr::null_mut() - } else { - return Err(type_error( - "Invalid FFI pointer type, expected null, integer or BigInt", - )); - }; - Ok(NativeValue { pointer }) -} - -#[inline] -fn ffi_parse_buffer_arg( - scope: &mut v8::HandleScope, - arg: v8::Local<v8::Value>, -) -> Result<NativeValue, AnyError> { - // Order of checking: - // 1. ArrayBuffer: Fairly common and not supported by Fast API, optimise this case. - // 2. ArrayBufferView: Common and supported by Fast API - // 5. Null: Very uncommon / can be represented by a 0. - - let pointer = if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(arg) { - if let Some(non_null) = value.data() { - non_null.as_ptr() - } else { - ptr::null_mut() - } - } 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") - })? - .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 { - ptr::null_mut() - } - } else if arg.is_null() { - ptr::null_mut() - } else { - return Err(type_error( - "Invalid FFI buffer type, expected null, 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 *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 - } else if arg.is_null() { - ptr::null_mut() - } 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>, - parameter_types: &[NativeType], -) -> Result<Vec<NativeValue>, AnyError> -where - 'scope: 'scope, -{ - if parameter_types.is_empty() { - return Ok(vec![]); - } - - let args = v8::Local::<v8::Array>::try_from(args.v8_value) - .map_err(|_| type_error("Invalid FFI parameters, expected Array"))?; - let mut ffi_args: Vec<NativeValue> = - Vec::with_capacity(parameter_types.len()); - - for (index, native_type) in parameter_types.iter().enumerate() { - let value = args.get_index(scope, index as u32).unwrap(); - match native_type { - NativeType::Bool => { - ffi_args.push(ffi_parse_bool_arg(value)?); - } - NativeType::U8 => { - ffi_args.push(ffi_parse_u8_arg(value)?); - } - NativeType::I8 => { - ffi_args.push(ffi_parse_i8_arg(value)?); - } - NativeType::U16 => { - ffi_args.push(ffi_parse_u16_arg(value)?); - } - NativeType::I16 => { - ffi_args.push(ffi_parse_i16_arg(value)?); - } - NativeType::U32 => { - ffi_args.push(ffi_parse_u32_arg(value)?); - } - NativeType::I32 => { - ffi_args.push(ffi_parse_i32_arg(value)?); - } - NativeType::U64 => { - ffi_args.push(ffi_parse_u64_arg(scope, value)?); - } - NativeType::I64 => { - ffi_args.push(ffi_parse_i64_arg(scope, value)?); - } - NativeType::USize => { - ffi_args.push(ffi_parse_usize_arg(scope, value)?); - } - NativeType::ISize => { - ffi_args.push(ffi_parse_isize_arg(scope, value)?); - } - NativeType::F32 => { - ffi_args.push(ffi_parse_f32_arg(value)?); - } - NativeType::F64 => { - ffi_args.push(ffi_parse_f64_arg(value)?); - } - NativeType::Buffer => { - ffi_args.push(ffi_parse_buffer_arg(scope, value)?); - } - NativeType::Pointer => { - ffi_args.push(ffi_parse_pointer_arg(scope, value)?); - } - NativeType::Function => { - ffi_args.push(ffi_parse_function_arg(scope, value)?); - } - NativeType::Void => { - unreachable!(); - } - } - } - - Ok(ffi_args) -} - -// A one-off synchronous FFI call. -fn ffi_call_sync<'scope>( - scope: &mut v8::HandleScope<'scope>, - args: v8::FunctionCallbackArguments, - symbol: &Symbol, -) -> Result<NativeValue, AnyError> -where - 'scope: 'scope, -{ - let Symbol { - parameter_types, - result_type, - cif, - ptr: fun_ptr, - .. - } = symbol; - let mut ffi_args: Vec<NativeValue> = - Vec::with_capacity(parameter_types.len()); - - for (index, native_type) in parameter_types.iter().enumerate() { - let value = args.get(index as i32); - match native_type { - NativeType::Bool => { - ffi_args.push(ffi_parse_bool_arg(value)?); - } - NativeType::U8 => { - ffi_args.push(ffi_parse_u8_arg(value)?); - } - NativeType::I8 => { - ffi_args.push(ffi_parse_i8_arg(value)?); - } - NativeType::U16 => { - ffi_args.push(ffi_parse_u16_arg(value)?); - } - NativeType::I16 => { - ffi_args.push(ffi_parse_i16_arg(value)?); - } - NativeType::U32 => { - ffi_args.push(ffi_parse_u32_arg(value)?); - } - NativeType::I32 => { - ffi_args.push(ffi_parse_i32_arg(value)?); - } - NativeType::U64 => { - ffi_args.push(ffi_parse_u64_arg(scope, value)?); - } - NativeType::I64 => { - ffi_args.push(ffi_parse_i64_arg(scope, value)?); - } - NativeType::USize => { - ffi_args.push(ffi_parse_usize_arg(scope, value)?); - } - NativeType::ISize => { - ffi_args.push(ffi_parse_isize_arg(scope, value)?); - } - NativeType::F32 => { - ffi_args.push(ffi_parse_f32_arg(value)?); - } - NativeType::F64 => { - ffi_args.push(ffi_parse_f64_arg(value)?); - } - NativeType::Buffer => { - ffi_args.push(ffi_parse_buffer_arg(scope, value)?); - } - NativeType::Pointer => { - ffi_args.push(ffi_parse_pointer_arg(scope, value)?); - } - NativeType::Function => { - ffi_args.push(ffi_parse_function_arg(scope, value)?); - } - NativeType::Void => { - unreachable!(); - } - } - } - let call_args: Vec<Arg> = ffi_args.iter().map(Arg::new).collect(); - // SAFETY: types in the `Cif` match the actual calling convention and - // 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::Pointer | NativeType::Function | NativeType::Buffer => { - NativeValue { - pointer: cif.call::<*mut c_void>(*fun_ptr, &call_args), - } - } - }) - } -} - -fn ffi_call( - call_args: Vec<NativeValue>, - cif: &libffi::middle::Cif, - fun_ptr: libffi::middle::CodePtr, - parameter_types: &[NativeType], - result_type: NativeType, -) -> Result<NativeValue, AnyError> { - let call_args: Vec<Arg> = call_args - .iter() - .enumerate() - .map(|(index, ffi_arg)| { - // SAFETY: the union field is initialized - unsafe { ffi_arg.as_arg(*parameter_types.get(index).unwrap()) } - }) - .collect(); - - // SAFETY: types in the `Cif` match the actual calling convention and - // 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::Pointer | NativeType::Function | NativeType::Buffer => { - NativeValue { - pointer: cif.call::<*mut c_void>(fun_ptr, &call_args), - } - } - }) - } -} - -struct UnsafeCallbackResource { - cancel: Rc<CancelHandle>, - // Closure is never directly touched, but it keeps the C callback alive - // until `close()` method is called. - #[allow(dead_code)] - closure: libffi::middle::Closure<'static>, - info: *mut CallbackInfo, -} - -impl Resource for UnsafeCallbackResource { - fn name(&self) -> Cow<str> { - "unsafecallback".into() - } - - fn close(self: Rc<Self>) { - self.cancel.cancel(); - // 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 info = Box::from_raw(self.info); - let isolate = info.isolate.as_mut().unwrap(); - let _ = v8::Global::from_raw(isolate, info.callback); - let _ = v8::Global::from_raw(isolate, info.context); - } - } -} - -struct CallbackInfo { - pub parameters: Vec<NativeType>, - pub result: NativeType, - pub async_work_sender: mpsc::UnboundedSender<PendingFfiAsyncWork>, - pub callback: NonNull<v8::Function>, - pub context: NonNull<v8::Context>, - pub isolate: *mut v8::Isolate, - pub waker: Option<Waker>, -} - -unsafe extern "C" fn deno_ffi_callback( - _cif: &libffi::low::ffi_cif, - result: &mut c_void, - args: *const *const c_void, - info: &CallbackInfo, -) { - LOCAL_ISOLATE_POINTER.with(|s| { - if ptr::eq(*s.borrow(), info.isolate) { - // Own isolate thread, okay to call directly - do_ffi_callback(info, result, args); - } else { - let async_work_sender = &info.async_work_sender; - // SAFETY: Safe as this function blocks until `do_ffi_callback` completes and a response message is received. - let result: &'static mut c_void = std::mem::transmute(result); - let info: &'static CallbackInfo = std::mem::transmute(info); - let (response_sender, response_receiver) = sync_channel::<()>(0); - let fut = Box::new(move || { - do_ffi_callback(info, result, args); - response_sender.send(()).unwrap(); - }); - async_work_sender.unbounded_send(fut).unwrap(); - if let Some(waker) = info.waker.as_ref() { - // Make sure event loop wakes up to receive our message before we start waiting for a response. - waker.wake_by_ref(); - } - response_receiver.recv().unwrap(); - } - }); -} - -unsafe fn do_ffi_callback( - info: &CallbackInfo, - result: &mut c_void, - args: *const *const c_void, -) { - let callback: NonNull<v8::Function> = info.callback; - let context: NonNull<v8::Context> = info.context; - let isolate: *mut v8::Isolate = info.isolate; - let isolate = &mut *isolate; - let callback = v8::Global::from_raw(isolate, callback); - let context = std::mem::transmute::< - NonNull<v8::Context>, - v8::Local<v8::Context>, - >(context); - // Call from main thread. If this callback is being triggered due to a - // function call coming from Deno itself, then this callback will build - // ontop of that stack. - // If this callback is being triggered outside of Deno (for example from a - // signal handler) then this will either create an empty new stack if - // Deno currently has nothing running and is waiting for promises to resolve, - // or will (very incorrectly) build ontop of whatever stack exists. - // The callback will even be called through from a `while (true)` liveloop, but - // 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 scope = &mut v8::HandleScope::new(&mut cb_scope); - let func = callback.open(scope); - let result = result as *mut c_void; - let vals: &[*const c_void] = - std::slice::from_raw_parts(args, info.parameters.len() as usize); - - let mut params: Vec<v8::Local<v8::Value>> = vec![]; - for (native_type, val) in info.parameters.iter().zip(vals) { - let value: v8::Local<v8::Value> = match native_type { - NativeType::Bool => { - let value = *((*val) as *const bool); - v8::Boolean::new(scope, value).into() - } - NativeType::F32 => { - let value = *((*val) as *const f32); - v8::Number::new(scope, value as f64).into() - } - NativeType::F64 => { - let value = *((*val) as *const f64); - v8::Number::new(scope, value).into() - } - NativeType::I8 => { - let value = *((*val) as *const i8); - v8::Integer::new(scope, value as i32).into() - } - NativeType::U8 => { - let value = *((*val) as *const u8); - v8::Integer::new_from_unsigned(scope, value as u32).into() - } - NativeType::I16 => { - let value = *((*val) as *const i16); - v8::Integer::new(scope, value as i32).into() - } - NativeType::U16 => { - let value = *((*val) as *const u16); - v8::Integer::new_from_unsigned(scope, value as u32).into() - } - NativeType::I32 => { - let value = *((*val) as *const i32); - v8::Integer::new(scope, value).into() - } - NativeType::U32 => { - let value = *((*val) as *const u32); - v8::Integer::new_from_unsigned(scope, value).into() - } - NativeType::I64 | NativeType::ISize => { - let result = *((*val) as *const i64); - 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() - } - } - NativeType::U64 | NativeType::USize => { - 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() - } - } - 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() - } else { - v8::Number::new(scope, result as f64).into() - } - } - NativeType::Void => unreachable!(), - }; - params.push(value); - } - - let recv = v8::undefined(scope); - let call_result = func.call(scope, recv.into(), ¶ms); - std::mem::forget(callback); - - if call_result.is_none() { - // JS function threw an exception. Set the return value to zero and return. - // The exception continue propagating up the call chain when the event loop - // resumes. - match info.result { - NativeType::Bool => { - *(result as *mut bool) = false; - } - NativeType::U32 | NativeType::I32 => { - // zero is equal for signed and unsigned alike - *(result as *mut u32) = 0; - } - NativeType::F32 => { - *(result as *mut f32) = 0.0; - } - NativeType::F64 => { - *(result as *mut f64) = 0.0; - } - NativeType::U8 | NativeType::I8 => { - // zero is equal for signed and unsigned alike - *(result as *mut u8) = 0; - } - NativeType::U16 | NativeType::I16 => { - // zero is equal for signed and unsigned alike - *(result as *mut u16) = 0; - } - NativeType::Pointer - | NativeType::Buffer - | NativeType::Function - | NativeType::U64 - | NativeType::I64 => { - *(result as *mut usize) = 0; - } - NativeType::Void => { - // nop - } - _ => { - unreachable!(); - } - }; - - return; - } - let value = call_result.unwrap(); - - match info.result { - NativeType::Bool => { - let value = if let Ok(value) = v8::Local::<v8::Boolean>::try_from(value) { - value.is_true() - } else { - value.boolean_value(scope) - }; - *(result as *mut bool) = value; - } - NativeType::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; - } - NativeType::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; - } - NativeType::F64 => { - 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; - } - NativeType::Pointer | NativeType::Buffer | NativeType::Function => { - let pointer = if let Ok(value) = - v8::Local::<v8::ArrayBufferView>::try_from(value) - { - let byte_offset = value.byte_offset(); - let backing_store = 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 - } 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() - } 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 - }; - *(result as *mut *const u8) = pointer; - } - NativeType::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; - } - NativeType::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; - } - NativeType::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; - } - NativeType::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; - } - NativeType::U32 => { - 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; - } - NativeType::I64 => { - 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(scope) - .expect("Unable to deserialize result parameter.") - as i64; - } - } - NativeType::U64 => { - 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(scope) - .expect("Unable to deserialize result parameter.") - as u64; - } - } - NativeType::Void => { - // nop - } - _ => { - unreachable!(); - } - }; -} - -#[derive(Deserialize)] -struct RegisterCallbackArgs { - parameters: Vec<NativeType>, - result: NativeType, -} - -#[op(v8)] -fn op_ffi_unsafe_callback_create<FP, 'scope>( - state: &mut deno_core::OpState, - scope: &mut v8::HandleScope<'scope>, - args: RegisterCallbackArgs, - cb: serde_v8::Value<'scope>, -) -> Result<serde_v8::Value<'scope>, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafeCallback"); - let permissions = state.borrow_mut::<FP>(); - permissions.check(None)?; - - let v8_value = cb.v8_value; - let cb = v8::Local::<v8::Function>::try_from(v8_value)?; - - let isolate: *mut v8::Isolate = &mut *scope as &mut v8::Isolate; - LOCAL_ISOLATE_POINTER.with(|s| { - if s.borrow().is_null() { - s.replace(isolate); - } - }); - - let async_work_sender = - state.borrow_mut::<FfiState>().async_work_sender.clone(); - let callback = v8::Global::new(scope, cb).into_raw(); - let current_context = scope.get_current_context(); - let context = v8::Global::new(scope, current_context).into_raw(); - - let info: *mut CallbackInfo = Box::leak(Box::new(CallbackInfo { - parameters: args.parameters.clone(), - result: args.result, - async_work_sender, - callback, - context, - isolate, - waker: None, - })); - let cif = Cif::new( - args.parameters.into_iter().map(libffi::middle::Type::from), - libffi::middle::Type::from(args.result), - ); - - // SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists. - let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, unsafe { - info.as_ref().unwrap() - }); - let ptr = *closure.code_ptr() as usize; - let resource = UnsafeCallbackResource { - cancel: CancelHandle::new_rc(), - closure, - info, - }; - 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 array = v8::Array::new(scope, 2); - array.set_index(scope, 0, rid_local.into()); - array.set_index(scope, 1, ptr_local); - let array_value: v8::Local<v8::Value> = array.into(); - - Ok(array_value.into()) -} - -#[op(v8)] -fn op_ffi_call_ptr<FP, 'scope>( - scope: &mut v8::HandleScope<'scope>, - state: Rc<RefCell<deno_core::OpState>>, - pointer: usize, - def: ForeignFunction, - parameters: serde_v8::Value<'scope>, -) -> Result<serde_v8::Value<'scope>, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - { - let mut state = state.borrow_mut(); - let permissions = state.borrow_mut::<FP>(); - permissions.check(None)?; - }; - - let symbol = PtrSymbol::new(pointer, &def); - let call_args = ffi_parse_args(scope, parameters, &def.parameters)?; - - let result = ffi_call( - call_args, - &symbol.cif, - symbol.ptr, - &def.parameters, - def.result, - )?; - // 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) -} - -impl Future for CallbackInfo { - type Output = (); - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll<Self::Output> { - // Always replace the waker to make sure it's bound to the proper Future. - self.waker.replace(cx.waker().clone()); - // The future for the CallbackInfo never resolves: It can only be canceled. - Poll::Pending - } -} - -#[op] -fn op_ffi_unsafe_callback_ref( - state: Rc<RefCell<deno_core::OpState>>, - rid: ResourceId, -) -> Result<impl Future<Output = Result<(), AnyError>>, AnyError> { - let state = state.borrow(); - let callback_resource = - state.resource_table.get::<UnsafeCallbackResource>(rid)?; - - Ok(async move { - let info: &mut CallbackInfo = - // SAFETY: CallbackInfo pointer stays valid as long as the resource is still alive. - unsafe { callback_resource.info.as_mut().unwrap() }; - // Ignore cancellation rejection - let _ = info - .into_future() - .or_cancel(callback_resource.cancel.clone()) - .await; - Ok(()) - }) -} - -#[op(fast)] -fn op_ffi_unsafe_callback_unref( - state: &mut deno_core::OpState, - rid: u32, -) -> Result<(), AnyError> { - state - .resource_table - .get::<UnsafeCallbackResource>(rid)? - .cancel - .cancel(); - Ok(()) -} - -#[op(v8)] -fn op_ffi_call_ptr_nonblocking<'scope, FP>( - scope: &mut v8::HandleScope<'scope>, - state: Rc<RefCell<deno_core::OpState>>, - pointer: usize, - def: ForeignFunction, - parameters: serde_v8::Value<'scope>, -) -> Result<impl Future<Output = Result<Value, AnyError>>, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - { - let mut state = state.borrow_mut(); - let permissions = state.borrow_mut::<FP>(); - permissions.check(None)?; - }; - - let symbol = PtrSymbol::new(pointer, &def); - let call_args = ffi_parse_args(scope, parameters, &def.parameters)?; - - let join_handle = tokio::task::spawn_blocking(move || { - let PtrSymbol { cif, ptr } = symbol.clone(); - ffi_call(call_args, &cif, ptr, &def.parameters, def.result) - }); - - Ok(async move { - let result = join_handle - .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) }) - }) -} - -#[op(v8)] -fn op_ffi_get_static<'scope>( - scope: &mut v8::HandleScope<'scope>, - state: &mut deno_core::OpState, - rid: ResourceId, - name: String, - static_type: NativeType, -) -> Result<serde_v8::Value<'scope>, AnyError> { - let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?; - - let data_ptr = resource.get_static(name)?; - - Ok(match static_type { - NativeType::Void => { - return Err(type_error("Invalid FFI static type 'void'")); - } - NativeType::Bool => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const bool) }; - let boolean: v8::Local<v8::Value> = - v8::Boolean::new(scope, result).into(); - boolean.into() - } - NativeType::U8 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const u8) }; - let number: v8::Local<v8::Value> = - v8::Integer::new_from_unsigned(scope, result as u32).into(); - number.into() - } - NativeType::I8 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const i8) }; - let number: v8::Local<v8::Value> = - v8::Integer::new(scope, result as i32).into(); - number.into() - } - NativeType::U16 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const u16) }; - let number: v8::Local<v8::Value> = - v8::Integer::new_from_unsigned(scope, result as u32).into(); - number.into() - } - NativeType::I16 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const i16) }; - let number: v8::Local<v8::Value> = - v8::Integer::new(scope, result as i32).into(); - number.into() - } - NativeType::U32 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const u32) }; - let number: v8::Local<v8::Value> = - v8::Integer::new_from_unsigned(scope, result).into(); - number.into() - } - NativeType::I32 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const i32) }; - let number: v8::Local<v8::Value> = v8::Integer::new(scope, result).into(); - number.into() - } - NativeType::U64 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const 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() - } - NativeType::I64 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_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() - }; - integer.into() - } - NativeType::USize => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const usize) }; - 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 integer: v8::Local<v8::Value> = - if !(MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&result) { - 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 - let result = unsafe { ptr::read_unaligned(data_ptr as *const f32) }; - let number: v8::Local<v8::Value> = - v8::Number::new(scope, result as f64).into(); - number.into() - } - NativeType::F64 => { - // SAFETY: ptr is user provided - let result = unsafe { ptr::read_unaligned(data_ptr as *const f64) }; - let number: v8::Local<v8::Value> = v8::Number::new(scope, result).into(); - 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() - } - }) -} - -/// A non-blocking FFI call. -#[op(v8)] -fn op_ffi_call_nonblocking<'scope>( - scope: &mut v8::HandleScope<'scope>, - state: Rc<RefCell<deno_core::OpState>>, - rid: ResourceId, - symbol: String, - parameters: serde_v8::Value<'scope>, -) -> Result<impl Future<Output = Result<Value, AnyError>> + 'static, AnyError> { - let symbol = { - let state = state.borrow(); - let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?; - let symbols = &resource.symbols; - *symbols - .get(&symbol) - .ok_or_else(|| type_error("Invalid FFI symbol name"))? - .clone() - }; - - let call_args = ffi_parse_args(scope, parameters, &symbol.parameter_types)?; - - let result_type = symbol.result_type; - let join_handle = tokio::task::spawn_blocking(move || { - let Symbol { - cif, - ptr, - parameter_types, - result_type, - .. - } = symbol.clone(); - ffi_call(call_args, &cif, ptr, ¶meter_types, result_type) - }); - - Ok(async move { - let result = join_handle - .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) }) - }) -} - -#[op(fast)] -fn op_ffi_ptr_of<FP>( - state: &mut deno_core::OpState, - buf: *const u8, - out: &mut [u32], -) -> Result<(), AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointer#of"); - let permissions = state.borrow_mut::<FP>(); - permissions.check(None)?; - - let outptr = out.as_ptr() as *mut usize; - let length = out.len(); - assert!( - length >= (std::mem::size_of::<usize>() / std::mem::size_of::<u32>()) - ); - assert_eq!(outptr as usize % std::mem::size_of::<usize>(), 0); - - // 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; - - Ok(()) -} - -unsafe extern "C" fn noop_deleter_callback( - _data: *mut c_void, - _byte_length: usize, - _deleter_data: *mut c_void, -) { -} - -#[op(v8)] -fn op_ffi_get_buf<FP, 'scope>( - scope: &mut v8::HandleScope<'scope>, - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, - len: usize, -) -> Result<serde_v8::Value<'scope>, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#arrayBuffer"); - - 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")); - } - - // 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. - let backing_store = unsafe { - v8::ArrayBuffer::new_backing_store_from_ptr( - ptr, - len, - noop_deleter_callback, - std::ptr::null_mut(), - ) - } - .make_shared(); - let array_buffer: v8::Local<v8::Value> = - v8::ArrayBuffer::with_backing_store(scope, &backing_store).into(); - Ok(array_buffer.into()) -} - -#[op(fast)] -fn op_ffi_buf_copy_into<FP>( - state: &mut deno_core::OpState, - src: usize, - offset: usize, - dst: &mut [u8], - len: usize, -) -> Result<(), AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#copyInto"); - - let permissions = state.borrow_mut::<FP>(); - permissions.check(None)?; - - 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. - // 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) }; - Ok(()) - } -} - -#[op(v8)] -fn op_ffi_cstr_read<FP, 'scope>( - scope: &mut v8::HandleScope<'scope>, - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<serde_v8::Value<'scope>, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getCString"); - - 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_str() - .map_err(|_| type_error("Invalid CString pointer, not valid UTF-8"))?; - let value: v8::Local<v8::Value> = v8::String::new(scope, cstr) - .ok_or_else(|| { - type_error("Invalid CString pointer, string exceeds max length") - })? - .into(); - Ok(value.into()) -} - -#[op(fast)] -fn op_ffi_read_bool<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<bool, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getBool"); - - 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) }) -} - -#[op(fast)] -fn op_ffi_read_u8<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<u32, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getUint8"); - - 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 }) -} - -#[op(fast)] -fn op_ffi_read_i8<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<i32, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getInt8"); - - 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 }) -} - -#[op(fast)] -fn op_ffi_read_u16<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<u32, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getUint16"); - - 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 - }) -} - -#[op(fast)] -fn op_ffi_read_i16<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<i32, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getInt16"); - - 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 - }) -} - -#[op(fast)] -fn op_ffi_read_u32<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<u32, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getUint32"); - - 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) as u32 - }) -} - -#[op(fast)] -fn op_ffi_read_i32<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<i32, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getInt32"); - - 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) as i32 - }) -} - -#[op] -fn op_ffi_read_u64<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, - out: &mut [u32], -) -> Result<(), AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getBigUint64"); - - let permissions = state.borrow_mut::<FP>(); - permissions.check(None)?; - - let outptr = out.as_mut_ptr() as *mut u64; - - assert!( - out.len() >= (std::mem::size_of::<u64>() / std::mem::size_of::<u32>()) - ); - 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) }; - - // SAFETY: Length and alignment of out slice were asserted to be correct. - unsafe { *outptr = value }; - Ok(()) -} - -#[op(fast)] -fn op_ffi_read_i64<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, - out: &mut [u32], -) -> Result<(), AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getBigUint64"); - - let permissions = state.borrow_mut::<FP>(); - permissions.check(None)?; - - let outptr = out.as_mut_ptr() as *mut i64; - - assert!( - out.len() >= (std::mem::size_of::<i64>() / std::mem::size_of::<u32>()) - ); - 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) }; - // SAFETY: Length and alignment of out slice were asserted to be correct. - unsafe { *outptr = value }; - Ok(()) -} - -#[op(fast)] -fn op_ffi_read_f32<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<f32, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getFloat32"); - - 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) }) -} - -#[op(fast)] -fn op_ffi_read_f64<FP>( - state: &mut deno_core::OpState, - ptr: usize, - offset: usize, -) -> Result<f64, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.UnsafePointerView#getFloat64"); - - 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) }) -} - -#[cfg(test)] -mod tests { - #[cfg(target_os = "windows")] - #[test] - fn test_format_error() { - use super::format_error; - - // BAD_EXE_FORMAT - let err = dlopen::Error::OpeningLibraryError( - std::io::Error::from_raw_os_error(0x000000C1), - ); - assert_eq!( - format_error(err, "foo.dll".to_string()), - "foo.dll is not a valid Win32 application.\r\n".to_string(), - ); - } -} |