summaryrefslogtreecommitdiff
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
parent4d176b7b7c11aabc584bee45423f108ea47faefe (diff)
feat(ext/ffi): implement UnsafePointer and UnsafePointerView (#12828)
-rw-r--r--cli/dts/lib.deno.unstable.d.ts80
-rw-r--r--ext/ffi/00_ffi.js185
-rw-r--r--ext/ffi/lib.rs269
-rw-r--r--runtime/build.rs2
-rw-r--r--runtime/js/90_deno_ns.js2
-rw-r--r--runtime/permissions.rs49
-rw-r--r--test_ffi/src/lib.rs12
-rw-r--r--test_ffi/tests/integration_tests.rs9
-rw-r--r--test_ffi/tests/test.js35
9 files changed, 591 insertions, 52 deletions
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index affa4b3fd..6b7755ee5 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -117,16 +117,90 @@ declare namespace Deno {
| "usize"
| "isize"
| "f32"
- | "f64";
+ | "f64"
+ | "pointer";
/** A foreign function as defined by its parameter and result types */
export interface ForeignFunction {
- parameters: (NativeType | "buffer")[];
+ parameters: NativeType[];
result: NativeType;
/** When true, function calls will run on a dedicated blocking thread and will return a Promise resolving to the `result`. */
nonblocking?: boolean;
}
+ type TypedArray =
+ | Int8Array
+ | Uint8Array
+ | Int16Array
+ | Uint16Array
+ | Int32Array
+ | Uint32Array
+ | Uint8ClampedArray
+ | Float32Array
+ | Float64Array
+ | BigInt64Array
+ | BigUint64Array;
+
+ /** **UNSTABLE**: Unsafe and new API, beware!
+ *
+ * An unsafe pointer to a memory location for passing and returning pointers to and from the ffi
+ */
+ export class UnsafePointer {
+ constructor(value: bigint);
+
+ value: bigint;
+
+ /**
+ * Return the direct memory pointer to the typed array in memory
+ */
+ static of(typedArray: TypedArray): UnsafePointer;
+
+ /**
+ * Returns the value of the pointer which is useful in certain scenarios.
+ */
+ valueOf(): bigint;
+ }
+
+ /** **UNSTABLE**: Unsafe and new API, beware!
+ *
+ * An unsafe pointer view to a memory location as specified by the `pointer`
+ * value. The `UnsafePointerView` API mimics the standard built in interface
+ * `DataView` for accessing the underlying types at an memory location
+ * (numbers, strings and raw bytes).
+ */
+ export class UnsafePointerView {
+ constructor(pointer: UnsafePointer);
+
+ pointer: UnsafePointer;
+
+ /** Gets an unsigned 8-bit integer at the specified byte offset from the pointer. */
+ getUint8(offset?: number): number;
+ /** Gets a signed 8-bit integer at the specified byte offset from the pointer. */
+ getInt8(offset?: number): number;
+ /** Gets an unsigned 16-bit integer at the specified byte offset from the pointer. */
+ getUint16(offset?: number): number;
+ /** Gets a signed 16-bit integer at the specified byte offset from the pointer. */
+ getInt16(offset?: number): number;
+ /** Gets an unsigned 32-bit integer at the specified byte offset from the pointer. */
+ getUint32(offset?: number): number;
+ /** Gets a signed 32-bit integer at the specified byte offset from the pointer. */
+ getInt32(offset?: number): number;
+ /** Gets an unsigned 64-bit integer at the specified byte offset from the pointer. */
+ getBigUint64(offset?: number): bigint;
+ /** Gets a signed 64-bit integer at the specified byte offset from the pointer. */
+ getBigInt64(offset?: number): bigint;
+ /** Gets a signed 32-bit float at the specified byte offset from the pointer. */
+ getFloat32(offset?: number): number;
+ /** Gets a signed 64-bit float at the specified byte offset from the pointer. */
+ getFloat64(offset?: number): number;
+ /** Gets a C string (null terminated string) at the specified byte offset from the pointer. */
+ getCString(offset?: number): string;
+ /** Gets an ArrayBuffer of length `byteLength` at the specified byte offset from the pointer. */
+ getArrayBuffer(byteLength: number, offset?: number): ArrayBuffer;
+ /** Copies the memory of the pointer into a typed array. Length is determined from the typed array's `byteLength`. Also takes optional offset from the pointer. */
+ copyInto(destination: TypedArray, offset?: number): void;
+ }
+
/** A dynamic library resource */
export interface DynamicLibrary<S extends Record<string, ForeignFunction>> {
/** All of the registered symbols along with functions for calling them */
@@ -135,7 +209,7 @@ declare namespace Deno {
close(): void;
}
- /** **UNSTABLE**: new API
+ /** **UNSTABLE**: Unsafe and new API, beware!
*
* Opens a dynamic library and registers symbols
*/
diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js
index 25eba0233..48e2e4f92 100644
--- a/ext/ffi/00_ffi.js
+++ b/ext/ffi/00_ffi.js
@@ -6,7 +6,142 @@
const __bootstrap = window.__bootstrap;
const {
ArrayBuffer,
+ Uint8Array,
+ BigInt,
+ Number,
+ TypeError,
} = window.__bootstrap.primordials;
+
+ function unpackU64([hi, lo]) {
+ return BigInt(hi) << 32n | BigInt(lo);
+ }
+
+ function packU64(value) {
+ return [Number(value >> 32n), Number(value & 0xFFFFFFFFn)];
+ }
+
+ function unpackI64([hi, lo]) {
+ const u64 = unpackU64([hi, lo]);
+ return u64 >> 63n ? u64 - 0x10000000000000000n : u64;
+ }
+
+ class UnsafePointerView {
+ pointer;
+
+ constructor(pointer) {
+ this.pointer = pointer;
+ }
+
+ getUint8(offset = 0) {
+ return core.opSync(
+ "op_ffi_read_u8",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getInt8(offset = 0) {
+ return core.opSync(
+ "op_ffi_read_i8",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getUint16(offset = 0) {
+ return core.opSync(
+ "op_ffi_read_u16",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getInt16(offset = 0) {
+ return core.opSync(
+ "op_ffi_read_i16",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getUint32(offset = 0) {
+ return core.opSync(
+ "op_ffi_read_u32",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getInt32(offset = 0) {
+ return core.opSync(
+ "op_ffi_read_i32",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getBigUint64(offset = 0) {
+ return unpackU64(core.opSync(
+ "op_ffi_read_u64",
+ packU64(this.pointer.value + BigInt(offset)),
+ ));
+ }
+
+ getBigInt64(offset = 0) {
+ return unpackI64(core.opSync(
+ "op_ffi_read_u64",
+ packU64(this.pointer.value + BigInt(offset)),
+ ));
+ }
+
+ getFloat32(offset = 0) {
+ return core.opSync(
+ "op_ffi_read_f32",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getFloat64(offset = 0) {
+ return core.opSync(
+ "op_ffi_read_f64",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getCString(offset = 0) {
+ return core.opSync(
+ "op_ffi_cstr_read",
+ packU64(this.pointer.value + BigInt(offset)),
+ );
+ }
+
+ getArrayBuffer(byteLength, offset = 0) {
+ const uint8array = new Uint8Array(byteLength);
+ this.copyInto(uint8array, offset);
+ return uint8array.buffer;
+ }
+
+ copyInto(destination, offset = 0) {
+ core.opSync("op_ffi_buf_copy_into", [
+ packU64(this.pointer.value + BigInt(offset)),
+ destination,
+ destination.byteLength,
+ ]);
+ }
+ }
+
+ class UnsafePointer {
+ value;
+
+ constructor(value) {
+ this.value = value;
+ }
+
+ static of(typedArray) {
+ return new UnsafePointer(
+ unpackU64(core.opSync("op_ffi_ptr_of", typedArray)),
+ );
+ }
+
+ valueOf() {
+ return this.value;
+ }
+ }
+
class DynamicLibrary {
#rid;
symbols = {};
@@ -16,37 +151,67 @@
for (const symbol in symbols) {
const isNonBlocking = symbols[symbol].nonblocking;
+ const types = symbols[symbol].parameters;
this.symbols[symbol] = (...args) => {
const parameters = [];
const buffers = [];
- for (const arg of args) {
- if (
- arg?.buffer instanceof ArrayBuffer &&
- arg.byteLength !== undefined
- ) {
- parameters.push(buffers.length);
- buffers.push(arg);
+ for (let i = 0; i < types.length; i++) {
+ const type = types[i];
+ const arg = args[i];
+
+ if (type === "pointer") {
+ if (
+ arg?.buffer instanceof ArrayBuffer &&
+ arg.byteLength !== undefined
+ ) {
+ parameters.push(buffers.length);
+ buffers.push(arg);
+ } else if (arg instanceof UnsafePointer) {
+ parameters.push(packU64(arg.value));
+ buffers.push(undefined);
+ } else if (arg === null) {
+ parameters.push(null);
+ buffers.push(undefined);
+ } else {
+ throw new TypeError(
+ "Invalid ffi arg value, expected TypedArray, UnsafePointer or null",
+ );
+ }
} else {
parameters.push(arg);
}
}
if (isNonBlocking) {
- return core.opAsync("op_ffi_call_nonblocking", {
+ const promise = core.opAsync("op_ffi_call_nonblocking", {
rid: this.#rid,
symbol,
parameters,
buffers,
});
+
+ if (symbols[symbol].result === "pointer") {
+ return promise.then((value) =>
+ new UnsafePointer(unpackU64(value))
+ );
+ }
+
+ return promise;
} else {
- return core.opSync("op_ffi_call", {
+ const result = core.opSync("op_ffi_call", {
rid: this.#rid,
symbol,
parameters,
buffers,
});
+
+ if (symbols[symbol].result === "pointer") {
+ return new UnsafePointer(unpackU64(result));
+ }
+
+ return result;
}
};
}
@@ -63,5 +228,5 @@
return new DynamicLibrary(pathFromURL(path), symbols);
}
- window.__bootstrap.ffi = { dlopen };
+ window.__bootstrap.ffi = { dlopen, UnsafePointer, UnsafePointerView };
})(this);
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")]
diff --git a/runtime/build.rs b/runtime/build.rs
index 14e2e0362..c83f13070 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -85,7 +85,7 @@ mod not_docs {
impl deno_ffi::FfiPermissions for Permissions {
fn check(
&mut self,
- _path: &Path,
+ _path: Option<&Path>,
) -> Result<(), deno_core::error::AnyError> {
unreachable!("snapshotting!")
}
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index e5e52b1f6..029423ee1 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -135,6 +135,8 @@
createHttpClient: __bootstrap.fetch.createHttpClient,
http: __bootstrap.http,
dlopen: __bootstrap.ffi.dlopen,
+ UnsafePointer: __bootstrap.ffi.UnsafePointer,
+ UnsafePointerView: __bootstrap.ffi.UnsafePointerView,
flock: __bootstrap.fs.flock,
flockSync: __bootstrap.fs.flockSync,
funlock: __bootstrap.fs.funlock,
diff --git a/runtime/permissions.rs b/runtime/permissions.rs
index 9a85e5e58..50c126f3f 100644
--- a/runtime/permissions.rs
+++ b/runtime/permissions.rs
@@ -1044,22 +1044,39 @@ impl UnaryPermission<FfiDescriptor> {
self.query(path)
}
- pub fn check(&mut self, path: &Path) -> Result<(), AnyError> {
- let (resolved_path, display_path) = resolved_and_display_path(path);
- let (result, prompted) = self.query(Some(&resolved_path)).check(
- self.name,
- Some(&format!("\"{}\"", display_path.display())),
- self.prompt,
- );
- if prompted {
- if result.is_ok() {
- self.granted_list.insert(FfiDescriptor(resolved_path));
- } else {
- self.denied_list.insert(FfiDescriptor(resolved_path));
- self.global_state = PermissionState::Denied;
+ pub fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
+ if let Some(path) = path {
+ let (resolved_path, display_path) = resolved_and_display_path(path);
+ let (result, prompted) = self.query(Some(&resolved_path)).check(
+ self.name,
+ Some(&format!("\"{}\"", display_path.display())),
+ self.prompt,
+ );
+
+ if prompted {
+ if result.is_ok() {
+ self.granted_list.insert(FfiDescriptor(resolved_path));
+ } else {
+ self.denied_list.insert(FfiDescriptor(resolved_path));
+ self.global_state = PermissionState::Denied;
+ }
}
+
+ result
+ } else {
+ let (result, prompted) =
+ self.query(None).check(self.name, None, self.prompt);
+
+ if prompted {
+ if result.is_ok() {
+ self.global_state = PermissionState::Granted;
+ } else {
+ self.global_state = PermissionState::Denied;
+ }
+ }
+
+ result
}
- result
}
pub fn check_all(&mut self) -> Result<(), AnyError> {
@@ -1314,7 +1331,7 @@ impl deno_websocket::WebSocketPermissions for Permissions {
}
impl deno_ffi::FfiPermissions for Permissions {
- fn check(&mut self, path: &Path) -> Result<(), AnyError> {
+ fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
self.ffi.check(path)
}
}
@@ -1740,7 +1757,7 @@ pub fn create_child_permissions(
.ffi
.granted_list
.iter()
- .all(|desc| main_perms.ffi.check(&desc.0).is_ok())
+ .all(|desc| main_perms.ffi.check(Some(&desc.0)).is_ok())
{
return Err(escalation_error());
}
diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs
index 38275e547..b0206276a 100644
--- a/test_ffi/src/lib.rs
+++ b/test_ffi/src/lib.rs
@@ -3,6 +3,8 @@
use std::thread::sleep;
use std::time::Duration;
+static BUFFER: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
+
#[no_mangle]
pub extern "C" fn print_something() {
println!("something");
@@ -29,6 +31,16 @@ pub extern "C" fn print_buffer2(
}
#[no_mangle]
+pub extern "C" fn return_buffer() -> *const u8 {
+ BUFFER.as_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn is_null_ptr(ptr: *const u8) -> u8 {
+ ptr.is_null() as u8
+}
+
+#[no_mangle]
pub extern "C" fn add_u32(a: u32, b: u32) -> u32 {
a + b
}
diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs
index e16e97e24..99a17f0a9 100644
--- a/test_ffi/tests/integration_tests.rs
+++ b/test_ffi/tests/integration_tests.rs
@@ -41,6 +41,15 @@ fn basic() {
something\n\
[1, 2, 3, 4, 5, 6, 7, 8]\n\
[1, 2, 3, 4, 5, 6, 7, 8] [9, 10]\n\
+ [1, 2, 3, 4, 5, 6, 7, 8]\n\
+ [ 1, 2, 3, 4, 5, 6 ]\n\
+ [ 4, 5, 6 ]\n\
+ [ 4, 5, 6 ]\n\
+ Hello from pointer!\n\
+ pointer!\n\
+ false\n\
+ true\n\
+ false\n\
579\n\
579\n\
579\n\
diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js
index 35d51c006..16e4d76b6 100644
--- a/test_ffi/tests/test.js
+++ b/test_ffi/tests/test.js
@@ -20,11 +20,13 @@ try {
const dylib = Deno.dlopen(libPath, {
"print_something": { parameters: [], result: "void" },
- "print_buffer": { parameters: ["buffer", "usize"], result: "void" },
+ "print_buffer": { parameters: ["pointer", "usize"], result: "void" },
"print_buffer2": {
- parameters: ["buffer", "usize", "buffer", "usize"],
+ parameters: ["pointer", "usize", "pointer", "usize"],
result: "void",
},
+ "return_buffer": { parameters: [], result: "pointer" },
+ "is_null_ptr": { parameters: ["pointer"], result: "u8" },
"add_u32": { parameters: ["u32", "u32"], result: "u32" },
"add_i32": { parameters: ["i32", "i32"], result: "i32" },
"add_u64": { parameters: ["u64", "u64"], result: "u64" },
@@ -33,10 +35,10 @@ const dylib = Deno.dlopen(libPath, {
"add_isize": { parameters: ["isize", "isize"], result: "isize" },
"add_f32": { parameters: ["f32", "f32"], result: "f32" },
"add_f64": { parameters: ["f64", "f64"], result: "f64" },
- "fill_buffer": { parameters: ["u8", "buffer", "usize"], result: "void" },
+ "fill_buffer": { parameters: ["u8", "pointer", "usize"], result: "void" },
"sleep_blocking": { parameters: ["u64"], result: "void", nonblocking: true },
"nonblocking_buffer": {
- parameters: ["buffer", "usize"],
+ parameters: ["pointer", "usize"],
result: "void",
nonblocking: true,
},
@@ -47,6 +49,31 @@ const buffer = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
const buffer2 = new Uint8Array([9, 10]);
dylib.symbols.print_buffer(buffer, buffer.length);
dylib.symbols.print_buffer2(buffer, buffer.length, buffer2, buffer2.length);
+const ptr = dylib.symbols.return_buffer();
+dylib.symbols.print_buffer(ptr, 8);
+const ptrView = new Deno.UnsafePointerView(ptr);
+const into = new Uint8Array(6);
+const into2 = new Uint8Array(3);
+const into2ptr = Deno.UnsafePointer.of(into2);
+const into2ptrView = new Deno.UnsafePointerView(into2ptr);
+const into3 = new Uint8Array(3);
+ptrView.copyInto(into);
+console.log([...into]);
+ptrView.copyInto(into2, 3);
+console.log([...into2]);
+into2ptrView.copyInto(into3);
+console.log([...into3]);
+const string = new Uint8Array([
+ ...new TextEncoder().encode("Hello from pointer!"),
+ 0,
+]);
+const stringPtr = Deno.UnsafePointer.of(string);
+const stringPtrview = new Deno.UnsafePointerView(stringPtr);
+console.log(stringPtrview.getCString());
+console.log(stringPtrview.getCString(11));
+console.log(Boolean(dylib.symbols.is_null_ptr(ptr)));
+console.log(Boolean(dylib.symbols.is_null_ptr(null)));
+console.log(Boolean(dylib.symbols.is_null_ptr(Deno.UnsafePointer.of(into))));
console.log(dylib.symbols.add_u32(123, 456));
console.log(dylib.symbols.add_i32(123, 456));
console.log(dylib.symbols.add_u64(123, 456));