diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/ffi/00_ffi.js | 115 | ||||
-rw-r--r-- | ext/ffi/lib.rs | 70 |
2 files changed, 154 insertions, 31 deletions
diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index b0bdbe8cf..abe806cc0 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -142,6 +142,84 @@ } } + function prepareArgs(types, args) { + const parameters = []; + const buffers = []; + + 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); + } + } + + return { parameters, buffers }; + } + + class UnsafeFnPointer { + pointer; + definition; + + constructor(pointer, definition) { + this.pointer = pointer; + this.definition = definition; + } + + call(...args) { + const { parameters, buffers } = prepareArgs( + this.definition.parameters, + args, + ); + if (this.definition.nonblocking) { + const promise = core.opAsync("op_ffi_call_ptr_nonblocking", { + pointer: packU64(this.pointer.value), + def: this.definition, + parameters, + buffers, + }); + + if (this.definition.result === "pointer") { + return promise.then((value) => new UnsafePointer(unpackU64(value))); + } + + return promise; + } else { + const result = core.opSync("op_ffi_call_ptr", { + pointer: packU64(this.pointer.value), + def: this.definition, + parameters, + buffers, + }); + + if (this.definition.result === "pointer") { + return new UnsafePointer(unpackU64(result)); + } + + return result; + } + } + } + class DynamicLibrary { #rid; symbols = {}; @@ -154,35 +232,7 @@ const types = symbols[symbol].parameters; this.symbols[symbol] = (...args) => { - const parameters = []; - const buffers = []; - - 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); - } - } + const { parameters, buffers } = prepareArgs(types, args); if (isNonBlocking) { const promise = core.opAsync("op_ffi_call_nonblocking", { @@ -228,5 +278,10 @@ return new DynamicLibrary(pathFromURL(path), symbols); } - window.__bootstrap.ffi = { dlopen, UnsafePointer, UnsafePointerView }; + window.__bootstrap.ffi = { + dlopen, + UnsafePointer, + UnsafePointerView, + UnsafeFnPointer, + }; })(this); diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 2a6799b5f..8f06854d6 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -130,6 +130,11 @@ 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_call_ptr", op_sync(op_ffi_call_ptr)), + ( + "op_ffi_call_ptr_nonblocking", + op_async(op_ffi_call_ptr_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>)), @@ -323,7 +328,7 @@ fn value_as_f64(value: Value) -> Result<f64, AnyError> { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] struct U32x2(u32, u32); impl From<u64> for U32x2 { @@ -469,6 +474,49 @@ struct FfiCallArgs { buffers: Vec<Option<ZeroCopyBuf>>, } +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct FfiCallPtrArgs { + pointer: U32x2, + def: ForeignFunction, + parameters: Vec<Value>, + buffers: Vec<Option<ZeroCopyBuf>>, +} + +impl From<FfiCallPtrArgs> for FfiCallArgs { + fn from(args: FfiCallPtrArgs) -> Self { + FfiCallArgs { + rid: 0, + symbol: String::new(), + parameters: args.parameters, + buffers: args.buffers, + } + } +} + +impl FfiCallPtrArgs { + fn get_symbol(&self) -> Symbol { + let fn_ptr: u64 = self.pointer.into(); + let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); + let cif = libffi::middle::Cif::new( + self + .def + .parameters + .clone() + .into_iter() + .map(libffi::middle::Type::from), + self.def.result.into(), + ); + + Symbol { + cif, + ptr, + parameter_types: self.def.parameters.clone(), + result_type: self.def.result, + } + } +} + fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result<Value, AnyError> { let buffers: Vec<Option<&[u8]>> = args .buffers @@ -563,6 +611,26 @@ fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result<Value, AnyError> { }) } +fn op_ffi_call_ptr( + _state: &mut deno_core::OpState, + args: FfiCallPtrArgs, + _: (), +) -> Result<Value, AnyError> { + let symbol = args.get_symbol(); + ffi_call(args.into(), &symbol) +} + +async fn op_ffi_call_ptr_nonblocking( + _state: Rc<RefCell<deno_core::OpState>>, + args: FfiCallPtrArgs, + _: (), +) -> Result<Value, AnyError> { + let symbol = args.get_symbol(); + tokio::task::spawn_blocking(move || ffi_call(args.into(), &symbol)) + .await + .unwrap() +} + fn op_ffi_call( state: &mut deno_core::OpState, args: FfiCallArgs, |