mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
feat(ext/ffi): Callbacks (#14663)
This commit adds support for unstable FFI callbacks. A callback is registered using the `Deno.UnsafeCallback` API. The backing memory for the callback can be disposed of using `Deno.UnsafeCallback#close`. It is not safe to pass the callback after calling close. Callbacks from other than the isolate thread are not supported. Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com> Co-authored-by: Bert Belder <bertbelder@gmail.com>
This commit is contained in:
parent
60869c2598
commit
3d6fa64f19
22 changed files with 2351 additions and 508 deletions
94
cli/dts/lib.deno.unstable.d.ts
vendored
94
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -346,9 +346,14 @@ declare namespace Deno {
|
||||||
| "f64"
|
| "f64"
|
||||||
| "pointer";
|
| "pointer";
|
||||||
|
|
||||||
|
type NativeParameterType =
|
||||||
|
| NativeType
|
||||||
|
| NativeCallbackType;
|
||||||
|
|
||||||
/** A foreign function as defined by its parameter and result types */
|
/** A foreign function as defined by its parameter and result types */
|
||||||
export interface ForeignFunction<
|
export interface ForeignFunction<
|
||||||
Parameters extends readonly NativeType[] = readonly NativeType[],
|
Parameters extends readonly NativeParameterType[] =
|
||||||
|
readonly NativeParameterType[],
|
||||||
Result extends NativeType = NativeType,
|
Result extends NativeType = NativeType,
|
||||||
NonBlocking extends boolean = boolean,
|
NonBlocking extends boolean = boolean,
|
||||||
> {
|
> {
|
||||||
|
@ -391,11 +396,17 @@ declare namespace Deno {
|
||||||
type StaticForeignFunctionParameter<T> = T extends "void" ? void
|
type StaticForeignFunctionParameter<T> = T extends "void" ? void
|
||||||
: T extends StaticNativeNumberType | StaticNativeBigIntType
|
: T extends StaticNativeNumberType | StaticNativeBigIntType
|
||||||
? number | bigint
|
? number | bigint
|
||||||
: T extends "pointer" ? Deno.UnsafePointer | Deno.TypedArray | null
|
: T extends "pointer" ? UnsafePointer | TypedArray | null
|
||||||
|
: T extends NativeCallbackType<
|
||||||
|
infer U extends readonly NativeType[],
|
||||||
|
infer V extends NativeParameterType
|
||||||
|
> ? UnsafeCallback<U, V> | UnsafePointer | null
|
||||||
: unknown;
|
: unknown;
|
||||||
|
|
||||||
/** Infers a foreign function parameter list. */
|
/** Infers a foreign function parameter list. */
|
||||||
type StaticForeignFunctionParameters<T extends readonly NativeType[]> = [
|
type StaticForeignFunctionParameters<
|
||||||
|
T extends readonly NativeParameterType[],
|
||||||
|
> = [
|
||||||
...{
|
...{
|
||||||
[K in keyof T]: StaticForeignFunctionParameter<T[K]>;
|
[K in keyof T]: StaticForeignFunctionParameter<T[K]>;
|
||||||
},
|
},
|
||||||
|
@ -513,6 +524,83 @@ declare namespace Deno {
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UnsafeCallbackDefinition<
|
||||||
|
Parameters extends readonly NativeType[] = readonly NativeType[],
|
||||||
|
Result extends NativeParameterType = NativeParameterType,
|
||||||
|
> {
|
||||||
|
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" ? UnsafePointer
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type UnsafeCallbackResult<T extends NativeParameterType> = T extends "void"
|
||||||
|
? void
|
||||||
|
: T extends StaticNativeBigIntType ? number | bigint
|
||||||
|
: T extends StaticNativeNumberType ? number
|
||||||
|
: T extends "pointer" ? UnsafePointer | TypedArray | null
|
||||||
|
: T extends NativeCallbackType<
|
||||||
|
infer U extends readonly NativeType[],
|
||||||
|
infer V extends NativeParameterType
|
||||||
|
> ? UnsafeCallback<U, V> | UnsafePointer | 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **UNSTABLE**: Unsafe and new API, beware!
|
||||||
|
*
|
||||||
|
* An unsafe function pointer for passing JavaScript functions
|
||||||
|
* as C function pointers to ffi calls.
|
||||||
|
*
|
||||||
|
* The function pointer remains valid until the `close()` method is called.
|
||||||
|
*/
|
||||||
|
export class UnsafeCallback<
|
||||||
|
Parameters extends readonly NativeType[] = readonly NativeType[],
|
||||||
|
Result extends NativeParameterType = NativeParameterType,
|
||||||
|
> {
|
||||||
|
constructor(
|
||||||
|
definition: UnsafeCallbackDefinition<Parameters, Result>,
|
||||||
|
callback: UnsafeCallbackFunction<Parameters, Result>,
|
||||||
|
);
|
||||||
|
|
||||||
|
pointer: UnsafePointer;
|
||||||
|
definition: UnsafeCallbackDefinition<Parameters, Result>;
|
||||||
|
callback: UnsafeCallbackFunction<Parameters, Result>;
|
||||||
|
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
|
||||||
/** A dynamic library resource */
|
/** A dynamic library resource */
|
||||||
export interface DynamicLibrary<S extends ForeignLibraryInterface> {
|
export interface DynamicLibrary<S extends ForeignLibraryInterface> {
|
||||||
/** All of the registered library along with functions for calling them */
|
/** All of the registered library along with functions for calling them */
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_10.js
vendored
2
cli/tests/testdata/unstable_ffi_10.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_i16", [0, 0]);
|
Deno.core.opSync("op_ffi_read_i16", 0n);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_11.js
vendored
2
cli/tests/testdata/unstable_ffi_11.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_u32", [0, 0]);
|
Deno.core.opSync("op_ffi_read_u32", 0n);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_12.js
vendored
2
cli/tests/testdata/unstable_ffi_12.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_i32", [0, 0]);
|
Deno.core.opSync("op_ffi_read_i32", 0n);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_13.js
vendored
2
cli/tests/testdata/unstable_ffi_13.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_u64", [0, 0]);
|
Deno.core.opSync("op_ffi_read_u64", 0n);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_14.js
vendored
2
cli/tests/testdata/unstable_ffi_14.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_f32", [0, 0]);
|
Deno.core.opSync("op_ffi_read_f32", 0n);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_15.js
vendored
2
cli/tests/testdata/unstable_ffi_15.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_f64", [0, 0]);
|
Deno.core.opSync("op_ffi_read_f64", 0n);
|
||||||
|
|
13
cli/tests/testdata/unstable_ffi_2.js
vendored
13
cli/tests/testdata/unstable_ffi_2.js
vendored
|
@ -1,10 +1,5 @@
|
||||||
Deno.core.opSync("op_ffi_call_ptr", {
|
Deno.core.opSync("op_ffi_call_ptr", 0n, {
|
||||||
pointer: [0, 0],
|
name: null,
|
||||||
def: {
|
|
||||||
name: null,
|
|
||||||
parameters: [],
|
|
||||||
result: "void",
|
|
||||||
},
|
|
||||||
parameters: [],
|
parameters: [],
|
||||||
buffers: [],
|
result: "void",
|
||||||
});
|
}, []);
|
||||||
|
|
13
cli/tests/testdata/unstable_ffi_3.js
vendored
13
cli/tests/testdata/unstable_ffi_3.js
vendored
|
@ -1,10 +1,5 @@
|
||||||
Deno.core.opAsync("op_ffi_call_ptr_nonblocking", {
|
Deno.core.opAsync("op_ffi_call_ptr_nonblocking", 0n, {
|
||||||
pointer: [0, 0],
|
name: null,
|
||||||
def: {
|
|
||||||
name: null,
|
|
||||||
parameters: [],
|
|
||||||
result: "void",
|
|
||||||
},
|
|
||||||
parameters: [],
|
parameters: [],
|
||||||
buffers: [],
|
result: "void",
|
||||||
});
|
}, []);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_5.js
vendored
2
cli/tests/testdata/unstable_ffi_5.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_buf_copy_into", [[0, 0], new Uint8Array(0), 0]);
|
Deno.core.opSync("op_ffi_buf_copy_into", 0n, new Uint8Array(0), 0);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_6.js
vendored
2
cli/tests/testdata/unstable_ffi_6.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_cstr_read", [0, 0]);
|
Deno.core.opSync("op_ffi_cstr_read", 0n);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_7.js
vendored
2
cli/tests/testdata/unstable_ffi_7.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_u8", [0, 0]);
|
Deno.core.opSync("op_ffi_read_u8", 0n);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_8.js
vendored
2
cli/tests/testdata/unstable_ffi_8.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_i8", [0, 0]);
|
Deno.core.opSync("op_ffi_read_i8", 0n);
|
||||||
|
|
2
cli/tests/testdata/unstable_ffi_9.js
vendored
2
cli/tests/testdata/unstable_ffi_9.js
vendored
|
@ -1 +1 @@
|
||||||
Deno.core.opSync("op_ffi_read_u16", [0, 0]);
|
Deno.core.opSync("op_ffi_read_u16", 0n);
|
||||||
|
|
|
@ -6,18 +6,18 @@
|
||||||
const __bootstrap = window.__bootstrap;
|
const __bootstrap = window.__bootstrap;
|
||||||
const {
|
const {
|
||||||
ArrayBufferPrototype,
|
ArrayBufferPrototype,
|
||||||
Uint8Array,
|
ArrayPrototypePush,
|
||||||
|
ArrayPrototypeSome,
|
||||||
BigInt,
|
BigInt,
|
||||||
Number,
|
NumberIsFinite,
|
||||||
|
NumberIsInteger,
|
||||||
ObjectDefineProperty,
|
ObjectDefineProperty,
|
||||||
ObjectPrototypeIsPrototypeOf,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
PromisePrototypeThen,
|
||||||
TypeError,
|
TypeError,
|
||||||
|
Uint8Array,
|
||||||
} = window.__bootstrap.primordials;
|
} = window.__bootstrap.primordials;
|
||||||
|
|
||||||
function pack64(value) {
|
|
||||||
return [Number(value >> 32n) >>> 0, Number(value & 0xFFFFFFFFn)];
|
|
||||||
}
|
|
||||||
|
|
||||||
function unpackU64([hi, lo]) {
|
function unpackU64([hi, lo]) {
|
||||||
return BigInt(hi) << 32n | BigInt(lo);
|
return BigInt(hi) << 32n | BigInt(lo);
|
||||||
}
|
}
|
||||||
|
@ -37,77 +37,77 @@
|
||||||
getUint8(offset = 0) {
|
getUint8(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_u8",
|
"op_ffi_read_u8",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getInt8(offset = 0) {
|
getInt8(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_i8",
|
"op_ffi_read_i8",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUint16(offset = 0) {
|
getUint16(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_u16",
|
"op_ffi_read_u16",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getInt16(offset = 0) {
|
getInt16(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_i16",
|
"op_ffi_read_i16",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUint32(offset = 0) {
|
getUint32(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_u32",
|
"op_ffi_read_u32",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getInt32(offset = 0) {
|
getInt32(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_i32",
|
"op_ffi_read_i32",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBigUint64(offset = 0) {
|
getBigUint64(offset = 0) {
|
||||||
return unpackU64(core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_u64",
|
"op_ffi_read_u64",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBigInt64(offset = 0) {
|
getBigInt64(offset = 0) {
|
||||||
return unpackI64(core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_u64",
|
"op_ffi_read_u64",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFloat32(offset = 0) {
|
getFloat32(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_f32",
|
"op_ffi_read_f32",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFloat64(offset = 0) {
|
getFloat64(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_read_f64",
|
"op_ffi_read_f64",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCString(offset = 0) {
|
getCString(offset = 0) {
|
||||||
return core.opSync(
|
return core.opSync(
|
||||||
"op_ffi_cstr_read",
|
"op_ffi_cstr_read",
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
this.pointer.value + BigInt(offset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,11 +118,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
copyInto(destination, offset = 0) {
|
copyInto(destination, offset = 0) {
|
||||||
core.opSync("op_ffi_buf_copy_into", [
|
core.opSync(
|
||||||
pack64(this.pointer.value + BigInt(offset)),
|
"op_ffi_buf_copy_into",
|
||||||
|
this.pointer.value + BigInt(offset),
|
||||||
destination,
|
destination,
|
||||||
destination.byteLength,
|
destination.byteLength,
|
||||||
]);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,12 +131,15 @@
|
||||||
value;
|
value;
|
||||||
|
|
||||||
constructor(value) {
|
constructor(value) {
|
||||||
|
if (typeof value === "number") {
|
||||||
|
value = BigInt(value);
|
||||||
|
}
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static of(typedArray) {
|
static of(typedArray) {
|
||||||
return new UnsafePointer(
|
return new UnsafePointer(
|
||||||
unpackU64(core.opSync("op_ffi_ptr_of", typedArray)),
|
core.opSync("op_ffi_ptr_of", typedArray),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,58 +151,118 @@
|
||||||
|
|
||||||
function prepareArgs(types, args) {
|
function prepareArgs(types, args) {
|
||||||
const parameters = [];
|
const parameters = [];
|
||||||
const buffers = [];
|
|
||||||
|
if (types.length !== args.length) {
|
||||||
|
throw new TypeError("Invalid FFI call, parameter vs args count mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < types.length; i++) {
|
for (let i = 0; i < types.length; i++) {
|
||||||
const type = types[i];
|
const type = types[i];
|
||||||
const arg = args[i];
|
const arg = args[i];
|
||||||
|
|
||||||
if (type === "pointer") {
|
if (type === "u8" || type === "u16" || type === "u32") {
|
||||||
|
if (!NumberIsInteger(arg) || arg < 0) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Expected FFI argument to be an unsigned integer, but got '${arg}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ArrayPrototypePush(parameters, arg);
|
||||||
|
} else if (type === "i8" || type === "i16" || type === "i32") {
|
||||||
|
if (!NumberIsInteger(arg)) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Expected FFI argument to be a signed integer, but got '${arg}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ArrayPrototypePush(parameters, arg);
|
||||||
|
} else if (type === "u64" || type === "usize") {
|
||||||
|
if (
|
||||||
|
!(NumberIsInteger(arg) && arg >= 0 ||
|
||||||
|
typeof arg === "bigint" && 0n <= arg && arg <= 0xffffffffffffffffn)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Expected FFI argument to be an unsigned integer, but got '${arg}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ArrayPrototypePush(parameters, arg);
|
||||||
|
} else if (type == "i64" || type === "isize") {
|
||||||
|
if (
|
||||||
|
!(NumberIsInteger(arg) ||
|
||||||
|
typeof arg === "bigint" && -1n * 2n ** 63n <= arg &&
|
||||||
|
arg <= 2n ** 63n - 1n)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Expected FFI argument to be a signed integer, but got '${arg}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ArrayPrototypePush(parameters, arg);
|
||||||
|
} else if (type === "f32" || type === "f64") {
|
||||||
|
if (!NumberIsFinite(arg)) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Expected FFI argument to be a number, but got '${arg}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ArrayPrototypePush(parameters, arg);
|
||||||
|
} else if (type === "pointer") {
|
||||||
if (
|
if (
|
||||||
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, arg?.buffer) &&
|
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, arg?.buffer) &&
|
||||||
arg.byteLength !== undefined
|
arg.byteLength !== undefined
|
||||||
) {
|
) {
|
||||||
parameters.push(buffers.length);
|
ArrayPrototypePush(parameters, arg);
|
||||||
buffers.push(arg);
|
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg)) {
|
} else if (ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg)) {
|
||||||
parameters.push(pack64(arg.value));
|
ArrayPrototypePush(parameters, arg.value);
|
||||||
buffers.push(undefined);
|
|
||||||
} else if (arg === null) {
|
} else if (arg === null) {
|
||||||
parameters.push(null);
|
ArrayPrototypePush(parameters, null);
|
||||||
buffers.push(undefined);
|
|
||||||
} else {
|
} else {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
"Invalid ffi arg value, expected TypedArray, UnsafePointer or null",
|
"Expected FFI argument to be TypedArray, UnsafePointer or null",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (typeof arg === "bigint") {
|
} else if (
|
||||||
if (arg > 0xffffffffffffffffn) {
|
typeof type === "object" && type !== null && "function" in type
|
||||||
|
) {
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, arg)) {
|
||||||
|
// Own registered callback, pass the pointer value
|
||||||
|
ArrayPrototypePush(parameters, arg.pointer.value);
|
||||||
|
} else if (arg === null) {
|
||||||
|
// nullptr
|
||||||
|
ArrayPrototypePush(parameters, null);
|
||||||
|
} else if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, arg)
|
||||||
|
) {
|
||||||
|
// Foreign function, pass the pointer value
|
||||||
|
ArrayPrototypePush(parameters, arg.pointer.value);
|
||||||
|
} else if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, arg)
|
||||||
|
) {
|
||||||
|
// Foreign function, pass the pointer value
|
||||||
|
ArrayPrototypePush(parameters, arg.value);
|
||||||
|
} else {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
"Invalid ffi arg value, it needs to be less than 0xffffffffffffffff",
|
"Expected FFI argument to be UnsafeCallback, UnsafeFnPointer, UnsafePointer or null",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters.push(pack64(arg));
|
|
||||||
} else {
|
} else {
|
||||||
parameters.push(arg);
|
throw new TypeError(`Invalid FFI argument type '${type}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { parameters, buffers };
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
function unpackResult(type, result) {
|
function unpackNonblockingReturnValue(type, result) {
|
||||||
|
if (
|
||||||
|
typeof type === "object" && type !== null && "function" in type ||
|
||||||
|
type === "pointer"
|
||||||
|
) {
|
||||||
|
return new UnsafePointer(unpackU64(result));
|
||||||
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "pointer":
|
case "isize":
|
||||||
return new UnsafePointer(unpackU64(result));
|
|
||||||
case "u64":
|
|
||||||
return unpackU64(result);
|
|
||||||
case "i64":
|
case "i64":
|
||||||
return unpackI64(result);
|
return unpackI64(result);
|
||||||
case "usize":
|
case "usize":
|
||||||
|
case "u64":
|
||||||
return unpackU64(result);
|
return unpackU64(result);
|
||||||
case "isize":
|
|
||||||
return unpackI64(result);
|
|
||||||
default:
|
default:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -214,33 +278,39 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
call(...args) {
|
call(...args) {
|
||||||
const { parameters, buffers } = prepareArgs(
|
const parameters = prepareArgs(
|
||||||
this.definition.parameters,
|
this.definition.parameters,
|
||||||
args,
|
args,
|
||||||
);
|
);
|
||||||
|
const resultType = this.definition.result;
|
||||||
if (this.definition.nonblocking) {
|
if (this.definition.nonblocking) {
|
||||||
const promise = core.opAsync("op_ffi_call_ptr_nonblocking", {
|
const promise = core.opAsync(
|
||||||
pointer: pack64(this.pointer.value),
|
"op_ffi_call_ptr_nonblocking",
|
||||||
def: this.definition,
|
this.pointer.value,
|
||||||
|
this.definition,
|
||||||
parameters,
|
parameters,
|
||||||
buffers,
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if (this.definition.result === "pointer") {
|
if (
|
||||||
return promise.then((value) => new UnsafePointer(unpackU64(value)));
|
isReturnedAsBigInt(resultType)
|
||||||
|
) {
|
||||||
|
return PromisePrototypeThen(
|
||||||
|
promise,
|
||||||
|
(result) => unpackNonblockingReturnValue(resultType, result),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
} else {
|
} else {
|
||||||
const result = core.opSync("op_ffi_call_ptr", {
|
const result = core.opSync(
|
||||||
pointer: pack64(this.pointer.value),
|
"op_ffi_call_ptr",
|
||||||
def: this.definition,
|
this.pointer.value,
|
||||||
|
this.definition,
|
||||||
parameters,
|
parameters,
|
||||||
buffers,
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if (this.definition.result === "pointer") {
|
if (isPointerType(resultType)) {
|
||||||
return new UnsafePointer(unpackU64(result));
|
return new UnsafePointer(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -248,6 +318,111 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UnsafeFnPointerPrototype = UnsafeFnPointer.prototype;
|
||||||
|
|
||||||
|
function isPointerType(type) {
|
||||||
|
return type === "pointer" ||
|
||||||
|
typeof type === "object" && type !== null && "function" in type;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isReturnedAsBigInt(type) {
|
||||||
|
return isPointerType(type) || type === "u64" || type === "i64" ||
|
||||||
|
type === "usize" || type === "isize";
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareUnsafeCallbackParameters(types, args) {
|
||||||
|
const parameters = [];
|
||||||
|
if (types.length === 0) {
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < types.length; i++) {
|
||||||
|
const type = types[i];
|
||||||
|
const arg = args[i];
|
||||||
|
ArrayPrototypePush(
|
||||||
|
parameters,
|
||||||
|
isPointerType(type) ? new UnsafePointer(arg) : arg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrapUnsafeCallbackReturnValue(result) {
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(UnsafePointerPrototype, result)
|
||||||
|
) {
|
||||||
|
// Foreign function, return the pointer value
|
||||||
|
ArrayPrototypePush(parameters, result.value);
|
||||||
|
} else if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(UnsafeFnPointerPrototype, result)
|
||||||
|
) {
|
||||||
|
// Foreign function, return the pointer value
|
||||||
|
ArrayPrototypePush(parameters, result.pointer.value);
|
||||||
|
} else if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, result)
|
||||||
|
) {
|
||||||
|
// Own registered callback, return the pointer value.
|
||||||
|
// Note that returning the ResourceId here would not work as
|
||||||
|
// the Rust side code cannot access OpState to get the resource.
|
||||||
|
ArrayPrototypePush(parameters, result.pointer.value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInternalCallback(definition, callback) {
|
||||||
|
const mustUnwrap = isPointerType(definition.result);
|
||||||
|
return (...args) => {
|
||||||
|
const convertedArgs = prepareUnsafeCallbackParameters(
|
||||||
|
definition.parameters,
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
const result = callback(...convertedArgs);
|
||||||
|
if (mustUnwrap) {
|
||||||
|
return unwrapUnsafeCallbackReturnValue(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnsafeCallback {
|
||||||
|
#rid;
|
||||||
|
#internal;
|
||||||
|
definition;
|
||||||
|
callback;
|
||||||
|
pointer;
|
||||||
|
|
||||||
|
constructor(definition, callback) {
|
||||||
|
if (definition.nonblocking) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Invalid UnsafeCallback, cannot be nonblocking",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const needsWrapping = isPointerType(definition.result) ||
|
||||||
|
ArrayPrototypeSome(definition.parameters, isPointerType);
|
||||||
|
const internalCallback = needsWrapping
|
||||||
|
? createInternalCallback(definition, callback)
|
||||||
|
: callback;
|
||||||
|
|
||||||
|
const [rid, pointer] = core.opSync(
|
||||||
|
"op_ffi_unsafe_callback_create",
|
||||||
|
definition,
|
||||||
|
internalCallback,
|
||||||
|
);
|
||||||
|
this.#rid = rid;
|
||||||
|
this.pointer = new UnsafePointer(pointer);
|
||||||
|
this.#internal = internalCallback;
|
||||||
|
this.definition = definition;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
core.close(this.#rid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnsafeCallbackPrototype = UnsafeCallback.prototype;
|
||||||
|
|
||||||
class DynamicLibrary {
|
class DynamicLibrary {
|
||||||
#rid;
|
#rid;
|
||||||
symbols = {};
|
symbols = {};
|
||||||
|
@ -267,19 +442,12 @@
|
||||||
const name = symbols[symbol].name || symbol;
|
const name = symbols[symbol].name || symbol;
|
||||||
let value = core.opSync(
|
let value = core.opSync(
|
||||||
"op_ffi_get_static",
|
"op_ffi_get_static",
|
||||||
{
|
this.#rid,
|
||||||
rid: this.#rid,
|
name,
|
||||||
name,
|
type,
|
||||||
type,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if (type === "pointer" || type === "u64") {
|
if (type === "pointer") {
|
||||||
value = unpackU64(value);
|
value = new UnsafePointer(value);
|
||||||
if (type === "pointer") {
|
|
||||||
value = new UnsafePointer(value);
|
|
||||||
}
|
|
||||||
} else if (type === "i64") {
|
|
||||||
value = unpackI64(value);
|
|
||||||
}
|
}
|
||||||
ObjectDefineProperty(
|
ObjectDefineProperty(
|
||||||
this.symbols,
|
this.symbols,
|
||||||
|
@ -298,33 +466,47 @@
|
||||||
const types = symbols[symbol].parameters;
|
const types = symbols[symbol].parameters;
|
||||||
const resultType = symbols[symbol].result;
|
const resultType = symbols[symbol].result;
|
||||||
|
|
||||||
const fn = (...args) => {
|
let fn;
|
||||||
const { parameters, buffers } = prepareArgs(types, args);
|
if (isNonBlocking) {
|
||||||
|
const needsUnpacking = isReturnedAsBigInt(resultType);
|
||||||
|
fn = (...args) => {
|
||||||
|
const parameters = prepareArgs(types, args);
|
||||||
|
|
||||||
if (isNonBlocking) {
|
const promise = core.opAsync(
|
||||||
const promise = core.opAsync("op_ffi_call_nonblocking", {
|
"op_ffi_call_nonblocking",
|
||||||
rid: this.#rid,
|
this.#rid,
|
||||||
symbol,
|
symbol,
|
||||||
parameters,
|
parameters,
|
||||||
buffers,
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if (resultType === "pointer") {
|
if (needsUnpacking) {
|
||||||
return promise.then((result) => unpackResult(resultType, result));
|
return PromisePrototypeThen(
|
||||||
|
promise,
|
||||||
|
(result) => unpackNonblockingReturnValue(resultType, result),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
} else {
|
};
|
||||||
const result = core.opSync("op_ffi_call", {
|
} else {
|
||||||
rid: this.#rid,
|
const mustWrap = isPointerType(resultType);
|
||||||
|
fn = (...args) => {
|
||||||
|
const parameters = prepareArgs(types, args);
|
||||||
|
|
||||||
|
const result = core.opSync(
|
||||||
|
"op_ffi_call",
|
||||||
|
this.#rid,
|
||||||
symbol,
|
symbol,
|
||||||
parameters,
|
parameters,
|
||||||
buffers,
|
);
|
||||||
});
|
|
||||||
|
|
||||||
return unpackResult(resultType, result);
|
if (mustWrap) {
|
||||||
}
|
return new UnsafePointer(result);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
ObjectDefineProperty(
|
ObjectDefineProperty(
|
||||||
this.symbols,
|
this.symbols,
|
||||||
|
@ -352,6 +534,7 @@
|
||||||
|
|
||||||
window.__bootstrap.ffi = {
|
window.__bootstrap.ffi = {
|
||||||
dlopen,
|
dlopen,
|
||||||
|
UnsafeCallback,
|
||||||
UnsafePointer,
|
UnsafePointer,
|
||||||
UnsafePointerView,
|
UnsafePointerView,
|
||||||
UnsafeFnPointer,
|
UnsafeFnPointer,
|
||||||
|
|
1268
ext/ffi/lib.rs
1268
ext/ffi/lib.rs
File diff suppressed because it is too large
Load diff
|
@ -139,6 +139,7 @@
|
||||||
createHttpClient: __bootstrap.fetch.createHttpClient,
|
createHttpClient: __bootstrap.fetch.createHttpClient,
|
||||||
http: __bootstrap.http,
|
http: __bootstrap.http,
|
||||||
dlopen: __bootstrap.ffi.dlopen,
|
dlopen: __bootstrap.ffi.dlopen,
|
||||||
|
UnsafeCallback: __bootstrap.ffi.UnsafeCallback,
|
||||||
UnsafePointer: __bootstrap.ffi.UnsafePointer,
|
UnsafePointer: __bootstrap.ffi.UnsafePointer,
|
||||||
UnsafePointerView: __bootstrap.ffi.UnsafePointerView,
|
UnsafePointerView: __bootstrap.ffi.UnsafePointerView,
|
||||||
UnsafeFnPointer: __bootstrap.ffi.UnsafeFnPointer,
|
UnsafeFnPointer: __bootstrap.ffi.UnsafeFnPointer,
|
||||||
|
|
|
@ -113,9 +113,234 @@ pub extern "C" fn get_sleep_blocking_ptr() -> *const c_void {
|
||||||
sleep_blocking as *const c_void
|
sleep_blocking as *const c_void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn call_fn_ptr(func: Option<extern "C" fn()>) {
|
||||||
|
if func.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let func = func.unwrap();
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn call_fn_ptr_many_parameters(
|
||||||
|
func: Option<
|
||||||
|
extern "C" fn(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, *const u8),
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
if func.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let func = func.unwrap();
|
||||||
|
func(1, -1, 2, -2, 3, -3, 4, -4, 0.5, -0.5, BUFFER.as_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn call_fn_ptr_return_u8(func: Option<extern "C" fn() -> u8>) {
|
||||||
|
if func.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let func = func.unwrap();
|
||||||
|
println!("u8: {}", func());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn call_fn_ptr_return_buffer(
|
||||||
|
func: Option<extern "C" fn() -> *const u8>,
|
||||||
|
) {
|
||||||
|
if func.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let func = func.unwrap();
|
||||||
|
let ptr = func();
|
||||||
|
let buf = unsafe { std::slice::from_raw_parts(ptr, 8) };
|
||||||
|
println!("buf: {:?}", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut STORED_FUNCTION: Option<extern "C" fn()> = None;
|
||||||
|
static mut STORED_FUNCTION_2: Option<extern "C" fn(u8) -> u8> = None;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn store_function(func: Option<extern "C" fn()>) {
|
||||||
|
unsafe { STORED_FUNCTION = func };
|
||||||
|
if func.is_none() {
|
||||||
|
println!("STORED_FUNCTION cleared");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn store_function_2(func: Option<extern "C" fn(u8) -> u8>) {
|
||||||
|
unsafe { STORED_FUNCTION_2 = func };
|
||||||
|
if func.is_none() {
|
||||||
|
println!("STORED_FUNCTION_2 cleared");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn call_stored_function() {
|
||||||
|
unsafe {
|
||||||
|
if STORED_FUNCTION.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
STORED_FUNCTION.unwrap()();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn call_stored_function_2(arg: u8) {
|
||||||
|
unsafe {
|
||||||
|
if STORED_FUNCTION_2.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println!("{}", STORED_FUNCTION_2.unwrap()(arg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FFI performance helper functions
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop() {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_u8(_a: u8) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_i8(_a: i8) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_u16(_a: u16) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_i16(_a: i16) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_u32(_a: u32) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_i32(_a: i32) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_u64(_a: u64) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_i64(_a: i64) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_usize(_a: usize) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_isize(_a: isize) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_f32(_a: f32) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_f64(_a: f64) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_buffer(_buffer: *mut [u8; 8]) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_u8() -> u8 {
|
||||||
|
255
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_i8() -> i8 {
|
||||||
|
-128
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_u16() -> u16 {
|
||||||
|
65535
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_i16() -> i16 {
|
||||||
|
-32768
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_u32() -> u32 {
|
||||||
|
4294967295
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_i32() -> i32 {
|
||||||
|
-2147483648
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_u64() -> u64 {
|
||||||
|
18446744073709551615
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_i64() -> i64 {
|
||||||
|
-9223372036854775808
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_usize() -> usize {
|
||||||
|
18446744073709551615
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_isize() -> isize {
|
||||||
|
-9223372036854775808
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_f32() -> f32 {
|
||||||
|
#[allow(clippy::excessive_precision)]
|
||||||
|
0.20298023223876953125
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_f64() -> f64 {
|
||||||
|
1e-10
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameters iteration
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn nop_many_parameters(
|
||||||
|
_: u8,
|
||||||
|
_: i8,
|
||||||
|
_: u16,
|
||||||
|
_: i16,
|
||||||
|
_: u32,
|
||||||
|
_: i32,
|
||||||
|
_: u64,
|
||||||
|
_: i64,
|
||||||
|
_: usize,
|
||||||
|
_: isize,
|
||||||
|
_: f32,
|
||||||
|
_: f64,
|
||||||
|
_: *mut [u8; 8],
|
||||||
|
_: u8,
|
||||||
|
_: i8,
|
||||||
|
_: u16,
|
||||||
|
_: i16,
|
||||||
|
_: u32,
|
||||||
|
_: i32,
|
||||||
|
_: u64,
|
||||||
|
_: i64,
|
||||||
|
_: usize,
|
||||||
|
_: isize,
|
||||||
|
_: f32,
|
||||||
|
_: f64,
|
||||||
|
_: *mut [u8; 8],
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statics
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static static_u32: u32 = 42;
|
pub static static_u32: u32 = 42;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub static static_i64: i64 = -1242464576485;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Structure {
|
pub struct Structure {
|
||||||
_data: u32,
|
_data: u32,
|
||||||
|
|
503
test_ffi/tests/bench.js
Normal file
503
test_ffi/tests/bench.js
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
|
const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
|
||||||
|
const [libPrefix, libSuffix] = {
|
||||||
|
darwin: ["lib", "dylib"],
|
||||||
|
linux: ["lib", "so"],
|
||||||
|
windows: ["", "dll"],
|
||||||
|
}[Deno.build.os];
|
||||||
|
const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`;
|
||||||
|
|
||||||
|
const dylib = Deno.dlopen(libPath, {
|
||||||
|
"nop": { parameters: [], result: "void" },
|
||||||
|
"nop_u8": { parameters: ["u8"], result: "void" },
|
||||||
|
"nop_i8": { parameters: ["i8"], result: "void" },
|
||||||
|
"nop_u16": { parameters: ["u16"], result: "void" },
|
||||||
|
"nop_i16": { parameters: ["i16"], result: "void" },
|
||||||
|
"nop_u32": { parameters: ["u32"], result: "void" },
|
||||||
|
"nop_i32": { parameters: ["i32"], result: "void" },
|
||||||
|
"nop_u64": { parameters: ["u64"], result: "void" },
|
||||||
|
"nop_i64": { parameters: ["i64"], result: "void" },
|
||||||
|
"nop_usize": { parameters: ["usize"], result: "void" },
|
||||||
|
"nop_isize": { parameters: ["isize"], result: "void" },
|
||||||
|
"nop_f32": { parameters: ["f32"], result: "void" },
|
||||||
|
"nop_f64": { parameters: ["f64"], result: "void" },
|
||||||
|
"nop_buffer": { parameters: ["pointer"], result: "void" },
|
||||||
|
"return_u8": { parameters: [], result: "u8" },
|
||||||
|
"return_i8": { parameters: [], result: "i8" },
|
||||||
|
"return_u16": { parameters: [], result: "u16" },
|
||||||
|
"return_i16": { parameters: [], result: "i16" },
|
||||||
|
"return_u32": { parameters: [], result: "u32" },
|
||||||
|
"return_i32": { parameters: [], result: "i32" },
|
||||||
|
"return_u64": { parameters: [], result: "u64" },
|
||||||
|
"return_i64": { parameters: [], result: "i64" },
|
||||||
|
"return_usize": { parameters: [], result: "usize" },
|
||||||
|
"return_isize": { parameters: [], result: "isize" },
|
||||||
|
"return_f32": { parameters: [], result: "f32" },
|
||||||
|
"return_f64": { parameters: [], result: "f64" },
|
||||||
|
"return_buffer": { parameters: [], result: "pointer" },
|
||||||
|
// Nonblocking calls
|
||||||
|
"nop_nonblocking": { name: "nop", parameters: [], result: "void" },
|
||||||
|
"nop_u8_nonblocking": { name: "nop_u8", parameters: ["u8"], result: "void" },
|
||||||
|
"nop_i8_nonblocking": { name: "nop_i8", parameters: ["i8"], result: "void" },
|
||||||
|
"nop_u16_nonblocking": {
|
||||||
|
name: "nop_u16",
|
||||||
|
parameters: ["u16"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_i16_nonblocking": {
|
||||||
|
name: "nop_i16",
|
||||||
|
parameters: ["i16"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_u32_nonblocking": {
|
||||||
|
name: "nop_u32",
|
||||||
|
parameters: ["u32"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_i32_nonblocking": {
|
||||||
|
name: "nop_i32",
|
||||||
|
parameters: ["i32"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_u64_nonblocking": {
|
||||||
|
name: "nop_u64",
|
||||||
|
parameters: ["u64"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_i64_nonblocking": {
|
||||||
|
name: "nop_i64",
|
||||||
|
parameters: ["i64"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_usize_nonblocking": {
|
||||||
|
name: "nop_usize",
|
||||||
|
parameters: ["usize"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_isize_nonblocking": {
|
||||||
|
name: "nop_isize",
|
||||||
|
parameters: ["isize"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_f32_nonblocking": {
|
||||||
|
name: "nop_f32",
|
||||||
|
parameters: ["f32"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_f64_nonblocking": {
|
||||||
|
name: "nop_f64",
|
||||||
|
parameters: ["f64"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_buffer_nonblocking": {
|
||||||
|
name: "nop_buffer",
|
||||||
|
parameters: ["pointer"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"return_u8_nonblocking": { name: "return_u8", parameters: [], result: "u8" },
|
||||||
|
"return_i8_nonblocking": { name: "return_i8", parameters: [], result: "i8" },
|
||||||
|
"return_u16_nonblocking": {
|
||||||
|
name: "return_u16",
|
||||||
|
parameters: [],
|
||||||
|
result: "u16",
|
||||||
|
},
|
||||||
|
"return_i16_nonblocking": {
|
||||||
|
name: "return_i16",
|
||||||
|
parameters: [],
|
||||||
|
result: "i16",
|
||||||
|
},
|
||||||
|
"return_u32_nonblocking": {
|
||||||
|
name: "return_u32",
|
||||||
|
parameters: [],
|
||||||
|
result: "u32",
|
||||||
|
},
|
||||||
|
"return_i32_nonblocking": {
|
||||||
|
name: "return_i32",
|
||||||
|
parameters: [],
|
||||||
|
result: "i32",
|
||||||
|
},
|
||||||
|
"return_u64_nonblocking": {
|
||||||
|
name: "return_u64",
|
||||||
|
parameters: [],
|
||||||
|
result: "u64",
|
||||||
|
},
|
||||||
|
"return_i64_nonblocking": {
|
||||||
|
name: "return_i64",
|
||||||
|
parameters: [],
|
||||||
|
result: "i64",
|
||||||
|
},
|
||||||
|
"return_usize_nonblocking": {
|
||||||
|
name: "return_usize",
|
||||||
|
parameters: [],
|
||||||
|
result: "usize",
|
||||||
|
},
|
||||||
|
"return_isize_nonblocking": {
|
||||||
|
name: "return_isize",
|
||||||
|
parameters: [],
|
||||||
|
result: "isize",
|
||||||
|
},
|
||||||
|
"return_f32_nonblocking": {
|
||||||
|
name: "return_f32",
|
||||||
|
parameters: [],
|
||||||
|
result: "f32",
|
||||||
|
},
|
||||||
|
"return_f64_nonblocking": {
|
||||||
|
name: "return_f64",
|
||||||
|
parameters: [],
|
||||||
|
result: "f64",
|
||||||
|
},
|
||||||
|
"return_buffer_nonblocking": {
|
||||||
|
name: "return_buffer",
|
||||||
|
parameters: [],
|
||||||
|
result: "pointer",
|
||||||
|
},
|
||||||
|
// Parameter checking
|
||||||
|
"nop_many_parameters": {
|
||||||
|
parameters: [
|
||||||
|
"u8",
|
||||||
|
"i8",
|
||||||
|
"u16",
|
||||||
|
"i16",
|
||||||
|
"u32",
|
||||||
|
"i32",
|
||||||
|
"u64",
|
||||||
|
"i64",
|
||||||
|
"usize",
|
||||||
|
"isize",
|
||||||
|
"f32",
|
||||||
|
"f64",
|
||||||
|
"pointer",
|
||||||
|
"u8",
|
||||||
|
"i8",
|
||||||
|
"u16",
|
||||||
|
"i16",
|
||||||
|
"u32",
|
||||||
|
"i32",
|
||||||
|
"u64",
|
||||||
|
"i64",
|
||||||
|
"usize",
|
||||||
|
"isize",
|
||||||
|
"f32",
|
||||||
|
"f64",
|
||||||
|
"pointer",
|
||||||
|
],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
"nop_many_parameters_nonblocking": {
|
||||||
|
name: "nop_many_parameters",
|
||||||
|
parameters: [
|
||||||
|
"u8",
|
||||||
|
"i8",
|
||||||
|
"u16",
|
||||||
|
"i16",
|
||||||
|
"u32",
|
||||||
|
"i32",
|
||||||
|
"u64",
|
||||||
|
"i64",
|
||||||
|
"usize",
|
||||||
|
"isize",
|
||||||
|
"f32",
|
||||||
|
"f64",
|
||||||
|
"pointer",
|
||||||
|
"u8",
|
||||||
|
"i8",
|
||||||
|
"u16",
|
||||||
|
"i16",
|
||||||
|
"u32",
|
||||||
|
"i32",
|
||||||
|
"u64",
|
||||||
|
"i64",
|
||||||
|
"usize",
|
||||||
|
"isize",
|
||||||
|
"f32",
|
||||||
|
"f64",
|
||||||
|
"pointer",
|
||||||
|
],
|
||||||
|
result: "void",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop()", () => {
|
||||||
|
dylib.symbols.nop();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_u8()", () => {
|
||||||
|
dylib.symbols.nop_u8(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_i8()", () => {
|
||||||
|
dylib.symbols.nop_i8(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_u16()", () => {
|
||||||
|
dylib.symbols.nop_u16(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_i16()", () => {
|
||||||
|
dylib.symbols.nop_i16(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_u32()", () => {
|
||||||
|
dylib.symbols.nop_u32(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_i32()", () => {
|
||||||
|
dylib.symbols.nop_i32(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_u64()", () => {
|
||||||
|
dylib.symbols.nop_u64(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_i64()", () => {
|
||||||
|
dylib.symbols.nop_i64(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_usize()", () => {
|
||||||
|
dylib.symbols.nop_usize(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_isize()", () => {
|
||||||
|
dylib.symbols.nop_isize(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_f32()", () => {
|
||||||
|
dylib.symbols.nop_f32(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_f64()", () => {
|
||||||
|
dylib.symbols.nop_f64(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
const buffer = new Uint8Array(8).fill(5);
|
||||||
|
Deno.bench("nop_buffer()", () => {
|
||||||
|
dylib.symbols.nop_buffer(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_u8()", () => {
|
||||||
|
dylib.symbols.return_u8();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_i8()", () => {
|
||||||
|
dylib.symbols.return_i8();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_u16()", () => {
|
||||||
|
dylib.symbols.return_u16();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_i16()", () => {
|
||||||
|
dylib.symbols.return_i16();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_u32()", () => {
|
||||||
|
dylib.symbols.return_u32();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_i32()", () => {
|
||||||
|
dylib.symbols.return_i32();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_u64()", () => {
|
||||||
|
dylib.symbols.return_u64();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_i64()", () => {
|
||||||
|
dylib.symbols.return_i64();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_usize()", () => {
|
||||||
|
dylib.symbols.return_usize();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_isize()", () => {
|
||||||
|
dylib.symbols.return_isize();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_f32()", () => {
|
||||||
|
dylib.symbols.return_f32();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_f64()", () => {
|
||||||
|
dylib.symbols.return_f64();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_buffer()", () => {
|
||||||
|
dylib.symbols.return_buffer();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Nonblocking calls
|
||||||
|
|
||||||
|
Deno.bench("nop_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_u8_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_u8_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_i8_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_i8_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_u16_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_u16_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_i16_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_i16_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_u32_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_u32_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_i32_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_i32_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_u64_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_u64_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_i64_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_i64_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_usize_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_usize_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_isize_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_isize_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_f32_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_f32_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_f64_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_f64_nonblocking(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_buffer_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.nop_buffer_nonblocking(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_u8_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_u8_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_i8_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_i8_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_u16_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_u16_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_i16_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_i16_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_u32_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_u32_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_i32_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_i32_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_u64_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_u64_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_i64_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_i64_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_usize_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_usize_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_isize_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_isize_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_f32_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_f32_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_f64_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_f64_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("return_buffer_nonblocking()", async () => {
|
||||||
|
await dylib.symbols.return_buffer_nonblocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
const buffer2 = new Uint8Array(8).fill(25);
|
||||||
|
Deno.bench("nop_many_parameters()", () => {
|
||||||
|
dylib.symbols.nop_many_parameters(
|
||||||
|
135,
|
||||||
|
47,
|
||||||
|
356,
|
||||||
|
-236,
|
||||||
|
7457,
|
||||||
|
-1356,
|
||||||
|
16471468n,
|
||||||
|
-1334748136n,
|
||||||
|
132658769535n,
|
||||||
|
-42745856824n,
|
||||||
|
13567.26437,
|
||||||
|
7.686234e-3,
|
||||||
|
buffer,
|
||||||
|
64,
|
||||||
|
-42,
|
||||||
|
83,
|
||||||
|
-136,
|
||||||
|
3657,
|
||||||
|
-2376,
|
||||||
|
3277918n,
|
||||||
|
-474628146n,
|
||||||
|
344657895n,
|
||||||
|
-2436732n,
|
||||||
|
135.26437e3,
|
||||||
|
264.3576468623546834,
|
||||||
|
buffer2,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.bench("nop_many_parameters_nonblocking()", () => {
|
||||||
|
dylib.symbols.nop_many_parameters_nonblocking(
|
||||||
|
135,
|
||||||
|
47,
|
||||||
|
356,
|
||||||
|
-236,
|
||||||
|
7457,
|
||||||
|
-1356,
|
||||||
|
16471468n,
|
||||||
|
-1334748136n,
|
||||||
|
132658769535n,
|
||||||
|
-42745856824n,
|
||||||
|
13567.26437,
|
||||||
|
7.686234e-3,
|
||||||
|
buffer,
|
||||||
|
64,
|
||||||
|
-42,
|
||||||
|
83,
|
||||||
|
-136,
|
||||||
|
3657,
|
||||||
|
-2376,
|
||||||
|
3277918n,
|
||||||
|
-474628146n,
|
||||||
|
344657895n,
|
||||||
|
-2436732n,
|
||||||
|
135.26437e3,
|
||||||
|
264.3576468623546834,
|
||||||
|
buffer2,
|
||||||
|
);
|
||||||
|
});
|
|
@ -24,6 +24,27 @@ const remote = Deno.dlopen(
|
||||||
method17: { parameters: [], result: "usize", nonblocking: true },
|
method17: { parameters: [], result: "usize", nonblocking: true },
|
||||||
method18: { parameters: [], result: "pointer" },
|
method18: { parameters: [], result: "pointer" },
|
||||||
method19: { parameters: [], result: "pointer", nonblocking: true },
|
method19: { parameters: [], result: "pointer", nonblocking: true },
|
||||||
|
method20: {
|
||||||
|
parameters: [{
|
||||||
|
function: { parameters: ["u8", "u32", "pointer"], result: "void" },
|
||||||
|
}],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
method21: {
|
||||||
|
parameters: [
|
||||||
|
{ function: { parameters: [], result: "u8" } },
|
||||||
|
],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
method22: {
|
||||||
|
parameters: [{
|
||||||
|
function: {
|
||||||
|
parameters: [],
|
||||||
|
result: { function: { parameters: [], result: "u8" } },
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
static1: { type: "usize" },
|
static1: { type: "usize" },
|
||||||
static2: { type: "pointer" },
|
static2: { type: "pointer" },
|
||||||
static3: { type: "usize" },
|
static3: { type: "usize" },
|
||||||
|
@ -41,6 +62,23 @@ const remote = Deno.dlopen(
|
||||||
} as const,
|
} as const,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Deno.dlopen(
|
||||||
|
"dummy_lib_2.so",
|
||||||
|
// @ts-expect-error: Returning a function pointer
|
||||||
|
// is declared using "pointer" + UnsafeFnPointer
|
||||||
|
{
|
||||||
|
wrong_method1: {
|
||||||
|
parameters: [],
|
||||||
|
result: {
|
||||||
|
function: {
|
||||||
|
parameters: [],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const,
|
||||||
|
);
|
||||||
|
|
||||||
// @ts-expect-error: Invalid argument
|
// @ts-expect-error: Invalid argument
|
||||||
remote.symbols.method1(0);
|
remote.symbols.method1(0);
|
||||||
// @ts-expect-error: Invalid return type
|
// @ts-expect-error: Invalid return type
|
||||||
|
@ -136,6 +174,100 @@ const fnptr = new Deno.UnsafeFnPointer(
|
||||||
fnptr.call(null, null);
|
fnptr.call(null, null);
|
||||||
fnptr.call(0, null);
|
fnptr.call(0, null);
|
||||||
|
|
||||||
|
const unsafe_callback_wrong1 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: ["i8"],
|
||||||
|
result: "void",
|
||||||
|
} as const,
|
||||||
|
// @ts-expect-error: i8 is not a pointer
|
||||||
|
(_: Deno.UnsafePointer) => {},
|
||||||
|
);
|
||||||
|
const unsafe_callback_wrong2 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: ["pointer"],
|
||||||
|
result: "u64",
|
||||||
|
} as const,
|
||||||
|
// @ts-expect-error: must return a number or bigint
|
||||||
|
(_: Deno.UnsafePointer) => {},
|
||||||
|
);
|
||||||
|
const unsafe_callback_wrong3 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: [],
|
||||||
|
result: "void",
|
||||||
|
} as const,
|
||||||
|
// @ts-expect-error: no parameters
|
||||||
|
(_: Deno.UnsafePointer) => {},
|
||||||
|
);
|
||||||
|
const unsafe_callback_wrong4 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: ["u64"],
|
||||||
|
result: "void",
|
||||||
|
} as const,
|
||||||
|
// @ts-expect-error: Callback's 64bit parameters are always called as bigint
|
||||||
|
(_: number) => {},
|
||||||
|
);
|
||||||
|
const unsafe_callback_right1 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: ["u8", "u32", "pointer"],
|
||||||
|
result: "void",
|
||||||
|
} as const,
|
||||||
|
(_1: number, _2: number, _3: Deno.UnsafePointer) => {},
|
||||||
|
);
|
||||||
|
const unsafe_callback_right2 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: [],
|
||||||
|
result: "u8",
|
||||||
|
} as const,
|
||||||
|
() => 3,
|
||||||
|
);
|
||||||
|
const unsafe_callback_right3 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: [],
|
||||||
|
result: {
|
||||||
|
function: {
|
||||||
|
parameters: [],
|
||||||
|
result: "u8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const,
|
||||||
|
// Callbacks can return other callbacks, if really wanted.
|
||||||
|
() => unsafe_callback_right2,
|
||||||
|
);
|
||||||
|
const unsafe_callback_right4 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: ["u8", "u32", "pointer"],
|
||||||
|
result: "u8",
|
||||||
|
} as const,
|
||||||
|
(_1: number, _2: number, _3: Deno.UnsafePointer) => 3,
|
||||||
|
);
|
||||||
|
const unsafe_callback_right5 = new Deno.UnsafeCallback(
|
||||||
|
{
|
||||||
|
parameters: ["u8", "i32", "pointer"],
|
||||||
|
result: "void",
|
||||||
|
} as const,
|
||||||
|
(_1: number, _2: number, _3: Deno.UnsafePointer) => {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// @ts-expect-error: Must pass callback
|
||||||
|
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
|
||||||
|
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);
|
||||||
|
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static1_wrong: null = remote.symbols.static1;
|
const static1_wrong: null = remote.symbols.static1;
|
||||||
const static1_right: bigint = remote.symbols.static1;
|
const static1_right: bigint = remote.symbols.static1;
|
||||||
|
|
|
@ -65,11 +65,27 @@ fn basic() {
|
||||||
-8589934590n\n\
|
-8589934590n\n\
|
||||||
579.9119873046875\n\
|
579.9119873046875\n\
|
||||||
579.912\n\
|
579.912\n\
|
||||||
|
579\n\
|
||||||
|
8589934590n\n\
|
||||||
|
-8589934590n\n\
|
||||||
|
8589934590n\n\
|
||||||
|
-8589934590n\n\
|
||||||
|
579.9119873046875\n\
|
||||||
|
579.912\n\
|
||||||
After sleep_blocking\n\
|
After sleep_blocking\n\
|
||||||
true\n\
|
true\n\
|
||||||
Before\n\
|
Before\n\
|
||||||
true\n\
|
true\n\
|
||||||
|
logCallback\n\
|
||||||
|
1 -1 2 -2 3 -3 4n -4n 0.5 -0.5 1 2 3 4 5 6 7 8\n\
|
||||||
|
u8: 8\n\
|
||||||
|
buf: [1, 2, 3, 4, 5, 6, 7, 8]\n\
|
||||||
|
logCallback\n\
|
||||||
|
30\n\
|
||||||
|
STORED_FUNCTION cleared\n\
|
||||||
|
STORED_FUNCTION_2 cleared\n\
|
||||||
Static u32: 42\n\
|
Static u32: 42\n\
|
||||||
|
Static i64: -1242464576485n\n\
|
||||||
Static ptr: true\n\
|
Static ptr: true\n\
|
||||||
Static ptr value: 42\n\
|
Static ptr value: 42\n\
|
||||||
After\n\
|
After\n\
|
||||||
|
|
|
@ -52,6 +52,54 @@ const dylib = Deno.dlopen(libPath, {
|
||||||
"add_isize": { parameters: ["isize", "isize"], result: "isize" },
|
"add_isize": { parameters: ["isize", "isize"], result: "isize" },
|
||||||
"add_f32": { parameters: ["f32", "f32"], result: "f32" },
|
"add_f32": { parameters: ["f32", "f32"], result: "f32" },
|
||||||
"add_f64": { parameters: ["f64", "f64"], result: "f64" },
|
"add_f64": { parameters: ["f64", "f64"], result: "f64" },
|
||||||
|
"add_u32_nonblocking": {
|
||||||
|
name: "add_u32",
|
||||||
|
parameters: ["u32", "u32"],
|
||||||
|
result: "u32",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
|
"add_i32_nonblocking": {
|
||||||
|
name: "add_i32",
|
||||||
|
parameters: ["i32", "i32"],
|
||||||
|
result: "i32",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
|
"add_u64_nonblocking": {
|
||||||
|
name: "add_u64",
|
||||||
|
parameters: ["u64", "u64"],
|
||||||
|
result: "u64",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
|
"add_i64_nonblocking": {
|
||||||
|
name: "add_i64",
|
||||||
|
parameters: ["i64", "i64"],
|
||||||
|
result: "i64",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
|
"add_usize_nonblocking": {
|
||||||
|
name: "add_usize",
|
||||||
|
parameters: ["usize", "usize"],
|
||||||
|
result: "usize",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
|
"add_isize_nonblocking": {
|
||||||
|
name: "add_isize",
|
||||||
|
parameters: ["isize", "isize"],
|
||||||
|
result: "isize",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
|
"add_f32_nonblocking": {
|
||||||
|
name: "add_f32",
|
||||||
|
parameters: ["f32", "f32"],
|
||||||
|
result: "f32",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
|
"add_f64_nonblocking": {
|
||||||
|
name: "add_f64",
|
||||||
|
parameters: ["f64", "f64"],
|
||||||
|
result: "f64",
|
||||||
|
nonblocking: true,
|
||||||
|
},
|
||||||
"fill_buffer": { parameters: ["u8", "pointer", "usize"], result: "void" },
|
"fill_buffer": { parameters: ["u8", "pointer", "usize"], result: "void" },
|
||||||
"sleep_nonblocking": {
|
"sleep_nonblocking": {
|
||||||
name: "sleep_blocking",
|
name: "sleep_blocking",
|
||||||
|
@ -73,9 +121,63 @@ const dylib = Deno.dlopen(libPath, {
|
||||||
parameters: [],
|
parameters: [],
|
||||||
result: "pointer",
|
result: "pointer",
|
||||||
},
|
},
|
||||||
|
// Callback function
|
||||||
|
call_fn_ptr: {
|
||||||
|
parameters: [{ function: { parameters: [], result: "void" } }],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
call_fn_ptr_many_parameters: {
|
||||||
|
parameters: [{
|
||||||
|
function: {
|
||||||
|
parameters: [
|
||||||
|
"u8",
|
||||||
|
"i8",
|
||||||
|
"u16",
|
||||||
|
"i16",
|
||||||
|
"u32",
|
||||||
|
"i32",
|
||||||
|
"u64",
|
||||||
|
"i64",
|
||||||
|
"f32",
|
||||||
|
"f64",
|
||||||
|
"pointer",
|
||||||
|
],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
call_fn_ptr_return_u8: {
|
||||||
|
parameters: [{ function: { parameters: [], result: "u8" } }],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
call_fn_ptr_return_buffer: {
|
||||||
|
parameters: [{ function: { parameters: [], result: "pointer" } }],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
store_function: {
|
||||||
|
parameters: [{ function: { parameters: [], result: "void" } }],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
store_function_2: {
|
||||||
|
parameters: [{ function: { parameters: ["u8"], result: "u8" } }],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
call_stored_function: {
|
||||||
|
parameters: [],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
call_stored_function_2: {
|
||||||
|
parameters: ["u8"],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
// Statics
|
||||||
"static_u32": {
|
"static_u32": {
|
||||||
type: "u32",
|
type: "u32",
|
||||||
},
|
},
|
||||||
|
"static_i64": {
|
||||||
|
type: "i64",
|
||||||
|
},
|
||||||
"static_ptr": {
|
"static_ptr": {
|
||||||
type: "pointer",
|
type: "pointer",
|
||||||
},
|
},
|
||||||
|
@ -135,14 +237,14 @@ assertThrows(
|
||||||
dylib.symbols.add_u32(-1, 100);
|
dylib.symbols.add_u32(-1, 100);
|
||||||
},
|
},
|
||||||
TypeError,
|
TypeError,
|
||||||
"Expected FFI argument to be an unsigned integer, but got Number(-1)",
|
"Expected FFI argument to be an unsigned integer, but got '-1'",
|
||||||
);
|
);
|
||||||
assertThrows(
|
assertThrows(
|
||||||
() => {
|
() => {
|
||||||
dylib.symbols.add_u32(null, 100);
|
dylib.symbols.add_u32(null, 100);
|
||||||
},
|
},
|
||||||
TypeError,
|
TypeError,
|
||||||
"Expected FFI argument to be an unsigned integer, but got Null",
|
"Expected FFI argument to be an unsigned integer, but got 'null'",
|
||||||
);
|
);
|
||||||
console.log(dylib.symbols.add_i32(123, 456));
|
console.log(dylib.symbols.add_i32(123, 456));
|
||||||
console.log(dylib.symbols.add_u64(0xffffffffn, 0xffffffffn));
|
console.log(dylib.symbols.add_u64(0xffffffffn, 0xffffffffn));
|
||||||
|
@ -152,6 +254,21 @@ console.log(dylib.symbols.add_isize(-0xffffffffn, -0xffffffffn));
|
||||||
console.log(dylib.symbols.add_f32(123.123, 456.789));
|
console.log(dylib.symbols.add_f32(123.123, 456.789));
|
||||||
console.log(dylib.symbols.add_f64(123.123, 456.789));
|
console.log(dylib.symbols.add_f64(123.123, 456.789));
|
||||||
|
|
||||||
|
// Test adders as nonblocking calls
|
||||||
|
console.log(await dylib.symbols.add_i32_nonblocking(123, 456));
|
||||||
|
console.log(await dylib.symbols.add_u64_nonblocking(0xffffffffn, 0xffffffffn));
|
||||||
|
console.log(
|
||||||
|
await dylib.symbols.add_i64_nonblocking(-0xffffffffn, -0xffffffffn),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
await dylib.symbols.add_usize_nonblocking(0xffffffffn, 0xffffffffn),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
await dylib.symbols.add_isize_nonblocking(-0xffffffffn, -0xffffffffn),
|
||||||
|
);
|
||||||
|
console.log(await dylib.symbols.add_f32_nonblocking(123.123, 456.789));
|
||||||
|
console.log(await dylib.symbols.add_f64_nonblocking(123.123, 456.789));
|
||||||
|
|
||||||
// test mutating sync calls
|
// test mutating sync calls
|
||||||
|
|
||||||
function test_fill_buffer(fillValue, arr) {
|
function test_fill_buffer(fillValue, arr) {
|
||||||
|
@ -207,7 +324,84 @@ dylib.symbols.sleep_nonblocking(100).then(() => {
|
||||||
console.log("Before");
|
console.log("Before");
|
||||||
console.log(performance.now() - start < 100);
|
console.log(performance.now() - start < 100);
|
||||||
|
|
||||||
|
// Test calls with callback parameters
|
||||||
|
const logCallback = new Deno.UnsafeCallback(
|
||||||
|
{ parameters: [], result: "void" },
|
||||||
|
() => console.log("logCallback"),
|
||||||
|
);
|
||||||
|
const logManyParametersCallback = new Deno.UnsafeCallback({
|
||||||
|
parameters: [
|
||||||
|
"u8",
|
||||||
|
"i8",
|
||||||
|
"u16",
|
||||||
|
"i16",
|
||||||
|
"u32",
|
||||||
|
"i32",
|
||||||
|
"u64",
|
||||||
|
"i64",
|
||||||
|
"f32",
|
||||||
|
"f64",
|
||||||
|
"pointer",
|
||||||
|
],
|
||||||
|
result: "void",
|
||||||
|
}, (u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, pointer) => {
|
||||||
|
const view = new Deno.UnsafePointerView(new Deno.UnsafePointer(pointer));
|
||||||
|
const copy_buffer = new Uint8Array(8);
|
||||||
|
view.copyInto(copy_buffer);
|
||||||
|
console.log(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, ...copy_buffer);
|
||||||
|
});
|
||||||
|
const returnU8Callback = new Deno.UnsafeCallback(
|
||||||
|
{ parameters: [], result: "u8" },
|
||||||
|
() => 8,
|
||||||
|
);
|
||||||
|
const returnBufferCallback = new Deno.UnsafeCallback({
|
||||||
|
parameters: [],
|
||||||
|
result: "pointer",
|
||||||
|
}, () => {
|
||||||
|
return buffer;
|
||||||
|
});
|
||||||
|
const add10Callback = new Deno.UnsafeCallback({
|
||||||
|
parameters: ["u8"],
|
||||||
|
result: "u8",
|
||||||
|
}, (value) => value + 10);
|
||||||
|
const throwCallback = new Deno.UnsafeCallback({
|
||||||
|
parameters: [],
|
||||||
|
result: "void",
|
||||||
|
}, () => {
|
||||||
|
throw new TypeError("hi");
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
() => {
|
||||||
|
dylib.symbols.call_fn_ptr(throwCallback);
|
||||||
|
},
|
||||||
|
TypeError,
|
||||||
|
"hi",
|
||||||
|
);
|
||||||
|
|
||||||
|
dylib.symbols.call_fn_ptr(logCallback);
|
||||||
|
dylib.symbols.call_fn_ptr_many_parameters(logManyParametersCallback);
|
||||||
|
dylib.symbols.call_fn_ptr_return_u8(returnU8Callback);
|
||||||
|
dylib.symbols.call_fn_ptr_return_buffer(returnBufferCallback);
|
||||||
|
dylib.symbols.store_function(logCallback);
|
||||||
|
dylib.symbols.call_stored_function();
|
||||||
|
dylib.symbols.store_function_2(add10Callback);
|
||||||
|
dylib.symbols.call_stored_function_2(20);
|
||||||
|
|
||||||
|
const nestedCallback = new Deno.UnsafeCallback(
|
||||||
|
{ parameters: [], result: "void" },
|
||||||
|
() => {
|
||||||
|
dylib.symbols.call_stored_function_2(10);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
dylib.symbols.store_function(nestedCallback);
|
||||||
|
|
||||||
|
dylib.symbols.store_function(null);
|
||||||
|
dylib.symbols.store_function_2(null);
|
||||||
|
|
||||||
|
// Test statics
|
||||||
console.log("Static u32:", dylib.symbols.static_u32);
|
console.log("Static u32:", dylib.symbols.static_u32);
|
||||||
|
console.log("Static i64:", dylib.symbols.static_i64);
|
||||||
console.log(
|
console.log(
|
||||||
"Static ptr:",
|
"Static ptr:",
|
||||||
dylib.symbols.static_ptr instanceof Deno.UnsafePointer,
|
dylib.symbols.static_ptr instanceof Deno.UnsafePointer,
|
||||||
|
@ -217,6 +411,13 @@ console.log("Static ptr value:", view.getUint32());
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
dylib.close();
|
dylib.close();
|
||||||
|
throwCallback.close();
|
||||||
|
logCallback.close();
|
||||||
|
logManyParametersCallback.close();
|
||||||
|
returnU8Callback.close();
|
||||||
|
returnBufferCallback.close();
|
||||||
|
add10Callback.close();
|
||||||
|
nestedCallback.close();
|
||||||
|
|
||||||
const resourcesPost = Deno.resources();
|
const resourcesPost = Deno.resources();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue