mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
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).
This commit is contained in:
parent
576d0db372
commit
af125c8e70
2 changed files with 215 additions and 80 deletions
209
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
209
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
|
@ -103,6 +103,31 @@ declare namespace Deno {
|
|||
*/
|
||||
type NativeStructType = { readonly struct: readonly NativeType[] };
|
||||
|
||||
/** @category FFI */
|
||||
const brand: unique symbol;
|
||||
|
||||
/** @category FFI */
|
||||
export type NativeU8Enum<T extends number> = "u8" & { [brand]: T };
|
||||
/** @category FFI */
|
||||
export type NativeI8Enum<T extends number> = "i8" & { [brand]: T };
|
||||
/** @category FFI */
|
||||
export type NativeU16Enum<T extends number> = "u16" & { [brand]: T };
|
||||
/** @category FFI */
|
||||
export type NativeI16Enum<T extends number> = "i16" & { [brand]: T };
|
||||
/** @category FFI */
|
||||
export type NativeU32Enum<T extends number> = "u32" & { [brand]: T };
|
||||
/** @category FFI */
|
||||
export type NativeI32Enum<T extends number> = "i32" & { [brand]: T };
|
||||
/** @category FFI */
|
||||
export type NativeTypedPointer<T extends PointerObject> = "pointer" & {
|
||||
[brand]: T;
|
||||
};
|
||||
export type NativeTypedFunction<T extends UnsafeCallbackDefinition> =
|
||||
& "function"
|
||||
& {
|
||||
[brand]: T;
|
||||
};
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* All supported types for interfacing with foreign functions.
|
||||
|
@ -124,21 +149,6 @@ declare namespace Deno {
|
|||
*/
|
||||
export type NativeResultType = NativeType | NativeVoidType;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* A utility type conversion for foreign symbol parameters and unsafe callback
|
||||
* return types.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
type ToNativeTypeMap =
|
||||
& Record<NativeNumberType, number>
|
||||
& Record<NativeBigIntType, number | bigint>
|
||||
& Record<NativeBooleanType, boolean>
|
||||
& Record<NativePointerType, PointerValue>
|
||||
& Record<NativeFunctionType, PointerValue>
|
||||
& Record<NativeBufferType, BufferSource | null>;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Type conversion for foreign symbol parameters and unsafe callback return
|
||||
|
@ -148,15 +158,22 @@ declare namespace Deno {
|
|||
*/
|
||||
type ToNativeType<T extends NativeType = NativeType> = T extends
|
||||
NativeStructType ? BufferSource
|
||||
: ToNativeTypeMap[Exclude<T, NativeStructType>];
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* A utility type for conversion for unsafe callback return types.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
type ToNativeResultTypeMap = ToNativeTypeMap & Record<NativeVoidType, void>;
|
||||
: T extends NativeNumberType ? T extends NativeU8Enum<infer U> ? U
|
||||
: T extends NativeI8Enum<infer U> ? U
|
||||
: T extends NativeU16Enum<infer U> ? U
|
||||
: T extends NativeI16Enum<infer U> ? U
|
||||
: T extends NativeU32Enum<infer U> ? U
|
||||
: T extends NativeI32Enum<infer U> ? U
|
||||
: number
|
||||
: T extends NativeBigIntType ? number | bigint
|
||||
: T extends NativeBooleanType ? boolean
|
||||
: T extends NativePointerType
|
||||
? T extends NativeTypedPointer<infer U> ? U | null : PointerValue
|
||||
: T extends NativeFunctionType
|
||||
? T extends NativeTypedFunction<infer U> ? PointerValue<U> | null
|
||||
: PointerValue
|
||||
: T extends NativeBufferType ? BufferSource | null
|
||||
: never;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
|
@ -166,7 +183,23 @@ declare namespace Deno {
|
|||
*/
|
||||
type ToNativeResultType<T extends NativeResultType = NativeResultType> =
|
||||
T extends NativeStructType ? BufferSource
|
||||
: ToNativeResultTypeMap[Exclude<T, NativeStructType>];
|
||||
: T extends NativeNumberType ? T extends NativeU8Enum<infer U> ? U
|
||||
: T extends NativeI8Enum<infer U> ? U
|
||||
: T extends NativeU16Enum<infer U> ? U
|
||||
: T extends NativeI16Enum<infer U> ? U
|
||||
: T extends NativeU32Enum<infer U> ? U
|
||||
: T extends NativeI32Enum<infer U> ? U
|
||||
: number
|
||||
: T extends NativeBigIntType ? number | bigint
|
||||
: T extends NativeBooleanType ? boolean
|
||||
: T extends NativePointerType
|
||||
? T extends NativeTypedPointer<infer U> ? U | null : PointerValue
|
||||
: T extends NativeFunctionType
|
||||
? T extends NativeTypedFunction<infer U> ? PointerObject<U> | null
|
||||
: PointerValue
|
||||
: T extends NativeBufferType ? BufferSource | null
|
||||
: T extends NativeVoidType ? void
|
||||
: never;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
|
@ -184,21 +217,6 @@ declare namespace Deno {
|
|||
}
|
||||
: never;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* A utility type for conversion of foreign symbol return types and unsafe
|
||||
* callback parameters.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
type FromNativeTypeMap =
|
||||
& Record<NativeNumberType, number>
|
||||
& Record<NativeBigIntType, number | bigint>
|
||||
& Record<NativeBooleanType, boolean>
|
||||
& Record<NativePointerType, PointerValue>
|
||||
& Record<NativeBufferType, PointerValue>
|
||||
& Record<NativeFunctionType, PointerValue>;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Type conversion for foreign symbol return types and unsafe callback
|
||||
|
@ -208,17 +226,22 @@ declare namespace Deno {
|
|||
*/
|
||||
type FromNativeType<T extends NativeType = NativeType> = T extends
|
||||
NativeStructType ? Uint8Array
|
||||
: FromNativeTypeMap[Exclude<T, NativeStructType>];
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* A utility type for conversion for foreign symbol return types.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
type FromNativeResultTypeMap =
|
||||
& FromNativeTypeMap
|
||||
& Record<NativeVoidType, void>;
|
||||
: T extends NativeNumberType ? T extends NativeU8Enum<infer U> ? U
|
||||
: T extends NativeI8Enum<infer U> ? U
|
||||
: T extends NativeU16Enum<infer U> ? U
|
||||
: T extends NativeI16Enum<infer U> ? U
|
||||
: T extends NativeU32Enum<infer U> ? U
|
||||
: T extends NativeI32Enum<infer U> ? U
|
||||
: number
|
||||
: T extends NativeBigIntType ? number | bigint
|
||||
: T extends NativeBooleanType ? boolean
|
||||
: T extends NativePointerType
|
||||
? T extends NativeTypedPointer<infer U> ? U | null : PointerValue
|
||||
: T extends NativeBufferType ? PointerValue
|
||||
: T extends NativeFunctionType
|
||||
? T extends NativeTypedFunction<infer U> ? PointerObject<U> | null
|
||||
: PointerValue
|
||||
: never;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
|
@ -228,7 +251,23 @@ declare namespace Deno {
|
|||
*/
|
||||
type FromNativeResultType<T extends NativeResultType = NativeResultType> =
|
||||
T extends NativeStructType ? Uint8Array
|
||||
: FromNativeResultTypeMap[Exclude<T, NativeStructType>];
|
||||
: T extends NativeNumberType ? T extends NativeU8Enum<infer U> ? U
|
||||
: T extends NativeI8Enum<infer U> ? U
|
||||
: T extends NativeU16Enum<infer U> ? U
|
||||
: T extends NativeI16Enum<infer U> ? U
|
||||
: T extends NativeU32Enum<infer U> ? U
|
||||
: T extends NativeI32Enum<infer U> ? U
|
||||
: number
|
||||
: T extends NativeBigIntType ? number | bigint
|
||||
: T extends NativeBooleanType ? boolean
|
||||
: T extends NativePointerType
|
||||
? T extends NativeTypedPointer<infer U> ? U | null : PointerValue
|
||||
: T extends NativeBufferType ? PointerValue
|
||||
: T extends NativeFunctionType
|
||||
? T extends NativeTypedFunction<infer U> ? PointerObject<U> | null
|
||||
: PointerValue
|
||||
: T extends NativeVoidType ? void
|
||||
: never;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
|
@ -354,42 +393,52 @@ declare namespace Deno {
|
|||
: StaticForeignSymbol<T[K]>;
|
||||
};
|
||||
|
||||
/** @category FFI */
|
||||
const brand: unique symbol;
|
||||
/** @category FFI */
|
||||
type PointerObject = { [brand]: unknown };
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Pointer type depends on the architecture and actual pointer value.
|
||||
* A non-null pointer, represented as an object
|
||||
* at runtime. The object's prototype is `null`
|
||||
* and cannot be changed. The object cannot be
|
||||
* assigned to either and is thus entirely read-only.
|
||||
*
|
||||
* On a 32 bit host system all pointer values are plain numbers. On a 64 bit
|
||||
* host system pointer values are represented as numbers if the value is below
|
||||
* `Number.MAX_SAFE_INTEGER`, otherwise they are provided as bigints.
|
||||
* To interact with memory through a pointer use the
|
||||
* {@linkcode UnsafePointerView} class. To create a
|
||||
* pointer from an address or the get the address of
|
||||
* a pointer use the static methods of the
|
||||
* {@linkcode UnsafePointer} class.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
export type PointerValue = null | PointerObject;
|
||||
export type PointerObject<T = unknown> = { [brand]: T };
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* An unsafe pointer to a memory location for passing and returning pointers
|
||||
* to and from the FFI.
|
||||
* Pointers are represented either with a {@linkcode PointerObject}
|
||||
* object or a `null` if the pointer is null.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
export type PointerValue<T = unknown> = null | PointerObject<T>;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* A collection of static functions for interacting with pointer objects.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
export class UnsafePointer {
|
||||
/** Create a pointer from a numeric value. This one is <i>really</i> dangerous! */
|
||||
static create(value: number | bigint): PointerValue;
|
||||
static create<T = unknown>(value: number | bigint): PointerValue<T>;
|
||||
/** Returns `true` if the two pointers point to the same address. */
|
||||
static equals(a: PointerValue, b: PointerValue): boolean;
|
||||
static equals<T = unknown>(a: PointerValue<T>, b: PointerValue<T>): boolean;
|
||||
/** Return the direct memory pointer to the typed array in memory. */
|
||||
static of(value: Deno.UnsafeCallback | BufferSource): PointerValue;
|
||||
static of<T = unknown>(
|
||||
value: Deno.UnsafeCallback | BufferSource,
|
||||
): PointerValue<T>;
|
||||
/** Return a new pointer offset from the original by `offset` bytes. */
|
||||
static offset(
|
||||
value: NonNullable<PointerValue>,
|
||||
static offset<T = unknown>(
|
||||
value: PointerObject,
|
||||
offset: number,
|
||||
): PointerValue;
|
||||
): PointerValue<T>;
|
||||
/** Get the numeric value of a pointer */
|
||||
static value(value: PointerValue): number | bigint;
|
||||
}
|
||||
|
@ -404,9 +453,9 @@ declare namespace Deno {
|
|||
* @category FFI
|
||||
*/
|
||||
export class UnsafePointerView {
|
||||
constructor(pointer: NonNullable<PointerValue>);
|
||||
constructor(pointer: PointerObject);
|
||||
|
||||
pointer: NonNullable<PointerValue>;
|
||||
pointer: PointerObject;
|
||||
|
||||
/** Gets a boolean at the specified byte offset from the pointer. */
|
||||
getBool(offset?: number): boolean;
|
||||
|
@ -441,14 +490,14 @@ declare namespace Deno {
|
|||
* pointer. */
|
||||
getFloat64(offset?: number): number;
|
||||
/** Gets a pointer at the specified byte offset from the pointer */
|
||||
getPointer(offset?: number): PointerValue;
|
||||
getPointer<T = unknown>(offset?: number): PointerValue<T>;
|
||||
/** Gets a C string (`null` terminated string) at the specified byte offset
|
||||
* from the pointer. */
|
||||
getCString(offset?: number): string;
|
||||
/** Gets a C string (`null` terminated string) at the specified byte offset
|
||||
* from the specified pointer. */
|
||||
static getCString(
|
||||
pointer: NonNullable<PointerValue>,
|
||||
pointer: PointerObject,
|
||||
offset?: number,
|
||||
): string;
|
||||
/** Gets an `ArrayBuffer` of length `byteLength` at the specified byte
|
||||
|
@ -457,7 +506,7 @@ declare namespace Deno {
|
|||
/** Gets an `ArrayBuffer` of length `byteLength` at the specified byte
|
||||
* offset from the specified pointer. */
|
||||
static getArrayBuffer(
|
||||
pointer: NonNullable<PointerValue>,
|
||||
pointer: PointerObject,
|
||||
byteLength: number,
|
||||
offset?: number,
|
||||
): ArrayBuffer;
|
||||
|
@ -473,7 +522,7 @@ declare namespace Deno {
|
|||
*
|
||||
* Also takes optional byte offset from the pointer. */
|
||||
static copyInto(
|
||||
pointer: NonNullable<PointerValue>,
|
||||
pointer: PointerObject,
|
||||
destination: BufferSource,
|
||||
offset?: number,
|
||||
): void;
|
||||
|
@ -488,11 +537,13 @@ declare namespace Deno {
|
|||
*/
|
||||
export class UnsafeFnPointer<Fn extends ForeignFunction> {
|
||||
/** The pointer to the function. */
|
||||
pointer: NonNullable<PointerValue>;
|
||||
pointer: PointerObject<Fn>;
|
||||
/** The definition of the function. */
|
||||
definition: Fn;
|
||||
|
||||
constructor(pointer: NonNullable<PointerValue>, definition: Const<Fn>);
|
||||
constructor(pointer: PointerObject<Fn>, definition: Const<Fn>);
|
||||
/** @deprecated Properly type {@linkcode pointer} using {@linkcode NativeTypedFunction} or {@linkcode UnsafeCallbackDefinition} types. */
|
||||
constructor(pointer: PointerObject, definition: Const<Fn>);
|
||||
|
||||
/** Call the foreign function. */
|
||||
call: FromForeignFunction<Fn>;
|
||||
|
@ -562,7 +613,7 @@ declare namespace Deno {
|
|||
);
|
||||
|
||||
/** The pointer to the unsafe callback. */
|
||||
readonly pointer: NonNullable<PointerValue>;
|
||||
readonly pointer: PointerObject<Definition>;
|
||||
/** The definition of the unsafe callback. */
|
||||
readonly definition: Definition;
|
||||
/** The callback function. */
|
||||
|
|
|
@ -173,7 +173,7 @@ result4.then((_0: Deno.BufferSource) => {});
|
|||
result4.then((_1: null | Deno.UnsafePointer) => {});
|
||||
|
||||
const fnptr = new Deno.UnsafeFnPointer(
|
||||
{} as NonNullable<Deno.PointerValue>,
|
||||
{} as Deno.PointerObject,
|
||||
{
|
||||
parameters: ["u32", "pointer"],
|
||||
result: "void",
|
||||
|
@ -358,6 +358,24 @@ type AssertNotEqual<
|
|||
$ = [Equal<Expected, Got>] extends [true] ? never : Expected,
|
||||
> = never;
|
||||
|
||||
const enum FooEnum {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
const foo = "u8" as Deno.NativeU8Enum<FooEnum>;
|
||||
|
||||
declare const brand: unique symbol;
|
||||
class MyPointerClass {}
|
||||
type MyPointer = Deno.PointerObject & { [brand]: MyPointerClass };
|
||||
const myPointer = "pointer" as Deno.NativeTypedPointer<MyPointer>;
|
||||
type MyFunctionDefinition = Deno.UnsafeCallbackDefinition<
|
||||
[typeof foo, "u32"],
|
||||
typeof myPointer
|
||||
>;
|
||||
const myFunction = "function" as Deno.NativeTypedFunction<
|
||||
MyFunctionDefinition
|
||||
>;
|
||||
|
||||
type __Tests__ = [
|
||||
empty: AssertEqual<
|
||||
{ symbols: Record<never, never>; 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<MyFunctionDefinition> | null) => void;
|
||||
};
|
||||
close(): void;
|
||||
},
|
||||
Deno.DynamicLibrary<
|
||||
{ foo: { parameters: [typeof myFunction]; result: "void" } }
|
||||
>
|
||||
>,
|
||||
typed_function_return: AssertEqual<
|
||||
{
|
||||
symbols: {
|
||||
foo: () => Deno.PointerObject<MyFunctionDefinition> | null;
|
||||
};
|
||||
close(): void;
|
||||
},
|
||||
Deno.DynamicLibrary<
|
||||
{ foo: { parameters: []; result: typeof myFunction } }
|
||||
>
|
||||
>,
|
||||
];
|
||||
|
|
Loading…
Reference in a new issue