diff options
Diffstat (limited to 'ext/ffi/lib.rs')
-rw-r--r-- | ext/ffi/lib.rs | 1228 |
1 files changed, 866 insertions, 362 deletions
diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 8a1a75b1d..691d44460 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,16 +1,19 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use deno_core::error::bad_resource_id; +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::Future; use deno_core::include_js_files; use deno_core::op; -use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; +use deno_core::serde_v8; +use deno_core::v8; use deno_core::Extension; use deno_core::OpState; use deno_core::Resource; @@ -18,6 +21,8 @@ use deno_core::ResourceId; use deno_core::ZeroCopyBuf; use dlopen::raw::Library; use libffi::middle::Arg; +use libffi::middle::Cif; +use libffi::raw::*; use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; @@ -31,6 +36,10 @@ use std::path::PathBuf; use std::ptr; use std::rc::Rc; +thread_local! { + static IS_ISOLATE_THREAD: RefCell<bool> = RefCell::new(false); +} + pub struct Unstable(pub bool); fn check_unstable(state: &OpState, api_name: &str) { @@ -66,6 +75,32 @@ struct Symbol { unsafe impl Send for Symbol {} unsafe impl Sync for Symbol {} +#[derive(Clone)] +struct PtrSymbol { + cif: libffi::middle::Cif, + ptr: libffi::middle::CodePtr, +} + +impl PtrSymbol { + fn new(fn_ptr: u64, 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)] +unsafe impl Send for PtrSymbol {} +unsafe impl Sync for PtrSymbol {} + struct DynamicLibraryResource { lib: Library, symbols: HashMap<String, Symbol>, @@ -87,6 +122,7 @@ impl DynamicLibraryResource { name: String, foreign_fn: ForeignFunction, ) -> Result<(), AnyError> { + IS_ISOLATE_THREAD.with(|s| s.replace(true)); let symbol = match &foreign_fn.name { Some(symbol) => symbol, None => &name, @@ -163,6 +199,7 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension { op_ffi_read_u64::decl::<P>(), op_ffi_read_f32::decl::<P>(), op_ffi_read_f64::decl::<P>(), + op_ffi_unsafe_callback_create::decl::<P>(), ]) .state(move |state| { // Stolen from deno_webgpu, is there a better option? @@ -172,6 +209,8 @@ 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 { @@ -189,6 +228,7 @@ enum NativeType { F32, F64, Pointer, + Function {}, } impl From<NativeType> for libffi::middle::Type { @@ -208,10 +248,13 @@ impl From<NativeType> for libffi::middle::Type { NativeType::F32 => libffi::middle::Type::f32(), NativeType::F64 => libffi::middle::Type::f64(), NativeType::Pointer => libffi::middle::Type::pointer(), + 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: (), @@ -231,68 +274,9 @@ union NativeValue { } impl NativeValue { - fn new(native_type: NativeType, value: Value) -> Result<Self, AnyError> { - let value = match native_type { - NativeType::Void => Self { void_value: () }, - NativeType::U8 => Self { - u8_value: value_as_uint::<u8>(value)?, - }, - NativeType::I8 => Self { - i8_value: value_as_int::<i8>(value)?, - }, - NativeType::U16 => Self { - u16_value: value_as_uint::<u16>(value)?, - }, - NativeType::I16 => Self { - i16_value: value_as_int::<i16>(value)?, - }, - NativeType::U32 => Self { - u32_value: value_as_uint::<u32>(value)?, - }, - NativeType::I32 => Self { - i32_value: value_as_int::<i32>(value)?, - }, - NativeType::U64 => Self { - u64_value: value_as_uint::<u64>(value)?, - }, - NativeType::I64 => Self { - i64_value: value_as_int::<i64>(value)?, - }, - NativeType::USize => Self { - usize_value: value_as_uint::<usize>(value)?, - }, - NativeType::ISize => Self { - isize_value: value_as_int::<isize>(value)?, - }, - NativeType::F32 => Self { - f32_value: value_as_f32(value)?, - }, - NativeType::F64 => Self { - f64_value: value_as_f64(value)?, - }, - NativeType::Pointer => { - if value.is_null() { - Self { - pointer: ptr::null(), - } - } else { - Self { - pointer: u64::from(serde_json::from_value::<U32x2>(value)?) - as *const u8, - } - } - } - }; - Ok(value) - } - - fn buffer(ptr: *const u8) -> Self { - Self { pointer: ptr } - } - unsafe fn as_arg(&self, native_type: NativeType) -> Arg { match native_type { - NativeType::Void => Arg::new(&self.void_value), + NativeType::Void => unreachable!(), NativeType::U8 => Arg::new(&self.u8_value), NativeType::I8 => Arg::new(&self.i8_value), NativeType::U16 => Arg::new(&self.u16_value), @@ -305,56 +289,123 @@ impl NativeValue { NativeType::ISize => Arg::new(&self.isize_value), NativeType::F32 => Arg::new(&self.f32_value), NativeType::F64 => Arg::new(&self.f64_value), - NativeType::Pointer => Arg::new(&self.pointer), + NativeType::Pointer | NativeType::Function {} => Arg::new(&self.pointer), } } -} - -fn value_as_uint<T: TryFrom<u64>>(value: Value) -> Result<T, AnyError> { - if value.is_array() { - let value = U32x2::try_from(value)?; - return T::try_from(u64::from(value)).map_err(|_| type_error(format!("Found U32x2 FFI argument but it could not be converted to an unsigned integer, got {:?}", value))); - } - - match value.as_u64().and_then(|v| T::try_from(v).ok()) { - Some(value) => Ok(value), - None => Err(type_error(format!( - "Expected FFI argument to be an unsigned integer, but got {:?}", - value - ))), - } -} -fn value_as_int<T: TryFrom<i64>>(value: Value) -> Result<T, AnyError> { - if value.is_array() { - let value = U32x2::try_from(value)?; - return T::try_from(u64::from(value) as i64).map_err(|_| type_error(format!("Found U32x2 FFI argument but it could not be converted to a signed integer, got {:?}", value))); + // 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::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 => { + json!(U32x2::from(self.u64_value)) + } + NativeType::I64 => { + json!(U32x2::from(self.i64_value as u64)) + } + NativeType::USize => { + json!(U32x2::from(self.usize_value as u64)) + } + NativeType::ISize => { + json!(U32x2::from(self.isize_value as u64)) + } + NativeType::F32 => Value::from(self.f32_value), + NativeType::F64 => Value::from(self.f64_value), + NativeType::Pointer | NativeType::Function {} => { + json!(U32x2::from(self.pointer as u64)) + } + } } - match value.as_i64().and_then(|v| T::try_from(v).ok()) { - Some(value) => Ok(value), - None => Err(type_error(format!( - "Expected FFI argument to be a signed integer, but got {:?}", - value - ))), + // SAFETY: native_type must correspond to the type of value represented by the union field + 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::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 local_value: v8::Local<v8::Value> = + v8::BigInt::new_from_u64(scope, self.u64_value).into(); + local_value.into() + } + NativeType::I64 => { + let local_value: v8::Local<v8::Value> = + v8::BigInt::new_from_i64(scope, self.i64_value).into(); + local_value.into() + } + NativeType::USize => { + let local_value: v8::Local<v8::Value> = + v8::BigInt::new_from_u64(scope, self.usize_value as u64).into(); + local_value.into() + } + NativeType::ISize => { + let local_value: v8::Local<v8::Value> = + v8::BigInt::new_from_i64(scope, self.isize_value as i64).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::Function {} => { + let local_value: v8::Local<v8::Value> = + v8::BigInt::new_from_u64(scope, self.pointer as u64).into(); + local_value.into() + } + } } } -fn value_as_f32(value: Value) -> Result<f32, AnyError> { - Ok(value_as_f64(value)? as f32) -} +unsafe impl Send for NativeValue {} -fn value_as_f64(value: Value) -> Result<f64, AnyError> { - match value.as_f64() { - Some(value) => Ok(value), - None => Err(type_error(format!( - "Expected FFI argument to be a double, but got {:?}", - value - ))), - } -} - -#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[derive(Serialize, Debug, Clone, Copy)] struct U32x2(u32, u32); impl From<u64> for U32x2 { @@ -363,31 +414,6 @@ impl From<u64> for U32x2 { } } -impl From<U32x2> for u64 { - fn from(value: U32x2) -> Self { - (value.0 as u64) << 32 | value.1 as u64 - } -} - -impl TryFrom<Value> for U32x2 { - type Error = AnyError; - - fn try_from(value: Value) -> Result<Self, Self::Error> { - if let Some(value) = value.as_array() { - if let Some(hi) = value[0].as_u64() { - if let Some(lo) = value[1].as_u64() { - return Ok(U32x2(hi as u32, lo as u32)); - } - } - } - - Err(type_error(format!( - "Expected FFI argument to be a signed integer, but got {:?}", - value - ))) - } -} - #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct ForeignFunction { @@ -535,322 +561,796 @@ where Ok(state.resource_table.add(resource)) } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct FfiCallArgs { - rid: ResourceId, - symbol: String, - parameters: Vec<Value>, - buffers: Vec<Option<ZeroCopyBuf>>, -} +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![]); + } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct FfiCallPtrArgs { - pointer: U32x2, - def: ForeignFunction, - parameters: Vec<Value>, - buffers: Vec<Option<ZeroCopyBuf>>, -} + 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()); -impl From<FfiCallPtrArgs> for FfiCallArgs { - fn from(args: FfiCallPtrArgs) -> Self { - FfiCallArgs { - rid: 0, - symbol: String::new(), - parameters: args.parameters, - buffers: args.buffers, + for (index, native_type) in parameter_types.iter().enumerate() { + let value = args.get_index(scope, index as u32).unwrap(); + match native_type { + NativeType::Void => { + unreachable!(); + } + NativeType::U8 => { + let value = value + .uint32_value(scope) + .ok_or_else(|| type_error("Invalid FFI u8 type, expected number"))? + as u8; + ffi_args.push(NativeValue { u8_value: value }); + } + NativeType::I8 => { + let value = value + .int32_value(scope) + .ok_or_else(|| type_error("Invalid FFI i8 type, expected number"))? + as i8; + ffi_args.push(NativeValue { i8_value: value }); + } + NativeType::U16 => { + let value = value + .uint32_value(scope) + .ok_or_else(|| type_error("Invalid FFI u16 type, expected number"))? + as u16; + ffi_args.push(NativeValue { u16_value: value }); + } + NativeType::I16 => { + let value = value + .int32_value(scope) + .ok_or_else(|| type_error("Invalid FFI i16 type, expected number"))? + as i16; + ffi_args.push(NativeValue { i16_value: value }); + } + NativeType::U32 => { + let value = value + .uint32_value(scope) + .ok_or_else(|| type_error("Invalid FFI u32 type, expected number"))? + as u32; + ffi_args.push(NativeValue { u32_value: value }); + } + NativeType::I32 => { + let value = value + .int32_value(scope) + .ok_or_else(|| type_error("Invalid FFI i32 type, expected number"))? + as i32; + ffi_args.push(NativeValue { i32_value: value }); + } + NativeType::U64 => { + let value: u64 = if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value)?; + value.u64_value().0 + } else { + value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI u64 type, expected number") + })? as u64 + }; + ffi_args.push(NativeValue { u64_value: value }); + } + NativeType::I64 => { + let value: i64 = if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value)?; + value.i64_value().0 + } else { + value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI i64 type, expected number") + })? as i64 + }; + ffi_args.push(NativeValue { i64_value: value }); + } + NativeType::USize => { + let value: usize = if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value)?; + value.u64_value().0 as usize + } else { + value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI usize type, expected number") + })? as usize + }; + ffi_args.push(NativeValue { usize_value: value }); + } + NativeType::ISize => { + let value: isize = if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value)?; + value.i64_value().0 as isize + } else { + value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI isize type, expected number") + })? as isize + }; + ffi_args.push(NativeValue { isize_value: value }); + } + NativeType::F32 => { + let value = value + .number_value(scope) + .ok_or_else(|| type_error("Invalid FFI f32 type, expected number"))? + as f32; + ffi_args.push(NativeValue { f32_value: value }); + } + NativeType::F64 => { + let value = value + .number_value(scope) + .ok_or_else(|| type_error("Invalid FFI f64 type, expected number"))? + as f64; + ffi_args.push(NativeValue { f64_value: value }); + } + NativeType::Pointer => { + if value.is_null() { + let value: *const u8 = ptr::null(); + ffi_args.push(NativeValue { pointer: value }) + } else if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value).unwrap(); + let value = value.u64_value().0 as *const u8; + ffi_args.push(NativeValue { pointer: value }); + } else if value.is_array_buffer() || value.is_array_buffer_view() { + let value: ZeroCopyBuf = serde_v8::from_v8(scope, value)?; + let value: &[u8] = &value[..]; + ffi_args.push(NativeValue { + pointer: value.as_ptr(), + }); + } else { + return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer, or ArrayBufferView")); + } + } + NativeType::Function {} => { + if value.is_null() { + let value: *const u8 = ptr::null(); + ffi_args.push(NativeValue { pointer: value }) + } else if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value).unwrap(); + let value = value.u64_value().0 as *const u8; + ffi_args.push(NativeValue { pointer: value }); + } else { + return Err(type_error( + "Invalid FFI function type, expected null, or BigInt", + )); + } + } } } + + Ok(ffi_args) } -impl FfiCallPtrArgs { - fn get_symbol(&self) -> Symbol { - let fn_ptr: u64 = self.pointer.into(); - let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); - let cif = libffi::middle::Cif::new( - self - .def - .parameters - .clone() - .into_iter() - .map(libffi::middle::Type::from), - self.def.result.into(), - ); +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)| unsafe { + ffi_arg.as_arg(*parameter_types.get(index).unwrap()) + }) + .collect(); - Symbol { - cif, - ptr, - parameter_types: self.def.parameters.clone(), - result_type: self.def.result, + Ok(match result_type { + NativeType::Void => NativeValue { + void_value: unsafe { cif.call::<()>(fun_ptr, &call_args) }, + }, + NativeType::U8 => NativeValue { + u8_value: unsafe { cif.call::<u8>(fun_ptr, &call_args) }, + }, + NativeType::I8 => NativeValue { + i8_value: unsafe { cif.call::<i8>(fun_ptr, &call_args) }, + }, + NativeType::U16 => NativeValue { + u16_value: unsafe { cif.call::<u16>(fun_ptr, &call_args) }, + }, + NativeType::I16 => NativeValue { + i16_value: unsafe { cif.call::<i16>(fun_ptr, &call_args) }, + }, + NativeType::U32 => NativeValue { + u32_value: unsafe { cif.call::<u32>(fun_ptr, &call_args) }, + }, + NativeType::I32 => NativeValue { + i32_value: unsafe { cif.call::<i32>(fun_ptr, &call_args) }, + }, + NativeType::U64 => NativeValue { + u64_value: unsafe { cif.call::<u64>(fun_ptr, &call_args) }, + }, + NativeType::I64 => NativeValue { + i64_value: unsafe { cif.call::<i64>(fun_ptr, &call_args) }, + }, + NativeType::USize => NativeValue { + usize_value: unsafe { cif.call::<usize>(fun_ptr, &call_args) }, + }, + NativeType::ISize => NativeValue { + isize_value: unsafe { cif.call::<isize>(fun_ptr, &call_args) }, + }, + NativeType::F32 => NativeValue { + f32_value: unsafe { cif.call::<f32>(fun_ptr, &call_args) }, + }, + NativeType::F64 => NativeValue { + f64_value: unsafe { cif.call::<f64>(fun_ptr, &call_args) }, + }, + NativeType::Pointer | NativeType::Function {} => NativeValue { + pointer: unsafe { cif.call::<*const u8>(fun_ptr, &call_args) }, + }, + }) +} + +struct UnsafeCallbackResource { + // 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: *const CallbackInfo, +} + +impl Resource for UnsafeCallbackResource { + fn name(&self) -> Cow<str> { + "unsafecallback".into() + } + + fn close(self: Rc<Self>) { + // 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 as *mut CallbackInfo); + let isolate = info.isolate.as_mut().unwrap(); + v8::Global::from_raw(isolate, info.callback); + v8::Global::from_raw(isolate, info.context); } } } -fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result<Value, AnyError> { - let buffers: Vec<Option<&[u8]>> = args - .buffers - .iter() - .map(|buffer| buffer.as_ref().map(|buffer| &buffer[..])) - .collect(); - - let mut native_values: Vec<NativeValue> = vec![]; +struct CallbackInfo { + pub callback: NonNull<v8::Function>, + pub context: NonNull<v8::Context>, + pub isolate: *mut v8::Isolate, +} - for (&native_type, value) in symbol - .parameter_types - .iter() - .zip(args.parameters.into_iter()) - { - match native_type { - NativeType::Pointer => match value.as_u64() { - Some(idx) => { - let buf = buffers - .get(idx as usize) - .ok_or_else(|| { - generic_error(format!("No buffer present at index {}", idx)) - })? - .unwrap(); - native_values.push(NativeValue::buffer(buf.as_ptr())); - } - _ => { - let value = NativeValue::new(native_type, value)?; - native_values.push(value); - } - }, +unsafe extern "C" fn deno_ffi_callback( + cif: &libffi::low::ffi_cif, + result: &mut c_void, + args: *const *const c_void, + info: &CallbackInfo, +) { + let isolate = &mut *info.isolate; + let callback = v8::Global::from_raw(isolate, info.callback); + let context = std::mem::transmute::< + NonNull<v8::Context>, + v8::Local<v8::Context>, + >(info.context); + IS_ISOLATE_THREAD.with(|is_event_loop_thread| { + if !(*is_event_loop_thread.borrow()) { + // Call from another thread, not yet supported. + eprintln!( + "Calling Deno FFI's callbacks from other threads is not supported" + ); + std::process::exit(1); + } + }); + // 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 mut scope = v8::HandleScope::new(&mut cb_scope); + let func = callback.open(&mut scope); + let result = result as *mut c_void; + let repr: &[*mut ffi_type] = + std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); + let vals: &[*const c_void] = + std::slice::from_raw_parts(args, cif.nargs as usize); + + let mut params: Vec<v8::Local<v8::Value>> = vec![]; + for (repr, val) in repr.iter().zip(vals) { + let value: v8::Local<v8::Value> = match (*(*repr)).type_ as _ { + FFI_TYPE_FLOAT => { + let value = *((*val) as *const f32); + v8::Number::new(&mut scope, value as f64).into() + } + FFI_TYPE_DOUBLE => { + let value = *((*val) as *const f64); + v8::Number::new(&mut scope, value).into() + } + FFI_TYPE_SINT8 => { + let value = *((*val) as *const i8); + v8::Integer::new(&mut scope, value as i32).into() + } + FFI_TYPE_UINT8 => { + let value = *((*val) as *const u8); + v8::Integer::new_from_unsigned(&mut scope, value as u32).into() + } + FFI_TYPE_SINT16 => { + let value = *((*val) as *const i16); + v8::Integer::new(&mut scope, value as i32).into() + } + FFI_TYPE_UINT16 => { + let value = *((*val) as *const u16); + v8::Integer::new_from_unsigned(&mut scope, value as u32).into() + } + FFI_TYPE_INT | FFI_TYPE_SINT32 => { + let value = *((*val) as *const i32); + v8::Integer::new(&mut scope, value).into() + } + FFI_TYPE_UINT32 => { + let value = *((*val) as *const u32); + v8::Integer::new_from_unsigned(&mut scope, value).into() + } + FFI_TYPE_SINT64 => { + let result = *((*val) as *const i64); + v8::BigInt::new_from_i64(&mut scope, result).into() + } + FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 => { + let result = *((*val) as *const u64); + v8::BigInt::new_from_u64(&mut scope, result).into() + } + FFI_TYPE_VOID => v8::undefined(&mut scope).into(), _ => { - let value = NativeValue::new(native_type, value)?; - native_values.push(value); + unreachable!() } - } + }; + params.push(value); } - let call_args = symbol - .parameter_types - .iter() - .zip(native_values.iter()) - .map(|(&native_type, native_value)| unsafe { - native_value.as_arg(native_type) - }) - .collect::<Vec<_>>(); + let recv = v8::undefined(&mut scope); + let call_result = func.call(&mut 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 (*cif.rtype).type_ as _ { + FFI_TYPE_INT | FFI_TYPE_SINT32 | FFI_TYPE_UINT32 => { + // zero is equal for signed and unsigned alike + *(result as *mut u32) = 0; + } + FFI_TYPE_FLOAT => { + *(result as *mut f32) = 0.0; + } + FFI_TYPE_DOUBLE => { + *(result as *mut f64) = 0.0; + } + FFI_TYPE_SINT8 | FFI_TYPE_UINT8 => { + // zero is equal for signed and unsigned alike + *(result as *mut u8) = 0; + } + FFI_TYPE_SINT16 | FFI_TYPE_UINT16 => { + // zero is equal for signed and unsigned alike + *(result as *mut u16) = 0; + } + FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 + | FFI_TYPE_SINT64 => { + *(result as *mut u64) = 0; + } + FFI_TYPE_VOID => { + // nop + } + _ => { + unreachable!(); + } + }; - Ok(match symbol.result_type { - NativeType::Void => { - json!(unsafe { symbol.cif.call::<()>(symbol.ptr, &call_args) }) - } - NativeType::U8 => { - json!(unsafe { symbol.cif.call::<u8>(symbol.ptr, &call_args) }) + return; + } + let value = call_result.unwrap(); + + match (*cif.rtype).type_ as _ { + FFI_TYPE_INT | FFI_TYPE_SINT32 => { + *(result as *mut i32) = value + .int32_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as i32; } - NativeType::I8 => { - json!(unsafe { symbol.cif.call::<i8>(symbol.ptr, &call_args) }) + FFI_TYPE_FLOAT => { + *(result as *mut f32) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as f32; } - NativeType::U16 => { - json!(unsafe { symbol.cif.call::<u16>(symbol.ptr, &call_args) }) + FFI_TYPE_DOUBLE => { + *(result as *mut f64) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter."); } - NativeType::I16 => { - json!(unsafe { symbol.cif.call::<i16>(symbol.ptr, &call_args) }) + FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { + if value.is_array_buffer() | value.is_array_buffer_view() { + let value: ZeroCopyBuf = serde_v8::from_v8(&mut scope, value) + .expect("Unable to deserialize result parameter."); + let value: &[u8] = &value[..]; + *(result as *mut *const u8) = value.as_ptr(); + } else if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value).unwrap(); + *(result as *mut u64) = value.u64_value().0; + } else if value.is_null() { + *(result as *mut *const c_void) = ptr::null(); + } else { + // Fallthrough: Probably someone returned a number but this could + // also be eg. a string. This is essentially UB. + *(result as *mut u64) = value + .integer_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u64; + } } - NativeType::U32 => { - json!(unsafe { symbol.cif.call::<u32>(symbol.ptr, &call_args) }) + FFI_TYPE_SINT8 => { + *(result as *mut i8) = value + .int32_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as i8; } - NativeType::I32 => { - json!(unsafe { symbol.cif.call::<i32>(symbol.ptr, &call_args) }) + FFI_TYPE_UINT8 => { + *(result as *mut u8) = value + .uint32_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u8; } - NativeType::U64 => { - json!(U32x2::from(unsafe { - symbol.cif.call::<u64>(symbol.ptr, &call_args) - })) + FFI_TYPE_SINT16 => { + *(result as *mut i16) = value + .int32_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as i16; } - NativeType::I64 => { - json!(U32x2::from(unsafe { - symbol.cif.call::<i64>(symbol.ptr, &call_args) - } as u64)) + FFI_TYPE_UINT16 => { + *(result as *mut u16) = value + .uint32_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u16; } - NativeType::USize => { - json!(U32x2::from(unsafe { - symbol.cif.call::<usize>(symbol.ptr, &call_args) - } as u64)) + FFI_TYPE_UINT32 => { + *(result as *mut u32) = value + .uint32_value(&mut scope) + .expect("Unable to deserialize result parameter."); } - NativeType::ISize => { - json!(U32x2::from(unsafe { - symbol.cif.call::<isize>(symbol.ptr, &call_args) - } as u64)) + FFI_TYPE_SINT64 => { + if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value).unwrap(); + *(result as *mut i64) = value.i64_value().0; + } else { + *(result as *mut i64) = value + .integer_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as i64; + } } - NativeType::F32 => { - json!(unsafe { symbol.cif.call::<f32>(symbol.ptr, &call_args) }) + FFI_TYPE_UINT64 => { + if value.is_big_int() { + let value = v8::Local::<v8::BigInt>::try_from(value).unwrap(); + *(result as *mut u64) = value.u64_value().0; + } else { + *(result as *mut u64) = value + .integer_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u64; + } } - NativeType::F64 => { - json!(unsafe { symbol.cif.call::<f64>(symbol.ptr, &call_args) }) + FFI_TYPE_VOID => { + // nop } - NativeType::Pointer => { - json!(U32x2::from(unsafe { - symbol.cif.call::<*const u8>(symbol.ptr, &call_args) - } as u64)) + _ => { + unreachable!(); } - }) + }; } -#[op] -fn op_ffi_call_ptr<FP>( +#[derive(Deserialize)] +struct RegisterCallbackArgs { + parameters: Vec<NativeType>, + result: NativeType, +} + +#[op(v8)] +fn op_ffi_unsafe_callback_create<FP, 'scope>( state: &mut deno_core::OpState, - args: FfiCallPtrArgs, -) -> Result<Value, AnyError> + 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.UnsafeFnPointer#call"); - + check_unstable(state, "Deno.UnsafeCallback"); let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let symbol = args.get_symbol(); - ffi_call(args.into(), &symbol) + 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; + 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 = Box::leak(Box::new(CallbackInfo { + callback, + context, + isolate, + })); + let cif = Cif::new( + args.parameters.into_iter().map(libffi::middle::Type::from), + libffi::middle::Type::from(args.result), + ); + + let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, info); + let ptr = *closure.code_ptr() as usize as u64; + let resource = UnsafeCallbackResource { closure, info }; + let rid = state.resource_table.add(resource); + + let rid_local = v8::Integer::new_from_unsigned(scope, rid); + let ptr_local = v8::BigInt::new_from_u64(scope, ptr); + let array = v8::Array::new(scope, 2); + array.set_index(scope, 0, rid_local.into()); + array.set_index(scope, 1, ptr_local.into()); + let array_value: v8::Local<v8::Value> = array.into(); + + Ok(array_value.into()) } -#[op] -async fn op_ffi_call_ptr_nonblocking<FP>( +#[op(v8)] +fn op_ffi_call_ptr<FP, 'scope>( + scope: &mut v8::HandleScope<'scope>, state: Rc<RefCell<deno_core::OpState>>, - args: FfiCallPtrArgs, -) -> Result<Value, AnyError> + pointer: u64, + 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 = args.get_symbol(); - tokio::task::spawn_blocking(move || ffi_call(args.into(), &symbol)) - .await - .unwrap() + 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) } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct FfiGetArgs { - rid: ResourceId, - name: String, - r#type: NativeType, +#[op(v8)] +fn op_ffi_call_ptr_nonblocking<'scope, FP>( + scope: &mut v8::HandleScope<'scope>, + state: Rc<RefCell<deno_core::OpState>>, + pointer: u64, + 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] -fn op_ffi_get_static( +#[op(v8)] +fn op_ffi_get_static<'scope>( + scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, - args: FfiGetArgs, -) -> Result<Value, AnyError> { - let resource = state - .resource_table - .get::<DynamicLibraryResource>(args.rid)?; + 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(args.name)? as *const u8; + let data_ptr = resource.get_static(name)? as *const u8; - Ok(match args.r#type { + Ok(match static_type { NativeType::Void => { - unreachable!(); + return Err(type_error("Invalid FFI static type 'void'")); } NativeType::U8 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const u8) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const u8) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::I8 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const i8) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const i8) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::U16 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const u16) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const u16) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::I16 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const i16) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const i16) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::U32 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const u32) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const u32) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::I32 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const i32) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const i32) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::U64 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const u64) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const u64) }; + let big_int = v8::BigInt::new_from_u64(scope, result); + serde_v8::from_v8(scope, big_int.into())? } NativeType::I64 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const i64) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const i64) }; + let big_int = v8::BigInt::new_from_i64(scope, result); + serde_v8::from_v8(scope, big_int.into())? } NativeType::USize => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const usize) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const usize) }; + let big_int = v8::BigInt::new_from_u64(scope, result as u64); + serde_v8::from_v8(scope, big_int.into())? } NativeType::ISize => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const isize) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const isize) }; + let big_int = v8::BigInt::new_from_i64(scope, result as i64); + serde_v8::from_v8(scope, big_int.into())? } NativeType::F32 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const f32) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const f32) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::F64 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const f64) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const f64) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } - NativeType::Pointer => { - json!(U32x2::from(data_ptr as *const u8 as u64)) + NativeType::Pointer | NativeType::Function {} => { + let result = data_ptr as *const u8 as u64; + let big_int = v8::BigInt::new_from_u64(scope, result); + serde_v8::from_v8(scope, big_int.into())? } }) } -#[op] -fn op_ffi_call( - state: &mut deno_core::OpState, - args: FfiCallArgs, -) -> Result<Value, AnyError> { - let resource = state - .resource_table - .get::<DynamicLibraryResource>(args.rid)?; - - let symbol = resource - .symbols - .get(&args.symbol) - .ok_or_else(bad_resource_id)?; - - ffi_call(args, symbol) +#[op(v8)] +fn op_ffi_call<'scope>( + scope: &mut v8::HandleScope<'scope>, + state: Rc<RefCell<deno_core::OpState>>, + rid: ResourceId, + symbol: String, + parameters: serde_v8::Value<'scope>, +) -> Result<serde_v8::Value<'scope>, AnyError> { + let symbol = { + let state = &mut state.borrow(); + let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?; + + resource + .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 = ffi_call( + call_args, + &symbol.cif, + symbol.ptr, + &symbol.parameter_types, + symbol.result_type, + )?; + // 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) }; + Ok(result) } /// A non-blocking FFI call. -#[op] -async fn op_ffi_call_nonblocking( +#[op(v8)] +fn op_ffi_call_nonblocking<'scope>( + scope: &mut v8::HandleScope<'scope>, state: Rc<RefCell<deno_core::OpState>>, - args: FfiCallArgs, -) -> Result<Value, AnyError> { - let resource = state - .borrow() - .resource_table - .get::<DynamicLibraryResource>(args.rid)?; - let symbols = &resource.symbols; - let symbol = symbols - .get(&args.symbol) - .ok_or_else(bad_resource_id)? - .clone(); - - tokio::task::spawn_blocking(move || ffi_call(args, &symbol)) - .await - .unwrap() + 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, + .. + } = 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] -fn op_ffi_ptr_of<FP>( +#[op(v8)] +fn op_ffi_ptr_of<FP, 'scope>( + scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, buf: ZeroCopyBuf, -) -> Result<U32x2, AnyError> +) -> Result<serde_v8::Value<'scope>, AnyError> where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointer#of"); - let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(U32x2::from(buf.as_ptr() as u64)) + let big_int: v8::Local<v8::Value> = + v8::BigInt::new_from_u64(scope, buf.as_ptr() as u64).into(); + Ok(big_int.into()) } #[op] fn op_ffi_buf_copy_into<FP>( state: &mut deno_core::OpState, - (src, mut dst, len): (U32x2, ZeroCopyBuf, usize), + src: u64, + mut dst: ZeroCopyBuf, + len: usize, ) -> Result<(), AnyError> where FP: FfiPermissions + 'static, @@ -865,7 +1365,7 @@ where "Destination length is smaller than source length", )) } else { - let src = u64::from(src) as *const u8; + let src = src as *const u8; unsafe { ptr::copy(src, dst.as_mut_ptr(), len) }; Ok(()) } @@ -874,7 +1374,7 @@ where #[op] fn op_ffi_cstr_read<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<String, AnyError> where FP: FfiPermissions + 'static, @@ -884,14 +1384,14 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - let ptr = u64::from(ptr) as *const c_char; + let ptr = ptr as *const c_char; Ok(unsafe { CStr::from_ptr(ptr) }.to_str()?.to_string()) } #[op] fn op_ffi_read_u8<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<u8, AnyError> where FP: FfiPermissions + 'static, @@ -901,13 +1401,13 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u8) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const u8) }) } #[op] fn op_ffi_read_i8<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<i8, AnyError> where FP: FfiPermissions + 'static, @@ -917,13 +1417,13 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i8) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const i8) }) } #[op] fn op_ffi_read_u16<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<u16, AnyError> where FP: FfiPermissions + 'static, @@ -933,13 +1433,13 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u16) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const u16) }) } #[op] fn op_ffi_read_i16<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<i16, AnyError> where FP: FfiPermissions + 'static, @@ -949,13 +1449,13 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i16) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const i16) }) } #[op] fn op_ffi_read_u32<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<u32, AnyError> where FP: FfiPermissions + 'static, @@ -965,13 +1465,13 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u32) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const u32) }) } #[op] fn op_ffi_read_i32<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<i32, AnyError> where FP: FfiPermissions + 'static, @@ -981,31 +1481,35 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i32) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const i32) }) } -#[op] -fn op_ffi_read_u64<FP>( +#[op(v8)] +fn op_ffi_read_u64<FP, 'scope>( + scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, - ptr: U32x2, -) -> Result<U32x2, AnyError> + ptr: u64, +) -> Result<serde_v8::Value<'scope>, AnyError> where FP: FfiPermissions + 'static, + 'scope: 'scope, { check_unstable(state, "Deno.UnsafePointerView#getBigUint64"); let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(U32x2::from(unsafe { - ptr::read_unaligned(u64::from(ptr) as *const u64) - })) + let result = unsafe { ptr::read_unaligned(ptr as *const u64) }; + + let big_int: v8::Local<v8::Value> = + v8::BigInt::new_from_u64(scope, result).into(); + Ok(big_int.into()) } #[op] fn op_ffi_read_f32<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<f32, AnyError> where FP: FfiPermissions + 'static, @@ -1015,13 +1519,13 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const f32) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const f32) }) } #[op] fn op_ffi_read_f64<FP>( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result<f64, AnyError> where FP: FfiPermissions + 'static, @@ -1031,7 +1535,7 @@ where let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const f64) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const f64) }) } #[cfg(test)] |