mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
feat(ext/ffi): Replace pointer integers with v8::External objects (#16889)
This commit is contained in:
parent
2bd7482295
commit
b56b8c8a75
47 changed files with 834 additions and 373 deletions
|
@ -2824,6 +2824,30 @@ itest!(unstable_ffi_15 {
|
||||||
exit_code: 70,
|
exit_code: 70,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(unstable_ffi_16 {
|
||||||
|
args: "run run/ffi/unstable_ffi_16.js",
|
||||||
|
output: "run/ffi/unstable_ffi_16.js.out",
|
||||||
|
exit_code: 70,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(unstable_ffi_17 {
|
||||||
|
args: "run run/ffi/unstable_ffi_17.js",
|
||||||
|
output: "run/ffi/unstable_ffi_17.js.out",
|
||||||
|
exit_code: 70,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(unstable_ffi_18 {
|
||||||
|
args: "run run/ffi/unstable_ffi_18.js",
|
||||||
|
output: "run/ffi/unstable_ffi_18.js.out",
|
||||||
|
exit_code: 70,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(unstable_ffi_19 {
|
||||||
|
args: "run run/ffi/unstable_ffi_19.js",
|
||||||
|
output: "run/ffi/unstable_ffi_19.js.out",
|
||||||
|
exit_code: 70,
|
||||||
|
});
|
||||||
|
|
||||||
itest!(future_check2 {
|
itest!(future_check2 {
|
||||||
args: "run --check run/future_check.ts",
|
args: "run --check run/future_check.ts",
|
||||||
output: "run/future_check2.out",
|
output: "run/future_check2.out",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_i16(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_read_i16(null, 0);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_u32(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_read_u32(null, 0);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_i32(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_read_i32(null, 0);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_u64(0n, 0, new Uint32Array(2));
|
Deno[Deno.internal].core.ops.op_ffi_read_u64(null, 0, new Uint32Array(2));
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_f32(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_read_f32(null, 0);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_f64(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_read_f64(null, 0);
|
||||||
|
|
1
cli/tests/testdata/run/ffi/unstable_ffi_16.js
vendored
Normal file
1
cli/tests/testdata/run/ffi/unstable_ffi_16.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Deno[Deno.internal].core.ops.op_ffi_ptr_value(null, new Uint32Array(2));
|
1
cli/tests/testdata/run/ffi/unstable_ffi_16.js.out
vendored
Normal file
1
cli/tests/testdata/run/ffi/unstable_ffi_16.js.out
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Unstable API 'Deno.UnsafePointer#value'. The --unstable flag must be provided.
|
1
cli/tests/testdata/run/ffi/unstable_ffi_17.js
vendored
Normal file
1
cli/tests/testdata/run/ffi/unstable_ffi_17.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Deno[Deno.internal].core.ops.op_ffi_get_buf(null, 0, 0);
|
1
cli/tests/testdata/run/ffi/unstable_ffi_17.js.out
vendored
Normal file
1
cli/tests/testdata/run/ffi/unstable_ffi_17.js.out
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Unstable API 'Deno.UnsafePointerView#getArrayBuffer'. The --unstable flag must be provided.
|
1
cli/tests/testdata/run/ffi/unstable_ffi_18.js
vendored
Normal file
1
cli/tests/testdata/run/ffi/unstable_ffi_18.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Deno[Deno.internal].core.ops.op_ffi_ptr_create(null);
|
1
cli/tests/testdata/run/ffi/unstable_ffi_18.js.out
vendored
Normal file
1
cli/tests/testdata/run/ffi/unstable_ffi_18.js.out
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Unstable API 'Deno.UnsafePointer#create'. The --unstable flag must be provided.
|
1
cli/tests/testdata/run/ffi/unstable_ffi_19.js
vendored
Normal file
1
cli/tests/testdata/run/ffi/unstable_ffi_19.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Deno[Deno.internal].core.ops.op_ffi_ptr_equals(null, null);
|
1
cli/tests/testdata/run/ffi/unstable_ffi_19.js.out
vendored
Normal file
1
cli/tests/testdata/run/ffi/unstable_ffi_19.js.out
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Unstable API 'Deno.UnsafePointer#equals'. The --unstable flag must be provided.
|
2
cli/tests/testdata/run/ffi/unstable_ffi_2.js
vendored
2
cli/tests/testdata/run/ffi/unstable_ffi_2.js
vendored
|
@ -1,4 +1,4 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_call_ptr(0n, {
|
Deno[Deno.internal].core.ops.op_ffi_call_ptr(null, {
|
||||||
name: null,
|
name: null,
|
||||||
parameters: [],
|
parameters: [],
|
||||||
result: "void",
|
result: "void",
|
||||||
|
|
2
cli/tests/testdata/run/ffi/unstable_ffi_3.js
vendored
2
cli/tests/testdata/run/ffi/unstable_ffi_3.js
vendored
|
@ -1,4 +1,4 @@
|
||||||
Deno[Deno.internal].core.opAsync("op_ffi_call_ptr_nonblocking", 0n, {
|
Deno[Deno.internal].core.opAsync("op_ffi_call_ptr_nonblocking", null, {
|
||||||
name: null,
|
name: null,
|
||||||
parameters: [],
|
parameters: [],
|
||||||
result: "void",
|
result: "void",
|
||||||
|
|
7
cli/tests/testdata/run/ffi/unstable_ffi_5.js
vendored
7
cli/tests/testdata/run/ffi/unstable_ffi_5.js
vendored
|
@ -1 +1,6 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_buf_copy_into(0n, 0, new Uint8Array(0), 0);
|
Deno[Deno.internal].core.ops.op_ffi_buf_copy_into(
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
new Uint8Array(0),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
2
cli/tests/testdata/run/ffi/unstable_ffi_6.js
vendored
2
cli/tests/testdata/run/ffi/unstable_ffi_6.js
vendored
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_cstr_read(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_cstr_read(null, 0);
|
||||||
|
|
2
cli/tests/testdata/run/ffi/unstable_ffi_7.js
vendored
2
cli/tests/testdata/run/ffi/unstable_ffi_7.js
vendored
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_u8(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_read_u8(null, 0);
|
||||||
|
|
2
cli/tests/testdata/run/ffi/unstable_ffi_8.js
vendored
2
cli/tests/testdata/run/ffi/unstable_ffi_8.js
vendored
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_i8(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_read_i8(null, 0);
|
||||||
|
|
2
cli/tests/testdata/run/ffi/unstable_ffi_9.js
vendored
2
cli/tests/testdata/run/ffi/unstable_ffi_9.js
vendored
|
@ -1 +1 @@
|
||||||
Deno[Deno.internal].core.ops.op_ffi_read_u16(0n, 0);
|
Deno[Deno.internal].core.ops.op_ffi_read_u16(null, 0);
|
||||||
|
|
|
@ -29,7 +29,8 @@ Deno.test({ permissions: { ffi: false } }, function ffiPermissionDenied() {
|
||||||
Deno.dlopen("/usr/lib/libc.so.6", {});
|
Deno.dlopen("/usr/lib/libc.so.6", {});
|
||||||
}, Deno.errors.PermissionDenied);
|
}, Deno.errors.PermissionDenied);
|
||||||
const fnptr = new Deno.UnsafeFnPointer(
|
const fnptr = new Deno.UnsafeFnPointer(
|
||||||
0n,
|
// @ts-expect-error: Not NonNullable but null check is after premissions check.
|
||||||
|
null,
|
||||||
{
|
{
|
||||||
parameters: ["u32", "pointer"],
|
parameters: ["u32", "pointer"],
|
||||||
result: "void",
|
result: "void",
|
||||||
|
@ -41,7 +42,10 @@ Deno.test({ permissions: { ffi: false } }, function ffiPermissionDenied() {
|
||||||
assertThrows(() => {
|
assertThrows(() => {
|
||||||
Deno.UnsafePointer.of(new Uint8Array(0));
|
Deno.UnsafePointer.of(new Uint8Array(0));
|
||||||
}, Deno.errors.PermissionDenied);
|
}, Deno.errors.PermissionDenied);
|
||||||
const ptrView = new Deno.UnsafePointerView(0n);
|
const ptrView = new Deno.UnsafePointerView(
|
||||||
|
// @ts-expect-error: Not NonNullable but null check is after premissions check.
|
||||||
|
null,
|
||||||
|
);
|
||||||
assertThrows(() => {
|
assertThrows(() => {
|
||||||
ptrView.copyInto(new Uint8Array(0));
|
ptrView.copyInto(new Uint8Array(0));
|
||||||
}, Deno.errors.PermissionDenied);
|
}, Deno.errors.PermissionDenied);
|
||||||
|
|
43
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
43
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
|
@ -131,10 +131,10 @@ declare namespace Deno {
|
||||||
*/
|
*/
|
||||||
type ToNativeTypeMap =
|
type ToNativeTypeMap =
|
||||||
& Record<NativeNumberType, number>
|
& Record<NativeNumberType, number>
|
||||||
& Record<NativeBigIntType, PointerValue>
|
& Record<NativeBigIntType, number | bigint>
|
||||||
& Record<NativeBooleanType, boolean>
|
& Record<NativeBooleanType, boolean>
|
||||||
& Record<NativePointerType, PointerValue | null>
|
& Record<NativePointerType, PointerValue>
|
||||||
& Record<NativeFunctionType, PointerValue | null>
|
& Record<NativeFunctionType, PointerValue>
|
||||||
& Record<NativeBufferType, BufferSource | null>;
|
& Record<NativeBufferType, BufferSource | null>;
|
||||||
|
|
||||||
/** **UNSTABLE**: New API, yet to be vetted.
|
/** **UNSTABLE**: New API, yet to be vetted.
|
||||||
|
@ -191,7 +191,7 @@ declare namespace Deno {
|
||||||
*/
|
*/
|
||||||
type FromNativeTypeMap =
|
type FromNativeTypeMap =
|
||||||
& Record<NativeNumberType, number>
|
& Record<NativeNumberType, number>
|
||||||
& Record<NativeBigIntType, PointerValue>
|
& Record<NativeBigIntType, number | bigint>
|
||||||
& Record<NativeBooleanType, boolean>
|
& Record<NativeBooleanType, boolean>
|
||||||
& Record<NativePointerType, PointerValue>
|
& Record<NativePointerType, PointerValue>
|
||||||
& Record<NativeBufferType, PointerValue>
|
& Record<NativeBufferType, PointerValue>
|
||||||
|
@ -340,6 +340,9 @@ declare namespace Deno {
|
||||||
[K in keyof T]: StaticForeignSymbol<T[K]>;
|
[K in keyof T]: StaticForeignSymbol<T[K]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const brand: unique symbol;
|
||||||
|
type PointerObject = { [brand]: unknown };
|
||||||
|
|
||||||
/** **UNSTABLE**: New API, yet to be vetted.
|
/** **UNSTABLE**: New API, yet to be vetted.
|
||||||
*
|
*
|
||||||
* Pointer type depends on the architecture and actual pointer value.
|
* Pointer type depends on the architecture and actual pointer value.
|
||||||
|
@ -350,7 +353,7 @@ declare namespace Deno {
|
||||||
*
|
*
|
||||||
* @category FFI
|
* @category FFI
|
||||||
*/
|
*/
|
||||||
export type PointerValue = number | bigint;
|
export type PointerValue = null | PointerObject;
|
||||||
|
|
||||||
/** **UNSTABLE**: New API, yet to be vetted.
|
/** **UNSTABLE**: New API, yet to be vetted.
|
||||||
*
|
*
|
||||||
|
@ -360,8 +363,16 @@ declare namespace Deno {
|
||||||
* @category FFI
|
* @category FFI
|
||||||
*/
|
*/
|
||||||
export class UnsafePointer {
|
export class UnsafePointer {
|
||||||
|
/** Create a pointer from a numeric value. This is one is <i>really</i> dangerous! */
|
||||||
|
static create(value: number | bigint): PointerValue;
|
||||||
|
/** Returns `true` if the two pointers point to the same address. */
|
||||||
|
static equals(a: PointerValue, b: PointerValue): boolean;
|
||||||
/** Return the direct memory pointer to the typed array in memory. */
|
/** Return the direct memory pointer to the typed array in memory. */
|
||||||
static of(value: Deno.UnsafeCallback | BufferSource): PointerValue;
|
static of(value: Deno.UnsafeCallback | BufferSource): PointerValue;
|
||||||
|
/** Return a new pointer offset from the original by `offset` bytes. */
|
||||||
|
static offset(value: NonNullable<PointerValue>, offset: number): PointerValue
|
||||||
|
/** Get the numeric value of a pointer */
|
||||||
|
static value(value: PointerValue): number | bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** **UNSTABLE**: New API, yet to be vetted.
|
/** **UNSTABLE**: New API, yet to be vetted.
|
||||||
|
@ -374,9 +385,9 @@ declare namespace Deno {
|
||||||
* @category FFI
|
* @category FFI
|
||||||
*/
|
*/
|
||||||
export class UnsafePointerView {
|
export class UnsafePointerView {
|
||||||
constructor(pointer: PointerValue);
|
constructor(pointer: NonNullable<PointerValue>);
|
||||||
|
|
||||||
pointer: PointerValue;
|
pointer: NonNullable<PointerValue>;
|
||||||
|
|
||||||
/** Gets a boolean at the specified byte offset from the pointer. */
|
/** Gets a boolean at the specified byte offset from the pointer. */
|
||||||
getBool(offset?: number): boolean;
|
getBool(offset?: number): boolean;
|
||||||
|
@ -400,29 +411,31 @@ declare namespace Deno {
|
||||||
getInt32(offset?: number): number;
|
getInt32(offset?: number): number;
|
||||||
/** Gets an unsigned 64-bit integer at the specified byte offset from the
|
/** Gets an unsigned 64-bit integer at the specified byte offset from the
|
||||||
* pointer. */
|
* pointer. */
|
||||||
getBigUint64(offset?: number): PointerValue;
|
getBigUint64(offset?: number): number | bigint;
|
||||||
/** Gets a signed 64-bit integer at the specified byte offset from the
|
/** Gets a signed 64-bit integer at the specified byte offset from the
|
||||||
* pointer. */
|
* pointer. */
|
||||||
getBigInt64(offset?: number): PointerValue;
|
getBigInt64(offset?: number): number | bigint;
|
||||||
/** Gets a signed 32-bit float at the specified byte offset from the
|
/** Gets a signed 32-bit float at the specified byte offset from the
|
||||||
* pointer. */
|
* pointer. */
|
||||||
getFloat32(offset?: number): number;
|
getFloat32(offset?: number): number;
|
||||||
/** Gets a signed 64-bit float at the specified byte offset from the
|
/** Gets a signed 64-bit float at the specified byte offset from the
|
||||||
* pointer. */
|
* pointer. */
|
||||||
getFloat64(offset?: number): number;
|
getFloat64(offset?: number): number;
|
||||||
|
/** Gets a pointer at the specified byte offset from the pointer */
|
||||||
|
getPointer(offset?: number): PointerValue;
|
||||||
/** Gets a C string (`null` terminated string) at the specified byte offset
|
/** Gets a C string (`null` terminated string) at the specified byte offset
|
||||||
* from the pointer. */
|
* from the pointer. */
|
||||||
getCString(offset?: number): string;
|
getCString(offset?: number): string;
|
||||||
/** Gets a C string (`null` terminated string) at the specified byte offset
|
/** Gets a C string (`null` terminated string) at the specified byte offset
|
||||||
* from the specified pointer. */
|
* from the specified pointer. */
|
||||||
static getCString(pointer: PointerValue, offset?: number): string;
|
static getCString(pointer: NonNullable<PointerValue>, offset?: number): string;
|
||||||
/** Gets an `ArrayBuffer` of length `byteLength` at the specified byte
|
/** Gets an `ArrayBuffer` of length `byteLength` at the specified byte
|
||||||
* offset from the pointer. */
|
* offset from the pointer. */
|
||||||
getArrayBuffer(byteLength: number, offset?: number): ArrayBuffer;
|
getArrayBuffer(byteLength: number, offset?: number): ArrayBuffer;
|
||||||
/** Gets an `ArrayBuffer` of length `byteLength` at the specified byte
|
/** Gets an `ArrayBuffer` of length `byteLength` at the specified byte
|
||||||
* offset from the specified pointer. */
|
* offset from the specified pointer. */
|
||||||
static getArrayBuffer(
|
static getArrayBuffer(
|
||||||
pointer: PointerValue,
|
pointer: NonNullable<PointerValue>,
|
||||||
byteLength: number,
|
byteLength: number,
|
||||||
offset?: number,
|
offset?: number,
|
||||||
): ArrayBuffer;
|
): ArrayBuffer;
|
||||||
|
@ -438,7 +451,7 @@ declare namespace Deno {
|
||||||
*
|
*
|
||||||
* Also takes optional byte offset from the pointer. */
|
* Also takes optional byte offset from the pointer. */
|
||||||
static copyInto(
|
static copyInto(
|
||||||
pointer: PointerValue,
|
pointer: NonNullable<PointerValue>,
|
||||||
destination: BufferSource,
|
destination: BufferSource,
|
||||||
offset?: number,
|
offset?: number,
|
||||||
): void;
|
): void;
|
||||||
|
@ -453,11 +466,11 @@ declare namespace Deno {
|
||||||
*/
|
*/
|
||||||
export class UnsafeFnPointer<Fn extends ForeignFunction> {
|
export class UnsafeFnPointer<Fn extends ForeignFunction> {
|
||||||
/** The pointer to the function. */
|
/** The pointer to the function. */
|
||||||
pointer: PointerValue;
|
pointer: NonNullable<PointerValue>;
|
||||||
/** The definition of the function. */
|
/** The definition of the function. */
|
||||||
definition: Fn;
|
definition: Fn;
|
||||||
|
|
||||||
constructor(pointer: PointerValue, definition: Const<Fn>);
|
constructor(pointer: NonNullable<PointerValue>, definition: Const<Fn>);
|
||||||
|
|
||||||
/** Call the foreign function. */
|
/** Call the foreign function. */
|
||||||
call: FromForeignFunction<Fn>;
|
call: FromForeignFunction<Fn>;
|
||||||
|
@ -516,7 +529,7 @@ declare namespace Deno {
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The pointer to the unsafe callback. */
|
/** The pointer to the unsafe callback. */
|
||||||
pointer: PointerValue;
|
pointer: NonNullable<PointerValue>;
|
||||||
/** The definition of the unsafe callback. */
|
/** The definition of the unsafe callback. */
|
||||||
definition: Definition;
|
definition: Definition;
|
||||||
/** The callback function. */
|
/** The callback function. */
|
||||||
|
|
|
@ -118,6 +118,13 @@ class UnsafePointerView {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPointer(offset = 0) {
|
||||||
|
return ops.op_ffi_read_ptr(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getCString(offset = 0) {
|
getCString(offset = 0) {
|
||||||
return ops.op_ffi_cstr_read(
|
return ops.op_ffi_cstr_read(
|
||||||
this.pointer,
|
this.pointer,
|
||||||
|
@ -170,11 +177,33 @@ class UnsafePointerView {
|
||||||
const OUT_BUFFER = new Uint32Array(2);
|
const OUT_BUFFER = new Uint32Array(2);
|
||||||
const OUT_BUFFER_64 = new BigInt64Array(OUT_BUFFER.buffer);
|
const OUT_BUFFER_64 = new BigInt64Array(OUT_BUFFER.buffer);
|
||||||
class UnsafePointer {
|
class UnsafePointer {
|
||||||
|
static create(value) {
|
||||||
|
return ops.op_ffi_ptr_create(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static equals(a, b) {
|
||||||
|
if (a === null || b === null) {
|
||||||
|
return a === b;
|
||||||
|
}
|
||||||
|
return ops.op_ffi_ptr_equals(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
static of(value) {
|
static of(value) {
|
||||||
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
|
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
|
||||||
return value.pointer;
|
return value.pointer;
|
||||||
}
|
}
|
||||||
ops.op_ffi_ptr_of(value, OUT_BUFFER);
|
return ops.op_ffi_ptr_of(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static offset(value, offset) {
|
||||||
|
return ops.op_ffi_ptr_offset(value, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static value(value) {
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
|
||||||
|
value = value.pointer;
|
||||||
|
}
|
||||||
|
ops.op_ffi_ptr_value(value, OUT_BUFFER);
|
||||||
const result = OUT_BUFFER[0] + 2 ** 32 * OUT_BUFFER[1];
|
const result = OUT_BUFFER[0] + 2 ** 32 * OUT_BUFFER[1];
|
||||||
if (NumberIsSafeInteger(result)) {
|
if (NumberIsSafeInteger(result)) {
|
||||||
return result;
|
return result;
|
||||||
|
@ -240,8 +269,7 @@ class UnsafeFnPointer {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isReturnedAsBigInt(type) {
|
function isReturnedAsBigInt(type) {
|
||||||
return type === "buffer" || type === "pointer" || type === "function" ||
|
return type === "u64" || type === "i64" ||
|
||||||
type === "u64" || type === "i64" ||
|
|
||||||
type === "usize" || type === "isize";
|
type === "usize" || type === "isize";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
140
ext/ffi/call.rs
140
ext/ffi/call.rs
|
@ -14,9 +14,11 @@ use deno_core::error::AnyError;
|
||||||
use deno_core::op;
|
use deno_core::op;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
use deno_core::serde_v8;
|
use deno_core::serde_v8;
|
||||||
|
use deno_core::serde_v8::ExternalPointer;
|
||||||
use deno_core::v8;
|
use deno_core::v8;
|
||||||
use deno_core::ResourceId;
|
use deno_core::ResourceId;
|
||||||
use libffi::middle::Arg;
|
use libffi::middle::Arg;
|
||||||
|
use serde::Serialize;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
@ -28,14 +30,13 @@ unsafe fn ffi_call_rtype_struct(
|
||||||
fn_ptr: &libffi::middle::CodePtr,
|
fn_ptr: &libffi::middle::CodePtr,
|
||||||
call_args: Vec<Arg>,
|
call_args: Vec<Arg>,
|
||||||
out_buffer: *mut u8,
|
out_buffer: *mut u8,
|
||||||
) -> NativeValue {
|
) {
|
||||||
libffi::raw::ffi_call(
|
libffi::raw::ffi_call(
|
||||||
cif.as_raw_ptr(),
|
cif.as_raw_ptr(),
|
||||||
Some(*fn_ptr.as_safe_fun()),
|
Some(*fn_ptr.as_safe_fun()),
|
||||||
out_buffer as *mut c_void,
|
out_buffer as *mut c_void,
|
||||||
call_args.as_ptr() as *mut *mut c_void,
|
call_args.as_ptr() as *mut *mut c_void,
|
||||||
);
|
);
|
||||||
NativeValue { void_value: () }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A one-off synchronous FFI call.
|
// A one-off synchronous FFI call.
|
||||||
|
@ -174,16 +175,25 @@ where
|
||||||
pointer: cif.call::<*mut c_void>(*fun_ptr, &call_args),
|
pointer: cif.call::<*mut c_void>(*fun_ptr, &call_args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NativeType::Struct(_) => ffi_call_rtype_struct(
|
NativeType::Struct(_) => NativeValue {
|
||||||
&symbol.cif,
|
void_value: ffi_call_rtype_struct(
|
||||||
&symbol.ptr,
|
&symbol.cif,
|
||||||
call_args,
|
&symbol.ptr,
|
||||||
out_buffer.unwrap().0,
|
call_args,
|
||||||
),
|
out_buffer.unwrap().0,
|
||||||
|
),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum FfiValue {
|
||||||
|
Value(Value),
|
||||||
|
External(ExternalPointer),
|
||||||
|
}
|
||||||
|
|
||||||
fn ffi_call(
|
fn ffi_call(
|
||||||
call_args: Vec<NativeValue>,
|
call_args: Vec<NativeValue>,
|
||||||
cif: &libffi::middle::Cif,
|
cif: &libffi::middle::Cif,
|
||||||
|
@ -191,7 +201,7 @@ fn ffi_call(
|
||||||
parameter_types: &[NativeType],
|
parameter_types: &[NativeType],
|
||||||
result_type: NativeType,
|
result_type: NativeType,
|
||||||
out_buffer: Option<OutBuffer>,
|
out_buffer: Option<OutBuffer>,
|
||||||
) -> Result<NativeValue, AnyError> {
|
) -> Result<FfiValue, AnyError> {
|
||||||
let call_args: Vec<Arg> = call_args
|
let call_args: Vec<Arg> = call_args
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
@ -205,55 +215,57 @@ fn ffi_call(
|
||||||
// types of symbol.
|
// types of symbol.
|
||||||
unsafe {
|
unsafe {
|
||||||
Ok(match result_type {
|
Ok(match result_type {
|
||||||
NativeType::Void => NativeValue {
|
NativeType::Void => {
|
||||||
void_value: cif.call::<()>(fun_ptr, &call_args),
|
cif.call::<()>(fun_ptr, &call_args);
|
||||||
},
|
FfiValue::Value(Value::from(()))
|
||||||
NativeType::Bool => NativeValue {
|
}
|
||||||
bool_value: cif.call::<bool>(fun_ptr, &call_args),
|
NativeType::Bool => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<bool>(fun_ptr, &call_args)))
|
||||||
NativeType::U8 => NativeValue {
|
}
|
||||||
u8_value: cif.call::<u8>(fun_ptr, &call_args),
|
NativeType::U8 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<u8>(fun_ptr, &call_args)))
|
||||||
NativeType::I8 => NativeValue {
|
}
|
||||||
i8_value: cif.call::<i8>(fun_ptr, &call_args),
|
NativeType::I8 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<i8>(fun_ptr, &call_args)))
|
||||||
NativeType::U16 => NativeValue {
|
}
|
||||||
u16_value: cif.call::<u16>(fun_ptr, &call_args),
|
NativeType::U16 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<u16>(fun_ptr, &call_args)))
|
||||||
NativeType::I16 => NativeValue {
|
}
|
||||||
i16_value: cif.call::<i16>(fun_ptr, &call_args),
|
NativeType::I16 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<i16>(fun_ptr, &call_args)))
|
||||||
NativeType::U32 => NativeValue {
|
}
|
||||||
u32_value: cif.call::<u32>(fun_ptr, &call_args),
|
NativeType::U32 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<u32>(fun_ptr, &call_args)))
|
||||||
NativeType::I32 => NativeValue {
|
}
|
||||||
i32_value: cif.call::<i32>(fun_ptr, &call_args),
|
NativeType::I32 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<i32>(fun_ptr, &call_args)))
|
||||||
NativeType::U64 => NativeValue {
|
}
|
||||||
u64_value: cif.call::<u64>(fun_ptr, &call_args),
|
NativeType::U64 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<u64>(fun_ptr, &call_args)))
|
||||||
NativeType::I64 => NativeValue {
|
}
|
||||||
i64_value: cif.call::<i64>(fun_ptr, &call_args),
|
NativeType::I64 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<i64>(fun_ptr, &call_args)))
|
||||||
NativeType::USize => NativeValue {
|
}
|
||||||
usize_value: cif.call::<usize>(fun_ptr, &call_args),
|
NativeType::USize => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<usize>(fun_ptr, &call_args)))
|
||||||
NativeType::ISize => NativeValue {
|
}
|
||||||
isize_value: cif.call::<isize>(fun_ptr, &call_args),
|
NativeType::ISize => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<isize>(fun_ptr, &call_args)))
|
||||||
NativeType::F32 => NativeValue {
|
}
|
||||||
f32_value: cif.call::<f32>(fun_ptr, &call_args),
|
NativeType::F32 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<f32>(fun_ptr, &call_args)))
|
||||||
NativeType::F64 => NativeValue {
|
}
|
||||||
f64_value: cif.call::<f64>(fun_ptr, &call_args),
|
NativeType::F64 => {
|
||||||
},
|
FfiValue::Value(Value::from(cif.call::<f64>(fun_ptr, &call_args)))
|
||||||
|
}
|
||||||
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
|
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
|
||||||
NativeValue {
|
FfiValue::External(ExternalPointer::from(
|
||||||
pointer: cif.call::<*mut c_void>(fun_ptr, &call_args),
|
cif.call::<*mut c_void>(fun_ptr, &call_args),
|
||||||
}
|
))
|
||||||
}
|
}
|
||||||
NativeType::Struct(_) => {
|
NativeType::Struct(_) => {
|
||||||
ffi_call_rtype_struct(cif, &fun_ptr, call_args, out_buffer.unwrap().0)
|
ffi_call_rtype_struct(cif, &fun_ptr, call_args, out_buffer.unwrap().0);
|
||||||
|
FfiValue::Value(Value::Null)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -263,11 +275,11 @@ fn ffi_call(
|
||||||
pub fn op_ffi_call_ptr_nonblocking<'scope, FP>(
|
pub fn op_ffi_call_ptr_nonblocking<'scope, FP>(
|
||||||
scope: &mut v8::HandleScope<'scope>,
|
scope: &mut v8::HandleScope<'scope>,
|
||||||
state: Rc<RefCell<deno_core::OpState>>,
|
state: Rc<RefCell<deno_core::OpState>>,
|
||||||
pointer: usize,
|
pointer: *mut c_void,
|
||||||
def: ForeignFunction,
|
def: ForeignFunction,
|
||||||
parameters: serde_v8::Value<'scope>,
|
parameters: serde_v8::Value<'scope>,
|
||||||
out_buffer: Option<serde_v8::Value<'scope>>,
|
out_buffer: Option<serde_v8::Value<'scope>>,
|
||||||
) -> Result<impl Future<Output = Result<Value, AnyError>>, AnyError>
|
) -> Result<impl Future<Output = Result<FfiValue, AnyError>>, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
{
|
{
|
||||||
|
@ -280,7 +292,6 @@ where
|
||||||
|
|
||||||
let symbol = PtrSymbol::new(pointer, &def)?;
|
let symbol = PtrSymbol::new(pointer, &def)?;
|
||||||
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
|
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
|
||||||
let def_result = def.result.clone();
|
|
||||||
|
|
||||||
let out_buffer = out_buffer
|
let out_buffer = out_buffer
|
||||||
.map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap());
|
.map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap());
|
||||||
|
@ -303,7 +314,7 @@ where
|
||||||
.await
|
.await
|
||||||
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
|
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
|
||||||
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
||||||
Ok(unsafe { result.to_value(def_result) })
|
Ok(result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +327,8 @@ pub fn op_ffi_call_nonblocking<'scope>(
|
||||||
symbol: String,
|
symbol: String,
|
||||||
parameters: serde_v8::Value<'scope>,
|
parameters: serde_v8::Value<'scope>,
|
||||||
out_buffer: Option<serde_v8::Value<'scope>>,
|
out_buffer: Option<serde_v8::Value<'scope>>,
|
||||||
) -> Result<impl Future<Output = Result<Value, AnyError>> + 'static, AnyError> {
|
) -> Result<impl Future<Output = Result<FfiValue, AnyError>> + 'static, AnyError>
|
||||||
|
{
|
||||||
let symbol = {
|
let symbol = {
|
||||||
let state = state.borrow();
|
let state = state.borrow();
|
||||||
let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?;
|
let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?;
|
||||||
|
@ -332,7 +344,6 @@ pub fn op_ffi_call_nonblocking<'scope>(
|
||||||
.map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap());
|
.map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap());
|
||||||
let out_buffer_ptr = out_buffer_as_ptr(scope, out_buffer);
|
let out_buffer_ptr = out_buffer_as_ptr(scope, out_buffer);
|
||||||
|
|
||||||
let result_type = symbol.result_type.clone();
|
|
||||||
let join_handle = tokio::task::spawn_blocking(move || {
|
let join_handle = tokio::task::spawn_blocking(move || {
|
||||||
let Symbol {
|
let Symbol {
|
||||||
cif,
|
cif,
|
||||||
|
@ -356,7 +367,7 @@ pub fn op_ffi_call_nonblocking<'scope>(
|
||||||
.await
|
.await
|
||||||
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
|
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
|
||||||
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
||||||
Ok(unsafe { result.to_value(result_type) })
|
Ok(result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,11 +375,11 @@ pub fn op_ffi_call_nonblocking<'scope>(
|
||||||
pub fn op_ffi_call_ptr<FP, 'scope>(
|
pub fn op_ffi_call_ptr<FP, 'scope>(
|
||||||
scope: &mut v8::HandleScope<'scope>,
|
scope: &mut v8::HandleScope<'scope>,
|
||||||
state: Rc<RefCell<deno_core::OpState>>,
|
state: Rc<RefCell<deno_core::OpState>>,
|
||||||
pointer: usize,
|
pointer: *mut c_void,
|
||||||
def: ForeignFunction,
|
def: ForeignFunction,
|
||||||
parameters: serde_v8::Value<'scope>,
|
parameters: serde_v8::Value<'scope>,
|
||||||
out_buffer: Option<serde_v8::Value<'scope>>,
|
out_buffer: Option<serde_v8::Value<'scope>>,
|
||||||
) -> Result<serde_v8::Value<'scope>, AnyError>
|
) -> Result<FfiValue, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
{
|
{
|
||||||
|
@ -395,6 +406,5 @@ where
|
||||||
out_buffer_ptr,
|
out_buffer_ptr,
|
||||||
)?;
|
)?;
|
||||||
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
// SAFETY: Same return type declared to libffi; trust user to have it right beyond that.
|
||||||
let result = unsafe { result.to_v8(scope, def.result) };
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,10 @@ pub struct PtrSymbol {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PtrSymbol {
|
impl PtrSymbol {
|
||||||
pub fn new(fn_ptr: usize, def: &ForeignFunction) -> Result<Self, AnyError> {
|
pub fn new(
|
||||||
|
fn_ptr: *mut c_void,
|
||||||
|
def: &ForeignFunction,
|
||||||
|
) -> Result<Self, AnyError> {
|
||||||
let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
|
let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
|
||||||
let cif = libffi::middle::Cif::new(
|
let cif = libffi::middle::Cif::new(
|
||||||
def
|
def
|
||||||
|
@ -236,11 +239,11 @@ unsafe fn do_ffi_callback(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
|
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
|
||||||
let result = *((*val) as *const usize);
|
let result = *((*val) as *const *mut c_void);
|
||||||
if result > MAX_SAFE_INTEGER as usize {
|
if result.is_null() {
|
||||||
v8::BigInt::new_from_u64(scope, result as u64).into()
|
v8::null(scope).into()
|
||||||
} else {
|
} else {
|
||||||
v8::Number::new(scope, result as f64).into()
|
v8::External::new(scope, result).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NativeType::Struct(_) => {
|
NativeType::Struct(_) => {
|
||||||
|
@ -353,34 +356,43 @@ unsafe fn do_ffi_callback(
|
||||||
};
|
};
|
||||||
*(result as *mut f64) = value;
|
*(result as *mut f64) = value;
|
||||||
}
|
}
|
||||||
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
|
NativeType::Buffer => {
|
||||||
let pointer = if let Ok(value) =
|
let pointer: *mut u8 = if let Ok(value) =
|
||||||
v8::Local::<v8::ArrayBufferView>::try_from(value)
|
v8::Local::<v8::ArrayBufferView>::try_from(value)
|
||||||
{
|
{
|
||||||
let byte_offset = value.byte_offset();
|
let byte_offset = value.byte_offset();
|
||||||
let backing_store = value
|
let pointer = value
|
||||||
.buffer(scope)
|
.buffer(scope)
|
||||||
.expect("Unable to deserialize result parameter.")
|
.expect("Unable to deserialize result parameter.")
|
||||||
.get_backing_store();
|
.data();
|
||||||
&backing_store[byte_offset..] as *const _ as *const u8
|
if let Some(non_null) = pointer {
|
||||||
} else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
|
// SAFETY: Pointer is non-null, and V8 guarantees that the byte_offset
|
||||||
value.u64_value().0 as usize as *const u8
|
// is within the buffer backing store.
|
||||||
|
unsafe { non_null.as_ptr().add(byte_offset) as *mut u8 }
|
||||||
|
} else {
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
} else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value) {
|
} else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value) {
|
||||||
let backing_store = value.get_backing_store();
|
let pointer = value.data();
|
||||||
&backing_store[..] as *const _ as *const u8
|
if let Some(non_null) = pointer {
|
||||||
} else if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
|
non_null.as_ptr() as *mut u8
|
||||||
value.value() as usize as *const u8
|
} else {
|
||||||
} else if value.is_null() {
|
ptr::null_mut()
|
||||||
ptr::null()
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallthrough: Probably someone returned a number but this could
|
ptr::null_mut()
|
||||||
// also be eg. a string. This is essentially UB.
|
|
||||||
value
|
|
||||||
.integer_value(scope)
|
|
||||||
.expect("Unable to deserialize result parameter.") as usize
|
|
||||||
as *const u8
|
|
||||||
};
|
};
|
||||||
*(result as *mut *const u8) = pointer;
|
*(result as *mut *mut u8) = pointer;
|
||||||
|
}
|
||||||
|
NativeType::Pointer | NativeType::Function => {
|
||||||
|
let pointer: *mut c_void =
|
||||||
|
if let Ok(external) = v8::Local::<v8::External>::try_from(value) {
|
||||||
|
external.value()
|
||||||
|
} else {
|
||||||
|
// TODO(@aapoalas): Start throwing errors into JS about invalid callback return values.
|
||||||
|
ptr::null_mut()
|
||||||
|
};
|
||||||
|
*(result as *mut *mut c_void) = pointer;
|
||||||
}
|
}
|
||||||
NativeType::I8 => {
|
NativeType::I8 => {
|
||||||
let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
|
let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
|
||||||
|
@ -591,7 +603,7 @@ where
|
||||||
let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, unsafe {
|
let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, unsafe {
|
||||||
info.as_ref().unwrap()
|
info.as_ref().unwrap()
|
||||||
});
|
});
|
||||||
let ptr = *closure.code_ptr() as usize;
|
let ptr = *closure.code_ptr() as *mut c_void;
|
||||||
let resource = UnsafeCallbackResource {
|
let resource = UnsafeCallbackResource {
|
||||||
cancel: CancelHandle::new_rc(),
|
cancel: CancelHandle::new_rc(),
|
||||||
closure,
|
closure,
|
||||||
|
@ -600,11 +612,7 @@ where
|
||||||
let rid = state.resource_table.add(resource);
|
let rid = state.resource_table.add(resource);
|
||||||
|
|
||||||
let rid_local = v8::Integer::new_from_unsigned(scope, rid);
|
let rid_local = v8::Integer::new_from_unsigned(scope, rid);
|
||||||
let ptr_local: v8::Local<v8::Value> = if ptr > MAX_SAFE_INTEGER as usize {
|
let ptr_local: v8::Local<v8::Value> = v8::External::new(scope, ptr).into();
|
||||||
v8::BigInt::new_from_u64(scope, ptr as u64).into()
|
|
||||||
} else {
|
|
||||||
v8::Number::new(scope, ptr as f64).into()
|
|
||||||
};
|
|
||||||
let array = v8::Array::new(scope, 2);
|
let array = v8::Array::new(scope, 2);
|
||||||
array.set_index(scope, 0, rid_local.into());
|
array.set_index(scope, 0, rid_local.into());
|
||||||
array.set_index(scope, 1, ptr_local);
|
array.set_index(scope, 1, ptr_local);
|
||||||
|
|
|
@ -38,13 +38,13 @@ impl Resource for DynamicLibraryResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicLibraryResource {
|
impl DynamicLibraryResource {
|
||||||
pub fn get_static(&self, symbol: String) -> Result<*const c_void, AnyError> {
|
pub fn get_static(&self, symbol: String) -> Result<*mut c_void, AnyError> {
|
||||||
// By default, Err returned by this function does not tell
|
// By default, Err returned by this function does not tell
|
||||||
// which symbol wasn't exported. So we'll modify the error
|
// which symbol wasn't exported. So we'll modify the error
|
||||||
// message to include the name of symbol.
|
// message to include the name of symbol.
|
||||||
//
|
//
|
||||||
// SAFETY: The obtained T symbol is the size of a pointer.
|
// SAFETY: The obtained T symbol is the size of a pointer.
|
||||||
match unsafe { self.lib.symbol::<*const c_void>(&symbol) } {
|
match unsafe { self.lib.symbol::<*mut c_void>(&symbol) } {
|
||||||
Ok(value) => Ok(Ok(value)),
|
Ok(value) => Ok(Ok(value)),
|
||||||
Err(err) => Err(generic_error(format!(
|
Err(err) => Err(generic_error(format!(
|
||||||
"Failed to register symbol {symbol}: {err}"
|
"Failed to register symbol {symbol}: {err}"
|
||||||
|
@ -56,13 +56,7 @@ impl DynamicLibraryResource {
|
||||||
pub fn needs_unwrap(rv: &NativeType) -> bool {
|
pub fn needs_unwrap(rv: &NativeType) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
rv,
|
rv,
|
||||||
NativeType::Function
|
NativeType::I64 | NativeType::ISize | NativeType::U64 | NativeType::USize
|
||||||
| NativeType::Pointer
|
|
||||||
| NativeType::Buffer
|
|
||||||
| NativeType::I64
|
|
||||||
| NativeType::ISize
|
|
||||||
| NativeType::U64
|
|
||||||
| NativeType::USize
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,26 +251,20 @@ fn make_sync_fn<'s>(
|
||||||
match needs_unwrap {
|
match needs_unwrap {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
let view: v8::Local<v8::ArrayBufferView> = v.try_into().unwrap();
|
let view: v8::Local<v8::ArrayBufferView> = v.try_into().unwrap();
|
||||||
let backing_store =
|
let pointer =
|
||||||
view.buffer(scope).unwrap().get_backing_store();
|
view.buffer(scope).unwrap().data().unwrap().as_ptr() as *mut u8;
|
||||||
|
|
||||||
if is_i64(&symbol.result_type) {
|
if is_i64(&symbol.result_type) {
|
||||||
// SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>,
|
// SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>,
|
||||||
// it points to a fixed continuous slice of bytes on the heap.
|
// it points to a fixed continuous slice of bytes on the heap.
|
||||||
let bs = unsafe {
|
let bs = unsafe { &mut *(pointer as *mut i64) };
|
||||||
&mut *(&backing_store[..] as *const _ as *mut [u8]
|
|
||||||
as *mut i64)
|
|
||||||
};
|
|
||||||
// SAFETY: We already checked that type == I64
|
// SAFETY: We already checked that type == I64
|
||||||
let value = unsafe { result.i64_value };
|
let value = unsafe { result.i64_value };
|
||||||
*bs = value;
|
*bs = value;
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>,
|
// SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>,
|
||||||
// it points to a fixed continuous slice of bytes on the heap.
|
// it points to a fixed continuous slice of bytes on the heap.
|
||||||
let bs = unsafe {
|
let bs = unsafe { &mut *(pointer as *mut u64) };
|
||||||
&mut *(&backing_store[..] as *const _ as *mut [u8]
|
|
||||||
as *mut u64)
|
|
||||||
};
|
|
||||||
// SAFETY: We checked that type == U64
|
// SAFETY: We checked that type == U64
|
||||||
let value = unsafe { result.u64_value };
|
let value = unsafe { result.u64_value };
|
||||||
*bs = value;
|
*bs = value;
|
||||||
|
|
|
@ -5,7 +5,6 @@ use crate::MAX_SAFE_INTEGER;
|
||||||
use crate::MIN_SAFE_INTEGER;
|
use crate::MIN_SAFE_INTEGER;
|
||||||
use deno_core::error::type_error;
|
use deno_core::error::type_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json::Value;
|
|
||||||
use deno_core::serde_v8;
|
use deno_core::serde_v8;
|
||||||
use deno_core::v8;
|
use deno_core::v8;
|
||||||
use libffi::middle::Arg;
|
use libffi::middle::Arg;
|
||||||
|
@ -80,33 +79,6 @@ impl NativeValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: native_type must correspond to the type of value represented by the union field
|
|
||||||
pub unsafe fn to_value(&self, native_type: NativeType) -> Value {
|
|
||||||
match native_type {
|
|
||||||
NativeType::Void => Value::Null,
|
|
||||||
NativeType::Bool => Value::from(self.bool_value),
|
|
||||||
NativeType::U8 => Value::from(self.u8_value),
|
|
||||||
NativeType::I8 => Value::from(self.i8_value),
|
|
||||||
NativeType::U16 => Value::from(self.u16_value),
|
|
||||||
NativeType::I16 => Value::from(self.i16_value),
|
|
||||||
NativeType::U32 => Value::from(self.u32_value),
|
|
||||||
NativeType::I32 => Value::from(self.i32_value),
|
|
||||||
NativeType::U64 => Value::from(self.u64_value),
|
|
||||||
NativeType::I64 => Value::from(self.i64_value),
|
|
||||||
NativeType::USize => Value::from(self.usize_value),
|
|
||||||
NativeType::ISize => Value::from(self.isize_value),
|
|
||||||
NativeType::F32 => Value::from(self.f32_value),
|
|
||||||
NativeType::F64 => Value::from(self.f64_value),
|
|
||||||
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
|
|
||||||
Value::from(self.pointer as usize)
|
|
||||||
}
|
|
||||||
NativeType::Struct(_) => {
|
|
||||||
// Return value is written to out_buffer
|
|
||||||
Value::Null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: native_type must correspond to the type of value represented by the union field
|
// SAFETY: native_type must correspond to the type of value represented by the union field
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn to_v8<'scope>(
|
pub unsafe fn to_v8<'scope>(
|
||||||
|
@ -206,13 +178,11 @@ impl NativeValue {
|
||||||
local_value.into()
|
local_value.into()
|
||||||
}
|
}
|
||||||
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
|
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
|
||||||
let value = self.pointer as u64;
|
let local_value: v8::Local<v8::Value> = if self.pointer.is_null() {
|
||||||
let local_value: v8::Local<v8::Value> =
|
v8::null(scope).into()
|
||||||
if value > MAX_SAFE_INTEGER as u64 {
|
} else {
|
||||||
v8::BigInt::new_from_u64(scope, value).into()
|
v8::External::new(scope, self.pointer).into()
|
||||||
} else {
|
};
|
||||||
v8::Number::new(scope, value as f64).into()
|
|
||||||
};
|
|
||||||
local_value.into()
|
local_value.into()
|
||||||
}
|
}
|
||||||
NativeType::Struct(_) => {
|
NativeType::Struct(_) => {
|
||||||
|
@ -396,22 +366,16 @@ pub fn ffi_parse_f64_arg(
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ffi_parse_pointer_arg(
|
pub fn ffi_parse_pointer_arg(
|
||||||
scope: &mut v8::HandleScope,
|
_scope: &mut v8::HandleScope,
|
||||||
arg: v8::Local<v8::Value>,
|
arg: v8::Local<v8::Value>,
|
||||||
) -> Result<NativeValue, AnyError> {
|
) -> Result<NativeValue, AnyError> {
|
||||||
// Order of checking:
|
let pointer = if let Ok(value) = v8::Local::<v8::External>::try_from(arg) {
|
||||||
// 1. BigInt: Uncommon and not supported by Fast API, optimise this case.
|
value.value()
|
||||||
// 2. Number: Common and supported by Fast API.
|
|
||||||
// 3. Null: Very uncommon / can be represented by a 0.
|
|
||||||
let pointer = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) {
|
|
||||||
value.u64_value().0 as usize as *mut c_void
|
|
||||||
} else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
|
|
||||||
value.integer_value(scope).unwrap() as usize as *mut c_void
|
|
||||||
} else if arg.is_null() {
|
} else if arg.is_null() {
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
} else {
|
} else {
|
||||||
return Err(type_error(
|
return Err(type_error(
|
||||||
"Invalid FFI pointer type, expected null, integer or BigInt",
|
"Invalid FFI pointer type, expected null, or External",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
Ok(NativeValue { pointer })
|
Ok(NativeValue { pointer })
|
||||||
|
@ -502,22 +466,16 @@ pub fn ffi_parse_struct_arg(
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ffi_parse_function_arg(
|
pub fn ffi_parse_function_arg(
|
||||||
scope: &mut v8::HandleScope,
|
_scope: &mut v8::HandleScope,
|
||||||
arg: v8::Local<v8::Value>,
|
arg: v8::Local<v8::Value>,
|
||||||
) -> Result<NativeValue, AnyError> {
|
) -> Result<NativeValue, AnyError> {
|
||||||
// Order of checking:
|
let pointer = if let Ok(value) = v8::Local::<v8::External>::try_from(arg) {
|
||||||
// 1. BigInt: Uncommon and not supported by Fast API, optimise this case.
|
value.value()
|
||||||
// 2. Number: Common and supported by Fast API, optimise this case as second.
|
|
||||||
// 3. Null: Very uncommon / can be represented by a 0.
|
|
||||||
let pointer = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) {
|
|
||||||
value.u64_value().0 as usize as *mut c_void
|
|
||||||
} else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
|
|
||||||
value.integer_value(scope).unwrap() as usize as *mut c_void
|
|
||||||
} else if arg.is_null() {
|
} else if arg.is_null() {
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
} else {
|
} else {
|
||||||
return Err(type_error(
|
return Err(type_error(
|
||||||
"Invalid FFI function type, expected null, integer, or BigInt",
|
"Invalid FFI function type, expected null, or External",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
Ok(NativeValue { pointer })
|
Ok(NativeValue { pointer })
|
||||||
|
|
|
@ -91,7 +91,11 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
|
||||||
op_ffi_call_nonblocking::decl(),
|
op_ffi_call_nonblocking::decl(),
|
||||||
op_ffi_call_ptr::decl::<P>(),
|
op_ffi_call_ptr::decl::<P>(),
|
||||||
op_ffi_call_ptr_nonblocking::decl::<P>(),
|
op_ffi_call_ptr_nonblocking::decl::<P>(),
|
||||||
|
op_ffi_ptr_create::decl::<P>(),
|
||||||
|
op_ffi_ptr_equals::decl::<P>(),
|
||||||
op_ffi_ptr_of::decl::<P>(),
|
op_ffi_ptr_of::decl::<P>(),
|
||||||
|
op_ffi_ptr_offset::decl::<P>(),
|
||||||
|
op_ffi_ptr_value::decl::<P>(),
|
||||||
op_ffi_get_buf::decl::<P>(),
|
op_ffi_get_buf::decl::<P>(),
|
||||||
op_ffi_buf_copy_into::decl::<P>(),
|
op_ffi_buf_copy_into::decl::<P>(),
|
||||||
op_ffi_cstr_read::decl::<P>(),
|
op_ffi_cstr_read::decl::<P>(),
|
||||||
|
@ -106,6 +110,7 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
|
||||||
op_ffi_read_i64::decl::<P>(),
|
op_ffi_read_i64::decl::<P>(),
|
||||||
op_ffi_read_f32::decl::<P>(),
|
op_ffi_read_f32::decl::<P>(),
|
||||||
op_ffi_read_f64::decl::<P>(),
|
op_ffi_read_f64::decl::<P>(),
|
||||||
|
op_ffi_read_ptr::decl::<P>(),
|
||||||
op_ffi_unsafe_callback_create::decl::<P>(),
|
op_ffi_unsafe_callback_create::decl::<P>(),
|
||||||
op_ffi_unsafe_callback_ref::decl(),
|
op_ffi_unsafe_callback_ref::decl(),
|
||||||
op_ffi_unsafe_callback_unref::decl(),
|
op_ffi_unsafe_callback_unref::decl(),
|
||||||
|
|
249
ext/ffi/repr.rs
249
ext/ffi/repr.rs
|
@ -13,16 +13,90 @@ use std::ffi::c_void;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
#[op(fast)]
|
||||||
|
fn op_ffi_ptr_create<FP>(
|
||||||
|
state: &mut deno_core::OpState,
|
||||||
|
ptr_number: usize,
|
||||||
|
) -> Result<*mut c_void, AnyError>
|
||||||
|
where
|
||||||
|
FP: FfiPermissions + 'static,
|
||||||
|
{
|
||||||
|
check_unstable(state, "Deno.UnsafePointer#create");
|
||||||
|
let permissions = state.borrow_mut::<FP>();
|
||||||
|
permissions.check(None)?;
|
||||||
|
|
||||||
|
Ok(ptr_number as *mut c_void)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op(fast)]
|
||||||
|
pub fn op_ffi_ptr_equals<FP>(
|
||||||
|
state: &mut deno_core::OpState,
|
||||||
|
a: *const c_void,
|
||||||
|
b: *const c_void,
|
||||||
|
) -> Result<bool, AnyError>
|
||||||
|
where
|
||||||
|
FP: FfiPermissions + 'static,
|
||||||
|
{
|
||||||
|
check_unstable(state, "Deno.UnsafePointer#equals");
|
||||||
|
let permissions = state.borrow_mut::<FP>();
|
||||||
|
permissions.check(None)?;
|
||||||
|
|
||||||
|
Ok(a == b)
|
||||||
|
}
|
||||||
|
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_ptr_of<FP>(
|
pub fn op_ffi_ptr_of<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
buf: *const u8,
|
buf: *const u8,
|
||||||
|
) -> Result<*mut c_void, AnyError>
|
||||||
|
where
|
||||||
|
FP: FfiPermissions + 'static,
|
||||||
|
{
|
||||||
|
check_unstable(state, "Deno.UnsafePointer#of");
|
||||||
|
let permissions = state.borrow_mut::<FP>();
|
||||||
|
permissions.check(None)?;
|
||||||
|
|
||||||
|
Ok(buf as *mut c_void)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op(fast)]
|
||||||
|
fn op_ffi_ptr_offset<FP>(
|
||||||
|
state: &mut deno_core::OpState,
|
||||||
|
ptr: *mut c_void,
|
||||||
|
offset: isize,
|
||||||
|
) -> Result<*mut c_void, AnyError>
|
||||||
|
where
|
||||||
|
FP: FfiPermissions + 'static,
|
||||||
|
{
|
||||||
|
check_unstable(state, "Deno.UnsafePointer#offset");
|
||||||
|
let permissions = state.borrow_mut::<FP>();
|
||||||
|
permissions.check(None)?;
|
||||||
|
|
||||||
|
if ptr.is_null() {
|
||||||
|
return Err(type_error("Invalid pointer to offset, pointer is null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Pointer and offset are user provided.
|
||||||
|
Ok(unsafe { ptr.offset(offset) })
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn noop_deleter_callback(
|
||||||
|
_data: *mut c_void,
|
||||||
|
_byte_length: usize,
|
||||||
|
_deleter_data: *mut c_void,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op(fast)]
|
||||||
|
fn op_ffi_ptr_value<FP>(
|
||||||
|
state: &mut deno_core::OpState,
|
||||||
|
ptr: *mut c_void,
|
||||||
out: &mut [u32],
|
out: &mut [u32],
|
||||||
) -> Result<(), AnyError>
|
) -> Result<(), AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
{
|
{
|
||||||
check_unstable(state, "Deno.UnsafePointer#of");
|
check_unstable(state, "Deno.UnsafePointer#value");
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
|
@ -35,47 +109,35 @@ where
|
||||||
|
|
||||||
// SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned.
|
// SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned.
|
||||||
let out = unsafe { &mut *outptr };
|
let out = unsafe { &mut *outptr };
|
||||||
*out = buf as usize;
|
*out = ptr as usize;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn noop_deleter_callback(
|
|
||||||
_data: *mut c_void,
|
|
||||||
_byte_length: usize,
|
|
||||||
_deleter_data: *mut c_void,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[op(v8)]
|
#[op(v8)]
|
||||||
pub fn op_ffi_get_buf<FP, 'scope>(
|
pub fn op_ffi_get_buf<FP, 'scope>(
|
||||||
scope: &mut v8::HandleScope<'scope>,
|
scope: &mut v8::HandleScope<'scope>,
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> Result<serde_v8::Value<'scope>, AnyError>
|
) -> Result<serde_v8::Value<'scope>, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
{
|
{
|
||||||
check_unstable(state, "Deno.UnsafePointerView#arrayBuffer");
|
check_unstable(state, "Deno.UnsafePointerView#getArrayBuffer");
|
||||||
|
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *mut c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid FFI pointer value, got nullptr"));
|
return Err(type_error("Invalid ArrayBuffer pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: Offset is user defined.
|
// SAFETY: Trust the user to have provided a real pointer, offset, and a valid matching size to it. Since this is a foreign pointer, we should not do any deletion.
|
||||||
let ptr = unsafe { ptr.add(offset) };
|
|
||||||
|
|
||||||
// SAFETY: Trust the user to have provided a real pointer, and a valid matching size to it. Since this is a foreign pointer, we should not do any deletion.
|
|
||||||
let backing_store = unsafe {
|
let backing_store = unsafe {
|
||||||
v8::ArrayBuffer::new_backing_store_from_ptr(
|
v8::ArrayBuffer::new_backing_store_from_ptr(
|
||||||
ptr,
|
ptr.offset(offset),
|
||||||
len,
|
len,
|
||||||
noop_deleter_callback,
|
noop_deleter_callback,
|
||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
|
@ -90,8 +152,8 @@ where
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_buf_copy_into<FP>(
|
pub fn op_ffi_buf_copy_into<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
src: usize,
|
src: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
dst: &mut [u8],
|
dst: &mut [u8],
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> Result<(), AnyError>
|
) -> Result<(), AnyError>
|
||||||
|
@ -103,19 +165,20 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
if dst.len() < len {
|
if src.is_null() {
|
||||||
|
Err(type_error("Invalid ArrayBuffer pointer, pointer is null"))
|
||||||
|
} else if dst.len() < len {
|
||||||
Err(range_error(
|
Err(range_error(
|
||||||
"Destination length is smaller than source length",
|
"Destination length is smaller than source length",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
let src = src as *const c_void;
|
let src = src as *const c_void;
|
||||||
|
|
||||||
// SAFETY: Offset is user defined.
|
// SAFETY: src and offset are user defined.
|
||||||
let src = unsafe { src.add(offset) as *const u8 };
|
|
||||||
|
|
||||||
// SAFETY: src is user defined.
|
|
||||||
// dest is properly aligned and is valid for writes of len * size_of::<T>() bytes.
|
// dest is properly aligned and is valid for writes of len * size_of::<T>() bytes.
|
||||||
unsafe { ptr::copy::<u8>(src, dst.as_mut_ptr(), len) };
|
unsafe {
|
||||||
|
ptr::copy::<u8>(src.offset(offset) as *const u8, dst.as_mut_ptr(), len)
|
||||||
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,8 +187,8 @@ where
|
||||||
pub fn op_ffi_cstr_read<FP, 'scope>(
|
pub fn op_ffi_cstr_read<FP, 'scope>(
|
||||||
scope: &mut v8::HandleScope<'scope>,
|
scope: &mut v8::HandleScope<'scope>,
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<serde_v8::Value<'scope>, AnyError>
|
) -> Result<serde_v8::Value<'scope>, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -135,17 +198,13 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid CString pointer, pointer is null"));
|
return Err(type_error("Invalid CString pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: Offset is user defined.
|
let cstr =
|
||||||
let ptr = unsafe { ptr.add(offset) };
|
// SAFETY: Pointer and offset are user provided.
|
||||||
|
unsafe { CStr::from_ptr(ptr.offset(offset) as *const c_char) }.to_bytes();
|
||||||
// SAFETY: Pointer is user provided.
|
|
||||||
let cstr = unsafe { CStr::from_ptr(ptr as *const c_char) }.to_bytes();
|
|
||||||
let value: v8::Local<v8::Value> =
|
let value: v8::Local<v8::Value> =
|
||||||
v8::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal)
|
v8::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
|
@ -158,8 +217,8 @@ where
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_bool<FP>(
|
pub fn op_ffi_read_bool<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<bool, AnyError>
|
) -> Result<bool, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -169,21 +228,19 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid bool pointer, pointer is null"));
|
return Err(type_error("Invalid bool pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe { ptr::read_unaligned::<bool>(ptr.add(offset) as *const bool) })
|
Ok(unsafe { ptr::read_unaligned::<bool>(ptr.offset(offset) as *const bool) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_u8<FP>(
|
pub fn op_ffi_read_u8<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<u32, AnyError>
|
) -> Result<u32, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -193,21 +250,21 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid u8 pointer, pointer is null"));
|
return Err(type_error("Invalid u8 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe { ptr::read_unaligned::<u8>(ptr.add(offset) as *const u8) as u32 })
|
Ok(unsafe {
|
||||||
|
ptr::read_unaligned::<u8>(ptr.offset(offset) as *const u8) as u32
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_i8<FP>(
|
pub fn op_ffi_read_i8<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<i32, AnyError>
|
) -> Result<i32, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -217,21 +274,21 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid i8 pointer, pointer is null"));
|
return Err(type_error("Invalid i8 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe { ptr::read_unaligned::<i8>(ptr.add(offset) as *const i8) as i32 })
|
Ok(unsafe {
|
||||||
|
ptr::read_unaligned::<i8>(ptr.offset(offset) as *const i8) as i32
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_u16<FP>(
|
pub fn op_ffi_read_u16<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<u32, AnyError>
|
) -> Result<u32, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -241,23 +298,21 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid u16 pointer, pointer is null"));
|
return Err(type_error("Invalid u16 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe {
|
Ok(unsafe {
|
||||||
ptr::read_unaligned::<u16>(ptr.add(offset) as *const u16) as u32
|
ptr::read_unaligned::<u16>(ptr.offset(offset) as *const u16) as u32
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_i16<FP>(
|
pub fn op_ffi_read_i16<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<i32, AnyError>
|
) -> Result<i32, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -267,23 +322,21 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid i16 pointer, pointer is null"));
|
return Err(type_error("Invalid i16 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe {
|
Ok(unsafe {
|
||||||
ptr::read_unaligned::<i16>(ptr.add(offset) as *const i16) as i32
|
ptr::read_unaligned::<i16>(ptr.offset(offset) as *const i16) as i32
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_u32<FP>(
|
pub fn op_ffi_read_u32<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<u32, AnyError>
|
) -> Result<u32, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -293,21 +346,19 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid u32 pointer, pointer is null"));
|
return Err(type_error("Invalid u32 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe { ptr::read_unaligned::<u32>(ptr.add(offset) as *const u32) })
|
Ok(unsafe { ptr::read_unaligned::<u32>(ptr.offset(offset) as *const u32) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_i32<FP>(
|
pub fn op_ffi_read_i32<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<i32, AnyError>
|
) -> Result<i32, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -317,21 +368,19 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid i32 pointer, pointer is null"));
|
return Err(type_error("Invalid i32 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe { ptr::read_unaligned::<i32>(ptr.add(offset) as *const i32) })
|
Ok(unsafe { ptr::read_unaligned::<i32>(ptr.offset(offset) as *const i32) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op]
|
#[op]
|
||||||
pub fn op_ffi_read_u64<FP>(
|
pub fn op_ffi_read_u64<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
out: &mut [u32],
|
out: &mut [u32],
|
||||||
) -> Result<(), AnyError>
|
) -> Result<(), AnyError>
|
||||||
where
|
where
|
||||||
|
@ -349,15 +398,13 @@ where
|
||||||
);
|
);
|
||||||
assert_eq!((outptr as usize % std::mem::size_of::<u64>()), 0);
|
assert_eq!((outptr as usize % std::mem::size_of::<u64>()), 0);
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid u64 pointer, pointer is null"));
|
return Err(type_error("Invalid u64 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let value =
|
let value =
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
unsafe { ptr::read_unaligned::<u64>(ptr.add(offset) as *const u64) };
|
unsafe { ptr::read_unaligned::<u64>(ptr.offset(offset) as *const u64) };
|
||||||
|
|
||||||
// SAFETY: Length and alignment of out slice were asserted to be correct.
|
// SAFETY: Length and alignment of out slice were asserted to be correct.
|
||||||
unsafe { *outptr = value };
|
unsafe { *outptr = value };
|
||||||
|
@ -367,8 +414,8 @@ where
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_i64<FP>(
|
pub fn op_ffi_read_i64<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
out: &mut [u32],
|
out: &mut [u32],
|
||||||
) -> Result<(), AnyError>
|
) -> Result<(), AnyError>
|
||||||
where
|
where
|
||||||
|
@ -386,15 +433,13 @@ where
|
||||||
);
|
);
|
||||||
assert_eq!((outptr as usize % std::mem::size_of::<i64>()), 0);
|
assert_eq!((outptr as usize % std::mem::size_of::<i64>()), 0);
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid i64 pointer, pointer is null"));
|
return Err(type_error("Invalid i64 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let value =
|
let value =
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
unsafe { ptr::read_unaligned::<i64>(ptr.add(offset) as *const i64) };
|
unsafe { ptr::read_unaligned::<i64>(ptr.offset(offset) as *const i64) };
|
||||||
// SAFETY: Length and alignment of out slice were asserted to be correct.
|
// SAFETY: Length and alignment of out slice were asserted to be correct.
|
||||||
unsafe { *outptr = value };
|
unsafe { *outptr = value };
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -403,8 +448,8 @@ where
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_f32<FP>(
|
pub fn op_ffi_read_f32<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<f32, AnyError>
|
) -> Result<f32, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -414,21 +459,19 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid f32 pointer, pointer is null"));
|
return Err(type_error("Invalid f32 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe { ptr::read_unaligned::<f32>(ptr.add(offset) as *const f32) })
|
Ok(unsafe { ptr::read_unaligned::<f32>(ptr.offset(offset) as *const f32) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(fast)]
|
#[op(fast)]
|
||||||
pub fn op_ffi_read_f64<FP>(
|
pub fn op_ffi_read_f64<FP>(
|
||||||
state: &mut deno_core::OpState,
|
state: &mut deno_core::OpState,
|
||||||
ptr: usize,
|
ptr: *mut c_void,
|
||||||
offset: usize,
|
offset: isize,
|
||||||
) -> Result<f64, AnyError>
|
) -> Result<f64, AnyError>
|
||||||
where
|
where
|
||||||
FP: FfiPermissions + 'static,
|
FP: FfiPermissions + 'static,
|
||||||
|
@ -438,12 +481,34 @@ where
|
||||||
let permissions = state.borrow_mut::<FP>();
|
let permissions = state.borrow_mut::<FP>();
|
||||||
permissions.check(None)?;
|
permissions.check(None)?;
|
||||||
|
|
||||||
let ptr = ptr as *const c_void;
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return Err(type_error("Invalid f64 pointer, pointer is null"));
|
return Err(type_error("Invalid f64 pointer, pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: ptr and offset are user provided.
|
// SAFETY: ptr and offset are user provided.
|
||||||
Ok(unsafe { ptr::read_unaligned::<f64>(ptr.add(offset) as *const f64) })
|
Ok(unsafe { ptr::read_unaligned::<f64>(ptr.offset(offset) as *const f64) })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op(fast)]
|
||||||
|
pub fn op_ffi_read_ptr<FP>(
|
||||||
|
state: &mut deno_core::OpState,
|
||||||
|
ptr: *mut c_void,
|
||||||
|
offset: isize,
|
||||||
|
) -> Result<*mut c_void, AnyError>
|
||||||
|
where
|
||||||
|
FP: FfiPermissions + 'static,
|
||||||
|
{
|
||||||
|
check_unstable(state, "Deno.UnsafePointerView#getPointer");
|
||||||
|
|
||||||
|
let permissions = state.borrow_mut::<FP>();
|
||||||
|
permissions.check(None)?;
|
||||||
|
|
||||||
|
if ptr.is_null() {
|
||||||
|
return Err(type_error("Invalid pointer pointer, pointer is null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: ptr and offset are user provided.
|
||||||
|
Ok(unsafe {
|
||||||
|
ptr::read_unaligned::<*mut c_void>(ptr.offset(offset) as *const *mut c_void)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use deno_core::op;
|
||||||
use deno_core::serde_v8;
|
use deno_core::serde_v8;
|
||||||
use deno_core::v8;
|
use deno_core::v8;
|
||||||
use deno_core::ResourceId;
|
use deno_core::ResourceId;
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
#[op(v8)]
|
#[op(v8)]
|
||||||
|
@ -134,13 +135,9 @@ pub fn op_ffi_get_static<'scope>(
|
||||||
number.into()
|
number.into()
|
||||||
}
|
}
|
||||||
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
|
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
|
||||||
let result = data_ptr as u64;
|
let external: v8::Local<v8::Value> =
|
||||||
let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as u64 {
|
v8::External::new(scope, data_ptr as *mut c_void).into();
|
||||||
v8::BigInt::new_from_u64(scope, result).into()
|
external.into()
|
||||||
} else {
|
|
||||||
v8::Number::new(scope, result as f64).into()
|
|
||||||
};
|
|
||||||
integer.into()
|
|
||||||
}
|
}
|
||||||
NativeType::Struct(_) => {
|
NativeType::Struct(_) => {
|
||||||
return Err(type_error("Invalid FFI static type 'struct'"));
|
return Err(type_error("Invalid FFI static type 'struct'"));
|
||||||
|
|
|
@ -48,6 +48,9 @@ pub(crate) fn make_template(sym: &Symbol, trampoline: &Trampoline) -> Template {
|
||||||
let ret = if needs_unwrap(&sym.result_type) {
|
let ret = if needs_unwrap(&sym.result_type) {
|
||||||
params.push(fast_api::Type::TypedArray(fast_api::CType::Int32));
|
params.push(fast_api::Type::TypedArray(fast_api::CType::Int32));
|
||||||
fast_api::Type::Void
|
fast_api::Type::Void
|
||||||
|
} else if sym.result_type == NativeType::Buffer {
|
||||||
|
// Buffer can be used as a return type and converts differently than in parameters.
|
||||||
|
fast_api::Type::Pointer
|
||||||
} else {
|
} else {
|
||||||
fast_api::Type::from(&sym.result_type)
|
fast_api::Type::from(&sym.result_type)
|
||||||
};
|
};
|
||||||
|
@ -71,9 +74,9 @@ impl Trampoline {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Template {
|
pub(crate) struct Template {
|
||||||
args: Box<[fast_api::Type]>,
|
pub args: Box<[fast_api::Type]>,
|
||||||
ret: fast_api::CType,
|
pub ret: fast_api::CType,
|
||||||
symbol_ptr: *const c_void,
|
pub symbol_ptr: *const c_void,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fast_api::FastFunction for Template {
|
impl fast_api::FastFunction for Template {
|
||||||
|
@ -106,9 +109,8 @@ impl From<&NativeType> for fast_api::Type {
|
||||||
NativeType::I64 => fast_api::Type::Int64,
|
NativeType::I64 => fast_api::Type::Int64,
|
||||||
NativeType::U64 => fast_api::Type::Uint64,
|
NativeType::U64 => fast_api::Type::Uint64,
|
||||||
NativeType::ISize => fast_api::Type::Int64,
|
NativeType::ISize => fast_api::Type::Int64,
|
||||||
NativeType::USize | NativeType::Pointer | NativeType::Function => {
|
NativeType::USize => fast_api::Type::Uint64,
|
||||||
fast_api::Type::Uint64
|
NativeType::Pointer | NativeType::Function => fast_api::Type::Pointer,
|
||||||
}
|
|
||||||
NativeType::Buffer => fast_api::Type::TypedArray(fast_api::CType::Uint8),
|
NativeType::Buffer => fast_api::Type::TypedArray(fast_api::CType::Uint8),
|
||||||
NativeType::Struct(_) => {
|
NativeType::Struct(_) => {
|
||||||
fast_api::Type::TypedArray(fast_api::CType::Uint8)
|
fast_api::Type::TypedArray(fast_api::CType::Uint8)
|
||||||
|
|
|
@ -418,13 +418,14 @@ pub(crate) fn generate(
|
||||||
fn q_fast_ty(v: &FastValue) -> Quote {
|
fn q_fast_ty(v: &FastValue) -> Quote {
|
||||||
match v {
|
match v {
|
||||||
FastValue::Void => q!({ () }),
|
FastValue::Void => q!({ () }),
|
||||||
|
FastValue::Bool => q!({ bool }),
|
||||||
FastValue::U32 => q!({ u32 }),
|
FastValue::U32 => q!({ u32 }),
|
||||||
FastValue::I32 => q!({ i32 }),
|
FastValue::I32 => q!({ i32 }),
|
||||||
FastValue::U64 => q!({ u64 }),
|
FastValue::U64 => q!({ u64 }),
|
||||||
FastValue::I64 => q!({ i64 }),
|
FastValue::I64 => q!({ i64 }),
|
||||||
FastValue::F32 => q!({ f32 }),
|
FastValue::F32 => q!({ f32 }),
|
||||||
FastValue::F64 => q!({ f64 }),
|
FastValue::F64 => q!({ f64 }),
|
||||||
FastValue::Bool => q!({ bool }),
|
FastValue::Pointer => q!({ *mut ::std::ffi::c_void }),
|
||||||
FastValue::V8Value => q!({ v8::Local<v8::Value> }),
|
FastValue::V8Value => q!({ v8::Local<v8::Value> }),
|
||||||
FastValue::Uint8Array
|
FastValue::Uint8Array
|
||||||
| FastValue::Uint32Array
|
| FastValue::Uint32Array
|
||||||
|
@ -436,13 +437,14 @@ fn q_fast_ty(v: &FastValue) -> Quote {
|
||||||
fn q_fast_ty_variant(v: &FastValue) -> Quote {
|
fn q_fast_ty_variant(v: &FastValue) -> Quote {
|
||||||
match v {
|
match v {
|
||||||
FastValue::Void => q!({ Void }),
|
FastValue::Void => q!({ Void }),
|
||||||
|
FastValue::Bool => q!({ Bool }),
|
||||||
FastValue::U32 => q!({ Uint32 }),
|
FastValue::U32 => q!({ Uint32 }),
|
||||||
FastValue::I32 => q!({ Int32 }),
|
FastValue::I32 => q!({ Int32 }),
|
||||||
FastValue::U64 => q!({ Uint64 }),
|
FastValue::U64 => q!({ Uint64 }),
|
||||||
FastValue::I64 => q!({ Int64 }),
|
FastValue::I64 => q!({ Int64 }),
|
||||||
FastValue::F32 => q!({ Float32 }),
|
FastValue::F32 => q!({ Float32 }),
|
||||||
FastValue::F64 => q!({ Float64 }),
|
FastValue::F64 => q!({ Float64 }),
|
||||||
FastValue::Bool => q!({ Bool }),
|
FastValue::Pointer => q!({ Pointer }),
|
||||||
FastValue::V8Value => q!({ V8Value }),
|
FastValue::V8Value => q!({ V8Value }),
|
||||||
FastValue::Uint8Array => q!({ TypedArray(CType::Uint8) }),
|
FastValue::Uint8Array => q!({ TypedArray(CType::Uint8) }),
|
||||||
FastValue::Uint32Array => q!({ TypedArray(CType::Uint32) }),
|
FastValue::Uint32Array => q!({ TypedArray(CType::Uint32) }),
|
||||||
|
|
38
ops/lib.rs
38
ops/lib.rs
|
@ -474,6 +474,13 @@ fn codegen_arg(
|
||||||
let #ident = #blk;
|
let #ident = #blk;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// Fast path for `*const c_void` and `*mut c_void`
|
||||||
|
if is_ptr_cvoid(&**ty) {
|
||||||
|
let blk = codegen_cvoid_ptr(core, idx);
|
||||||
|
return quote! {
|
||||||
|
let #ident = #blk;
|
||||||
|
};
|
||||||
|
}
|
||||||
// Otherwise deserialize it via serde_v8
|
// Otherwise deserialize it via serde_v8
|
||||||
quote! {
|
quote! {
|
||||||
let #ident = args.get(#idx as i32);
|
let #ident = args.get(#idx as i32);
|
||||||
|
@ -560,6 +567,19 @@ fn codegen_u8_ptr(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn codegen_cvoid_ptr(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
||||||
|
quote! {{
|
||||||
|
let value = args.get(#idx as i32);
|
||||||
|
if value.is_null() {
|
||||||
|
std::ptr::null_mut()
|
||||||
|
} else if let Ok(b) = #core::v8::Local::<#core::v8::External>::try_from(value) {
|
||||||
|
b.value()
|
||||||
|
} else {
|
||||||
|
return #core::_ops::throw_type_error(scope, format!("Expected External at position {}", #idx));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
fn codegen_u32_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
fn codegen_u32_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 {
|
||||||
quote! {
|
quote! {
|
||||||
if let Ok(view) = #core::v8::Local::<#core::v8::Uint32Array>::try_from(args.get(#idx as i32)) {
|
if let Ok(view) = #core::v8::Local::<#core::v8::Uint32Array>::try_from(args.get(#idx as i32)) {
|
||||||
|
@ -626,6 +646,15 @@ fn codegen_sync_ret(
|
||||||
quote! {
|
quote! {
|
||||||
rv.set_uint32(result as u32);
|
rv.set_uint32(result as u32);
|
||||||
}
|
}
|
||||||
|
} else if is_ptr_cvoid(output) || is_ptr_cvoid_rv(output) {
|
||||||
|
quote! {
|
||||||
|
if result.is_null() {
|
||||||
|
// External canot contain a null pointer, null pointers are instead represented as null.
|
||||||
|
rv.set_null();
|
||||||
|
} else {
|
||||||
|
rv.set(v8::External::new(scope, result as *mut ::std::ffi::c_void).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
match #core::serde_v8::to_v8(scope, result) {
|
match #core::serde_v8::to_v8(scope, result) {
|
||||||
|
@ -723,6 +752,15 @@ fn is_ptr_u8(ty: impl ToTokens) -> bool {
|
||||||
tokens(ty) == "* const u8"
|
tokens(ty) == "* const u8"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_ptr_cvoid(ty: impl ToTokens) -> bool {
|
||||||
|
tokens(&ty) == "* const c_void" || tokens(&ty) == "* mut c_void"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ptr_cvoid_rv(ty: impl ToTokens) -> bool {
|
||||||
|
tokens(&ty).contains("Result < * const c_void")
|
||||||
|
|| tokens(&ty).contains("Result < * mut c_void")
|
||||||
|
}
|
||||||
|
|
||||||
fn is_optional_fast_callback_option(ty: impl ToTokens) -> bool {
|
fn is_optional_fast_callback_option(ty: impl ToTokens) -> bool {
|
||||||
tokens(&ty).contains("Option < & mut FastApiCallbackOptions")
|
tokens(&ty).contains("Option < & mut FastApiCallbackOptions")
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ enum TransformKind {
|
||||||
SliceU8(bool),
|
SliceU8(bool),
|
||||||
SliceF64(bool),
|
SliceF64(bool),
|
||||||
PtrU8,
|
PtrU8,
|
||||||
|
PtrVoid,
|
||||||
WasmMemory,
|
WasmMemory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +91,13 @@ impl Transform {
|
||||||
index,
|
index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn void_ptr(index: usize) -> Self {
|
||||||
|
Transform {
|
||||||
|
kind: TransformKind::PtrVoid,
|
||||||
|
index,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -195,19 +203,25 @@ impl Transform {
|
||||||
.as_ptr();
|
.as_ptr();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
TransformKind::PtrVoid => {
|
||||||
|
*ty = parse_quote! { *mut ::std::ffi::c_void };
|
||||||
|
|
||||||
|
q!(Vars {}, {})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_fast_scalar(s: &str) -> Option<FastValue> {
|
fn get_fast_scalar(s: &str) -> Option<FastValue> {
|
||||||
match s {
|
match s {
|
||||||
|
"bool" => Some(FastValue::Bool),
|
||||||
"u32" => Some(FastValue::U32),
|
"u32" => Some(FastValue::U32),
|
||||||
"i32" => Some(FastValue::I32),
|
"i32" => Some(FastValue::I32),
|
||||||
"u64" => Some(FastValue::U64),
|
"u64" => Some(FastValue::U64),
|
||||||
"i64" => Some(FastValue::I64),
|
"i64" => Some(FastValue::I64),
|
||||||
"f32" => Some(FastValue::F32),
|
"f32" => Some(FastValue::F32),
|
||||||
"f64" => Some(FastValue::F64),
|
"f64" => Some(FastValue::F64),
|
||||||
"bool" => Some(FastValue::Bool),
|
"* const c_void" | "* mut c_void" => Some(FastValue::Pointer),
|
||||||
"ResourceId" => Some(FastValue::U32),
|
"ResourceId" => Some(FastValue::U32),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -226,13 +240,14 @@ fn can_return_fast(v: &FastValue) -> bool {
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub(crate) enum FastValue {
|
pub(crate) enum FastValue {
|
||||||
Void,
|
Void,
|
||||||
|
Bool,
|
||||||
U32,
|
U32,
|
||||||
I32,
|
I32,
|
||||||
U64,
|
U64,
|
||||||
I64,
|
I64,
|
||||||
F32,
|
F32,
|
||||||
F64,
|
F64,
|
||||||
Bool,
|
Pointer,
|
||||||
V8Value,
|
V8Value,
|
||||||
Uint8Array,
|
Uint8Array,
|
||||||
Uint32Array,
|
Uint32Array,
|
||||||
|
@ -414,6 +429,31 @@ impl Optimizer {
|
||||||
{
|
{
|
||||||
self.fast_result = Some(FastValue::Void);
|
self.fast_result = Some(FastValue::Void);
|
||||||
}
|
}
|
||||||
|
Some(GenericArgument::Type(Type::Ptr(TypePtr {
|
||||||
|
mutability: Some(_),
|
||||||
|
elem,
|
||||||
|
..
|
||||||
|
}))) => {
|
||||||
|
match &**elem {
|
||||||
|
Type::Path(TypePath {
|
||||||
|
path: Path { segments, .. },
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
// Is `T` a c_void?
|
||||||
|
let segment = single_segment(segments)?;
|
||||||
|
match segment {
|
||||||
|
PathSegment { ident, .. } if ident == "c_void" => {
|
||||||
|
self.fast_result = Some(FastValue::Pointer);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(BailoutReason::FastUnsupportedParamType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,6 +470,29 @@ impl Optimizer {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Type::Ptr(TypePtr {
|
||||||
|
mutability: Some(_),
|
||||||
|
elem,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
match &**elem {
|
||||||
|
Type::Path(TypePath {
|
||||||
|
path: Path { segments, .. },
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
// Is `T` a c_void?
|
||||||
|
let segment = single_segment(segments)?;
|
||||||
|
match segment {
|
||||||
|
PathSegment { ident, .. } if ident == "c_void" => {
|
||||||
|
self.fast_result = Some(FastValue::Pointer);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -684,6 +747,31 @@ impl Optimizer {
|
||||||
}
|
}
|
||||||
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
},
|
},
|
||||||
|
// *const T
|
||||||
|
Type::Ptr(TypePtr {
|
||||||
|
elem,
|
||||||
|
mutability: Some(_),
|
||||||
|
..
|
||||||
|
}) => match &**elem {
|
||||||
|
Type::Path(TypePath {
|
||||||
|
path: Path { segments, .. },
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let segment = single_segment(segments)?;
|
||||||
|
match segment {
|
||||||
|
// Is `T` a c_void?
|
||||||
|
PathSegment { ident, .. } if ident == "c_void" => {
|
||||||
|
self.fast_parameters.push(FastValue::Pointer);
|
||||||
|
assert!(self
|
||||||
|
.transforms
|
||||||
|
.insert(index, Transform::void_ptr(index))
|
||||||
|
.is_none());
|
||||||
|
}
|
||||||
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
|
},
|
||||||
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
},
|
},
|
||||||
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
_ => return Err(BailoutReason::FastUnsupportedParamType),
|
||||||
|
|
11
ops/optimizer_tests/op_ffi_ptr_value.expected
Normal file
11
ops/optimizer_tests/op_ffi_ptr_value.expected
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
=== Optimizer Dump ===
|
||||||
|
returns_result: false
|
||||||
|
has_ref_opstate: false
|
||||||
|
has_rc_opstate: false
|
||||||
|
has_fast_callback_option: false
|
||||||
|
needs_fast_callback_option: true
|
||||||
|
fast_result: Some(Void)
|
||||||
|
fast_parameters: [V8Value, Pointer, Uint32Array]
|
||||||
|
transforms: {0: Transform { kind: PtrVoid, index: 0 }, 1: Transform { kind: SliceU32(true), index: 1 }}
|
||||||
|
is_async: false
|
||||||
|
fast_compatible: true
|
127
ops/optimizer_tests/op_ffi_ptr_value.out
Normal file
127
ops/optimizer_tests/op_ffi_ptr_value.out
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
///Auto-generated by `deno_ops`, i.e: `#[op]`
|
||||||
|
///
|
||||||
|
///Use `op_ffi_ptr_value::decl()` to get an op-declaration
|
||||||
|
///you can include in a `deno_core::Extension`.
|
||||||
|
pub struct op_ffi_ptr_value;
|
||||||
|
#[doc(hidden)]
|
||||||
|
impl op_ffi_ptr_value {
|
||||||
|
pub fn name() -> &'static str {
|
||||||
|
stringify!(op_ffi_ptr_value)
|
||||||
|
}
|
||||||
|
pub fn v8_fn_ptr<'scope>() -> deno_core::v8::FunctionCallback {
|
||||||
|
use deno_core::v8::MapFnTo;
|
||||||
|
Self::v8_func.map_fn_to()
|
||||||
|
}
|
||||||
|
pub fn decl<'scope>() -> deno_core::OpDecl {
|
||||||
|
deno_core::OpDecl {
|
||||||
|
name: Self::name(),
|
||||||
|
v8_fn_ptr: Self::v8_fn_ptr(),
|
||||||
|
enabled: true,
|
||||||
|
fast_fn: Some(
|
||||||
|
Box::new(op_ffi_ptr_value_fast {
|
||||||
|
_phantom: ::std::marker::PhantomData,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
is_async: false,
|
||||||
|
is_unstable: false,
|
||||||
|
is_v8: false,
|
||||||
|
argc: 2usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn call(ptr: *mut c_void, out: &mut [u32]) {}
|
||||||
|
pub fn v8_func<'scope>(
|
||||||
|
scope: &mut deno_core::v8::HandleScope<'scope>,
|
||||||
|
args: deno_core::v8::FunctionCallbackArguments,
|
||||||
|
mut rv: deno_core::v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let ctx = unsafe {
|
||||||
|
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
||||||
|
as *const deno_core::_ops::OpCtx)
|
||||||
|
};
|
||||||
|
let arg_0 = {
|
||||||
|
let value = args.get(0usize as i32);
|
||||||
|
if value.is_null() {
|
||||||
|
std::ptr::null_mut()
|
||||||
|
} else if let Ok(b)
|
||||||
|
= deno_core::v8::Local::<deno_core::v8::External>::try_from(value) {
|
||||||
|
b.value()
|
||||||
|
} else {
|
||||||
|
return deno_core::_ops::throw_type_error(
|
||||||
|
scope,
|
||||||
|
format!("Expected External at position {}", 0usize),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let arg_1 = if let Ok(view)
|
||||||
|
= deno_core::v8::Local::<
|
||||||
|
deno_core::v8::Uint32Array,
|
||||||
|
>::try_from(args.get(1usize as i32)) {
|
||||||
|
let (offset, len) = (view.byte_offset(), view.byte_length());
|
||||||
|
let buffer = match view.buffer(scope) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
return deno_core::_ops::throw_type_error(
|
||||||
|
scope,
|
||||||
|
format!("Expected Uint32Array at position {}", 1usize),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(data) = buffer.data() {
|
||||||
|
let store = data.cast::<u8>().as_ptr();
|
||||||
|
unsafe {
|
||||||
|
::std::slice::from_raw_parts_mut(
|
||||||
|
store.add(offset) as *mut u32,
|
||||||
|
len / 4,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
&mut []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return deno_core::_ops::throw_type_error(
|
||||||
|
scope,
|
||||||
|
format!("Expected Uint32Array at position {}", 1usize),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let result = Self::call(arg_0, arg_1);
|
||||||
|
let op_state = ::std::cell::RefCell::borrow(&*ctx.state);
|
||||||
|
op_state.tracker.track_sync(ctx.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct op_ffi_ptr_value_fast {
|
||||||
|
_phantom: ::std::marker::PhantomData<()>,
|
||||||
|
}
|
||||||
|
impl<'scope> deno_core::v8::fast_api::FastFunction for op_ffi_ptr_value_fast {
|
||||||
|
fn function(&self) -> *const ::std::ffi::c_void {
|
||||||
|
op_ffi_ptr_value_fast_fn as *const ::std::ffi::c_void
|
||||||
|
}
|
||||||
|
fn args(&self) -> &'static [deno_core::v8::fast_api::Type] {
|
||||||
|
use deno_core::v8::fast_api::Type::*;
|
||||||
|
use deno_core::v8::fast_api::CType;
|
||||||
|
&[V8Value, Pointer, TypedArray(CType::Uint32), CallbackOptions]
|
||||||
|
}
|
||||||
|
fn return_type(&self) -> deno_core::v8::fast_api::CType {
|
||||||
|
deno_core::v8::fast_api::CType::Void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn op_ffi_ptr_value_fast_fn<'scope>(
|
||||||
|
_: deno_core::v8::Local<deno_core::v8::Object>,
|
||||||
|
ptr: *mut ::std::ffi::c_void,
|
||||||
|
out: *const deno_core::v8::fast_api::FastApiTypedArray<u32>,
|
||||||
|
fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
|
||||||
|
) -> () {
|
||||||
|
use deno_core::v8;
|
||||||
|
use deno_core::_ops;
|
||||||
|
let out = match unsafe { &*out }.get_storage_if_aligned() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
unsafe { &mut *fast_api_callback_options }.fallback = true;
|
||||||
|
return Default::default();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = op_ffi_ptr_value::call(ptr, out);
|
||||||
|
result
|
||||||
|
}
|
3
ops/optimizer_tests/op_ffi_ptr_value.rs
Normal file
3
ops/optimizer_tests/op_ffi_ptr_value.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub fn op_ffi_ptr_value(ptr: *mut c_void, out: &mut [u32]) {
|
||||||
|
// ...
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ pub enum Error {
|
||||||
ExpectedObject,
|
ExpectedObject,
|
||||||
ExpectedBuffer,
|
ExpectedBuffer,
|
||||||
ExpectedDetachable,
|
ExpectedDetachable,
|
||||||
|
ExpectedExternal,
|
||||||
|
|
||||||
ExpectedUtf8,
|
ExpectedUtf8,
|
||||||
ExpectedLatin1,
|
ExpectedLatin1,
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub use magic::bytestring::ByteString;
|
||||||
pub use magic::detached_buffer::DetachedBuffer;
|
pub use magic::detached_buffer::DetachedBuffer;
|
||||||
pub use magic::string_or_buffer::StringOrBuffer;
|
pub use magic::string_or_buffer::StringOrBuffer;
|
||||||
pub use magic::u16string::U16String;
|
pub use magic::u16string::U16String;
|
||||||
|
pub use magic::ExternalPointer;
|
||||||
pub use magic::Global;
|
pub use magic::Global;
|
||||||
pub use magic::Value;
|
pub use magic::Value;
|
||||||
pub use ser::to_v8;
|
pub use ser::to_v8;
|
||||||
|
|
56
serde_v8/magic/external_pointer.rs
Normal file
56
serde_v8/magic/external_pointer.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
use super::transl8::impl_magic;
|
||||||
|
use super::transl8::FromV8;
|
||||||
|
use super::transl8::ToV8;
|
||||||
|
|
||||||
|
pub struct ExternalPointer(*mut c_void);
|
||||||
|
|
||||||
|
// SAFETY: Nonblocking FFI is user controller and we must trust user to have it right.
|
||||||
|
unsafe impl Send for ExternalPointer {}
|
||||||
|
// SAFETY: Nonblocking FFI is user controller and we must trust user to have it right.
|
||||||
|
unsafe impl Sync for ExternalPointer {}
|
||||||
|
|
||||||
|
impl_magic!(ExternalPointer);
|
||||||
|
|
||||||
|
impl ToV8 for ExternalPointer {
|
||||||
|
fn to_v8<'a>(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut v8::HandleScope<'a>,
|
||||||
|
) -> Result<v8::Local<'a, v8::Value>, crate::Error> {
|
||||||
|
if self.0.is_null() {
|
||||||
|
Ok(v8::null(scope).into())
|
||||||
|
} else {
|
||||||
|
Ok(v8::External::new(scope, self.0).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromV8 for ExternalPointer {
|
||||||
|
fn from_v8(
|
||||||
|
_scope: &mut v8::HandleScope,
|
||||||
|
value: v8::Local<v8::Value>,
|
||||||
|
) -> Result<Self, crate::Error> {
|
||||||
|
if value.is_null() {
|
||||||
|
Ok(ExternalPointer(std::ptr::null_mut()))
|
||||||
|
} else if let Ok(external) = v8::Local::<v8::External>::try_from(value) {
|
||||||
|
Ok(ExternalPointer(external.value()))
|
||||||
|
} else {
|
||||||
|
Err(crate::Error::ExpectedExternal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<*mut c_void> for ExternalPointer {
|
||||||
|
fn from(value: *mut c_void) -> Self {
|
||||||
|
ExternalPointer(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<*const c_void> for ExternalPointer {
|
||||||
|
fn from(value: *const c_void) -> Self {
|
||||||
|
ExternalPointer(value as *mut c_void)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
pub mod bytestring;
|
pub mod bytestring;
|
||||||
pub mod detached_buffer;
|
pub mod detached_buffer;
|
||||||
|
mod external_pointer;
|
||||||
mod global;
|
mod global;
|
||||||
pub(super) mod rawbytes;
|
pub(super) mod rawbytes;
|
||||||
pub mod string_or_buffer;
|
pub mod string_or_buffer;
|
||||||
|
@ -9,5 +10,6 @@ pub mod transl8;
|
||||||
pub mod u16string;
|
pub mod u16string;
|
||||||
pub mod v8slice;
|
pub mod v8slice;
|
||||||
mod value;
|
mod value;
|
||||||
|
pub use external_pointer::ExternalPointer;
|
||||||
pub use global::Global;
|
pub use global::Global;
|
||||||
pub use value::Value;
|
pub use value::Value;
|
||||||
|
|
|
@ -16,6 +16,7 @@ use crate::magic::transl8::ToV8;
|
||||||
use crate::magic::transl8::MAGIC_FIELD;
|
use crate::magic::transl8::MAGIC_FIELD;
|
||||||
use crate::ByteString;
|
use crate::ByteString;
|
||||||
use crate::DetachedBuffer;
|
use crate::DetachedBuffer;
|
||||||
|
use crate::ExternalPointer;
|
||||||
use crate::StringOrBuffer;
|
use crate::StringOrBuffer;
|
||||||
use crate::U16String;
|
use crate::U16String;
|
||||||
use crate::ZeroCopyBuf;
|
use crate::ZeroCopyBuf;
|
||||||
|
@ -269,6 +270,7 @@ impl<'a, 'b, 'c, T: MagicType + ToV8> ser::SerializeStruct
|
||||||
|
|
||||||
// Dispatches between magic and regular struct serializers
|
// Dispatches between magic and regular struct serializers
|
||||||
pub enum StructSerializers<'a, 'b, 'c> {
|
pub enum StructSerializers<'a, 'b, 'c> {
|
||||||
|
ExternalPointer(MagicalSerializer<'a, 'b, 'c, magic::ExternalPointer>),
|
||||||
Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>),
|
Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>),
|
||||||
ZeroCopyBuf(MagicalSerializer<'a, 'b, 'c, ZeroCopyBuf>),
|
ZeroCopyBuf(MagicalSerializer<'a, 'b, 'c, ZeroCopyBuf>),
|
||||||
MagicDetached(MagicalSerializer<'a, 'b, 'c, DetachedBuffer>),
|
MagicDetached(MagicalSerializer<'a, 'b, 'c, DetachedBuffer>),
|
||||||
|
@ -288,6 +290,7 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
|
||||||
value: &T,
|
value: &T,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
|
StructSerializers::ExternalPointer(s) => s.serialize_field(key, value),
|
||||||
StructSerializers::Magic(s) => s.serialize_field(key, value),
|
StructSerializers::Magic(s) => s.serialize_field(key, value),
|
||||||
StructSerializers::ZeroCopyBuf(s) => s.serialize_field(key, value),
|
StructSerializers::ZeroCopyBuf(s) => s.serialize_field(key, value),
|
||||||
StructSerializers::MagicDetached(s) => s.serialize_field(key, value),
|
StructSerializers::MagicDetached(s) => s.serialize_field(key, value),
|
||||||
|
@ -302,6 +305,7 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
|
||||||
|
|
||||||
fn end(self) -> JsResult<'a> {
|
fn end(self) -> JsResult<'a> {
|
||||||
match self {
|
match self {
|
||||||
|
StructSerializers::ExternalPointer(s) => s.end(),
|
||||||
StructSerializers::Magic(s) => s.end(),
|
StructSerializers::Magic(s) => s.end(),
|
||||||
StructSerializers::ZeroCopyBuf(s) => s.end(),
|
StructSerializers::ZeroCopyBuf(s) => s.end(),
|
||||||
StructSerializers::MagicDetached(s) => s.end(),
|
StructSerializers::MagicDetached(s) => s.end(),
|
||||||
|
@ -385,8 +389,8 @@ macro_rules! forward_to {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_SAFE_INTEGER: i64 = (1 << 53) - 1;
|
pub(crate) const MAX_SAFE_INTEGER: i64 = (1 << 53) - 1;
|
||||||
const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER;
|
pub(crate) const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER;
|
||||||
|
|
||||||
impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
|
impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
|
||||||
type Ok = v8::Local<'a, v8::Value>;
|
type Ok = v8::Local<'a, v8::Value>;
|
||||||
|
@ -564,6 +568,10 @@ impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> Result<Self::SerializeStruct> {
|
) -> Result<Self::SerializeStruct> {
|
||||||
match name {
|
match name {
|
||||||
|
magic::ExternalPointer::MAGIC_NAME => {
|
||||||
|
let m = MagicalSerializer::<ExternalPointer>::new(self.scope);
|
||||||
|
Ok(StructSerializers::ExternalPointer(m))
|
||||||
|
}
|
||||||
ByteString::MAGIC_NAME => {
|
ByteString::MAGIC_NAME => {
|
||||||
let m = MagicalSerializer::<ByteString>::new(self.scope);
|
let m = MagicalSerializer::<ByteString>::new(self.scope);
|
||||||
Ok(StructSerializers::MagicByteString(m))
|
Ok(StructSerializers::MagicByteString(m))
|
||||||
|
|
|
@ -140,30 +140,31 @@ remote.symbols.method14(0);
|
||||||
remote.symbols.method15("foo");
|
remote.symbols.method15("foo");
|
||||||
// @ts-expect-error: Invalid argument
|
// @ts-expect-error: Invalid argument
|
||||||
remote.symbols.method15(new Uint16Array(1));
|
remote.symbols.method15(new Uint16Array(1));
|
||||||
remote.symbols.method15(0n);
|
remote.symbols.method15(null);
|
||||||
|
remote.symbols.method15({} as Deno.PointerValue);
|
||||||
|
|
||||||
const result = remote.symbols.method16();
|
const result = remote.symbols.method16();
|
||||||
// @ts-expect-error: Invalid argument
|
// @ts-expect-error: Invalid argument
|
||||||
let r_0: string = result;
|
let r_0: string = result;
|
||||||
let r_1: Deno.PointerValue = result;
|
let r_1: number | bigint = result;
|
||||||
|
|
||||||
const result2 = remote.symbols.method17();
|
const result2 = remote.symbols.method17();
|
||||||
// @ts-expect-error: Invalid argument
|
// @ts-expect-error: Invalid argument
|
||||||
result2.then((_0: string) => {});
|
result2.then((_0: string) => {});
|
||||||
result2.then((_1: Deno.PointerValue) => {});
|
result2.then((_1: number | bigint) => {});
|
||||||
|
|
||||||
const result3 = remote.symbols.method18();
|
const result3 = remote.symbols.method18();
|
||||||
// @ts-expect-error: Invalid argument
|
// @ts-expect-error: Invalid argument
|
||||||
let r3_0: Deno.BufferSource = result3;
|
let r3_0: Deno.BufferSource = result3;
|
||||||
let r3_1: Deno.UnsafePointer = result3;
|
let r3_1: null | Deno.UnsafePointer = result3;
|
||||||
|
|
||||||
const result4 = remote.symbols.method19();
|
const result4 = remote.symbols.method19();
|
||||||
// @ts-expect-error: Invalid argument
|
// @ts-expect-error: Invalid argument
|
||||||
result4.then((_0: Deno.BufferSource) => {});
|
result4.then((_0: Deno.BufferSource) => {});
|
||||||
result4.then((_1: Deno.UnsafePointer) => {});
|
result4.then((_1: null | Deno.UnsafePointer) => {});
|
||||||
|
|
||||||
const fnptr = new Deno.UnsafeFnPointer(
|
const fnptr = new Deno.UnsafeFnPointer(
|
||||||
0n,
|
{} as NonNullable<Deno.PointerValue>,
|
||||||
{
|
{
|
||||||
parameters: ["u32", "pointer"],
|
parameters: ["u32", "pointer"],
|
||||||
result: "void",
|
result: "void",
|
||||||
|
@ -210,7 +211,7 @@ const unsafe_callback_right1 = new Deno.UnsafeCallback(
|
||||||
parameters: ["u8", "u32", "pointer"],
|
parameters: ["u8", "u32", "pointer"],
|
||||||
result: "void",
|
result: "void",
|
||||||
},
|
},
|
||||||
(_1: number, _2: number, _3: Deno.PointerValue) => {},
|
(_1: number, _2: number, _3: null | Deno.PointerValue) => {},
|
||||||
);
|
);
|
||||||
const unsafe_callback_right2 = new Deno.UnsafeCallback(
|
const unsafe_callback_right2 = new Deno.UnsafeCallback(
|
||||||
{
|
{
|
||||||
|
@ -232,14 +233,14 @@ const unsafe_callback_right4 = new Deno.UnsafeCallback(
|
||||||
parameters: ["u8", "u32", "pointer"],
|
parameters: ["u8", "u32", "pointer"],
|
||||||
result: "u8",
|
result: "u8",
|
||||||
},
|
},
|
||||||
(_1: number, _2: number, _3: Deno.PointerValue) => 3,
|
(_1: number, _2: number, _3: null | Deno.PointerValue) => 3,
|
||||||
);
|
);
|
||||||
const unsafe_callback_right5 = new Deno.UnsafeCallback(
|
const unsafe_callback_right5 = new Deno.UnsafeCallback(
|
||||||
{
|
{
|
||||||
parameters: ["u8", "i32", "pointer"],
|
parameters: ["u8", "i32", "pointer"],
|
||||||
result: "void",
|
result: "void",
|
||||||
},
|
},
|
||||||
(_1: number, _2: number, _3: Deno.PointerValue) => {},
|
(_1: number, _2: number, _3: null | Deno.PointerValue) => {},
|
||||||
);
|
);
|
||||||
|
|
||||||
// @ts-expect-error: Must pass callback
|
// @ts-expect-error: Must pass callback
|
||||||
|
@ -255,9 +256,9 @@ remote.symbols.method23(new Uint32Array(1));
|
||||||
remote.symbols.method23(new Uint8Array(1));
|
remote.symbols.method23(new Uint8Array(1));
|
||||||
|
|
||||||
// @ts-expect-error: Cannot pass pointer values as buffer.
|
// @ts-expect-error: Cannot pass pointer values as buffer.
|
||||||
remote.symbols.method23(0);
|
remote.symbols.method23({});
|
||||||
// @ts-expect-error: Cannot pass pointer values as buffer.
|
// @ts-expect-error: Cannot pass pointer values as buffer.
|
||||||
remote.symbols.method23(0n);
|
remote.symbols.method23({});
|
||||||
remote.symbols.method23(null);
|
remote.symbols.method23(null);
|
||||||
|
|
||||||
// @ts-expect-error: Cannot pass number as bool.
|
// @ts-expect-error: Cannot pass number as bool.
|
||||||
|
@ -278,16 +279,16 @@ let r42_1: number = remote.symbols.method24(true);
|
||||||
|
|
||||||
// @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: Deno.PointerValue = remote.symbols.static1;
|
const static1_right: number | bigint = remote.symbols.static1;
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static2_wrong: null = remote.symbols.static2;
|
const static2_wrong: null = remote.symbols.static2;
|
||||||
const static2_right: Deno.UnsafePointer = remote.symbols.static2;
|
const static2_right: null | Deno.UnsafePointer = remote.symbols.static2;
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static3_wrong: null = remote.symbols.static3;
|
const static3_wrong: null = remote.symbols.static3;
|
||||||
const static3_right: Deno.PointerValue = remote.symbols.static3;
|
const static3_right: number | bigint = remote.symbols.static3;
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static4_wrong: null = remote.symbols.static4;
|
const static4_wrong: null = remote.symbols.static4;
|
||||||
const static4_right: Deno.PointerValue = remote.symbols.static4;
|
const static4_right: number | bigint = remote.symbols.static4;
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static5_wrong: null = remote.symbols.static5;
|
const static5_wrong: null = remote.symbols.static5;
|
||||||
const static5_right: number = remote.symbols.static5;
|
const static5_right: number = remote.symbols.static5;
|
||||||
|
@ -299,7 +300,7 @@ const static7_wrong: null = remote.symbols.static7;
|
||||||
const static7_right: number = remote.symbols.static7;
|
const static7_right: number = remote.symbols.static7;
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static8_wrong: null = remote.symbols.static8;
|
const static8_wrong: null = remote.symbols.static8;
|
||||||
const static8_right: Deno.PointerValue = remote.symbols.static8;
|
const static8_right: number | bigint = remote.symbols.static8;
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static9_wrong: null = remote.symbols.static9;
|
const static9_wrong: null = remote.symbols.static9;
|
||||||
const static9_right: number = remote.symbols.static9;
|
const static9_right: number = remote.symbols.static9;
|
||||||
|
@ -311,7 +312,7 @@ const static11_wrong: null = remote.symbols.static11;
|
||||||
const static11_right: number = remote.symbols.static11;
|
const static11_right: number = remote.symbols.static11;
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static12_wrong: null = remote.symbols.static12;
|
const static12_wrong: null = remote.symbols.static12;
|
||||||
const static12_right: Deno.PointerValue = remote.symbols.static12;
|
const static12_right: number | bigint = remote.symbols.static12;
|
||||||
// @ts-expect-error: Invalid member type
|
// @ts-expect-error: Invalid member type
|
||||||
const static13_wrong: null = remote.symbols.static13;
|
const static13_wrong: null = remote.symbols.static13;
|
||||||
const static13_right: number = remote.symbols.static13;
|
const static13_right: number = remote.symbols.static13;
|
||||||
|
@ -376,8 +377,8 @@ type __Tests__ = [
|
||||||
symbols: {
|
symbols: {
|
||||||
pushBuf: (
|
pushBuf: (
|
||||||
buf: BufferSource | null,
|
buf: BufferSource | null,
|
||||||
ptr: Deno.PointerValue | null,
|
ptr: Deno.PointerValue,
|
||||||
func: Deno.PointerValue | null,
|
func: Deno.PointerValue,
|
||||||
) => Deno.PointerValue;
|
) => Deno.PointerValue;
|
||||||
};
|
};
|
||||||
close(): void;
|
close(): void;
|
||||||
|
@ -395,8 +396,8 @@ type __Tests__ = [
|
||||||
{
|
{
|
||||||
symbols: {
|
symbols: {
|
||||||
foo: (
|
foo: (
|
||||||
...args: (Deno.PointerValue | null)[]
|
...args: (number | Deno.PointerValue | null)[]
|
||||||
) => Deno.PointerValue;
|
) => number | bigint;
|
||||||
};
|
};
|
||||||
close(): void;
|
close(): void;
|
||||||
},
|
},
|
||||||
|
|
|
@ -97,12 +97,8 @@ fn basic() {
|
||||||
-9007199254740992n\n\
|
-9007199254740992n\n\
|
||||||
579.9119873046875\n\
|
579.9119873046875\n\
|
||||||
579.912\n\
|
579.912\n\
|
||||||
After sleep_blocking\n\
|
|
||||||
true\n\
|
|
||||||
Before\n\
|
Before\n\
|
||||||
true\n\
|
|
||||||
After\n\
|
After\n\
|
||||||
true\n\
|
|
||||||
logCallback\n\
|
logCallback\n\
|
||||||
1 -1 2 -2 3 -3 4 -4 0.5 -0.5 1 2 3 4 5 6 7 8\n\
|
1 -1 2 -2 3 -3 4 -4 0.5 -0.5 1 2 3 4 5 6 7 8\n\
|
||||||
u8: 8\n\
|
u8: 8\n\
|
||||||
|
@ -119,23 +115,12 @@ fn basic() {
|
||||||
78\n\
|
78\n\
|
||||||
STORED_FUNCTION cleared\n\
|
STORED_FUNCTION cleared\n\
|
||||||
STORED_FUNCTION_2 cleared\n\
|
STORED_FUNCTION_2 cleared\n\
|
||||||
Thread safe call counter: 0\n\
|
|
||||||
logCallback\n\
|
logCallback\n\
|
||||||
Thread safe call counter: 1\n\
|
|
||||||
u8: 8\n\
|
u8: 8\n\
|
||||||
Static u32: 42\n\
|
|
||||||
Static i64: -1242464576485\n\
|
|
||||||
Static ptr: true\n\
|
|
||||||
Static ptr value: 42\n\
|
|
||||||
Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }\n\
|
Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }\n\
|
||||||
Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }\n\
|
Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }\n\
|
||||||
Rect { x: 20.0, y: 20.0, w: 100.0, h: 200.0 }\n\
|
Rect { x: 20.0, y: 20.0, w: 100.0, h: 200.0 }\n\
|
||||||
Mixed { u8: 3, f32: 12.515, rect: Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }, usize: 12456789, array: [8, 32] }\n\
|
Mixed { u8: 3, f32: 12.515, rect: Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }, usize: 12456789, array: [8, 32] }\n\
|
||||||
arrayBuffer.byteLength: 4\n\
|
|
||||||
uint32Array.length: 1\n\
|
|
||||||
uint32Array[0]: 42\n\
|
|
||||||
uint32Array[0] after mutation: 55\n\
|
|
||||||
Static ptr value after mutation: 55\n\
|
|
||||||
2264956937\n\
|
2264956937\n\
|
||||||
2264956937\n\
|
2264956937\n\
|
||||||
Correct number of resources\n";
|
Correct number of resources\n";
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
// deno-lint-ignore-file
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
// Run using cargo test or `--v8-options=--allow-natives-syntax`
|
// Run using cargo test or `--v8-flags=--allow-natives-syntax`
|
||||||
|
|
||||||
import { assertEquals, assertInstanceOf, assertNotEquals } from "https://deno.land/std@0.149.0/testing/asserts.ts";
|
|
||||||
import {
|
import {
|
||||||
assertThrows,
|
assertThrows,
|
||||||
assert,
|
assert,
|
||||||
|
assertNotEquals,
|
||||||
|
assertInstanceOf,
|
||||||
|
assertEquals,
|
||||||
|
assertFalse,
|
||||||
} from "../../test_util/std/testing/asserts.ts";
|
} from "../../test_util/std/testing/asserts.ts";
|
||||||
|
|
||||||
const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
|
const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
|
||||||
|
@ -368,8 +371,8 @@ assertEquals(isNullBufferDeopt(externalOneBuffer), false, "isNullBufferDeopt(ext
|
||||||
// Due to ops macro using `Local<ArrayBuffer>->Data()` to get the pointer for the slice that is then used to get
|
// Due to ops macro using `Local<ArrayBuffer>->Data()` to get the pointer for the slice that is then used to get
|
||||||
// the pointer of an ArrayBuffer / TypedArray, the same effect can be seen where a zero byte length buffer returns
|
// the pointer of an ArrayBuffer / TypedArray, the same effect can be seen where a zero byte length buffer returns
|
||||||
// a null pointer as its pointer value.
|
// a null pointer as its pointer value.
|
||||||
assertEquals(Deno.UnsafePointer.of(externalZeroBuffer), 0, "Deno.UnsafePointer.of(externalZeroBuffer) !== 0");
|
assertEquals(Deno.UnsafePointer.of(externalZeroBuffer), null, "Deno.UnsafePointer.of(externalZeroBuffer) !== null");
|
||||||
assertNotEquals(Deno.UnsafePointer.of(externalOneBuffer), 0, "Deno.UnsafePointer.of(externalOneBuffer) !== 0");
|
assertNotEquals(Deno.UnsafePointer.of(externalOneBuffer), null, "Deno.UnsafePointer.of(externalOneBuffer) === null");
|
||||||
|
|
||||||
const addU32Ptr = dylib.symbols.get_add_u32_ptr();
|
const addU32Ptr = dylib.symbols.get_add_u32_ptr();
|
||||||
const addU32 = new Deno.UnsafeFnPointer(addU32Ptr, {
|
const addU32 = new Deno.UnsafeFnPointer(addU32Ptr, {
|
||||||
|
@ -486,16 +489,15 @@ await promise;
|
||||||
|
|
||||||
let start = performance.now();
|
let start = performance.now();
|
||||||
dylib.symbols.sleep_blocking(100);
|
dylib.symbols.sleep_blocking(100);
|
||||||
console.log("After sleep_blocking");
|
assert(performance.now() - start >= 100);
|
||||||
console.log(performance.now() - start >= 100);
|
|
||||||
|
|
||||||
start = performance.now();
|
start = performance.now();
|
||||||
const promise_2 = dylib.symbols.sleep_nonblocking(100).then(() => {
|
const promise_2 = dylib.symbols.sleep_nonblocking(100).then(() => {
|
||||||
console.log("After");
|
console.log("After");
|
||||||
console.log(performance.now() - start >= 100);
|
assert(performance.now() - start >= 100);
|
||||||
});
|
});
|
||||||
console.log("Before");
|
console.log("Before");
|
||||||
console.log(performance.now() - start < 100);
|
assert(performance.now() - start < 100);
|
||||||
|
|
||||||
// Await to make sure `sleep_nonblocking` calls and logs before we proceed
|
// Await to make sure `sleep_nonblocking` calls and logs before we proceed
|
||||||
await promise_2;
|
await promise_2;
|
||||||
|
@ -532,7 +534,7 @@ const returnU8Callback = new Deno.UnsafeCallback(
|
||||||
);
|
);
|
||||||
const returnBufferCallback = new Deno.UnsafeCallback({
|
const returnBufferCallback = new Deno.UnsafeCallback({
|
||||||
parameters: [],
|
parameters: [],
|
||||||
result: "pointer",
|
result: "buffer",
|
||||||
}, () => {
|
}, () => {
|
||||||
return buffer;
|
return buffer;
|
||||||
});
|
});
|
||||||
|
@ -617,27 +619,29 @@ const addToFooCallback = new Deno.UnsafeCallback({
|
||||||
}, () => counter++);
|
}, () => counter++);
|
||||||
|
|
||||||
// Test thread safe callbacks
|
// Test thread safe callbacks
|
||||||
console.log("Thread safe call counter:", counter);
|
assertEquals(counter, 0);
|
||||||
addToFooCallback.ref();
|
addToFooCallback.ref();
|
||||||
await dylib.symbols.call_fn_ptr_thread_safe(addToFooCallback.pointer);
|
await dylib.symbols.call_fn_ptr_thread_safe(addToFooCallback.pointer);
|
||||||
addToFooCallback.unref();
|
addToFooCallback.unref();
|
||||||
logCallback.ref();
|
logCallback.ref();
|
||||||
await dylib.symbols.call_fn_ptr_thread_safe(logCallback.pointer);
|
await dylib.symbols.call_fn_ptr_thread_safe(logCallback.pointer);
|
||||||
logCallback.unref();
|
logCallback.unref();
|
||||||
console.log("Thread safe call counter:", counter);
|
assertEquals(counter, 1);
|
||||||
returnU8Callback.ref();
|
returnU8Callback.ref();
|
||||||
await dylib.symbols.call_fn_ptr_return_u8_thread_safe(returnU8Callback.pointer);
|
await dylib.symbols.call_fn_ptr_return_u8_thread_safe(returnU8Callback.pointer);
|
||||||
// Purposefully do not unref returnU8Callback: Instead use it to test close() unrefing.
|
// Purposefully do not unref returnU8Callback: Instead use it to test close() unrefing.
|
||||||
|
|
||||||
// Test statics
|
// Test statics
|
||||||
console.log("Static u32:", dylib.symbols.static_u32);
|
assertEquals(dylib.symbols.static_u32, 42);
|
||||||
console.log("Static i64:", dylib.symbols.static_i64);
|
assertEquals(dylib.symbols.static_i64, -1242464576485);
|
||||||
console.log(
|
assert(
|
||||||
"Static ptr:",
|
typeof dylib.symbols.static_ptr === "object"
|
||||||
typeof dylib.symbols.static_ptr === "number",
|
);
|
||||||
|
assertEquals(
|
||||||
|
Object.keys(dylib.symbols.static_ptr).length, 0
|
||||||
);
|
);
|
||||||
const view = new Deno.UnsafePointerView(dylib.symbols.static_ptr);
|
const view = new Deno.UnsafePointerView(dylib.symbols.static_ptr);
|
||||||
console.log("Static ptr value:", view.getUint32());
|
assertEquals(view.getUint32(), 42);
|
||||||
|
|
||||||
// Test struct returning
|
// Test struct returning
|
||||||
const rect_sync = dylib.symbols.make_rect(10, 20, 100, 200);
|
const rect_sync = dylib.symbols.make_rect(10, 20, 100, 200);
|
||||||
|
@ -656,7 +660,7 @@ assertEquals(rect_async.length, 4 * 8);
|
||||||
assertEquals(Array.from(new Float64Array(rect_async.buffer)), [10, 20, 100, 200]);
|
assertEquals(Array.from(new Float64Array(rect_async.buffer)), [10, 20, 100, 200]);
|
||||||
|
|
||||||
// Test complex, mixed struct returning and passing
|
// Test complex, mixed struct returning and passing
|
||||||
const mixedStruct = dylib.symbols.create_mixed(3, 12.515000343322754, rect_async, 12456789, new Uint32Array([8, 32]));
|
const mixedStruct = dylib.symbols.create_mixed(3, 12.515000343322754, rect_async, Deno.UnsafePointer.create(12456789), new Uint32Array([8, 32]));
|
||||||
assertEquals(mixedStruct.length, 56);
|
assertEquals(mixedStruct.length, 56);
|
||||||
assertEquals(Array.from(mixedStruct.subarray(0, 4)), [3, 0, 0, 0]);
|
assertEquals(Array.from(mixedStruct.subarray(0, 4)), [3, 0, 0, 0]);
|
||||||
assertEquals(new Float32Array(mixedStruct.buffer, 4, 1)[0], 12.515000343322754);
|
assertEquals(new Float32Array(mixedStruct.buffer, 4, 1)[0], 12.515000343322754);
|
||||||
|
@ -681,12 +685,31 @@ cb.close();
|
||||||
|
|
||||||
const arrayBuffer = view.getArrayBuffer(4);
|
const arrayBuffer = view.getArrayBuffer(4);
|
||||||
const uint32Array = new Uint32Array(arrayBuffer);
|
const uint32Array = new Uint32Array(arrayBuffer);
|
||||||
console.log("arrayBuffer.byteLength:", arrayBuffer.byteLength);
|
assertEquals(arrayBuffer.byteLength, 4);
|
||||||
console.log("uint32Array.length:", uint32Array.length);
|
assertEquals(uint32Array.length, 1);
|
||||||
console.log("uint32Array[0]:", uint32Array[0]);
|
assertEquals(uint32Array[0], 42);
|
||||||
uint32Array[0] = 55; // MUTATES!
|
uint32Array[0] = 55; // MUTATES!
|
||||||
console.log("uint32Array[0] after mutation:", uint32Array[0]);
|
assertEquals(uint32Array[0], 55);
|
||||||
console.log("Static ptr value after mutation:", view.getUint32());
|
assertEquals(view.getUint32(), 55);
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// Test UnsafePointer APIs
|
||||||
|
assertEquals(Deno.UnsafePointer.create(0), null);
|
||||||
|
const createdPointer = Deno.UnsafePointer.create(1);
|
||||||
|
assertNotEquals(createdPointer, null);
|
||||||
|
assertEquals(typeof createdPointer, "object");
|
||||||
|
assertEquals(Deno.UnsafePointer.value(null), 0);
|
||||||
|
assertEquals(Deno.UnsafePointer.value(createdPointer), 1);
|
||||||
|
assert(Deno.UnsafePointer.equals(null, null));
|
||||||
|
assertFalse(Deno.UnsafePointer.equals(null, createdPointer));
|
||||||
|
assertFalse(Deno.UnsafePointer.equals(Deno.UnsafePointer.create(2), createdPointer));
|
||||||
|
// Do not allow offsetting from null, `create` function should be used instead.
|
||||||
|
assertThrows(() => Deno.UnsafePointer.offset(null, 5));
|
||||||
|
const offsetPointer = Deno.UnsafePointer.offset(createdPointer, 5);
|
||||||
|
assertEquals(Deno.UnsafePointer.value(offsetPointer), 6);
|
||||||
|
assertEquals(Deno.UnsafePointer.offset(offsetPointer, -6), null);
|
||||||
|
}
|
||||||
|
|
||||||
// Test non-UTF-8 characters
|
// Test non-UTF-8 characters
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue