summaryrefslogtreecommitdiff
path: root/ext/ffi/repr.rs
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2022-12-12 06:14:20 -0800
committerGitHub <noreply@github.com>2022-12-12 14:14:20 +0000
commita2db70a8d0820722695e9094c8dbc888bde1ffa3 (patch)
tree6885e2aee424ab0264a8b01df9b0bafbcfe85792 /ext/ffi/repr.rs
parent890b0653104388bfef60ca7ebd0af758caf5f0be (diff)
refactor(ext/ffi): split into multiple parts (#16950)
- [x] `dlfcn.rs` - `dlopen()`-related code. - [x] `turbocall.rs` - Call trampoline JIT compiler. - [x] `repr.rs` - Pointer representation. Home of the UnsafePointerView ops. - [x] `symbol.rs` - Function symbol related code. - [x] `callback.rs` - Home of `Deno.UnsafeCallback` ops. - [x] `ir.rs` - Intermediate representation for values. Home of the `NativeValue` type. - [x] `call.rs` - Generic call ops. Home to everything related to calling FFI symbols. - [x] `static.rs` - static symbol support I find easier to work with this setup, I eventually want to expand TurboCall to unroll type conversion loop in generic calls, generate code for individual symbols (lazy function pointers), etc.
Diffstat (limited to 'ext/ffi/repr.rs')
-rw-r--r--ext/ffi/repr.rs454
1 files changed, 454 insertions, 0 deletions
diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs
new file mode 100644
index 000000000..22cf03a6b
--- /dev/null
+++ b/ext/ffi/repr.rs
@@ -0,0 +1,454 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use crate::check_unstable;
+use crate::FfiPermissions;
+use deno_core::error::range_error;
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::op;
+use deno_core::serde_v8;
+use deno_core::v8;
+use std::ffi::c_char;
+use std::ffi::c_void;
+use std::ffi::CStr;
+use std::ptr;
+
+#[op(fast)]
+pub fn op_ffi_ptr_of<FP>(
+ state: &mut deno_core::OpState,
+ buf: *const u8,
+ out: &mut [u32],
+) -> Result<(), AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointer#of");
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let outptr = out.as_ptr() as *mut usize;
+ let length = out.len();
+ assert!(
+ length >= (std::mem::size_of::<usize>() / std::mem::size_of::<u32>())
+ );
+ assert_eq!(outptr as usize % std::mem::size_of::<usize>(), 0);
+
+ // SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned.
+ let out = unsafe { &mut *outptr };
+ *out = buf as usize;
+
+ Ok(())
+}
+
+unsafe extern "C" fn noop_deleter_callback(
+ _data: *mut c_void,
+ _byte_length: usize,
+ _deleter_data: *mut c_void,
+) {
+}
+
+#[op(v8)]
+pub fn op_ffi_get_buf<FP, 'scope>(
+ scope: &mut v8::HandleScope<'scope>,
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+ len: usize,
+) -> Result<serde_v8::Value<'scope>, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#arrayBuffer");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *mut c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid FFI pointer value, got nullptr"));
+ }
+
+ // SAFETY: Offset is user defined.
+ let ptr = unsafe { ptr.add(offset) };
+
+ // SAFETY: Trust the user to have provided a real pointer, and a valid matching size to it. Since this is a foreign pointer, we should not do any deletion.
+ let backing_store = unsafe {
+ v8::ArrayBuffer::new_backing_store_from_ptr(
+ ptr,
+ len,
+ noop_deleter_callback,
+ std::ptr::null_mut(),
+ )
+ }
+ .make_shared();
+ let array_buffer: v8::Local<v8::Value> =
+ v8::ArrayBuffer::with_backing_store(scope, &backing_store).into();
+ Ok(array_buffer.into())
+}
+
+#[op(fast)]
+pub fn op_ffi_buf_copy_into<FP>(
+ state: &mut deno_core::OpState,
+ src: usize,
+ offset: usize,
+ dst: &mut [u8],
+ len: usize,
+) -> Result<(), AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#copyInto");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ if dst.len() < len {
+ Err(range_error(
+ "Destination length is smaller than source length",
+ ))
+ } else {
+ let src = src as *const c_void;
+
+ // SAFETY: Offset is user defined.
+ let src = unsafe { src.add(offset) as *const u8 };
+
+ // SAFETY: src is user defined.
+ // dest is properly aligned and is valid for writes of len * size_of::<T>() bytes.
+ unsafe { ptr::copy::<u8>(src, dst.as_mut_ptr(), len) };
+ Ok(())
+ }
+}
+
+#[op(v8)]
+pub fn op_ffi_cstr_read<FP, 'scope>(
+ scope: &mut v8::HandleScope<'scope>,
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<serde_v8::Value<'scope>, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getCString");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid CString pointer, pointer is null"));
+ }
+
+ // SAFETY: Offset is user defined.
+ let ptr = unsafe { ptr.add(offset) };
+
+ // SAFETY: Pointer is user provided.
+ let cstr = unsafe { CStr::from_ptr(ptr as *const c_char) }
+ .to_str()
+ .map_err(|_| type_error("Invalid CString pointer, not valid UTF-8"))?;
+ let value: v8::Local<v8::Value> = v8::String::new(scope, cstr)
+ .ok_or_else(|| {
+ type_error("Invalid CString pointer, string exceeds max length")
+ })?
+ .into();
+ Ok(value.into())
+}
+
+#[op(fast)]
+pub fn op_ffi_read_bool<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<bool, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getBool");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid bool pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe { ptr::read_unaligned::<bool>(ptr.add(offset) as *const bool) })
+}
+
+#[op(fast)]
+pub fn op_ffi_read_u8<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<u32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getUint8");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid u8 pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe { ptr::read_unaligned::<u8>(ptr.add(offset) as *const u8) as u32 })
+}
+
+#[op(fast)]
+pub fn op_ffi_read_i8<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<i32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getInt8");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid i8 pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe { ptr::read_unaligned::<i8>(ptr.add(offset) as *const i8) as i32 })
+}
+
+#[op(fast)]
+pub fn op_ffi_read_u16<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<u32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getUint16");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid u16 pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe {
+ ptr::read_unaligned::<u16>(ptr.add(offset) as *const u16) as u32
+ })
+}
+
+#[op(fast)]
+pub fn op_ffi_read_i16<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<i32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getInt16");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid i16 pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe {
+ ptr::read_unaligned::<i16>(ptr.add(offset) as *const i16) as i32
+ })
+}
+
+#[op(fast)]
+pub fn op_ffi_read_u32<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<u32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getUint32");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid u32 pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe {
+ ptr::read_unaligned::<u32>(ptr.add(offset) as *const u32) as u32
+ })
+}
+
+#[op(fast)]
+pub fn op_ffi_read_i32<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<i32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getInt32");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid i32 pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe {
+ ptr::read_unaligned::<i32>(ptr.add(offset) as *const i32) as i32
+ })
+}
+
+#[op]
+pub fn op_ffi_read_u64<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+ out: &mut [u32],
+) -> Result<(), AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getBigUint64");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let outptr = out.as_mut_ptr() as *mut u64;
+
+ assert!(
+ out.len() >= (std::mem::size_of::<u64>() / std::mem::size_of::<u32>())
+ );
+ assert_eq!((outptr as usize % std::mem::size_of::<u64>()), 0);
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid u64 pointer, pointer is null"));
+ }
+
+ let value =
+ // SAFETY: ptr and offset are user provided.
+ unsafe { ptr::read_unaligned::<u64>(ptr.add(offset) as *const u64) };
+
+ // SAFETY: Length and alignment of out slice were asserted to be correct.
+ unsafe { *outptr = value };
+ Ok(())
+}
+
+#[op(fast)]
+pub fn op_ffi_read_i64<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+ out: &mut [u32],
+) -> Result<(), AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getBigUint64");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let outptr = out.as_mut_ptr() as *mut i64;
+
+ assert!(
+ out.len() >= (std::mem::size_of::<i64>() / std::mem::size_of::<u32>())
+ );
+ assert_eq!((outptr as usize % std::mem::size_of::<i64>()), 0);
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid i64 pointer, pointer is null"));
+ }
+
+ let value =
+ // SAFETY: ptr and offset are user provided.
+ unsafe { ptr::read_unaligned::<i64>(ptr.add(offset) as *const i64) };
+ // SAFETY: Length and alignment of out slice were asserted to be correct.
+ unsafe { *outptr = value };
+ Ok(())
+}
+
+#[op(fast)]
+pub fn op_ffi_read_f32<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<f32, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getFloat32");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid f32 pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe { ptr::read_unaligned::<f32>(ptr.add(offset) as *const f32) })
+}
+
+#[op(fast)]
+pub fn op_ffi_read_f64<FP>(
+ state: &mut deno_core::OpState,
+ ptr: usize,
+ offset: usize,
+) -> Result<f64, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.UnsafePointerView#getFloat64");
+
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(None)?;
+
+ let ptr = ptr as *const c_void;
+
+ if ptr.is_null() {
+ return Err(type_error("Invalid f64 pointer, pointer is null"));
+ }
+
+ // SAFETY: ptr and offset are user provided.
+ Ok(unsafe { ptr::read_unaligned::<f64>(ptr.add(offset) as *const f64) })
+}