diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 389d08f9fb..643386ced4 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -329,32 +329,86 @@ declare namespace Deno { */ export function getGid(): number | null; - /** All possible types for interfacing with foreign functions */ - export type NativeType = - | "void" + /** All plain number types for interfacing with foreign functions */ + type NativeNumberType = | "u8" | "i8" | "u16" | "i16" | "u32" | "i32" + | "f32" + | "f64"; + + /** All BigInt number type sfor interfacing with foreign functions */ + type NativeBigIntType = | "u64" | "i64" | "usize" - | "isize" - | "f32" - | "f64" - | "pointer"; + | "isize"; - type NativeParameterType = - | NativeType - | NativeCallbackType; + type NativePointerType = "pointer"; + + type NativeFunctionType = "function"; + + type NativeVoidType = "void"; + + /** All possible types for interfacing with foreign functions */ + export type NativeType = + | NativeNumberType + | NativeBigIntType + | NativePointerType + | NativeFunctionType; + + export type NativeResultType = NativeType | NativeVoidType; + + /** Type conversion for foreign symbol parameters and unsafe callback return types */ + type ToNativeType = T extends + NativeNumberType ? number + : T extends NativeBigIntType ? bigint | number + : T extends NativePointerType ? TypedArray | bigint | null + : T extends NativeFunctionType ? bigint | null + : never; + + /** Type conversion for unsafe callback return types */ + type ToNativeResultType = + T extends NativeType ? ToNativeType + : T extends NativeVoidType ? void + : never; + + type ToNativeParameterTypes = T extends + readonly [] ? [] + : T extends readonly [ + infer U extends NativeType, + ...(infer V extends NativeType[]), + ] ? [ToNativeType, ...ToNativeParameterTypes] + : never; + + /** Type conversion for foreign symbol return types and unsafe callback parameters */ + type FromNativeType = T extends + NativeNumberType ? number + : T extends NativeBigIntType | NativePointerType | NativeFunctionType + ? bigint + : never; + + /** Type conversion for foregin symbol return types */ + type FromNativeResultType = + T extends NativeType ? FromNativeType + : T extends NativeVoidType ? void + : never; + + type FromNativeParameterTypes = T extends + readonly [] ? [] + : T extends readonly [ + infer U extends NativeType, + ...(infer V extends NativeType[]), + ] ? [FromNativeType, ...FromNativeParameterTypes] + : never; /** A foreign function as defined by its parameter and result types */ export interface ForeignFunction< - Parameters extends readonly NativeParameterType[] = - readonly NativeParameterType[], - Result extends NativeType = NativeType, + Parameters extends readonly NativeType[] = readonly NativeType[], + Result extends NativeResultType = NativeResultType, NonBlocking extends boolean = boolean, > { /** Name of the symbol, defaults to the key name in symbols object. */ @@ -368,7 +422,7 @@ declare namespace Deno { export interface ForeignStatic { /** Name of the symbol, defaults to the key name in symbols object. */ name?: string; - type: Exclude; + type: Type; } /** A foreign library interface descriptor */ @@ -376,50 +430,21 @@ declare namespace Deno { [name: string]: ForeignFunction | ForeignStatic; } - /** All possible number types interfacing with foreign functions */ - type StaticNativeNumberType = Exclude< - NativeType, - "void" | "pointer" | StaticNativeBigIntType - >; - - /** All possible bigint types interfacing with foreign functions */ - type StaticNativeBigIntType = "u64" | "i64" | "usize" | "isize"; - - /** Infers a foreign function return type */ - type StaticForeignFunctionResult = T extends "void" - ? void - : T extends StaticNativeBigIntType ? bigint - : T extends StaticNativeNumberType ? number - : T extends "pointer" ? bigint - : never; - - type StaticForeignFunctionParameter = T extends "void" ? void - : T extends StaticNativeNumberType | StaticNativeBigIntType - ? number | bigint - : T extends "pointer" ? bigint | TypedArray | null - : T extends NativeCallbackType ? bigint | null - : unknown; - - /** Infers a foreign function parameter list. */ - 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 ? FromForeignFunction + : T extends ForeignStatic ? FromNativeType : never; + type FromForeignFunction = T["parameters"] extends + readonly [] ? () => StaticForeignSymbolReturnType + : ( + ...args: ToNativeParameterTypes + ) => StaticForeignSymbolReturnType; + + type StaticForeignSymbolReturnType = + ConditionalAsync>; + type ConditionalAsync = IsAsync extends true ? Promise : T; @@ -504,63 +529,23 @@ declare namespace Deno { constructor(pointer: bigint, definition: Fn); - call( - ...args: StaticForeignFunctionParameters - ): ConditionalAsync< - Fn["nonblocking"], - StaticForeignFunctionResult - >; + call: FromForeignFunction; } export interface UnsafeCallbackDefinition< Parameters extends readonly NativeType[] = readonly NativeType[], - Result extends NativeParameterType = NativeParameterType, + Result extends NativeResultType = NativeResultType, > { 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" ? bigint - : never; - - type UnsafeCallbackResult = T extends "void" - ? void - : T extends StaticNativeBigIntType ? number | bigint - : T extends StaticNativeNumberType ? number - : T extends "pointer" ? bigint | TypedArray | null - : T extends NativeCallbackType ? bigint | 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; + Result extends NativeResultType = NativeResultType, + > = Parameters extends readonly [] ? () => ToNativeResultType : ( + ...args: FromNativeParameterTypes + ) => ToNativeResultType; /** * **UNSTABLE**: Unsafe and new API, beware! @@ -571,17 +556,22 @@ declare namespace Deno { * The function pointer remains valid until the `close()` method is called. */ export class UnsafeCallback< - Parameters extends readonly NativeType[] = readonly NativeType[], - Result extends NativeParameterType = NativeParameterType, + Definition extends UnsafeCallbackDefinition = UnsafeCallbackDefinition, > { constructor( - definition: UnsafeCallbackDefinition, - callback: UnsafeCallbackFunction, + definition: Definition, + callback: UnsafeCallbackFunction< + Definition["parameters"], + Definition["result"] + >, ); pointer: bigint; - definition: UnsafeCallbackDefinition; - callback: UnsafeCallbackFunction; + definition: Definition; + callback: UnsafeCallbackFunction< + Definition["parameters"], + Definition["result"] + >; close(): void; } diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 691d44460b..f380546b0d 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -228,7 +228,7 @@ enum NativeType { F32, F64, Pointer, - Function {}, + Function, } impl From for libffi::middle::Type { @@ -248,7 +248,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(), } } } @@ -289,7 +289,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 | NativeType::Function {} => Arg::new(&self.pointer), + NativeType::Pointer | NativeType::Function => Arg::new(&self.pointer), } } @@ -317,7 +317,7 @@ impl NativeValue { } NativeType::F32 => Value::from(self.f32_value), NativeType::F64 => Value::from(self.f64_value), - NativeType::Pointer | NativeType::Function {} => { + NativeType::Pointer | NativeType::Function => { json!(U32x2::from(self.pointer as u64)) } } @@ -394,7 +394,7 @@ impl NativeValue { v8::Number::new(scope, self.f64_value).into(); local_value.into() } - NativeType::Pointer | NativeType::Function {} => { + NativeType::Pointer | NativeType::Function => { let local_value: v8::Local = v8::BigInt::new_from_u64(scope, self.pointer as u64).into(); local_value.into() @@ -702,7 +702,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 }) @@ -777,7 +777,7 @@ fn ffi_call( NativeType::F64 => NativeValue { f64_value: unsafe { cif.call::(fun_ptr, &call_args) }, }, - NativeType::Pointer | NativeType::Function {} => NativeValue { + NativeType::Pointer | NativeType::Function => NativeValue { pointer: unsafe { cif.call::<*const u8>(fun_ptr, &call_args) }, }, }) @@ -1245,7 +1245,7 @@ fn op_ffi_get_static<'scope>( let number = v8::Number::new(scope, result as f64); serde_v8::from_v8(scope, number.into())? } - NativeType::Pointer | NativeType::Function {} => { + 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())? diff --git a/test_ffi/tests/ffi_types.ts b/test_ffi/tests/ffi_types.ts index 92ac138926..742f92748e 100644 --- a/test_ffi/tests/ffi_types.ts +++ b/test_ffi/tests/ffi_types.ts @@ -6,7 +6,7 @@ const remote = Deno.dlopen( "dummy_lib.so", { method1: { parameters: ["usize", "usize"], result: "void" }, - method2: { parameters: ["void"], result: "void" }, + method2: { parameters: [], result: "void" }, method3: { parameters: ["usize"], result: "void" }, method4: { parameters: ["isize"], result: "void" }, method5: { parameters: ["u8"], result: "void" }, @@ -25,24 +25,17 @@ const remote = Deno.dlopen( method18: { parameters: [], result: "pointer" }, method19: { parameters: [], result: "pointer", nonblocking: true }, method20: { - parameters: [{ - function: { parameters: ["u8", "u32", "pointer"], result: "void" }, - }], + parameters: ["pointer"], result: "void", }, method21: { parameters: [ - { function: { parameters: [], result: "u8" } }, + "pointer", ], result: "void", }, method22: { - parameters: [{ - function: { - parameters: [], - result: { function: { parameters: [], result: "u8" } }, - }, - }], + parameters: ["pointer"], result: "void", }, static1: { type: "usize" }, @@ -65,7 +58,7 @@ const remote = Deno.dlopen( Deno.dlopen( "dummy_lib_2.so", // @ts-expect-error: Returning a function pointer - // is declared using "pointer" + UnsafeFnPointer + // is declared using "pointer" or "function" + UnsafeFnPointer { wrong_method1: { parameters: [], @@ -85,9 +78,9 @@ remote.symbols.method1(0); remote.symbols.method1(0, 0); remote.symbols.method1(0n, 0n); -// @ts-expect-error: Invalid argument +// @ts-expect-error: Expected 0 arguments, but got 1. remote.symbols.method2(null); -remote.symbols.method2(void 0); +remote.symbols.method2(); // @ts-expect-error: Invalid argument remote.symbols.method3(null); @@ -140,7 +133,7 @@ remote.symbols.method14(0); // @ts-expect-error: Invalid argument remote.symbols.method15(0); remote.symbols.method15(new Uint16Array(1)); -remote.symbols.method15({} as Deno.UnsafePointer); +remote.symbols.method15(0n); const result = remote.symbols.method16(); // @ts-expect-error: Invalid argument @@ -162,9 +155,8 @@ const result4 = remote.symbols.method19(); result4.then((_0: Deno.TypedArray) => {}); result4.then((_1: Deno.UnsafePointer) => {}); -const ptr = new Deno.UnsafePointer(0n); const fnptr = new Deno.UnsafeFnPointer( - ptr, + 0n, { parameters: ["u32", "pointer"], result: "void", @@ -180,7 +172,7 @@ const unsafe_callback_wrong1 = new Deno.UnsafeCallback( result: "void", } as const, // @ts-expect-error: i8 is not a pointer - (_: Deno.UnsafePointer) => {}, + (_: bigint) => {}, ); const unsafe_callback_wrong2 = new Deno.UnsafeCallback( { @@ -223,15 +215,10 @@ const unsafe_callback_right2 = new Deno.UnsafeCallback( const unsafe_callback_right3 = new Deno.UnsafeCallback( { parameters: [], - result: { - function: { - parameters: [], - result: "u8", - }, - }, + result: "function", } as const, - // Callbacks can return other callbacks, if really wanted. - () => unsafe_callback_right2, + // Callbacks can return other callbacks' pointers, if really wanted. + () => unsafe_callback_right2.pointer, ); const unsafe_callback_right4 = new Deno.UnsafeCallback( { @@ -252,21 +239,9 @@ const unsafe_callback_right5 = new Deno.UnsafeCallback( 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 +// @ts-expect-error: Callback cannot be passed directly 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); +remote.symbols.method20(unsafe_callback_right1.pointer); // @ts-expect-error: Invalid member type const static1_wrong: null = remote.symbols.static1; diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 0caa416a15..8190b3c8e2 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -127,44 +127,27 @@ const dylib = Deno.dlopen(libPath, { }, // Callback function call_fn_ptr: { - parameters: [{ function: { parameters: [], result: "void" } }], + parameters: ["function"], result: "void", }, call_fn_ptr_many_parameters: { - parameters: [{ - function: { - parameters: [ - "u8", - "i8", - "u16", - "i16", - "u32", - "i32", - "u64", - "i64", - "f32", - "f64", - "pointer", - ], - result: "void", - }, - }], + parameters: ["function"], result: "void", }, call_fn_ptr_return_u8: { - parameters: [{ function: { parameters: [], result: "u8" } }], + parameters: ["function"], result: "void", }, call_fn_ptr_return_buffer: { - parameters: [{ function: { parameters: [], result: "pointer" } }], + parameters: ["function"], result: "void", }, store_function: { - parameters: [{ function: { parameters: [], result: "void" } }], + parameters: ["function"], result: "void", }, store_function_2: { - parameters: [{ function: { parameters: ["u8"], result: "u8" } }], + parameters: ["function"], result: "void", }, call_stored_function: {