summaryrefslogtreecommitdiff
path: root/ext/ffi/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ffi/lib.rs')
-rw-r--r--ext/ffi/lib.rs1228
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(), &params);
+ 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, &parameter_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)]