From 71c0704ad74ceb536df7252553e628e9bbf6aa90 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 8 Jan 2025 14:32:20 +0530 Subject: [PATCH] feat: object wrap `UnsafePointerView` --- ext/ffi/00_ffi.js | 159 +---------- ext/ffi/lib.rs | 15 -- ext/ffi/repr.rs | 658 ++++++++++++++++++++++++---------------------- 3 files changed, 350 insertions(+), 482 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index d3b07dc373c53a..67f0a65a04129b 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -7,12 +7,9 @@ const { isTypedArray, } = core; import { - op_ffi_buf_copy_into, op_ffi_call_nonblocking, op_ffi_call_ptr, op_ffi_call_ptr_nonblocking, - op_ffi_cstr_read, - op_ffi_get_buf, op_ffi_get_static, op_ffi_load, op_ffi_ptr_create, @@ -21,21 +18,10 @@ import { op_ffi_ptr_of_exact, op_ffi_ptr_offset, op_ffi_ptr_value, - op_ffi_read_bool, - op_ffi_read_f32, - op_ffi_read_f64, - op_ffi_read_i16, - op_ffi_read_i32, - op_ffi_read_i64, - op_ffi_read_i8, - op_ffi_read_ptr, - op_ffi_read_u16, - op_ffi_read_u32, - op_ffi_read_u64, - op_ffi_read_u8, op_ffi_unsafe_callback_close, op_ffi_unsafe_callback_create, op_ffi_unsafe_callback_ref, + UnsafePointerView, } from "ext:core/ops"; const { ArrayBufferIsView, @@ -74,149 +60,6 @@ function getBufferSourceByteLength(source) { } return ArrayBufferPrototypeGetByteLength(source); } -class UnsafePointerView { - pointer; - - constructor(pointer) { - this.pointer = pointer; - } - - getBool(offset = 0) { - return op_ffi_read_bool( - this.pointer, - offset, - ); - } - - getUint8(offset = 0) { - return op_ffi_read_u8( - this.pointer, - offset, - ); - } - - getInt8(offset = 0) { - return op_ffi_read_i8( - this.pointer, - offset, - ); - } - - getUint16(offset = 0) { - return op_ffi_read_u16( - this.pointer, - offset, - ); - } - - getInt16(offset = 0) { - return op_ffi_read_i16( - this.pointer, - offset, - ); - } - - getUint32(offset = 0) { - return op_ffi_read_u32( - this.pointer, - offset, - ); - } - - getInt32(offset = 0) { - return op_ffi_read_i32( - this.pointer, - offset, - ); - } - - getBigUint64(offset = 0) { - return op_ffi_read_u64( - this.pointer, - // We return a BigInt, so the turbocall - // is forced to use BigInts everywhere. - BigInt(offset), - ); - } - - getBigInt64(offset = 0) { - return op_ffi_read_i64( - this.pointer, - // We return a BigInt, so the turbocall - // is forced to use BigInts everywhere. - BigInt(offset), - ); - } - - getFloat32(offset = 0) { - return op_ffi_read_f32( - this.pointer, - offset, - ); - } - - getFloat64(offset = 0) { - return op_ffi_read_f64( - this.pointer, - offset, - ); - } - - getPointer(offset = 0) { - return op_ffi_read_ptr( - this.pointer, - offset, - ); - } - - getCString(offset = 0) { - return op_ffi_cstr_read( - this.pointer, - offset, - ); - } - - static getCString(pointer, offset = 0) { - return op_ffi_cstr_read( - pointer, - offset, - ); - } - - getArrayBuffer(byteLength, offset = 0) { - return op_ffi_get_buf( - this.pointer, - offset, - byteLength, - ); - } - - static getArrayBuffer(pointer, byteLength, offset = 0) { - return op_ffi_get_buf( - pointer, - offset, - byteLength, - ); - } - - copyInto(destination, offset = 0) { - op_ffi_buf_copy_into( - this.pointer, - offset, - destination, - getBufferSourceByteLength(destination), - ); - } - - static copyInto(pointer, destination, offset = 0) { - op_ffi_buf_copy_into( - pointer, - offset, - destination, - getBufferSourceByteLength(destination), - ); - } -} const POINTER_TO_BUFFER_WEAK_MAP = new SafeWeakMap(); class UnsafePointer { diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 73ec7757abc4e9..cf43a02c7436df 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -87,21 +87,6 @@ deno_core::extension!(deno_ffi, op_ffi_ptr_of_exact

, op_ffi_ptr_offset

, op_ffi_ptr_value

, - op_ffi_get_buf

, - op_ffi_buf_copy_into

, - op_ffi_cstr_read

, - op_ffi_read_bool

, - op_ffi_read_u8

, - op_ffi_read_i8

, - op_ffi_read_u16

, - op_ffi_read_i16

, - op_ffi_read_u32

, - op_ffi_read_i32

, - op_ffi_read_u64

, - op_ffi_read_i64

, - op_ffi_read_f32

, - op_ffi_read_f64

, - op_ffi_read_ptr

, op_ffi_unsafe_callback_create

, op_ffi_unsafe_callback_close, op_ffi_unsafe_callback_ref, diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs index eea15f3e9771dd..4e4d43692c1af3 100644 --- a/ext/ffi/repr.rs +++ b/ext/ffi/repr.rs @@ -3,7 +3,9 @@ use crate::FfiPermissions; use deno_core::op2; use deno_core::v8; +use deno_core::GarbageCollected; use deno_core::OpState; +use deno_permissions::PermissionsContainer; use std::ffi::c_char; use std::ffi::c_void; use std::ffi::CStr; @@ -157,351 +159,389 @@ where Ok(ptr as usize) } -#[op2(stack_trace)] -pub fn op_ffi_get_buf( - scope: &mut v8::HandleScope<'scope>, - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, - #[number] len: usize, -) -> Result, ReprError> -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidArrayBuffer); +pub struct UnsafePointerView(*mut c_void); + +impl GarbageCollected for UnsafePointerView {} + +impl UnsafePointerView { + fn get_c_string<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + state: &mut OpState, + offset: isize, + ) -> Result, ReprError> { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidCString); + } + + let cstr = + // SAFETY: Pointer and offset are user provided. + unsafe { CStr::from_ptr(self.0.offset(offset) as *const c_char) }.to_bytes(); + let value = + v8::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal) + .ok_or_else(|| ReprError::CStringTooLong)?; + Ok(value) } - // SAFETY: Trust the user to have provided a real pointer, offset, 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.offset(offset), - len, - noop_deleter_callback, - ptr::null_mut(), - ) + fn get_array_buffer<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + state: &mut OpState, + offset: isize, + len: usize, + ) -> Result, ReprError> { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidArrayBuffer); + } + + // SAFETY: Trust the user to have provided a real pointer, offset, 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( + self.0.offset(offset), + len, + noop_deleter_callback, + ptr::null_mut(), + ) + } + .make_shared(); + let array_buffer = + v8::ArrayBuffer::with_backing_store(scope, &backing_store); + Ok(array_buffer) } - .make_shared(); - let array_buffer = v8::ArrayBuffer::with_backing_store(scope, &backing_store); - Ok(array_buffer) -} -#[op2(stack_trace)] -pub fn op_ffi_buf_copy_into( - state: &mut OpState, - src: *mut c_void, - #[number] offset: isize, - #[anybuffer] dst: &mut [u8], - #[number] len: usize, -) -> Result<(), ReprError> -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if src.is_null() { - Err(ReprError::InvalidArrayBuffer) - } else if dst.len() < len { - Err(ReprError::DestinationLengthTooShort) - } else { - let src = src as *const c_void; - - // SAFETY: src and offset are user defined. - // dest is properly aligned and is valid for writes of len * size_of::() bytes. - unsafe { - ptr::copy::(src.offset(offset) as *const u8, dst.as_mut_ptr(), len) - }; - Ok(()) + fn copy_into( + &self, + state: &mut OpState, + offset: isize, + dst: &mut [u8], + len: usize, + ) -> Result<(), ReprError> { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidArrayBuffer); + } else if dst.len() < len { + return Err(ReprError::DestinationLengthTooShort); + } else { + let src = self.0 as *const c_void; + + // SAFETY: src and offset are user defined. + // dest is properly aligned and is valid for writes of len * size_of::() bytes. + unsafe { + ptr::copy::(src.offset(offset) as *const u8, dst.as_mut_ptr(), len) + }; + Ok(()) + } } } -#[op2(stack_trace)] -pub fn op_ffi_cstr_read( - scope: &mut v8::HandleScope<'scope>, - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result, ReprError> -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidCString); +#[op2] +impl UnsafePointerView { + #[constructor] + #[cppgc] + fn new(ptr: *mut c_void) -> UnsafePointerView { + UnsafePointerView(ptr) } - let cstr = - // SAFETY: Pointer and offset are user provided. - unsafe { CStr::from_ptr(ptr.offset(offset) as *const c_char) }.to_bytes(); - let value = v8::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal) - .ok_or_else(|| ReprError::CStringTooLong)?; - Ok(value) -} - -#[op2(fast, stack_trace)] -pub fn op_ffi_read_bool( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidBool); + #[fast] + fn get_bool( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidU8); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const bool) + }) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const bool) }) -} - -#[op2(fast, stack_trace)] -pub fn op_ffi_read_u8( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidU8); + #[fast] + fn get_uint8( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidU8); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const u8) as u32 + }) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { - ptr::read_unaligned::(ptr.offset(offset) as *const u8) as u32 - }) -} - -#[op2(fast, stack_trace)] -pub fn op_ffi_read_i8( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidI8); + #[fast] + fn get_int8( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidI8); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const i8) as i32 + }) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { - ptr::read_unaligned::(ptr.offset(offset) as *const i8) as i32 - }) -} - -#[op2(fast, stack_trace)] -pub fn op_ffi_read_u16( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidU16); + #[fast] + fn get_uint16( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidU16); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const u16) as u32 + }) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { - ptr::read_unaligned::(ptr.offset(offset) as *const u16) as u32 - }) -} - -#[op2(fast, stack_trace)] -pub fn op_ffi_read_i16( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidI16); + #[fast] + fn get_int16( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidI16); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const i16) as i32 + }) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { - ptr::read_unaligned::(ptr.offset(offset) as *const i16) as i32 - }) -} - -#[op2(fast, stack_trace)] -pub fn op_ffi_read_u32( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidU32); + #[fast] + fn get_uint32( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidU32); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const u32) + }) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const u32) }) -} - -#[op2(fast, stack_trace)] -pub fn op_ffi_read_i32( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidI32); + #[fast] + fn get_int32( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidI32); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const i32) + }) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const i32) }) -} - -#[op2(fast, stack_trace)] -#[bigint] -pub fn op_ffi_read_u64( - state: &mut OpState, - ptr: *mut c_void, - // Note: The representation of 64-bit integers is function-wide. We cannot - // choose to take this parameter as a number while returning a bigint. - #[bigint] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidU64); + #[fast] + #[bigint] + fn get_big_uint64( + &self, + state: &mut OpState, + #[bigint] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidU64); + } + + let value = + // SAFETY: ptr and offset are user provided. + unsafe { ptr::read_unaligned::(self.0.offset(offset) as *const u64) }; + + Ok(value) } - let value = - // SAFETY: ptr and offset are user provided. - unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const u64) }; - - Ok(value) -} - -#[op2(fast, stack_trace)] -#[bigint] -pub fn op_ffi_read_i64( - state: &mut OpState, - ptr: *mut c_void, - // Note: The representation of 64-bit integers is function-wide. We cannot - // choose to take this parameter as a number while returning a bigint. - #[bigint] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; - - if ptr.is_null() { - return Err(ReprError::InvalidI64); + #[fast] + #[bigint] + fn get_big_int64( + &self, + state: &mut OpState, + #[bigint] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidI64); + } + + let value = + // SAFETY: ptr and offset are user provided. + unsafe { ptr::read_unaligned::(self.0.offset(offset) as *const i64) }; + // SAFETY: Length and alignment of out slice were asserted to be correct. + Ok(value) } - let value = - // SAFETY: ptr and offset are user provided. - unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const i64) }; - // SAFETY: Length and alignment of out slice were asserted to be correct. - Ok(value) -} - -#[op2(fast, stack_trace)] -pub fn op_ffi_read_f32( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; + #[fast] + fn get_float32( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidF32); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const f32) + }) + } - if ptr.is_null() { - return Err(ReprError::InvalidF32); + #[fast] + fn get_float64( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidF64); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::(self.0.offset(offset) as *const f64) + }) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const f32) }) -} + #[fast] + fn get_pointer( + &self, + state: &mut OpState, + #[number] offset: isize, + ) -> Result<*mut c_void, ReprError> { + let permissions = state.borrow_mut::(); + permissions.check_partial_no_path()?; + + if self.0.is_null() { + return Err(ReprError::InvalidPointer); + } + + // SAFETY: ptr and offset are user provided. + Ok(unsafe { + ptr::read_unaligned::<*mut c_void>( + self.0.offset(offset) as *const *mut c_void + ) + }) + } -#[op2(fast, stack_trace)] -pub fn op_ffi_read_f64( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; + fn get_c_string<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + state: &mut OpState, + #[number] offset: isize, + ) -> Result, ReprError> { + self.get_c_string(scope, state, offset) + } - if ptr.is_null() { - return Err(ReprError::InvalidF64); + #[static_method] + fn get_c_string<'s>( + scope: &mut v8::HandleScope<'s>, + state: &mut OpState, + ptr: *mut c_void, + #[number] offset: isize, + ) -> Result, ReprError> { + UnsafePointerView(ptr).get_c_string(scope, state, offset) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { ptr::read_unaligned::(ptr.offset(offset) as *const f64) }) -} + fn get_array_buffer<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + state: &mut OpState, + #[number] offset: isize, + #[number] len: usize, + ) -> Result, ReprError> { + self.get_array_buffer(scope, state, offset, len) + } -#[op2(fast, stack_trace)] -pub fn op_ffi_read_ptr( - state: &mut OpState, - ptr: *mut c_void, - #[number] offset: isize, -) -> Result<*mut c_void, ReprError> -where - FP: FfiPermissions + 'static, -{ - let permissions = state.borrow_mut::(); - permissions.check_partial_no_path()?; + #[static_method] + fn get_array_buffer<'s>( + scope: &mut v8::HandleScope<'s>, + state: &mut OpState, + ptr: *mut c_void, + #[number] offset: isize, + #[number] len: usize, + ) -> Result, ReprError> { + UnsafePointerView(ptr).get_array_buffer(scope, state, offset, len) + } - if ptr.is_null() { - return Err(ReprError::InvalidPointer); + fn copy_into( + &self, + state: &mut OpState, + #[number] offset: isize, + #[anybuffer] dst: &mut [u8], + #[number] len: usize, + ) -> Result<(), ReprError> { + self.copy_into(state, offset, dst, len) } - // SAFETY: ptr and offset are user provided. - Ok(unsafe { - ptr::read_unaligned::<*mut c_void>(ptr.offset(offset) as *const *mut c_void) - }) + #[static_method] + fn copy_into( + state: &mut OpState, + ptr: *mut c_void, + #[number] offset: isize, + #[anybuffer] dst: &mut [u8], + #[number] len: usize, + ) -> Result<(), ReprError> { + UnsafePointerView(ptr).copy_into(state, offset, dst, len) + } }