summaryrefslogtreecommitdiff
path: root/ext/ffi/lib.rs
diff options
context:
space:
mode:
authorElias Sjögreen <eliassjogreen1@gmail.com>2021-12-15 15:41:49 +0100
committerGitHub <noreply@github.com>2021-12-15 15:41:49 +0100
commitee49cce726c27cdcb372b9dba7f2deab840d9e6b (patch)
treed1a9a843eb5fba782a7cff5e50cb973ee3806225 /ext/ffi/lib.rs
parent4d176b7b7c11aabc584bee45423f108ea47faefe (diff)
feat(ext/ffi): implement UnsafePointer and UnsafePointerView (#12828)
Diffstat (limited to 'ext/ffi/lib.rs')
-rw-r--r--ext/ffi/lib.rs269
1 files changed, 251 insertions, 18 deletions
diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs
index a21601e3b..de4ff3ef2 100644
--- a/ext/ffi/lib.rs
+++ b/ext/ffi/lib.rs
@@ -1,10 +1,12 @@
// Copyright 2021 the Deno authors. All rights reserved. MIT license.
use deno_core::error::bad_resource_id;
+use deno_core::error::range_error;
use deno_core::error::AnyError;
use deno_core::include_js_files;
use deno_core::op_async;
use deno_core::op_sync;
+use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::Extension;
@@ -15,12 +17,15 @@ use deno_core::ZeroCopyBuf;
use dlopen::raw::Library;
use libffi::middle::Arg;
use serde::Deserialize;
+use serde::Serialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::c_void;
+use std::ffi::CStr;
use std::path::Path;
use std::path::PathBuf;
+use std::ptr;
use std::rc::Rc;
pub struct Unstable(pub bool);
@@ -38,7 +43,7 @@ fn check_unstable(state: &OpState, api_name: &str) {
}
pub trait FfiPermissions {
- fn check(&mut self, path: &Path) -> Result<(), AnyError>;
+ fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError>;
}
#[derive(Clone)]
@@ -108,6 +113,18 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
("op_ffi_load", op_sync(op_ffi_load::<P>)),
("op_ffi_call", op_sync(op_ffi_call)),
("op_ffi_call_nonblocking", op_async(op_ffi_call_nonblocking)),
+ ("op_ffi_ptr_of", op_sync(op_ffi_ptr_of::<P>)),
+ ("op_ffi_buf_copy_into", op_sync(op_ffi_buf_copy_into::<P>)),
+ ("op_ffi_cstr_read", op_sync(op_ffi_cstr_read::<P>)),
+ ("op_ffi_read_u8", op_sync(op_ffi_read_u8::<P>)),
+ ("op_ffi_read_i8", op_sync(op_ffi_read_i8::<P>)),
+ ("op_ffi_read_u16", op_sync(op_ffi_read_u16::<P>)),
+ ("op_ffi_read_i16", op_sync(op_ffi_read_i16::<P>)),
+ ("op_ffi_read_u32", op_sync(op_ffi_read_u32::<P>)),
+ ("op_ffi_read_i32", op_sync(op_ffi_read_i32::<P>)),
+ ("op_ffi_read_u64", op_sync(op_ffi_read_u64::<P>)),
+ ("op_ffi_read_f32", op_sync(op_ffi_read_f32::<P>)),
+ ("op_ffi_read_f64", op_sync(op_ffi_read_f64::<P>)),
])
.state(move |state| {
// Stolen from deno_webgpu, is there a better option?
@@ -133,7 +150,7 @@ enum NativeType {
ISize,
F32,
F64,
- Buffer,
+ Pointer,
}
impl From<NativeType> for libffi::middle::Type {
@@ -152,7 +169,7 @@ impl From<NativeType> for libffi::middle::Type {
NativeType::ISize => libffi::middle::Type::isize(),
NativeType::F32 => libffi::middle::Type::f32(),
NativeType::F64 => libffi::middle::Type::f64(),
- NativeType::Buffer => libffi::middle::Type::pointer(),
+ NativeType::Pointer => libffi::middle::Type::pointer(),
}
}
}
@@ -172,7 +189,7 @@ union NativeValue {
isize_value: isize,
f32_value: f32,
f64_value: f64,
- buffer: *const u8,
+ pointer: *const u8,
}
impl NativeValue {
@@ -215,12 +232,25 @@ impl NativeValue {
NativeType::F64 => Self {
f64_value: value_as_f64(value),
},
- NativeType::Buffer => unreachable!(),
+ NativeType::Pointer => {
+ if value.is_null() {
+ Self {
+ pointer: ptr::null(),
+ }
+ } else {
+ Self {
+ pointer: u64::from(
+ serde_json::from_value::<U32x2>(value)
+ .expect("Expected ffi arg value to be a tuple of the low and high bits of a pointer address")
+ ) as *const u8,
+ }
+ }
+ }
}
}
fn buffer(ptr: *const u8) -> Self {
- Self { buffer: ptr }
+ Self { pointer: ptr }
}
unsafe fn as_arg(&self, native_type: NativeType) -> Arg {
@@ -238,7 +268,7 @@ impl NativeValue {
NativeType::ISize => Arg::new(&self.isize_value),
NativeType::F32 => Arg::new(&self.f32_value),
NativeType::F64 => Arg::new(&self.f64_value),
- NativeType::Buffer => Arg::new(&self.buffer),
+ NativeType::Pointer => Arg::new(&self.pointer),
}
}
}
@@ -267,6 +297,21 @@ fn value_as_f64(value: Value) -> f64 {
.expect("Expected ffi arg value to be a float")
}
+#[derive(Serialize, Deserialize, Debug)]
+struct U32x2(u32, u32);
+
+impl From<u64> for U32x2 {
+ fn from(value: u64) -> Self {
+ Self((value >> 32) as u32, value as u32)
+ }
+}
+
+impl From<U32x2> for u64 {
+ fn from(value: U32x2) -> Self {
+ (value.0 as u64) << 32 | value.1 as u64
+ }
+}
+
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct ForeignFunction {
@@ -367,7 +412,7 @@ where
check_unstable(state, "Deno.dlopen");
let permissions = state.borrow_mut::<FP>();
- permissions.check(&PathBuf::from(&path))?;
+ permissions.check(Some(&PathBuf::from(&path)))?;
let lib = Library::open(&path).map_err(|e| {
dlopen::Error::OpeningLibraryError(std::io::Error::new(
@@ -394,25 +439,30 @@ struct FfiCallArgs {
rid: ResourceId,
symbol: String,
parameters: Vec<Value>,
- buffers: Vec<ZeroCopyBuf>,
+ buffers: Vec<Option<ZeroCopyBuf>>,
}
fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result<Value, AnyError> {
- let buffers: Vec<&[u8]> =
- args.buffers.iter().map(|buffer| &buffer[..]).collect();
+ let buffers: Vec<Option<&[u8]>> = args
+ .buffers
+ .iter()
+ .map(|buffer| buffer.as_ref().map(|buffer| &buffer[..]))
+ .collect();
let native_values = symbol
.parameter_types
.iter()
.zip(args.parameters.into_iter())
.map(|(&native_type, value)| {
- if let NativeType::Buffer = native_type {
- let idx: usize = value_as_uint(value);
- let ptr = buffers[idx].as_ptr();
- NativeValue::buffer(ptr)
- } else {
- NativeValue::new(native_type, value)
+ if let NativeType::Pointer = native_type {
+ if let Some(idx) = value.as_u64() {
+ if let Some(&Some(buf)) = buffers.get(idx as usize) {
+ return NativeValue::buffer(buf.as_ptr());
+ }
+ }
}
+
+ NativeValue::new(native_type, value)
})
.collect::<Vec<_>>();
@@ -465,7 +515,11 @@ fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result<Value, AnyError> {
NativeType::F64 => {
json!(unsafe { symbol.cif.call::<f64>(symbol.ptr, &call_args) })
}
- NativeType::Buffer => unreachable!(),
+ NativeType::Pointer => {
+ json!(U32x2::from(unsafe {
+ symbol.cif.call::<*const u8>(symbol.ptr, &call_args)
+ } as u64))
+ }
})
}
@@ -507,6 +561,185 @@ async fn op_ffi_call_nonblocking(
.unwrap()
}
+fn op_ffi_ptr_of<FP>(
+ state: &mut deno_core::OpState,
+ buf: ZeroCopyBuf,
+ _: (),
+) -> Result<U32x2, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(U32x2::from(buf.as_ptr() as u64))
+}
+
+fn op_ffi_buf_copy_into<FP>(
+ state: &mut deno_core::OpState,
+ (src, mut dst, len): (U32x2, ZeroCopyBuf, usize),
+ _: (),
+) -> Result<(), AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ 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 = u64::from(src) as *const u8;
+ unsafe { ptr::copy(src, dst.as_mut_ptr(), len) };
+ Ok(())
+ }
+}
+
+fn op_ffi_cstr_read<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<String, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = u64::from(ptr) as *const i8;
+ Ok(unsafe { CStr::from_ptr(ptr) }.to_str()?.to_string())
+}
+
+fn op_ffi_read_u8<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<u8, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u8) })
+}
+
+fn op_ffi_read_i8<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<i8, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i8) })
+}
+
+fn op_ffi_read_u16<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<u16, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u16) })
+}
+
+fn op_ffi_read_i16<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<i16, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i16) })
+}
+
+fn op_ffi_read_u32<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<u32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u32) })
+}
+
+fn op_ffi_read_i32<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<i32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i32) })
+}
+
+fn op_ffi_read_u64<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<U32x2, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(U32x2::from(unsafe {
+ ptr::read_unaligned(u64::from(ptr) as *const u64)
+ }))
+}
+
+fn op_ffi_read_f32<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<f32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const f32) })
+}
+
+fn op_ffi_read_f64<FP>(
+ state: &mut deno_core::OpState,
+ ptr: U32x2,
+ _: (),
+) -> Result<f64, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const f64) })
+}
+
#[cfg(test)]
mod tests {
#[cfg(target_os = "windows")]