mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
feat(ext/ffi): implement UnsafePointer and UnsafePointerView (#12828)
This commit is contained in:
parent
4d176b7b7c
commit
ee49cce726
9 changed files with 591 additions and 52 deletions
80
cli/dts/lib.deno.unstable.d.ts
vendored
80
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
269
ext/ffi/lib.rs
269
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")]
|
||||
|
|
|
@ -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!")
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
@ -28,6 +30,16 @@ pub extern "C" fn print_buffer2(
|
|||
println!("{:?} {:?}", buf1, buf2);
|
||||
}
|
||||
|
||||
#[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
|
||||
|
|
|
@ -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\
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue