From af125c8e700eacc7360b3d81ffe4de13e12a612a Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 21 Aug 2023 11:06:26 +0300 Subject: feat(unstable): Improve FFI types (#20215) Few improvements to FFI types: 1. Export `PointerObject` for convenience. It's fairly commonly used in library code and thus should be exported. 2. Fix various comments around `PointerValue` and `UnsafePointer` and expand upon them to better reflect reality. 3. Instead of using a `Record<"value", type>[T]` for determining the type of an FFI symbol parameter use direct `T extends "value" ? type : never` comparison. The last part enables smuggling extra information into the parameter and return value string declarations at the type level. eg. Instead of just `"u8"` the parameter can be `"u8" & { [brand]: T }` for some `T extends number`. That `T` can then be extracted from the parameter to form the TypeScript function's parameter or return value type. Essentially, this enables type-safe FFI! The foremost use-cases for this are enums and pointer safety. These are implemented in the second commit which should enable, in a backwards compatible way, for pointer parameters to declare what sort of pointer they mean, functions to declare what the API definition of the native function is, and for numbers to declare what Enum they stand for (if any). --- test_ffi/tests/ffi_types.ts | 86 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) (limited to 'test_ffi') diff --git a/test_ffi/tests/ffi_types.ts b/test_ffi/tests/ffi_types.ts index ff2a4fd5d..485224da3 100644 --- a/test_ffi/tests/ffi_types.ts +++ b/test_ffi/tests/ffi_types.ts @@ -173,7 +173,7 @@ result4.then((_0: Deno.BufferSource) => {}); result4.then((_1: null | Deno.UnsafePointer) => {}); const fnptr = new Deno.UnsafeFnPointer( - {} as NonNullable, + {} as Deno.PointerObject, { parameters: ["u32", "pointer"], result: "void", @@ -358,6 +358,24 @@ type AssertNotEqual< $ = [Equal] extends [true] ? never : Expected, > = never; +const enum FooEnum { + Foo, + Bar, +} +const foo = "u8" as Deno.NativeU8Enum; + +declare const brand: unique symbol; +class MyPointerClass {} +type MyPointer = Deno.PointerObject & { [brand]: MyPointerClass }; +const myPointer = "pointer" as Deno.NativeTypedPointer; +type MyFunctionDefinition = Deno.UnsafeCallbackDefinition< + [typeof foo, "u32"], + typeof myPointer +>; +const myFunction = "function" as Deno.NativeTypedFunction< + MyFunctionDefinition +>; + type __Tests__ = [ empty: AssertEqual< { symbols: Record; close(): void }, @@ -442,4 +460,70 @@ type __Tests__ = [ { foo: { parameters: []; result: "i32" } } > >, + enum_param: AssertEqual< + { + symbols: { + foo: (arg: FooEnum) => void; + }; + close(): void; + }, + Deno.DynamicLibrary< + { foo: { parameters: [typeof foo]; result: "void" } } + > + >, + enum_return: AssertEqual< + { + symbols: { + foo: () => FooEnum; + }; + close(): void; + }, + Deno.DynamicLibrary< + { foo: { parameters: []; result: typeof foo } } + > + >, + typed_pointer_param: AssertEqual< + { + symbols: { + foo: (arg: MyPointer | null) => void; + }; + close(): void; + }, + Deno.DynamicLibrary< + { foo: { parameters: [typeof myPointer]; result: "void" } } + > + >, + typed_pointer_return: AssertEqual< + { + symbols: { + foo: () => MyPointer | null; + }; + close(): void; + }, + Deno.DynamicLibrary< + { foo: { parameters: []; result: typeof myPointer } } + > + >, + typed_function_param: AssertEqual< + { + symbols: { + foo: (arg: Deno.PointerObject | null) => void; + }; + close(): void; + }, + Deno.DynamicLibrary< + { foo: { parameters: [typeof myFunction]; result: "void" } } + > + >, + typed_function_return: AssertEqual< + { + symbols: { + foo: () => Deno.PointerObject | null; + }; + close(): void; + }, + Deno.DynamicLibrary< + { foo: { parameters: []; result: typeof myFunction } } + > + >, ]; -- cgit v1.2.3