mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
chore(ext/ffi): simplify FFI types (#14920)
This commit simplifies the TypeScript types used for interacting with Deno FFI. The basis is that types are now first grouped into logical wholes, NativeNumberType, NativeBigIntType etc. These wholes are combined into the NativeType and NativeResultType general types. Additionally, this PR removes the { function: { parameters: [], result: "void" } } type declaration from parameters (and result types. Now functions are merely passed and returned as "function".
This commit is contained in:
parent
ac2cf2cb3e
commit
a38a1f91cf
4 changed files with 125 additions and 177 deletions
202
cli/dts/lib.deno.unstable.d.ts
vendored
202
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -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 NativeType = NativeType> = 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 NativeResultType = NativeResultType> =
|
||||
T extends NativeType ? ToNativeType<T>
|
||||
: T extends NativeVoidType ? void
|
||||
: never;
|
||||
|
||||
type ToNativeParameterTypes<T extends readonly NativeType[]> = T extends
|
||||
readonly [] ? []
|
||||
: T extends readonly [
|
||||
infer U extends NativeType,
|
||||
...(infer V extends NativeType[]),
|
||||
] ? [ToNativeType<U>, ...ToNativeParameterTypes<V>]
|
||||
: never;
|
||||
|
||||
/** Type conversion for foreign symbol return types and unsafe callback parameters */
|
||||
type FromNativeType<T extends NativeType = NativeType> = T extends
|
||||
NativeNumberType ? number
|
||||
: T extends NativeBigIntType | NativePointerType | NativeFunctionType
|
||||
? bigint
|
||||
: never;
|
||||
|
||||
/** Type conversion for foregin symbol return types */
|
||||
type FromNativeResultType<T extends NativeResultType = NativeResultType> =
|
||||
T extends NativeType ? FromNativeType<T>
|
||||
: T extends NativeVoidType ? void
|
||||
: never;
|
||||
|
||||
type FromNativeParameterTypes<T extends readonly NativeType[]> = T extends
|
||||
readonly [] ? []
|
||||
: T extends readonly [
|
||||
infer U extends NativeType,
|
||||
...(infer V extends NativeType[]),
|
||||
] ? [FromNativeType<U>, ...FromNativeParameterTypes<V>]
|
||||
: 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<Type extends NativeType = NativeType> {
|
||||
/** Name of the symbol, defaults to the key name in symbols object. */
|
||||
name?: string;
|
||||
type: Exclude<Type, "void">;
|
||||
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 NativeType> = T extends "void"
|
||||
? void
|
||||
: T extends StaticNativeBigIntType ? bigint
|
||||
: T extends StaticNativeNumberType ? number
|
||||
: T extends "pointer" ? bigint
|
||||
: never;
|
||||
|
||||
type StaticForeignFunctionParameter<T> = 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<T[K]>;
|
||||
},
|
||||
];
|
||||
|
||||
/** Infers a foreign symbol */
|
||||
type StaticForeignSymbol<T extends ForeignFunction | ForeignStatic> =
|
||||
T extends ForeignFunction ? (
|
||||
...args: StaticForeignFunctionParameters<T["parameters"]>
|
||||
) => ConditionalAsync<
|
||||
T["nonblocking"],
|
||||
StaticForeignFunctionResult<T["result"]>
|
||||
>
|
||||
: T extends ForeignStatic ? StaticForeignFunctionResult<T["type"]>
|
||||
T extends ForeignFunction ? FromForeignFunction<T>
|
||||
: T extends ForeignStatic ? FromNativeType<T["type"]>
|
||||
: never;
|
||||
|
||||
type FromForeignFunction<T extends ForeignFunction> = T["parameters"] extends
|
||||
readonly [] ? () => StaticForeignSymbolReturnType<T>
|
||||
: (
|
||||
...args: ToNativeParameterTypes<T["parameters"]>
|
||||
) => StaticForeignSymbolReturnType<T>;
|
||||
|
||||
type StaticForeignSymbolReturnType<T extends ForeignFunction> =
|
||||
ConditionalAsync<T["nonblocking"], FromNativeResultType<T["result"]>>;
|
||||
|
||||
type ConditionalAsync<IsAsync extends boolean | undefined, T> =
|
||||
IsAsync extends true ? Promise<T> : T;
|
||||
|
||||
|
@ -504,63 +529,23 @@ declare namespace Deno {
|
|||
|
||||
constructor(pointer: bigint, definition: Fn);
|
||||
|
||||
call(
|
||||
...args: StaticForeignFunctionParameters<Fn["parameters"]>
|
||||
): ConditionalAsync<
|
||||
Fn["nonblocking"],
|
||||
StaticForeignFunctionResult<Fn["result"]>
|
||||
>;
|
||||
call: FromForeignFunction<Fn>;
|
||||
}
|
||||
|
||||
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<Parameters, Result>;
|
||||
}
|
||||
|
||||
type UnsafeCallbackParameters<T extends readonly NativeType[]> = T extends []
|
||||
? []
|
||||
: T extends
|
||||
readonly [infer U extends NativeType, ...(infer V extends NativeType[])]
|
||||
? [
|
||||
UnsafeCallbackParameter<U>,
|
||||
...UnsafeCallbackParameters<V>,
|
||||
]
|
||||
: never;
|
||||
|
||||
type UnsafeCallbackParameter<T extends NativeType> = T extends
|
||||
StaticNativeBigIntType ? bigint
|
||||
: T extends StaticNativeNumberType ? number
|
||||
: T extends "pointer" ? bigint
|
||||
: never;
|
||||
|
||||
type UnsafeCallbackResult<T extends NativeParameterType> = 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<Result>
|
||||
: Parameters extends readonly NativeType[] ? (
|
||||
...args: UnsafeCallbackParameters<Parameters>
|
||||
) => UnsafeCallbackResult<Result>
|
||||
: never
|
||||
: never;
|
||||
Result extends NativeResultType = NativeResultType,
|
||||
> = Parameters extends readonly [] ? () => ToNativeResultType<Result> : (
|
||||
...args: FromNativeParameterTypes<Parameters>
|
||||
) => ToNativeResultType<Result>;
|
||||
|
||||
/**
|
||||
* **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<Parameters, Result>,
|
||||
callback: UnsafeCallbackFunction<Parameters, Result>,
|
||||
definition: Definition,
|
||||
callback: UnsafeCallbackFunction<
|
||||
Definition["parameters"],
|
||||
Definition["result"]
|
||||
>,
|
||||
);
|
||||
|
||||
pointer: bigint;
|
||||
definition: UnsafeCallbackDefinition<Parameters, Result>;
|
||||
callback: UnsafeCallbackFunction<Parameters, Result>;
|
||||
definition: Definition;
|
||||
callback: UnsafeCallbackFunction<
|
||||
Definition["parameters"],
|
||||
Definition["result"]
|
||||
>;
|
||||
|
||||
close(): void;
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ enum NativeType {
|
|||
F32,
|
||||
F64,
|
||||
Pointer,
|
||||
Function {},
|
||||
Function,
|
||||
}
|
||||
|
||||
impl From<NativeType> for libffi::middle::Type {
|
||||
|
@ -248,7 +248,7 @@ impl From<NativeType> 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::Value> =
|
||||
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::<f64>(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())?
|
||||
|
|
|
@ -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);
|
|||
<number> remote.symbols.method1(0, 0);
|
||||
<void> 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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
Loading…
Reference in a new issue