diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index cdaf69e0d9..ac8dda3174 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -10,7 +10,6 @@ ObjectPrototypeIsPrototypeOf, PromisePrototypeThen, TypeError, - Uint8Array, } = window.__bootstrap.primordials; function unpackU64([hi, lo]) { @@ -107,9 +106,11 @@ } getArrayBuffer(byteLength, offset = 0) { - const uint8array = new Uint8Array(byteLength); - this.copyInto(uint8array, offset); - return uint8array.buffer; + return core.opSync( + "op_ffi_get_buf", + offset ? this.pointer + BigInt(offset) : this.pointer, + byteLength, + ); } copyInto(destination, offset = 0) { diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 05dcba362c..396affdb3f 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -165,6 +165,7 @@ pub fn init(unstable: bool) -> Extension { op_ffi_call_ptr::decl::

(), op_ffi_call_ptr_nonblocking::decl::

(), op_ffi_ptr_of::decl::

(), + op_ffi_get_buf::decl::

(), op_ffi_buf_copy_into::decl::

(), op_ffi_cstr_read::decl::

(), op_ffi_read_u8::decl::

(), @@ -1912,6 +1913,50 @@ where Ok(big_int.into()) } +unsafe extern "C" fn noop_deleter_callback( + _data: *mut c_void, + _byte_length: usize, + _deleter_data: *mut c_void, +) { +} + +#[op(v8)] +fn op_ffi_get_buf( + scope: &mut v8::HandleScope<'scope>, + state: &mut deno_core::OpState, + src: serde_v8::Value<'scope>, + len: usize, +) -> Result, AnyError> +where + FP: FfiPermissions + 'static, +{ + check_unstable(state, "Deno.UnsafePointerView#arrayBuffer"); + + let permissions = state.borrow_mut::(); + permissions.check(None)?; + + let value = v8::Local::::try_from(src.v8_value) + .map_err(|_| type_error("Invalid FFI pointer value, expected BigInt"))?; + let ptr = value.u64_value().0 as usize as *mut c_void; + if std::ptr::eq(ptr, std::ptr::null()) { + return Err(type_error("Invalid FFI pointer value, got nullptr")); + } + + // 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::ArrayBuffer::with_backing_store(scope, &backing_store).into(); + Ok(array_buffer.into()) +} + #[op] fn op_ffi_buf_copy_into( state: &mut deno_core::OpState, diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs index cde914c22e..95b7d900ca 100644 --- a/test_ffi/src/lib.rs +++ b/test_ffi/src/lib.rs @@ -393,4 +393,4 @@ pub struct Structure { } #[no_mangle] -pub static static_ptr: Structure = Structure { _data: 42 }; +pub static mut static_ptr: Structure = Structure { _data: 42 }; diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs index 26de8ce0d3..4a910a836e 100644 --- a/test_ffi/tests/integration_tests.rs +++ b/test_ffi/tests/integration_tests.rs @@ -101,6 +101,11 @@ fn basic() { Static i64: -1242464576485n\n\ Static ptr: true\n\ Static ptr value: 42\n\ + arrayBuffer.byteLength: 4\n\ + uint32Array.length: 1\n\ + uint32Array[0]: 42\n\ + uint32Array[0] after mutation: 55\n\ + Static ptr value after mutation: 55\n\ Correct number of resources\n"; assert_eq!(stdout, expected); assert_eq!(stderr, ""); diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index ff81f302ed..516182f6f6 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -3,7 +3,9 @@ // Run using cargo test or `--v8-options=--allow-natives-syntax` -import { assertThrows } from "../../test_util/std/testing/asserts.ts"; +import { + assertThrows, +} from "../../test_util/std/testing/asserts.ts"; const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); const [libPrefix, libSuffix] = { @@ -442,6 +444,15 @@ console.log( const view = new Deno.UnsafePointerView(dylib.symbols.static_ptr); console.log("Static ptr value:", view.getUint32()); +const arrayBuffer = view.getArrayBuffer(4); +const uint32Array = new Uint32Array(arrayBuffer); +console.log("arrayBuffer.byteLength:", arrayBuffer.byteLength); +console.log("uint32Array.length:", uint32Array.length); +console.log("uint32Array[0]:", uint32Array[0]); +uint32Array[0] = 55; // MUTATES! +console.log("uint32Array[0] after mutation:", uint32Array[0]); +console.log("Static ptr value after mutation:", view.getUint32()); + (function cleanup() { dylib.close(); throwCallback.close();