From 291ca64b45efd25b7635173244fac5ce812b9588 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 13 May 2022 21:46:44 +0300 Subject: [PATCH 01/64] Start rewrite --- cli/dts/lib.deno.unstable.d.ts | 110 +++++++++++---------- ext/ffi/lib.rs | 172 +++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+), 52 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 12f53b784f3968..1c666d4b4fa176 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -81,10 +81,7 @@ declare namespace Deno { * }); * ``` */ - export function bench( - name: string, - fn: () => void | Promise, - ): void; + export function bench(name: string, fn: () => void | Promise): void; /** Register a bench which will be run when `deno bench` is used on the command * line and the containing module looks like a bench module. @@ -127,7 +124,7 @@ declare namespace Deno { export function bench( name: string, options: Omit, - fn: () => void | Promise, + fn: () => void | Promise ): void; /** Register a bench which will be run when `deno bench` is used on the command @@ -150,7 +147,7 @@ declare namespace Deno { */ export function bench( options: Omit, - fn: () => void | Promise, + fn: () => void | Promise ): void; /** Register a bench which will be run when `deno bench` is used on the command @@ -173,7 +170,7 @@ declare namespace Deno { */ export function bench( options: Omit, - fn: () => void | Promise, + fn: () => void | Promise ): void; /** @@ -201,9 +198,7 @@ declare namespace Deno { * const { columns, rows } = Deno.consoleSize(Deno.stdout.rid); * ``` */ - export function consoleSize( - rid: number, - ): { + export function consoleSize(rid: number): { columns: number; rows: number; }; @@ -346,11 +341,17 @@ declare namespace Deno { | "f64" | "pointer"; + type NativeParameterType = + | NativeType + | { + function: Omit; + }; + /** A foreign function as defined by its parameter and result types */ export interface ForeignFunction< - Parameters extends readonly NativeType[] = readonly NativeType[], + Parameters extends readonly NativeParameterType[] = readonly NativeParameterType[], Result extends NativeType = NativeType, - NonBlocking extends boolean = boolean, + NonBlocking extends boolean = boolean > { /** Name of the symbol, defaults to the key name in symbols object. */ name?: string; @@ -395,25 +396,31 @@ declare namespace Deno { : unknown; /** Infers a foreign function parameter list. */ - type StaticForeignFunctionParameters = [ + type StaticForeignFunctionParameters< + T extends readonly NativeParameterType[] + > = [ ...{ [K in keyof T]: StaticForeignFunctionParameter; - }, + } ]; /** Infers a foreign symbol */ type StaticForeignSymbol = - T extends ForeignFunction ? ( - ...args: StaticForeignFunctionParameters - ) => ConditionalAsync< - T["nonblocking"], - StaticForeignFunctionResult - > - : T extends ForeignStatic ? StaticForeignFunctionResult + T extends ForeignFunction + ? ( + ...args: StaticForeignFunctionParameters + ) => ConditionalAsync< + T["nonblocking"], + StaticForeignFunctionResult + > + : T extends ForeignStatic + ? StaticForeignFunctionResult : never; - type ConditionalAsync = - IsAsync extends true ? Promise : T; + type ConditionalAsync< + IsAsync extends boolean | undefined, + T + > = IsAsync extends true ? Promise : T; /** Infers a foreign library interface */ type StaticForeignLibraryInterface = { @@ -526,7 +533,7 @@ declare namespace Deno { */ export function dlopen( filename: string | URL, - symbols: S, + symbols: S ): DynamicLibrary; /** The log category for a diagnostic message. */ @@ -596,7 +603,7 @@ declare namespace Deno { export function setRaw( rid: number, mode: boolean, - options?: SetRawOptions, + options?: SetRawOptions ): void; /** **UNSTABLE**: needs investigation into high precision time. @@ -613,7 +620,7 @@ declare namespace Deno { export function utimeSync( path: string | URL, atime: number | Date, - mtime: number | Date, + mtime: number | Date ): void; /** **UNSTABLE**: needs investigation into high precision time. @@ -630,7 +637,7 @@ declare namespace Deno { export function utime( path: string | URL, atime: number | Date, - mtime: number | Date, + mtime: number | Date ): Promise; export function run< @@ -642,7 +649,7 @@ declare namespace Deno { clearEnv?: boolean; gid?: number; uid?: number; - }, + } >(opt: T): Process; /** **UNSTABLE**: New API, yet to be vetted. Additional consideration is still @@ -714,7 +721,7 @@ declare namespace Deno { * ``` */ export function createHttpClient( - options: CreateHttpClientOptions, + options: CreateHttpClientOptions ): HttpClient; /** **UNSTABLE**: needs investigation into high precision time. @@ -731,7 +738,7 @@ declare namespace Deno { export function futimeSync( rid: number, atime: number | Date, - mtime: number | Date, + mtime: number | Date ): void; /** **UNSTABLE**: needs investigation into high precision time. @@ -748,7 +755,7 @@ declare namespace Deno { export function futime( rid: number, atime: number | Date, - mtime: number | Date, + mtime: number | Date ): Promise; /** **UNSTABLE**: new API, yet to be vetted. @@ -799,7 +806,7 @@ declare namespace Deno { * * Requires `allow-read` and `allow-write` permission. */ export function listen( - options: UnixListenOptions & { transport: "unix" }, + options: UnixListenOptions & { transport: "unix" } ): Listener; /** **UNSTABLE**: new API, yet to be vetted @@ -820,7 +827,7 @@ declare namespace Deno { * * Requires `allow-net` permission. */ export function listenDatagram( - options: ListenOptions & { transport: "udp" }, + options: ListenOptions & { transport: "udp" } ): DatagramConn; /** **UNSTABLE**: new API, yet to be vetted @@ -836,7 +843,7 @@ declare namespace Deno { * * Requires `allow-read` and `allow-write` permission. */ export function listenDatagram( - options: UnixListenOptions & { transport: "unixpacket" }, + options: UnixListenOptions & { transport: "unixpacket" } ): DatagramConn; export interface UnixConnectOptions { @@ -859,12 +866,8 @@ declare namespace Deno { * ``` * * Requires `allow-net` permission for "tcp" and `allow-read` for "unix". */ - export function connect( - options: ConnectOptions, - ): Promise; - export function connect( - options: UnixConnectOptions, - ): Promise; + export function connect(options: ConnectOptions): Promise; + export function connect(options: UnixConnectOptions): Promise; export interface ConnectTlsOptions { /** PEM formatted client certificate chain. */ @@ -998,7 +1001,7 @@ declare namespace Deno { * `request`, otherwise event loop might deadlock. */ export function upgradeHttp( - request: Request, + request: Request ): Promise<[Deno.Conn, Uint8Array]>; export interface SpawnOptions { @@ -1063,15 +1066,18 @@ declare namespace Deno { */ export function spawnChild( command: string | URL, - options?: T, + options?: T ): Child; export class Child { - readonly stdin: T["stdin"] extends "piped" ? WritableStream + readonly stdin: T["stdin"] extends "piped" + ? WritableStream : null; - readonly stdout: T["stdout"] extends "inherit" | "null" ? null + readonly stdout: T["stdout"] extends "inherit" | "null" + ? null : ReadableStream; - readonly stderr: T["stderr"] extends "inherit" | "null" ? null + readonly stderr: T["stderr"] extends "inherit" | "null" + ? null : ReadableStream; readonly pid: number; @@ -1103,7 +1109,7 @@ declare namespace Deno { */ export function spawn( command: string | URL, - options?: T, + options?: T ): Promise>; /** @@ -1125,15 +1131,15 @@ declare namespace Deno { */ export function spawnSync( command: string | URL, - options?: T, + options?: T ): SpawnOutput; export type ChildStatus = | { - success: true; - code: 0; - signal: null; - } + success: true; + code: 0; + signal: null; + } | { success: false; code: number; @@ -1149,7 +1155,7 @@ declare namespace Deno { declare function fetch( input: Request | URL | string, - init?: RequestInit & { client: Deno.HttpClient }, + init?: RequestInit & { client: Deno.HttpClient } ): Promise; declare interface WorkerOptions { diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 8a1a75b1d9a80a..87326204d58ba5 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,5 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use core::ptr::NonNull; use deno_core::error::bad_resource_id; use deno_core::error::generic_error; use deno_core::error::range_error; @@ -11,13 +12,18 @@ use deno_core::op; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; +use deno_core::serde_v8; +use deno_core::v8; use deno_core::Extension; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ZeroCopyBuf; +use deno_core::v8::Handle; use dlopen::raw::Library; use libffi::middle::Arg; +use libffi::middle::Cif; +use libffi::raw::*; use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; @@ -689,6 +695,172 @@ fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result { }) } +struct RegisteredCallbackResource<'a> { + cb: NonNull, + closure: libffi::middle::Closure<'a>, + scope: v8::HandleScope<'a>, + //isolate: v8::Isolate, +} + +impl Resource for RegisteredCallbackResource<'static> { + fn name(&self) -> Cow { + "registeredcallback".into() + } + + fn close(self: Rc) { + drop(self) + } +} + +unsafe extern "C" fn deno_ffi_callback<'s>( + cif: &libffi::low::ffi_cif, + result: &mut c_void, + args: *const *const c_void, + cb: &v8::Function, + scope: &mut v8::HandleScope<'s>, + isolate: &mut v8::Isolate, +) { + let cb = v8::Global::from_raw( + isolate, + std::mem::transmute::<&v8::Function, NonNull>(cb), + ); + let func = cb.open(scope); + let result = result as *mut c_void; + let repr = std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); + let vals = std::slice::from_raw_parts(args, cif.nargs as usize); + + let mut params: Vec> = vec![]; + for (&repr, &val) in repr.iter().zip(vals) { + let value = match (*repr).type_ as _ { + FFI_TYPE_INT => serde_v8::to_v8(scope, *(val as *const i32)), + FFI_TYPE_FLOAT => serde_v8::to_v8(scope, *(val as *const f32)), + FFI_TYPE_DOUBLE => serde_v8::to_v8(scope, *(val as *const f64)), + FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { + let ptr = U32x2::from(*(val as *const u64)); + serde_v8::to_v8(scope, ptr) + } + FFI_TYPE_SINT8 => serde_v8::to_v8(scope, *(val as *const i8)), + FFI_TYPE_UINT8 => serde_v8::to_v8(scope, *(val as *const u8)), + FFI_TYPE_SINT16 => serde_v8::to_v8(scope, *(val as *const i16)), + FFI_TYPE_UINT16 => serde_v8::to_v8(scope, *(val as *const u16)), + FFI_TYPE_SINT32 => serde_v8::to_v8(scope, *(val as *const i32)), + FFI_TYPE_UINT32 => serde_v8::to_v8(scope, *(val as *const u32)), + FFI_TYPE_SINT64 => serde_v8::to_v8(scope, *(val as *const i64)), + FFI_TYPE_UINT64 => serde_v8::to_v8(scope, *(val as *const u64)), + FFI_TYPE_VOID => serde_v8::to_v8(scope, ()), + _ => { + panic!("Unsupported parameter type") + } + }; + params.push(value.expect("Unable to serialize callback parameter.")); + } + + let func_name = func.get_name(scope).to_rust_string_lossy(scope); + + println!("Func name: {}", func_name); + + let recv = v8::undefined(scope); + let value = match func.call(scope, recv.into(), ¶ms) { + Some(value) => value, + None => v8::Number::new(scope, 60.0).into(), + }; + std::mem::forget(cb); + + match (*cif.rtype).type_ as _ { + FFI_TYPE_INT | FFI_TYPE_SINT32 => { + *(result as *mut i32) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_FLOAT => { + *(result as *mut f32) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_DOUBLE => { + *(result as *mut f64) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { + let u32x2: U32x2 = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + *(result as *mut u64) = u64::from(u32x2); + } + FFI_TYPE_SINT8 => { + *(result as *mut i8) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_UINT8 => { + *(result as *mut u8) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_SINT16 => { + *(result as *mut i16) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_UINT16 => { + *(result as *mut u16) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_UINT32 => { + *(result as *mut u32) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_SINT64 => { + *(result as *mut i64) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_UINT64 => { + *(result as *mut u64) = serde_v8::from_v8(scope, value) + .expect("Unable to deserialize result parameter."); + } + FFI_TYPE_VOID => {} + _ => { + panic!("Unsupported callback return type") + } + }; +} + +#[derive(Deserialize)] +struct RegisterCallbackArgs { + parameters: Vec, + result: NativeType, +} + +#[op(v8)] +fn op_ffi_register_callback( + state: &mut deno_core::OpState, + scope: &mut v8::HandleScope, + args: RegisterCallbackArgs, + cb: serde_v8::Value<'_>, +) -> Result { + let v8_value = cb.v8_value; + let cb = v8::Local::::try_from(v8_value).unwrap(); + + let global = unsafe { + // NOTE: `into_raw` leaks. + let global = v8::Global::new(&mut *scope, cb); + global.into_raw() + }; + + //let info = CallbackInfo::new(global); + let cif = Cif::new( + args.parameters.iter().map(libffi::middle::Type::from), + libffi::middle::Type::from(args.result.clone()), + ); + + let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, unsafe { std::mem::transmute::, &'static v8::Function>(global) }); + + //std::mem::forget(info); + + let resource = RegisteredCallbackResource { + // info, + cb: global, + closure, + scope: *scope, + }; + + Ok(state.resource_table.add(resource)) +} + #[op] fn op_ffi_call_ptr( state: &mut deno_core::OpState, From 6f955d07fedb83c0d0754710f74bec9a47663bda Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 14 May 2022 00:44:19 +0300 Subject: [PATCH 02/64] Further work --- ext/ffi/lib.rs | 152 +++++++++++++++++++++++++++++-------------------- 1 file changed, 91 insertions(+), 61 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 87326204d58ba5..13eec30ab2cb46 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -14,12 +14,12 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::serde_v8; use deno_core::v8; +use deno_core::v8::Handle; use deno_core::Extension; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ZeroCopyBuf; -use deno_core::v8::Handle; use dlopen::raw::Library; use libffi::middle::Arg; use libffi::middle::Cif; @@ -169,6 +169,9 @@ pub fn init(unstable: bool) -> Extension { op_ffi_read_u64::decl::

(), op_ffi_read_f32::decl::

(), op_ffi_read_f64::decl::

(), + op_ffi_register_callback::decl(), + op_ffi_deregister_callback::decl(), + test_registered_callback::decl(), ]) .state(move |state| { // Stolen from deno_webgpu, is there a better option? @@ -695,14 +698,12 @@ fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result { }) } -struct RegisteredCallbackResource<'a> { - cb: NonNull, - closure: libffi::middle::Closure<'a>, - scope: v8::HandleScope<'a>, - //isolate: v8::Isolate, +struct RegisteredCallbackResource { + closure: libffi::middle::Closure<'static>, + info: *const CallbackInfo, } -impl Resource for RegisteredCallbackResource<'static> { +impl Resource for RegisteredCallbackResource { fn name(&self) -> Cow { "registeredcallback".into() } @@ -712,19 +713,23 @@ impl Resource for RegisteredCallbackResource<'static> { } } -unsafe extern "C" fn deno_ffi_callback<'s>( +struct CallbackInfo { + pub callback: NonNull, + pub context: NonNull, + pub isolate: *mut v8::Isolate, +} + +unsafe extern "C" fn deno_ffi_callback( cif: &libffi::low::ffi_cif, result: &mut c_void, args: *const *const c_void, - cb: &v8::Function, - scope: &mut v8::HandleScope<'s>, - isolate: &mut v8::Isolate, + info: &CallbackInfo, ) { - let cb = v8::Global::from_raw( - isolate, - std::mem::transmute::<&v8::Function, NonNull>(cb), - ); - let func = cb.open(scope); + let isolate = &mut *info.isolate; + let callback = v8::Global::from_raw(isolate, info.callback); + let context = v8::Global::from_raw(isolate, info.context); + let mut scope = v8::HandleScope::with_context(isolate, context.clone()); + let func = callback.open(&mut scope); let result = result as *mut c_void; let repr = std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); let vals = std::slice::from_raw_parts(args, cif.nargs as usize); @@ -732,22 +737,22 @@ unsafe extern "C" fn deno_ffi_callback<'s>( let mut params: Vec> = vec![]; for (&repr, &val) in repr.iter().zip(vals) { let value = match (*repr).type_ as _ { - FFI_TYPE_INT => serde_v8::to_v8(scope, *(val as *const i32)), - FFI_TYPE_FLOAT => serde_v8::to_v8(scope, *(val as *const f32)), - FFI_TYPE_DOUBLE => serde_v8::to_v8(scope, *(val as *const f64)), + FFI_TYPE_INT => serde_v8::to_v8(&mut scope, *(val as *const i32)), + FFI_TYPE_FLOAT => serde_v8::to_v8(&mut scope, *(val as *const f32)), + FFI_TYPE_DOUBLE => serde_v8::to_v8(&mut scope, *(val as *const f64)), FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { let ptr = U32x2::from(*(val as *const u64)); - serde_v8::to_v8(scope, ptr) + serde_v8::to_v8(&mut scope, ptr) } - FFI_TYPE_SINT8 => serde_v8::to_v8(scope, *(val as *const i8)), - FFI_TYPE_UINT8 => serde_v8::to_v8(scope, *(val as *const u8)), - FFI_TYPE_SINT16 => serde_v8::to_v8(scope, *(val as *const i16)), - FFI_TYPE_UINT16 => serde_v8::to_v8(scope, *(val as *const u16)), - FFI_TYPE_SINT32 => serde_v8::to_v8(scope, *(val as *const i32)), - FFI_TYPE_UINT32 => serde_v8::to_v8(scope, *(val as *const u32)), - FFI_TYPE_SINT64 => serde_v8::to_v8(scope, *(val as *const i64)), - FFI_TYPE_UINT64 => serde_v8::to_v8(scope, *(val as *const u64)), - FFI_TYPE_VOID => serde_v8::to_v8(scope, ()), + FFI_TYPE_SINT8 => serde_v8::to_v8(&mut scope, *(val as *const i8)), + FFI_TYPE_UINT8 => serde_v8::to_v8(&mut scope, *(val as *const u8)), + FFI_TYPE_SINT16 => serde_v8::to_v8(&mut scope, *(val as *const i16)), + FFI_TYPE_UINT16 => serde_v8::to_v8(&mut scope, *(val as *const u16)), + FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *(val as *const i32)), + FFI_TYPE_UINT32 => serde_v8::to_v8(&mut scope, *(val as *const u32)), + FFI_TYPE_SINT64 => serde_v8::to_v8(&mut scope, *(val as *const i64)), + FFI_TYPE_UINT64 => serde_v8::to_v8(&mut scope, *(val as *const u64)), + FFI_TYPE_VOID => serde_v8::to_v8(&mut scope, ()), _ => { panic!("Unsupported parameter type") } @@ -755,61 +760,63 @@ unsafe extern "C" fn deno_ffi_callback<'s>( params.push(value.expect("Unable to serialize callback parameter.")); } - let func_name = func.get_name(scope).to_rust_string_lossy(scope); + let func_name = func.get_name(&mut scope).to_rust_string_lossy(&mut scope); println!("Func name: {}", func_name); - let recv = v8::undefined(scope); - let value = match func.call(scope, recv.into(), ¶ms) { + let recv = v8::undefined(&mut scope); + let value = match func.call(&mut scope, recv.into(), ¶ms) { Some(value) => value, - None => v8::Number::new(scope, 60.0).into(), + None => v8::Number::new(&mut scope, 60.0).into(), }; - std::mem::forget(cb); + + std::mem::forget(callback); + std::mem::forget(context); match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 => { - *(result as *mut i32) = serde_v8::from_v8(scope, value) + *(result as *mut i32) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_FLOAT => { - *(result as *mut f32) = serde_v8::from_v8(scope, value) + *(result as *mut f32) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_DOUBLE => { - *(result as *mut f64) = serde_v8::from_v8(scope, value) + *(result as *mut f64) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { - let u32x2: U32x2 = serde_v8::from_v8(scope, value) + let u32x2: U32x2 = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); *(result as *mut u64) = u64::from(u32x2); } FFI_TYPE_SINT8 => { - *(result as *mut i8) = serde_v8::from_v8(scope, value) + *(result as *mut i8) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_UINT8 => { - *(result as *mut u8) = serde_v8::from_v8(scope, value) + *(result as *mut u8) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_SINT16 => { - *(result as *mut i16) = serde_v8::from_v8(scope, value) + *(result as *mut i16) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_UINT16 => { - *(result as *mut u16) = serde_v8::from_v8(scope, value) + *(result as *mut u16) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_UINT32 => { - *(result as *mut u32) = serde_v8::from_v8(scope, value) + *(result as *mut u32) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_SINT64 => { - *(result as *mut i64) = serde_v8::from_v8(scope, value) + *(result as *mut i64) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_UINT64 => { - *(result as *mut u64) = serde_v8::from_v8(scope, value) + *(result as *mut u64) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); } FFI_TYPE_VOID => {} @@ -835,32 +842,55 @@ fn op_ffi_register_callback( let v8_value = cb.v8_value; let cb = v8::Local::::try_from(v8_value).unwrap(); - let global = unsafe { - // NOTE: `into_raw` leaks. - let global = v8::Global::new(&mut *scope, cb); - global.into_raw() + let info = { + let isolate_ptr: *mut v8::Isolate = { + let isolate: &mut v8::Isolate = &mut *scope; + isolate + }; + let isolate = unsafe { &mut *isolate_ptr }; + let callback = v8::Global::new(isolate, cb).into_raw(); + let context = + v8::Global::new(isolate, scope.get_current_context()).into_raw(); + + Box::leak(Box::new(CallbackInfo { + callback, + context, + isolate, + })) }; - - //let info = CallbackInfo::new(global); let cif = Cif::new( - args.parameters.iter().map(libffi::middle::Type::from), + args.parameters.into_iter().map(libffi::middle::Type::from), libffi::middle::Type::from(args.result.clone()), ); - let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, unsafe { std::mem::transmute::, &'static v8::Function>(global) }); + let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, info); - //std::mem::forget(info); - - let resource = RegisteredCallbackResource { - // info, - cb: global, - closure, - scope: *scope, - }; + let resource = RegisteredCallbackResource { closure, info }; Ok(state.resource_table.add(resource)) } +#[op] +fn op_ffi_deregister_callback(state: &mut deno_core::OpState, rid: ResourceId) { + let resource = state + .resource_table + .take::(rid) + .unwrap(); + + unsafe { Box::from_raw(resource.info as *mut CallbackInfo) }; +} + +#[op] +fn test_registered_callback(state: &mut deno_core::OpState, rid: ResourceId) { + let resource = state + .resource_table + .get::(rid) + .unwrap(); + + let fn_ptr: unsafe extern "C" fn() = *resource.closure.code_ptr(); + unsafe { fn_ptr() }; +} + #[op] fn op_ffi_call_ptr( state: &mut deno_core::OpState, From 352d98eedb5a3590d719a1630768f883208f8b0c Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 14 May 2022 10:37:39 +0530 Subject: [PATCH 03/64] Scope in stack or not --- ext/ffi/lib.rs | 15 ++++++++++++--- test.js | 7 +++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 test.js diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 13eec30ab2cb46..ddb65399861fca 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -14,7 +14,6 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::serde_v8; use deno_core::v8; -use deno_core::v8::Handle; use deno_core::Extension; use deno_core::OpState; use deno_core::Resource; @@ -28,6 +27,7 @@ use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; use std::cell::RefCell; +use std::cell::Cell; use std::collections::HashMap; use std::ffi::c_void; use std::ffi::CStr; @@ -717,6 +717,7 @@ struct CallbackInfo { pub callback: NonNull, pub context: NonNull, pub isolate: *mut v8::Isolate, + pub create_scope: Cell, } unsafe extern "C" fn deno_ffi_callback( @@ -728,7 +729,12 @@ unsafe extern "C" fn deno_ffi_callback( let isolate = &mut *info.isolate; let callback = v8::Global::from_raw(isolate, info.callback); let context = v8::Global::from_raw(isolate, info.context); - let mut scope = v8::HandleScope::with_context(isolate, context.clone()); + let mut scope = if info.create_scope.get() { + v8::HandleScope::with_context(isolate, context.clone()) + } else { + // CallbackScope + panic!("Scope is alive. Use it ;)") + }; let func = callback.open(&mut scope); let result = result as *mut c_void; let repr = std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); @@ -856,6 +862,7 @@ fn op_ffi_register_callback( callback, context, isolate, + create_scope: Cell::new(true), })) }; let cif = Cif::new( @@ -886,9 +893,11 @@ fn test_registered_callback(state: &mut deno_core::OpState, rid: ResourceId) { .resource_table .get::(rid) .unwrap(); - + let info: &CallbackInfo = unsafe { &*resource.info }; + info.create_scope.set(false); let fn_ptr: unsafe extern "C" fn() = *resource.closure.code_ptr(); unsafe { fn_ptr() }; + info.create_scope.set(true); } #[op] diff --git a/test.js b/test.js new file mode 100644 index 00000000000000..f122872a846c4d --- /dev/null +++ b/test.js @@ -0,0 +1,7 @@ +const callback = Deno.core.opSync("op_ffi_register_callback", { + parameters: [], + result: "void", +}, console.log); + +Deno.core.opSync("test_registered_callback", callback) + From 15261ea8d8c799be3bb04b6cf193cf18eced4a3e Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 14 May 2022 14:02:34 +0300 Subject: [PATCH 04/64] Functioning synchronous registered callbacks --- ext/ffi/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index ddb65399861fca..d8d21369311a63 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -728,12 +728,12 @@ unsafe extern "C" fn deno_ffi_callback( ) { let isolate = &mut *info.isolate; let callback = v8::Global::from_raw(isolate, info.callback); - let context = v8::Global::from_raw(isolate, info.context); + let context = std::mem::transmute::, v8::Local>(info.context); + let mut cb_scope = v8::CallbackScope::new(context); let mut scope = if info.create_scope.get() { - v8::HandleScope::with_context(isolate, context.clone()) + v8::HandleScope::with_context(isolate, context) } else { - // CallbackScope - panic!("Scope is alive. Use it ;)") + v8::HandleScope::new(&mut cb_scope) }; let func = callback.open(&mut scope); let result = result as *mut c_void; From f98424ff28de7fc21cfd675d2aadc24c512cf050 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 14 May 2022 17:58:21 +0530 Subject: [PATCH 05/64] Don't borrow across callback call --- ext/ffi/lib.rs | 29 ++++++++++++++++++++--------- test.js | 7 ++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index d8d21369311a63..12ed076b04cf16 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -26,8 +26,8 @@ use libffi::raw::*; use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; -use std::cell::RefCell; use std::cell::Cell; +use std::cell::RefCell; use std::collections::HashMap; use std::ffi::c_void; use std::ffi::CStr; @@ -728,7 +728,10 @@ unsafe extern "C" fn deno_ffi_callback( ) { let isolate = &mut *info.isolate; let callback = v8::Global::from_raw(isolate, info.callback); - let context = std::mem::transmute::, v8::Local>(info.context); + let context = std::mem::transmute::< + NonNull, + v8::Local, + >(info.context); let mut cb_scope = v8::CallbackScope::new(context); let mut scope = if info.create_scope.get() { v8::HandleScope::with_context(isolate, context) @@ -888,14 +891,22 @@ fn op_ffi_deregister_callback(state: &mut deno_core::OpState, rid: ResourceId) { } #[op] -fn test_registered_callback(state: &mut deno_core::OpState, rid: ResourceId) { - let resource = state - .resource_table - .get::(rid) - .unwrap(); - let info: &CallbackInfo = unsafe { &*resource.info }; +fn test_registered_callback( + state: Rc>, + rid: ResourceId, +) { + let (fn_ptr, info) = { + let state = &mut state.borrow_mut(); + let resource = state + .resource_table + .get::(rid) + .unwrap(); + let info: &CallbackInfo = unsafe { &*resource.info }; + + let fn_ptr: unsafe extern "C" fn() = *resource.closure.code_ptr(); + (fn_ptr, info) + }; info.create_scope.set(false); - let fn_ptr: unsafe extern "C" fn() = *resource.closure.code_ptr(); unsafe { fn_ptr() }; info.create_scope.set(true); } diff --git a/test.js b/test.js index f122872a846c4d..b5e0ace1715e71 100644 --- a/test.js +++ b/test.js @@ -1,7 +1,8 @@ const callback = Deno.core.opSync("op_ffi_register_callback", { parameters: [], result: "void", -}, console.log); - -Deno.core.opSync("test_registered_callback", callback) +}, function daCallback() { + console.log("Called"); +}); +Deno.core.opSync("test_registered_callback", callback); From 8b46001134678ae0aeb4d243219fe88f9e1dce3c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 17 May 2022 22:48:56 +0300 Subject: [PATCH 06/64] Move forward with parameter types in FFI callbacks --- ext/ffi/lib.rs | 189 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 127 insertions(+), 62 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 12ed076b04cf16..f86b4202904bc5 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -181,6 +181,8 @@ pub fn init(unstable: bool) -> Extension { .build() } +/// Defines the accepted types that can be used as +/// parameters and return values in FFI. #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] #[serde(rename_all = "lowercase")] enum NativeType { @@ -198,6 +200,7 @@ enum NativeType { F32, F64, Pointer, + Function, } impl From for libffi::middle::Type { @@ -217,10 +220,13 @@ impl From for libffi::middle::Type { NativeType::F32 => libffi::middle::Type::f32(), NativeType::F64 => libffi::middle::Type::f64(), NativeType::Pointer => libffi::middle::Type::pointer(), + NativeType::Function => libffi::middle::Type::pointer(), } } } +/// Intermediate format for easy translation from NativeType + V8 value +/// to libffi argument types. #[repr(C)] union NativeValue { void_value: (), @@ -291,6 +297,12 @@ impl NativeValue { } } } + NativeType::Function => { + // Self { + // pointer: value_as_uint::(value)?.into(), + // } + unreachable!(); + } }; Ok(value) } @@ -299,7 +311,10 @@ impl NativeValue { Self { pointer: ptr } } - unsafe fn as_arg(&self, native_type: NativeType) -> Arg { + unsafe fn as_arg( + &self, + native_type: NativeType + ) -> Arg { match native_type { NativeType::Void => Arg::new(&self.void_value), NativeType::U8 => Arg::new(&self.u8_value), @@ -314,7 +329,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::Pointer => Arg::new(&self.pointer), + NativeType::Pointer | NativeType::Function => Arg::new(&self.pointer), } } } @@ -544,13 +559,23 @@ where Ok(state.resource_table.add(resource)) } +#[derive(Deserialize)] +#[serde(untagged)] +enum FfiParameter { + Null, + //Bool(bool), + Number(serde_json::Number), + //Value(Value), + Buffer(Option), + //Function(Resource), +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct FfiCallArgs { rid: ResourceId, symbol: String, - parameters: Vec, - buffers: Vec>, + parameters: Vec, } #[derive(Deserialize)] @@ -558,8 +583,7 @@ struct FfiCallArgs { struct FfiCallPtrArgs { pointer: U32x2, def: ForeignFunction, - parameters: Vec, - buffers: Vec>, + parameters: Vec, } impl From for FfiCallArgs { @@ -568,7 +592,7 @@ impl From for FfiCallArgs { rid: 0, symbol: String::new(), parameters: args.parameters, - buffers: args.buffers, + //buffers: args.buffers, } } } @@ -596,51 +620,63 @@ impl FfiCallPtrArgs { } } -fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result { - let buffers: Vec> = args - .buffers - .iter() - .map(|buffer| buffer.as_ref().map(|buffer| &buffer[..])) - .collect(); - +fn ffi_parse_args( + parameters: &Vec, + parameter_types: &Vec, + resource_table: &deno_core::ResourceTable, +) -> Result, AnyError> { let mut native_values: Vec = vec![]; - for (&native_type, value) in symbol - .parameter_types - .iter() - .zip(args.parameters.into_iter()) + for (native_type, value) in + parameter_types.iter().zip(parameters.into_iter()) { - match native_type { - NativeType::Pointer => match value.as_u64() { - Some(idx) => { - let buf = buffers - .get(idx as usize) - .ok_or_else(|| { - generic_error(format!("No buffer present at index {}", idx)) - })? - .unwrap(); - native_values.push(NativeValue::buffer(buf.as_ptr())); - } - _ => { - let value = NativeValue::new(native_type, value)?; - native_values.push(value); - } - }, + match (native_type, &value) { + ( + NativeType::Pointer | NativeType::Function, + FfiParameter::Null | FfiParameter::Buffer(None), + ) => { + native_values.push(NativeValue { + pointer: ptr::null(), + }); + } + (NativeType::Pointer, FfiParameter::Buffer(Some(buf))) => { + native_values.push(NativeValue { + pointer: buf.as_ptr(), + }); + } + (NativeType::Function, FfiParameter::Number(value)) => { + let resource = resource_table + .get::(value.as_u64().unwrap() as u32) + .unwrap(); + native_values.push(NativeValue { + pointer: *resource.closure.code_ptr() as *const u8, + }); + } + (native_type, FfiParameter::Number(value)) => { + native_values.push(NativeValue::new(*native_type, serde_json::Value::Number(value.clone()))?); + } _ => { - let value = NativeValue::new(native_type, value)?; - native_values.push(value); + panic!("SHIIIT"); } } } - let call_args = symbol - .parameter_types - .iter() - .zip(native_values.iter()) - .map(|(&native_type, native_value)| unsafe { - native_value.as_arg(native_type) - }) - .collect::>(); + Ok( + parameter_types + .iter() + .zip(native_values.iter()) + .map(|(&native_type, native_value)| unsafe { + native_value.as_arg(native_type) + }) + .collect::>(), + ) +} + +fn ffi_call( + call_args: Vec, + symbol: &Symbol, +) -> Result { + //let call_args = ffi_parse_args(&args.parameters, symbol, resource_table)?; Ok(match symbol.result_type { NativeType::Void => { @@ -690,7 +726,7 @@ fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result { NativeType::F64 => { json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) } - NativeType::Pointer => { + NativeType::Pointer | NativeType::Function => { json!(U32x2::from(unsafe { symbol.cif.call::<*const u8>(symbol.ptr, &call_args) } as u64)) @@ -709,6 +745,10 @@ impl Resource for RegisteredCallbackResource { } fn close(self: Rc) { + let info = unsafe { Box::from_raw(self.info as *mut CallbackInfo) }; + let isolate = unsafe { info.isolate.as_mut().unwrap() }; + unsafe { v8::Global::from_raw(isolate, info.callback) }; + unsafe { v8::Global::from_raw(isolate, info.context) }; drop(self) } } @@ -882,33 +922,48 @@ fn op_ffi_register_callback( #[op] fn op_ffi_deregister_callback(state: &mut deno_core::OpState, rid: ResourceId) { - let resource = state + state .resource_table .take::(rid) - .unwrap(); + .unwrap() + .close(); +} - unsafe { Box::from_raw(resource.info as *mut CallbackInfo) }; +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestCallbackArgs { + rid: ResourceId, + parameters: Vec, } #[op] fn test_registered_callback( state: Rc>, - rid: ResourceId, -) { + args: TestCallbackArgs, +) -> Result { let (fn_ptr, info) = { let state = &mut state.borrow_mut(); let resource = state .resource_table - .get::(rid) + .get::(args.rid) .unwrap(); let info: &CallbackInfo = unsafe { &*resource.info }; - let fn_ptr: unsafe extern "C" fn() = *resource.closure.code_ptr(); + let fn_ptr: unsafe extern "C" fn(u32) -> u32 = unsafe { *resource.closure.instantiate_code_ptr() }; (fn_ptr, info) }; info.create_scope.set(false); - unsafe { fn_ptr() }; + let mut arg: u32 = 0; + match &args.parameters[0] { + FfiParameter::Null => {}, + FfiParameter::Number(value) => { + arg = value_as_uint(serde_json::Value::Number(value.clone()))?; + }, + FfiParameter::Buffer(_) => {}, +} + let result = unsafe { fn_ptr(arg) }; info.create_scope.set(true); + Ok(result.into()) } #[op] @@ -925,7 +980,8 @@ where permissions.check(None)?; let symbol = args.get_symbol(); - ffi_call(args.into(), &symbol) + let call_args = ffi_parse_args(&args.parameters, &symbol.parameter_types, &state.resource_table)?; + ffi_call(call_args, &symbol) } #[op] @@ -945,9 +1001,13 @@ where } let symbol = args.get_symbol(); - tokio::task::spawn_blocking(move || ffi_call(args.into(), &symbol)) - .await - .unwrap() + let call_args = ffi_parse_args(&args.parameters, &symbol.parameter_types, &state.borrow_mut().resource_table)?; + tokio::task::spawn_blocking(move || { + //ffi_call(call_args, &symbol) + Ok(serde_json::to_value(()).unwrap()) + }) + .await + .unwrap() } #[derive(Deserialize)] @@ -1009,7 +1069,7 @@ fn op_ffi_get_static( NativeType::F64 => { json!(unsafe { ptr::read_unaligned(data_ptr as *const f64) }) } - NativeType::Pointer => { + NativeType::Pointer | NativeType::Function => { json!(U32x2::from(data_ptr as *const u8 as u64)) } }) @@ -1028,8 +1088,10 @@ fn op_ffi_call( .symbols .get(&args.symbol) .ok_or_else(bad_resource_id)?; + + let call_args = ffi_parse_args(&args.parameters, &symbol.parameter_types, &state.resource_table)?; - ffi_call(args, symbol) + ffi_call(call_args, symbol) } /// A non-blocking FFI call. @@ -1048,9 +1110,12 @@ async fn op_ffi_call_nonblocking( .ok_or_else(bad_resource_id)? .clone(); - tokio::task::spawn_blocking(move || ffi_call(args, &symbol)) - .await - .unwrap() + tokio::task::spawn_blocking(move || { + //ffi_call(args, &symbol, state.borrow_mut().resource_table) + Ok(serde_json::to_value(()).unwrap()) + }) + .await + .unwrap() } #[op] From 7a10c38b07fae6e7fb9ed765b47c9a3a43386c64 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 18 May 2022 00:00:33 +0300 Subject: [PATCH 07/64] Adapt, does not function properly --- ext/ffi/00_ffi.js | 16 ++++------------ ext/ffi/lib.rs | 10 ++++++++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index fb4fcbfb83171f..6612027d601db3 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -147,7 +147,6 @@ function prepareArgs(types, args) { const parameters = []; - const buffers = []; for (let i = 0; i < types.length; i++) { const type = types[i]; @@ -158,14 +157,11 @@ ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, arg?.buffer) && arg.byteLength !== undefined ) { - parameters.push(buffers.length); - buffers.push(arg); + parameters.push(arg); } else if (ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg)) { parameters.push(pack64(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", @@ -184,7 +180,7 @@ } } - return { parameters, buffers }; + return { parameters }; } function unpackResult(type, result) { @@ -214,7 +210,7 @@ } call(...args) { - const { parameters, buffers } = prepareArgs( + const { parameters } = prepareArgs( this.definition.parameters, args, ); @@ -223,7 +219,6 @@ pointer: pack64(this.pointer.value), def: this.definition, parameters, - buffers, }); if (this.definition.result === "pointer") { @@ -236,7 +231,6 @@ pointer: pack64(this.pointer.value), def: this.definition, parameters, - buffers, }); if (this.definition.result === "pointer") { @@ -299,14 +293,13 @@ const resultType = symbols[symbol].result; const fn = (...args) => { - const { parameters, buffers } = prepareArgs(types, args); + const { parameters } = prepareArgs(types, args); if (isNonBlocking) { const promise = core.opAsync("op_ffi_call_nonblocking", { rid: this.#rid, symbol, parameters, - buffers, }); if (resultType === "pointer") { @@ -319,7 +312,6 @@ rid: this.#rid, symbol, parameters, - buffers, }); return unpackResult(resultType, result); diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index f86b4202904bc5..08e3d957fc934e 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -566,6 +566,7 @@ enum FfiParameter { //Bool(bool), Number(serde_json::Number), //Value(Value), + Pointer(U32x2), Buffer(Option), //Function(Resource), } @@ -639,6 +640,11 @@ fn ffi_parse_args( pointer: ptr::null(), }); } + (NativeType::Pointer | NativeType::Function, FfiParameter::Pointer(val)) => { + native_values.push(NativeValue { + pointer: u64::from(*val) as *const u8, + }) + } (NativeType::Pointer, FfiParameter::Buffer(Some(buf))) => { native_values.push(NativeValue { pointer: buf.as_ptr(), @@ -955,11 +961,11 @@ fn test_registered_callback( info.create_scope.set(false); let mut arg: u32 = 0; match &args.parameters[0] { - FfiParameter::Null => {}, FfiParameter::Number(value) => { arg = value_as_uint(serde_json::Value::Number(value.clone()))?; }, - FfiParameter::Buffer(_) => {}, + _ => {}, + } let result = unsafe { fn_ptr(arg) }; info.create_scope.set(true); From 2335d2a7f70825629eda267ae9fab94719306b41 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 19 May 2022 00:07:11 +0300 Subject: [PATCH 08/64] Use serde_v8::Value --- ext/ffi/00_ffi.js | 6 +- ext/ffi/lib.rs | 311 +++++++++++++++++++++++++--------------------- 2 files changed, 170 insertions(+), 147 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 6612027d601db3..04c3934af8228f 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -159,7 +159,7 @@ ) { parameters.push(arg); } else if (ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg)) { - parameters.push(pack64(arg.value)); + parameters.push(arg.value); } else if (arg === null) { parameters.push(null); } else { @@ -216,7 +216,7 @@ ); if (this.definition.nonblocking) { const promise = core.opAsync("op_ffi_call_ptr_nonblocking", { - pointer: pack64(this.pointer.value), + pointer: this.pointer.value, def: this.definition, parameters, }); @@ -228,7 +228,7 @@ return promise; } else { const result = core.opSync("op_ffi_call_ptr", { - pointer: pack64(this.pointer.value), + pointer: this.pointer.value, def: this.definition, parameters, }); diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 08e3d957fc934e..58d73fdb669cc7 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -154,9 +154,9 @@ pub fn init(unstable: bool) -> Extension { op_ffi_load::decl::

(), op_ffi_get_static::decl(), op_ffi_call::decl(), - op_ffi_call_nonblocking::decl(), + //op_ffi_call_nonblocking::decl(), op_ffi_call_ptr::decl::

(), - op_ffi_call_ptr_nonblocking::decl::

(), + //op_ffi_call_ptr_nonblocking::decl::

(), op_ffi_ptr_of::decl::

(), op_ffi_buf_copy_into::decl::

(), op_ffi_cstr_read::decl::

(), @@ -311,10 +311,7 @@ impl NativeValue { Self { pointer: ptr } } - unsafe fn as_arg( - &self, - native_type: NativeType - ) -> Arg { + unsafe fn as_arg(&self, native_type: NativeType) -> Arg { match native_type { NativeType::Void => Arg::new(&self.void_value), NativeType::U8 => Arg::new(&self.u8_value), @@ -559,36 +556,24 @@ where Ok(state.resource_table.add(resource)) } -#[derive(Deserialize)] -#[serde(untagged)] -enum FfiParameter { - Null, - //Bool(bool), - Number(serde_json::Number), - //Value(Value), - Pointer(U32x2), - Buffer(Option), - //Function(Resource), -} - #[derive(Deserialize)] #[serde(rename_all = "camelCase")] -struct FfiCallArgs { +struct FfiCallArgs<'scope> { rid: ResourceId, symbol: String, - parameters: Vec, + parameters: serde_v8::Value<'scope>, } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] -struct FfiCallPtrArgs { +struct FfiCallPtrArgs<'scope> { pointer: U32x2, def: ForeignFunction, - parameters: Vec, + parameters: serde_v8::Value<'scope>, } -impl From for FfiCallArgs { - fn from(args: FfiCallPtrArgs) -> Self { +impl<'scope> From> for FfiCallArgs<'scope> { + fn from(args: FfiCallPtrArgs<'scope>) -> Self { FfiCallArgs { rid: 0, symbol: String::new(), @@ -598,7 +583,7 @@ impl From for FfiCallArgs { } } -impl FfiCallPtrArgs { +impl<'scope> FfiCallPtrArgs<'scope> { fn get_symbol(&self) -> Symbol { let fn_ptr: u64 = self.pointer.into(); let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); @@ -621,67 +606,96 @@ impl FfiCallPtrArgs { } } -fn ffi_parse_args( - parameters: &Vec, +fn ffi_parse_args<'scope>( + scope: &mut v8::HandleScope<'scope>, + args: serde_v8::Value<'scope>, parameter_types: &Vec, resource_table: &deno_core::ResourceTable, -) -> Result, AnyError> { - let mut native_values: Vec = vec![]; - - for (native_type, value) in - parameter_types.iter().zip(parameters.into_iter()) - { - match (native_type, &value) { - ( - NativeType::Pointer | NativeType::Function, - FfiParameter::Null | FfiParameter::Buffer(None), - ) => { - native_values.push(NativeValue { - pointer: ptr::null(), - }); +) -> Result, AnyError> +where + 'scope: 'scope, +{ + let args = v8::Local::::try_from(args.v8_value).unwrap(); + let mut ffi_args: Vec = Vec::with_capacity(parameter_types.len()); + + for (index, native_type) in parameter_types.iter().enumerate() { + let value = args.get_index(scope, index as u32).unwrap(); + match native_type { + NativeType::Void => { + unreachable!(); } - (NativeType::Pointer | NativeType::Function, FfiParameter::Pointer(val)) => { - native_values.push(NativeValue { - pointer: u64::from(*val) as *const u8, - }) + NativeType::U8 => { + let value = value.uint32_value(scope).unwrap() as u8; + ffi_args.push(Arg::new(&value)) } - (NativeType::Pointer, FfiParameter::Buffer(Some(buf))) => { - native_values.push(NativeValue { - pointer: buf.as_ptr(), - }); + NativeType::I8 => { + let value = value.int32_value(scope).unwrap() as i8; + ffi_args.push(Arg::new(&value)); } - (NativeType::Function, FfiParameter::Number(value)) => { - let resource = resource_table - .get::(value.as_u64().unwrap() as u32) - .unwrap(); - native_values.push(NativeValue { - pointer: *resource.closure.code_ptr() as *const u8, - }); + NativeType::U16 => { + let value = value.uint32_value(scope).unwrap() as u16; + ffi_args.push(Arg::new(&value)); } - (native_type, FfiParameter::Number(value)) => { - native_values.push(NativeValue::new(*native_type, serde_json::Value::Number(value.clone()))?); + NativeType::I16 => { + let value = value.int32_value(scope).unwrap() as i16; + ffi_args.push(Arg::new(&value)); } - _ => { - panic!("SHIIIT"); + NativeType::U32 => { + let value = value.uint32_value(scope).unwrap(); + ffi_args.push(Arg::new(&value)); + } + NativeType::I32 => { + let value = value.int32_value(scope).unwrap(); + ffi_args.push(Arg::new(&value)); + } + NativeType::U64 => { + let value = value.integer_value(scope).unwrap() as u64; + ffi_args.push(Arg::new(&value)); + } + NativeType::I64 => { + let value = value.integer_value(scope).unwrap() as i64; + ffi_args.push(Arg::new(&value)); + } + NativeType::USize => { + let value = value.integer_value(scope).unwrap() as usize; + ffi_args.push(Arg::new(&value)); + } + NativeType::ISize => { + let value = value.integer_value(scope).unwrap() as isize; + ffi_args.push(Arg::new(&value)); + } + NativeType::F32 => { + let value = value.number_value(scope).unwrap() as f32; + ffi_args.push(Arg::new(&value)); + } + NativeType::F64 => { + let value = value.integer_value(scope).unwrap() as isize; + ffi_args.push(Arg::new(&value)); + } + NativeType::Pointer => { + if value.is_null() { + let value: *const c_void = ptr::null(); + ffi_args.push(Arg::new(&value)) + } else if value.is_big_int() { + let value = v8::Local::::try_from(value)?; + let value = value.u64_value().0 as *const u8; + ffi_args.push(Arg::new(&value)); + } else { + let value: ZeroCopyBuf = serde_v8::from_v8(scope, value).unwrap(); + let value: &[u8] = &value[..]; + ffi_args.push(Arg::new(&value.as_ptr())); + } + } + NativeType::Function => { + todo!(); } } } - Ok( - parameter_types - .iter() - .zip(native_values.iter()) - .map(|(&native_type, native_value)| unsafe { - native_value.as_arg(native_type) - }) - .collect::>(), - ) + Ok(ffi_args) } -fn ffi_call( - call_args: Vec, - symbol: &Symbol, -) -> Result { +fn ffi_call(call_args: Vec, symbol: &Symbol) -> Result { //let call_args = ffi_parse_args(&args.parameters, symbol, resource_table)?; Ok(match symbol.result_type { @@ -935,45 +949,37 @@ fn op_ffi_deregister_callback(state: &mut deno_core::OpState, rid: ResourceId) { .close(); } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestCallbackArgs { - rid: ResourceId, - parameters: Vec, -} - -#[op] +#[op(v8)] fn test_registered_callback( + scope: &mut v8::HandleScope, state: Rc>, - args: TestCallbackArgs, + rid: ResourceId, + args: serde_v8::Value, ) -> Result { let (fn_ptr, info) = { let state = &mut state.borrow_mut(); let resource = state .resource_table - .get::(args.rid) + .get::(rid) .unwrap(); let info: &CallbackInfo = unsafe { &*resource.info }; - let fn_ptr: unsafe extern "C" fn(u32) -> u32 = unsafe { *resource.closure.instantiate_code_ptr() }; + let fn_ptr: unsafe extern "C" fn(u32) -> u32 = + unsafe { *resource.closure.instantiate_code_ptr() }; (fn_ptr, info) }; info.create_scope.set(false); - let mut arg: u32 = 0; - match &args.parameters[0] { - FfiParameter::Number(value) => { - arg = value_as_uint(serde_json::Value::Number(value.clone()))?; - }, - _ => {}, - -} + let args = v8::Local::::try_from(args.v8_value).unwrap(); + let arg = args.get_index(scope, 0).unwrap(); + let arg: u32 = arg.uint32_value(scope).unwrap(); let result = unsafe { fn_ptr(arg) }; info.create_scope.set(true); Ok(result.into()) } -#[op] +#[op(v8)] fn op_ffi_call_ptr( + scope: &mut v8::HandleScope, state: &mut deno_core::OpState, args: FfiCallPtrArgs, ) -> Result @@ -986,35 +992,46 @@ where permissions.check(None)?; let symbol = args.get_symbol(); - let call_args = ffi_parse_args(&args.parameters, &symbol.parameter_types, &state.resource_table)?; + let call_args = ffi_parse_args( + unsafe { std::mem::transmute(scope) }, + args.parameters, + &symbol.parameter_types, + &state.resource_table, + )?; ffi_call(call_args, &symbol) } -#[op] -async fn op_ffi_call_ptr_nonblocking( - state: Rc>, - args: FfiCallPtrArgs, -) -> Result -where - FP: FfiPermissions + 'static, -{ - check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - - { - let mut state = state.borrow_mut(); - let permissions = state.borrow_mut::(); - permissions.check(None)?; - } - - let symbol = args.get_symbol(); - let call_args = ffi_parse_args(&args.parameters, &symbol.parameter_types, &state.borrow_mut().resource_table)?; - tokio::task::spawn_blocking(move || { - //ffi_call(call_args, &symbol) - Ok(serde_json::to_value(()).unwrap()) - }) - .await - .unwrap() -} +// #[op(v8)] +// async fn op_ffi_call_ptr_nonblocking( +// scope: &mut v8::HandleScope<'scope>, +// state: Rc>, +// args: FfiCallPtrArgs<'scope>, +// ) -> Result +// where +// FP: FfiPermissions + 'static, +// 'scope: 'scope +// { +// check_unstable2(&state, "Deno.UnsafeFnPointer#call"); + +// { +// let mut state = state.borrow_mut(); +// let permissions = state.borrow_mut::(); +// permissions.check(None)?; +// } + +// let symbol = args.get_symbol(); +// let call_args = ffi_parse_args( +// &args.parameters, +// &symbol.parameter_types, +// &state.borrow_mut().resource_table, +// )?; +// tokio::task::spawn_blocking(move || { +// //ffi_call(call_args, &symbol) +// Ok(serde_json::to_value(()).unwrap()) +// }) +// .await +// .unwrap() +// } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] @@ -1081,8 +1098,9 @@ fn op_ffi_get_static( }) } -#[op] +#[op(v8)] fn op_ffi_call( + scope: &mut v8::HandleScope, state: &mut deno_core::OpState, args: FfiCallArgs, ) -> Result { @@ -1094,35 +1112,40 @@ fn op_ffi_call( .symbols .get(&args.symbol) .ok_or_else(bad_resource_id)?; - - let call_args = ffi_parse_args(&args.parameters, &symbol.parameter_types, &state.resource_table)?; + + let call_args = ffi_parse_args( + unsafe { std::mem::transmute(scope) }, + args.parameters, + &symbol.parameter_types, + &state.resource_table, + )?; ffi_call(call_args, symbol) } /// A non-blocking FFI call. -#[op] -async fn op_ffi_call_nonblocking( - state: Rc>, - args: FfiCallArgs, -) -> Result { - let resource = state - .borrow() - .resource_table - .get::(args.rid)?; - let symbols = &resource.symbols; - let symbol = symbols - .get(&args.symbol) - .ok_or_else(bad_resource_id)? - .clone(); - - tokio::task::spawn_blocking(move || { - //ffi_call(args, &symbol, state.borrow_mut().resource_table) - Ok(serde_json::to_value(()).unwrap()) - }) - .await - .unwrap() -} +// #[op] +// async fn op_ffi_call_nonblocking( +// state: Rc>, +// args: FfiCallArgs, +// ) -> Result { +// let resource = state +// .borrow() +// .resource_table +// .get::(args.rid)?; +// let symbols = &resource.symbols; +// let symbol = symbols +// .get(&args.symbol) +// .ok_or_else(bad_resource_id)? +// .clone(); + +// tokio::task::spawn_blocking(move || { +// //ffi_call(args, &symbol, state.borrow_mut().resource_table) +// Ok(serde_json::to_value(()).unwrap()) +// }) +// .await +// .unwrap() +// } #[op] fn op_ffi_ptr_of( From f63cebaad7ab3d906abcdcec1ba989c2c83afcea Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 19 May 2022 07:18:10 +0300 Subject: [PATCH 09/64] fmt --- cli/dts/lib.deno.unstable.d.ts | 86 ++++++++++++++++------------------ 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 1c666d4b4fa176..4422df0488b567 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -124,7 +124,7 @@ declare namespace Deno { export function bench( name: string, options: Omit, - fn: () => void | Promise + fn: () => void | Promise, ): void; /** Register a bench which will be run when `deno bench` is used on the command @@ -147,7 +147,7 @@ declare namespace Deno { */ export function bench( options: Omit, - fn: () => void | Promise + fn: () => void | Promise, ): void; /** Register a bench which will be run when `deno bench` is used on the command @@ -170,7 +170,7 @@ declare namespace Deno { */ export function bench( options: Omit, - fn: () => void | Promise + fn: () => void | Promise, ): void; /** @@ -344,14 +344,15 @@ declare namespace Deno { type NativeParameterType = | NativeType | { - function: Omit; - }; + function: Omit; + }; /** A foreign function as defined by its parameter and result types */ export interface ForeignFunction< - Parameters extends readonly NativeParameterType[] = readonly NativeParameterType[], + Parameters extends readonly NativeParameterType[] = + readonly NativeParameterType[], Result extends NativeType = NativeType, - NonBlocking extends boolean = boolean + NonBlocking extends boolean = boolean, > { /** Name of the symbol, defaults to the key name in symbols object. */ name?: string; @@ -397,29 +398,27 @@ declare namespace Deno { /** Infers a foreign function parameter list. */ type StaticForeignFunctionParameters< - T extends readonly NativeParameterType[] + T extends readonly NativeParameterType[], > = [ ...{ [K in keyof T]: StaticForeignFunctionParameter; - } + }, ]; /** Infers a foreign symbol */ type StaticForeignSymbol = - T extends ForeignFunction - ? ( - ...args: StaticForeignFunctionParameters - ) => ConditionalAsync< - T["nonblocking"], - StaticForeignFunctionResult - > - : T extends ForeignStatic - ? StaticForeignFunctionResult + T extends ForeignFunction ? ( + ...args: StaticForeignFunctionParameters + ) => ConditionalAsync< + T["nonblocking"], + StaticForeignFunctionResult + > + : T extends ForeignStatic ? StaticForeignFunctionResult : never; type ConditionalAsync< IsAsync extends boolean | undefined, - T + T, > = IsAsync extends true ? Promise : T; /** Infers a foreign library interface */ @@ -533,7 +532,7 @@ declare namespace Deno { */ export function dlopen( filename: string | URL, - symbols: S + symbols: S, ): DynamicLibrary; /** The log category for a diagnostic message. */ @@ -603,7 +602,7 @@ declare namespace Deno { export function setRaw( rid: number, mode: boolean, - options?: SetRawOptions + options?: SetRawOptions, ): void; /** **UNSTABLE**: needs investigation into high precision time. @@ -620,7 +619,7 @@ declare namespace Deno { export function utimeSync( path: string | URL, atime: number | Date, - mtime: number | Date + mtime: number | Date, ): void; /** **UNSTABLE**: needs investigation into high precision time. @@ -637,7 +636,7 @@ declare namespace Deno { export function utime( path: string | URL, atime: number | Date, - mtime: number | Date + mtime: number | Date, ): Promise; export function run< @@ -649,7 +648,7 @@ declare namespace Deno { clearEnv?: boolean; gid?: number; uid?: number; - } + }, >(opt: T): Process; /** **UNSTABLE**: New API, yet to be vetted. Additional consideration is still @@ -721,7 +720,7 @@ declare namespace Deno { * ``` */ export function createHttpClient( - options: CreateHttpClientOptions + options: CreateHttpClientOptions, ): HttpClient; /** **UNSTABLE**: needs investigation into high precision time. @@ -738,7 +737,7 @@ declare namespace Deno { export function futimeSync( rid: number, atime: number | Date, - mtime: number | Date + mtime: number | Date, ): void; /** **UNSTABLE**: needs investigation into high precision time. @@ -755,7 +754,7 @@ declare namespace Deno { export function futime( rid: number, atime: number | Date, - mtime: number | Date + mtime: number | Date, ): Promise; /** **UNSTABLE**: new API, yet to be vetted. @@ -806,7 +805,7 @@ declare namespace Deno { * * Requires `allow-read` and `allow-write` permission. */ export function listen( - options: UnixListenOptions & { transport: "unix" } + options: UnixListenOptions & { transport: "unix" }, ): Listener; /** **UNSTABLE**: new API, yet to be vetted @@ -827,7 +826,7 @@ declare namespace Deno { * * Requires `allow-net` permission. */ export function listenDatagram( - options: ListenOptions & { transport: "udp" } + options: ListenOptions & { transport: "udp" }, ): DatagramConn; /** **UNSTABLE**: new API, yet to be vetted @@ -843,7 +842,7 @@ declare namespace Deno { * * Requires `allow-read` and `allow-write` permission. */ export function listenDatagram( - options: UnixListenOptions & { transport: "unixpacket" } + options: UnixListenOptions & { transport: "unixpacket" }, ): DatagramConn; export interface UnixConnectOptions { @@ -1001,7 +1000,7 @@ declare namespace Deno { * `request`, otherwise event loop might deadlock. */ export function upgradeHttp( - request: Request + request: Request, ): Promise<[Deno.Conn, Uint8Array]>; export interface SpawnOptions { @@ -1066,18 +1065,15 @@ declare namespace Deno { */ export function spawnChild( command: string | URL, - options?: T + options?: T, ): Child; export class Child { - readonly stdin: T["stdin"] extends "piped" - ? WritableStream + readonly stdin: T["stdin"] extends "piped" ? WritableStream : null; - readonly stdout: T["stdout"] extends "inherit" | "null" - ? null + readonly stdout: T["stdout"] extends "inherit" | "null" ? null : ReadableStream; - readonly stderr: T["stderr"] extends "inherit" | "null" - ? null + readonly stderr: T["stderr"] extends "inherit" | "null" ? null : ReadableStream; readonly pid: number; @@ -1109,7 +1105,7 @@ declare namespace Deno { */ export function spawn( command: string | URL, - options?: T + options?: T, ): Promise>; /** @@ -1131,15 +1127,15 @@ declare namespace Deno { */ export function spawnSync( command: string | URL, - options?: T + options?: T, ): SpawnOutput; export type ChildStatus = | { - success: true; - code: 0; - signal: null; - } + success: true; + code: 0; + signal: null; + } | { success: false; code: number; @@ -1155,7 +1151,7 @@ declare namespace Deno { declare function fetch( input: Request | URL | string, - init?: RequestInit & { client: Deno.HttpClient } + init?: RequestInit & { client: Deno.HttpClient }, ): Promise; declare interface WorkerOptions { From cd8260d941e9e83ee6ede94a597dcde5ea8e06e6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 21 May 2022 20:07:11 +0300 Subject: [PATCH 10/64] Pick ops stuff from sqlite bindings --- ops/lib.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ops/lib.rs b/ops/lib.rs index 167ca1ce668079..a777355055c449 100644 --- a/ops/lib.rs +++ b/ops/lib.rs @@ -73,7 +73,18 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream { let MacroArgs { is_unstable, is_v8 } = margs; let func = syn::parse::(item).expect("expected a function"); let name = &func.sig.ident; - let generics = &func.sig.generics; + let mut generics = func.sig.generics.clone(); + let scope_lifetime = + syn::LifetimeDef::new(syn::Lifetime::new("'scope", Span::call_site())); + if generics + .lifetimes() + .find(|def| **def == scope_lifetime) + .is_none() + { + generics + .params + .push(syn::GenericParam::Lifetime(scope_lifetime)); + } let type_params = exclude_lifetime_params(&func.sig.generics.params); let where_clause = &func.sig.generics.where_clause; @@ -131,7 +142,7 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream { #original_func pub fn v8_func #generics ( - scope: &mut #core::v8::HandleScope, + scope: &mut #core::v8::HandleScope<'scope>, args: #core::v8::FunctionCallbackArguments, mut rv: #core::v8::ReturnValue, ) #where_clause { @@ -386,6 +397,7 @@ fn is_unit_result(ty: impl ToTokens) -> bool { fn is_mut_ref_opstate(arg: &syn::FnArg) -> bool { tokens(arg).ends_with(": & mut OpState") || tokens(arg).ends_with(": & mut deno_core :: OpState") + || tokens(arg).ends_with("mut OpState") } fn is_rc_refcell_opstate(arg: &syn::FnArg) -> bool { @@ -398,6 +410,7 @@ fn is_handle_scope(arg: &syn::FnArg) -> bool { || tokens(arg).ends_with(": & mut v8 :: HandleScope < 'a >") || tokens(arg).ends_with(": & mut deno_core :: v8 :: HandleScope") || tokens(arg).ends_with(": & mut deno_core :: v8 :: HandleScope < 'a >") + || tokens(arg).contains("mut v8 :: HandleScope") } fn is_future(ty: impl ToTokens) -> bool { From 78a12cda93cd4699ded571beb6b43b6f02e8b4ce Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 21 May 2022 23:50:54 +0300 Subject: [PATCH 11/64] Try async fixing --- ext/ffi/lib.rs | 126 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 42 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 58d73fdb669cc7..e8e2bfe7447b60 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -229,7 +229,7 @@ impl From for libffi::middle::Type { /// to libffi argument types. #[repr(C)] union NativeValue { - void_value: (), + //void_value: (), u8_value: u8, i8_value: i8, u16_value: u16, @@ -245,56 +245,71 @@ union NativeValue { pointer: *const u8, } +unsafe impl Send for NativeValue {} + impl NativeValue { - fn new(native_type: NativeType, value: Value) -> Result { + fn new( + scope: &mut v8::HandleScope, + native_type: NativeType, + value: v8::Local, + ) -> Result { let value = match native_type { - NativeType::Void => Self { void_value: () }, + NativeType::Void => { + unreachable!() + } NativeType::U8 => Self { - u8_value: value_as_uint::(value)?, + u8_value: value.uint32_value(scope).unwrap() as u8, }, NativeType::I8 => Self { - i8_value: value_as_int::(value)?, + i8_value: value.int32_value(scope).unwrap() as i8, }, NativeType::U16 => Self { - u16_value: value_as_uint::(value)?, + u16_value: value.uint32_value(scope).unwrap() as u16, }, NativeType::I16 => Self { - i16_value: value_as_int::(value)?, + i16_value: value.int32_value(scope).unwrap() as i16, }, NativeType::U32 => Self { - u32_value: value_as_uint::(value)?, + u32_value: value.uint32_value(scope).unwrap(), }, NativeType::I32 => Self { - i32_value: value_as_int::(value)?, + i32_value: value.int32_value(scope).unwrap(), }, NativeType::U64 => Self { - u64_value: value_as_uint::(value)?, + u64_value: value.integer_value(scope).unwrap() as u64, }, NativeType::I64 => Self { - i64_value: value_as_int::(value)?, + i64_value: value.integer_value(scope).unwrap() as i64, }, NativeType::USize => Self { - usize_value: value_as_uint::(value)?, + usize_value: value.integer_value(scope).unwrap() as usize, }, NativeType::ISize => Self { - isize_value: value_as_int::(value)?, + isize_value: value.integer_value(scope).unwrap() as isize, }, NativeType::F32 => Self { - f32_value: value_as_f32(value)?, + f32_value: value.number_value(scope).unwrap() as f32, }, NativeType::F64 => Self { - f64_value: value_as_f64(value)?, + f64_value: value.number_value(scope).unwrap() as f64, }, NativeType::Pointer => { if value.is_null() { Self { pointer: ptr::null(), } - } else { + } else if value.is_big_int() { + let value = v8::Local::::try_from(value)?; + let value = value.u64_value().0 as *const u8; + Self { pointer: value } + } else if value.is_array_buffer() || value.is_array_buffer_view() { + let value: ZeroCopyBuf = serde_v8::from_v8(scope, value).unwrap(); + let value: &[u8] = &value[..]; Self { - pointer: u64::from(serde_json::from_value::(value)?) - as *const u8, + pointer: value.as_ptr(), } + } else { + return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer or ArrayBufferview")); } } NativeType::Function => { @@ -313,7 +328,9 @@ impl NativeValue { unsafe fn as_arg(&self, native_type: NativeType) -> Arg { match native_type { - NativeType::Void => Arg::new(&self.void_value), + NativeType::Void => { + unreachable!() + } NativeType::U8 => Arg::new(&self.u8_value), NativeType::I8 => Arg::new(&self.i8_value), NativeType::U16 => Arg::new(&self.u16_value), @@ -669,7 +686,7 @@ where ffi_args.push(Arg::new(&value)); } NativeType::F64 => { - let value = value.integer_value(scope).unwrap() as isize; + let value = value.integer_value(scope).unwrap() as f64; ffi_args.push(Arg::new(&value)); } NativeType::Pointer => { @@ -680,10 +697,12 @@ where let value = v8::Local::::try_from(value)?; let value = value.u64_value().0 as *const u8; ffi_args.push(Arg::new(&value)); - } else { + } else if value.is_array_buffer() || value.is_array_buffer_view() { let value: ZeroCopyBuf = serde_v8::from_v8(scope, value).unwrap(); let value: &[u8] = &value[..]; ffi_args.push(Arg::new(&value.as_ptr())); + } else { + return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer or ArrayBufferView")); } } NativeType::Function => { @@ -1124,28 +1143,51 @@ fn op_ffi_call( } /// A non-blocking FFI call. -// #[op] -// async fn op_ffi_call_nonblocking( -// state: Rc>, -// args: FfiCallArgs, -// ) -> Result { -// let resource = state -// .borrow() -// .resource_table -// .get::(args.rid)?; -// let symbols = &resource.symbols; -// let symbol = symbols -// .get(&args.symbol) -// .ok_or_else(bad_resource_id)? -// .clone(); +#[op(v8)] +async fn op_ffi_call_nonblocking<'scope>( + scope: &mut v8::HandleScope<'scope>, + state: Rc>, + args: FfiCallArgs<'scope>, +) -> Result +where + 'scope: 'scope, +{ + let resource = state + .borrow() + .resource_table + .get::(args.rid)?; + let symbols = &resource.symbols; + let symbol = symbols + .get(&args.symbol) + .ok_or_else(bad_resource_id)? + .clone(); + + let v8_array = + v8::Local::::try_from(args.parameters.v8_value).unwrap(); + let mut native_values: Vec<(NativeType, NativeValue)> = + Vec::with_capacity(symbol.parameter_types.len()); + + for (index, native_type) in symbol.parameter_types.iter().enumerate() { + let value = v8_array.get_index(scope, index as u32).unwrap(); + native_values.push(( + native_type.clone(), + NativeValue::new(scope, *native_type, value)?, + )); + } -// tokio::task::spawn_blocking(move || { -// //ffi_call(args, &symbol, state.borrow_mut().resource_table) -// Ok(serde_json::to_value(()).unwrap()) -// }) -// .await -// .unwrap() -// } + tokio::task::spawn_blocking(move || { + ffi_call( + native_values + .iter() + .map(|(native_type, native_value)| unsafe { + native_value.as_arg(*native_type) + }) + .collect(), + &symbol, + ) + }) + .await? +} #[op] fn op_ffi_ptr_of( From 4de8ef8b4297aab90ce8efecd2f1c64930dff55c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 22 May 2022 00:42:32 +0300 Subject: [PATCH 12/64] Work work --- ext/ffi/00_ffi.js | 17 ++++++ ext/ffi/lib.rs | 126 ++++++++++++++++++++++----------------- runtime/js/90_deno_ns.js | 1 + 3 files changed, 89 insertions(+), 55 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 04c3934af8228f..796c3790938fd3 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -242,6 +242,22 @@ } } + class RegisteredCallback { + #rid; + + constructor(definition, callback) { + this.#rid = core.opSync("op_ffi_register_callback", definition, callback); + } + + close() { + core.close(this.#rid); + } + } + + function registerCallback(definition, callback) { + return new RegisteredCallback(definition, callback); + } + class DynamicLibrary { #rid; symbols = {}; @@ -344,6 +360,7 @@ window.__bootstrap.ffi = { dlopen, + registerCallback, UnsafePointer, UnsafePointerView, UnsafeFnPointer, diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index e8e2bfe7447b60..092bbd3340b9e0 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -9,7 +9,6 @@ use deno_core::error::AnyError; use deno_core::include_js_files; use deno_core::op; -use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::serde_v8; @@ -26,7 +25,6 @@ use libffi::raw::*; use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; -use std::cell::Cell; use std::cell::RefCell; use std::collections::HashMap; use std::ffi::c_void; @@ -37,6 +35,10 @@ use std::path::PathBuf; use std::ptr; use std::rc::Rc; +thread_local! { + static CREATE_SCOPE: RefCell> = RefCell::new(None); +} + pub struct Unstable(pub bool); fn check_unstable(state: &OpState, api_name: &str) { @@ -93,6 +95,7 @@ impl DynamicLibraryResource { name: String, foreign_fn: ForeignFunction, ) -> Result<(), AnyError> { + CREATE_SCOPE.with(|s| s.replace(Some(true))); let symbol = match &foreign_fn.name { Some(symbol) => symbol, None => &name, @@ -796,7 +799,6 @@ struct CallbackInfo { pub callback: NonNull, pub context: NonNull, pub isolate: *mut v8::Isolate, - pub create_scope: Cell, } unsafe extern "C" fn deno_ffi_callback( @@ -812,11 +814,22 @@ unsafe extern "C" fn deno_ffi_callback( v8::Local, >(info.context); let mut cb_scope = v8::CallbackScope::new(context); - let mut scope = if info.create_scope.get() { - v8::HandleScope::with_context(isolate, context) - } else { - v8::HandleScope::new(&mut cb_scope) - }; + let mut scope = CREATE_SCOPE.with(|s| match *s.borrow() { + Some(create_scope) => { + if create_scope { + // Call from main thread without an active scope. + // This shouldn't really be possible but here we are. + v8::HandleScope::with_context(isolate, context) + } else { + // Call from main thread with an active scope in existence, piggyback on it. + v8::HandleScope::new(&mut cb_scope) + } + } + None => { + // Call from another thread, PANIC IN THE DISCO! + todo!("Call from another thread"); + } + }); let func = callback.open(&mut scope); let result = result as *mut c_void; let repr = std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); @@ -944,7 +957,6 @@ fn op_ffi_register_callback( callback, context, isolate, - create_scope: Cell::new(true), })) }; let cif = Cif::new( @@ -987,12 +999,16 @@ fn test_registered_callback( unsafe { *resource.closure.instantiate_code_ptr() }; (fn_ptr, info) }; - info.create_scope.set(false); + CREATE_SCOPE.with(|s| { + s.replace(Some(true)); + }); let args = v8::Local::::try_from(args.v8_value).unwrap(); let arg = args.get_index(scope, 0).unwrap(); let arg: u32 = arg.uint32_value(scope).unwrap(); let result = unsafe { fn_ptr(arg) }; - info.create_scope.set(true); + CREATE_SCOPE.with(|s| { + s.replace(Some(false)); + }); Ok(result.into()) } @@ -1143,51 +1159,51 @@ fn op_ffi_call( } /// A non-blocking FFI call. -#[op(v8)] -async fn op_ffi_call_nonblocking<'scope>( - scope: &mut v8::HandleScope<'scope>, - state: Rc>, - args: FfiCallArgs<'scope>, -) -> Result -where - 'scope: 'scope, -{ - let resource = state - .borrow() - .resource_table - .get::(args.rid)?; - let symbols = &resource.symbols; - let symbol = symbols - .get(&args.symbol) - .ok_or_else(bad_resource_id)? - .clone(); - - let v8_array = - v8::Local::::try_from(args.parameters.v8_value).unwrap(); - let mut native_values: Vec<(NativeType, NativeValue)> = - Vec::with_capacity(symbol.parameter_types.len()); - - for (index, native_type) in symbol.parameter_types.iter().enumerate() { - let value = v8_array.get_index(scope, index as u32).unwrap(); - native_values.push(( - native_type.clone(), - NativeValue::new(scope, *native_type, value)?, - )); - } +// #[op(v8)] +// async fn op_ffi_call_nonblocking<'scope>( +// scope: &mut v8::HandleScope<'scope>, +// state: Rc>, +// args: FfiCallArgs<'scope>, +// ) -> Result +// where +// 'scope: 'scope, +// { +// let resource = state +// .borrow() +// .resource_table +// .get::(args.rid)?; +// let symbols = &resource.symbols; +// let symbol = symbols +// .get(&args.symbol) +// .ok_or_else(bad_resource_id)? +// .clone(); + +// let v8_array = +// v8::Local::::try_from(args.parameters.v8_value).unwrap(); +// let mut native_values: Vec<(NativeType, NativeValue)> = +// Vec::with_capacity(symbol.parameter_types.len()); + +// for (index, native_type) in symbol.parameter_types.iter().enumerate() { +// let value = v8_array.get_index(scope, index as u32).unwrap(); +// native_values.push(( +// native_type.clone(), +// NativeValue::new(scope, *native_type, value)?, +// )); +// } - tokio::task::spawn_blocking(move || { - ffi_call( - native_values - .iter() - .map(|(native_type, native_value)| unsafe { - native_value.as_arg(*native_type) - }) - .collect(), - &symbol, - ) - }) - .await? -} +// tokio::task::spawn_blocking(move || { +// ffi_call( +// native_values +// .iter() +// .map(|(native_type, native_value)| unsafe { +// native_value.as_arg(*native_type) +// }) +// .collect(), +// &symbol, +// ) +// }) +// .await? +// } #[op] fn op_ffi_ptr_of( diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 24a31bc31c788b..c61fbbadfbd44d 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -140,6 +140,7 @@ createHttpClient: __bootstrap.fetch.createHttpClient, http: __bootstrap.http, dlopen: __bootstrap.ffi.dlopen, + registerCallback: __bootstrap.ffi.registerCallback, UnsafePointer: __bootstrap.ffi.UnsafePointer, UnsafePointerView: __bootstrap.ffi.UnsafePointerView, UnsafeFnPointer: __bootstrap.ffi.UnsafeFnPointer, From 8a6dace9d834fe2ad46a7ecc43b534b8f1fe95b7 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 22 May 2022 10:57:26 +0300 Subject: [PATCH 13/64] Somewhat handle nonblocking calls --- ext/ffi/lib.rs | 285 ++++++++++++++----------------------------------- 1 file changed, 78 insertions(+), 207 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 092bbd3340b9e0..4e5a60afe907bc 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -39,6 +39,27 @@ thread_local! { static CREATE_SCOPE: RefCell> = RefCell::new(None); } +/// Local copy of `libffi::middle::Arg` to enable `Send`. +#[derive(Clone, Debug)] +#[repr(C)] +struct FfiCallArg(*mut c_void); + +impl FfiCallArg { + /// Coerces an argument reference into the [`FfiCallArg`] type. + pub fn new(r: &T) -> Self { + FfiCallArg(r as *const T as *mut c_void) + } +} + +unsafe impl Send for FfiCallArg {} + +impl From for Arg { + fn from(arg: FfiCallArg) -> Self { + // SAFETY: The types are identical + unsafe { std::mem::transmute::(arg) } + } +} + pub struct Unstable(pub bool); fn check_unstable(state: &OpState, api_name: &str) { @@ -250,151 +271,6 @@ union NativeValue { unsafe impl Send for NativeValue {} -impl NativeValue { - fn new( - scope: &mut v8::HandleScope, - native_type: NativeType, - value: v8::Local, - ) -> Result { - let value = match native_type { - NativeType::Void => { - unreachable!() - } - NativeType::U8 => Self { - u8_value: value.uint32_value(scope).unwrap() as u8, - }, - NativeType::I8 => Self { - i8_value: value.int32_value(scope).unwrap() as i8, - }, - NativeType::U16 => Self { - u16_value: value.uint32_value(scope).unwrap() as u16, - }, - NativeType::I16 => Self { - i16_value: value.int32_value(scope).unwrap() as i16, - }, - NativeType::U32 => Self { - u32_value: value.uint32_value(scope).unwrap(), - }, - NativeType::I32 => Self { - i32_value: value.int32_value(scope).unwrap(), - }, - NativeType::U64 => Self { - u64_value: value.integer_value(scope).unwrap() as u64, - }, - NativeType::I64 => Self { - i64_value: value.integer_value(scope).unwrap() as i64, - }, - NativeType::USize => Self { - usize_value: value.integer_value(scope).unwrap() as usize, - }, - NativeType::ISize => Self { - isize_value: value.integer_value(scope).unwrap() as isize, - }, - NativeType::F32 => Self { - f32_value: value.number_value(scope).unwrap() as f32, - }, - NativeType::F64 => Self { - f64_value: value.number_value(scope).unwrap() as f64, - }, - NativeType::Pointer => { - if value.is_null() { - Self { - pointer: ptr::null(), - } - } else if value.is_big_int() { - let value = v8::Local::::try_from(value)?; - let value = value.u64_value().0 as *const u8; - Self { pointer: value } - } else if value.is_array_buffer() || value.is_array_buffer_view() { - let value: ZeroCopyBuf = serde_v8::from_v8(scope, value).unwrap(); - let value: &[u8] = &value[..]; - Self { - pointer: value.as_ptr(), - } - } else { - return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer or ArrayBufferview")); - } - } - NativeType::Function => { - // Self { - // pointer: value_as_uint::(value)?.into(), - // } - unreachable!(); - } - }; - Ok(value) - } - - fn buffer(ptr: *const u8) -> Self { - Self { pointer: ptr } - } - - unsafe fn as_arg(&self, native_type: NativeType) -> Arg { - match native_type { - NativeType::Void => { - unreachable!() - } - NativeType::U8 => Arg::new(&self.u8_value), - NativeType::I8 => Arg::new(&self.i8_value), - NativeType::U16 => Arg::new(&self.u16_value), - NativeType::I16 => Arg::new(&self.i16_value), - NativeType::U32 => Arg::new(&self.u32_value), - NativeType::I32 => Arg::new(&self.i32_value), - NativeType::U64 => Arg::new(&self.u64_value), - NativeType::I64 => Arg::new(&self.i64_value), - NativeType::USize => Arg::new(&self.usize_value), - NativeType::ISize => Arg::new(&self.isize_value), - NativeType::F32 => Arg::new(&self.f32_value), - NativeType::F64 => Arg::new(&self.f64_value), - NativeType::Pointer | NativeType::Function => Arg::new(&self.pointer), - } - } -} - -fn value_as_uint>(value: Value) -> Result { - if value.is_array() { - let value = U32x2::try_from(value)?; - return T::try_from(u64::from(value)).map_err(|_| type_error(format!("Found U32x2 FFI argument but it could not be converted to an unsigned integer, got {:?}", value))); - } - - match value.as_u64().and_then(|v| T::try_from(v).ok()) { - Some(value) => Ok(value), - None => Err(type_error(format!( - "Expected FFI argument to be an unsigned integer, but got {:?}", - value - ))), - } -} - -fn value_as_int>(value: Value) -> Result { - if value.is_array() { - let value = U32x2::try_from(value)?; - return T::try_from(u64::from(value) as i64).map_err(|_| type_error(format!("Found U32x2 FFI argument but it could not be converted to a signed integer, got {:?}", value))); - } - - match value.as_i64().and_then(|v| T::try_from(v).ok()) { - Some(value) => Ok(value), - None => Err(type_error(format!( - "Expected FFI argument to be a signed integer, but got {:?}", - value - ))), - } -} - -fn value_as_f32(value: Value) -> Result { - Ok(value_as_f64(value)? as f32) -} - -fn value_as_f64(value: Value) -> Result { - match value.as_f64() { - Some(value) => Ok(value), - None => Err(type_error(format!( - "Expected FFI argument to be a double, but got {:?}", - value - ))), - } -} - #[derive(Serialize, Deserialize, Debug, Clone, Copy)] struct U32x2(u32, u32); @@ -631,12 +507,12 @@ fn ffi_parse_args<'scope>( args: serde_v8::Value<'scope>, parameter_types: &Vec, resource_table: &deno_core::ResourceTable, -) -> Result, AnyError> +) -> Result, AnyError> where 'scope: 'scope, { let args = v8::Local::::try_from(args.v8_value).unwrap(); - let mut ffi_args: Vec = Vec::with_capacity(parameter_types.len()); + let mut ffi_args: Vec = Vec::with_capacity(parameter_types.len()); for (index, native_type) in parameter_types.iter().enumerate() { let value = args.get_index(scope, index as u32).unwrap(); @@ -646,64 +522,68 @@ where } NativeType::U8 => { let value = value.uint32_value(scope).unwrap() as u8; - ffi_args.push(Arg::new(&value)) + ffi_args.push(FfiCallArg::new(&value)) } NativeType::I8 => { let value = value.int32_value(scope).unwrap() as i8; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::U16 => { let value = value.uint32_value(scope).unwrap() as u16; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::I16 => { let value = value.int32_value(scope).unwrap() as i16; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::U32 => { let value = value.uint32_value(scope).unwrap(); - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::I32 => { let value = value.int32_value(scope).unwrap(); - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::U64 => { + // TODO: Handle BigInt let value = value.integer_value(scope).unwrap() as u64; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::I64 => { + // TODO: Handle BigInt let value = value.integer_value(scope).unwrap() as i64; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::USize => { + // TODO: Handle BigInt let value = value.integer_value(scope).unwrap() as usize; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::ISize => { + // TODO: Handle BigInt let value = value.integer_value(scope).unwrap() as isize; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::F32 => { let value = value.number_value(scope).unwrap() as f32; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::F64 => { let value = value.integer_value(scope).unwrap() as f64; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } NativeType::Pointer => { if value.is_null() { let value: *const c_void = ptr::null(); - ffi_args.push(Arg::new(&value)) + ffi_args.push(FfiCallArg::new(&value)) } else if value.is_big_int() { let value = v8::Local::::try_from(value)?; let value = value.u64_value().0 as *const u8; - ffi_args.push(Arg::new(&value)); + ffi_args.push(FfiCallArg::new(&value)); } else if value.is_array_buffer() || value.is_array_buffer_view() { let value: ZeroCopyBuf = serde_v8::from_v8(scope, value).unwrap(); let value: &[u8] = &value[..]; - ffi_args.push(Arg::new(&value.as_ptr())); + ffi_args.push(FfiCallArg::new(&value.as_ptr())); } else { return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer or ArrayBufferView")); } @@ -717,9 +597,17 @@ where Ok(ffi_args) } -fn ffi_call(call_args: Vec, symbol: &Symbol) -> Result { +fn ffi_call( + call_args: Vec, + symbol: &Symbol, +) -> Result { //let call_args = ffi_parse_args(&args.parameters, symbol, resource_table)?; + let call_args: Vec = call_args + .iter() + .map(|ffi_arg| ffi_arg.clone().into()) + .collect(); + Ok(match symbol.result_type { NativeType::Void => { json!(unsafe { symbol.cif.call::<()>(symbol.ptr, &call_args) }) @@ -1159,51 +1047,34 @@ fn op_ffi_call( } /// A non-blocking FFI call. -// #[op(v8)] -// async fn op_ffi_call_nonblocking<'scope>( -// scope: &mut v8::HandleScope<'scope>, -// state: Rc>, -// args: FfiCallArgs<'scope>, -// ) -> Result -// where -// 'scope: 'scope, -// { -// let resource = state -// .borrow() -// .resource_table -// .get::(args.rid)?; -// let symbols = &resource.symbols; -// let symbol = symbols -// .get(&args.symbol) -// .ok_or_else(bad_resource_id)? -// .clone(); - -// let v8_array = -// v8::Local::::try_from(args.parameters.v8_value).unwrap(); -// let mut native_values: Vec<(NativeType, NativeValue)> = -// Vec::with_capacity(symbol.parameter_types.len()); - -// for (index, native_type) in symbol.parameter_types.iter().enumerate() { -// let value = v8_array.get_index(scope, index as u32).unwrap(); -// native_values.push(( -// native_type.clone(), -// NativeValue::new(scope, *native_type, value)?, -// )); -// } +//#[op(v8)] +async fn op_ffi_call_nonblocking<'scope>( + scope: &mut v8::HandleScope<'scope>, + state: Rc>, + args: FfiCallArgs<'scope>, +) -> Result +where + 'scope: 'scope, +{ + let resource = state + .borrow() + .resource_table + .get::(args.rid)?; + let symbols = &resource.symbols; + let symbol = symbols + .get(&args.symbol) + .ok_or_else(bad_resource_id)? + .clone(); -// tokio::task::spawn_blocking(move || { -// ffi_call( -// native_values -// .iter() -// .map(|(native_type, native_value)| unsafe { -// native_value.as_arg(*native_type) -// }) -// .collect(), -// &symbol, -// ) -// }) -// .await? -// } + let call_args = ffi_parse_args( + scope, + args.parameters, + &symbol.parameter_types, + &state.borrow().resource_table, + )?; + + tokio::task::spawn_blocking(move || ffi_call(call_args, &symbol)).await? +} #[op] fn op_ffi_ptr_of( From 90c932d4a3ce131b3b87da2e86742f9c99a64c3f Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 22 May 2022 16:33:12 +0300 Subject: [PATCH 14/64] Not really well working --- ext/ffi/00_ffi.js | 40 +++++++++++++++++++++++++++++++++++++++- ext/ffi/lib.rs | 46 +++++++++++++++++++++++++++++++++------------- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 796c3790938fd3..108bd05f3f24a5 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -148,6 +148,10 @@ function prepareArgs(types, args) { const parameters = []; + if (types.length !== args.length) { + throw new TypeError("Invalid FFI call, parameter vs args count mismatch"); + } + for (let i = 0; i < types.length; i++) { const type = types[i]; const arg = args[i]; @@ -174,7 +178,13 @@ ); } - parameters.push(pack64(arg)); + parameters.push(arg); + } else if (type === "function") { + if (ObjectPrototypeIsPrototypeOf(RegisteredCallback, arg)) { + parameters.push(arg); + } else { + throw new TypeError("Invalid ffi arg value, expected RegisteredCallback"); + } } else { parameters.push(arg); } @@ -244,9 +254,34 @@ class RegisteredCallback { #rid; + definition; + callback; constructor(definition, callback) { + if (definition.nonblocking) { + throw new TypeError("Invalid ffi RegisteredCallback, cannot be nonblocking"); + } this.#rid = core.opSync("op_ffi_register_callback", definition, callback); + this.definition = definition; + this.callback = callback; + } + + call(...args) { + const { parameters } = prepareArgs( + this.definition.parameters, + args, + ); + if (this.definition.nonblocking) { + throw new Error("Unreachable"); + } else { + const result = core.opSync("op_ffi_call_registered_callback", this.#rid, parameters); + + if (this.definition.result === "pointer") { + return new UnsafePointer(unpackU64(result)); + } + + return result; + } } close() { @@ -255,6 +290,9 @@ } function registerCallback(definition, callback) { + if (!definition || !callback) { + throw new TypeError("Invalid arguments"); + } return new RegisteredCallback(definition, callback); } diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 4e5a60afe907bc..501e6d6edd4bbe 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -21,6 +21,7 @@ use deno_core::ZeroCopyBuf; use dlopen::raw::Library; use libffi::middle::Arg; use libffi::middle::Cif; +use libffi::middle::CodePtr; use libffi::raw::*; use serde::Deserialize; use serde::Serialize; @@ -195,7 +196,7 @@ pub fn init(unstable: bool) -> Extension { op_ffi_read_f64::decl::

(), op_ffi_register_callback::decl(), op_ffi_deregister_callback::decl(), - test_registered_callback::decl(), + op_ffi_call_registered_callback::decl(), ]) .state(move |state| { // Stolen from deno_webgpu, is there a better option? @@ -538,6 +539,7 @@ where } NativeType::U32 => { let value = value.uint32_value(scope).unwrap(); + println!("U32 being passed: {}", value); ffi_args.push(FfiCallArg::new(&value)); } NativeType::I32 => { @@ -601,13 +603,13 @@ fn ffi_call( call_args: Vec, symbol: &Symbol, ) -> Result { - //let call_args = ffi_parse_args(&args.parameters, symbol, resource_table)?; - let call_args: Vec = call_args .iter() .map(|ffi_arg| ffi_arg.clone().into()) .collect(); + dbg!(call_args.clone()); + Ok(match symbol.result_type { NativeType::Void => { json!(unsafe { symbol.cif.call::<()>(symbol.ptr, &call_args) }) @@ -687,6 +689,8 @@ struct CallbackInfo { pub callback: NonNull, pub context: NonNull, pub isolate: *mut v8::Isolate, + pub parameters: Vec, + pub result: NativeType, } unsafe extern "C" fn deno_ffi_callback( @@ -738,7 +742,10 @@ unsafe extern "C" fn deno_ffi_callback( FFI_TYPE_SINT16 => serde_v8::to_v8(&mut scope, *(val as *const i16)), FFI_TYPE_UINT16 => serde_v8::to_v8(&mut scope, *(val as *const u16)), FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *(val as *const i32)), - FFI_TYPE_UINT32 => serde_v8::to_v8(&mut scope, *(val as *const u32)), + FFI_TYPE_UINT32 => { + println!("U32 being received: {:?}, {}", val as *const u32, *(val as *const u32)); + serde_v8::to_v8(&mut scope, *(val as *const u32)) + }, FFI_TYPE_SINT64 => serde_v8::to_v8(&mut scope, *(val as *const i64)), FFI_TYPE_UINT64 => serde_v8::to_v8(&mut scope, *(val as *const u64)), FFI_TYPE_VOID => serde_v8::to_v8(&mut scope, ()), @@ -845,6 +852,8 @@ fn op_ffi_register_callback( callback, context, isolate, + parameters: args.parameters.clone(), + result: args.result.clone(), })) }; let cif = Cif::new( @@ -869,7 +878,7 @@ fn op_ffi_deregister_callback(state: &mut deno_core::OpState, rid: ResourceId) { } #[op(v8)] -fn test_registered_callback( +fn op_ffi_call_registered_callback( scope: &mut v8::HandleScope, state: Rc>, rid: ResourceId, @@ -883,19 +892,30 @@ fn test_registered_callback( .unwrap(); let info: &CallbackInfo = unsafe { &*resource.info }; - let fn_ptr: unsafe extern "C" fn(u32) -> u32 = - unsafe { *resource.closure.instantiate_code_ptr() }; + let fn_ptr = + *resource.closure.code_ptr() as *mut c_void; (fn_ptr, info) }; + let call_args = ffi_parse_args(unsafe { std::mem::transmute(scope) }, args, &info.parameters, &state.borrow().resource_table)?; CREATE_SCOPE.with(|s| { - s.replace(Some(true)); + s.replace(Some(false)); }); - let args = v8::Local::::try_from(args.v8_value).unwrap(); - let arg = args.get_index(scope, 0).unwrap(); - let arg: u32 = arg.uint32_value(scope).unwrap(); - let result = unsafe { fn_ptr(arg) }; + let cif = libffi::middle::Cif::new( + info.parameters + .clone() + .into_iter() + .map(libffi::middle::Type::from), + info.result.into(), + ); + let symbol = Symbol { + cif, + ptr: libffi::middle::CodePtr::from_ptr(fn_ptr), + parameter_types: info.parameters.clone(), + result_type: info.result, + }; + let result = ffi_call(call_args, &symbol)?; CREATE_SCOPE.with(|s| { - s.replace(Some(false)); + s.replace(Some(true)); }); Ok(result.into()) } From 3c10894e7abda869ca721466348ce8e2844607c1 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 7 Jun 2022 22:46:31 +0300 Subject: [PATCH 15/64] Fix lifetime issues with ffi_parse_args --- ext/ffi/lib.rs | 165 ++++++++++++++++++++++++++----------------------- 1 file changed, 88 insertions(+), 77 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 501e6d6edd4bbe..7686e108b061a9 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -21,7 +21,6 @@ use deno_core::ZeroCopyBuf; use dlopen::raw::Library; use libffi::middle::Arg; use libffi::middle::Cif; -use libffi::middle::CodePtr; use libffi::raw::*; use serde::Deserialize; use serde::Serialize; @@ -40,27 +39,6 @@ thread_local! { static CREATE_SCOPE: RefCell> = RefCell::new(None); } -/// Local copy of `libffi::middle::Arg` to enable `Send`. -#[derive(Clone, Debug)] -#[repr(C)] -struct FfiCallArg(*mut c_void); - -impl FfiCallArg { - /// Coerces an argument reference into the [`FfiCallArg`] type. - pub fn new(r: &T) -> Self { - FfiCallArg(r as *const T as *mut c_void) - } -} - -unsafe impl Send for FfiCallArg {} - -impl From for Arg { - fn from(arg: FfiCallArg) -> Self { - // SAFETY: The types are identical - unsafe { std::mem::transmute::(arg) } - } -} - pub struct Unstable(pub bool); fn check_unstable(state: &OpState, api_name: &str) { @@ -270,6 +248,28 @@ union NativeValue { pointer: *const u8, } +impl NativeValue { + unsafe fn as_arg(&self, native_type: NativeType) -> Arg { + match native_type { + NativeType::Void => unreachable!(), + NativeType::U8 => Arg::new(&self.u8_value), + NativeType::I8 => Arg::new(&self.i8_value), + NativeType::U16 => Arg::new(&self.u16_value), + NativeType::I16 => Arg::new(&self.i16_value), + NativeType::U32 => Arg::new(&self.u32_value), + NativeType::I32 => Arg::new(&self.i32_value), + NativeType::U64 => Arg::new(&self.u64_value), + NativeType::I64 => Arg::new(&self.i64_value), + NativeType::USize => Arg::new(&self.usize_value), + NativeType::ISize => Arg::new(&self.isize_value), + NativeType::F32 => Arg::new(&self.f32_value), + NativeType::F64 => Arg::new(&self.f64_value), + NativeType::Pointer => Arg::new(&self.pointer), + NativeType::Function => Arg::new(&self.pointer), + } + } +} + unsafe impl Send for NativeValue {} #[derive(Serialize, Deserialize, Debug, Clone, Copy)] @@ -508,12 +508,12 @@ fn ffi_parse_args<'scope>( args: serde_v8::Value<'scope>, parameter_types: &Vec, resource_table: &deno_core::ResourceTable, -) -> Result, AnyError> +) -> Result, AnyError> where 'scope: 'scope, { let args = v8::Local::::try_from(args.v8_value).unwrap(); - let mut ffi_args: Vec = Vec::with_capacity(parameter_types.len()); + let mut ffi_args: Vec = Vec::with_capacity(parameter_types.len()); for (index, native_type) in parameter_types.iter().enumerate() { let value = args.get_index(scope, index as u32).unwrap(); @@ -523,75 +523,89 @@ where } NativeType::U8 => { let value = value.uint32_value(scope).unwrap() as u8; - ffi_args.push(FfiCallArg::new(&value)) + ffi_args.push(NativeValue { u8_value: value }); } NativeType::I8 => { let value = value.int32_value(scope).unwrap() as i8; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { i8_value: value }); } NativeType::U16 => { let value = value.uint32_value(scope).unwrap() as u16; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { u16_value: value }); } NativeType::I16 => { let value = value.int32_value(scope).unwrap() as i16; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { i16_value: value }); } NativeType::U32 => { let value = value.uint32_value(scope).unwrap(); - println!("U32 being passed: {}", value); - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { u32_value: value }); } NativeType::I32 => { let value = value.int32_value(scope).unwrap(); - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { i32_value: value }); } NativeType::U64 => { // TODO: Handle BigInt let value = value.integer_value(scope).unwrap() as u64; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { u64_value: value }); } NativeType::I64 => { // TODO: Handle BigInt let value = value.integer_value(scope).unwrap() as i64; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { i64_value: value }); } NativeType::USize => { // TODO: Handle BigInt let value = value.integer_value(scope).unwrap() as usize; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { usize_value: value }); } NativeType::ISize => { // TODO: Handle BigInt let value = value.integer_value(scope).unwrap() as isize; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { isize_value: value }); } NativeType::F32 => { let value = value.number_value(scope).unwrap() as f32; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { f32_value: value }); } NativeType::F64 => { let value = value.integer_value(scope).unwrap() as f64; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { f64_value: value }); } NativeType::Pointer => { if value.is_null() { - let value: *const c_void = ptr::null(); - ffi_args.push(FfiCallArg::new(&value)) + let value: *const u8 = ptr::null(); + ffi_args.push(NativeValue { pointer: value }) } else if value.is_big_int() { let value = v8::Local::::try_from(value)?; let value = value.u64_value().0 as *const u8; - ffi_args.push(FfiCallArg::new(&value)); + ffi_args.push(NativeValue { pointer: value }); } else if value.is_array_buffer() || value.is_array_buffer_view() { let value: ZeroCopyBuf = serde_v8::from_v8(scope, value).unwrap(); let value: &[u8] = &value[..]; - ffi_args.push(FfiCallArg::new(&value.as_ptr())); + ffi_args.push(NativeValue { pointer: value.as_ptr() }); } else { return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer or ArrayBufferView")); } } NativeType::Function => { - todo!(); + if value.is_null() { + let value: *const u8 = ptr::null(); + ffi_args.push(NativeValue { pointer: value }) + } else if value.is_number() { + let value: ResourceId = value.uint32_value(scope).unwrap(); + let rc = resource_table.get::(value)?; + let fn_ref = rc.closure.code_ptr() as *const unsafe extern fn() as *const u8; + ffi_args.push(NativeValue { pointer: fn_ref }); + } else if value.is_big_int() { + // Do we support this? + let value = v8::Local::::try_from(value)?; + let value = value.u64_value().0 as *const u8; + ffi_args.push(NativeValue { pointer: value }); + } else { + return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer or ArrayBufferView")); + } } } } @@ -600,16 +614,15 @@ where } fn ffi_call( - call_args: Vec, + call_args: Vec, symbol: &Symbol, ) -> Result { let call_args: Vec = call_args .iter() - .map(|ffi_arg| ffi_arg.clone().into()) + .enumerate() + .map(|(index, ffi_arg)| unsafe { ffi_arg.as_arg(*symbol.parameter_types.get(index).unwrap()) } ) .collect(); - dbg!(call_args.clone()); - Ok(match symbol.result_type { NativeType::Void => { json!(unsafe { symbol.cif.call::<()>(symbol.ptr, &call_args) }) @@ -724,30 +737,29 @@ unsafe extern "C" fn deno_ffi_callback( }); let func = callback.open(&mut scope); let result = result as *mut c_void; - let repr = std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); - let vals = std::slice::from_raw_parts(args, cif.nargs as usize); + let repr: &[*mut ffi_type] = std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); + let vals: &[*const c_void] = std::slice::from_raw_parts(args, cif.nargs as usize); let mut params: Vec> = vec![]; - for (&repr, &val) in repr.iter().zip(vals) { - let value = match (*repr).type_ as _ { - FFI_TYPE_INT => serde_v8::to_v8(&mut scope, *(val as *const i32)), - FFI_TYPE_FLOAT => serde_v8::to_v8(&mut scope, *(val as *const f32)), - FFI_TYPE_DOUBLE => serde_v8::to_v8(&mut scope, *(val as *const f64)), + for (repr, val) in repr.iter().zip(vals) { + let value = match (*(*repr)).type_ as _ { + FFI_TYPE_INT => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), + FFI_TYPE_FLOAT => serde_v8::to_v8(&mut scope, *((*val) as *const f32)), + FFI_TYPE_DOUBLE => serde_v8::to_v8(&mut scope, *((*val) as *const f64)), FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { - let ptr = U32x2::from(*(val as *const u64)); + let ptr = U32x2::from(*((*val) as *const u64)); serde_v8::to_v8(&mut scope, ptr) } - FFI_TYPE_SINT8 => serde_v8::to_v8(&mut scope, *(val as *const i8)), - FFI_TYPE_UINT8 => serde_v8::to_v8(&mut scope, *(val as *const u8)), - FFI_TYPE_SINT16 => serde_v8::to_v8(&mut scope, *(val as *const i16)), - FFI_TYPE_UINT16 => serde_v8::to_v8(&mut scope, *(val as *const u16)), - FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *(val as *const i32)), + FFI_TYPE_SINT8 => serde_v8::to_v8(&mut scope, *((*val) as *const i8)), + FFI_TYPE_UINT8 => serde_v8::to_v8(&mut scope, *((*val) as *const u8)), + FFI_TYPE_SINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const i16)), + FFI_TYPE_UINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const u16)), + FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), FFI_TYPE_UINT32 => { - println!("U32 being received: {:?}, {}", val as *const u32, *(val as *const u32)); - serde_v8::to_v8(&mut scope, *(val as *const u32)) + serde_v8::to_v8(&mut scope, *((*val) as *const u32)) }, - FFI_TYPE_SINT64 => serde_v8::to_v8(&mut scope, *(val as *const i64)), - FFI_TYPE_UINT64 => serde_v8::to_v8(&mut scope, *(val as *const u64)), + FFI_TYPE_SINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const i64)), + FFI_TYPE_UINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const u64)), FFI_TYPE_VOID => serde_v8::to_v8(&mut scope, ()), _ => { panic!("Unsupported parameter type") @@ -756,14 +768,10 @@ unsafe extern "C" fn deno_ffi_callback( params.push(value.expect("Unable to serialize callback parameter.")); } - let func_name = func.get_name(&mut scope).to_rust_string_lossy(&mut scope); - - println!("Func name: {}", func_name); - let recv = v8::undefined(&mut scope); let value = match func.call(&mut scope, recv.into(), ¶ms) { Some(value) => value, - None => v8::Number::new(&mut scope, 60.0).into(), + None => panic!("OH SHIT"), }; std::mem::forget(callback); @@ -878,12 +886,15 @@ fn op_ffi_deregister_callback(state: &mut deno_core::OpState, rid: ResourceId) { } #[op(v8)] -fn op_ffi_call_registered_callback( - scope: &mut v8::HandleScope, +fn op_ffi_call_registered_callback<'a>( + scope: &mut v8::HandleScope<'a>, state: Rc>, rid: ResourceId, - args: serde_v8::Value, -) -> Result { + args: serde_v8::Value<'a>, +) -> Result +where + 'a: 'a +{ let (fn_ptr, info) = { let state = &mut state.borrow_mut(); let resource = state @@ -896,7 +907,7 @@ fn op_ffi_call_registered_callback( *resource.closure.code_ptr() as *mut c_void; (fn_ptr, info) }; - let call_args = ffi_parse_args(unsafe { std::mem::transmute(scope) }, args, &info.parameters, &state.borrow().resource_table)?; + let call_args = ffi_parse_args(scope, args, &info.parameters, &state.borrow().resource_table)?; CREATE_SCOPE.with(|s| { s.replace(Some(false)); }); @@ -1068,13 +1079,13 @@ fn op_ffi_call( /// A non-blocking FFI call. //#[op(v8)] -async fn op_ffi_call_nonblocking<'scope>( - scope: &mut v8::HandleScope<'scope>, +async fn op_ffi_call_nonblocking<'a>( + scope: &mut v8::HandleScope<'a>, state: Rc>, - args: FfiCallArgs<'scope>, + args: FfiCallArgs<'a>, ) -> Result where - 'scope: 'scope, + 'a: 'a, { let resource = state .borrow() From e3b55f6279a20d92011d8777d08450603e2fef70 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 7 Jun 2022 23:04:26 +0300 Subject: [PATCH 16/64] Error handling --- ext/ffi/lib.rs | 182 ++++++++++++++++++++++++++++++------------------- 1 file changed, 113 insertions(+), 69 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 7686e108b061a9..b0f03f7b149a8c 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -513,7 +513,8 @@ where 'scope: 'scope, { let args = v8::Local::::try_from(args.v8_value).unwrap(); - let mut ffi_args: Vec = Vec::with_capacity(parameter_types.len()); + let mut ffi_args: Vec = + Vec::with_capacity(parameter_types.len()); for (index, native_type) in parameter_types.iter().enumerate() { let value = args.get_index(scope, index as u32).unwrap(); @@ -522,55 +523,89 @@ where unreachable!(); } NativeType::U8 => { - let value = value.uint32_value(scope).unwrap() as u8; + let value = value + .uint32_value(scope) + .ok_or(type_error("Invalid FFI u8 type, expected number"))? + as u8; ffi_args.push(NativeValue { u8_value: value }); } NativeType::I8 => { - let value = value.int32_value(scope).unwrap() as i8; + let value = value + .int32_value(scope) + .ok_or(type_error("Invalid FFI i8 type, expected number"))? + as i8; ffi_args.push(NativeValue { i8_value: value }); } NativeType::U16 => { - let value = value.uint32_value(scope).unwrap() as u16; + let value = value + .uint32_value(scope) + .ok_or(type_error("Invalid FFI u16 type, expected number"))? + as u16; ffi_args.push(NativeValue { u16_value: value }); } NativeType::I16 => { - let value = value.int32_value(scope).unwrap() as i16; + let value = value + .int32_value(scope) + .ok_or(type_error("Invalid FFI i16 type, expected number"))? + as i16; ffi_args.push(NativeValue { i16_value: value }); } NativeType::U32 => { - let value = value.uint32_value(scope).unwrap(); + let value = value + .uint32_value(scope) + .ok_or(type_error("Invalid FFI u32 type, expected number"))?; ffi_args.push(NativeValue { u32_value: value }); } NativeType::I32 => { - let value = value.int32_value(scope).unwrap(); + let value = value + .int32_value(scope) + .ok_or(type_error("Invalid FFI i32 type, expected number"))?; ffi_args.push(NativeValue { i32_value: value }); } NativeType::U64 => { // TODO: Handle BigInt - let value = value.integer_value(scope).unwrap() as u64; + let value = value + .integer_value(scope) + .ok_or(type_error("Invalid FFI u64 type, expected number"))? + as u64; ffi_args.push(NativeValue { u64_value: value }); } NativeType::I64 => { // TODO: Handle BigInt - let value = value.integer_value(scope).unwrap() as i64; + let value = value + .integer_value(scope) + .ok_or(type_error("Invalid FFI i64 type, expected number"))? + as i64; ffi_args.push(NativeValue { i64_value: value }); } NativeType::USize => { // TODO: Handle BigInt - let value = value.integer_value(scope).unwrap() as usize; + let value = value + .integer_value(scope) + .ok_or(type_error("Invalid FFI usize type, expected number"))? + as usize; ffi_args.push(NativeValue { usize_value: value }); } NativeType::ISize => { // TODO: Handle BigInt - let value = value.integer_value(scope).unwrap() as isize; + let value = value + .integer_value(scope) + .ok_or(type_error("Invalid FFI isize type, expected number"))? + as isize; ffi_args.push(NativeValue { isize_value: value }); } NativeType::F32 => { - let value = value.number_value(scope).unwrap() as f32; + let value = value + .number_value(scope) + .ok_or(type_error("Invalid FFI f32 type, expected number"))? + as f32; ffi_args.push(NativeValue { f32_value: value }); } NativeType::F64 => { - let value = value.integer_value(scope).unwrap() as f64; + let value = value + .integer_value(scope) + .ok_or(type_error("Invalid FFI f64 type, expected number"))? + as f64; ffi_args.push(NativeValue { f64_value: value }); } NativeType::Pointer => { @@ -578,15 +613,17 @@ where let value: *const u8 = ptr::null(); ffi_args.push(NativeValue { pointer: value }) } else if value.is_big_int() { - let value = v8::Local::::try_from(value)?; + let value = v8::Local::::try_from(value).unwrap(); let value = value.u64_value().0 as *const u8; ffi_args.push(NativeValue { pointer: value }); } else if value.is_array_buffer() || value.is_array_buffer_view() { - let value: ZeroCopyBuf = serde_v8::from_v8(scope, value).unwrap(); + let value: ZeroCopyBuf = serde_v8::from_v8(scope, value)?; let value: &[u8] = &value[..]; - ffi_args.push(NativeValue { pointer: value.as_ptr() }); + ffi_args.push(NativeValue { + pointer: value.as_ptr(), + }); } else { - return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer or ArrayBufferView")); + return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer, or ArrayBufferView")); } } NativeType::Function => { @@ -596,7 +633,8 @@ where } else if value.is_number() { let value: ResourceId = value.uint32_value(scope).unwrap(); let rc = resource_table.get::(value)?; - let fn_ref = rc.closure.code_ptr() as *const unsafe extern fn() as *const u8; + let fn_ref = + rc.closure.code_ptr() as *const unsafe extern "C" fn() as *const u8; ffi_args.push(NativeValue { pointer: fn_ref }); } else if value.is_big_int() { // Do we support this? @@ -604,7 +642,7 @@ where let value = value.u64_value().0 as *const u8; ffi_args.push(NativeValue { pointer: value }); } else { - return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer or ArrayBufferView")); + return Err(type_error("Invalid FFI function type, expected null, RegisteredCallback, or BigInt")); } } } @@ -620,7 +658,9 @@ fn ffi_call( let call_args: Vec = call_args .iter() .enumerate() - .map(|(index, ffi_arg)| unsafe { ffi_arg.as_arg(*symbol.parameter_types.get(index).unwrap()) } ) + .map(|(index, ffi_arg)| unsafe { + ffi_arg.as_arg(*symbol.parameter_types.get(index).unwrap()) + }) .collect(); Ok(match symbol.result_type { @@ -737,8 +777,10 @@ unsafe extern "C" fn deno_ffi_callback( }); let func = callback.open(&mut scope); let result = result as *mut c_void; - let repr: &[*mut ffi_type] = std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); - let vals: &[*const c_void] = std::slice::from_raw_parts(args, cif.nargs as usize); + let repr: &[*mut ffi_type] = + std::slice::from_raw_parts(cif.arg_types, cif.nargs as usize); + let vals: &[*const c_void] = + std::slice::from_raw_parts(args, cif.nargs as usize); let mut params: Vec> = vec![]; for (repr, val) in repr.iter().zip(vals) { @@ -755,9 +797,7 @@ unsafe extern "C" fn deno_ffi_callback( FFI_TYPE_SINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const i16)), FFI_TYPE_UINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const u16)), FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), - FFI_TYPE_UINT32 => { - serde_v8::to_v8(&mut scope, *((*val) as *const u32)) - }, + FFI_TYPE_UINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const u32)), FFI_TYPE_SINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const i64)), FFI_TYPE_UINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const u64)), FFI_TYPE_VOID => serde_v8::to_v8(&mut scope, ()), @@ -844,7 +884,7 @@ fn op_ffi_register_callback( cb: serde_v8::Value<'_>, ) -> Result { let v8_value = cb.v8_value; - let cb = v8::Local::::try_from(v8_value).unwrap(); + let cb = v8::Local::::try_from(v8_value)?; let info = { let isolate_ptr: *mut v8::Isolate = { @@ -893,31 +933,35 @@ fn op_ffi_call_registered_callback<'a>( args: serde_v8::Value<'a>, ) -> Result where - 'a: 'a + 'a: 'a, { let (fn_ptr, info) = { let state = &mut state.borrow_mut(); let resource = state .resource_table - .get::(rid) - .unwrap(); + .get::(rid)?; let info: &CallbackInfo = unsafe { &*resource.info }; - let fn_ptr = - *resource.closure.code_ptr() as *mut c_void; + let fn_ptr = *resource.closure.code_ptr() as *mut c_void; (fn_ptr, info) }; - let call_args = ffi_parse_args(scope, args, &info.parameters, &state.borrow().resource_table)?; + let call_args = ffi_parse_args( + scope, + args, + &info.parameters, + &state.borrow().resource_table, + )?; CREATE_SCOPE.with(|s| { s.replace(Some(false)); }); let cif = libffi::middle::Cif::new( - info.parameters - .clone() - .into_iter() - .map(libffi::middle::Type::from), - info.result.into(), - ); + info + .parameters + .clone() + .into_iter() + .map(libffi::middle::Type::from), + info.result.into(), + ); let symbol = Symbol { cif, ptr: libffi::middle::CodePtr::from_ptr(fn_ptr), @@ -955,37 +999,37 @@ where ffi_call(call_args, &symbol) } -// #[op(v8)] -// async fn op_ffi_call_ptr_nonblocking( -// scope: &mut v8::HandleScope<'scope>, -// state: Rc>, -// args: FfiCallPtrArgs<'scope>, -// ) -> Result -// where -// FP: FfiPermissions + 'static, -// 'scope: 'scope -// { -// check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - -// { -// let mut state = state.borrow_mut(); -// let permissions = state.borrow_mut::(); -// permissions.check(None)?; -// } - -// let symbol = args.get_symbol(); -// let call_args = ffi_parse_args( -// &args.parameters, -// &symbol.parameter_types, -// &state.borrow_mut().resource_table, -// )?; -// tokio::task::spawn_blocking(move || { -// //ffi_call(call_args, &symbol) -// Ok(serde_json::to_value(()).unwrap()) -// }) -// .await -// .unwrap() -// } +//#[op(v8)] +async fn op_ffi_call_ptr_nonblocking<'a, FP>( + scope: &mut v8::HandleScope<'a>, + state: Rc>, + args: FfiCallPtrArgs<'a>, +) -> Result +where + FP: FfiPermissions + 'static, + 'a: 'a +{ + check_unstable2(&state, "Deno.UnsafeFnPointer#call"); + + { + let mut state = state.borrow_mut(); + let permissions = state.borrow_mut::(); + permissions.check(None)?; + } + + let symbol = args.get_symbol(); + let call_args = ffi_parse_args( + scope, + args.parameters, + &args.def.parameters, + &state.borrow_mut().resource_table + )?; + tokio::task::spawn_blocking(move || { + ffi_call(call_args, &symbol) + }) + .await + .unwrap() +} #[derive(Deserialize)] #[serde(rename_all = "camelCase")] From 8e637976dfe49202bfbd4c39097c148ed0939645 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 8 Jun 2022 08:26:52 +0300 Subject: [PATCH 17/64] async work, thanks divy --- ext/ffi/lib.rs | 76 +++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index b0f03f7b149a8c..62484eee6633a6 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -6,6 +6,7 @@ use deno_core::error::generic_error; use deno_core::error::range_error; use deno_core::error::type_error; use deno_core::error::AnyError; +use deno_core::futures::Future; use deno_core::include_js_files; use deno_core::op; @@ -1007,7 +1008,7 @@ async fn op_ffi_call_ptr_nonblocking<'a, FP>( ) -> Result where FP: FfiPermissions + 'static, - 'a: 'a + 'a: 'a, { check_unstable2(&state, "Deno.UnsafeFnPointer#call"); @@ -1019,16 +1020,14 @@ where let symbol = args.get_symbol(); let call_args = ffi_parse_args( - scope, - args.parameters, - &args.def.parameters, - &state.borrow_mut().resource_table + scope, + args.parameters, + &args.def.parameters, + &state.borrow_mut().resource_table, )?; - tokio::task::spawn_blocking(move || { - ffi_call(call_args, &symbol) - }) - .await - .unwrap() + tokio::task::spawn_blocking(move || ffi_call(call_args, &symbol)) + .await + .unwrap() } #[derive(Deserialize)] @@ -1123,32 +1122,39 @@ fn op_ffi_call( /// A non-blocking FFI call. //#[op(v8)] -async fn op_ffi_call_nonblocking<'a>( - scope: &mut v8::HandleScope<'a>, - state: Rc>, - args: FfiCallArgs<'a>, -) -> Result -where - 'a: 'a, -{ - let resource = state - .borrow() - .resource_table - .get::(args.rid)?; - let symbols = &resource.symbols; - let symbol = symbols - .get(&args.symbol) - .ok_or_else(bad_resource_id)? - .clone(); - - let call_args = ffi_parse_args( - scope, - args.parameters, - &symbol.parameter_types, - &state.borrow().resource_table, - )?; +fn op_ffi_call_nonblocking( + scope: &mut v8::HandleScope, + state: &mut deno_core::OpState, + args: FfiCallArgs, +) -> impl Future> + 'static { + let block = || -> Result<(Symbol, Vec), AnyError> { + let resource = state + .resource_table + .get::(args.rid)?; + let symbols = &resource.symbols; + let symbol = symbols.get(&args.symbol).ok_or_else(bad_resource_id)?; + + Ok(( + symbol.clone(), + ffi_parse_args( + unsafe { std::mem::transmute(scope) }, + args.parameters, + &symbol.parameter_types, + &state.resource_table, + )?, + )) + }(); - tokio::task::spawn_blocking(move || ffi_call(call_args, &symbol)).await? + let join_handle = tokio::task::spawn_blocking(move || { + let (symbol, call_args) = match block { + Ok((sym, c_args)) => (sym, c_args), + Err(err) => return Err(err), + }; + ffi_call(call_args, &symbol) + }); + async move { + join_handle.await? + } } #[op] From 15d0fbfdfaebf1915a227ae846eec8eb45f81423 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 8 Jun 2022 08:27:33 +0300 Subject: [PATCH 18/64] Return lifetimes --- ext/ffi/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 62484eee6633a6..a23b838cee4ec1 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1122,10 +1122,10 @@ fn op_ffi_call( /// A non-blocking FFI call. //#[op(v8)] -fn op_ffi_call_nonblocking( - scope: &mut v8::HandleScope, +fn op_ffi_call_nonblocking<'a>( + scope: &mut v8::HandleScope<'a>, state: &mut deno_core::OpState, - args: FfiCallArgs, + args: FfiCallArgs<'a>, ) -> impl Future> + 'static { let block = || -> Result<(Symbol, Vec), AnyError> { let resource = state @@ -1137,7 +1137,7 @@ fn op_ffi_call_nonblocking( Ok(( symbol.clone(), ffi_parse_args( - unsafe { std::mem::transmute(scope) }, + scope, args.parameters, &symbol.parameter_types, &state.resource_table, From 69ae9bfbba6f496e00153f9478a438d737c11e21 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 8 Jun 2022 13:34:59 +0300 Subject: [PATCH 19/64] fmt & lint --- ext/ffi/00_ffi.js | 17 +++++++++++++---- ext/ffi/lib.rs | 39 +++++++++++++++++++-------------------- ops/lib.rs | 11 +++++------ 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 108bd05f3f24a5..9894fc625bdb2b 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -6,12 +6,13 @@ const __bootstrap = window.__bootstrap; const { ArrayBufferPrototype, - Uint8Array, BigInt, + Error, Number, ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, TypeError, + Uint8Array, } = window.__bootstrap.primordials; function pack64(value) { @@ -183,7 +184,9 @@ if (ObjectPrototypeIsPrototypeOf(RegisteredCallback, arg)) { parameters.push(arg); } else { - throw new TypeError("Invalid ffi arg value, expected RegisteredCallback"); + throw new TypeError( + "Invalid ffi arg value, expected RegisteredCallback", + ); } } else { parameters.push(arg); @@ -259,7 +262,9 @@ constructor(definition, callback) { if (definition.nonblocking) { - throw new TypeError("Invalid ffi RegisteredCallback, cannot be nonblocking"); + throw new TypeError( + "Invalid ffi RegisteredCallback, cannot be nonblocking", + ); } this.#rid = core.opSync("op_ffi_register_callback", definition, callback); this.definition = definition; @@ -274,7 +279,11 @@ if (this.definition.nonblocking) { throw new Error("Unreachable"); } else { - const result = core.opSync("op_ffi_call_registered_callback", this.#rid, parameters); + const result = core.opSync( + "op_ffi_call_registered_callback", + this.#rid, + parameters, + ); if (this.definition.result === "pointer") { return new UnsafePointer(unpackU64(result)); diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index a23b838cee4ec1..699d69e21e83e8 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -507,7 +507,7 @@ impl<'scope> FfiCallPtrArgs<'scope> { fn ffi_parse_args<'scope>( scope: &mut v8::HandleScope<'scope>, args: serde_v8::Value<'scope>, - parameter_types: &Vec, + parameter_types: &[NativeType], resource_table: &deno_core::ResourceTable, ) -> Result, AnyError> where @@ -526,48 +526,48 @@ where NativeType::U8 => { let value = value .uint32_value(scope) - .ok_or(type_error("Invalid FFI u8 type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI u8 type, expected number"))? as u8; ffi_args.push(NativeValue { u8_value: value }); } NativeType::I8 => { let value = value .int32_value(scope) - .ok_or(type_error("Invalid FFI i8 type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI i8 type, expected number"))? as i8; ffi_args.push(NativeValue { i8_value: value }); } NativeType::U16 => { let value = value .uint32_value(scope) - .ok_or(type_error("Invalid FFI u16 type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI u16 type, expected number"))? as u16; ffi_args.push(NativeValue { u16_value: value }); } NativeType::I16 => { let value = value .int32_value(scope) - .ok_or(type_error("Invalid FFI i16 type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI i16 type, expected number"))? as i16; ffi_args.push(NativeValue { i16_value: value }); } NativeType::U32 => { let value = value .uint32_value(scope) - .ok_or(type_error("Invalid FFI u32 type, expected number"))?; + .ok_or_else(|| type_error("Invalid FFI u32 type, expected number"))?; ffi_args.push(NativeValue { u32_value: value }); } NativeType::I32 => { let value = value .int32_value(scope) - .ok_or(type_error("Invalid FFI i32 type, expected number"))?; + .ok_or_else(|| type_error("Invalid FFI i32 type, expected number"))?; ffi_args.push(NativeValue { i32_value: value }); } NativeType::U64 => { // TODO: Handle BigInt let value = value .integer_value(scope) - .ok_or(type_error("Invalid FFI u64 type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI u64 type, expected number"))? as u64; ffi_args.push(NativeValue { u64_value: value }); } @@ -575,7 +575,7 @@ where // TODO: Handle BigInt let value = value .integer_value(scope) - .ok_or(type_error("Invalid FFI i64 type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI i64 type, expected number"))? as i64; ffi_args.push(NativeValue { i64_value: value }); } @@ -583,7 +583,7 @@ where // TODO: Handle BigInt let value = value .integer_value(scope) - .ok_or(type_error("Invalid FFI usize type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI usize type, expected number"))? as usize; ffi_args.push(NativeValue { usize_value: value }); } @@ -591,21 +591,21 @@ where // TODO: Handle BigInt let value = value .integer_value(scope) - .ok_or(type_error("Invalid FFI isize type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI isize type, expected number"))? as isize; ffi_args.push(NativeValue { isize_value: value }); } NativeType::F32 => { let value = value .number_value(scope) - .ok_or(type_error("Invalid FFI f32 type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI f32 type, expected number"))? as f32; ffi_args.push(NativeValue { f32_value: value }); } NativeType::F64 => { let value = value .integer_value(scope) - .ok_or(type_error("Invalid FFI f64 type, expected number"))? + .ok_or_else(|| type_error("Invalid FFI f64 type, expected number"))? as f64; ffi_args.push(NativeValue { f64_value: value }); } @@ -816,7 +816,6 @@ unsafe extern "C" fn deno_ffi_callback( }; std::mem::forget(callback); - std::mem::forget(context); match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 => { @@ -902,12 +901,12 @@ fn op_ffi_register_callback( context, isolate, parameters: args.parameters.clone(), - result: args.result.clone(), + result: args.result, })) }; let cif = Cif::new( args.parameters.into_iter().map(libffi::middle::Type::from), - libffi::middle::Type::from(args.result.clone()), + libffi::middle::Type::from(args.result), ); let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, info); @@ -973,7 +972,7 @@ where CREATE_SCOPE.with(|s| { s.replace(Some(true)); }); - Ok(result.into()) + Ok(result) } #[op(v8)] @@ -1001,6 +1000,7 @@ where } //#[op(v8)] +#[allow(dead_code)] async fn op_ffi_call_ptr_nonblocking<'a, FP>( scope: &mut v8::HandleScope<'a>, state: Rc>, @@ -1122,6 +1122,7 @@ fn op_ffi_call( /// A non-blocking FFI call. //#[op(v8)] +#[allow(dead_code)] fn op_ffi_call_nonblocking<'a>( scope: &mut v8::HandleScope<'a>, state: &mut deno_core::OpState, @@ -1152,9 +1153,7 @@ fn op_ffi_call_nonblocking<'a>( }; ffi_call(call_args, &symbol) }); - async move { - join_handle.await? - } + async move { join_handle.await? } } #[op] diff --git a/ops/lib.rs b/ops/lib.rs index a777355055c449..e9d33cb364839f 100644 --- a/ops/lib.rs +++ b/ops/lib.rs @@ -76,14 +76,13 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream { let mut generics = func.sig.generics.clone(); let scope_lifetime = syn::LifetimeDef::new(syn::Lifetime::new("'scope", Span::call_site())); - if generics - .lifetimes() - .find(|def| **def == scope_lifetime) - .is_none() + if !generics + .lifetimes() + .any(|def| *def == scope_lifetime) { generics - .params - .push(syn::GenericParam::Lifetime(scope_lifetime)); + .params + .push(syn::GenericParam::Lifetime(scope_lifetime)); } let type_params = exclude_lifetime_params(&func.sig.generics.params); let where_clause = &func.sig.generics.where_clause; From 48f439d3e941047cd3d1b8a24789f4470b2038d6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 8 Jun 2022 19:04:22 +0300 Subject: [PATCH 20/64] fmt & lint --- ext/ffi/lib.rs | 14 ++++++-------- ops/lib.rs | 5 +---- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 699d69e21e83e8..c1130f355f5753 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -581,18 +581,16 @@ where } NativeType::USize => { // TODO: Handle BigInt - let value = value - .integer_value(scope) - .ok_or_else(|| type_error("Invalid FFI usize type, expected number"))? - as usize; + let value = value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI usize type, expected number") + })? as usize; ffi_args.push(NativeValue { usize_value: value }); } NativeType::ISize => { // TODO: Handle BigInt - let value = value - .integer_value(scope) - .ok_or_else(|| type_error("Invalid FFI isize type, expected number"))? - as isize; + let value = value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI isize type, expected number") + })? as isize; ffi_args.push(NativeValue { isize_value: value }); } NativeType::F32 => { diff --git a/ops/lib.rs b/ops/lib.rs index e9d33cb364839f..8d9684d4fe9a2b 100644 --- a/ops/lib.rs +++ b/ops/lib.rs @@ -76,10 +76,7 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream { let mut generics = func.sig.generics.clone(); let scope_lifetime = syn::LifetimeDef::new(syn::Lifetime::new("'scope", Span::call_site())); - if !generics - .lifetimes() - .any(|def| *def == scope_lifetime) - { + if !generics.lifetimes().any(|def| *def == scope_lifetime) { generics .params .push(syn::GenericParam::Lifetime(scope_lifetime)); From 23f5800085145af0f8eaa65d56008957ac7dcbfa Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 8 Jun 2022 22:03:55 +0300 Subject: [PATCH 21/64] Actually pass RegisteredCallback RID to FFI --- ext/ffi/00_ffi.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 9894fc625bdb2b..c8d02ebb7eb9fd 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -11,6 +11,7 @@ Number, ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, + Symbol, TypeError, Uint8Array, } = window.__bootstrap.primordials; @@ -181,8 +182,8 @@ parameters.push(arg); } else if (type === "function") { - if (ObjectPrototypeIsPrototypeOf(RegisteredCallback, arg)) { - parameters.push(arg); + if (ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { + parameters.push(arg[_rid]); } else { throw new TypeError( "Invalid ffi arg value, expected RegisteredCallback", @@ -255,8 +256,10 @@ } } + const _rid = Symbol("[[rid]]"); + class RegisteredCallback { - #rid; + [_rid]; definition; callback; @@ -266,7 +269,7 @@ "Invalid ffi RegisteredCallback, cannot be nonblocking", ); } - this.#rid = core.opSync("op_ffi_register_callback", definition, callback); + this[_rid] = core.opSync("op_ffi_register_callback", definition, callback); this.definition = definition; this.callback = callback; } @@ -281,7 +284,7 @@ } else { const result = core.opSync( "op_ffi_call_registered_callback", - this.#rid, + this[_rid], parameters, ); @@ -294,10 +297,12 @@ } close() { - core.close(this.#rid); + core.close(this[_rid]); } } + const RegisteredCallbackPrototype = RegisteredCallback.prototype; + function registerCallback(definition, callback) { if (!definition || !callback) { throw new TypeError("Invalid arguments"); From 6971d16a89ec74278c129e9db66eebdea388d1c9 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 9 Jun 2022 16:25:47 +0300 Subject: [PATCH 22/64] Actually functioning registered callbacks! --- ext/ffi/00_ffi.js | 2 +- ext/ffi/lib.rs | 90 ++++++++++++++++++++++++++++++----------------- 2 files changed, 58 insertions(+), 34 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index c8d02ebb7eb9fd..20edc756559f0e 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -181,7 +181,7 @@ } parameters.push(arg); - } else if (type === "function") { + } else if (typeof type === "object" && type !== null && "function" in type) { if (ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { parameters.push(arg[_rid]); } else { diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index c1130f355f5753..944f74bff92cba 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -185,6 +185,11 @@ pub fn init(unstable: bool) -> Extension { .build() } + +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] +struct NativeTypeFunction { +} + /// Defines the accepted types that can be used as /// parameters and return values in FFI. #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] @@ -204,7 +209,7 @@ enum NativeType { F32, F64, Pointer, - Function, + Function(NativeTypeFunction), } impl From for libffi::middle::Type { @@ -224,7 +229,7 @@ impl From for libffi::middle::Type { NativeType::F32 => libffi::middle::Type::f32(), NativeType::F64 => libffi::middle::Type::f64(), NativeType::Pointer => libffi::middle::Type::pointer(), - NativeType::Function => libffi::middle::Type::pointer(), + NativeType::Function(_) => libffi::middle::Type::pointer(), } } } @@ -266,7 +271,7 @@ impl NativeValue { NativeType::F32 => Arg::new(&self.f32_value), NativeType::F64 => Arg::new(&self.f64_value), NativeType::Pointer => Arg::new(&self.pointer), - NativeType::Function => Arg::new(&self.pointer), + NativeType::Function(_) => Arg::new(&self.pointer), } } } @@ -625,16 +630,16 @@ where return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer, or ArrayBufferView")); } } - NativeType::Function => { + NativeType::Function(_) => { if value.is_null() { let value: *const u8 = ptr::null(); ffi_args.push(NativeValue { pointer: value }) } else if value.is_number() { let value: ResourceId = value.uint32_value(scope).unwrap(); let rc = resource_table.get::(value)?; - let fn_ref = - rc.closure.code_ptr() as *const unsafe extern "C" fn() as *const u8; - ffi_args.push(NativeValue { pointer: fn_ref }); + let function = + *rc.closure.code_ptr(); + ffi_args.push(NativeValue { pointer: function as u64 as *const u8 }); } else if value.is_big_int() { // Do we support this? let value = v8::Local::::try_from(value)?; @@ -710,7 +715,7 @@ fn ffi_call( NativeType::F64 => { json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) } - NativeType::Pointer | NativeType::Function => { + NativeType::Pointer | NativeType::Function(_) => { json!(U32x2::from(unsafe { symbol.cif.call::<*const u8>(symbol.ptr, &call_args) } as u64)) @@ -787,10 +792,10 @@ unsafe extern "C" fn deno_ffi_callback( FFI_TYPE_INT => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), FFI_TYPE_FLOAT => serde_v8::to_v8(&mut scope, *((*val) as *const f32)), FFI_TYPE_DOUBLE => serde_v8::to_v8(&mut scope, *((*val) as *const f64)), - FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { - let ptr = U32x2::from(*((*val) as *const u64)); - serde_v8::to_v8(&mut scope, ptr) - } + // FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { + // let ptr = U32x2::from(*((*val) as *const u64)); + // serde_v8::to_v8(&mut scope, ptr) + // } FFI_TYPE_SINT8 => serde_v8::to_v8(&mut scope, *((*val) as *const i8)), FFI_TYPE_UINT8 => serde_v8::to_v8(&mut scope, *((*val) as *const u8)), FFI_TYPE_SINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const i16)), @@ -798,7 +803,7 @@ unsafe extern "C" fn deno_ffi_callback( FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), FFI_TYPE_UINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const u32)), FFI_TYPE_SINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const i64)), - FFI_TYPE_UINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const u64)), + FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const u64)), FFI_TYPE_VOID => serde_v8::to_v8(&mut scope, ()), _ => { panic!("Unsupported parameter type") @@ -814,7 +819,7 @@ unsafe extern "C" fn deno_ffi_callback( }; std::mem::forget(callback); - + match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 => { *(result as *mut i32) = serde_v8::from_v8(&mut scope, value) @@ -994,7 +999,14 @@ where &symbol.parameter_types, &state.resource_table, )?; - ffi_call(call_args, &symbol) + CREATE_SCOPE.with(|s| { + s.replace(Some(false)); + }); + let result = ffi_call(call_args, &symbol); + CREATE_SCOPE.with(|s| { + s.replace(Some(true)); + }); + result } //#[op(v8)] @@ -1087,7 +1099,7 @@ fn op_ffi_get_static( NativeType::F64 => { json!(unsafe { ptr::read_unaligned(data_ptr as *const f64) }) } - NativeType::Pointer | NativeType::Function => { + NativeType::Pointer | NativeType::Function(_) => { json!(U32x2::from(data_ptr as *const u8 as u64)) } }) @@ -1096,26 +1108,38 @@ fn op_ffi_get_static( #[op(v8)] fn op_ffi_call( scope: &mut v8::HandleScope, - state: &mut deno_core::OpState, + state: Rc>, args: FfiCallArgs, ) -> Result { - let resource = state - .resource_table - .get::(args.rid)?; - - let symbol = resource - .symbols - .get(&args.symbol) - .ok_or_else(bad_resource_id)?; - - let call_args = ffi_parse_args( - unsafe { std::mem::transmute(scope) }, - args.parameters, - &symbol.parameter_types, - &state.resource_table, - )?; + let (symbol, call_args) = { + let state = &mut state.borrow_mut(); + let resource = state + .resource_table + .get::(args.rid)?; + + let symbol = resource + .symbols + .get(&args.symbol) + .ok_or_else(bad_resource_id)? + .clone(); + + let call_args = ffi_parse_args( + unsafe { std::mem::transmute(scope) }, + args.parameters, + &symbol.parameter_types, + &state.resource_table, + )?; + (symbol, call_args) + }; - ffi_call(call_args, symbol) + CREATE_SCOPE.with(|s| { + s.replace(Some(false)); + }); + let result = ffi_call(call_args, &symbol); + CREATE_SCOPE.with(|s| { + s.replace(Some(true)); + }); + result } /// A non-blocking FFI call. From e271cc7c5cb92a96e1dee2a86a7687e08ba7d37d Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 9 Jun 2022 16:28:09 +0300 Subject: [PATCH 23/64] fmt & lint --- ext/ffi/00_ffi.js | 10 ++++++++-- ext/ffi/lib.rs | 21 +++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 20edc756559f0e..cdf7384c117c5a 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -181,7 +181,9 @@ } parameters.push(arg); - } else if (typeof type === "object" && type !== null && "function" in type) { + } else if ( + typeof type === "object" && type !== null && "function" in type + ) { if (ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { parameters.push(arg[_rid]); } else { @@ -269,7 +271,11 @@ "Invalid ffi RegisteredCallback, cannot be nonblocking", ); } - this[_rid] = core.opSync("op_ffi_register_callback", definition, callback); + this[_rid] = core.opSync( + "op_ffi_register_callback", + definition, + callback, + ); this.definition = definition; this.callback = callback; } diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 944f74bff92cba..a494723c1204a1 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -185,10 +185,8 @@ pub fn init(unstable: bool) -> Extension { .build() } - #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] -struct NativeTypeFunction { -} +struct NativeTypeFunction {} /// Defines the accepted types that can be used as /// parameters and return values in FFI. @@ -637,9 +635,10 @@ where } else if value.is_number() { let value: ResourceId = value.uint32_value(scope).unwrap(); let rc = resource_table.get::(value)?; - let function = - *rc.closure.code_ptr(); - ffi_args.push(NativeValue { pointer: function as u64 as *const u8 }); + let function = *rc.closure.code_ptr(); + ffi_args.push(NativeValue { + pointer: function as usize as *const u8, + }); } else if value.is_big_int() { // Do we support this? let value = v8::Local::::try_from(value)?; @@ -803,7 +802,9 @@ unsafe extern "C" fn deno_ffi_callback( FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), FFI_TYPE_UINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const u32)), FFI_TYPE_SINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const i64)), - FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const u64)), + FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 => { + serde_v8::to_v8(&mut scope, *((*val) as *const u64)) + } FFI_TYPE_VOID => serde_v8::to_v8(&mut scope, ()), _ => { panic!("Unsupported parameter type") @@ -819,7 +820,7 @@ unsafe extern "C" fn deno_ffi_callback( }; std::mem::forget(callback); - + match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 => { *(result as *mut i32) = serde_v8::from_v8(&mut scope, value) @@ -1116,13 +1117,13 @@ fn op_ffi_call( let resource = state .resource_table .get::(args.rid)?; - + let symbol = resource .symbols .get(&args.symbol) .ok_or_else(bad_resource_id)? .clone(); - + let call_args = ffi_parse_args( unsafe { std::mem::transmute(scope) }, args.parameters, From 98110af14ead752655ae5e4e444a017b21db99f0 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 11 Jun 2022 00:01:04 +0300 Subject: [PATCH 24/64] Remove likely unnecessary active scope tracking from ffi calls --- ext/ffi/lib.rs | 51 ++++++++++++-------------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index a494723c1204a1..0bb5fe6129bec8 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -37,7 +37,7 @@ use std::ptr; use std::rc::Rc; thread_local! { - static CREATE_SCOPE: RefCell> = RefCell::new(None); + static IS_EVENT_LOOP_THREAD: RefCell = RefCell::new(false); } pub struct Unstable(pub bool); @@ -96,7 +96,7 @@ impl DynamicLibraryResource { name: String, foreign_fn: ForeignFunction, ) -> Result<(), AnyError> { - CREATE_SCOPE.with(|s| s.replace(Some(true))); + IS_EVENT_LOOP_THREAD.with(|s| s.replace(true)); let symbol = match &foreign_fn.name { Some(symbol) => symbol, None => &name, @@ -761,23 +761,17 @@ unsafe extern "C" fn deno_ffi_callback( NonNull, v8::Local, >(info.context); - let mut cb_scope = v8::CallbackScope::new(context); - let mut scope = CREATE_SCOPE.with(|s| match *s.borrow() { - Some(create_scope) => { - if create_scope { - // Call from main thread without an active scope. - // This shouldn't really be possible but here we are. - v8::HandleScope::with_context(isolate, context) - } else { - // Call from main thread with an active scope in existence, piggyback on it. - v8::HandleScope::new(&mut cb_scope) - } - } - None => { + IS_EVENT_LOOP_THREAD.with(|is_event_loop_thread| { + if !(*is_event_loop_thread.borrow()) { // Call from another thread, PANIC IN THE DISCO! todo!("Call from another thread"); } }); + // Call from main thread: Presume a scope exists already. + // If there wasn't then deno_ffi_callback would be getting called + // by eg. an FFI side interrupt but it's not clear if that's possible. + let mut cb_scope = v8::CallbackScope::new(context); + let mut scope = v8::HandleScope::new(&mut cb_scope); let func = callback.open(&mut scope); let result = result as *mut c_void; let repr: &[*mut ffi_type] = @@ -955,9 +949,6 @@ where &info.parameters, &state.borrow().resource_table, )?; - CREATE_SCOPE.with(|s| { - s.replace(Some(false)); - }); let cif = libffi::middle::Cif::new( info .parameters @@ -972,11 +963,7 @@ where parameter_types: info.parameters.clone(), result_type: info.result, }; - let result = ffi_call(call_args, &symbol)?; - CREATE_SCOPE.with(|s| { - s.replace(Some(true)); - }); - Ok(result) + ffi_call(call_args, &symbol) } #[op(v8)] @@ -1000,14 +987,7 @@ where &symbol.parameter_types, &state.resource_table, )?; - CREATE_SCOPE.with(|s| { - s.replace(Some(false)); - }); - let result = ffi_call(call_args, &symbol); - CREATE_SCOPE.with(|s| { - s.replace(Some(true)); - }); - result + ffi_call(call_args, &symbol) } //#[op(v8)] @@ -1133,14 +1113,7 @@ fn op_ffi_call( (symbol, call_args) }; - CREATE_SCOPE.with(|s| { - s.replace(Some(false)); - }); - let result = ffi_call(call_args, &symbol); - CREATE_SCOPE.with(|s| { - s.replace(Some(true)); - }); - result + ffi_call(call_args, &symbol) } /// A non-blocking FFI call. From ccdd12218b154b0f55ea8b7b2a13ab010e78c223 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 11 Jun 2022 10:35:33 +0530 Subject: [PATCH 25/64] ops: support a result returning a future returning a result. --- ext/ffi/lib.rs | 51 +++++++++++++-------------------- ops/lib.rs | 78 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 70 insertions(+), 59 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 0bb5fe6129bec8..75cbb0e8e1086f 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1117,39 +1117,28 @@ fn op_ffi_call( } /// A non-blocking FFI call. -//#[op(v8)] -#[allow(dead_code)] -fn op_ffi_call_nonblocking<'a>( - scope: &mut v8::HandleScope<'a>, +#[op(v8)] +fn op_ffi_call_nonblocking<'scope>( + scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, - args: FfiCallArgs<'a>, -) -> impl Future> + 'static { - let block = || -> Result<(Symbol, Vec), AnyError> { - let resource = state - .resource_table - .get::(args.rid)?; - let symbols = &resource.symbols; - let symbol = symbols.get(&args.symbol).ok_or_else(bad_resource_id)?; - - Ok(( - symbol.clone(), - ffi_parse_args( - scope, - args.parameters, - &symbol.parameter_types, - &state.resource_table, - )?, - )) - }(); + args: FfiCallArgs<'scope>, +) -> Result> + 'static, AnyError> { + let resource = state + .resource_table + .get::(args.rid)?; + let symbols = &resource.symbols; + let symbol = symbols.get(&args.symbol).ok_or_else(bad_resource_id)?; + let symbol = symbol.clone(); + let call_args = ffi_parse_args( + scope, + args.parameters, + &symbol.parameter_types, + &state.resource_table, + )?; - let join_handle = tokio::task::spawn_blocking(move || { - let (symbol, call_args) = match block { - Ok((sym, c_args)) => (sym, c_args), - Err(err) => return Err(err), - }; - ffi_call(call_args, &symbol) - }); - async move { join_handle.await? } + let join_handle = + tokio::task::spawn_blocking(move || ffi_call(call_args, &symbol)); + Ok(async move { join_handle.await? }) } #[op] diff --git a/ops/lib.rs b/ops/lib.rs index 8d9684d4fe9a2b..f5af772fecc2f1 100644 --- a/ops/lib.rs +++ b/ops/lib.rs @@ -152,32 +152,49 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream { fn codegen_v8_async( core: &TokenStream2, f: &syn::ItemFn, - _margs: MacroArgs, + margs: MacroArgs, asyncness: bool, ) -> TokenStream2 { - let arg0 = f.sig.inputs.first(); - let uses_opstate = arg0.map(is_rc_refcell_opstate).unwrap_or_default(); - let args_head = if uses_opstate { - quote! { state, } - } else { - quote! {} - }; - let rust_i0 = if uses_opstate { 1 } else { 0 }; + let MacroArgs { is_v8, .. } = margs; + let special_args = f + .sig + .inputs + .iter() + .map_while(|a| { + (if is_v8 { scope_arg(a) } else { None }).or_else(|| opstate_arg(a)) + }) + .collect::>(); + let rust_i0 = special_args.len(); + let args_head = special_args.into_iter().collect::(); + let (arg_decls, args_tail) = codegen_args(core, f, rust_i0, 1); let type_params = exclude_lifetime_params(&f.sig.generics.params); - let (pre_result, result_fut) = match asyncness { + let (pre_result, mut result_fut) = match asyncness { true => ( quote! {}, - quote! { Self::call::<#type_params>(#args_head #args_tail) }, + quote! { Self::call::<#type_params>(#args_head #args_tail).await; }, ), false => ( quote! { let result_fut = Self::call::<#type_params>(#args_head #args_tail); }, - quote! { result_fut }, + quote! { result_fut.await; }, ), }; let result_wrapper = match is_result(&f.sig.output) { - true => quote! {}, + true => { + // Support `Result> + 'static, AnyError>` + if !asyncness { + result_fut = quote! { result_fut; }; + quote! { + let result = match result { + Ok(fut) => fut.await, + Err(e) => return (promise_id, op_id, #core::_ops::to_op_result::<()>(get_class, Err(e))), + }; + } + } else { + quote! {} + } + } false => quote! { let result = Ok(result); }, }; @@ -216,13 +233,31 @@ fn codegen_v8_async( #pre_result #core::_ops::queue_async_op(scope, async move { - let result = #result_fut.await; + let result = #result_fut #result_wrapper (promise_id, op_id, #core::_ops::to_op_result(get_class, result)) }); } } +fn scope_arg(arg: &FnArg) -> Option { + if is_handle_scope(arg) { + Some(quote! { scope, }) + } else { + None + } +} + +fn opstate_arg(arg: &FnArg) -> Option { + match arg { + arg if is_rc_refcell_opstate(arg) => Some(quote! { ctx.state.clone(), }), + arg if is_mut_ref_opstate(arg) => { + Some(quote! { &mut ctx.state.borrow_mut(), }) + } + _ => None, + } +} + /// Generate the body of a v8 func for a sync op fn codegen_v8_sync( core: &TokenStream2, @@ -230,20 +265,6 @@ fn codegen_v8_sync( margs: MacroArgs, ) -> TokenStream2 { let MacroArgs { is_v8, .. } = margs; - let scope_arg = |arg: &FnArg| { - if is_handle_scope(arg) { - Some(quote! { scope, }) - } else { - None - } - }; - let opstate_arg = |arg: &FnArg| match arg { - arg if is_rc_refcell_opstate(arg) => Some(quote! { ctx.state.clone(), }), - arg if is_mut_ref_opstate(arg) => { - Some(quote! { &mut ctx.state.borrow_mut(), }) - } - _ => None, - }; let special_args = f .sig .inputs @@ -407,6 +428,7 @@ fn is_handle_scope(arg: &syn::FnArg) -> bool { || tokens(arg).ends_with(": & mut deno_core :: v8 :: HandleScope") || tokens(arg).ends_with(": & mut deno_core :: v8 :: HandleScope < 'a >") || tokens(arg).contains("mut v8 :: HandleScope") + || tokens(arg).ends_with(": & mut v8 :: HandeScope < 'scope >") } fn is_future(ty: impl ToTokens) -> bool { From 283dc1548a413ff2a32f23574c437cf8aed31e46 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 11 Jun 2022 10:49:39 +0530 Subject: [PATCH 26/64] reenable nonblocking ops as hybrids --- ext/ffi/lib.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 75cbb0e8e1086f..6ce8afb11a45e9 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -158,9 +158,9 @@ pub fn init(unstable: bool) -> Extension { op_ffi_load::decl::

(), op_ffi_get_static::decl(), op_ffi_call::decl(), - //op_ffi_call_nonblocking::decl(), + op_ffi_call_nonblocking::decl(), op_ffi_call_ptr::decl::

(), - //op_ffi_call_ptr_nonblocking::decl::

(), + op_ffi_call_ptr_nonblocking::decl::

(), op_ffi_ptr_of::decl::

(), op_ffi_buf_copy_into::decl::

(), op_ffi_cstr_read::decl::

(), @@ -990,16 +990,14 @@ where ffi_call(call_args, &symbol) } -//#[op(v8)] -#[allow(dead_code)] -async fn op_ffi_call_ptr_nonblocking<'a, FP>( - scope: &mut v8::HandleScope<'a>, +#[op(v8)] +fn op_ffi_call_ptr_nonblocking<'scope, FP>( + scope: &mut v8::HandleScope<'scope>, state: Rc>, - args: FfiCallPtrArgs<'a>, -) -> Result + args: FfiCallPtrArgs<'scope>, +) -> Result>, AnyError> where FP: FfiPermissions + 'static, - 'a: 'a, { check_unstable2(&state, "Deno.UnsafeFnPointer#call"); @@ -1016,9 +1014,10 @@ where &args.def.parameters, &state.borrow_mut().resource_table, )?; - tokio::task::spawn_blocking(move || ffi_call(call_args, &symbol)) - .await - .unwrap() + + Ok(async move { + tokio::task::spawn_blocking(move || ffi_call(call_args, &symbol)).await? + }) } #[derive(Deserialize)] From 4fd175f9e289ae9dcc408ba7303e2dc2fe6a7596 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 11 Jun 2022 16:46:17 +0300 Subject: [PATCH 27/64] Rewrite ext/ffi to return serde_v8::Value --- ext/ffi/00_ffi.js | 121 +++---- ext/ffi/lib.rs | 783 +++++++++++++++++++++++++++++----------------- 2 files changed, 547 insertions(+), 357 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index cdf7384c117c5a..dfc8b5308e0a8c 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -8,7 +8,6 @@ ArrayBufferPrototype, BigInt, Error, - Number, ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, Symbol, @@ -16,19 +15,6 @@ Uint8Array, } = window.__bootstrap.primordials; - function pack64(value) { - return [Number(value >> 32n) >>> 0, Number(value & 0xFFFFFFFFn)]; - } - - function unpackU64([hi, lo]) { - return BigInt(hi) << 32n | BigInt(lo); - } - - function unpackI64([hi, lo]) { - const u64 = unpackU64([hi, lo]); - return u64 >> 63n ? u64 - 0x10000000000000000n : u64; - } - class UnsafePointerView { pointer; @@ -39,77 +25,77 @@ getUint8(offset = 0) { return core.opSync( "op_ffi_read_u8", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } getInt8(offset = 0) { return core.opSync( "op_ffi_read_i8", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } getUint16(offset = 0) { return core.opSync( "op_ffi_read_u16", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } getInt16(offset = 0) { return core.opSync( "op_ffi_read_i16", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } getUint32(offset = 0) { return core.opSync( "op_ffi_read_u32", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } getInt32(offset = 0) { return core.opSync( "op_ffi_read_i32", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } getBigUint64(offset = 0) { - return unpackU64(core.opSync( + return core.opSync( "op_ffi_read_u64", - pack64(this.pointer.value + BigInt(offset)), - )); + this.pointer.value + BigInt(offset), + ); } getBigInt64(offset = 0) { - return unpackI64(core.opSync( + return core.opSync( "op_ffi_read_u64", - pack64(this.pointer.value + BigInt(offset)), - )); + this.pointer.value + BigInt(offset), + ); } getFloat32(offset = 0) { return core.opSync( "op_ffi_read_f32", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } getFloat64(offset = 0) { return core.opSync( "op_ffi_read_f64", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } getCString(offset = 0) { return core.opSync( "op_ffi_cstr_read", - pack64(this.pointer.value + BigInt(offset)), + this.pointer.value + BigInt(offset), ); } @@ -120,11 +106,12 @@ } copyInto(destination, offset = 0) { - core.opSync("op_ffi_buf_copy_into", [ - pack64(this.pointer.value + BigInt(offset)), + core.opSync( + "op_ffi_buf_copy_into", + this.pointer.value + BigInt(offset), destination, destination.byteLength, - ]); + ); } } @@ -132,12 +119,15 @@ value; constructor(value) { + if (typeof value === "number") { + value = BigInt(value); + } this.value = value; } static of(typedArray) { return new UnsafePointer( - unpackU64(core.opSync("op_ffi_ptr_of", typedArray)), + core.opSync("op_ffi_ptr_of", typedArray), ); } @@ -202,15 +192,7 @@ function unpackResult(type, result) { switch (type) { case "pointer": - return new UnsafePointer(unpackU64(result)); - case "u64": - return unpackU64(result); - case "i64": - return unpackI64(result); - case "usize": - return unpackU64(result); - case "isize": - return unpackI64(result); + return new UnsafePointer(result); default: return result; } @@ -231,26 +213,28 @@ args, ); if (this.definition.nonblocking) { - const promise = core.opAsync("op_ffi_call_ptr_nonblocking", { - pointer: this.pointer.value, - def: this.definition, + const promise = core.opAsync( + "op_ffi_call_ptr_nonblocking", + this.pointer.value, + this.definition, parameters, - }); + ); if (this.definition.result === "pointer") { - return promise.then((value) => new UnsafePointer(unpackU64(value))); + return promise.then((value) => new UnsafePointer(value)); } return promise; } else { - const result = core.opSync("op_ffi_call_ptr", { - pointer: this.pointer.value, - def: this.definition, + const result = core.opSync( + "op_ffi_call_ptr", + this.pointer.value, + this.definition, parameters, - }); + ); if (this.definition.result === "pointer") { - return new UnsafePointer(unpackU64(result)); + return new UnsafePointer(result); } return result; @@ -295,7 +279,7 @@ ); if (this.definition.result === "pointer") { - return new UnsafePointer(unpackU64(result)); + return new UnsafePointer(result); } return result; @@ -335,19 +319,12 @@ const name = symbols[symbol].name || symbol; let value = core.opSync( "op_ffi_get_static", - { - rid: this.#rid, - name, - type, - }, + this.#rid, + name, + type, ); - if (type === "pointer" || type === "u64") { - value = unpackU64(value); - if (type === "pointer") { - value = new UnsafePointer(value); - } - } else if (type === "i64") { - value = unpackI64(value); + if (type === "pointer") { + value = new UnsafePointer(value); } ObjectDefineProperty( this.symbols, @@ -370,11 +347,12 @@ const { parameters } = prepareArgs(types, args); if (isNonBlocking) { - const promise = core.opAsync("op_ffi_call_nonblocking", { - rid: this.#rid, + const promise = core.opAsync( + "op_ffi_call_nonblocking", + this.#rid, symbol, parameters, - }); + ); if (resultType === "pointer") { return promise.then((result) => unpackResult(resultType, result)); @@ -382,11 +360,12 @@ return promise; } else { - const result = core.opSync("op_ffi_call", { - rid: this.#rid, + const result = core.opSync( + "op_ffi_call", + this.#rid, symbol, parameters, - }); + ); return unpackResult(resultType, result); } diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 6ce8afb11a45e9..50776b3d218782 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -10,8 +10,8 @@ use deno_core::futures::Future; use deno_core::include_js_files; use deno_core::op; -use deno_core::serde_json::json; -use deno_core::serde_json::Value; +//use deno_core::serde_json::json; +//use deno_core::serde_json::Value; use deno_core::serde_v8; use deno_core::v8; use deno_core::Extension; @@ -24,7 +24,7 @@ use libffi::middle::Arg; use libffi::middle::Cif; use libffi::raw::*; use serde::Deserialize; -use serde::Serialize; +//use serde::Serialize; use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; @@ -185,9 +185,6 @@ pub fn init(unstable: bool) -> Extension { .build() } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] -struct NativeTypeFunction {} - /// Defines the accepted types that can be used as /// parameters and return values in FFI. #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] @@ -207,7 +204,7 @@ enum NativeType { F32, F64, Pointer, - Function(NativeTypeFunction), + Function {}, } impl From for libffi::middle::Type { @@ -227,7 +224,7 @@ impl From for libffi::middle::Type { NativeType::F32 => libffi::middle::Type::f32(), NativeType::F64 => libffi::middle::Type::f64(), NativeType::Pointer => libffi::middle::Type::pointer(), - NativeType::Function(_) => libffi::middle::Type::pointer(), + NativeType::Function {} => libffi::middle::Type::pointer(), } } } @@ -236,7 +233,7 @@ impl From for libffi::middle::Type { /// to libffi argument types. #[repr(C)] union NativeValue { - //void_value: (), + void_value: (), u8_value: u8, i8_value: i8, u16_value: u16, @@ -268,48 +265,118 @@ impl NativeValue { NativeType::ISize => Arg::new(&self.isize_value), NativeType::F32 => Arg::new(&self.f32_value), NativeType::F64 => Arg::new(&self.f64_value), - NativeType::Pointer => Arg::new(&self.pointer), - NativeType::Function(_) => Arg::new(&self.pointer), + NativeType::Pointer | NativeType::Function {} => Arg::new(&self.pointer), } } -} - -unsafe impl Send for NativeValue {} - -#[derive(Serialize, Deserialize, Debug, Clone, Copy)] -struct U32x2(u32, u32); -impl From for U32x2 { - fn from(value: u64) -> Self { - Self((value >> 32) as u32, value as u32) - } -} - -impl From for u64 { - fn from(value: U32x2) -> Self { - (value.0 as u64) << 32 | value.1 as u64 - } -} - -impl TryFrom for U32x2 { - type Error = AnyError; - - fn try_from(value: Value) -> Result { - if let Some(value) = value.as_array() { - if let Some(hi) = value[0].as_u64() { - if let Some(lo) = value[1].as_u64() { - return Ok(U32x2(hi as u32, lo as u32)); + fn to_v8<'scope>( + &self, + scope: &mut v8::HandleScope<'scope>, + native_type: NativeType, + ) -> serde_v8::Value<'scope> { + match native_type { + NativeType::Void => { + let local_value = v8::undefined(scope); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::U8 => { + let local_value = + v8::Number::new(scope, unsafe { self.u8_value } as f64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::I8 => { + let local_value = + v8::Number::new(scope, unsafe { self.i8_value } as f64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::U16 => { + let local_value = + v8::Number::new(scope, unsafe { self.u16_value } as f64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::I16 => { + let local_value = + v8::Number::new(scope, unsafe { self.i16_value } as f64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::U32 => { + let local_value = + v8::Number::new(scope, unsafe { self.u32_value } as f64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::I32 => { + let local_value = + v8::Number::new(scope, unsafe { self.i32_value } as f64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::U64 => { + let local_value = + v8::BigInt::new_from_u64(scope, unsafe { self.u64_value }); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::I64 => { + let local_value = + v8::BigInt::new_from_i64(scope, unsafe { self.i64_value }); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::USize => { + let local_value = + v8::BigInt::new_from_u64(scope, unsafe { self.usize_value } as u64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::ISize => { + let local_value = + v8::BigInt::new_from_i64(scope, unsafe { self.isize_value } as i64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::F32 => { + let local_value = + v8::Number::new(scope, unsafe { self.f32_value } as f64); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::F64 => { + let local_value = v8::Number::new(scope, unsafe { self.f64_value }); + serde_v8::Value { + v8_value: local_value.into(), + } + } + NativeType::Pointer | NativeType::Function {} => { + let local_value = + v8::BigInt::new_from_u64(scope, unsafe { self.pointer } as u64); + serde_v8::Value { + v8_value: local_value.into(), } } } - - Err(type_error(format!( - "Expected FFI argument to be a signed integer, but got {:?}", - value - ))) } } +unsafe impl Send for NativeValue {} + #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct ForeignFunction { @@ -457,53 +524,22 @@ where Ok(state.resource_table.add(resource)) } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct FfiCallArgs<'scope> { - rid: ResourceId, - symbol: String, - parameters: serde_v8::Value<'scope>, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct FfiCallPtrArgs<'scope> { - pointer: U32x2, - def: ForeignFunction, - parameters: serde_v8::Value<'scope>, -} - -impl<'scope> From> for FfiCallArgs<'scope> { - fn from(args: FfiCallPtrArgs<'scope>) -> Self { - FfiCallArgs { - rid: 0, - symbol: String::new(), - parameters: args.parameters, - //buffers: args.buffers, - } - } -} - -impl<'scope> FfiCallPtrArgs<'scope> { - 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(), - ); +fn get_symbol_for_ptr(fn_ptr: u64, def: ForeignFunction) -> Symbol { + let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); + let cif = libffi::middle::Cif::new( + def + .parameters + .clone() + .into_iter() + .map(libffi::middle::Type::from), + def.result.into(), + ); - Symbol { - cif, - ptr, - parameter_types: self.def.parameters.clone(), - result_type: self.def.result, - } + Symbol { + cif, + ptr, + parameter_types: def.parameters.clone(), + result_type: def.result, } } @@ -628,7 +664,7 @@ where return Err(type_error("Invalid FFI pointer type, expected null, BigInt, ArrayBuffer, or ArrayBufferView")); } } - NativeType::Function(_) => { + NativeType::Function {} => { if value.is_null() { let value: *const u8 = ptr::null(); ffi_args.push(NativeValue { pointer: value }) @@ -656,69 +692,62 @@ where fn ffi_call( call_args: Vec, - symbol: &Symbol, -) -> Result { + cif: &libffi::middle::Cif, + fun_ptr: libffi::middle::CodePtr, + parameter_types: &[NativeType], + result_type: NativeType, +) -> Result { let call_args: Vec = call_args .iter() .enumerate() .map(|(index, ffi_arg)| unsafe { - ffi_arg.as_arg(*symbol.parameter_types.get(index).unwrap()) + ffi_arg.as_arg(*parameter_types.get(index).unwrap()) }) .collect(); - Ok(match symbol.result_type { - NativeType::Void => { - json!(unsafe { symbol.cif.call::<()>(symbol.ptr, &call_args) }) - } - NativeType::U8 => { - json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) - } - NativeType::I8 => { - json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) - } - NativeType::U16 => { - json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) - } - NativeType::I16 => { - json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) - } - NativeType::U32 => { - json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) - } - NativeType::I32 => { - json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) - } - NativeType::U64 => { - json!(U32x2::from(unsafe { - symbol.cif.call::(symbol.ptr, &call_args) - })) - } - NativeType::I64 => { - json!(U32x2::from(unsafe { - symbol.cif.call::(symbol.ptr, &call_args) - } as u64)) - } - NativeType::USize => { - json!(U32x2::from(unsafe { - symbol.cif.call::(symbol.ptr, &call_args) - } as u64)) - } - NativeType::ISize => { - json!(U32x2::from(unsafe { - symbol.cif.call::(symbol.ptr, &call_args) - } as u64)) - } - NativeType::F32 => { - json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) - } - NativeType::F64 => { - json!(unsafe { symbol.cif.call::(symbol.ptr, &call_args) }) - } - NativeType::Pointer | NativeType::Function(_) => { - json!(U32x2::from(unsafe { - symbol.cif.call::<*const u8>(symbol.ptr, &call_args) - } as u64)) - } + Ok(match result_type { + NativeType::Void => NativeValue { + void_value: unsafe { cif.call::<()>(fun_ptr, &call_args) }, + }, + NativeType::U8 => NativeValue { + u8_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::I8 => NativeValue { + i8_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::U16 => NativeValue { + u16_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::I16 => NativeValue { + i16_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::U32 => NativeValue { + u32_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::I32 => NativeValue { + i32_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::U64 => NativeValue { + u64_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::I64 => NativeValue { + i64_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::USize => NativeValue { + usize_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::ISize => NativeValue { + isize_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::F32 => NativeValue { + f32_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::F64 => NativeValue { + f64_value: unsafe { cif.call::(fun_ptr, &call_args) }, + }, + NativeType::Pointer | NativeType::Function {} => NativeValue { + pointer: unsafe { cif.call::<*const u8>(fun_ptr, &call_args) }, + }, }) } @@ -785,33 +814,85 @@ unsafe extern "C" fn deno_ffi_callback( FFI_TYPE_INT => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), FFI_TYPE_FLOAT => serde_v8::to_v8(&mut scope, *((*val) as *const f32)), FFI_TYPE_DOUBLE => serde_v8::to_v8(&mut scope, *((*val) as *const f64)), - // FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { - // let ptr = U32x2::from(*((*val) as *const u64)); - // serde_v8::to_v8(&mut scope, ptr) - // } FFI_TYPE_SINT8 => serde_v8::to_v8(&mut scope, *((*val) as *const i8)), FFI_TYPE_UINT8 => serde_v8::to_v8(&mut scope, *((*val) as *const u8)), FFI_TYPE_SINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const i16)), FFI_TYPE_UINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const u16)), FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), FFI_TYPE_UINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const u32)), - FFI_TYPE_SINT64 => serde_v8::to_v8(&mut scope, *((*val) as *const i64)), + FFI_TYPE_SINT64 => { + let result = *((*val) as *const i64); + Ok(v8::BigInt::new_from_i64(&mut scope, result).into()) + } FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 => { - serde_v8::to_v8(&mut scope, *((*val) as *const u64)) + let result = *((*val) as *const u64); + Ok(v8::BigInt::new_from_u64(&mut scope, result).into()) } FFI_TYPE_VOID => serde_v8::to_v8(&mut scope, ()), _ => { - panic!("Unsupported parameter type") + unreachable!() } }; params.push(value.expect("Unable to serialize callback parameter.")); } let recv = v8::undefined(&mut scope); - let value = match func.call(&mut scope, recv.into(), ¶ms) { - Some(value) => value, - None => panic!("OH SHIT"), - }; + let call_result = func.call(&mut scope, recv.into(), ¶ms); + if call_result.is_none() { + // JS function threw an exception. Set the return value to zero and return. + // TODO: Somehow get the exception back into JS + match (*cif.rtype).type_ as _ { + FFI_TYPE_INT | FFI_TYPE_SINT32 => { + *(result as *mut i32) = 0; + return; + } + FFI_TYPE_FLOAT => { + *(result as *mut f32) = 0.0; + return; + } + FFI_TYPE_DOUBLE => { + *(result as *mut f64) = 0.0; + return; + } + FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { + *(result as *mut u64) = 0; + return; + } + FFI_TYPE_SINT8 => { + *(result as *mut i8) = 0; + return; + } + FFI_TYPE_UINT8 => { + *(result as *mut u8) = 0; + return; + } + FFI_TYPE_SINT16 => { + *(result as *mut i16) = 0; + return; + } + FFI_TYPE_UINT16 => { + *(result as *mut u16) = 0; + return; + } + FFI_TYPE_UINT32 => { + *(result as *mut u32) = 0; + return; + } + FFI_TYPE_SINT64 => { + *(result as *mut i64) = 0; + return; + } + FFI_TYPE_UINT64 => { + *(result as *mut u64) = 0; + return; + } + FFI_TYPE_VOID => {} + _ => { + panic!("Unsupported callback return type") + } + }; + } + let value = call_result.unwrap(); std::mem::forget(callback); @@ -829,9 +910,8 @@ unsafe extern "C" fn deno_ffi_callback( .expect("Unable to deserialize result parameter."); } FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { - let u32x2: U32x2 = serde_v8::from_v8(&mut scope, value) + *(result as *mut u64) = serde_v8::from_v8(&mut scope, value) .expect("Unable to deserialize result parameter."); - *(result as *mut u64) = u64::from(u32x2); } FFI_TYPE_SINT8 => { *(result as *mut i8) = serde_v8::from_v8(&mut scope, value) @@ -915,12 +995,17 @@ fn op_ffi_register_callback( } #[op] -fn op_ffi_deregister_callback(state: &mut deno_core::OpState, rid: ResourceId) { - state - .resource_table - .take::(rid) - .unwrap() - .close(); +fn op_ffi_deregister_callback( + state: &mut deno_core::OpState, + rid: ResourceId, +) -> Result<(), AnyError> { + let resource = state.resource_table.take::(rid); + + if resource.is_err() { + return Err(bad_resource_id()); + } + resource.unwrap().close(); + Ok(()) } #[op(v8)] @@ -929,26 +1014,22 @@ fn op_ffi_call_registered_callback<'a>( state: Rc>, rid: ResourceId, args: serde_v8::Value<'a>, -) -> Result +) -> Result, AnyError> where 'a: 'a, { - let (fn_ptr, info) = { - let state = &mut state.borrow_mut(); + let (fn_ptr, info, call_args) = { + let state = &mut state.borrow(); let resource = state .resource_table .get::(rid)?; let info: &CallbackInfo = unsafe { &*resource.info }; let fn_ptr = *resource.closure.code_ptr() as *mut c_void; - (fn_ptr, info) + let call_args = + ffi_parse_args(scope, args, &info.parameters, &state.resource_table)?; + (fn_ptr, info, call_args) }; - let call_args = ffi_parse_args( - scope, - args, - &info.parameters, - &state.borrow().resource_table, - )?; let cif = libffi::middle::Cif::new( info .parameters @@ -957,66 +1038,114 @@ where .map(libffi::middle::Type::from), info.result.into(), ); - let symbol = Symbol { - cif, - ptr: libffi::middle::CodePtr::from_ptr(fn_ptr), - parameter_types: info.parameters.clone(), - result_type: info.result, - }; - ffi_call(call_args, &symbol) + let fun_ptr = libffi::middle::CodePtr::from_ptr(fn_ptr); + let result = + ffi_call(call_args, &cif, fun_ptr, &info.parameters, info.result)?; + Ok(result.to_v8(scope, info.result)) } #[op(v8)] -fn op_ffi_call_ptr( - scope: &mut v8::HandleScope, - state: &mut deno_core::OpState, - args: FfiCallPtrArgs, -) -> Result +fn op_ffi_call_ptr( + scope: &mut v8::HandleScope<'scope>, + state: Rc>, + pointer: u64, + def: ForeignFunction, + parameters: serde_v8::Value<'scope>, +) -> Result, AnyError> where FP: FfiPermissions + 'static, { - check_unstable(state, "Deno.UnsafeFnPointer#call"); + let (symbol, call_args) = { + let mut state = state.borrow_mut(); + check_unstable(state.borrow(), "Deno.UnsafeFnPointer#call"); - let permissions = state.borrow_mut::(); - permissions.check(None)?; + let permissions = state.borrow_mut::(); + permissions.check(None)?; - let symbol = args.get_symbol(); - let call_args = ffi_parse_args( - unsafe { std::mem::transmute(scope) }, - args.parameters, + let symbol = get_symbol_for_ptr(pointer, def); + let call_args = ffi_parse_args( + scope, + parameters, + &symbol.parameter_types, + &state.resource_table, + )?; + (symbol, call_args) + }; + let result = ffi_call( + call_args, + &symbol.cif, + symbol.ptr, &symbol.parameter_types, - &state.resource_table, + symbol.result_type, )?; - ffi_call(call_args, &symbol) + let result = result.to_v8(scope, symbol.result_type); + Ok(result) } #[op(v8)] fn op_ffi_call_ptr_nonblocking<'scope, FP>( scope: &mut v8::HandleScope<'scope>, state: Rc>, - args: FfiCallPtrArgs<'scope>, -) -> Result>, AnyError> + pointer: u64, + def: ForeignFunction, + parameters: serde_v8::Value<'scope>, +) -> Result< + impl Future, AnyError>>, + AnyError, +> where FP: FfiPermissions + 'static, { check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - { + let (symbol, call_args) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); permissions.check(None)?; - } - let symbol = args.get_symbol(); - let call_args = ffi_parse_args( - scope, - args.parameters, - &args.def.parameters, - &state.borrow_mut().resource_table, - )?; + let symbol = get_symbol_for_ptr(pointer, def); + let call_args = ffi_parse_args( + scope, + parameters, + &symbol.parameter_types, + &state.resource_table, + )?; + (symbol, call_args) + }; + + // Start the other blocking thread first... + let result_type = symbol.result_type; + let join_handle = tokio::task::spawn_blocking(move || { + let Symbol { + cif, + ptr, + parameter_types, + .. + } = symbol.clone(); + ffi_call(call_args, &cif, ptr, ¶meter_types, result_type) + }); + + // ...then prepare for handling its result. + let isolate_ptr: *mut v8::Isolate = { + let isolate: &mut v8::Isolate = &mut *scope; + isolate + }; + let isolate = unsafe { &mut *isolate_ptr }; + let context = + v8::Global::new(isolate, scope.get_current_context()).into_raw(); Ok(async move { - tokio::task::spawn_blocking(move || ffi_call(call_args, &symbol)).await? + let result = join_handle.await??; + let context = unsafe { + std::mem::transmute::, v8::Local>( + context, + ) + }; + let mut cb_scope = unsafe { v8::CallbackScope::new(context) }; + let mut scope = v8::HandleScope::new(&mut cb_scope); + let result = + result.to_v8(unsafe { std::mem::transmute(&mut scope) }, result_type); + Ok(result) }) } @@ -1028,11 +1157,12 @@ struct FfiGetArgs { r#type: NativeType, } -#[op] -fn op_ffi_get_static( +#[op(v8)] +fn op_ffi_get_static<'scope>( + scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, args: FfiGetArgs, -) -> Result { +) -> Result, AnyError> { let resource = state .resource_table .get::(args.rid)?; @@ -1041,110 +1171,182 @@ fn op_ffi_get_static( Ok(match args.r#type { NativeType::Void => { - unreachable!(); + return Err(type_error("Invalid FFI static type 'void'")); } NativeType::U8 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const u8) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const u8) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::I8 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const i8) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const i8) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::U16 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const u16) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const u16) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::I16 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const i16) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const i16) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::U32 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const u32) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const u32) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::I32 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const i32) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const i32) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::U64 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const u64) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const u64) }; + let big_int = v8::BigInt::new_from_u64(scope, result); + serde_v8::from_v8(scope, big_int.into())? } NativeType::I64 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const i64) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const i64) }; + let big_int = v8::BigInt::new_from_i64(scope, result); + serde_v8::from_v8(scope, big_int.into())? } NativeType::USize => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const usize) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const usize) }; + let big_int = v8::BigInt::new_from_u64(scope, result as u64); + serde_v8::from_v8(scope, big_int.into())? } NativeType::ISize => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const isize) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const isize) }; + let big_int = v8::BigInt::new_from_i64(scope, result as i64); + serde_v8::from_v8(scope, big_int.into())? } NativeType::F32 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const f32) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const f32) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } NativeType::F64 => { - json!(unsafe { ptr::read_unaligned(data_ptr as *const f64) }) + let result = unsafe { ptr::read_unaligned(data_ptr as *const f64) }; + let number = v8::Number::new(scope, result as f64); + serde_v8::from_v8(scope, number.into())? } - NativeType::Pointer | NativeType::Function(_) => { - json!(U32x2::from(data_ptr as *const u8 as u64)) + NativeType::Pointer | NativeType::Function {} => { + let result = data_ptr as *const u8 as u64; + let big_int = v8::BigInt::new_from_u64(scope, result); + serde_v8::from_v8(scope, big_int.into())? } }) } #[op(v8)] -fn op_ffi_call( - scope: &mut v8::HandleScope, +fn op_ffi_call<'scope>( + scope: &mut v8::HandleScope<'scope>, state: Rc>, - args: FfiCallArgs, -) -> Result { + rid: ResourceId, + symbol: String, + parameters: serde_v8::Value<'scope>, +) -> Result, AnyError> { let (symbol, call_args) = { - let state = &mut state.borrow_mut(); - let resource = state - .resource_table - .get::(args.rid)?; + let state = &mut state.borrow(); + let resource = state.resource_table.get::(rid)?; let symbol = resource .symbols - .get(&args.symbol) + .get(&symbol) .ok_or_else(bad_resource_id)? .clone(); let call_args = ffi_parse_args( - unsafe { std::mem::transmute(scope) }, - args.parameters, + scope, + parameters, &symbol.parameter_types, &state.resource_table, )?; (symbol, call_args) }; - ffi_call(call_args, &symbol) + let result = ffi_call( + call_args, + &symbol.cif, + symbol.ptr, + &symbol.parameter_types, + symbol.result_type, + )?; + let result = result.to_v8(scope, symbol.result_type); + Ok(result) } /// A non-blocking FFI call. #[op(v8)] fn op_ffi_call_nonblocking<'scope>( scope: &mut v8::HandleScope<'scope>, - state: &mut deno_core::OpState, - args: FfiCallArgs<'scope>, -) -> Result> + 'static, AnyError> { - let resource = state - .resource_table - .get::(args.rid)?; - let symbols = &resource.symbols; - let symbol = symbols.get(&args.symbol).ok_or_else(bad_resource_id)?; - let symbol = symbol.clone(); - let call_args = ffi_parse_args( - scope, - args.parameters, - &symbol.parameter_types, - &state.resource_table, - )?; + state: Rc>, + rid: ResourceId, + symbol: String, + parameters: serde_v8::Value<'scope>, +) -> Result< + impl Future, AnyError>> + 'static, + AnyError, +> { + let (symbol, call_args) = { + let state = state.borrow(); + let resource = state.resource_table.get::(rid)?; + let symbols = &resource.symbols; + let symbol = symbols.get(&symbol).ok_or_else(bad_resource_id)?.clone(); + let call_args = ffi_parse_args( + scope, + parameters, + &symbol.parameter_types, + &state.resource_table, + )?; + (symbol, call_args) + }; + + // Start the other blocking thread first... + let result_type = symbol.result_type; + let join_handle = tokio::task::spawn_blocking(move || { + let Symbol { + cif, + ptr, + parameter_types, + .. + } = symbol.clone(); + ffi_call(call_args, &cif, ptr, ¶meter_types, result_type) + }); - let join_handle = - tokio::task::spawn_blocking(move || ffi_call(call_args, &symbol)); - Ok(async move { join_handle.await? }) + // ...then prepare for handling its result. + let isolate_ptr: *mut v8::Isolate = { + let isolate: &mut v8::Isolate = &mut *scope; + isolate + }; + let isolate = unsafe { &mut *isolate_ptr }; + let context = + v8::Global::new(isolate, scope.get_current_context()).into_raw(); + + Ok(async move { + let result = join_handle.await??; + let context = unsafe { + std::mem::transmute::, v8::Local>( + context, + ) + }; + let mut cb_scope = unsafe { v8::CallbackScope::new(context) }; + let mut scope = v8::HandleScope::new(&mut cb_scope); + let result = + result.to_v8(unsafe { std::mem::transmute(&mut scope) }, result_type); + Ok(result) + }) } -#[op] -fn op_ffi_ptr_of( +#[op(v8)] +fn op_ffi_ptr_of( + scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, buf: ZeroCopyBuf, -) -> Result +) -> Result, AnyError> where FP: FfiPermissions + 'static, { @@ -1153,13 +1355,19 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(U32x2::from(buf.as_ptr() as u64)) + let big_int = v8::BigInt::new_from_u64(scope, buf.as_ptr() as u64); + //Ok(serde_v8::from_v8(scope, big_int.into())?) + Ok(serde_v8::Value { + v8_value: big_int.into(), + }) } #[op] fn op_ffi_buf_copy_into( state: &mut deno_core::OpState, - (src, mut dst, len): (U32x2, ZeroCopyBuf, usize), + src: u64, + mut dst: ZeroCopyBuf, + len: usize, ) -> Result<(), AnyError> where FP: FfiPermissions + 'static, @@ -1174,7 +1382,7 @@ where "Destination length is smaller than source length", )) } else { - let src = u64::from(src) as *const u8; + let src = src as *const u8; unsafe { ptr::copy(src, dst.as_mut_ptr(), len) }; Ok(()) } @@ -1183,7 +1391,7 @@ where #[op] fn op_ffi_cstr_read( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1193,14 +1401,14 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - let ptr = u64::from(ptr) as *const c_char; + let ptr = ptr as *const c_char; Ok(unsafe { CStr::from_ptr(ptr) }.to_str()?.to_string()) } #[op] fn op_ffi_read_u8( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1210,13 +1418,13 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u8) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const u8) }) } #[op] fn op_ffi_read_i8( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1226,13 +1434,13 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i8) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const i8) }) } #[op] fn op_ffi_read_u16( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1242,13 +1450,13 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u16) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const u16) }) } #[op] fn op_ffi_read_i16( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1258,13 +1466,13 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i16) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const i16) }) } #[op] fn op_ffi_read_u32( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1274,13 +1482,13 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const u32) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const u32) }) } #[op] fn op_ffi_read_i32( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1290,31 +1498,34 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const i32) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const i32) }) } -#[op] -fn op_ffi_read_u64( +#[op(v8)] +fn op_ffi_read_u64( + scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, - ptr: U32x2, -) -> Result + ptr: u64, +) -> Result, AnyError> where FP: FfiPermissions + 'static, + 'scope: 'scope, { check_unstable(state, "Deno.UnsafePointerView#getBigUint64"); let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(U32x2::from(unsafe { - ptr::read_unaligned(u64::from(ptr) as *const u64) - })) + let result = unsafe { ptr::read_unaligned(ptr as *const u64) }; + + let big_int = v8::BigInt::new_from_u64(scope, result); + Ok(serde_v8::from_v8(scope, big_int.into())?) } #[op] fn op_ffi_read_f32( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1324,13 +1535,13 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const f32) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const f32) }) } #[op] fn op_ffi_read_f64( state: &mut deno_core::OpState, - ptr: U32x2, + ptr: u64, ) -> Result where FP: FfiPermissions + 'static, @@ -1340,7 +1551,7 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - Ok(unsafe { ptr::read_unaligned(u64::from(ptr) as *const f64) }) + Ok(unsafe { ptr::read_unaligned(ptr as *const f64) }) } #[cfg(test)] From f4a8fe243e77624e6dddcca73f9bea6ddf24f1e3 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 11 Jun 2022 17:06:40 +0300 Subject: [PATCH 28/64] Fix --- ext/ffi/lib.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 50776b3d218782..9788907b73e372 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1057,7 +1057,7 @@ where { let (symbol, call_args) = { let mut state = state.borrow_mut(); - check_unstable(state.borrow(), "Deno.UnsafeFnPointer#call"); + //check_unstable(state.borrow(), "Deno.UnsafeFnPointer#call"); let permissions = state.borrow_mut::(); permissions.check(None)?; @@ -1149,27 +1149,19 @@ where }) } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct FfiGetArgs { - rid: ResourceId, - name: String, - r#type: NativeType, -} - #[op(v8)] fn op_ffi_get_static<'scope>( scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, - args: FfiGetArgs, + rid: ResourceId, + name: String, + static_type: NativeType, ) -> Result, AnyError> { - let resource = state - .resource_table - .get::(args.rid)?; + let resource = state.resource_table.get::(rid)?; - let data_ptr = resource.get_static(args.name)? as *const u8; + let data_ptr = resource.get_static(name)? as *const u8; - Ok(match args.r#type { + Ok(match static_type { NativeType::Void => { return Err(type_error("Invalid FFI static type 'void'")); } From f1eed6330f121fedea7f026f65d821bcde74af79 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 11 Jun 2022 19:25:14 +0300 Subject: [PATCH 29/64] Move parameter validity checks to JS side --- ext/ffi/00_ffi.js | 13 +- ext/ffi/lib.rs | 210 +++++++++++++--------------- test_ffi/src/lib.rs | 3 + test_ffi/tests/integration_tests.rs | 1 + test_ffi/tests/test.js | 8 +- 5 files changed, 119 insertions(+), 116 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index dfc8b5308e0a8c..c00c462ad6ea7d 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -148,7 +148,18 @@ const type = types[i]; const arg = args[i]; - if (type === "pointer") { + if (type === "u8" || type === "u16" || type === "u32") { + if (!Number.isInteger(arg) || arg < 0) { + throw new TypeError(`Expected FFI argument to be an unsigned integer, but got '${arg}'`) + } + parameters.push(arg); + } else if (type === "u64" || type === "usize") { + if (Number.isInteger(arg) && arg >= 0 || typeof arg === "bigint" && arg >= 0n) { + parameters.push(arg); + } else { + throw new TypeError(`Expected FFI argument to be an unsigned integer, but got '${arg}'`) + } + } else if (type === "pointer") { if ( ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, arg?.buffer) && arg.byteLength !== undefined diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 9788907b73e372..5ea1ff5c09aca6 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,7 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use core::ptr::NonNull; -use deno_core::error::bad_resource_id; use deno_core::error::generic_error; use deno_core::error::range_error; use deno_core::error::type_error; @@ -276,100 +275,76 @@ impl NativeValue { ) -> serde_v8::Value<'scope> { match native_type { NativeType::Void => { - let local_value = v8::undefined(scope); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = v8::undefined(scope).into(); + local_value.into() } NativeType::U8 => { - let local_value = - v8::Number::new(scope, unsafe { self.u8_value } as f64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::Number::new(scope, unsafe { self.u8_value } as f64).into(); + local_value.into() } NativeType::I8 => { - let local_value = - v8::Number::new(scope, unsafe { self.i8_value } as f64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::Number::new(scope, unsafe { self.i8_value } as f64).into(); + local_value.into() } NativeType::U16 => { - let local_value = - v8::Number::new(scope, unsafe { self.u16_value } as f64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::Number::new(scope, unsafe { self.u16_value } as f64).into(); + local_value.into() } NativeType::I16 => { - let local_value = - v8::Number::new(scope, unsafe { self.i16_value } as f64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::Number::new(scope, unsafe { self.i16_value } as f64).into(); + local_value.into() } NativeType::U32 => { - let local_value = - v8::Number::new(scope, unsafe { self.u32_value } as f64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::Number::new(scope, unsafe { self.u32_value } as f64).into(); + local_value.into() } NativeType::I32 => { - let local_value = - v8::Number::new(scope, unsafe { self.i32_value } as f64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::Number::new(scope, unsafe { self.i32_value } as f64).into(); + local_value.into() } NativeType::U64 => { - let local_value = - v8::BigInt::new_from_u64(scope, unsafe { self.u64_value }); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::BigInt::new_from_u64(scope, unsafe { self.u64_value }).into(); + local_value.into() } NativeType::I64 => { - let local_value = - v8::BigInt::new_from_i64(scope, unsafe { self.i64_value }); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::BigInt::new_from_i64(scope, unsafe { self.i64_value }).into(); + local_value.into() } NativeType::USize => { - let local_value = - v8::BigInt::new_from_u64(scope, unsafe { self.usize_value } as u64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::BigInt::new_from_u64(scope, unsafe { self.usize_value } as u64) + .into(); + local_value.into() } NativeType::ISize => { - let local_value = - v8::BigInt::new_from_i64(scope, unsafe { self.isize_value } as i64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::BigInt::new_from_i64(scope, unsafe { self.isize_value } as i64) + .into(); + local_value.into() } NativeType::F32 => { - let local_value = - v8::Number::new(scope, unsafe { self.f32_value } as f64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::Number::new(scope, unsafe { self.f32_value } as f64).into(); + local_value.into() } NativeType::F64 => { - let local_value = v8::Number::new(scope, unsafe { self.f64_value }); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::Number::new(scope, unsafe { self.f64_value }).into(); + local_value.into() } NativeType::Pointer | NativeType::Function {} => { - let local_value = - v8::BigInt::new_from_u64(scope, unsafe { self.pointer } as u64); - serde_v8::Value { - v8_value: local_value.into(), - } + let local_value: v8::Local = + v8::BigInt::new_from_u64(scope, unsafe { self.pointer } as u64) + .into(); + local_value.into() } } } @@ -564,72 +539,88 @@ where } NativeType::U8 => { let value = value - .uint32_value(scope) + .number_value(scope) .ok_or_else(|| type_error("Invalid FFI u8 type, expected number"))? as u8; ffi_args.push(NativeValue { u8_value: value }); } NativeType::I8 => { let value = value - .int32_value(scope) + .number_value(scope) .ok_or_else(|| type_error("Invalid FFI i8 type, expected number"))? as i8; ffi_args.push(NativeValue { i8_value: value }); } NativeType::U16 => { let value = value - .uint32_value(scope) + .number_value(scope) .ok_or_else(|| type_error("Invalid FFI u16 type, expected number"))? as u16; ffi_args.push(NativeValue { u16_value: value }); } NativeType::I16 => { let value = value - .int32_value(scope) + .number_value(scope) .ok_or_else(|| type_error("Invalid FFI i16 type, expected number"))? as i16; ffi_args.push(NativeValue { i16_value: value }); } NativeType::U32 => { let value = value - .uint32_value(scope) - .ok_or_else(|| type_error("Invalid FFI u32 type, expected number"))?; + .number_value(scope) + .ok_or_else(|| type_error("Invalid FFI u32 type, expected number"))? + as u32; ffi_args.push(NativeValue { u32_value: value }); } NativeType::I32 => { let value = value - .int32_value(scope) - .ok_or_else(|| type_error("Invalid FFI i32 type, expected number"))?; + .number_value(scope) + .ok_or_else(|| type_error("Invalid FFI i32 type, expected number"))? + as i32; ffi_args.push(NativeValue { i32_value: value }); } NativeType::U64 => { - // TODO: Handle BigInt - let value = value - .integer_value(scope) - .ok_or_else(|| type_error("Invalid FFI u64 type, expected number"))? - as u64; + let value: u64 = if value.is_big_int() { + let value = v8::Local::::try_from(value)?; + value.u64_value().0 + } else { + value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI u64 type, expected number") + })? as u64 + }; ffi_args.push(NativeValue { u64_value: value }); } NativeType::I64 => { - // TODO: Handle BigInt - let value = value - .integer_value(scope) - .ok_or_else(|| type_error("Invalid FFI i64 type, expected number"))? - as i64; + let value: i64 = if value.is_big_int() { + let value = v8::Local::::try_from(value)?; + value.i64_value().0 + } else { + value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI i64 type, expected number") + })? as i64 + }; ffi_args.push(NativeValue { i64_value: value }); } NativeType::USize => { - // TODO: Handle BigInt - let value = value.integer_value(scope).ok_or_else(|| { - type_error("Invalid FFI usize type, expected number") - })? as usize; + let value: usize = if value.is_big_int() { + let value = v8::Local::::try_from(value)?; + value.u64_value().0 as usize + } else { + value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI usize type, expected number") + })? as usize + }; ffi_args.push(NativeValue { usize_value: value }); } NativeType::ISize => { - // TODO: Handle BigInt - let value = value.integer_value(scope).ok_or_else(|| { - type_error("Invalid FFI isize type, expected number") - })? as isize; + let value: isize = if value.is_big_int() { + let value = v8::Local::::try_from(value)?; + value.i64_value().0 as isize + } else { + value.integer_value(scope).ok_or_else(|| { + type_error("Invalid FFI isize type, expected number") + })? as isize + }; ffi_args.push(NativeValue { isize_value: value }); } NativeType::F32 => { @@ -641,7 +632,7 @@ where } NativeType::F64 => { let value = value - .integer_value(scope) + .number_value(scope) .ok_or_else(|| type_error("Invalid FFI f64 type, expected number"))? as f64; ffi_args.push(NativeValue { f64_value: value }); @@ -669,7 +660,7 @@ where let value: *const u8 = ptr::null(); ffi_args.push(NativeValue { pointer: value }) } else if value.is_number() { - let value: ResourceId = value.uint32_value(scope).unwrap(); + let value: ResourceId = value.number_value(scope).unwrap() as u32; let rc = resource_table.get::(value)?; let function = *rc.closure.code_ptr(); ffi_args.push(NativeValue { @@ -999,13 +990,7 @@ fn op_ffi_deregister_callback( state: &mut deno_core::OpState, rid: ResourceId, ) -> Result<(), AnyError> { - let resource = state.resource_table.take::(rid); - - if resource.is_err() { - return Err(bad_resource_id()); - } - resource.unwrap().close(); - Ok(()) + Ok(state.resource_table.take::(rid)?.close()) } #[op(v8)] @@ -1248,7 +1233,7 @@ fn op_ffi_call<'scope>( let symbol = resource .symbols .get(&symbol) - .ok_or_else(bad_resource_id)? + .ok_or_else(|| type_error("Invalid FFI symbol name"))? .clone(); let call_args = ffi_parse_args( @@ -1287,7 +1272,7 @@ fn op_ffi_call_nonblocking<'scope>( let state = state.borrow(); let resource = state.resource_table.get::(rid)?; let symbols = &resource.symbols; - let symbol = symbols.get(&symbol).ok_or_else(bad_resource_id)?.clone(); + let symbol = symbols.get(&symbol).ok_or_else(|| type_error("Invalid FFI symbol name"))?.clone(); let call_args = ffi_parse_args( scope, parameters, @@ -1347,11 +1332,9 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - let big_int = v8::BigInt::new_from_u64(scope, buf.as_ptr() as u64); - //Ok(serde_v8::from_v8(scope, big_int.into())?) - Ok(serde_v8::Value { - v8_value: big_int.into(), - }) + let big_int: v8::Local = + v8::BigInt::new_from_u64(scope, buf.as_ptr() as u64).into(); + Ok(big_int.into()) } #[op] @@ -1510,8 +1493,9 @@ where let result = unsafe { ptr::read_unaligned(ptr as *const u64) }; - let big_int = v8::BigInt::new_from_u64(scope, result); - Ok(serde_v8::from_v8(scope, big_int.into())?) + let big_int: v8::Local = + v8::BigInt::new_from_u64(scope, result).into(); + Ok(big_int.into()) } #[op] diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs index 9a06e29e74ed30..bbdc0ae166e02c 100644 --- a/test_ffi/src/lib.rs +++ b/test_ffi/src/lib.rs @@ -116,6 +116,9 @@ pub extern "C" fn get_sleep_blocking_ptr() -> *const c_void { #[no_mangle] pub static static_u32: u32 = 42; +#[no_mangle] +pub static static_i64: i64 = -1242464576485; + #[repr(C)] pub struct Structure { _data: u32, diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs index 5b9bb2fc292c54..8d796c2bb01f52 100644 --- a/test_ffi/tests/integration_tests.rs +++ b/test_ffi/tests/integration_tests.rs @@ -70,6 +70,7 @@ fn basic() { Before\n\ true\n\ Static u32: 42\n\ + Static i64: -1242464576485n\n\ Static ptr: true\n\ Static ptr value: 42\n\ After\n\ diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index b89dca64811592..2c9b0d77e49e99 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -76,6 +76,9 @@ const dylib = Deno.dlopen(libPath, { "static_u32": { type: "u32", }, + "static_i64": { + type: "i64", + }, "static_ptr": { type: "pointer", }, @@ -135,14 +138,14 @@ assertThrows( dylib.symbols.add_u32(-1, 100); }, TypeError, - "Expected FFI argument to be an unsigned integer, but got Number(-1)", + "Expected FFI argument to be an unsigned integer, but got '-1'", ); assertThrows( () => { dylib.symbols.add_u32(null, 100); }, TypeError, - "Expected FFI argument to be an unsigned integer, but got Null", + "Expected FFI argument to be an unsigned integer, but got 'null'", ); console.log(dylib.symbols.add_i32(123, 456)); console.log(dylib.symbols.add_u64(0xffffffffn, 0xffffffffn)); @@ -208,6 +211,7 @@ console.log("Before"); console.log(performance.now() - start < 100); console.log("Static u32:", dylib.symbols.static_u32); +console.log("Static i64:", dylib.symbols.static_i64); console.log( "Static ptr:", dylib.symbols.static_ptr instanceof Deno.UnsafePointer, From 7478eadefa90dfc73466991cea57f5600a0314b7 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 11 Jun 2022 21:27:48 +0300 Subject: [PATCH 30/64] Fix nonblocking calls by reverting them to json::Value and U32x2 returning --- ext/ffi/00_ffi.js | 79 ++++++++++++------ ext/ffi/lib.rs | 123 +++++++++++++++++----------- test_ffi/tests/integration_tests.rs | 7 ++ test_ffi/tests/test.js | 17 ++++ 4 files changed, 149 insertions(+), 77 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index c00c462ad6ea7d..c0efc1f292b5fb 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -15,6 +15,15 @@ Uint8Array, } = window.__bootstrap.primordials; + function unpackU64([hi, lo]) { + return BigInt(hi) << 32n | BigInt(lo); + } + + function unpackI64([hi, lo]) { + const u64 = unpackU64([hi, lo]); + return u64 >> 63n ? u64 - 0x10000000000000000n : u64; + } + class UnsafePointerView { pointer; @@ -153,12 +162,26 @@ throw new TypeError(`Expected FFI argument to be an unsigned integer, but got '${arg}'`) } parameters.push(arg); + } else if (type === "i8" || type === "i16" || type === "i32") { + if (!Number.isInteger(arg)) { + throw new TypeError(`Expected FFI argument to be a signed integer, but got '${arg}'`) + } + parameters.push(arg); } else if (type === "u64" || type === "usize") { - if (Number.isInteger(arg) && arg >= 0 || typeof arg === "bigint" && arg >= 0n) { - parameters.push(arg); - } else { + if (!(Number.isInteger(arg) && arg >= 0 || typeof arg === "bigint" && 0n <= arg && arg <= 0xffffffffffffffffn)) { throw new TypeError(`Expected FFI argument to be an unsigned integer, but got '${arg}'`) } + parameters.push(arg); + } else if (type == "i64" || type === "isize") { + if (!(Number.isInteger(arg) || typeof arg === "bigint" && -1n * 2n ** 63n <= arg && arg <= 2n ** 63n - 1n)) { + throw new TypeError(`Expected FFI argument to be a signed integer, but got '${arg}'`) + } + parameters.push(arg); + } else if (type === "f32" || type === "f64") { + if (!Number.isFinite(arg)) { + throw new TypeError(`Expected FFI argument to be a number, but got '${arg}'`) + } + parameters.push(arg); } else if (type === "pointer") { if ( ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, arg?.buffer) && @@ -171,39 +194,36 @@ parameters.push(null); } else { throw new TypeError( - "Invalid ffi arg value, expected TypedArray, UnsafePointer or null", + "Expected FFI argument to be TypedArray, UnsafePointer or null", ); } - } else if (typeof arg === "bigint") { - if (arg > 0xffffffffffffffffn) { - throw new TypeError( - "Invalid ffi arg value, it needs to be less than 0xffffffffffffffff", - ); - } - - parameters.push(arg); } else if ( typeof type === "object" && type !== null && "function" in type ) { - if (ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { - parameters.push(arg[_rid]); - } else { + if (!ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { throw new TypeError( - "Invalid ffi arg value, expected RegisteredCallback", + "Expected FFI argument to be RegisteredCallback", ); } + parameters.push(arg[_rid]); } else { - parameters.push(arg); + throw new TypeError(`Invalid FFI argument type '${type}'`); } } - return { parameters }; + return parameters; } function unpackResult(type, result) { switch (type) { + case "isize": + case "i64": + return unpackI64(result); + case "usize": + case "u64": + return unpackU64(result); case "pointer": - return new UnsafePointer(result); + return new UnsafePointer(unpackU64(result)); default: return result; } @@ -219,10 +239,11 @@ } call(...args) { - const { parameters } = prepareArgs( + const parameters = prepareArgs( this.definition.parameters, args, ); + const resultType = this.definition.result; if (this.definition.nonblocking) { const promise = core.opAsync( "op_ffi_call_ptr_nonblocking", @@ -231,8 +252,8 @@ parameters, ); - if (this.definition.result === "pointer") { - return promise.then((value) => new UnsafePointer(value)); + if (resultType === "pointer" || resultType === "u64" || resultType === "i64" || resultType === "usize" || resultType === "isize") { + return promise.then((result) => unpackResult(resultType, result)); } return promise; @@ -244,7 +265,7 @@ parameters, ); - if (this.definition.result === "pointer") { + if (resultType === "pointer") { return new UnsafePointer(result); } @@ -276,7 +297,7 @@ } call(...args) { - const { parameters } = prepareArgs( + const parameters = prepareArgs( this.definition.parameters, args, ); @@ -355,7 +376,7 @@ const resultType = symbols[symbol].result; const fn = (...args) => { - const { parameters } = prepareArgs(types, args); + const parameters = prepareArgs(types, args); if (isNonBlocking) { const promise = core.opAsync( @@ -365,7 +386,7 @@ parameters, ); - if (resultType === "pointer") { + if (resultType === "pointer" || resultType === "u64" || resultType === "i64" || resultType === "usize" || resultType === "isize") { return promise.then((result) => unpackResult(resultType, result)); } @@ -378,7 +399,11 @@ parameters, ); - return unpackResult(resultType, result); + if (resultType === "pointer") { + return new UnsafePointer(result); + } + + return result; } }; diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 5ea1ff5c09aca6..77500b29c7d8ff 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -9,6 +9,8 @@ use deno_core::futures::Future; use deno_core::include_js_files; use deno_core::op; +use deno_core::serde_json::json; +use deno_core::serde_json::Value; //use deno_core::serde_json::json; //use deno_core::serde_json::Value; use deno_core::serde_v8; @@ -23,6 +25,7 @@ use libffi::middle::Arg; use libffi::middle::Cif; use libffi::raw::*; use serde::Deserialize; +use serde::Serialize; //use serde::Serialize; use std::borrow::Cow; use std::cell::RefCell; @@ -268,6 +271,53 @@ impl NativeValue { } } + fn to_value(self, native_type: NativeType) -> Value { + match native_type { + NativeType::Void => { + json!(()) + } + NativeType::U8 => { + json!(unsafe { self.u8_value }) + } + NativeType::I8 => { + json!(unsafe { self.i8_value }) + } + NativeType::U16 => { + json!(unsafe { self.u16_value }) + } + NativeType::I16 => { + json!(unsafe { self.i16_value }) + } + NativeType::U32 => { + json!(unsafe { self.u32_value }) + } + NativeType::I32 => { + json!(unsafe { self.i32_value }) + } + NativeType::U64 => { + json!(U32x2::from(unsafe { self.u64_value })) + } + NativeType::I64 => { + json!(U32x2::from(unsafe { self.i64_value } as u64)) + } + NativeType::USize => { + json!(U32x2::from(unsafe { self.usize_value } as u64)) + } + NativeType::ISize => { + json!(U32x2::from(unsafe { self.isize_value } as u64)) + } + NativeType::F32 => { + json!(unsafe { self.f32_value }) + } + NativeType::F64 => { + json!(unsafe { self.f64_value }) + } + NativeType::Pointer | NativeType::Function {} => { + json!(U32x2::from(unsafe { self.pointer } as u64)) + } + } + } + fn to_v8<'scope>( &self, scope: &mut v8::HandleScope<'scope>, @@ -352,6 +402,15 @@ impl NativeValue { unsafe impl Send for NativeValue {} +#[derive(Serialize, Debug, Clone, Copy)] +struct U32x2(u32, u32); + +impl From for U32x2 { + fn from(value: u64) -> Self { + Self((value >> 32) as u32, value as u32) + } +} + #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct ForeignFunction { @@ -990,7 +1049,12 @@ fn op_ffi_deregister_callback( state: &mut deno_core::OpState, rid: ResourceId, ) -> Result<(), AnyError> { - Ok(state.resource_table.take::(rid)?.close()) + Ok( + state + .resource_table + .take::(rid)? + .close(), + ) } #[op(v8)] @@ -1074,10 +1138,7 @@ fn op_ffi_call_ptr_nonblocking<'scope, FP>( pointer: u64, def: ForeignFunction, parameters: serde_v8::Value<'scope>, -) -> Result< - impl Future, AnyError>>, - AnyError, -> +) -> Result>, AnyError> where FP: FfiPermissions + 'static, { @@ -1098,7 +1159,6 @@ where (symbol, call_args) }; - // Start the other blocking thread first... let result_type = symbol.result_type; let join_handle = tokio::task::spawn_blocking(move || { let Symbol { @@ -1110,27 +1170,9 @@ where ffi_call(call_args, &cif, ptr, ¶meter_types, result_type) }); - // ...then prepare for handling its result. - let isolate_ptr: *mut v8::Isolate = { - let isolate: &mut v8::Isolate = &mut *scope; - isolate - }; - let isolate = unsafe { &mut *isolate_ptr }; - let context = - v8::Global::new(isolate, scope.get_current_context()).into_raw(); - Ok(async move { let result = join_handle.await??; - let context = unsafe { - std::mem::transmute::, v8::Local>( - context, - ) - }; - let mut cb_scope = unsafe { v8::CallbackScope::new(context) }; - let mut scope = v8::HandleScope::new(&mut cb_scope); - let result = - result.to_v8(unsafe { std::mem::transmute(&mut scope) }, result_type); - Ok(result) + Ok(result.to_value(result_type)) }) } @@ -1264,15 +1306,15 @@ fn op_ffi_call_nonblocking<'scope>( rid: ResourceId, symbol: String, parameters: serde_v8::Value<'scope>, -) -> Result< - impl Future, AnyError>> + 'static, - AnyError, -> { +) -> Result> + 'static, AnyError> { let (symbol, call_args) = { let state = state.borrow(); let resource = state.resource_table.get::(rid)?; let symbols = &resource.symbols; - let symbol = symbols.get(&symbol).ok_or_else(|| type_error("Invalid FFI symbol name"))?.clone(); + let symbol = symbols + .get(&symbol) + .ok_or_else(|| type_error("Invalid FFI symbol name"))? + .clone(); let call_args = ffi_parse_args( scope, parameters, @@ -1282,7 +1324,6 @@ fn op_ffi_call_nonblocking<'scope>( (symbol, call_args) }; - // Start the other blocking thread first... let result_type = symbol.result_type; let join_handle = tokio::task::spawn_blocking(move || { let Symbol { @@ -1294,27 +1335,9 @@ fn op_ffi_call_nonblocking<'scope>( ffi_call(call_args, &cif, ptr, ¶meter_types, result_type) }); - // ...then prepare for handling its result. - let isolate_ptr: *mut v8::Isolate = { - let isolate: &mut v8::Isolate = &mut *scope; - isolate - }; - let isolate = unsafe { &mut *isolate_ptr }; - let context = - v8::Global::new(isolate, scope.get_current_context()).into_raw(); - Ok(async move { let result = join_handle.await??; - let context = unsafe { - std::mem::transmute::, v8::Local>( - context, - ) - }; - let mut cb_scope = unsafe { v8::CallbackScope::new(context) }; - let mut scope = v8::HandleScope::new(&mut cb_scope); - let result = - result.to_v8(unsafe { std::mem::transmute(&mut scope) }, result_type); - Ok(result) + Ok(result.to_value(result_type)) }) } diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs index 8d796c2bb01f52..4c3efada7ee47d 100644 --- a/test_ffi/tests/integration_tests.rs +++ b/test_ffi/tests/integration_tests.rs @@ -65,6 +65,13 @@ fn basic() { -8589934590n\n\ 579.9119873046875\n\ 579.912\n\ + 579\n\ + 8589934590n\n\ + -8589934590n\n\ + 8589934590n\n\ + -8589934590n\n\ + 579.9119873046875\n\ + 579.912\n\ After sleep_blocking\n\ true\n\ Before\n\ diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 2c9b0d77e49e99..3f0dc3043f5701 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -52,6 +52,14 @@ 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" }, + "add_u32_nonblocking": { name: "add_u32", parameters: ["u32", "u32"], result: "u32", nonblocking: true }, + "add_i32_nonblocking": { name: "add_i32", parameters: ["i32", "i32"], result: "i32", nonblocking: true }, + "add_u64_nonblocking": { name: "add_u64", parameters: ["u64", "u64"], result: "u64", nonblocking: true }, + "add_i64_nonblocking": { name: "add_i64", parameters: ["i64", "i64"], result: "i64", nonblocking: true }, + "add_usize_nonblocking": { name: "add_usize", parameters: ["usize", "usize"], result: "usize", nonblocking: true }, + "add_isize_nonblocking": { name: "add_isize", parameters: ["isize", "isize"], result: "isize", nonblocking: true }, + "add_f32_nonblocking": { name: "add_f32", parameters: ["f32", "f32"], result: "f32", nonblocking: true }, + "add_f64_nonblocking": { name: "add_f64", parameters: ["f64", "f64"], result: "f64", nonblocking: true }, "fill_buffer": { parameters: ["u8", "pointer", "usize"], result: "void" }, "sleep_nonblocking": { name: "sleep_blocking", @@ -155,6 +163,15 @@ console.log(dylib.symbols.add_isize(-0xffffffffn, -0xffffffffn)); console.log(dylib.symbols.add_f32(123.123, 456.789)); console.log(dylib.symbols.add_f64(123.123, 456.789)); +// Test adders as nonblocking calls +console.log(await dylib.symbols.add_i32_nonblocking(123, 456)); +console.log(await dylib.symbols.add_u64_nonblocking(0xffffffffn, 0xffffffffn)); +console.log(await dylib.symbols.add_i64_nonblocking(-0xffffffffn, -0xffffffffn)); +console.log(await dylib.symbols.add_usize_nonblocking(0xffffffffn, 0xffffffffn)); +console.log(await dylib.symbols.add_isize_nonblocking(-0xffffffffn, -0xffffffffn)); +console.log(await dylib.symbols.add_f32_nonblocking(123.123, 456.789)); +console.log(await dylib.symbols.add_f64_nonblocking(123.123, 456.789)); + // test mutating sync calls function test_fill_buffer(fillValue, arr) { From c98241b2b107194d82c0dff6a883cae25932b63b Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 11 Jun 2022 23:05:56 +0300 Subject: [PATCH 31/64] Add extensive FFI baseline benchmarks --- test_ffi/src/lib.rs | 137 +++++++++++ test_ffi/tests/bench.js | 504 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 641 insertions(+) create mode 100644 test_ffi/tests/bench.js diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs index bbdc0ae166e02c..75f5be574555f7 100644 --- a/test_ffi/src/lib.rs +++ b/test_ffi/src/lib.rs @@ -113,6 +113,143 @@ pub extern "C" fn get_sleep_blocking_ptr() -> *const c_void { sleep_blocking as *const c_void } +// FFI performance helper functions +#[no_mangle] +pub extern "C" fn nop() {} + +#[no_mangle] +pub extern "C" fn nop_u8(_a: u8) {} + +#[no_mangle] +pub extern "C" fn nop_i8(_a: i8) {} + +#[no_mangle] +pub extern "C" fn nop_u16(_a: u16) {} + +#[no_mangle] +pub extern "C" fn nop_i16(_a: i16) {} + +#[no_mangle] +pub extern "C" fn nop_u32(_a: u32) {} + +#[no_mangle] +pub extern "C" fn nop_i32(_a: i32) {} + +#[no_mangle] +pub extern "C" fn nop_u64(_a: u64) {} + +#[no_mangle] +pub extern "C" fn nop_i64(_a: i64) {} + +#[no_mangle] +pub extern "C" fn nop_usize(_a: usize) {} + +#[no_mangle] +pub extern "C" fn nop_isize(_a: isize) {} + +#[no_mangle] +pub extern "C" fn nop_f32(_a: f32) {} + +#[no_mangle] +pub extern "C" fn nop_f64(_a: f64) {} + +#[no_mangle] +pub extern "C" fn nop_buffer(_buffer: *mut [u8; 8]) {} + +#[no_mangle] +pub extern "C" fn return_u8() -> u8 { + 255 +} + +#[no_mangle] +pub extern "C" fn return_i8() -> i8 { + -128 +} + +#[no_mangle] +pub extern "C" fn return_u16() -> u16 { + 65535 +} + +#[no_mangle] +pub extern "C" fn return_i16() -> i16 { + -32768 +} + +#[no_mangle] +pub extern "C" fn return_u32() -> u32 { + 4294967295 +} + +#[no_mangle] +pub extern "C" fn return_i32() -> i32 { + -2147483648 +} + +#[no_mangle] +pub extern "C" fn return_u64() -> u64 { + 18446744073709551615 +} + +#[no_mangle] +pub extern "C" fn return_i64() -> i64 { + -9223372036854775808 +} + +#[no_mangle] +pub extern "C" fn return_usize() -> usize { + 18446744073709551615 +} + +#[no_mangle] +pub extern "C" fn return_isize() -> isize { + -9223372036854775808 +} + +#[no_mangle] +pub extern "C" fn return_f32() -> f32 { + 0.20000000298023223876953125 +} + +#[no_mangle] +pub extern "C" fn return_f64() -> f64 { + 1e-10 +} + +// Parameters iteration + +#[no_mangle] +pub extern "C" fn nop_many_parameters( + _: u8, + _: i8, + _: u16, + _: i16, + _: u32, + _: i32, + _: u64, + _: i64, + _: usize, + _: isize, + _: f32, + _: f64, + _: *mut [u8; 8], + _: u8, + _: i8, + _: u16, + _: i16, + _: u32, + _: i32, + _: u64, + _: i64, + _: usize, + _: isize, + _: f32, + _: f64, + _: *mut [u8; 8], +) { +} + +// Statics #[no_mangle] pub static static_u32: u32 = 42; diff --git a/test_ffi/tests/bench.js b/test_ffi/tests/bench.js new file mode 100644 index 00000000000000..f94991c5b96b4a --- /dev/null +++ b/test_ffi/tests/bench.js @@ -0,0 +1,504 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file + +//const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); +const targetDir = "/home/tiffany/dev/rust/deno/target/debug"; +const [libPrefix, libSuffix] = { + darwin: ["lib", "dylib"], + linux: ["lib", "so"], + windows: ["", "dll"], +}[Deno.build.os]; +const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`; + +const dylib = Deno.dlopen(libPath, { + "nop": { parameters: [], result: "void" }, + "nop_u8": { parameters: ["u8"], result: "void" }, + "nop_i8": { parameters: ["i8"], result: "void" }, + "nop_u16": { parameters: ["u16"], result: "void" }, + "nop_i16": { parameters: ["i16"], result: "void" }, + "nop_u32": { parameters: ["u32"], result: "void" }, + "nop_i32": { parameters: ["i32"], result: "void" }, + "nop_u64": { parameters: ["u64"], result: "void" }, + "nop_i64": { parameters: ["i64"], result: "void" }, + "nop_usize": { parameters: ["usize"], result: "void" }, + "nop_isize": { parameters: ["isize"], result: "void" }, + "nop_f32": { parameters: ["f32"], result: "void" }, + "nop_f64": { parameters: ["f64"], result: "void" }, + "nop_buffer": { parameters: ["pointer"], result: "void" }, + "return_u8": { parameters: [], result: "u8" }, + "return_i8": { parameters: [], result: "i8" }, + "return_u16": { parameters: [], result: "u16" }, + "return_i16": { parameters: [], result: "i16" }, + "return_u32": { parameters: [], result: "u32" }, + "return_i32": { parameters: [], result: "i32" }, + "return_u64": { parameters: [], result: "u64" }, + "return_i64": { parameters: [], result: "i64" }, + "return_usize": { parameters: [], result: "usize" }, + "return_isize": { parameters: [], result: "isize" }, + "return_f32": { parameters: [], result: "f32" }, + "return_f64": { parameters: [], result: "f64" }, + "return_buffer": { parameters: [], result: "pointer" }, + // Nonblocking calls + "nop_nonblocking": { name: "nop", parameters: [], result: "void" }, + "nop_u8_nonblocking": { name: "nop_u8", parameters: ["u8"], result: "void" }, + "nop_i8_nonblocking": { name: "nop_i8", parameters: ["i8"], result: "void" }, + "nop_u16_nonblocking": { + name: "nop_u16", + parameters: ["u16"], + result: "void", + }, + "nop_i16_nonblocking": { + name: "nop_i16", + parameters: ["i16"], + result: "void", + }, + "nop_u32_nonblocking": { + name: "nop_u32", + parameters: ["u32"], + result: "void", + }, + "nop_i32_nonblocking": { + name: "nop_i32", + parameters: ["i32"], + result: "void", + }, + "nop_u64_nonblocking": { + name: "nop_u64", + parameters: ["u64"], + result: "void", + }, + "nop_i64_nonblocking": { + name: "nop_i64", + parameters: ["i64"], + result: "void", + }, + "nop_usize_nonblocking": { + name: "nop_usize", + parameters: ["usize"], + result: "void", + }, + "nop_isize_nonblocking": { + name: "nop_isize", + parameters: ["isize"], + result: "void", + }, + "nop_f32_nonblocking": { + name: "nop_f32", + parameters: ["f32"], + result: "void", + }, + "nop_f64_nonblocking": { + name: "nop_f64", + parameters: ["f64"], + result: "void", + }, + "nop_buffer_nonblocking": { + name: "nop_buffer", + parameters: ["pointer"], + result: "void", + }, + "return_u8_nonblocking": { name: "return_u8", parameters: [], result: "u8" }, + "return_i8_nonblocking": { name: "return_i8", parameters: [], result: "i8" }, + "return_u16_nonblocking": { + name: "return_u16", + parameters: [], + result: "u16", + }, + "return_i16_nonblocking": { + name: "return_i16", + parameters: [], + result: "i16", + }, + "return_u32_nonblocking": { + name: "return_u32", + parameters: [], + result: "u32", + }, + "return_i32_nonblocking": { + name: "return_i32", + parameters: [], + result: "i32", + }, + "return_u64_nonblocking": { + name: "return_u64", + parameters: [], + result: "u64", + }, + "return_i64_nonblocking": { + name: "return_i64", + parameters: [], + result: "i64", + }, + "return_usize_nonblocking": { + name: "return_usize", + parameters: [], + result: "usize", + }, + "return_isize_nonblocking": { + name: "return_isize", + parameters: [], + result: "isize", + }, + "return_f32_nonblocking": { + name: "return_f32", + parameters: [], + result: "f32", + }, + "return_f64_nonblocking": { + name: "return_f64", + parameters: [], + result: "f64", + }, + "return_buffer_nonblocking": { + name: "return_buffer", + parameters: [], + result: "pointer", + }, + // Parameter checking + "nop_many_parameters": { + parameters: [ + "u8", + "i8", + "u16", + "i16", + "u32", + "i32", + "u64", + "i64", + "usize", + "isize", + "f32", + "f64", + "pointer", + "u8", + "i8", + "u16", + "i16", + "u32", + "i32", + "u64", + "i64", + "usize", + "isize", + "f32", + "f64", + "pointer", + ], + result: "void", + }, + "nop_many_parameters_nonblocking": { + name: "nop_many_parameters", + parameters: [ + "u8", + "i8", + "u16", + "i16", + "u32", + "i32", + "u64", + "i64", + "usize", + "isize", + "f32", + "f64", + "pointer", + "u8", + "i8", + "u16", + "i16", + "u32", + "i32", + "u64", + "i64", + "usize", + "isize", + "f32", + "f64", + "pointer", + ], + result: "void", + nonblocking: true, + }, +}); + +Deno.bench("nop()", () => { + dylib.symbols.nop(); +}); + +Deno.bench("nop_u8()", () => { + dylib.symbols.nop_u8(100); +}); + +Deno.bench("nop_i8()", () => { + dylib.symbols.nop_i8(100); +}); + +Deno.bench("nop_u16()", () => { + dylib.symbols.nop_u16(100); +}); + +Deno.bench("nop_i16()", () => { + dylib.symbols.nop_i16(100); +}); + +Deno.bench("nop_u32()", () => { + dylib.symbols.nop_u32(100); +}); + +Deno.bench("nop_i32()", () => { + dylib.symbols.nop_i32(100); +}); + +Deno.bench("nop_u64()", () => { + dylib.symbols.nop_u64(100); +}); + +Deno.bench("nop_i64()", () => { + dylib.symbols.nop_i64(100); +}); + +Deno.bench("nop_usize()", () => { + dylib.symbols.nop_usize(100); +}); + +Deno.bench("nop_isize()", () => { + dylib.symbols.nop_isize(100); +}); + +Deno.bench("nop_f32()", () => { + dylib.symbols.nop_f32(100); +}); + +Deno.bench("nop_f64()", () => { + dylib.symbols.nop_f64(100); +}); + +const buffer = new Uint8Array(8).fill(5); +Deno.bench("nop_buffer()", () => { + dylib.symbols.nop_buffer(buffer); +}); + +Deno.bench("return_u8()", () => { + dylib.symbols.return_u8(); +}); + +Deno.bench("return_i8()", () => { + dylib.symbols.return_i8(); +}); + +Deno.bench("return_u16()", () => { + dylib.symbols.return_u16(); +}); + +Deno.bench("return_i16()", () => { + dylib.symbols.return_i16(); +}); + +Deno.bench("return_u32()", () => { + dylib.symbols.return_u32(); +}); + +Deno.bench("return_i32()", () => { + dylib.symbols.return_i32(); +}); + +Deno.bench("return_u64()", () => { + dylib.symbols.return_u64(); +}); + +Deno.bench("return_i64()", () => { + dylib.symbols.return_i64(); +}); + +Deno.bench("return_usize()", () => { + dylib.symbols.return_usize(); +}); + +Deno.bench("return_isize()", () => { + dylib.symbols.return_isize(); +}); + +Deno.bench("return_f32()", () => { + dylib.symbols.return_f32(); +}); + +Deno.bench("return_f64()", () => { + dylib.symbols.return_f64(); +}); + +Deno.bench("return_buffer()", () => { + dylib.symbols.return_buffer(); +}); + +// Nonblocking calls + +Deno.bench("nop_nonblocking()", async () => { + await dylib.symbols.nop_nonblocking(); +}); + +Deno.bench("nop_u8_nonblocking()", async () => { + await dylib.symbols.nop_u8_nonblocking(100); +}); + +Deno.bench("nop_i8_nonblocking()", async () => { + await dylib.symbols.nop_i8_nonblocking(100); +}); + +Deno.bench("nop_u16_nonblocking()", async () => { + await dylib.symbols.nop_u16_nonblocking(100); +}); + +Deno.bench("nop_i16_nonblocking()", async () => { + await dylib.symbols.nop_i16_nonblocking(100); +}); + +Deno.bench("nop_u32_nonblocking()", async () => { + await dylib.symbols.nop_u32_nonblocking(100); +}); + +Deno.bench("nop_i32_nonblocking()", async () => { + await dylib.symbols.nop_i32_nonblocking(100); +}); + +Deno.bench("nop_u64_nonblocking()", async () => { + await dylib.symbols.nop_u64_nonblocking(100); +}); + +Deno.bench("nop_i64_nonblocking()", async () => { + await dylib.symbols.nop_i64_nonblocking(100); +}); + +Deno.bench("nop_usize_nonblocking()", async () => { + await dylib.symbols.nop_usize_nonblocking(100); +}); + +Deno.bench("nop_isize_nonblocking()", async () => { + await dylib.symbols.nop_isize_nonblocking(100); +}); + +Deno.bench("nop_f32_nonblocking()", async () => { + await dylib.symbols.nop_f32_nonblocking(100); +}); + +Deno.bench("nop_f64_nonblocking()", async () => { + await dylib.symbols.nop_f64_nonblocking(100); +}); + +Deno.bench("nop_buffer_nonblocking()", async () => { + await dylib.symbols.nop_buffer_nonblocking(buffer); +}); + +Deno.bench("return_u8_nonblocking()", async () => { + await dylib.symbols.return_u8_nonblocking(); +}); + +Deno.bench("return_i8_nonblocking()", async () => { + await dylib.symbols.return_i8_nonblocking(); +}); + +Deno.bench("return_u16_nonblocking()", async () => { + await dylib.symbols.return_u16_nonblocking(); +}); + +Deno.bench("return_i16_nonblocking()", async () => { + await dylib.symbols.return_i16_nonblocking(); +}); + +Deno.bench("return_u32_nonblocking()", async () => { + await dylib.symbols.return_u32_nonblocking(); +}); + +Deno.bench("return_i32_nonblocking()", async () => { + await dylib.symbols.return_i32_nonblocking(); +}); + +Deno.bench("return_u64_nonblocking()", async () => { + await dylib.symbols.return_u64_nonblocking(); +}); + +Deno.bench("return_i64_nonblocking()", async () => { + await dylib.symbols.return_i64_nonblocking(); +}); + +Deno.bench("return_usize_nonblocking()", async () => { + await dylib.symbols.return_usize_nonblocking(); +}); + +Deno.bench("return_isize_nonblocking()", async () => { + await dylib.symbols.return_isize_nonblocking(); +}); + +Deno.bench("return_f32_nonblocking()", async () => { + await dylib.symbols.return_f32_nonblocking(); +}); + +Deno.bench("return_f64_nonblocking()", async () => { + await dylib.symbols.return_f64_nonblocking(); +}); + +Deno.bench("return_buffer_nonblocking()", async () => { + await dylib.symbols.return_buffer_nonblocking(); +}); + +const buffer2 = new Uint8Array(8).fill(25); +Deno.bench("nop_many_parameters()", () => { + dylib.symbols.nop_many_parameters( + 135, + 47, + 356, + -236, + 7457, + -1356, + 16471468n, + -1334748136n, + 132658769535n, + -42745856824n, + 13567.26437, + 7.686234e-3, + buffer, + 64, + -42, + 83, + -136, + 3657, + -2376, + 3277918n, + -474628146n, + 344657895n, + -2436732n, + 135.26437e3, + 264.3576468623546834, + buffer2, + ); +}); + +Deno.bench("nop_many_parameters_nonblocking()", () => { + dylib.symbols.nop_many_parameters_nonblocking( + 135, + 47, + 356, + -236, + 7457, + -1356, + 16471468n, + -1334748136n, + 132658769535n, + -42745856824n, + 13567.26437, + 7.686234e-3, + buffer, + 64, + -42, + 83, + -136, + 3657, + -2376, + 3277918n, + -474628146n, + 344657895n, + -2436732n, + 135.26437e3, + 264.3576468623546834, + buffer2, + ); +}); From 6c0ffe8712b2d84dff6db17875fbe814b423d267 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 11 Jun 2022 23:09:19 +0300 Subject: [PATCH 32/64] fmt & lint --- ext/ffi/00_ffi.js | 51 +++++++++++++++++++++++-------- test_ffi/tests/test.js | 68 +++++++++++++++++++++++++++++++++++------- 2 files changed, 96 insertions(+), 23 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index c0efc1f292b5fb..d4f09aceb41327 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -8,6 +8,8 @@ ArrayBufferPrototype, BigInt, Error, + NumberIsFinite, + NumberIsInteger, ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, Symbol, @@ -158,28 +160,45 @@ const arg = args[i]; if (type === "u8" || type === "u16" || type === "u32") { - if (!Number.isInteger(arg) || arg < 0) { - throw new TypeError(`Expected FFI argument to be an unsigned integer, but got '${arg}'`) + if (!NumberIsInteger(arg) || arg < 0) { + throw new TypeError( + `Expected FFI argument to be an unsigned integer, but got '${arg}'`, + ); } parameters.push(arg); } else if (type === "i8" || type === "i16" || type === "i32") { - if (!Number.isInteger(arg)) { - throw new TypeError(`Expected FFI argument to be a signed integer, but got '${arg}'`) + if (!NumberIsInteger(arg)) { + throw new TypeError( + `Expected FFI argument to be a signed integer, but got '${arg}'`, + ); } parameters.push(arg); } else if (type === "u64" || type === "usize") { - if (!(Number.isInteger(arg) && arg >= 0 || typeof arg === "bigint" && 0n <= arg && arg <= 0xffffffffffffffffn)) { - throw new TypeError(`Expected FFI argument to be an unsigned integer, but got '${arg}'`) + if ( + !(NumberIsInteger(arg) && arg >= 0 || + typeof arg === "bigint" && 0n <= arg && arg <= 0xffffffffffffffffn) + ) { + throw new TypeError( + `Expected FFI argument to be an unsigned integer, but got '${arg}'`, + ); } parameters.push(arg); } else if (type == "i64" || type === "isize") { - if (!(Number.isInteger(arg) || typeof arg === "bigint" && -1n * 2n ** 63n <= arg && arg <= 2n ** 63n - 1n)) { - throw new TypeError(`Expected FFI argument to be a signed integer, but got '${arg}'`) + if ( + !(NumberIsInteger(arg) || + typeof arg === "bigint" && -1n * 2n ** 63n <= arg && + arg <= 2n ** 63n - 1n) + ) { + throw new TypeError( + `Expected FFI argument to be a signed integer, but got '${arg}'`, + ); } parameters.push(arg); } else if (type === "f32" || type === "f64") { - if (!Number.isFinite(arg)) { - throw new TypeError(`Expected FFI argument to be a number, but got '${arg}'`) + if (!NumberIsFinite(arg)) { + throw new TypeError( + `Expected FFI argument to be a number, but got '${arg}'`, + ); } parameters.push(arg); } else if (type === "pointer") { @@ -252,7 +271,11 @@ parameters, ); - if (resultType === "pointer" || resultType === "u64" || resultType === "i64" || resultType === "usize" || resultType === "isize") { + if ( + resultType === "pointer" || resultType === "u64" || + resultType === "i64" || resultType === "usize" || + resultType === "isize" + ) { return promise.then((result) => unpackResult(resultType, result)); } @@ -386,7 +409,11 @@ parameters, ); - if (resultType === "pointer" || resultType === "u64" || resultType === "i64" || resultType === "usize" || resultType === "isize") { + if ( + resultType === "pointer" || resultType === "u64" || + resultType === "i64" || resultType === "usize" || + resultType === "isize" + ) { return promise.then((result) => unpackResult(resultType, result)); } diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 3f0dc3043f5701..61eaefa316b998 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -52,14 +52,54 @@ 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" }, - "add_u32_nonblocking": { name: "add_u32", parameters: ["u32", "u32"], result: "u32", nonblocking: true }, - "add_i32_nonblocking": { name: "add_i32", parameters: ["i32", "i32"], result: "i32", nonblocking: true }, - "add_u64_nonblocking": { name: "add_u64", parameters: ["u64", "u64"], result: "u64", nonblocking: true }, - "add_i64_nonblocking": { name: "add_i64", parameters: ["i64", "i64"], result: "i64", nonblocking: true }, - "add_usize_nonblocking": { name: "add_usize", parameters: ["usize", "usize"], result: "usize", nonblocking: true }, - "add_isize_nonblocking": { name: "add_isize", parameters: ["isize", "isize"], result: "isize", nonblocking: true }, - "add_f32_nonblocking": { name: "add_f32", parameters: ["f32", "f32"], result: "f32", nonblocking: true }, - "add_f64_nonblocking": { name: "add_f64", parameters: ["f64", "f64"], result: "f64", nonblocking: true }, + "add_u32_nonblocking": { + name: "add_u32", + parameters: ["u32", "u32"], + result: "u32", + nonblocking: true, + }, + "add_i32_nonblocking": { + name: "add_i32", + parameters: ["i32", "i32"], + result: "i32", + nonblocking: true, + }, + "add_u64_nonblocking": { + name: "add_u64", + parameters: ["u64", "u64"], + result: "u64", + nonblocking: true, + }, + "add_i64_nonblocking": { + name: "add_i64", + parameters: ["i64", "i64"], + result: "i64", + nonblocking: true, + }, + "add_usize_nonblocking": { + name: "add_usize", + parameters: ["usize", "usize"], + result: "usize", + nonblocking: true, + }, + "add_isize_nonblocking": { + name: "add_isize", + parameters: ["isize", "isize"], + result: "isize", + nonblocking: true, + }, + "add_f32_nonblocking": { + name: "add_f32", + parameters: ["f32", "f32"], + result: "f32", + nonblocking: true, + }, + "add_f64_nonblocking": { + name: "add_f64", + parameters: ["f64", "f64"], + result: "f64", + nonblocking: true, + }, "fill_buffer": { parameters: ["u8", "pointer", "usize"], result: "void" }, "sleep_nonblocking": { name: "sleep_blocking", @@ -166,9 +206,15 @@ console.log(dylib.symbols.add_f64(123.123, 456.789)); // Test adders as nonblocking calls console.log(await dylib.symbols.add_i32_nonblocking(123, 456)); console.log(await dylib.symbols.add_u64_nonblocking(0xffffffffn, 0xffffffffn)); -console.log(await dylib.symbols.add_i64_nonblocking(-0xffffffffn, -0xffffffffn)); -console.log(await dylib.symbols.add_usize_nonblocking(0xffffffffn, 0xffffffffn)); -console.log(await dylib.symbols.add_isize_nonblocking(-0xffffffffn, -0xffffffffn)); +console.log( + await dylib.symbols.add_i64_nonblocking(-0xffffffffn, -0xffffffffn), +); +console.log( + await dylib.symbols.add_usize_nonblocking(0xffffffffn, 0xffffffffn), +); +console.log( + await dylib.symbols.add_isize_nonblocking(-0xffffffffn, -0xffffffffn), +); console.log(await dylib.symbols.add_f32_nonblocking(123.123, 456.789)); console.log(await dylib.symbols.add_f64_nonblocking(123.123, 456.789)); From 297dab71eb3b0ba3e1629aef027e8f5c3658b9d6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 10:15:51 +0300 Subject: [PATCH 33/64] Minor improvements --- ext/ffi/00_ffi.js | 45 +++++++++++++------- ext/ffi/lib.rs | 100 ++++++++++++++++++++------------------------ test_ffi/src/lib.rs | 3 +- 3 files changed, 78 insertions(+), 70 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index d4f09aceb41327..e24eed5d1f5b64 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -219,12 +219,22 @@ } else if ( typeof type === "object" && type !== null && "function" in type ) { - if (!ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { + if (ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { + parameters.push(arg[_rid]); + } else if (arg === null) { + // nullptr + parameters.push(null); + } else if ( + ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, arg) || + ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg) + ) { + // Foreign function given to us, we're passing it on + parameters.push(arg.value); + } else { throw new TypeError( - "Expected FFI argument to be RegisteredCallback", + "Expected FFI argument to be RegisteredCallback, UnsafeFn", ); } - parameters.push(arg[_rid]); } else { throw new TypeError(`Invalid FFI argument type '${type}'`); } @@ -297,6 +307,8 @@ } } + const UnsafeFnPointerPrototype = UnsafeFnPointer.prototype; + const _rid = Symbol("[[rid]]"); class RegisteredCallback { @@ -398,10 +410,15 @@ const types = symbols[symbol].parameters; const resultType = symbols[symbol].result; - const fn = (...args) => { - const parameters = prepareArgs(types, args); + let fn; + if (isNonBlocking) { + const needsUnpacking = resultType === "pointer" || + resultType === "u64" || + resultType === "i64" || resultType === "usize" || + resultType === "isize"; + fn = (...args) => { + const parameters = prepareArgs(types, args); - if (isNonBlocking) { const promise = core.opAsync( "op_ffi_call_nonblocking", this.#rid, @@ -409,16 +426,16 @@ parameters, ); - if ( - resultType === "pointer" || resultType === "u64" || - resultType === "i64" || resultType === "usize" || - resultType === "isize" - ) { + if (needsUnpacking) { return promise.then((result) => unpackResult(resultType, result)); } return promise; - } else { + }; + } else { + fn = (...args) => { + const parameters = prepareArgs(types, args); + const result = core.opSync( "op_ffi_call", this.#rid, @@ -431,8 +448,8 @@ } return result; - } - }; + }; + } ObjectDefineProperty( this.symbols, diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 77500b29c7d8ff..feeec9a6217542 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,6 +1,7 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use core::ptr::NonNull; +use deno_core::anyhow::anyhow; use deno_core::error::generic_error; use deno_core::error::range_error; use deno_core::error::type_error; @@ -271,29 +272,15 @@ impl NativeValue { } } - fn to_value(self, native_type: NativeType) -> Value { + fn to_value(&self, native_type: NativeType) -> Value { match native_type { - NativeType::Void => { - json!(()) - } - NativeType::U8 => { - json!(unsafe { self.u8_value }) - } - NativeType::I8 => { - json!(unsafe { self.i8_value }) - } - NativeType::U16 => { - json!(unsafe { self.u16_value }) - } - NativeType::I16 => { - json!(unsafe { self.i16_value }) - } - NativeType::U32 => { - json!(unsafe { self.u32_value }) - } - NativeType::I32 => { - json!(unsafe { self.i32_value }) - } + NativeType::Void => Value::Null, + NativeType::U8 => Value::from(unsafe { self.u8_value }), + NativeType::I8 => Value::from(unsafe { self.i8_value }), + NativeType::U16 => Value::from(unsafe { self.u16_value }), + NativeType::I16 => Value::from(unsafe { self.i16_value }), + NativeType::U32 => Value::from(unsafe { self.u32_value }), + NativeType::I32 => Value::from(unsafe { self.i32_value }), NativeType::U64 => { json!(U32x2::from(unsafe { self.u64_value })) } @@ -306,12 +293,8 @@ impl NativeValue { NativeType::ISize => { json!(U32x2::from(unsafe { self.isize_value } as u64)) } - NativeType::F32 => { - json!(unsafe { self.f32_value }) - } - NativeType::F64 => { - json!(unsafe { self.f64_value }) - } + NativeType::F32 => Value::from(unsafe { self.f32_value }), + NativeType::F64 => Value::from(unsafe { self.f64_value }), NativeType::Pointer | NativeType::Function {} => { json!(U32x2::from(unsafe { self.pointer } as u64)) } @@ -558,7 +541,7 @@ where Ok(state.resource_table.add(resource)) } -fn get_symbol_for_ptr(fn_ptr: u64, def: ForeignFunction) -> Symbol { +fn get_symbol_for_ptr(fn_ptr: u64, def: &ForeignFunction) -> Symbol { let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); let cif = libffi::middle::Cif::new( def @@ -572,7 +555,8 @@ fn get_symbol_for_ptr(fn_ptr: u64, def: ForeignFunction) -> Symbol { Symbol { cif, ptr, - parameter_types: def.parameters.clone(), + /// Purposefully avoid cloning the definition parameters; this is never used. + parameter_types: vec![], result_type: def.result, } } @@ -586,7 +570,12 @@ fn ffi_parse_args<'scope>( where 'scope: 'scope, { - let args = v8::Local::::try_from(args.v8_value).unwrap(); + if parameter_types.is_empty() { + return Ok(vec![]); + } + + let args = v8::Local::::try_from(args.v8_value) + .map_err(|_| type_error("Invalid FFI parameters, expected Array"))?; let mut ffi_args: Vec = Vec::with_capacity(parameter_types.len()); @@ -726,8 +715,8 @@ where pointer: function as usize as *const u8, }); } else if value.is_big_int() { - // Do we support this? - let value = v8::Local::::try_from(value)?; + // Do we support this? This would be a foreign function pointer given to us by an FFI library. + let value = v8::Local::::try_from(value).unwrap(); let value = value.u64_value().0 as *const u8; ffi_args.push(NativeValue { pointer: value }); } else { @@ -1049,12 +1038,11 @@ fn op_ffi_deregister_callback( state: &mut deno_core::OpState, rid: ResourceId, ) -> Result<(), AnyError> { - Ok( - state - .resource_table - .take::(rid)? - .close(), - ) + state + .resource_table + .take::(rid)? + .close(); + Ok(()) } #[op(v8)] @@ -1104,30 +1092,29 @@ fn op_ffi_call_ptr( where FP: FfiPermissions + 'static, { - let (symbol, call_args) = { + let symbol = get_symbol_for_ptr(pointer, &def); + let call_args = { let mut state = state.borrow_mut(); - //check_unstable(state.borrow(), "Deno.UnsafeFnPointer#call"); + check_unstable(state.borrow(), "Deno.UnsafeFnPointer#call"); let permissions = state.borrow_mut::(); permissions.check(None)?; - let symbol = get_symbol_for_ptr(pointer, def); - let call_args = ffi_parse_args( + ffi_parse_args( scope, parameters, &symbol.parameter_types, &state.resource_table, - )?; - (symbol, call_args) + )? }; let result = ffi_call( call_args, &symbol.cif, symbol.ptr, - &symbol.parameter_types, - symbol.result_type, + &def.parameters, + def.result, )?; - let result = result.to_v8(scope, symbol.result_type); + let result = result.to_v8(scope, def.result); Ok(result) } @@ -1144,19 +1131,18 @@ where { check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - let (symbol, call_args) = { + let symbol = get_symbol_for_ptr(pointer, &def); + let call_args = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); permissions.check(None)?; - let symbol = get_symbol_for_ptr(pointer, def); - let call_args = ffi_parse_args( + ffi_parse_args( scope, parameters, &symbol.parameter_types, &state.resource_table, - )?; - (symbol, call_args) + )? }; let result_type = symbol.result_type; @@ -1171,7 +1157,9 @@ where }); Ok(async move { - let result = join_handle.await??; + let result = join_handle + .await + .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; Ok(result.to_value(result_type)) }) } @@ -1336,7 +1324,9 @@ fn op_ffi_call_nonblocking<'scope>( }); Ok(async move { - let result = join_handle.await??; + let result = join_handle + .await + .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; Ok(result.to_value(result_type)) }) } diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs index 75f5be574555f7..848e3ea948438a 100644 --- a/test_ffi/src/lib.rs +++ b/test_ffi/src/lib.rs @@ -208,7 +208,8 @@ pub extern "C" fn return_isize() -> isize { #[no_mangle] pub extern "C" fn return_f32() -> f32 { - 0.20000000298023223876953125 + #[allow(clippy::excessive_precision)] + 0.20298023223876953125 } #[no_mangle] From 87e10ca303290d573ae4f18a4771e8a8dc618aff Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 10:33:00 +0300 Subject: [PATCH 34/64] Fix tests FFI calls --- cli/tests/testdata/unstable_ffi_10.js | 2 +- cli/tests/testdata/unstable_ffi_11.js | 2 +- cli/tests/testdata/unstable_ffi_12.js | 2 +- cli/tests/testdata/unstable_ffi_13.js | 2 +- cli/tests/testdata/unstable_ffi_14.js | 2 +- cli/tests/testdata/unstable_ffi_15.js | 2 +- cli/tests/testdata/unstable_ffi_2.js | 11 +++++------ cli/tests/testdata/unstable_ffi_3.js | 11 +++++------ cli/tests/testdata/unstable_ffi_5.js | 2 +- cli/tests/testdata/unstable_ffi_6.js | 2 +- cli/tests/testdata/unstable_ffi_7.js | 2 +- cli/tests/testdata/unstable_ffi_8.js | 2 +- cli/tests/testdata/unstable_ffi_9.js | 2 +- 13 files changed, 21 insertions(+), 23 deletions(-) diff --git a/cli/tests/testdata/unstable_ffi_10.js b/cli/tests/testdata/unstable_ffi_10.js index c7cafd3ea4b820..e8fc9b3f0cebe4 100644 --- a/cli/tests/testdata/unstable_ffi_10.js +++ b/cli/tests/testdata/unstable_ffi_10.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_i16", [0, 0]); +Deno.core.opSync("op_ffi_read_i16", 0n); diff --git a/cli/tests/testdata/unstable_ffi_11.js b/cli/tests/testdata/unstable_ffi_11.js index 37bd75cc970c05..77c86109a57b3f 100644 --- a/cli/tests/testdata/unstable_ffi_11.js +++ b/cli/tests/testdata/unstable_ffi_11.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_u32", [0, 0]); +Deno.core.opSync("op_ffi_read_u32", 0n); diff --git a/cli/tests/testdata/unstable_ffi_12.js b/cli/tests/testdata/unstable_ffi_12.js index b05f92d39376a2..65934a82fd8119 100644 --- a/cli/tests/testdata/unstable_ffi_12.js +++ b/cli/tests/testdata/unstable_ffi_12.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_i32", [0, 0]); +Deno.core.opSync("op_ffi_read_i32", 0n); diff --git a/cli/tests/testdata/unstable_ffi_13.js b/cli/tests/testdata/unstable_ffi_13.js index a83b8dc1881d3c..0ab43781b31251 100644 --- a/cli/tests/testdata/unstable_ffi_13.js +++ b/cli/tests/testdata/unstable_ffi_13.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_u64", [0, 0]); +Deno.core.opSync("op_ffi_read_u64", 0n); diff --git a/cli/tests/testdata/unstable_ffi_14.js b/cli/tests/testdata/unstable_ffi_14.js index b39b99da5f17f7..b65a50a2098ab6 100644 --- a/cli/tests/testdata/unstable_ffi_14.js +++ b/cli/tests/testdata/unstable_ffi_14.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_f32", [0, 0]); +Deno.core.opSync("op_ffi_read_f32", 0n); diff --git a/cli/tests/testdata/unstable_ffi_15.js b/cli/tests/testdata/unstable_ffi_15.js index afd49b722f8cad..de9f291688f0a5 100644 --- a/cli/tests/testdata/unstable_ffi_15.js +++ b/cli/tests/testdata/unstable_ffi_15.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_f64", [0, 0]); +Deno.core.opSync("op_ffi_read_f64", 0n); diff --git a/cli/tests/testdata/unstable_ffi_2.js b/cli/tests/testdata/unstable_ffi_2.js index de392fa7d57a6d..cffbc81024b1ca 100644 --- a/cli/tests/testdata/unstable_ffi_2.js +++ b/cli/tests/testdata/unstable_ffi_2.js @@ -1,10 +1,9 @@ -Deno.core.opSync("op_ffi_call_ptr", { - pointer: [0, 0], - def: { +Deno.core.opSync("op_ffi_call_ptr", + [0, 0], + { name: null, parameters: [], result: "void", }, - parameters: [], - buffers: [], -}); + [], +); diff --git a/cli/tests/testdata/unstable_ffi_3.js b/cli/tests/testdata/unstable_ffi_3.js index 4924d9d6746793..a1084caa579b08 100644 --- a/cli/tests/testdata/unstable_ffi_3.js +++ b/cli/tests/testdata/unstable_ffi_3.js @@ -1,10 +1,9 @@ -Deno.core.opAsync("op_ffi_call_ptr_nonblocking", { - pointer: [0, 0], - def: { +Deno.core.opAsync("op_ffi_call_ptr_nonblocking", + [0, 0], + { name: null, parameters: [], result: "void", }, - parameters: [], - buffers: [], -}); + [], +); diff --git a/cli/tests/testdata/unstable_ffi_5.js b/cli/tests/testdata/unstable_ffi_5.js index 447ff584276048..dc494023d49568 100644 --- a/cli/tests/testdata/unstable_ffi_5.js +++ b/cli/tests/testdata/unstable_ffi_5.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_buf_copy_into", [[0, 0], new Uint8Array(0), 0]); +Deno.core.opSync("op_ffi_buf_copy_into", 0n, new Uint8Array(0), 0); diff --git a/cli/tests/testdata/unstable_ffi_6.js b/cli/tests/testdata/unstable_ffi_6.js index cc791b8f09dfe4..c66681225e2173 100644 --- a/cli/tests/testdata/unstable_ffi_6.js +++ b/cli/tests/testdata/unstable_ffi_6.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_cstr_read", [0, 0]); +Deno.core.opSync("op_ffi_cstr_read", 0n); diff --git a/cli/tests/testdata/unstable_ffi_7.js b/cli/tests/testdata/unstable_ffi_7.js index 02ef455ee4d292..a0c27a71c64b0a 100644 --- a/cli/tests/testdata/unstable_ffi_7.js +++ b/cli/tests/testdata/unstable_ffi_7.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_u8", [0, 0]); +Deno.core.opSync("op_ffi_read_u8", 0n); diff --git a/cli/tests/testdata/unstable_ffi_8.js b/cli/tests/testdata/unstable_ffi_8.js index d250c9f21994c3..7c51f8aa3d2bfa 100644 --- a/cli/tests/testdata/unstable_ffi_8.js +++ b/cli/tests/testdata/unstable_ffi_8.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_i8", [0, 0]); +Deno.core.opSync("op_ffi_read_i8", 0n); diff --git a/cli/tests/testdata/unstable_ffi_9.js b/cli/tests/testdata/unstable_ffi_9.js index f21a4cdbf2117a..7798e4d2c44943 100644 --- a/cli/tests/testdata/unstable_ffi_9.js +++ b/cli/tests/testdata/unstable_ffi_9.js @@ -1 +1 @@ -Deno.core.opSync("op_ffi_read_u16", [0, 0]); +Deno.core.opSync("op_ffi_read_u16", 0n); From 7b2c176b0b1092ced30540254a6e5d4e3860223c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 10:36:22 +0300 Subject: [PATCH 35/64] fmt --- cli/tests/testdata/unstable_ffi_2.js | 14 +++++--------- cli/tests/testdata/unstable_ffi_3.js | 14 +++++--------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/cli/tests/testdata/unstable_ffi_2.js b/cli/tests/testdata/unstable_ffi_2.js index cffbc81024b1ca..ca00f1a32d7027 100644 --- a/cli/tests/testdata/unstable_ffi_2.js +++ b/cli/tests/testdata/unstable_ffi_2.js @@ -1,9 +1,5 @@ -Deno.core.opSync("op_ffi_call_ptr", - [0, 0], - { - name: null, - parameters: [], - result: "void", - }, - [], -); +Deno.core.opSync("op_ffi_call_ptr", [0, 0], { + name: null, + parameters: [], + result: "void", +}, []); diff --git a/cli/tests/testdata/unstable_ffi_3.js b/cli/tests/testdata/unstable_ffi_3.js index a1084caa579b08..8c440edc2383b8 100644 --- a/cli/tests/testdata/unstable_ffi_3.js +++ b/cli/tests/testdata/unstable_ffi_3.js @@ -1,9 +1,5 @@ -Deno.core.opAsync("op_ffi_call_ptr_nonblocking", - [0, 0], - { - name: null, - parameters: [], - result: "void", - }, - [], -); +Deno.core.opAsync("op_ffi_call_ptr_nonblocking", [0, 0], { + name: null, + parameters: [], + result: "void", +}, []); From 3d53899b1e36aaa84dbb621a2b3c27e222644ab6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 10:53:11 +0300 Subject: [PATCH 36/64] Fix more FFI ops call signatures --- cli/tests/testdata/unstable_ffi_2.js | 2 +- cli/tests/testdata/unstable_ffi_3.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/tests/testdata/unstable_ffi_2.js b/cli/tests/testdata/unstable_ffi_2.js index ca00f1a32d7027..fe3d9d709e5ed8 100644 --- a/cli/tests/testdata/unstable_ffi_2.js +++ b/cli/tests/testdata/unstable_ffi_2.js @@ -1,4 +1,4 @@ -Deno.core.opSync("op_ffi_call_ptr", [0, 0], { +Deno.core.opSync("op_ffi_call_ptr", 0n, { name: null, parameters: [], result: "void", diff --git a/cli/tests/testdata/unstable_ffi_3.js b/cli/tests/testdata/unstable_ffi_3.js index 8c440edc2383b8..a8f7f41806c4fb 100644 --- a/cli/tests/testdata/unstable_ffi_3.js +++ b/cli/tests/testdata/unstable_ffi_3.js @@ -1,4 +1,4 @@ -Deno.core.opAsync("op_ffi_call_ptr_nonblocking", [0, 0], { +Deno.core.opAsync("op_ffi_call_ptr_nonblocking", 0n, { name: null, parameters: [], result: "void", From 2f370c9ff5c9db533b6b571aa0c987c7c41074f9 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 11:13:43 +0300 Subject: [PATCH 37/64] Fix check_unstable2 usage --- ext/ffi/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index feeec9a6217542..edfff999621685 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1092,10 +1092,11 @@ fn op_ffi_call_ptr( where FP: FfiPermissions + 'static, { + check_unstable2(&state, "Deno.UnsafeFnPointer#call"); + let symbol = get_symbol_for_ptr(pointer, &def); let call_args = { let mut state = state.borrow_mut(); - check_unstable(state.borrow(), "Deno.UnsafeFnPointer#call"); let permissions = state.borrow_mut::(); permissions.check(None)?; From e6fad7aeb63f8f5d7962ac61518816ea831762b0 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 11:49:42 +0300 Subject: [PATCH 38/64] Fix mistaken Symbol usage with ptr FFI calls --- ext/ffi/lib.rs | 78 ++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index edfff999621685..92ddc5028e1382 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -78,6 +78,32 @@ struct Symbol { unsafe impl Send for Symbol {} unsafe impl Sync for Symbol {} +#[derive(Clone)] +struct PtrSymbol { + cif: libffi::middle::Cif, + ptr: libffi::middle::CodePtr, +} + +impl PtrSymbol { + fn new(fn_ptr: u64, def: &ForeignFunction) -> Self { + let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); + let cif = libffi::middle::Cif::new( + def + .parameters + .clone() + .into_iter() + .map(libffi::middle::Type::from), + def.result.into(), + ); + + Self { cif, ptr } + } +} + +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for PtrSymbol {} +unsafe impl Sync for PtrSymbol {} + struct DynamicLibraryResource { lib: Library, symbols: HashMap, @@ -541,26 +567,6 @@ where Ok(state.resource_table.add(resource)) } -fn get_symbol_for_ptr(fn_ptr: u64, def: &ForeignFunction) -> Symbol { - let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); - let cif = libffi::middle::Cif::new( - def - .parameters - .clone() - .into_iter() - .map(libffi::middle::Type::from), - def.result.into(), - ); - - Symbol { - cif, - ptr, - /// Purposefully avoid cloning the definition parameters; this is never used. - parameter_types: vec![], - result_type: def.result, - } -} - fn ffi_parse_args<'scope>( scope: &mut v8::HandleScope<'scope>, args: serde_v8::Value<'scope>, @@ -1094,20 +1100,15 @@ where { check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - let symbol = get_symbol_for_ptr(pointer, &def); + let symbol = PtrSymbol::new(pointer, &def); let call_args = { let mut state = state.borrow_mut(); - let permissions = state.borrow_mut::(); permissions.check(None)?; - ffi_parse_args( - scope, - parameters, - &symbol.parameter_types, - &state.resource_table, - )? + ffi_parse_args(scope, parameters, &def.parameters, &state.resource_table)? }; + let result = ffi_call( call_args, &symbol.cif, @@ -1132,36 +1133,25 @@ where { check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - let symbol = get_symbol_for_ptr(pointer, &def); + let symbol = PtrSymbol::new(pointer, &def); let call_args = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); permissions.check(None)?; - ffi_parse_args( - scope, - parameters, - &symbol.parameter_types, - &state.resource_table, - )? + ffi_parse_args(scope, parameters, &def.parameters, &state.resource_table)? }; - let result_type = symbol.result_type; let join_handle = tokio::task::spawn_blocking(move || { - let Symbol { - cif, - ptr, - parameter_types, - .. - } = symbol.clone(); - ffi_call(call_args, &cif, ptr, ¶meter_types, result_type) + let PtrSymbol { cif, ptr } = symbol.clone(); + ffi_call(call_args, &cif, ptr, &def.parameters, def.result) }); Ok(async move { let result = join_handle .await .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; - Ok(result.to_value(result_type)) + Ok(result.to_value(def.result)) }) } From 06dee1f58068dded8f05c8dcf26dbb77028bb751 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 16:37:22 +0300 Subject: [PATCH 39/64] Fix buffer returning with quite manual V8 value picking --- ext/ffi/lib.rs | 130 +++++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 58 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 92ddc5028e1382..cf6867016c9d67 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -883,112 +883,126 @@ unsafe extern "C" fn deno_ffi_callback( let recv = v8::undefined(&mut scope); let call_result = func.call(&mut scope, recv.into(), ¶ms); + std::mem::forget(callback); + + if (*cif.rtype).type_ == FFI_TYPE_VOID as u16 { + return; + } + if call_result.is_none() { // JS function threw an exception. Set the return value to zero and return. // TODO: Somehow get the exception back into JS match (*cif.rtype).type_ as _ { - FFI_TYPE_INT | FFI_TYPE_SINT32 => { - *(result as *mut i32) = 0; - return; + FFI_TYPE_INT | FFI_TYPE_SINT32 | FFI_TYPE_UINT32 => { + // zero is equal for signed and unsigned alike + *(result as *mut u32) = 0; } FFI_TYPE_FLOAT => { *(result as *mut f32) = 0.0; - return; } FFI_TYPE_DOUBLE => { *(result as *mut f64) = 0.0; - return; - } - FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { - *(result as *mut u64) = 0; - return; - } - FFI_TYPE_SINT8 => { - *(result as *mut i8) = 0; - return; } - FFI_TYPE_UINT8 => { + FFI_TYPE_SINT8 | FFI_TYPE_UINT8 => { + // zero is equal for signed and unsigned alike *(result as *mut u8) = 0; - return; } - FFI_TYPE_SINT16 => { - *(result as *mut i16) = 0; - return; - } - FFI_TYPE_UINT16 => { + FFI_TYPE_SINT16 | FFI_TYPE_UINT16 => { + // zero is equal for signed and unsigned alike *(result as *mut u16) = 0; - return; - } - FFI_TYPE_UINT32 => { - *(result as *mut u32) = 0; - return; - } - FFI_TYPE_SINT64 => { - *(result as *mut i64) = 0; - return; } - FFI_TYPE_UINT64 => { + FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 + | FFI_TYPE_SINT64 => { *(result as *mut u64) = 0; - return; } - FFI_TYPE_VOID => {} + FFI_TYPE_VOID => { + // nop + } _ => { - panic!("Unsupported callback return type") + unreachable!(); } }; + return; } let value = call_result.unwrap(); - std::mem::forget(callback); - match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 => { - *(result as *mut i32) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + *(result as *mut i32) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as i32; } FFI_TYPE_FLOAT => { - *(result as *mut f32) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + *(result as *mut f32) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as f32; } FFI_TYPE_DOUBLE => { - *(result as *mut f64) = serde_v8::from_v8(&mut scope, value) + *(result as *mut f64) = value.number_value(&mut scope) .expect("Unable to deserialize result parameter."); } FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { - *(result as *mut u64) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + if value.is_array_buffer() | value.is_array_buffer_view() { + let value: ZeroCopyBuf = serde_v8::from_v8(&mut scope, value) + .expect("Unable to deserialize result parameter."); + let value: &[u8] = &value[..]; + *(result as *mut *const u8) = value.as_ptr(); + } else if value.is_big_int() | value.is_big_int_object() { + let value = v8::Local::::try_from(value).unwrap(); + *(result as *mut u64) = value.u64_value().0; + } else if value.is_null() { + *(result as *mut *const c_void) = ptr::null(); + } else if value.is_object() { + let value = v8::Local::::try_from(value).unwrap(); + let value_key = v8::String::new(&mut scope, "value").unwrap(); + let value_field = value + .get(&mut scope, value_key.into()) + .expect("Unable to deserialize result parameter."); + let value = v8::Local::::try_from(value_field) + .expect("Unable to deserialize result parameter."); + *(result as *mut u64) = value.u64_value().0; + } else { + // Fallthrough: Probably someone returned a number but this could + // also be eg. a string. This is essentially UB. + *(result as *mut u64) = value.number_value(&mut scope).expect("Unable to deserialize result parameter.") as u64; + } } FFI_TYPE_SINT8 => { - *(result as *mut i8) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + *(result as *mut i8) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as i8; } FFI_TYPE_UINT8 => { - *(result as *mut u8) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + *(result as *mut u8) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as u8; } FFI_TYPE_SINT16 => { - *(result as *mut i16) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + *(result as *mut i16) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as i16; } FFI_TYPE_UINT16 => { - *(result as *mut u16) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + *(result as *mut u16) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as u16; } FFI_TYPE_UINT32 => { - *(result as *mut u32) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + *(result as *mut u32) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as u32; } FFI_TYPE_SINT64 => { - *(result as *mut i64) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + if value.is_big_int() | value.is_big_int_object() { + let value = v8::Local::::try_from(value).unwrap(); + *(result as *mut u64) = value.u64_value().0; + } else { + *(result as *mut i64) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as i64; + } } FFI_TYPE_UINT64 => { - *(result as *mut u64) = serde_v8::from_v8(&mut scope, value) - .expect("Unable to deserialize result parameter."); + *(result as *mut u64) = value.number_value(&mut scope) + .expect("Unable to deserialize result parameter.") as u64; + } + FFI_TYPE_VOID => { + // nop } - FFI_TYPE_VOID => {} _ => { - panic!("Unsupported callback return type") + unreachable!(); } }; } From c29d8fd9f1aeca5b1130167bf7441ee0b99c1a4f Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 16:37:36 +0300 Subject: [PATCH 40/64] Add callback tests --- test_ffi/src/lib.rs | 79 +++++++++++++++++++++++++++++ test_ffi/tests/integration_tests.rs | 8 +++ test_ffi/tests/test.js | 74 +++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs index 848e3ea948438a..979cc6d8c5cb4e 100644 --- a/test_ffi/src/lib.rs +++ b/test_ffi/src/lib.rs @@ -113,6 +113,85 @@ pub extern "C" fn get_sleep_blocking_ptr() -> *const c_void { sleep_blocking as *const c_void } +#[no_mangle] +pub extern "C" fn call_fn_ptr(func: Option) { + if func.is_none() { + return; + } + let func = func.unwrap(); + func(); +} + +#[no_mangle] +pub extern "C" fn call_fn_ptr_many_parameters( + func: Option< + extern "C" fn(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, *const u8), + >, +) { + if func.is_none() { + return; + } + let func = func.unwrap(); + func(1, -1, 2, -2, 3, -3, 4, -4, 0.5, -0.5, BUFFER.as_ptr()); +} + +#[no_mangle] +pub extern "C" fn call_fn_ptr_return_u8(func: Option u8>) { + if func.is_none() { + return; + } + let func = func.unwrap(); + println!("u8: {}", func()); +} + +#[no_mangle] +pub extern "C" fn call_fn_ptr_return_buffer( + func: Option *const u8>, +) { + if func.is_none() { + return; + } + let func = func.unwrap(); + let ptr = func(); + let buf = unsafe { std::slice::from_raw_parts(ptr, 8) }; + println!("buf: {:?}", buf); +} + +static mut STORED_FUNCTION: Option = None; +static mut STORED_FUNCTION_2: Option u8> = None; + +#[no_mangle] +pub extern "C" fn store_function(func: Option) { + unsafe { STORED_FUNCTION = func }; + if func.is_none() { + println!("STORED_FUNCTION cleared"); + } +} + +#[no_mangle] +pub extern "C" fn store_function_2(func: Option u8>) { + unsafe { STORED_FUNCTION_2 = func }; + if func.is_none() { + println!("STORED_FUNCTION_2 cleared"); + } +} + +#[no_mangle] +pub unsafe extern "C" fn call_stored_function() { + if STORED_FUNCTION.is_none() { + return; + } + STORED_FUNCTION.unwrap()(); +} + +#[no_mangle] +pub unsafe extern "C" fn call_stored_function_2(arg: u8) { + if STORED_FUNCTION_2.is_none() { + return; + } + println!("{}", STORED_FUNCTION_2.unwrap()(arg)); +} + // FFI performance helper functions #[no_mangle] pub extern "C" fn nop() {} diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs index 4c3efada7ee47d..93f3687871ca6f 100644 --- a/test_ffi/tests/integration_tests.rs +++ b/test_ffi/tests/integration_tests.rs @@ -76,6 +76,14 @@ fn basic() { true\n\ Before\n\ true\n\ + logCallback\n\ + 1 -1 2 -2 3 -3 4n -4n 0.5 -0.5 1 2 3 4 5 6 7 8\n\ + u8: 8\n\ + buf: [1, 2, 3, 4, 5, 6, 7, 8]\n\ + logCallback\n\ + 30\n\ + STORED_FUNCTION cleared\n\ + STORED_FUNCTION_2 cleared\n\ Static u32: 42\n\ Static i64: -1242464576485n\n\ Static ptr: true\n\ diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 61eaefa316b998..7c3644b9552487 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -121,6 +121,40 @@ const dylib = Deno.dlopen(libPath, { parameters: [], result: "pointer", }, + // Callback function + call_fn_ptr: { + parameters: [{ function: { parameters: [], result: "void" } }], + result: "void" + }, + call_fn_ptr_many_parameters: { + parameters: [{ function: { parameters: ["u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f32", "f64", "pointer"], result: "void" } }], + result: "void" + }, + call_fn_ptr_return_u8: { + parameters: [{ function: { parameters: [], result: "u8" } }], + result: "void" + }, + call_fn_ptr_return_buffer: { + parameters: [{ function: { parameters: [], result: "pointer" } }], + result: "void" + }, + store_function: { + parameters: [{ function: { parameters: [], result: "void" } }], + result: "void" + }, + store_function_2: { + parameters: [{ function: { parameters: ["u8"], result: "u8" } }], + result: "void" + }, + call_stored_function: { + parameters: [], + result: "void" + }, + call_stored_function_2: { + parameters: ["u8"], + result: "void" + }, + // Statics "static_u32": { type: "u32", }, @@ -273,6 +307,40 @@ dylib.symbols.sleep_nonblocking(100).then(() => { console.log("Before"); console.log(performance.now() - start < 100); +// Test calls with callback parameters +const logCallback = Deno.registerCallback({ parameters: [], result: "void" }, () => console.log("logCallback")); +const logManyParametersCallback = Deno.registerCallback({ parameters: ["u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f32", "f64", "pointer"], result: "void" }, + (u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, pointer) => { + const view = new Deno.UnsafePointerView(new Deno.UnsafePointer(pointer)); + const copy_buffer = new Uint8Array(8); + view.copyInto(copy_buffer); + console.log(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, ...copy_buffer); + } +); +const returnU8Callback = Deno.registerCallback({ parameters: [], result: "u8" }, () => 8); +const returnBufferCallback = Deno.registerCallback({ parameters: [], result: "pointer" }, () => { + return buffer; +}); +const add10Callback = Deno.registerCallback({ parameters: ["u8"], result: "u8" }, (value) => value + 10); + +dylib.symbols.call_fn_ptr(logCallback); +dylib.symbols.call_fn_ptr_many_parameters(logManyParametersCallback); +dylib.symbols.call_fn_ptr_return_u8(returnU8Callback); +dylib.symbols.call_fn_ptr_return_buffer(returnBufferCallback); +dylib.symbols.store_function(logCallback); +dylib.symbols.call_stored_function(); +dylib.symbols.store_function_2(add10Callback); +dylib.symbols.call_stored_function_2(20); + +const nestedCallback = Deno.registerCallback({ parameters: [], result: "void" }, () => { + dylib.symbols.call_stored_function_2(10); +}); +dylib.symbols.store_function(nestedCallback); + +dylib.symbols.store_function(null); +dylib.symbols.store_function_2(null); + +// Test statics console.log("Static u32:", dylib.symbols.static_u32); console.log("Static i64:", dylib.symbols.static_i64); console.log( @@ -284,6 +352,12 @@ console.log("Static ptr value:", view.getUint32()); function cleanup() { dylib.close(); + logCallback.close(); + logManyParametersCallback.close(); + returnU8Callback.close(); + returnBufferCallback.close(); + add10Callback.close(); + nestedCallback.close(); const resourcesPost = Deno.resources(); From 69eec01607ba693cf510b572d78ffe800f936cdb Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 16:42:21 +0300 Subject: [PATCH 41/64] fmt & lint --- ext/ffi/lib.rs | 64 +++++++++++++++++++---------- test_ffi/src/lib.rs | 21 ++++++---- test_ffi/tests/test.js | 91 +++++++++++++++++++++++++++++++----------- 3 files changed, 124 insertions(+), 52 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index cf6867016c9d67..0d5d5014e94919 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -928,15 +928,20 @@ unsafe extern "C" fn deno_ffi_callback( match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 => { - *(result as *mut i32) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as i32; + *(result as *mut i32) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as i32; } FFI_TYPE_FLOAT => { - *(result as *mut f32) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as f32; + *(result as *mut f32) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as f32; } FFI_TYPE_DOUBLE => { - *(result as *mut f64) = value.number_value(&mut scope) + *(result as *mut f64) = value + .number_value(&mut scope) .expect("Unable to deserialize result parameter."); } FFI_TYPE_POINTER | FFI_TYPE_STRUCT => { @@ -962,47 +967,64 @@ unsafe extern "C" fn deno_ffi_callback( } else { // Fallthrough: Probably someone returned a number but this could // also be eg. a string. This is essentially UB. - *(result as *mut u64) = value.number_value(&mut scope).expect("Unable to deserialize result parameter.") as u64; + *(result as *mut u64) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u64; } } FFI_TYPE_SINT8 => { - *(result as *mut i8) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as i8; + *(result as *mut i8) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as i8; } FFI_TYPE_UINT8 => { - *(result as *mut u8) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as u8; + *(result as *mut u8) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u8; } FFI_TYPE_SINT16 => { - *(result as *mut i16) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as i16; + *(result as *mut i16) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as i16; } FFI_TYPE_UINT16 => { - *(result as *mut u16) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as u16; + *(result as *mut u16) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u16; } FFI_TYPE_UINT32 => { - *(result as *mut u32) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as u32; + *(result as *mut u32) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u32; } FFI_TYPE_SINT64 => { if value.is_big_int() | value.is_big_int_object() { let value = v8::Local::::try_from(value).unwrap(); *(result as *mut u64) = value.u64_value().0; } else { - *(result as *mut i64) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as i64; + *(result as *mut i64) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as i64; } } FFI_TYPE_UINT64 => { - *(result as *mut u64) = value.number_value(&mut scope) - .expect("Unable to deserialize result parameter.") as u64; + *(result as *mut u64) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u64; } FFI_TYPE_VOID => { // nop } _ => { - unreachable!(); + unreachable!(); } }; } diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs index 979cc6d8c5cb4e..5b813cd012e85f 100644 --- a/test_ffi/src/lib.rs +++ b/test_ffi/src/lib.rs @@ -144,6 +144,7 @@ pub extern "C" fn call_fn_ptr_return_u8(func: Option u8>) { println!("u8: {}", func()); } +#[allow(clippy::not_unsafe_ptr_arg_deref)] #[no_mangle] pub extern "C" fn call_fn_ptr_return_buffer( func: Option *const u8>, @@ -177,19 +178,23 @@ pub extern "C" fn store_function_2(func: Option u8>) { } #[no_mangle] -pub unsafe extern "C" fn call_stored_function() { - if STORED_FUNCTION.is_none() { - return; +pub extern "C" fn call_stored_function() { + unsafe { + if STORED_FUNCTION.is_none() { + return; + } + STORED_FUNCTION.unwrap()(); } - STORED_FUNCTION.unwrap()(); } #[no_mangle] -pub unsafe extern "C" fn call_stored_function_2(arg: u8) { - if STORED_FUNCTION_2.is_none() { - return; +pub extern "C" fn call_stored_function_2(arg: u8) { + unsafe { + if STORED_FUNCTION_2.is_none() { + return; + } + println!("{}", STORED_FUNCTION_2.unwrap()(arg)); } - println!("{}", STORED_FUNCTION_2.unwrap()(arg)); } // FFI performance helper functions diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 7c3644b9552487..f9721e91ada547 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -124,35 +124,52 @@ const dylib = Deno.dlopen(libPath, { // Callback function call_fn_ptr: { parameters: [{ function: { parameters: [], result: "void" } }], - result: "void" + result: "void", }, call_fn_ptr_many_parameters: { - parameters: [{ function: { parameters: ["u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f32", "f64", "pointer"], result: "void" } }], - result: "void" + parameters: [{ + function: { + parameters: [ + "u8", + "i8", + "u16", + "i16", + "u32", + "i32", + "u64", + "i64", + "f32", + "f64", + "pointer", + ], + result: "void", + }, + }], + result: "void", }, call_fn_ptr_return_u8: { parameters: [{ function: { parameters: [], result: "u8" } }], - result: "void" + result: "void", }, call_fn_ptr_return_buffer: { parameters: [{ function: { parameters: [], result: "pointer" } }], - result: "void" + result: "void", }, store_function: { parameters: [{ function: { parameters: [], result: "void" } }], - result: "void" + result: "void", }, store_function_2: { parameters: [{ function: { parameters: ["u8"], result: "u8" } }], - result: "void" + result: "void", }, call_stored_function: { parameters: [], - result: "void" + result: "void", }, call_stored_function_2: { parameters: ["u8"], - result: "void" + result: "void", }, // Statics "static_u32": { @@ -308,20 +325,45 @@ console.log("Before"); console.log(performance.now() - start < 100); // Test calls with callback parameters -const logCallback = Deno.registerCallback({ parameters: [], result: "void" }, () => console.log("logCallback")); -const logManyParametersCallback = Deno.registerCallback({ parameters: ["u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f32", "f64", "pointer"], result: "void" }, - (u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, pointer) => { - const view = new Deno.UnsafePointerView(new Deno.UnsafePointer(pointer)); - const copy_buffer = new Uint8Array(8); - view.copyInto(copy_buffer); - console.log(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, ...copy_buffer); - } +const logCallback = Deno.registerCallback( + { parameters: [], result: "void" }, + () => console.log("logCallback"), +); +const logManyParametersCallback = Deno.registerCallback({ + parameters: [ + "u8", + "i8", + "u16", + "i16", + "u32", + "i32", + "u64", + "i64", + "f32", + "f64", + "pointer", + ], + result: "void", +}, (u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, pointer) => { + const view = new Deno.UnsafePointerView(new Deno.UnsafePointer(pointer)); + const copy_buffer = new Uint8Array(8); + view.copyInto(copy_buffer); + console.log(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, ...copy_buffer); +}); +const returnU8Callback = Deno.registerCallback( + { parameters: [], result: "u8" }, + () => 8, ); -const returnU8Callback = Deno.registerCallback({ parameters: [], result: "u8" }, () => 8); -const returnBufferCallback = Deno.registerCallback({ parameters: [], result: "pointer" }, () => { +const returnBufferCallback = Deno.registerCallback({ + parameters: [], + result: "pointer", +}, () => { return buffer; }); -const add10Callback = Deno.registerCallback({ parameters: ["u8"], result: "u8" }, (value) => value + 10); +const add10Callback = Deno.registerCallback({ + parameters: ["u8"], + result: "u8", +}, (value) => value + 10); dylib.symbols.call_fn_ptr(logCallback); dylib.symbols.call_fn_ptr_many_parameters(logManyParametersCallback); @@ -332,9 +374,12 @@ dylib.symbols.call_stored_function(); dylib.symbols.store_function_2(add10Callback); dylib.symbols.call_stored_function_2(20); -const nestedCallback = Deno.registerCallback({ parameters: [], result: "void" }, () => { - dylib.symbols.call_stored_function_2(10); -}); +const nestedCallback = Deno.registerCallback( + { parameters: [], result: "void" }, + () => { + dylib.symbols.call_stored_function_2(10); + }, +); dylib.symbols.store_function(nestedCallback); dylib.symbols.store_function(null); From 6db458b10457ad260637ecf5748489db72eb5d7f Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 21:07:56 +0300 Subject: [PATCH 42/64] Expose registered callback pointer values --- ext/ffi/00_ffi.js | 8 ++++++++ ext/ffi/lib.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index e24eed5d1f5b64..3d5b0d4010c268 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -313,6 +313,7 @@ class RegisteredCallback { [_rid]; + #value; definition; callback; @@ -356,6 +357,13 @@ close() { core.close(this[_rid]); } + + get value() { + if (!this.#value) { + this.#value = core.opSync("op_ffi_ptr_of_cb", this[_rid]); + } + return this.#value; + } } const RegisteredCallbackPrototype = RegisteredCallback.prototype; diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 0d5d5014e94919..4b3c42d857e645 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -191,6 +191,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_ptr_of_cb::decl::

(), op_ffi_buf_copy_into::decl::

(), op_ffi_cstr_read::decl::

(), op_ffi_read_u8::decl::

(), @@ -1368,7 +1369,6 @@ where FP: FfiPermissions + 'static, { check_unstable(state, "Deno.UnsafePointer#of"); - let permissions = state.borrow_mut::(); permissions.check(None)?; @@ -1377,6 +1377,31 @@ where Ok(big_int.into()) } +#[op(v8)] +fn op_ffi_ptr_of_cb( + scope: &mut v8::HandleScope<'scope>, + state: &mut deno_core::OpState, + rid: ResourceId, +) -> Result, AnyError> +where + FP: FfiPermissions + 'static, +{ + check_unstable(state, "Deno.RegisterableCallback.value"); + let permissions = state.borrow_mut::(); + permissions.check(None)?; + + let resource = state + .resource_table + .get::(rid)?; + + let big_int: v8::Local = v8::BigInt::new_from_u64( + scope, + *resource.closure.code_ptr() as usize as u64, + ) + .into(); + Ok(big_int.into()) +} + #[op] fn op_ffi_buf_copy_into( state: &mut deno_core::OpState, From 84ddbda0287dd0402bd97a47a4c948ed06c46f55 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 22:02:11 +0300 Subject: [PATCH 43/64] Use primordials --- ext/ffi/00_ffi.js | 92 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 3d5b0d4010c268..31d50624fa66a3 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -6,12 +6,15 @@ const __bootstrap = window.__bootstrap; const { ArrayBufferPrototype, + ArrayPrototypePush, + ArrayPrototypeSome, BigInt, Error, NumberIsFinite, NumberIsInteger, ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, + PromisePrototypeThen, Symbol, TypeError, Uint8Array, @@ -165,14 +168,14 @@ `Expected FFI argument to be an unsigned integer, but got '${arg}'`, ); } - parameters.push(arg); + ArrayPrototypePush(parameters, arg); } else if (type === "i8" || type === "i16" || type === "i32") { if (!NumberIsInteger(arg)) { throw new TypeError( `Expected FFI argument to be a signed integer, but got '${arg}'`, ); } - parameters.push(arg); + ArrayPrototypePush(parameters, arg); } else if (type === "u64" || type === "usize") { if ( !(NumberIsInteger(arg) && arg >= 0 || @@ -182,7 +185,7 @@ `Expected FFI argument to be an unsigned integer, but got '${arg}'`, ); } - parameters.push(arg); + ArrayPrototypePush(parameters, arg); } else if (type == "i64" || type === "isize") { if ( !(NumberIsInteger(arg) || @@ -193,24 +196,24 @@ `Expected FFI argument to be a signed integer, but got '${arg}'`, ); } - parameters.push(arg); + ArrayPrototypePush(parameters, arg); } else if (type === "f32" || type === "f64") { if (!NumberIsFinite(arg)) { throw new TypeError( `Expected FFI argument to be a number, but got '${arg}'`, ); } - parameters.push(arg); + ArrayPrototypePush(parameters, arg); } else if (type === "pointer") { if ( ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, arg?.buffer) && arg.byteLength !== undefined ) { - parameters.push(arg); + ArrayPrototypePush(parameters, arg); } else if (ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg)) { - parameters.push(arg.value); + ArrayPrototypePush(parameters, arg.value); } else if (arg === null) { - parameters.push(null); + ArrayPrototypePush(parameters, null); } else { throw new TypeError( "Expected FFI argument to be TypedArray, UnsafePointer or null", @@ -220,16 +223,16 @@ typeof type === "object" && type !== null && "function" in type ) { if (ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { - parameters.push(arg[_rid]); + ArrayPrototypePush(parameters, arg[_rid]); } else if (arg === null) { // nullptr - parameters.push(null); + ArrayPrototypePush(parameters, null); } else if ( ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, arg) || ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg) ) { // Foreign function given to us, we're passing it on - parameters.push(arg.value); + ArrayPrototypePush(parameters, arg.value); } else { throw new TypeError( "Expected FFI argument to be RegisteredCallback, UnsafeFn", @@ -286,7 +289,10 @@ resultType === "i64" || resultType === "usize" || resultType === "isize" ) { - return promise.then((result) => unpackResult(resultType, result)); + return PromisePrototypeThen( + promise, + (result) => unpackResult(resultType, result), + ); } return promise; @@ -311,9 +317,57 @@ const _rid = Symbol("[[rid]]"); + function isPointerType(type) { + return type === "pointer" || + typeof type === "object" && type !== null && "function" in type; + } + + function convertArgs(types, args) { + const parameters = []; + if (types.length === 0) { + return parameters; + } + + for (let i = 0; i < types.length; i++) { + const type = types[i]; + const arg = args[i]; + ArrayPrototypePush( + parameters, + isPointerType(type) ? new UnsafePointer(arg) : arg, + ); + } + + return parameters; + } + + function unwrapResult(result) { + if ( + ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, result) || + ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, result) || + ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, result) + ) { + // Foreign function given to us, we're passing it on + ArrayPrototypePush(parameters, result.value); + } + return result; + } + + function createInternalCallback(definition, callback) { + const mustUnwrap = isPointerType(definition.result); + return (...args) => { + const convertedArgs = convertArgs(definition.parameters, args); + const result = callback(...convertedArgs); + if (mustUnwrap) { + return unwrapResult(result); + } + return result; + }; + } + class RegisteredCallback { [_rid]; #value; + #internal; definition; callback; @@ -323,13 +377,20 @@ "Invalid ffi RegisteredCallback, cannot be nonblocking", ); } + const needsWrapping = isPointerType(definition.result) || + ArrayPrototypeSome(definition.parameters, isPointerType); + const internalCallback = needsWrapping + ? createInternalCallback(definition, callback) + : callback; + this[_rid] = core.opSync( "op_ffi_register_callback", definition, - callback, + internalCallback, ); this.definition = definition; this.callback = callback; + this.#internal = internalCallback; } call(...args) { @@ -435,7 +496,10 @@ ); if (needsUnpacking) { - return promise.then((result) => unpackResult(resultType, result)); + return PromisePrototypeThen( + promise, + (result) => unpackResult(resultType, result), + ); } return promise; From bcc5caf95bb5e1cb984fc1f5950a4d3ff198776d Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 22:33:23 +0300 Subject: [PATCH 44/64] Improve deno_ffi_callback inline comment --- ext/ffi/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 4b3c42d857e645..48541a8c9eeaac 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -842,9 +842,16 @@ unsafe extern "C" fn deno_ffi_callback( todo!("Call from another thread"); } }); - // Call from main thread: Presume a scope exists already. - // If there wasn't then deno_ffi_callback would be getting called - // by eg. an FFI side interrupt but it's not clear if that's possible. + // Call from main thread. If this callback is being triggered due to a + // function call coming from Deno itself, then this callback will build + // ontop of that stack. + // If this callback is being triggered outside of Deno (for example from a + // signal handler) then this will either create an empty new stack if + // Deno currently has nothing running and is waiting for promises to resolve, + // or will (very incorrectly) build ontop of whatever stack exists. + // The callback will even be called through from a `while (true)` liveloop, but + // it somehow cannot change the values that the loop sees, even if they both + // refer the same `let bool_value`. let mut cb_scope = v8::CallbackScope::new(context); let mut scope = v8::HandleScope::new(&mut cb_scope); let func = callback.open(&mut scope); From 77f3f517641f5502d0309b774288c8e22906c5c4 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 12 Jun 2022 23:07:24 +0300 Subject: [PATCH 45/64] Catch and rethrow errors from FFI callbacks --- ext/ffi/lib.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 48541a8c9eeaac..e42f65819cc6fe 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -853,7 +853,7 @@ unsafe extern "C" fn deno_ffi_callback( // it somehow cannot change the values that the loop sees, even if they both // refer the same `let bool_value`. let mut cb_scope = v8::CallbackScope::new(context); - let mut scope = v8::HandleScope::new(&mut cb_scope); + let mut scope = v8::TryCatch::new(&mut cb_scope); let func = callback.open(&mut scope); let result = result as *mut c_void; let repr: &[*mut ffi_type] = @@ -893,13 +893,8 @@ unsafe extern "C" fn deno_ffi_callback( let call_result = func.call(&mut scope, recv.into(), ¶ms); std::mem::forget(callback); - if (*cif.rtype).type_ == FFI_TYPE_VOID as u16 { - return; - } - if call_result.is_none() { // JS function threw an exception. Set the return value to zero and return. - // TODO: Somehow get the exception back into JS match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 | FFI_TYPE_UINT32 => { // zero is equal for signed and unsigned alike @@ -930,6 +925,12 @@ unsafe extern "C" fn deno_ffi_callback( unreachable!(); } }; + + if scope.has_caught() { + // Rethrow exception to pass it up the call chain, if one exists. + scope.rethrow(); + } + return; } let value = call_result.unwrap(); From ce5b3cb79aca0b6b0986a6ac38561303f8ca3ab6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 13 Jun 2022 23:02:28 +0300 Subject: [PATCH 46/64] Fix idiosyncracies, add some types at least --- cli/dts/lib.deno.unstable.d.ts | 25 +++++++ ext/ffi/00_ffi.js | 126 +++++++++++++++------------------ ext/ffi/lib.rs | 5 +- runtime/js/90_deno_ns.js | 2 +- test_ffi/tests/bench.js | 3 +- test_ffi/tests/test.js | 12 ++-- 6 files changed, 93 insertions(+), 80 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 4422df0488b567..96372ec2c40752 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -519,6 +519,31 @@ declare namespace Deno { >; } + /** + * **UNSTABLE**: Unsafe and new API, beware! + * + * A registered callback for passing JavaScript functions + * as C function pointers to ffi calls. + * + * The function pointer remains valid until the `close()` method is called. + */ + export class RegisteredCallback { + constructor( + definition: Fn, + callback: ( + ...args: StaticForeignFunctionParameters + ) => StaticForeignFunctionResult, + ); + + pointer: UnsafePointer; + definition: Fn; + callback: ( + ...args: StaticForeignFunctionParameters + ) => StaticForeignFunctionResult; + + close(): void; + } + /** A dynamic library resource */ export interface DynamicLibrary { /** All of the registered library along with functions for calling them */ diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 31d50624fa66a3..355605413e5dca 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -9,7 +9,6 @@ ArrayPrototypePush, ArrayPrototypeSome, BigInt, - Error, NumberIsFinite, NumberIsInteger, ObjectDefineProperty, @@ -223,19 +222,24 @@ typeof type === "object" && type !== null && "function" in type ) { if (ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { - ArrayPrototypePush(parameters, arg[_rid]); + // Own registered callback, pass the pointer value + ArrayPrototypePush(parameters, arg.pointer.value); } else if (arg === null) { // nullptr ArrayPrototypePush(parameters, null); } else if ( - ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, arg) || + ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, arg) + ) { + // Foreign function, pass the pointer value + ArrayPrototypePush(parameters, arg.pointer.value); + } else if ( ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg) ) { - // Foreign function given to us, we're passing it on + // Foreign function, pass the pointer value ArrayPrototypePush(parameters, arg.value); } else { throw new TypeError( - "Expected FFI argument to be RegisteredCallback, UnsafeFn", + "Expected FFI argument to be RegisteredCallback, UnsafeFnPointer, UnsafePointer or null", ); } } else { @@ -246,7 +250,13 @@ return parameters; } - function unpackResult(type, result) { + function unpackNonblockingReturnValue(type, result) { + if ( + typeof type === "object" && type !== null && "function" in type || + type === "pointer" + ) { + return new UnsafePointer(unpackU64(result)); + } switch (type) { case "isize": case "i64": @@ -254,8 +264,6 @@ case "usize": case "u64": return unpackU64(result); - case "pointer": - return new UnsafePointer(unpackU64(result)); default: return result; } @@ -285,13 +293,11 @@ ); if ( - resultType === "pointer" || resultType === "u64" || - resultType === "i64" || resultType === "usize" || - resultType === "isize" + isReturnedAsBigInt(resultType) ) { return PromisePrototypeThen( promise, - (result) => unpackResult(resultType, result), + (result) => unpackNonblockingReturnValue(resultType, result), ); } @@ -304,7 +310,7 @@ parameters, ); - if (resultType === "pointer") { + if (isPointerType(resultType)) { return new UnsafePointer(result); } @@ -315,14 +321,17 @@ const UnsafeFnPointerPrototype = UnsafeFnPointer.prototype; - const _rid = Symbol("[[rid]]"); - function isPointerType(type) { return type === "pointer" || typeof type === "object" && type !== null && "function" in type; } - function convertArgs(types, args) { + function isReturnedAsBigInt(type) { + return isPointerType(type) || type === "u64" || type === "i64" || + type === "usize" || type === "isize"; + } + + function prepareRegisteredCallbackParameters(types, args) { const parameters = []; if (types.length === 0) { return parameters; @@ -340,14 +349,24 @@ return parameters; } - function unwrapResult(result) { + function unwrapRegisteredCallbackReturnValue(result) { if ( - ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, result) || - ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, result) || - ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, result) + ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, result) ) { - // Foreign function given to us, we're passing it on + // Foreign function, return the pointer value ArrayPrototypePush(parameters, result.value); + } else if ( + ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, result) + ) { + // Foreign function, return the pointer value + ArrayPrototypePush(parameters, result.pointer.value); + } else if ( + ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, result) + ) { + // Own registered callback, return the pointer value. + // Note that returning the ResourceId here would not work as + // the Rust side code cannot access OpState to get the resource. + ArrayPrototypePush(parameters, result.pointer.value); } return result; } @@ -355,21 +374,26 @@ function createInternalCallback(definition, callback) { const mustUnwrap = isPointerType(definition.result); return (...args) => { - const convertedArgs = convertArgs(definition.parameters, args); + const convertedArgs = prepareRegisteredCallbackParameters( + definition.parameters, + args, + ); const result = callback(...convertedArgs); if (mustUnwrap) { - return unwrapResult(result); + return unwrapRegisteredCallbackReturnValue(result); } return result; }; } + const _rid = Symbol("[[rid]]"); + class RegisteredCallback { [_rid]; - #value; #internal; definition; callback; + pointer; constructor(definition, callback) { if (definition.nonblocking) { @@ -383,59 +407,25 @@ ? createInternalCallback(definition, callback) : callback; - this[_rid] = core.opSync( + const [rid, pointer] = core.opSync( "op_ffi_register_callback", definition, internalCallback, ); + this[_rid] = rid; + this.pointer = new UnsafePointer(unpackU64(pointer)); + this.#internal = internalCallback; this.definition = definition; this.callback = callback; - this.#internal = internalCallback; - } - - call(...args) { - const parameters = prepareArgs( - this.definition.parameters, - args, - ); - if (this.definition.nonblocking) { - throw new Error("Unreachable"); - } else { - const result = core.opSync( - "op_ffi_call_registered_callback", - this[_rid], - parameters, - ); - - if (this.definition.result === "pointer") { - return new UnsafePointer(result); - } - - return result; - } } close() { core.close(this[_rid]); } - - get value() { - if (!this.#value) { - this.#value = core.opSync("op_ffi_ptr_of_cb", this[_rid]); - } - return this.#value; - } } const RegisteredCallbackPrototype = RegisteredCallback.prototype; - function registerCallback(definition, callback) { - if (!definition || !callback) { - throw new TypeError("Invalid arguments"); - } - return new RegisteredCallback(definition, callback); - } - class DynamicLibrary { #rid; symbols = {}; @@ -481,10 +471,7 @@ let fn; if (isNonBlocking) { - const needsUnpacking = resultType === "pointer" || - resultType === "u64" || - resultType === "i64" || resultType === "usize" || - resultType === "isize"; + const needsUnpacking = isReturnedAsBigInt(resultType); fn = (...args) => { const parameters = prepareArgs(types, args); @@ -498,13 +485,14 @@ if (needsUnpacking) { return PromisePrototypeThen( promise, - (result) => unpackResult(resultType, result), + (result) => unpackNonblockingReturnValue(resultType, result), ); } return promise; }; } else { + const mustWrap = isPointerType(resultType); fn = (...args) => { const parameters = prepareArgs(types, args); @@ -515,7 +503,7 @@ parameters, ); - if (resultType === "pointer") { + if (mustWrap) { return new UnsafePointer(result); } @@ -549,7 +537,7 @@ window.__bootstrap.ffi = { dlopen, - registerCallback, + RegisteredCallback, UnsafePointer, UnsafePointerView, UnsafeFnPointer, diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index e42f65819cc6fe..bfb2f3f24886b2 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1050,7 +1050,7 @@ fn op_ffi_register_callback( scope: &mut v8::HandleScope, args: RegisterCallbackArgs, cb: serde_v8::Value<'_>, -) -> Result { +) -> Result<(ResourceId, U32x2), AnyError> { let v8_value = cb.v8_value; let cb = v8::Local::::try_from(v8_value)?; @@ -1078,10 +1078,11 @@ fn op_ffi_register_callback( ); let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, info); + let u3x2 = U32x2::from(*closure.code_ptr() as usize as u64); let resource = RegisteredCallbackResource { closure, info }; - Ok(state.resource_table.add(resource)) + Ok((state.resource_table.add(resource), u3x2)) } #[op] diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index c61fbbadfbd44d..3c24a236710389 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -140,7 +140,7 @@ createHttpClient: __bootstrap.fetch.createHttpClient, http: __bootstrap.http, dlopen: __bootstrap.ffi.dlopen, - registerCallback: __bootstrap.ffi.registerCallback, + RegisteredCallback: __bootstrap.ffi.RegisteredCallback, UnsafePointer: __bootstrap.ffi.UnsafePointer, UnsafePointerView: __bootstrap.ffi.UnsafePointerView, UnsafeFnPointer: __bootstrap.ffi.UnsafeFnPointer, diff --git a/test_ffi/tests/bench.js b/test_ffi/tests/bench.js index f94991c5b96b4a..398732cc8de908 100644 --- a/test_ffi/tests/bench.js +++ b/test_ffi/tests/bench.js @@ -1,8 +1,7 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. // deno-lint-ignore-file -//const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); -const targetDir = "/home/tiffany/dev/rust/deno/target/debug"; +const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); const [libPrefix, libSuffix] = { darwin: ["lib", "dylib"], linux: ["lib", "so"], diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index f9721e91ada547..ec89f69c577c53 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -325,11 +325,11 @@ console.log("Before"); console.log(performance.now() - start < 100); // Test calls with callback parameters -const logCallback = Deno.registerCallback( +const logCallback = new Deno.RegisteredCallback( { parameters: [], result: "void" }, () => console.log("logCallback"), ); -const logManyParametersCallback = Deno.registerCallback({ +const logManyParametersCallback = new Deno.RegisteredCallback({ parameters: [ "u8", "i8", @@ -350,17 +350,17 @@ const logManyParametersCallback = Deno.registerCallback({ view.copyInto(copy_buffer); console.log(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, ...copy_buffer); }); -const returnU8Callback = Deno.registerCallback( +const returnU8Callback = new Deno.RegisteredCallback( { parameters: [], result: "u8" }, () => 8, ); -const returnBufferCallback = Deno.registerCallback({ +const returnBufferCallback = new Deno.RegisteredCallback({ parameters: [], result: "pointer", }, () => { return buffer; }); -const add10Callback = Deno.registerCallback({ +const add10Callback = new Deno.RegisteredCallback({ parameters: ["u8"], result: "u8", }, (value) => value + 10); @@ -374,7 +374,7 @@ dylib.symbols.call_stored_function(); dylib.symbols.store_function_2(add10Callback); dylib.symbols.call_stored_function_2(20); -const nestedCallback = Deno.registerCallback( +const nestedCallback = new Deno.RegisteredCallback( { parameters: [], result: "void" }, () => { dylib.symbols.call_stored_function_2(10); From 3f1177092d0dc57395a4ae0f6d130e280ff00c98 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 13 Jun 2022 23:41:29 +0300 Subject: [PATCH 47/64] Use pointer values for RegisteredCallbacks, removing resource_table usage from argument parsing --- ext/ffi/00_ffi.js | 9 ++-- ext/ffi/lib.rs | 118 +++++++++------------------------------------- 2 files changed, 26 insertions(+), 101 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 355605413e5dca..bebf4bddc7b220 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -14,7 +14,6 @@ ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, PromisePrototypeThen, - Symbol, TypeError, Uint8Array, } = window.__bootstrap.primordials; @@ -386,10 +385,8 @@ }; } - const _rid = Symbol("[[rid]]"); - class RegisteredCallback { - [_rid]; + #rid; #internal; definition; callback; @@ -412,7 +409,7 @@ definition, internalCallback, ); - this[_rid] = rid; + this.#rid = rid; this.pointer = new UnsafePointer(unpackU64(pointer)); this.#internal = internalCallback; this.definition = definition; @@ -420,7 +417,7 @@ } close() { - core.close(this[_rid]); + core.close(this.#rid); } } diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index bfb2f3f24886b2..ecc87c5f9a3782 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -12,8 +12,6 @@ use deno_core::op; use deno_core::serde_json::json; use deno_core::serde_json::Value; -//use deno_core::serde_json::json; -//use deno_core::serde_json::Value; use deno_core::serde_v8; use deno_core::v8; use deno_core::Extension; @@ -27,7 +25,6 @@ use libffi::middle::Cif; use libffi::raw::*; use serde::Deserialize; use serde::Serialize; -//use serde::Serialize; use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; @@ -205,7 +202,6 @@ pub fn init(unstable: bool) -> Extension { op_ffi_read_f64::decl::

(), op_ffi_register_callback::decl(), op_ffi_deregister_callback::decl(), - op_ffi_call_registered_callback::decl(), ]) .state(move |state| { // Stolen from deno_webgpu, is there a better option? @@ -572,7 +568,6 @@ fn ffi_parse_args<'scope>( scope: &mut v8::HandleScope<'scope>, args: serde_v8::Value<'scope>, parameter_types: &[NativeType], - resource_table: &deno_core::ResourceTable, ) -> Result, AnyError> where 'scope: 'scope, @@ -714,20 +709,15 @@ where if value.is_null() { let value: *const u8 = ptr::null(); ffi_args.push(NativeValue { pointer: value }) - } else if value.is_number() { - let value: ResourceId = value.number_value(scope).unwrap() as u32; - let rc = resource_table.get::(value)?; - let function = *rc.closure.code_ptr(); - ffi_args.push(NativeValue { - pointer: function as usize as *const u8, - }); } else if value.is_big_int() { // Do we support this? This would be a foreign function pointer given to us by an FFI library. let value = v8::Local::::try_from(value).unwrap(); let value = value.u64_value().0 as *const u8; ffi_args.push(NativeValue { pointer: value }); } else { - return Err(type_error("Invalid FFI function type, expected null, RegisteredCallback, or BigInt")); + return Err(type_error( + "Invalid FFI function type, expected null, or BigInt", + )); } } } @@ -820,8 +810,6 @@ struct CallbackInfo { pub callback: NonNull, pub context: NonNull, pub isolate: *mut v8::Isolate, - pub parameters: Vec, - pub result: NativeType, } unsafe extern "C" fn deno_ffi_callback( @@ -959,20 +947,11 @@ unsafe extern "C" fn deno_ffi_callback( .expect("Unable to deserialize result parameter."); let value: &[u8] = &value[..]; *(result as *mut *const u8) = value.as_ptr(); - } else if value.is_big_int() | value.is_big_int_object() { + } else if value.is_big_int() { let value = v8::Local::::try_from(value).unwrap(); *(result as *mut u64) = value.u64_value().0; } else if value.is_null() { *(result as *mut *const c_void) = ptr::null(); - } else if value.is_object() { - let value = v8::Local::::try_from(value).unwrap(); - let value_key = v8::String::new(&mut scope, "value").unwrap(); - let value_field = value - .get(&mut scope, value_key.into()) - .expect("Unable to deserialize result parameter."); - let value = v8::Local::::try_from(value_field) - .expect("Unable to deserialize result parameter."); - *(result as *mut u64) = value.u64_value().0; } else { // Fallthrough: Probably someone returned a number but this could // also be eg. a string. This is essentially UB. @@ -1013,7 +992,7 @@ unsafe extern "C" fn deno_ffi_callback( as u32; } FFI_TYPE_SINT64 => { - if value.is_big_int() | value.is_big_int_object() { + if value.is_big_int() { let value = v8::Local::::try_from(value).unwrap(); *(result as *mut u64) = value.u64_value().0; } else { @@ -1068,8 +1047,6 @@ fn op_ffi_register_callback( callback, context, isolate, - parameters: args.parameters.clone(), - result: args.result, })) }; let cif = Cif::new( @@ -1097,42 +1074,6 @@ fn op_ffi_deregister_callback( Ok(()) } -#[op(v8)] -fn op_ffi_call_registered_callback<'a>( - scope: &mut v8::HandleScope<'a>, - state: Rc>, - rid: ResourceId, - args: serde_v8::Value<'a>, -) -> Result, AnyError> -where - 'a: 'a, -{ - let (fn_ptr, info, call_args) = { - let state = &mut state.borrow(); - let resource = state - .resource_table - .get::(rid)?; - let info: &CallbackInfo = unsafe { &*resource.info }; - - let fn_ptr = *resource.closure.code_ptr() as *mut c_void; - let call_args = - ffi_parse_args(scope, args, &info.parameters, &state.resource_table)?; - (fn_ptr, info, call_args) - }; - let cif = libffi::middle::Cif::new( - info - .parameters - .clone() - .into_iter() - .map(libffi::middle::Type::from), - info.result.into(), - ); - let fun_ptr = libffi::middle::CodePtr::from_ptr(fn_ptr); - let result = - ffi_call(call_args, &cif, fun_ptr, &info.parameters, info.result)?; - Ok(result.to_v8(scope, info.result)) -} - #[op(v8)] fn op_ffi_call_ptr( scope: &mut v8::HandleScope<'scope>, @@ -1145,16 +1086,15 @@ where FP: FfiPermissions + 'static, { check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - - let symbol = PtrSymbol::new(pointer, &def); - let call_args = { + { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); permissions.check(None)?; - - ffi_parse_args(scope, parameters, &def.parameters, &state.resource_table)? }; + let symbol = PtrSymbol::new(pointer, &def); + let call_args = ffi_parse_args(scope, parameters, &def.parameters)?; + let result = ffi_call( call_args, &symbol.cif, @@ -1178,16 +1118,15 @@ where FP: FfiPermissions + 'static, { check_unstable2(&state, "Deno.UnsafeFnPointer#call"); - - let symbol = PtrSymbol::new(pointer, &def); - let call_args = { + { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); permissions.check(None)?; - - ffi_parse_args(scope, parameters, &def.parameters, &state.resource_table)? }; + let symbol = PtrSymbol::new(pointer, &def); + let call_args = ffi_parse_args(scope, parameters, &def.parameters)?; + let join_handle = tokio::task::spawn_blocking(move || { let PtrSymbol { cif, ptr } = symbol.clone(); ffi_call(call_args, &cif, ptr, &def.parameters, def.result) @@ -1293,25 +1232,19 @@ fn op_ffi_call<'scope>( symbol: String, parameters: serde_v8::Value<'scope>, ) -> Result, AnyError> { - let (symbol, call_args) = { + let symbol = { let state = &mut state.borrow(); let resource = state.resource_table.get::(rid)?; - let symbol = resource + resource .symbols .get(&symbol) .ok_or_else(|| type_error("Invalid FFI symbol name"))? - .clone(); - - let call_args = ffi_parse_args( - scope, - parameters, - &symbol.parameter_types, - &state.resource_table, - )?; - (symbol, call_args) + .clone() }; + let call_args = ffi_parse_args(scope, parameters, &symbol.parameter_types)?; + let result = ffi_call( call_args, &symbol.cif, @@ -1332,23 +1265,18 @@ fn op_ffi_call_nonblocking<'scope>( symbol: String, parameters: serde_v8::Value<'scope>, ) -> Result> + 'static, AnyError> { - let (symbol, call_args) = { + let symbol = { let state = state.borrow(); let resource = state.resource_table.get::(rid)?; let symbols = &resource.symbols; - let symbol = symbols + symbols .get(&symbol) .ok_or_else(|| type_error("Invalid FFI symbol name"))? - .clone(); - let call_args = ffi_parse_args( - scope, - parameters, - &symbol.parameter_types, - &state.resource_table, - )?; - (symbol, call_args) + .clone() }; + let call_args = ffi_parse_args(scope, parameters, &symbol.parameter_types)?; + let result_type = symbol.result_type; let join_handle = tokio::task::spawn_blocking(move || { let Symbol { From 53bfa8fc1e426529f433f09da02d731515b4657a Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 13 Jun 2022 23:48:37 +0300 Subject: [PATCH 48/64] Fix errors in i64 / u64 callback return values --- ext/ffi/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index ecc87c5f9a3782..c9dbd685cb54cc 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -994,7 +994,7 @@ unsafe extern "C" fn deno_ffi_callback( FFI_TYPE_SINT64 => { if value.is_big_int() { let value = v8::Local::::try_from(value).unwrap(); - *(result as *mut u64) = value.u64_value().0; + *(result as *mut i64) = value.i64_value().0; } else { *(result as *mut i64) = value .number_value(&mut scope) @@ -1003,10 +1003,15 @@ unsafe extern "C" fn deno_ffi_callback( } } FFI_TYPE_UINT64 => { - *(result as *mut u64) = value - .number_value(&mut scope) - .expect("Unable to deserialize result parameter.") - as u64; + if value.is_big_int() { + let value = v8::Local::::try_from(value).unwrap(); + *(result as *mut u64) = value.u64_value().0; + } else { + *(result as *mut u64) = value + .number_value(&mut scope) + .expect("Unable to deserialize result parameter.") + as u64; + } } FFI_TYPE_VOID => { // nop From 7d75f7db68228a38fa77b9c8c4562b4e00a7883a Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 14 Jun 2022 17:26:53 +0300 Subject: [PATCH 49/64] Review comments --- ext/ffi/lib.rs | 33 ++++++++++++++------------------- test.js | 8 -------- 2 files changed, 14 insertions(+), 27 deletions(-) delete mode 100644 test.js diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index c9dbd685cb54cc..ee7dea6ab32f66 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -710,7 +710,6 @@ where let value: *const u8 = ptr::null(); ffi_args.push(NativeValue { pointer: value }) } else if value.is_big_int() { - // Do we support this? This would be a foreign function pointer given to us by an FFI library. let value = v8::Local::::try_from(value).unwrap(); let value = value.u64_value().0 as *const u8; ffi_args.push(NativeValue { pointer: value }); @@ -826,8 +825,9 @@ unsafe extern "C" fn deno_ffi_callback( >(info.context); IS_EVENT_LOOP_THREAD.with(|is_event_loop_thread| { if !(*is_event_loop_thread.borrow()) { - // Call from another thread, PANIC IN THE DISCO! - todo!("Call from another thread"); + // Call from another thread, not yet supported. + eprintln!("Calling Deno FFI's registered callbacks from other threads is not supported"); + std::process::exit(1); } }); // Call from main thread. If this callback is being triggered due to a @@ -1038,28 +1038,23 @@ fn op_ffi_register_callback( let v8_value = cb.v8_value; let cb = v8::Local::::try_from(v8_value)?; - let info = { - let isolate_ptr: *mut v8::Isolate = { - let isolate: &mut v8::Isolate = &mut *scope; - isolate - }; - let isolate = unsafe { &mut *isolate_ptr }; - let callback = v8::Global::new(isolate, cb).into_raw(); - let context = - v8::Global::new(isolate, scope.get_current_context()).into_raw(); - - Box::leak(Box::new(CallbackInfo { - callback, - context, - isolate, - })) - }; + let isolate: *mut v8::Isolate = &mut *scope as &mut v8::Isolate; + let callback = v8::Global::new(scope, cb).into_raw(); + let current_context = scope.get_current_context(); + let context = v8::Global::new(scope, current_context).into_raw(); + + let info = Box::leak(Box::new(CallbackInfo { + callback, + context, + isolate, + })); let cif = Cif::new( args.parameters.into_iter().map(libffi::middle::Type::from), libffi::middle::Type::from(args.result), ); let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, info); + // TODO(@aapoalas): Use BigInt later let u3x2 = U32x2::from(*closure.code_ptr() as usize as u64); let resource = RegisteredCallbackResource { closure, info }; diff --git a/test.js b/test.js deleted file mode 100644 index b5e0ace1715e71..00000000000000 --- a/test.js +++ /dev/null @@ -1,8 +0,0 @@ -const callback = Deno.core.opSync("op_ffi_register_callback", { - parameters: [], - result: "void", -}, function daCallback() { - console.log("Called"); -}); - -Deno.core.opSync("test_registered_callback", callback); From 3b8a881d3001b9246954cbf3dab7bbfd57c029c9 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 14 Jun 2022 22:46:16 +0300 Subject: [PATCH 50/64] Rename RegisteredCallback to UnsafeCallback --- cli/dts/lib.deno.unstable.d.ts | 4 ++-- ext/ffi/00_ffi.js | 22 +++++++++++----------- ext/ffi/lib.rs | 16 ++++++++-------- runtime/js/90_deno_ns.js | 2 +- test_ffi/tests/test.js | 12 ++++++------ 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 96372ec2c40752..64e365a97863d8 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -522,12 +522,12 @@ declare namespace Deno { /** * **UNSTABLE**: Unsafe and new API, beware! * - * A registered callback for passing JavaScript functions + * An unsafe function pointer for passing JavaScript functions * as C function pointers to ffi calls. * * The function pointer remains valid until the `close()` method is called. */ - export class RegisteredCallback { + export class UnsafeCallback { constructor( definition: Fn, callback: ( diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index bebf4bddc7b220..dbaad1b8eb8f9d 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -220,7 +220,7 @@ } else if ( typeof type === "object" && type !== null && "function" in type ) { - if (ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, arg)) { + if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, arg)) { // Own registered callback, pass the pointer value ArrayPrototypePush(parameters, arg.pointer.value); } else if (arg === null) { @@ -238,7 +238,7 @@ ArrayPrototypePush(parameters, arg.value); } else { throw new TypeError( - "Expected FFI argument to be RegisteredCallback, UnsafeFnPointer, UnsafePointer or null", + "Expected FFI argument to be UnsafeCallback, UnsafeFnPointer, UnsafePointer or null", ); } } else { @@ -330,7 +330,7 @@ type === "usize" || type === "isize"; } - function prepareRegisteredCallbackParameters(types, args) { + function prepareUnsafeCallbackParameters(types, args) { const parameters = []; if (types.length === 0) { return parameters; @@ -348,7 +348,7 @@ return parameters; } - function unwrapRegisteredCallbackReturnValue(result) { + function unwrapUnsafeCallbackReturnValue(result) { if ( ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, result) ) { @@ -360,7 +360,7 @@ // Foreign function, return the pointer value ArrayPrototypePush(parameters, result.pointer.value); } else if ( - ObjectPrototypeIsPrototypeOf(RegisteredCallbackPrototype, result) + ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, result) ) { // Own registered callback, return the pointer value. // Note that returning the ResourceId here would not work as @@ -373,19 +373,19 @@ function createInternalCallback(definition, callback) { const mustUnwrap = isPointerType(definition.result); return (...args) => { - const convertedArgs = prepareRegisteredCallbackParameters( + const convertedArgs = prepareUnsafeCallbackParameters( definition.parameters, args, ); const result = callback(...convertedArgs); if (mustUnwrap) { - return unwrapRegisteredCallbackReturnValue(result); + return unwrapUnsafeCallbackReturnValue(result); } return result; }; } - class RegisteredCallback { + class UnsafeCallback { #rid; #internal; definition; @@ -395,7 +395,7 @@ constructor(definition, callback) { if (definition.nonblocking) { throw new TypeError( - "Invalid ffi RegisteredCallback, cannot be nonblocking", + "Invalid UnsafeCallback, cannot be nonblocking", ); } const needsWrapping = isPointerType(definition.result) || @@ -421,7 +421,7 @@ } } - const RegisteredCallbackPrototype = RegisteredCallback.prototype; + const UnsafeCallbackPrototype = UnsafeCallback.prototype; class DynamicLibrary { #rid; @@ -534,7 +534,7 @@ window.__bootstrap.ffi = { dlopen, - RegisteredCallback, + UnsafeCallback, UnsafePointer, UnsafePointerView, UnsafeFnPointer, diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index ee7dea6ab32f66..8ec0ace16f78f6 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -786,12 +786,12 @@ fn ffi_call( }) } -struct RegisteredCallbackResource { +struct UnsafeCallbackResource { closure: libffi::middle::Closure<'static>, info: *const CallbackInfo, } -impl Resource for RegisteredCallbackResource { +impl Resource for UnsafeCallbackResource { fn name(&self) -> Cow { "registeredcallback".into() } @@ -826,7 +826,9 @@ unsafe extern "C" fn deno_ffi_callback( IS_EVENT_LOOP_THREAD.with(|is_event_loop_thread| { if !(*is_event_loop_thread.borrow()) { // Call from another thread, not yet supported. - eprintln!("Calling Deno FFI's registered callbacks from other threads is not supported"); + eprintln!( + "Calling Deno FFI's callbacks from other threads is not supported" + ); std::process::exit(1); } }); @@ -1057,7 +1059,7 @@ fn op_ffi_register_callback( // TODO(@aapoalas): Use BigInt later let u3x2 = U32x2::from(*closure.code_ptr() as usize as u64); - let resource = RegisteredCallbackResource { closure, info }; + let resource = UnsafeCallbackResource { closure, info }; Ok((state.resource_table.add(resource), u3x2)) } @@ -1069,7 +1071,7 @@ fn op_ffi_deregister_callback( ) -> Result<(), AnyError> { state .resource_table - .take::(rid)? + .take::(rid)? .close(); Ok(()) } @@ -1327,9 +1329,7 @@ where let permissions = state.borrow_mut::(); permissions.check(None)?; - let resource = state - .resource_table - .get::(rid)?; + let resource = state.resource_table.get::(rid)?; let big_int: v8::Local = v8::BigInt::new_from_u64( scope, diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 3c24a236710389..fe02b635474ae5 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -140,7 +140,7 @@ createHttpClient: __bootstrap.fetch.createHttpClient, http: __bootstrap.http, dlopen: __bootstrap.ffi.dlopen, - RegisteredCallback: __bootstrap.ffi.RegisteredCallback, + UnsafeCallback: __bootstrap.ffi.UnsafeCallback, UnsafePointer: __bootstrap.ffi.UnsafePointer, UnsafePointerView: __bootstrap.ffi.UnsafePointerView, UnsafeFnPointer: __bootstrap.ffi.UnsafeFnPointer, diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index ec89f69c577c53..43b2f760e49463 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -325,11 +325,11 @@ console.log("Before"); console.log(performance.now() - start < 100); // Test calls with callback parameters -const logCallback = new Deno.RegisteredCallback( +const logCallback = new Deno.UnsafeCallback( { parameters: [], result: "void" }, () => console.log("logCallback"), ); -const logManyParametersCallback = new Deno.RegisteredCallback({ +const logManyParametersCallback = new Deno.UnsafeCallback({ parameters: [ "u8", "i8", @@ -350,17 +350,17 @@ const logManyParametersCallback = new Deno.RegisteredCallback({ view.copyInto(copy_buffer); console.log(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, ...copy_buffer); }); -const returnU8Callback = new Deno.RegisteredCallback( +const returnU8Callback = new Deno.UnsafeCallback( { parameters: [], result: "u8" }, () => 8, ); -const returnBufferCallback = new Deno.RegisteredCallback({ +const returnBufferCallback = new Deno.UnsafeCallback({ parameters: [], result: "pointer", }, () => { return buffer; }); -const add10Callback = new Deno.RegisteredCallback({ +const add10Callback = new Deno.UnsafeCallback({ parameters: ["u8"], result: "u8", }, (value) => value + 10); @@ -374,7 +374,7 @@ dylib.symbols.call_stored_function(); dylib.symbols.store_function_2(add10Callback); dylib.symbols.call_stored_function_2(20); -const nestedCallback = new Deno.RegisteredCallback( +const nestedCallback = new Deno.UnsafeCallback( { parameters: [], result: "void" }, () => { dylib.symbols.call_stored_function_2(10); From 6ac347135d67b55d140aa8de461ff186faa2614c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 14 Jun 2022 23:16:17 +0300 Subject: [PATCH 51/64] Use specific APIs for ints --- ext/ffi/lib.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 8ec0ace16f78f6..b36247fa567951 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -336,32 +336,37 @@ impl NativeValue { } NativeType::U8 => { let local_value: v8::Local = - v8::Number::new(scope, unsafe { self.u8_value } as f64).into(); + v8::Integer::new_from_unsigned(scope, unsafe { self.u8_value } + as u32) + .into(); local_value.into() } NativeType::I8 => { let local_value: v8::Local = - v8::Number::new(scope, unsafe { self.i8_value } as f64).into(); + v8::Integer::new(scope, unsafe { self.i8_value } as i32).into(); local_value.into() } NativeType::U16 => { let local_value: v8::Local = - v8::Number::new(scope, unsafe { self.u16_value } as f64).into(); + v8::Integer::new_from_unsigned(scope, unsafe { self.u16_value } + as u32) + .into(); local_value.into() } NativeType::I16 => { let local_value: v8::Local = - v8::Number::new(scope, unsafe { self.i16_value } as f64).into(); + v8::Integer::new(scope, unsafe { self.i16_value } as i32).into(); local_value.into() } NativeType::U32 => { let local_value: v8::Local = - v8::Number::new(scope, unsafe { self.u32_value } as f64).into(); + v8::Integer::new_from_unsigned(scope, unsafe { self.u32_value }) + .into(); local_value.into() } NativeType::I32 => { let local_value: v8::Local = - v8::Number::new(scope, unsafe { self.i32_value } as f64).into(); + v8::Integer::new(scope, unsafe { self.i32_value }).into(); local_value.into() } NativeType::U64 => { @@ -589,42 +594,42 @@ where } NativeType::U8 => { let value = value - .number_value(scope) + .uint32_value(scope) .ok_or_else(|| type_error("Invalid FFI u8 type, expected number"))? as u8; ffi_args.push(NativeValue { u8_value: value }); } NativeType::I8 => { let value = value - .number_value(scope) + .int32_value(scope) .ok_or_else(|| type_error("Invalid FFI i8 type, expected number"))? as i8; ffi_args.push(NativeValue { i8_value: value }); } NativeType::U16 => { let value = value - .number_value(scope) + .uint32_value(scope) .ok_or_else(|| type_error("Invalid FFI u16 type, expected number"))? as u16; ffi_args.push(NativeValue { u16_value: value }); } NativeType::I16 => { let value = value - .number_value(scope) + .int32_value(scope) .ok_or_else(|| type_error("Invalid FFI i16 type, expected number"))? as i16; ffi_args.push(NativeValue { i16_value: value }); } NativeType::U32 => { let value = value - .number_value(scope) + .uint32_value(scope) .ok_or_else(|| type_error("Invalid FFI u32 type, expected number"))? as u32; ffi_args.push(NativeValue { u32_value: value }); } NativeType::I32 => { let value = value - .number_value(scope) + .int32_value(scope) .ok_or_else(|| type_error("Invalid FFI i32 type, expected number"))? as i32; ffi_args.push(NativeValue { i32_value: value }); From a78146353755f3e43b82d880b5d4bd3bb4d6446a Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Tue, 14 Jun 2022 23:30:55 +0300 Subject: [PATCH 52/64] Use specific APIs for ints in callbacks, avoid serde_v8 in callback params --- ext/ffi/lib.rs | 72 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index b36247fa567951..c7f555ac38111a 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -858,30 +858,53 @@ unsafe extern "C" fn deno_ffi_callback( let mut params: Vec> = vec![]; for (repr, val) in repr.iter().zip(vals) { - let value = match (*(*repr)).type_ as _ { - FFI_TYPE_INT => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), - FFI_TYPE_FLOAT => serde_v8::to_v8(&mut scope, *((*val) as *const f32)), - FFI_TYPE_DOUBLE => serde_v8::to_v8(&mut scope, *((*val) as *const f64)), - FFI_TYPE_SINT8 => serde_v8::to_v8(&mut scope, *((*val) as *const i8)), - FFI_TYPE_UINT8 => serde_v8::to_v8(&mut scope, *((*val) as *const u8)), - FFI_TYPE_SINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const i16)), - FFI_TYPE_UINT16 => serde_v8::to_v8(&mut scope, *((*val) as *const u16)), - FFI_TYPE_SINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const i32)), - FFI_TYPE_UINT32 => serde_v8::to_v8(&mut scope, *((*val) as *const u32)), + let value: v8::Local = match (*(*repr)).type_ as _ { + FFI_TYPE_FLOAT => { + let value = *((*val) as *const f32); + v8::Number::new(&mut scope, value as f64).into() + } + FFI_TYPE_DOUBLE => { + let value = *((*val) as *const f64); + v8::Number::new(&mut scope, value).into() + } + FFI_TYPE_SINT8 => { + let value = *((*val) as *const i8); + v8::Integer::new(&mut scope, value as i32).into() + } + FFI_TYPE_UINT8 => { + let value = *((*val) as *const u8); + v8::Integer::new_from_unsigned(&mut scope, value as u32).into() + } + FFI_TYPE_SINT16 => { + let value = *((*val) as *const i16); + v8::Integer::new(&mut scope, value as i32).into() + } + FFI_TYPE_UINT16 => { + let value = *((*val) as *const u16); + v8::Integer::new_from_unsigned(&mut scope, value as u32).into() + } + FFI_TYPE_INT | FFI_TYPE_SINT32 => { + let value = *((*val) as *const i32); + v8::Integer::new(&mut scope, value).into() + } + FFI_TYPE_UINT32 => { + let value = *((*val) as *const u32); + v8::Integer::new_from_unsigned(&mut scope, value).into() + } FFI_TYPE_SINT64 => { let result = *((*val) as *const i64); - Ok(v8::BigInt::new_from_i64(&mut scope, result).into()) + v8::BigInt::new_from_i64(&mut scope, result).into() } FFI_TYPE_POINTER | FFI_TYPE_STRUCT | FFI_TYPE_UINT64 => { let result = *((*val) as *const u64); - Ok(v8::BigInt::new_from_u64(&mut scope, result).into()) + v8::BigInt::new_from_u64(&mut scope, result).into() } - FFI_TYPE_VOID => serde_v8::to_v8(&mut scope, ()), + FFI_TYPE_VOID => v8::undefined(&mut scope).into(), _ => { unreachable!() } }; - params.push(value.expect("Unable to serialize callback parameter.")); + params.push(value); } let recv = v8::undefined(&mut scope); @@ -933,7 +956,7 @@ unsafe extern "C" fn deno_ffi_callback( match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 => { *(result as *mut i32) = value - .number_value(&mut scope) + .int32_value(&mut scope) .expect("Unable to deserialize result parameter.") as i32; } @@ -963,40 +986,39 @@ unsafe extern "C" fn deno_ffi_callback( // Fallthrough: Probably someone returned a number but this could // also be eg. a string. This is essentially UB. *(result as *mut u64) = value - .number_value(&mut scope) + .integer_value(&mut scope) .expect("Unable to deserialize result parameter.") as u64; } } FFI_TYPE_SINT8 => { *(result as *mut i8) = value - .number_value(&mut scope) + .int32_value(&mut scope) .expect("Unable to deserialize result parameter.") as i8; } FFI_TYPE_UINT8 => { *(result as *mut u8) = value - .number_value(&mut scope) + .uint32_value(&mut scope) .expect("Unable to deserialize result parameter.") as u8; } FFI_TYPE_SINT16 => { *(result as *mut i16) = value - .number_value(&mut scope) + .int32_value(&mut scope) .expect("Unable to deserialize result parameter.") as i16; } FFI_TYPE_UINT16 => { *(result as *mut u16) = value - .number_value(&mut scope) + .uint32_value(&mut scope) .expect("Unable to deserialize result parameter.") as u16; } FFI_TYPE_UINT32 => { *(result as *mut u32) = value - .number_value(&mut scope) - .expect("Unable to deserialize result parameter.") - as u32; + .uint32_value(&mut scope) + .expect("Unable to deserialize result parameter."); } FFI_TYPE_SINT64 => { if value.is_big_int() { @@ -1004,7 +1026,7 @@ unsafe extern "C" fn deno_ffi_callback( *(result as *mut i64) = value.i64_value().0; } else { *(result as *mut i64) = value - .number_value(&mut scope) + .integer_value(&mut scope) .expect("Unable to deserialize result parameter.") as i64; } @@ -1015,7 +1037,7 @@ unsafe extern "C" fn deno_ffi_callback( *(result as *mut u64) = value.u64_value().0; } else { *(result as *mut u64) = value - .number_value(&mut scope) + .integer_value(&mut scope) .expect("Unable to deserialize result parameter.") as u64; } From 0c678526e4faf00f8f3c0ebacc098aa3aedc2e01 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 15 Jun 2022 13:31:14 +0300 Subject: [PATCH 53/64] Implicit drop Co-authored-by: Divy Srivastava --- ext/ffi/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index c7f555ac38111a..1c37de65ed4f9c 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -806,7 +806,6 @@ impl Resource for UnsafeCallbackResource { let isolate = unsafe { info.isolate.as_mut().unwrap() }; unsafe { v8::Global::from_raw(isolate, info.callback) }; unsafe { v8::Global::from_raw(isolate, info.context) }; - drop(self) } } From d936617f653fda5522d6a496f55e4b8e9dde87f1 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 15 Jun 2022 18:12:19 +0300 Subject: [PATCH 54/64] Review fixes, light renaming --- ext/ffi/00_ffi.js | 2 +- ext/ffi/lib.rs | 103 ++++++++++++++++++++-------------------------- 2 files changed, 45 insertions(+), 60 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index dbaad1b8eb8f9d..de6b7599302777 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -405,7 +405,7 @@ : callback; const [rid, pointer] = core.opSync( - "op_ffi_register_callback", + "op_ffi_unsafe_callback_create", definition, internalCallback, ); diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 1c37de65ed4f9c..951cec911d3b3a 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -37,7 +37,7 @@ use std::ptr; use std::rc::Rc; thread_local! { - static IS_EVENT_LOOP_THREAD: RefCell = RefCell::new(false); + static IS_ISOLATE_THREAD: RefCell = RefCell::new(false); } pub struct Unstable(pub bool); @@ -122,7 +122,7 @@ impl DynamicLibraryResource { name: String, foreign_fn: ForeignFunction, ) -> Result<(), AnyError> { - IS_EVENT_LOOP_THREAD.with(|s| s.replace(true)); + IS_ISOLATE_THREAD.with(|s| s.replace(true)); let symbol = match &foreign_fn.name { Some(symbol) => symbol, None => &name, @@ -200,8 +200,7 @@ pub fn init(unstable: bool) -> Extension { op_ffi_read_u64::decl::

(), op_ffi_read_f32::decl::

(), op_ffi_read_f64::decl::

(), - op_ffi_register_callback::decl(), - op_ffi_deregister_callback::decl(), + op_ffi_unsafe_callback_create::decl(), ]) .state(move |state| { // Stolen from deno_webgpu, is there a better option? @@ -295,36 +294,38 @@ impl NativeValue { } } - fn to_value(&self, native_type: NativeType) -> Value { + // SAFETY: native_type must correspond to the type of value represented by the union field + unsafe fn to_value(&self, native_type: NativeType) -> Value { match native_type { NativeType::Void => Value::Null, - NativeType::U8 => Value::from(unsafe { self.u8_value }), - NativeType::I8 => Value::from(unsafe { self.i8_value }), - NativeType::U16 => Value::from(unsafe { self.u16_value }), - NativeType::I16 => Value::from(unsafe { self.i16_value }), - NativeType::U32 => Value::from(unsafe { self.u32_value }), - NativeType::I32 => Value::from(unsafe { self.i32_value }), + NativeType::U8 => Value::from(self.u8_value), + NativeType::I8 => Value::from(self.i8_value), + NativeType::U16 => Value::from(self.u16_value), + NativeType::I16 => Value::from(self.i16_value), + NativeType::U32 => Value::from(self.u32_value), + NativeType::I32 => Value::from(self.i32_value), NativeType::U64 => { - json!(U32x2::from(unsafe { self.u64_value })) + json!(U32x2::from(self.u64_value)) } NativeType::I64 => { - json!(U32x2::from(unsafe { self.i64_value } as u64)) + json!(U32x2::from(self.i64_value as u64)) } NativeType::USize => { - json!(U32x2::from(unsafe { self.usize_value } as u64)) + json!(U32x2::from(self.usize_value as u64)) } NativeType::ISize => { - json!(U32x2::from(unsafe { self.isize_value } as u64)) + json!(U32x2::from(self.isize_value as u64)) } - NativeType::F32 => Value::from(unsafe { self.f32_value }), - NativeType::F64 => Value::from(unsafe { self.f64_value }), + NativeType::F32 => Value::from(self.f32_value), + NativeType::F64 => Value::from(self.f64_value), NativeType::Pointer | NativeType::Function {} => { - json!(U32x2::from(unsafe { self.pointer } as u64)) + json!(U32x2::from(self.pointer as u64)) } } } - fn to_v8<'scope>( + // SAFETY: native_type must correspond to the type of value represented by the union field + unsafe fn to_v8<'scope>( &self, scope: &mut v8::HandleScope<'scope>, native_type: NativeType, @@ -336,75 +337,67 @@ impl NativeValue { } NativeType::U8 => { let local_value: v8::Local = - v8::Integer::new_from_unsigned(scope, unsafe { self.u8_value } - as u32) - .into(); + v8::Integer::new_from_unsigned(scope, self.u8_value as u32).into(); local_value.into() } NativeType::I8 => { let local_value: v8::Local = - v8::Integer::new(scope, unsafe { self.i8_value } as i32).into(); + v8::Integer::new(scope, self.i8_value as i32).into(); local_value.into() } NativeType::U16 => { let local_value: v8::Local = - v8::Integer::new_from_unsigned(scope, unsafe { self.u16_value } - as u32) - .into(); + v8::Integer::new_from_unsigned(scope, self.u16_value as u32).into(); local_value.into() } NativeType::I16 => { let local_value: v8::Local = - v8::Integer::new(scope, unsafe { self.i16_value } as i32).into(); + v8::Integer::new(scope, self.i16_value as i32).into(); local_value.into() } NativeType::U32 => { let local_value: v8::Local = - v8::Integer::new_from_unsigned(scope, unsafe { self.u32_value }) - .into(); + v8::Integer::new_from_unsigned(scope, self.u32_value).into(); local_value.into() } NativeType::I32 => { let local_value: v8::Local = - v8::Integer::new(scope, unsafe { self.i32_value }).into(); + v8::Integer::new(scope, self.i32_value).into(); local_value.into() } NativeType::U64 => { let local_value: v8::Local = - v8::BigInt::new_from_u64(scope, unsafe { self.u64_value }).into(); + v8::BigInt::new_from_u64(scope, self.u64_value).into(); local_value.into() } NativeType::I64 => { let local_value: v8::Local = - v8::BigInt::new_from_i64(scope, unsafe { self.i64_value }).into(); + v8::BigInt::new_from_i64(scope, self.i64_value).into(); local_value.into() } NativeType::USize => { let local_value: v8::Local = - v8::BigInt::new_from_u64(scope, unsafe { self.usize_value } as u64) - .into(); + v8::BigInt::new_from_u64(scope, self.usize_value as u64).into(); local_value.into() } NativeType::ISize => { let local_value: v8::Local = - v8::BigInt::new_from_i64(scope, unsafe { self.isize_value } as i64) - .into(); + v8::BigInt::new_from_i64(scope, self.isize_value as i64).into(); local_value.into() } NativeType::F32 => { let local_value: v8::Local = - v8::Number::new(scope, unsafe { self.f32_value } as f64).into(); + v8::Number::new(scope, self.f32_value as f64).into(); local_value.into() } NativeType::F64 => { let local_value: v8::Local = - v8::Number::new(scope, unsafe { self.f64_value }).into(); + v8::Number::new(scope, self.f64_value).into(); local_value.into() } NativeType::Pointer | NativeType::Function {} => { let local_value: v8::Local = - v8::BigInt::new_from_u64(scope, unsafe { self.pointer } as u64) - .into(); + v8::BigInt::new_from_u64(scope, self.pointer as u64).into(); local_value.into() } } @@ -798,7 +791,7 @@ struct UnsafeCallbackResource { impl Resource for UnsafeCallbackResource { fn name(&self) -> Cow { - "registeredcallback".into() + "unsafecallback".into() } fn close(self: Rc) { @@ -827,7 +820,7 @@ unsafe extern "C" fn deno_ffi_callback( NonNull, v8::Local, >(info.context); - IS_EVENT_LOOP_THREAD.with(|is_event_loop_thread| { + IS_ISOLATE_THREAD.with(|is_event_loop_thread| { if !(*is_event_loop_thread.borrow()) { // Call from another thread, not yet supported. eprintln!( @@ -1057,7 +1050,7 @@ struct RegisterCallbackArgs { } #[op(v8)] -fn op_ffi_register_callback( +fn op_ffi_unsafe_callback_create( state: &mut deno_core::OpState, scope: &mut v8::HandleScope, args: RegisterCallbackArgs, @@ -1090,18 +1083,6 @@ fn op_ffi_register_callback( Ok((state.resource_table.add(resource), u3x2)) } -#[op] -fn op_ffi_deregister_callback( - state: &mut deno_core::OpState, - rid: ResourceId, -) -> Result<(), AnyError> { - state - .resource_table - .take::(rid)? - .close(); - Ok(()) -} - #[op(v8)] fn op_ffi_call_ptr( scope: &mut v8::HandleScope<'scope>, @@ -1130,7 +1111,8 @@ where &def.parameters, def.result, )?; - let result = result.to_v8(scope, def.result); + // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. + let result = unsafe { result.to_v8(scope, def.result) }; Ok(result) } @@ -1164,7 +1146,8 @@ where let result = join_handle .await .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; - Ok(result.to_value(def.result)) + // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. + Ok(unsafe { result.to_value(def.result) }) }) } @@ -1280,7 +1263,8 @@ fn op_ffi_call<'scope>( &symbol.parameter_types, symbol.result_type, )?; - let result = result.to_v8(scope, symbol.result_type); + // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. + let result = unsafe { result.to_v8(scope, symbol.result_type) }; Ok(result) } @@ -1320,7 +1304,8 @@ fn op_ffi_call_nonblocking<'scope>( let result = join_handle .await .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; - Ok(result.to_value(result_type)) + // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. + Ok(unsafe { result.to_value(result_type) }) }) } From 798ebf5ce7038f7e55e6e6ddc81e415c50f5d6af Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 15 Jun 2022 18:25:05 +0300 Subject: [PATCH 55/64] Safety notice on UnsafeCallbackResource close() --- ext/ffi/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 951cec911d3b3a..a46984ae4f7198 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -795,10 +795,16 @@ impl Resource for UnsafeCallbackResource { } fn close(self: Rc) { - let info = unsafe { Box::from_raw(self.info as *mut CallbackInfo) }; - let isolate = unsafe { info.isolate.as_mut().unwrap() }; - unsafe { v8::Global::from_raw(isolate, info.callback) }; - unsafe { v8::Global::from_raw(isolate, info.context) }; + // SAFETY: This drops the closure and the callback info associated with it. + // Any retained function pointers to the closure become dangling pointers. + // It is up to the user to know that it is safe to call the `close()` on the + // UnsafeCallback instance. + unsafe { + let info = Box::from_raw(self.info as *mut CallbackInfo); + let isolate = info.isolate.as_mut().unwrap(); + v8::Global::from_raw(isolate, info.callback); + v8::Global::from_raw(isolate, info.context); + } } } From 56004f2248c839f021b241dc766f87b46f9ef67e Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 15 Jun 2022 20:46:48 +0300 Subject: [PATCH 56/64] Missing permissions check, remove left-over op, add notice about UnsafeCallbackResource -> closure not being dead code --- ext/ffi/lib.rs | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index a46984ae4f7198..0fb76abe3c6749 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -188,7 +188,6 @@ pub fn init(unstable: bool) -> Extension { op_ffi_call_ptr::decl::

(), op_ffi_call_ptr_nonblocking::decl::

(), op_ffi_ptr_of::decl::

(), - op_ffi_ptr_of_cb::decl::

(), op_ffi_buf_copy_into::decl::

(), op_ffi_cstr_read::decl::

(), op_ffi_read_u8::decl::

(), @@ -200,7 +199,7 @@ pub fn init(unstable: bool) -> Extension { op_ffi_read_u64::decl::

(), op_ffi_read_f32::decl::

(), op_ffi_read_f64::decl::

(), - op_ffi_unsafe_callback_create::decl(), + op_ffi_unsafe_callback_create::decl::

(), ]) .state(move |state| { // Stolen from deno_webgpu, is there a better option? @@ -785,6 +784,9 @@ fn ffi_call( } struct UnsafeCallbackResource { + // Closure is never directly touched, but it keeps the C callback alive + // until `close()` method is called. + #[allow(dead_code)] closure: libffi::middle::Closure<'static>, info: *const CallbackInfo, } @@ -1056,12 +1058,19 @@ struct RegisterCallbackArgs { } #[op(v8)] -fn op_ffi_unsafe_callback_create( +fn op_ffi_unsafe_callback_create( state: &mut deno_core::OpState, scope: &mut v8::HandleScope, args: RegisterCallbackArgs, cb: serde_v8::Value<'_>, -) -> Result<(ResourceId, U32x2), AnyError> { +) -> Result<(ResourceId, U32x2), AnyError> +where + FP: FfiPermissions + 'static, +{ + check_unstable(state, "Deno.UnsafeCallback"); + let permissions = state.borrow_mut::(); + permissions.check(None)?; + let v8_value = cb.v8_value; let cb = v8::Local::::try_from(v8_value)?; @@ -1333,29 +1342,6 @@ where Ok(big_int.into()) } -#[op(v8)] -fn op_ffi_ptr_of_cb( - scope: &mut v8::HandleScope<'scope>, - state: &mut deno_core::OpState, - rid: ResourceId, -) -> Result, AnyError> -where - FP: FfiPermissions + 'static, -{ - check_unstable(state, "Deno.RegisterableCallback.value"); - let permissions = state.borrow_mut::(); - permissions.check(None)?; - - let resource = state.resource_table.get::(rid)?; - - let big_int: v8::Local = v8::BigInt::new_from_u64( - scope, - *resource.closure.code_ptr() as usize as u64, - ) - .into(); - Ok(big_int.into()) -} - #[op] fn op_ffi_buf_copy_into( state: &mut deno_core::OpState, From 3cf98c441eee0bbcfcf0d1c84f599e15d358c1ed Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Wed, 15 Jun 2022 21:15:36 +0300 Subject: [PATCH 57/64] Return v8::Value directly in UnsafeCallback constructor --- ext/ffi/00_ffi.js | 2 +- ext/ffi/lib.rs | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index de6b7599302777..d29e83fce2ac41 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -410,7 +410,7 @@ internalCallback, ); this.#rid = rid; - this.pointer = new UnsafePointer(unpackU64(pointer)); + this.pointer = new UnsafePointer(pointer); this.#internal = internalCallback; this.definition = definition; this.callback = callback; diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 0fb76abe3c6749..7857fc8959daeb 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1058,12 +1058,12 @@ struct RegisterCallbackArgs { } #[op(v8)] -fn op_ffi_unsafe_callback_create( +fn op_ffi_unsafe_callback_create( state: &mut deno_core::OpState, - scope: &mut v8::HandleScope, + scope: &mut v8::HandleScope<'scope>, args: RegisterCallbackArgs, - cb: serde_v8::Value<'_>, -) -> Result<(ResourceId, U32x2), AnyError> + cb: serde_v8::Value<'scope>, +) -> Result, AnyError> where FP: FfiPermissions + 'static, { @@ -1090,12 +1090,18 @@ where ); let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, info); - // TODO(@aapoalas): Use BigInt later - let u3x2 = U32x2::from(*closure.code_ptr() as usize as u64); - + let ptr = *closure.code_ptr() as usize as u64; let resource = UnsafeCallbackResource { closure, info }; + let rid = state.resource_table.add(resource); + + let rid_local = v8::Integer::new_from_unsigned(scope, rid); + let ptr_local = v8::BigInt::new_from_u64(scope, ptr); + let array = v8::Array::new(scope, 2); + array.set_index(scope, 0, rid_local.into()); + array.set_index(scope, 1, ptr_local.into()); + let array_value: v8::Local = array.into(); - Ok((state.resource_table.add(resource), u3x2)) + Ok(array_value.into()) } #[op(v8)] From 9f5c8df44557d1de7687ef01e1c472ede89e8a2b Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 16 Jun 2022 20:49:47 +0300 Subject: [PATCH 58/64] Fix UnsafeCallback types and add extensive type tests --- cli/dts/lib.deno.unstable.d.ts | 81 ++++++++++++++++---- test_ffi/tests/ffi_types.ts | 132 +++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 13 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index f3bb1663909d6f..5671ed5346ae58 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -343,9 +343,7 @@ declare namespace Deno { type NativeParameterType = | NativeType - | { - function: Omit; - }; + | NativeCallbackType; /** A foreign function as defined by its parameter and result types */ export interface ForeignFunction< @@ -393,7 +391,12 @@ declare namespace Deno { type StaticForeignFunctionParameter = T extends "void" ? void : T extends StaticNativeNumberType | StaticNativeBigIntType ? number | bigint - : T extends "pointer" ? Deno.UnsafePointer | Deno.TypedArray | null + : T extends "pointer" ? UnsafePointer | TypedArray | null + : T extends + NativeCallbackType< + infer U extends readonly NativeType[], + infer V extends NativeParameterType + > ? UnsafeCallback | UnsafePointer | null : unknown; /** Infers a foreign function parameter list. */ @@ -519,6 +522,59 @@ declare namespace Deno { >; } + export interface UnsafeCallbackDefinition< + Parameters extends readonly NativeType[] = readonly NativeType[], + Result extends NativeParameterType = NativeParameterType, + > { + parameters: Parameters; + result: Result; + } + + interface NativeCallbackType< + Parameters extends readonly NativeType[] = readonly NativeType[], + Result extends NativeParameterType = NativeParameterType, + > { + readonly function: UnsafeCallbackDefinition; + } + + type UnsafeCallbackParameters = T extends [] + ? [] + : T extends + readonly [infer U extends NativeType, ...(infer V extends NativeType[])] + ? [ + UnsafeCallbackParameter, + ...UnsafeCallbackParameters, + ] + : never; + + type UnsafeCallbackParameter = T extends + StaticNativeBigIntType ? bigint + : T extends StaticNativeNumberType ? number + : T extends "pointer" ? UnsafePointer + : never; + + type UnsafeCallbackResult = T extends "void" + ? void + : T extends StaticNativeBigIntType ? number | bigint + : T extends StaticNativeNumberType ? number + : T extends "pointer" ? UnsafePointer | TypedArray | null + : T extends NativeCallbackType< + infer U extends readonly NativeType[], + infer V extends NativeParameterType + > ? UnsafeCallback | UnsafePointer | null + : never; + + type UnsafeCallbackFunction< + Parameters extends readonly NativeType[] = readonly NativeType[], + Result extends NativeParameterType = NativeParameterType, + > = Result extends NativeParameterType + ? Parameters extends readonly [] ? () => UnsafeCallbackResult + : Parameters extends readonly NativeType[] ? ( + ...args: UnsafeCallbackParameters + ) => UnsafeCallbackResult + : never + : never; + /** * **UNSTABLE**: Unsafe and new API, beware! * @@ -527,19 +583,18 @@ declare namespace Deno { * * The function pointer remains valid until the `close()` method is called. */ - export class UnsafeCallback { + export class UnsafeCallback< + Parameters extends readonly NativeType[] = readonly NativeType[], + Result extends NativeParameterType = NativeParameterType, + > { constructor( - definition: Fn, - callback: ( - ...args: StaticForeignFunctionParameters - ) => StaticForeignFunctionResult, + definition: UnsafeCallbackDefinition, + callback: UnsafeCallbackFunction, ); pointer: UnsafePointer; - definition: Fn; - callback: ( - ...args: StaticForeignFunctionParameters - ) => StaticForeignFunctionResult; + definition: UnsafeCallbackDefinition; + callback: UnsafeCallbackFunction; close(): void; } diff --git a/test_ffi/tests/ffi_types.ts b/test_ffi/tests/ffi_types.ts index 9ad51e67c978a1..92ac1389265436 100644 --- a/test_ffi/tests/ffi_types.ts +++ b/test_ffi/tests/ffi_types.ts @@ -24,6 +24,27 @@ const remote = Deno.dlopen( method17: { parameters: [], result: "usize", nonblocking: true }, method18: { parameters: [], result: "pointer" }, method19: { parameters: [], result: "pointer", nonblocking: true }, + method20: { + parameters: [{ + function: { parameters: ["u8", "u32", "pointer"], result: "void" }, + }], + result: "void", + }, + method21: { + parameters: [ + { function: { parameters: [], result: "u8" } }, + ], + result: "void", + }, + method22: { + parameters: [{ + function: { + parameters: [], + result: { function: { parameters: [], result: "u8" } }, + }, + }], + result: "void", + }, static1: { type: "usize" }, static2: { type: "pointer" }, static3: { type: "usize" }, @@ -41,6 +62,23 @@ const remote = Deno.dlopen( } as const, ); +Deno.dlopen( + "dummy_lib_2.so", + // @ts-expect-error: Returning a function pointer + // is declared using "pointer" + UnsafeFnPointer + { + wrong_method1: { + parameters: [], + result: { + function: { + parameters: [], + result: "void", + }, + }, + }, + } as const, +); + // @ts-expect-error: Invalid argument remote.symbols.method1(0); // @ts-expect-error: Invalid return type @@ -136,6 +174,100 @@ const fnptr = new Deno.UnsafeFnPointer( fnptr.call(null, null); fnptr.call(0, null); +const unsafe_callback_wrong1 = new Deno.UnsafeCallback( + { + parameters: ["i8"], + result: "void", + } as const, + // @ts-expect-error: i8 is not a pointer + (_: Deno.UnsafePointer) => {}, +); +const unsafe_callback_wrong2 = new Deno.UnsafeCallback( + { + parameters: ["pointer"], + result: "u64", + } as const, + // @ts-expect-error: must return a number or bigint + (_: Deno.UnsafePointer) => {}, +); +const unsafe_callback_wrong3 = new Deno.UnsafeCallback( + { + parameters: [], + result: "void", + } as const, + // @ts-expect-error: no parameters + (_: Deno.UnsafePointer) => {}, +); +const unsafe_callback_wrong4 = new Deno.UnsafeCallback( + { + parameters: ["u64"], + result: "void", + } as const, + // @ts-expect-error: Callback's 64bit parameters are always called as bigint + (_: number) => {}, +); +const unsafe_callback_right1 = new Deno.UnsafeCallback( + { + parameters: ["u8", "u32", "pointer"], + result: "void", + } as const, + (_1: number, _2: number, _3: Deno.UnsafePointer) => {}, +); +const unsafe_callback_right2 = new Deno.UnsafeCallback( + { + parameters: [], + result: "u8", + } as const, + () => 3, +); +const unsafe_callback_right3 = new Deno.UnsafeCallback( + { + parameters: [], + result: { + function: { + parameters: [], + result: "u8", + }, + }, + } as const, + // Callbacks can return other callbacks, if really wanted. + () => unsafe_callback_right2, +); +const unsafe_callback_right4 = new Deno.UnsafeCallback( + { + parameters: ["u8", "u32", "pointer"], + result: "u8", + } as const, + (_1: number, _2: number, _3: Deno.UnsafePointer) => 3, +); +const unsafe_callback_right5 = new Deno.UnsafeCallback( + { + parameters: ["u8", "i32", "pointer"], + result: "void", + } as const, + (_1: number, _2: number, _3: Deno.UnsafePointer) => {}, +); + +// @ts-expect-error: Must pass callback +remote.symbols.method20(); +// nullptr is okay +remote.symbols.method20(null); +// Foreign function ptr received as UnsafePointer is okay +remote.symbols.method20({} as Deno.UnsafePointer); +// @ts-expect-error: Callback does not match the parameter +remote.symbols.method20(unsafe_callback_right2); +remote.symbols.method20(unsafe_callback_right1); +// @ts-expect-error: Callback must match return value as well +remote.symbols.method20(unsafe_callback_right4); +// @ts-expect-error: Subtle differences in parameter types are not allowed (i32 vs u32) +remote.symbols.method20(unsafe_callback_right5); +remote.symbols.method21(unsafe_callback_right2); +remote.symbols.method22(unsafe_callback_right3); +// @ts-expect-error: Callback returns a callback with the wrong return value +remote.symbols.method21(unsafe_callback_right3); +// @ts-expect-error: Callback returns a callback with the wrong return value +remote.symbols.method22(unsafe_callback_right2); + // @ts-expect-error: Invalid member type const static1_wrong: null = remote.symbols.static1; const static1_right: bigint = remote.symbols.static1; From fc2f2e14db3917024e8a5234be212102a7024258 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 16 Jun 2022 21:00:55 +0300 Subject: [PATCH 59/64] Fix format --- cli/dts/lib.deno.unstable.d.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 5671ed5346ae58..3467dc7782f8ad 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -392,11 +392,10 @@ declare namespace Deno { : T extends StaticNativeNumberType | StaticNativeBigIntType ? number | bigint : T extends "pointer" ? UnsafePointer | TypedArray | null - : T extends - NativeCallbackType< - infer U extends readonly NativeType[], - infer V extends NativeParameterType - > ? UnsafeCallback | UnsafePointer | null + : T extends NativeCallbackType< + infer U extends readonly NativeType[], + infer V extends NativeParameterType + > ? UnsafeCallback | UnsafePointer | null : unknown; /** Infers a foreign function parameter list. */ From 27accd9f85a25943008f1d005ca1b90c044c6728 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 16 Jun 2022 22:56:50 +0300 Subject: [PATCH 60/64] Remove unnecessary try-catch from deno_ffi_callback --- ext/ffi/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 7857fc8959daeb..691d44460bdad7 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -848,7 +848,7 @@ unsafe extern "C" fn deno_ffi_callback( // it somehow cannot change the values that the loop sees, even if they both // refer the same `let bool_value`. let mut cb_scope = v8::CallbackScope::new(context); - let mut scope = v8::TryCatch::new(&mut cb_scope); + let mut scope = v8::HandleScope::new(&mut cb_scope); let func = callback.open(&mut scope); let result = result as *mut c_void; let repr: &[*mut ffi_type] = @@ -913,6 +913,8 @@ unsafe extern "C" fn deno_ffi_callback( if call_result.is_none() { // JS function threw an exception. Set the return value to zero and return. + // The exception continue propagating up the call chain when the event loop + // resumes. match (*cif.rtype).type_ as _ { FFI_TYPE_INT | FFI_TYPE_SINT32 | FFI_TYPE_UINT32 => { // zero is equal for signed and unsigned alike @@ -944,11 +946,6 @@ unsafe extern "C" fn deno_ffi_callback( } }; - if scope.has_caught() { - // Rethrow exception to pass it up the call chain, if one exists. - scope.rethrow(); - } - return; } let value = call_result.unwrap(); From 4131bfa8495af2d8423f0ec95aa346d77821e679 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 17 Jun 2022 20:15:57 +0200 Subject: [PATCH 61/64] Undo unrelated formatting changes # Conflicts: # ext/ffi/00_ffi.js # test_ffi/tests/test.js --- cli/dts/lib.deno.unstable.d.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 3467dc7782f8ad..3482d1c80c1e85 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -81,7 +81,10 @@ declare namespace Deno { * }); * ``` */ - export function bench(name: string, fn: () => void | Promise): void; + export function bench( + name: string, + fn: () => void | Promise, + ): void; /** Register a bench which will be run when `deno bench` is used on the command * line and the containing module looks like a bench module. @@ -198,7 +201,9 @@ declare namespace Deno { * const { columns, rows } = Deno.consoleSize(Deno.stdout.rid); * ``` */ - export function consoleSize(rid: number): { + export function consoleSize( + rid: number, + ): { columns: number; rows: number; }; @@ -418,10 +423,8 @@ declare namespace Deno { : T extends ForeignStatic ? StaticForeignFunctionResult : never; - type ConditionalAsync< - IsAsync extends boolean | undefined, - T, - > = IsAsync extends true ? Promise : T; + type ConditionalAsync = + IsAsync extends true ? Promise : T; /** Infers a foreign library interface */ type StaticForeignLibraryInterface = { @@ -933,8 +936,12 @@ declare namespace Deno { * ``` * * Requires `allow-net` permission for "tcp" and `allow-read` for "unix". */ - export function connect(options: ConnectOptions): Promise; - export function connect(options: UnixConnectOptions): Promise; + export function connect( + options: ConnectOptions, + ): Promise; + export function connect( + options: UnixConnectOptions, + ): Promise; export interface ConnectTlsOptions { /** PEM formatted client certificate chain. */ From 5184ddd4202045662c80bcf750cfdf413289d1ed Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 18 Jun 2022 22:13:50 +0530 Subject: [PATCH 62/64] Add test for error propogation --- test_ffi/tests/bench.js | 3 ++- test_ffi/tests/test.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/test_ffi/tests/bench.js b/test_ffi/tests/bench.js index 398732cc8de908..f650a499e4d66d 100644 --- a/test_ffi/tests/bench.js +++ b/test_ffi/tests/bench.js @@ -1,7 +1,8 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. // deno-lint-ignore-file -const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); +const targetDir = "./target/release" || + Deno.execPath().replace(/[^\/\\]+$/, ""); const [libPrefix, libSuffix] = { darwin: ["lib", "dylib"], linux: ["lib", "so"], diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 43b2f760e49463..27669dfda88624 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -364,6 +364,20 @@ const add10Callback = new Deno.UnsafeCallback({ parameters: ["u8"], result: "u8", }, (value) => value + 10); +const throwCallback = new Deno.UnsafeCallback({ + parameters: [], + result: "void", +}, () => { + throw new TypeError("hi"); +}); + +assertThrows( + () => { + dylib.symbols.call_fn_ptr(throwCallback); + }, + TypeError, + "hi", +); dylib.symbols.call_fn_ptr(logCallback); dylib.symbols.call_fn_ptr_many_parameters(logManyParametersCallback); From 40aac23a7cdb8b11eab76e27d3b2373b0785408f Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 18 Jun 2022 22:15:34 +0530 Subject: [PATCH 63/64] Format --- test_ffi/tests/bench.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test_ffi/tests/bench.js b/test_ffi/tests/bench.js index f650a499e4d66d..398732cc8de908 100644 --- a/test_ffi/tests/bench.js +++ b/test_ffi/tests/bench.js @@ -1,8 +1,7 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. // deno-lint-ignore-file -const targetDir = "./target/release" || - Deno.execPath().replace(/[^\/\\]+$/, ""); +const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); const [libPrefix, libSuffix] = { darwin: ["lib", "dylib"], linux: ["lib", "so"], From 0780dbcc7fbd8880ee42fdfe6e372f619ac3a0ff Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sun, 19 Jun 2022 08:45:15 +0530 Subject: [PATCH 64/64] throwCallback.close() --- test_ffi/tests/test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 27669dfda88624..1568abcbd32bb6 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -411,6 +411,7 @@ console.log("Static ptr value:", view.getUint32()); function cleanup() { dylib.close(); + throwCallback.close(); logCallback.close(); logManyParametersCallback.close(); returnU8Callback.close();