From 8b5b327b18b1cc6b0519e632d0e9b709af18820e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=B1=AA?= <504595380@qq.com> Date: Sat, 3 Dec 2022 20:15:35 +0800 Subject: [PATCH] feat(ext/ffi): better type hints for Deno.dlopen (#16874) --- cli/tsc/dts/lib.deno.unstable.d.ts | 17 +++++++++++--- test_ffi/tests/ffi_types.ts | 36 +++++++++++++++++------------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index ce609736b0..9e91c88004 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -443,7 +443,7 @@ declare namespace Deno { /** The definition of the function. */ definition: Fn; - constructor(pointer: PointerValue, definition: Fn); + constructor(pointer: PointerValue, definition: Const); /** Call the foreign function. */ call: FromForeignFunction; @@ -494,7 +494,7 @@ declare namespace Deno { Definition extends UnsafeCallbackDefinition = UnsafeCallbackDefinition, > { constructor( - definition: Definition, + definition: Const, callback: UnsafeCallbackFunction< Definition["parameters"], Definition["result"] @@ -562,6 +562,17 @@ declare namespace Deno { close(): void; } + /** + * This magic code used to implement better type hints for {@linkcode Deno.dlopen} + */ + type Cast = A extends B ? A : B; + type Const = Cast< + T, + | (T extends string | number | bigint | boolean ? T : never) + | { [K in keyof T]: Const } + | [] + >; + /** **UNSTABLE**: New API, yet to be vetted. * * Opens an external dynamic library and registers symbols, making foreign @@ -611,7 +622,7 @@ declare namespace Deno { */ export function dlopen( filename: string | URL, - symbols: S, + symbols: Const, ): DynamicLibrary; /** **UNSTABLE**: New API, yet to be vetted. diff --git a/test_ffi/tests/ffi_types.ts b/test_ffi/tests/ffi_types.ts index e2054cfe1b..a49224cfdc 100644 --- a/test_ffi/tests/ffi_types.ts +++ b/test_ffi/tests/ffi_types.ts @@ -5,7 +5,7 @@ const remote = Deno.dlopen( "dummy_lib.so", { - method1: { parameters: ["usize", "usize"], result: "void", callback: true }, + method1: { parameters: ["usize", "bool"], result: "void", callback: true }, method2: { parameters: [], result: "void" }, method3: { parameters: ["usize"], result: "void" }, method4: { parameters: ["isize"], result: "void" }, @@ -61,16 +61,16 @@ const remote = Deno.dlopen( static13: { type: "f32" }, static14: { type: "f64" }, static15: { type: "bool" }, - } as const, + }, ); Deno.dlopen( "dummy_lib_2.so", - // @ts-expect-error: Returning a function pointer // is declared using "pointer" or "function" + UnsafeFnPointer { wrong_method1: { parameters: [], + // @ts-expect-error not assignable to type 'NativeResultType' result: { function: { parameters: [], @@ -78,14 +78,18 @@ Deno.dlopen( }, }, }, - } as const, + }, ); // @ts-expect-error: Invalid argument remote.symbols.method1(0); +// @ts-expect-error: Invalid argument +remote.symbols.method1(0, 0); +// @ts-expect-error: Invalid argument +remote.symbols.method1(true, true); // @ts-expect-error: Invalid return type - remote.symbols.method1(0, 0); - remote.symbols.method1(0n, 0n); + remote.symbols.method1(0, true); + remote.symbols.method1(0n, true); // @ts-expect-error: Expected 0 arguments, but got 1. remote.symbols.method2(null); @@ -170,7 +174,7 @@ const fnptr = new Deno.UnsafeFnPointer( { parameters: ["u32", "pointer"], result: "void", - } as const, + }, ); // @ts-expect-error: Invalid argument fnptr.call(null, null); @@ -180,7 +184,7 @@ const unsafe_callback_wrong1 = new Deno.UnsafeCallback( { parameters: ["i8"], result: "void", - } as const, + }, // @ts-expect-error: i8 is not a pointer (_: bigint) => {}, ); @@ -188,7 +192,7 @@ const unsafe_callback_wrong2 = new Deno.UnsafeCallback( { parameters: ["pointer"], result: "u64", - } as const, + }, // @ts-expect-error: must return a number or bigint (_: Deno.UnsafePointer) => {}, ); @@ -196,7 +200,7 @@ const unsafe_callback_wrong3 = new Deno.UnsafeCallback( { parameters: [], result: "void", - } as const, + }, // @ts-expect-error: no parameters (_: Deno.UnsafePointer) => {}, ); @@ -204,7 +208,7 @@ const unsafe_callback_wrong4 = new Deno.UnsafeCallback( { parameters: ["u64"], result: "void", - } as const, + }, // @ts-expect-error: Callback's 64bit parameters are either number or bigint (_: number) => {}, ); @@ -212,21 +216,21 @@ const unsafe_callback_right1 = new Deno.UnsafeCallback( { parameters: ["u8", "u32", "pointer"], result: "void", - } as const, + }, (_1: number, _2: number, _3: Deno.PointerValue) => {}, ); const unsafe_callback_right2 = new Deno.UnsafeCallback( { parameters: [], result: "u8", - } as const, + }, () => 3, ); const unsafe_callback_right3 = new Deno.UnsafeCallback( { parameters: [], result: "function", - } as const, + }, // Callbacks can return other callbacks' pointers, if really wanted. () => unsafe_callback_right2.pointer, ); @@ -234,14 +238,14 @@ const unsafe_callback_right4 = new Deno.UnsafeCallback( { parameters: ["u8", "u32", "pointer"], result: "u8", - } as const, + }, (_1: number, _2: number, _3: Deno.PointerValue) => 3, ); const unsafe_callback_right5 = new Deno.UnsafeCallback( { parameters: ["u8", "i32", "pointer"], result: "void", - } as const, + }, (_1: number, _2: number, _3: Deno.PointerValue) => {}, );