summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAapo Alasuutari <aapo.alasuutari@gmail.com>2023-02-22 19:32:38 +0200
committerGitHub <noreply@github.com>2023-02-22 19:32:38 +0200
commitb56b8c8a753442036ace3bb0f4403c952f18d408 (patch)
tree675c47da4847a754cf2cc3cb664dc8047944246d
parent2bd7482295daae4edfd367d84505bb43735cd410 (diff)
feat(ext/ffi): Replace pointer integers with v8::External objects (#16889)
-rw-r--r--cli/tests/integration/run_tests.rs24
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_10.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_11.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_12.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_13.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_14.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_15.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_16.js1
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_16.js.out1
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_17.js1
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_17.js.out1
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_18.js1
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_18.js.out1
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_19.js1
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_19.js.out1
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_2.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_3.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_5.js7
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_6.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_7.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_8.js2
-rw-r--r--cli/tests/testdata/run/ffi/unstable_ffi_9.js2
-rw-r--r--cli/tests/unit/ffi_test.ts8
-rw-r--r--cli/tsc/dts/lib.deno.unstable.d.ts43
-rw-r--r--ext/ffi/00_ffi.js34
-rw-r--r--ext/ffi/call.rs140
-rw-r--r--ext/ffi/callback.rs70
-rw-r--r--ext/ffi/dlfcn.rs26
-rw-r--r--ext/ffi/ir.rs68
-rw-r--r--ext/ffi/lib.rs5
-rw-r--r--ext/ffi/repr.rs249
-rw-r--r--ext/ffi/static.rs11
-rw-r--r--ext/ffi/turbocall.rs14
-rw-r--r--ops/fast_call.rs6
-rw-r--r--ops/lib.rs38
-rw-r--r--ops/optimizer.rs92
-rw-r--r--ops/optimizer_tests/op_ffi_ptr_value.expected11
-rw-r--r--ops/optimizer_tests/op_ffi_ptr_value.out127
-rw-r--r--ops/optimizer_tests/op_ffi_ptr_value.rs3
-rw-r--r--serde_v8/error.rs1
-rw-r--r--serde_v8/lib.rs1
-rw-r--r--serde_v8/magic/external_pointer.rs56
-rw-r--r--serde_v8/magic/mod.rs2
-rw-r--r--serde_v8/ser.rs12
-rw-r--r--test_ffi/tests/ffi_types.ts43
-rw-r--r--test_ffi/tests/integration_tests.rs15
-rw-r--r--test_ffi/tests/test.js69
47 files changed, 834 insertions, 373 deletions
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index 6a436c318..d42136f84 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -2824,6 +2824,30 @@ itest!(unstable_ffi_15 {
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 {
args: "run --check run/future_check.ts",
output: "run/future_check2.out",
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_10.js b/cli/tests/testdata/run/ffi/unstable_ffi_10.js
index da1c5b3a2..d291c6bbc 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_10.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_10.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_read_i16(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_read_i16(null, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_11.js b/cli/tests/testdata/run/ffi/unstable_ffi_11.js
index c2d9213b4..fc00fac38 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_11.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_11.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_read_u32(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_read_u32(null, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_12.js b/cli/tests/testdata/run/ffi/unstable_ffi_12.js
index d3aaa7a71..6f085115d 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_12.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_12.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_read_i32(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_read_i32(null, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_13.js b/cli/tests/testdata/run/ffi/unstable_ffi_13.js
index 859fbad60..c3b5105db 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_13.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_13.js
@@ -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));
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_14.js b/cli/tests/testdata/run/ffi/unstable_ffi_14.js
index 19f8a48c8..2d095c5d6 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_14.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_14.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_read_f32(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_read_f32(null, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_15.js b/cli/tests/testdata/run/ffi/unstable_ffi_15.js
index 3a4c0252b..a3cf2b0c5 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_15.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_15.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_read_f64(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_read_f64(null, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_16.js b/cli/tests/testdata/run/ffi/unstable_ffi_16.js
new file mode 100644
index 000000000..2bf3759b3
--- /dev/null
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_16.js
@@ -0,0 +1 @@
+Deno[Deno.internal].core.ops.op_ffi_ptr_value(null, new Uint32Array(2));
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_16.js.out b/cli/tests/testdata/run/ffi/unstable_ffi_16.js.out
new file mode 100644
index 000000000..d68870784
--- /dev/null
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_16.js.out
@@ -0,0 +1 @@
+Unstable API 'Deno.UnsafePointer#value'. The --unstable flag must be provided.
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_17.js b/cli/tests/testdata/run/ffi/unstable_ffi_17.js
new file mode 100644
index 000000000..595727092
--- /dev/null
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_17.js
@@ -0,0 +1 @@
+Deno[Deno.internal].core.ops.op_ffi_get_buf(null, 0, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_17.js.out b/cli/tests/testdata/run/ffi/unstable_ffi_17.js.out
new file mode 100644
index 000000000..294931243
--- /dev/null
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_17.js.out
@@ -0,0 +1 @@
+Unstable API 'Deno.UnsafePointerView#getArrayBuffer'. The --unstable flag must be provided.
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_18.js b/cli/tests/testdata/run/ffi/unstable_ffi_18.js
new file mode 100644
index 000000000..d222a7b7d
--- /dev/null
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_18.js
@@ -0,0 +1 @@
+Deno[Deno.internal].core.ops.op_ffi_ptr_create(null);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_18.js.out b/cli/tests/testdata/run/ffi/unstable_ffi_18.js.out
new file mode 100644
index 000000000..6f7ea0d8f
--- /dev/null
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_18.js.out
@@ -0,0 +1 @@
+Unstable API 'Deno.UnsafePointer#create'. The --unstable flag must be provided.
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_19.js b/cli/tests/testdata/run/ffi/unstable_ffi_19.js
new file mode 100644
index 000000000..97d650022
--- /dev/null
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_19.js
@@ -0,0 +1 @@
+Deno[Deno.internal].core.ops.op_ffi_ptr_equals(null, null);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_19.js.out b/cli/tests/testdata/run/ffi/unstable_ffi_19.js.out
new file mode 100644
index 000000000..15a99b9ab
--- /dev/null
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_19.js.out
@@ -0,0 +1 @@
+Unstable API 'Deno.UnsafePointer#equals'. The --unstable flag must be provided.
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_2.js b/cli/tests/testdata/run/ffi/unstable_ffi_2.js
index 4ed91a9c2..c99b1e586 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_2.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_2.js
@@ -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,
parameters: [],
result: "void",
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_3.js b/cli/tests/testdata/run/ffi/unstable_ffi_3.js
index 284ecbc91..b59a264ea 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_3.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_3.js
@@ -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,
parameters: [],
result: "void",
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_5.js b/cli/tests/testdata/run/ffi/unstable_ffi_5.js
index 278c3c9d2..416c78175 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_5.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_5.js
@@ -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,
+);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_6.js b/cli/tests/testdata/run/ffi/unstable_ffi_6.js
index e6add70d6..7a079f5fb 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_6.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_6.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_cstr_read(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_cstr_read(null, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_7.js b/cli/tests/testdata/run/ffi/unstable_ffi_7.js
index 6ba28b3e3..1f9e5f0c0 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_7.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_7.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_read_u8(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_read_u8(null, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_8.js b/cli/tests/testdata/run/ffi/unstable_ffi_8.js
index 2b0e0343b..cbd0ec9ec 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_8.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_8.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_read_i8(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_read_i8(null, 0);
diff --git a/cli/tests/testdata/run/ffi/unstable_ffi_9.js b/cli/tests/testdata/run/ffi/unstable_ffi_9.js
index 729a8584e..9e8da12db 100644
--- a/cli/tests/testdata/run/ffi/unstable_ffi_9.js
+++ b/cli/tests/testdata/run/ffi/unstable_ffi_9.js
@@ -1 +1 @@
-Deno[Deno.internal].core.ops.op_ffi_read_u16(0n, 0);
+Deno[Deno.internal].core.ops.op_ffi_read_u16(null, 0);
diff --git a/cli/tests/unit/ffi_test.ts b/cli/tests/unit/ffi_test.ts
index 690b37afc..65b257774 100644
--- a/cli/tests/unit/ffi_test.ts
+++ b/cli/tests/unit/ffi_test.ts
@@ -29,7 +29,8 @@ Deno.test({ permissions: { ffi: false } }, function ffiPermissionDenied() {
Deno.dlopen("/usr/lib/libc.so.6", {});
}, Deno.errors.PermissionDenied);
const fnptr = new Deno.UnsafeFnPointer(
- 0n,
+ // @ts-expect-error: Not NonNullable but null check is after premissions check.
+ null,
{
parameters: ["u32", "pointer"],
result: "void",
@@ -41,7 +42,10 @@ Deno.test({ permissions: { ffi: false } }, function ffiPermissionDenied() {
assertThrows(() => {
Deno.UnsafePointer.of(new Uint8Array(0));
}, 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(() => {
ptrView.copyInto(new Uint8Array(0));
}, Deno.errors.PermissionDenied);
diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts
index 0362b416d..a1584701f 100644
--- a/cli/tsc/dts/lib.deno.unstable.d.ts
+++ b/cli/tsc/dts/lib.deno.unstable.d.ts
@@ -131,10 +131,10 @@ declare namespace Deno {
*/
type ToNativeTypeMap =
& Record<NativeNumberType, number>
- & Record<NativeBigIntType, PointerValue>
+ & Record<NativeBigIntType, number | bigint>
& Record<NativeBooleanType, boolean>
- & Record<NativePointerType, PointerValue | null>
- & Record<NativeFunctionType, PointerValue | null>
+ & Record<NativePointerType, PointerValue>
+ & Record<NativeFunctionType, PointerValue>
& Record<NativeBufferType, BufferSource | null>;
/** **UNSTABLE**: New API, yet to be vetted.
@@ -191,7 +191,7 @@ declare namespace Deno {
*/
type FromNativeTypeMap =
& Record<NativeNumberType, number>
- & Record<NativeBigIntType, PointerValue>
+ & Record<NativeBigIntType, number | bigint>
& Record<NativeBooleanType, boolean>
& Record<NativePointerType, PointerValue>
& Record<NativeBufferType, PointerValue>
@@ -340,6 +340,9 @@ declare namespace Deno {
[K in keyof T]: StaticForeignSymbol<T[K]>;
};
+ const brand: unique symbol;
+ type PointerObject = { [brand]: unknown };
+
/** **UNSTABLE**: New API, yet to be vetted.
*
* Pointer type depends on the architecture and actual pointer value.
@@ -350,7 +353,7 @@ declare namespace Deno {
*
* @category FFI
*/
- export type PointerValue = number | bigint;
+ export type PointerValue = null | PointerObject;
/** **UNSTABLE**: New API, yet to be vetted.
*
@@ -360,8 +363,16 @@ declare namespace Deno {
* @category FFI
*/
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. */
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.
@@ -374,9 +385,9 @@ declare namespace Deno {
* @category FFI
*/
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. */
getBool(offset?: number): boolean;
@@ -400,29 +411,31 @@ declare namespace Deno {
getInt32(offset?: number): number;
/** Gets an unsigned 64-bit integer at the specified byte offset from the
* pointer. */
- getBigUint64(offset?: number): PointerValue;
+ getBigUint64(offset?: number): number | bigint;
/** Gets a signed 64-bit integer at the specified byte offset from the
* pointer. */
- getBigInt64(offset?: number): PointerValue;
+ getBigInt64(offset?: number): number | bigint;
/** Gets a signed 32-bit float at the specified byte offset from the
* pointer. */
getFloat32(offset?: number): number;
/** Gets a signed 64-bit float at the specified byte offset from the
* pointer. */
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
* from the pointer. */
getCString(offset?: number): string;
/** Gets a C string (`null` terminated string) at the specified byte offset
* from the specified pointer. */
- static getCString(pointer: PointerValue, offset?: number): string;
+ static getCString(pointer: NonNullable<PointerValue>, offset?: number): string;
/** Gets an `ArrayBuffer` of length `byteLength` at the specified byte
* offset from the pointer. */
getArrayBuffer(byteLength: number, offset?: number): ArrayBuffer;
/** Gets an `ArrayBuffer` of length `byteLength` at the specified byte
* offset from the specified pointer. */
static getArrayBuffer(
- pointer: PointerValue,
+ pointer: NonNullable<PointerValue>,
byteLength: number,
offset?: number,
): ArrayBuffer;
@@ -438,7 +451,7 @@ declare namespace Deno {
*
* Also takes optional byte offset from the pointer. */
static copyInto(
- pointer: PointerValue,
+ pointer: NonNullable<PointerValue>,
destination: BufferSource,
offset?: number,
): void;
@@ -453,11 +466,11 @@ declare namespace Deno {
*/
export class UnsafeFnPointer<Fn extends ForeignFunction> {
/** The pointer to the function. */
- pointer: PointerValue;
+ pointer: NonNullable<PointerValue>;
/** The definition of the function. */
definition: Fn;
- constructor(pointer: PointerValue, definition: Const<Fn>);
+ constructor(pointer: NonNullable<PointerValue>, definition: Const<Fn>);
/** Call the foreign function. */
call: FromForeignFunction<Fn>;
@@ -516,7 +529,7 @@ declare namespace Deno {
);
/** The pointer to the unsafe callback. */
- pointer: PointerValue;
+ pointer: NonNullable<PointerValue>;
/** The definition of the unsafe callback. */
definition: Definition;
/** The callback function. */
diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js
index 6864fd638..24a0dc913 100644
--- a/ext/ffi/00_ffi.js
+++ b/ext/ffi/00_ffi.js
@@ -118,6 +118,13 @@ class UnsafePointerView {
);
}
+ getPointer(offset = 0) {
+ return ops.op_ffi_read_ptr(
+ this.pointer,
+ offset,
+ );
+ }
+
getCString(offset = 0) {
return ops.op_ffi_cstr_read(
this.pointer,
@@ -170,11 +177,33 @@ class UnsafePointerView {
const OUT_BUFFER = new Uint32Array(2);
const OUT_BUFFER_64 = new BigInt64Array(OUT_BUFFER.buffer);
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) {
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
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];
if (NumberIsSafeInteger(result)) {
return result;
@@ -240,8 +269,7 @@ class UnsafeFnPointer {
}
function isReturnedAsBigInt(type) {
- return type === "buffer" || type === "pointer" || type === "function" ||
- type === "u64" || type === "i64" ||
+ return type === "u64" || type === "i64" ||
type === "usize" || type === "isize";
}
diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs
index 731460af9..8a9b393c3 100644
--- a/ext/ffi/call.rs
+++ b/ext/ffi/call.rs
@@ -14,9 +14,11 @@ use deno_core::error::AnyError;
use deno_core::op;
use deno_core::serde_json::Value;
use deno_core::serde_v8;
+use deno_core::serde_v8::ExternalPointer;
use deno_core::v8;
use deno_core::ResourceId;
use libffi::middle::Arg;
+use serde::Serialize;
use std::cell::RefCell;
use std::ffi::c_void;
use std::future::Future;
@@ -28,14 +30,13 @@ unsafe fn ffi_call_rtype_struct(
fn_ptr: &libffi::middle::CodePtr,
call_args: Vec<Arg>,
out_buffer: *mut u8,
-) -> NativeValue {
+) {
libffi::raw::ffi_call(
cif.as_raw_ptr(),
Some(*fn_ptr.as_safe_fun()),
out_buffer as *mut c_void,
call_args.as_ptr() as *mut *mut c_void,
);
- NativeValue { void_value: () }
}
// A one-off synchronous FFI call.
@@ -174,16 +175,25 @@ where
pointer: cif.call::<*mut c_void>(*fun_ptr, &call_args),
}
}
- NativeType::Struct(_) => ffi_call_rtype_struct(
- &symbol.cif,
- &symbol.ptr,
- call_args,
- out_buffer.unwrap().0,
- ),
+ NativeType::Struct(_) => NativeValue {
+ void_value: ffi_call_rtype_struct(
+ &symbol.cif,
+ &symbol.ptr,
+ call_args,
+ out_buffer.unwrap().0,
+ ),
+ },
})
}
}
+#[derive(Serialize)]
+#[serde(untagged)]
+pub enum FfiValue {
+ Value(Value),
+ External(ExternalPointer),
+}
+
fn ffi_call(
call_args: Vec<NativeValue>,
cif: &libffi::middle::Cif,
@@ -191,7 +201,7 @@ fn ffi_call(
parameter_types: &[NativeType],
result_type: NativeType,
out_buffer: Option<OutBuffer>,
-) -> Result<NativeValue, AnyError> {
+) -> Result<FfiValue, AnyError> {
let call_args: Vec<Arg> = call_args
.iter()
.enumerate()
@@ -205,55 +215,57 @@ fn ffi_call(
// types of symbol.
unsafe {
Ok(match result_type {
- NativeType::Void => NativeValue {
- void_value: cif.call::<()>(fun_ptr, &call_args),
- },
- NativeType::Bool => NativeValue {
- bool_value: cif.call::<bool>(fun_ptr, &call_args),
- },
- NativeType::U8 => NativeValue {
- u8_value: cif.call::<u8>(fun_ptr, &call_args),
- },
- NativeType::I8 => NativeValue {
- i8_value: cif.call::<i8>(fun_ptr, &call_args),
- },
- NativeType::U16 => NativeValue {
- u16_value: cif.call::<u16>(fun_ptr, &call_args),
- },
- NativeType::I16 => NativeValue {
- i16_value: cif.call::<i16>(fun_ptr, &call_args),
- },
- NativeType::U32 => NativeValue {
- u32_value: cif.call::<u32>(fun_ptr, &call_args),
- },
- NativeType::I32 => NativeValue {
- i32_value: cif.call::<i32>(fun_ptr, &call_args),
- },
- NativeType::U64 => NativeValue {
- u64_value: cif.call::<u64>(fun_ptr, &call_args),
- },
- NativeType::I64 => NativeValue {
- i64_value: cif.call::<i64>(fun_ptr, &call_args),
- },
- NativeType::USize => NativeValue {
- usize_value: cif.call::<usize>(fun_ptr, &call_args),
- },
- NativeType::ISize => NativeValue {
- isize_value: cif.call::<isize>(fun_ptr, &call_args),
- },
- NativeType::F32 => NativeValue {
- f32_value: cif.call::<f32>(fun_ptr, &call_args),
- },
- NativeType::F64 => NativeValue {
- f64_value: cif.call::<f64>(fun_ptr, &call_args),
- },
+ NativeType::Void => {
+ cif.call::<()>(fun_ptr, &call_args);
+ FfiValue::Value(Value::from(()))
+ }
+ NativeType::Bool => {
+ FfiValue::Value(Value::from(cif.call::<bool>(fun_ptr, &call_args)))
+ }
+ NativeType::U8 => {
+ FfiValue::Value(Value::from(cif.call::<u8>(fun_ptr, &call_args)))
+ }
+ NativeType::I8 => {
+ FfiValue::Value(Value::from(cif.call::<i8>(fun_ptr, &call_args)))
+ }
+ NativeType::U16 => {
+ FfiValue::Value(Value::from(cif.call::<u16>(fun_ptr, &call_args)))
+ }
+ NativeType::I16 => {
+ FfiValue::Value(Value::from(cif.call::<i16>(fun_ptr, &call_args)))
+ }
+ NativeType::U32 => {
+ FfiValue::Value(Value::from(cif.call::<u32>(fun_ptr, &call_args)))
+ }
+ NativeType::I32 => {
+ FfiValue::Value(Value::from(cif.call::<i32>(fun_ptr, &call_args)))
+ }
+ NativeType::U64 => {
+ FfiValue::Value(Value::from(cif.call::<u64>(fun_ptr, &call_args)))
+ }
+ NativeType::I64 => {
+ FfiValue::Value(Value::from(cif.call::<i64>(fun_ptr, &call_args)))
+ }
+ NativeType::USize => {
+ FfiValue::Value(Value::from(cif.call::<usize>(fun_ptr, &call_args)))
+ }
+ NativeType::ISize => {
+ FfiValue::Value(Value::from(cif.call::<isize>(fun_ptr, &call_args)))
+ }
+ NativeType::F32 => {
+ FfiValue::Value(Value::from(cif.call::<f32>(fun_ptr, &call_args)))
+ }
+ NativeType::F64 => {
+ FfiValue::Value(Value::from(cif.call::<f64>(fun_ptr, &call_args)))
+ }
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
- NativeValue {
- pointer: cif.call::<*mut c_void>(fun_ptr, &call_args),
- }
+ FfiValue::External(ExternalPointer::from(
+ cif.call::<*mut c_void>(fun_ptr, &call_args),
+ ))
}
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>(
scope: &mut v8::HandleScope<'scope>,
state: Rc<RefCell<deno_core::OpState>>,
- pointer: usize,
+ pointer: *mut c_void,
def: ForeignFunction,
parameters: 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
FP: FfiPermissions + 'static,
{
@@ -280,7 +292,6 @@ where
let symbol = PtrSymbol::new(pointer, &def)?;
let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
- let def_result = def.result.clone();
let out_buffer = out_buffer
.map(|v| v8::Local::<v8::TypedArray>::try_from(v.v8_value).unwrap());
@@ -303,7 +314,7 @@ where
.await
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
// 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,
parameters: 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 state = state.borrow();
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());
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 Symbol {
cif,
@@ -356,7 +367,7 @@ pub fn op_ffi_call_nonblocking<'scope>(
.await
.map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??;
// 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>(
scope: &mut v8::HandleScope<'scope>,
state: Rc<RefCell<deno_core::OpState>>,
- pointer: usize,
+ pointer: *mut c_void,
def: ForeignFunction,
parameters: serde_v8::Value<'scope>,
out_buffer: Option<serde_v8::Value<'scope>>,
-) -> Result<serde_v8::Value<'scope>, AnyError>
+) -> Result<FfiValue, AnyError>
where
FP: FfiPermissions + 'static,
{
@@ -395,6 +406,5 @@ where
out_buffer_ptr,
)?;
// 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)
}
diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs
index 1558d950e..d608c5432 100644
--- a/ext/ffi/callback.rs
+++ b/ext/ffi/callback.rs
@@ -40,7 +40,10 @@ pub struct 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 cif = libffi::middle::Cif::new(
def
@@ -236,11 +239,11 @@ unsafe fn do_ffi_callback(
}
}
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
- let result = *((*val) as *const usize);
- if result > MAX_SAFE_INTEGER as usize {
- v8::BigInt::new_from_u64(scope, result as u64).into()
+ let result = *((*val) as *const *mut c_void);
+ if result.is_null() {
+ v8::null(scope).into()
} else {
- v8::Number::new(scope, result as f64).into()
+ v8::External::new(scope, result).into()
}
}
NativeType::Struct(_) => {
@@ -353,34 +356,43 @@ unsafe fn do_ffi_callback(
};
*(result as *mut f64) = value;
}
- NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
- let pointer = if let Ok(value) =
+ NativeType::Buffer => {
+ let pointer: *mut u8 = if let Ok(value) =
v8::Local::<v8::ArrayBufferView>::try_from(value)
{
let byte_offset = value.byte_offset();
- let backing_store = value
+ let pointer = value
.buffer(scope)
.expect("Unable to deserialize result parameter.")
- .get_backing_store();
- &backing_store[byte_offset..] as *const _ as *const u8
- } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) {
- value.u64_value().0 as usize as *const u8
+ .data();
+ if let Some(non_null) = pointer {
+ // SAFETY: Pointer is non-null, and V8 guarantees that the byte_offset
+ // 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) {
- let backing_store = value.get_backing_store();
- &backing_store[..] as *const _ as *const u8
- } else if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) {
- value.value() as usize as *const u8
- } else if value.is_null() {
- ptr::null()
+ let pointer = value.data();
+ if let Some(non_null) = pointer {
+ non_null.as_ptr() as *mut u8
+ } else {
+ ptr::null_mut()
+ }
} else {
- // Fallthrough: Probably someone returned a number but this could
- // also be eg. a string. This is essentially UB.
- value
- .integer_value(scope)
- .expect("Unable to deserialize result parameter.") as usize
- as *const u8
+ ptr::null_mut()
};
- *(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 => {
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 {
info.as_ref().unwrap()
});
- let ptr = *closure.code_ptr() as usize;
+ let ptr = *closure.code_ptr() as *mut c_void;
let resource = UnsafeCallbackResource {
cancel: CancelHandle::new_rc(),
closure,
@@ -600,11 +612,7 @@ where
let rid = state.resource_table.add(resource);
let rid_local = v8::Integer::new_from_unsigned(scope, rid);
- let ptr_local: v8::Local<v8::Value> = if ptr > MAX_SAFE_INTEGER as usize {
- v8::BigInt::new_from_u64(scope, ptr as u64).into()
- } else {
- v8::Number::new(scope, ptr as f64).into()
- };
+ let ptr_local: v8::Local<v8::Value> = v8::External::new(scope, ptr).into();
let array = v8::Array::new(scope, 2);
array.set_index(scope, 0, rid_local.into());
array.set_index(scope, 1, ptr_local);
diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs
index c2b123246..cb5009de7 100644
--- a/ext/ffi/dlfcn.rs
+++ b/ext/ffi/dlfcn.rs
@@ -38,13 +38,13 @@ impl Resource for 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
// which symbol wasn't exported. So we'll modify the error
// message to include the name of symbol.
//
// 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)),
Err(err) => Err(generic_error(format!(
"Failed to register symbol {symbol}: {err}"
@@ -56,13 +56,7 @@ impl DynamicLibraryResource {
pub fn needs_unwrap(rv: &NativeType) -> bool {
matches!(
rv,
- NativeType::Function
- | NativeType::Pointer
- | NativeType::Buffer
- | NativeType::I64
- | NativeType::ISize
- | NativeType::U64
- | NativeType::USize
+ NativeType::I64 | NativeType::ISize | NativeType::U64 | NativeType::USize
)
}
@@ -257,26 +251,20 @@ fn make_sync_fn<'s>(
match needs_unwrap {
Some(v) => {
let view: v8::Local<v8::ArrayBufferView> = v.try_into().unwrap();
- let backing_store =
- view.buffer(scope).unwrap().get_backing_store();
+ let pointer =
+ view.buffer(scope).unwrap().data().unwrap().as_ptr() as *mut u8;
if is_i64(&symbol.result_type) {
// SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>,
// it points to a fixed continuous slice of bytes on the heap.
- let bs = unsafe {
- &mut *(&backing_store[..] as *const _ as *mut [u8]
- as *mut i64)
- };
+ let bs = unsafe { &mut *(pointer as *mut i64) };
// SAFETY: We already checked that type == I64
let value = unsafe { result.i64_value };
*bs = value;
} else {
// SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>,
// it points to a fixed continuous slice of bytes on the heap.
- let bs = unsafe {
- &mut *(&backing_store[..] as *const _ as *mut [u8]
- as *mut u64)
- };
+ let bs = unsafe { &mut *(pointer as *mut u64) };
// SAFETY: We checked that type == U64
let value = unsafe { result.u64_value };
*bs = value;
diff --git a/ext/ffi/ir.rs b/ext/ffi/ir.rs
index 80f727cd2..8ca96a280 100644
--- a/ext/ffi/ir.rs
+++ b/ext/ffi/ir.rs
@@ -5,7 +5,6 @@ use crate::MAX_SAFE_INTEGER;
use crate::MIN_SAFE_INTEGER;
use deno_core::error::type_error;
use deno_core::error::AnyError;
-use deno_core::serde_json::Value;
use deno_core::serde_v8;
use deno_core::v8;
use libffi::middle::Arg;
@@ -81,33 +80,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
#[inline]
pub unsafe fn to_v8<'scope>(
&self,
@@ -206,13 +178,11 @@ impl NativeValue {
local_value.into()
}
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
- let value = self.pointer as u64;
- let local_value: v8::Local<v8::Value> =
- if value > MAX_SAFE_INTEGER as u64 {
- v8::BigInt::new_from_u64(scope, value).into()
- } else {
- v8::Number::new(scope, value as f64).into()
- };
+ let local_value: v8::Local<v8::Value> = if self.pointer.is_null() {
+ v8::null(scope).into()
+ } else {
+ v8::External::new(scope, self.pointer).into()
+ };
local_value.into()
}
NativeType::Struct(_) => {
@@ -396,22 +366,16 @@ pub fn ffi_parse_f64_arg(
#[inline]
pub fn ffi_parse_pointer_arg(
- scope: &mut v8::HandleScope,
+ _scope: &mut v8::HandleScope,
arg: v8::Local<v8::Value>,
) -> Result<NativeValue, AnyError> {
- // Order of checking:
- // 1. BigInt: Uncommon and not supported by Fast API, optimise this case.
- // 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
+ let pointer = if let Ok(value) = v8::Local::<v8::External>::try_from(arg) {
+ value.value()
} else if arg.is_null() {
ptr::null_mut()
} else {
return Err(type_error(
- "Invalid FFI pointer type, expected null, integer or BigInt",
+ "Invalid FFI pointer type, expected null, or External",
));
};
Ok(NativeValue { pointer })
@@ -502,22 +466,16 @@ pub fn ffi_parse_struct_arg(
#[inline]
pub fn ffi_parse_function_arg(
- scope: &mut v8::HandleScope,
+ _scope: &mut v8::HandleScope,
arg: v8::Local<v8::Value>,
) -> Result<NativeValue, AnyError> {
- // Order of checking:
- // 1. BigInt: Uncommon and not supported by Fast API, optimise this case.
- // 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
+ let pointer = if let Ok(value) = v8::Local::<v8::External>::try_from(arg) {
+ value.value()
} else if arg.is_null() {
ptr::null_mut()
} else {
return Err(type_error(
- "Invalid FFI function type, expected null, integer, or BigInt",
+ "Invalid FFI function type, expected null, or External",
));
};
Ok(NativeValue { pointer })
diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs
index a14a28a83..d49b66274 100644
--- a/ext/ffi/lib.rs
+++ b/ext/ffi/lib.rs
@@ -91,7 +91,11 @@ pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
op_ffi_call_nonblocking::decl(),
op_ffi_call_ptr::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_offset::decl::<P>(),
+ op_ffi_ptr_value::decl::<P>(),
op_ffi_get_buf::decl::<P>(),
op_ffi_buf_copy_into::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_f32::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_ref::decl(),
op_ffi_unsafe_callback_unref::decl(),
diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs
index c21b2b0f1..4372b0f1b 100644
--- a/ext/ffi/repr.rs
+++ b/ext/ffi/repr.rs
@@ -14,15 +14,89 @@ use std::ffi::CStr;
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)]
pub fn op_ffi_ptr_of<FP>(
state: &mut deno_core::OpState,
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],
) -> Result<(), AnyError>
where
FP: FfiPermissions + 'static,
{
- check_unstable(state, "Deno.UnsafePointer#of");
+ check_unstable(state, "Deno.UnsafePointer#value");
let permissions = state.borrow_mut::<FP>();
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.
let out = unsafe { &mut *outptr };
- *out = buf as usize;
+ *out = ptr as usize;
Ok(())
}
-unsafe extern "C" fn noop_deleter_callback(
- _data: *mut c_void,
- _byte_length: usize,
- _deleter_data: *mut c_void,
-) {
-}
-
#[op(v8)]
pub fn op_ffi_get_buf<FP, 'scope>(
scope: &mut v8::HandleScope<'scope>,
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
len: usize,
) -> Result<serde_v8::Value<'scope>, AnyError>
where
FP: FfiPermissions + 'static,
{
- check_unstable(state, "Deno.UnsafePointerView#arrayBuffer");
+ check_unstable(state, "Deno.UnsafePointerView#getArrayBuffer");
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *mut c_void;
-
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.
- 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.
+ // 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 backing_store = unsafe {
v8::ArrayBuffer::new_backing_store_from_ptr(
- ptr,
+ ptr.offset(offset),
len,
noop_deleter_callback,
std::ptr::null_mut(),
@@ -90,8 +152,8 @@ where
#[op(fast)]
pub fn op_ffi_buf_copy_into<FP>(
state: &mut deno_core::OpState,
- src: usize,
- offset: usize,
+ src: *mut c_void,
+ offset: isize,
dst: &mut [u8],
len: usize,
) -> Result<(), AnyError>
@@ -103,19 +165,20 @@ where
let permissions = state.borrow_mut::<FP>();
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(
"Destination length is smaller than source length",
))
} else {
let src = src as *const c_void;
- // SAFETY: Offset is user defined.
- let src = unsafe { src.add(offset) as *const u8 };
-
- // SAFETY: src is user defined.
+ // SAFETY: src and offset are user defined.
// 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(())
}
}
@@ -124,8 +187,8 @@ where
pub fn op_ffi_cstr_read<FP, 'scope>(
scope: &mut v8::HandleScope<'scope>,
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<serde_v8::Value<'scope>, AnyError>
where
FP: FfiPermissions + 'static,
@@ -135,17 +198,13 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid CString pointer, pointer is null"));
}
- // SAFETY: Offset is user defined.
- let ptr = unsafe { ptr.add(offset) };
-
- // SAFETY: Pointer is user provided.
- let cstr = unsafe { CStr::from_ptr(ptr as *const c_char) }.to_bytes();
+ let cstr =
+ // SAFETY: Pointer and offset are user provided.
+ unsafe { CStr::from_ptr(ptr.offset(offset) as *const c_char) }.to_bytes();
let value: v8::Local<v8::Value> =
v8::String::new_from_utf8(scope, cstr, v8::NewStringType::Normal)
.ok_or_else(|| {
@@ -158,8 +217,8 @@ where
#[op(fast)]
pub fn op_ffi_read_bool<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<bool, AnyError>
where
FP: FfiPermissions + 'static,
@@ -169,21 +228,19 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid bool pointer, pointer is null"));
}
// 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)]
pub fn op_ffi_read_u8<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<u32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -193,21 +250,21 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid u8 pointer, pointer is null"));
}
// 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)]
pub fn op_ffi_read_i8<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<i32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -217,21 +274,21 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid i8 pointer, pointer is null"));
}
// 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)]
pub fn op_ffi_read_u16<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<u32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -241,23 +298,21 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid u16 pointer, pointer is null"));
}
// SAFETY: ptr and offset are user provided.
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)]
pub fn op_ffi_read_i16<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<i32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -267,23 +322,21 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid i16 pointer, pointer is null"));
}
// SAFETY: ptr and offset are user provided.
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)]
pub fn op_ffi_read_u32<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<u32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -293,21 +346,19 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid u32 pointer, pointer is null"));
}
// 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)]
pub fn op_ffi_read_i32<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<i32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -317,21 +368,19 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid i32 pointer, pointer is null"));
}
// 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]
pub fn op_ffi_read_u64<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
out: &mut [u32],
) -> Result<(), AnyError>
where
@@ -349,15 +398,13 @@ where
);
assert_eq!((outptr as usize % std::mem::size_of::<u64>()), 0);
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid u64 pointer, pointer is null"));
}
let value =
// 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.
unsafe { *outptr = value };
@@ -367,8 +414,8 @@ where
#[op(fast)]
pub fn op_ffi_read_i64<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
out: &mut [u32],
) -> Result<(), AnyError>
where
@@ -386,15 +433,13 @@ where
);
assert_eq!((outptr as usize % std::mem::size_of::<i64>()), 0);
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid i64 pointer, pointer is null"));
}
let value =
// 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.
unsafe { *outptr = value };
Ok(())
@@ -403,8 +448,8 @@ where
#[op(fast)]
pub fn op_ffi_read_f32<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<f32, AnyError>
where
FP: FfiPermissions + 'static,
@@ -414,21 +459,19 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid f32 pointer, pointer is null"));
}
// 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)]
pub fn op_ffi_read_f64<FP>(
state: &mut deno_core::OpState,
- ptr: usize,
- offset: usize,
+ ptr: *mut c_void,
+ offset: isize,
) -> Result<f64, AnyError>
where
FP: FfiPermissions + 'static,
@@ -438,12 +481,34 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check(None)?;
- let ptr = ptr as *const c_void;
-
if ptr.is_null() {
return Err(type_error("Invalid f64 pointer, pointer is null"));
}
// 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)
+ })
}
diff --git a/ext/ffi/static.rs b/ext/ffi/static.rs
index 9ea0d616d..2f32f03fd 100644
--- a/ext/ffi/static.rs
+++ b/ext/ffi/static.rs
@@ -10,6 +10,7 @@ use deno_core::op;
use deno_core::serde_v8;
use deno_core::v8;
use deno_core::ResourceId;
+use std::ffi::c_void;
use std::ptr;
#[op(v8)]
@@ -134,13 +135,9 @@ pub fn op_ffi_get_static<'scope>(
number.into()
}
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
- let result = data_ptr as u64;
- let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as u64 {
- v8::BigInt::new_from_u64(scope, result).into()
- } else {
- v8::Number::new(scope, result as f64).into()
- };
- integer.into()
+ let external: v8::Local<v8::Value> =
+ v8::External::new(scope, data_ptr as *mut c_void).into();
+ external.into()
}
NativeType::Struct(_) => {
return Err(type_error("Invalid FFI static type 'struct'"));
diff --git a/ext/ffi/turbocall.rs b/ext/ffi/turbocall.rs
index 1eb1655e1..3d01f00f5 100644
--- a/ext/ffi/turbocall.rs
+++ b/ext/ffi/turbocall.rs
@@ -48,6 +48,9 @@ pub(crate) fn make_template(sym: &Symbol, trampoline: &Trampoline) -> Template {
let ret = if needs_unwrap(&sym.result_type) {
params.push(fast_api::Type::TypedArray(fast_api::CType::Int32));
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 {
fast_api::Type::from(&sym.result_type)
};
@@ -71,9 +74,9 @@ impl Trampoline {
}
pub(crate) struct Template {
- args: Box<[fast_api::Type]>,
- ret: fast_api::CType,
- symbol_ptr: *const c_void,
+ pub args: Box<[fast_api::Type]>,
+ pub ret: fast_api::CType,
+ pub symbol_ptr: *const c_void,
}
impl fast_api::FastFunction for Template {
@@ -106,9 +109,8 @@ impl From<&NativeType> for fast_api::Type {
NativeType::I64 => fast_api::Type::Int64,
NativeType::U64 => fast_api::Type::Uint64,
NativeType::ISize => fast_api::Type::Int64,
- NativeType::USize | NativeType::Pointer | NativeType::Function => {
- fast_api::Type::Uint64
- }
+ NativeType::USize => fast_api::Type::Uint64,
+ NativeType::Pointer | NativeType::Function => fast_api::Type::Pointer,
NativeType::Buffer => fast_api::Type::TypedArray(fast_api::CType::Uint8),
NativeType::Struct(_) => {
fast_api::Type::TypedArray(fast_api::CType::Uint8)
diff --git a/ops/fast_call.rs b/ops/fast_call.rs
index fe6455f37..a7ca51d4f 100644
--- a/ops/fast_call.rs
+++ b/ops/fast_call.rs
@@ -418,13 +418,14 @@ pub(crate) fn generate(
fn q_fast_ty(v: &FastValue) -> Quote {
match v {
FastValue::Void => q!({ () }),
+ FastValue::Bool => q!({ bool }),
FastValue::U32 => q!({ u32 }),
FastValue::I32 => q!({ i32 }),
FastValue::U64 => q!({ u64 }),
FastValue::I64 => q!({ i64 }),
FastValue::F32 => q!({ f32 }),
FastValue::F64 => q!({ f64 }),
- FastValue::Bool => q!({ bool }),
+ FastValue::Pointer => q!({ *mut ::std::ffi::c_void }),
FastValue::V8Value => q!({ v8::Local<v8::Value> }),
FastValue::Uint8Array
| FastValue::Uint32Array
@@ -436,13 +437,14 @@ fn q_fast_ty(v: &FastValue) -> Quote {
fn q_fast_ty_variant(v: &FastValue) -> Quote {
match v {
FastValue::Void => q!({ Void }),
+ FastValue::Bool => q!({ Bool }),
FastValue::U32 => q!({ Uint32 }),
FastValue::I32 => q!({ Int32 }),
FastValue::U64 => q!({ Uint64 }),
FastValue::I64 => q!({ Int64 }),
FastValue::F32 => q!({ Float32 }),
FastValue::F64 => q!({ Float64 }),
- FastValue::Bool => q!({ Bool }),
+ FastValue::Pointer => q!({ Pointer }),
FastValue::V8Value => q!({ V8Value }),
FastValue::Uint8Array => q!({ TypedArray(CType::Uint8) }),
FastValue::Uint32Array => q!({ TypedArray(CType::Uint32) }),
diff --git a/ops/lib.rs b/ops/lib.rs
index d8f28dd37..28cb3c320 100644
--- a/ops/lib.rs
+++ b/ops/lib.rs
@@ -474,6 +474,13 @@ fn codegen_arg(
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
quote! {
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 {
quote! {
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! {
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 {
quote! {
match #core::serde_v8::to_v8(scope, result) {
@@ -723,6 +752,15 @@ fn is_ptr_u8(ty: impl ToTokens) -> bool {
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 {
tokens(&ty).contains("Option < & mut FastApiCallbackOptions")
}
diff --git a/ops/optimizer.rs b/ops/optimizer.rs
index ae3175511..a3ccd51b3 100644
--- a/ops/optimizer.rs
+++ b/ops/optimizer.rs
@@ -45,6 +45,7 @@ enum TransformKind {
SliceU8(bool),
SliceF64(bool),
PtrU8,
+ PtrVoid,
WasmMemory,
}
@@ -90,6 +91,13 @@ impl Transform {
index,
}
}
+
+ fn void_ptr(index: usize) -> Self {
+ Transform {
+ kind: TransformKind::PtrVoid,
+ index,
+ }
+ }
}
#[derive(Debug, PartialEq)]
@@ -195,19 +203,25 @@ impl Transform {
.as_ptr();
})
}
+ TransformKind::PtrVoid => {
+ *ty = parse_quote! { *mut ::std::ffi::c_void };
+
+ q!(Vars {}, {})
+ }
}
}
}
fn get_fast_scalar(s: &str) -> Option<FastValue> {
match s {
+ "bool" => Some(FastValue::Bool),
"u32" => Some(FastValue::U32),
"i32" => Some(FastValue::I32),
"u64" => Some(FastValue::U64),
"i64" => Some(FastValue::I64),
"f32" => Some(FastValue::F32),
"f64" => Some(FastValue::F64),
- "bool" => Some(FastValue::Bool),
+ "* const c_void" | "* mut c_void" => Some(FastValue::Pointer),
"ResourceId" => Some(FastValue::U32),
_ => None,
}
@@ -226,13 +240,14 @@ fn can_return_fast(v: &FastValue) -> bool {
#[derive(Debug, PartialEq, Clone)]
pub(crate) enum FastValue {
Void,
+ Bool,
U32,
I32,
U64,
I64,
F32,
F64,
- Bool,
+ Pointer,
V8Value,
Uint8Array,
Uint32Array,
@@ -414,6 +429,31 @@ impl Optimizer {
{
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),
}
}
@@ -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),
};
@@ -684,6 +747,31 @@ impl Optimizer {
}
_ => 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),
diff --git a/ops/optimizer_tests/op_ffi_ptr_value.expected b/ops/optimizer_tests/op_ffi_ptr_value.expected
new file mode 100644
index 000000000..00a28591c
--- /dev/null
+++ b/ops/optimizer_tests/op_ffi_ptr_value.expected
@@ -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
diff --git a/ops/optimizer_tests/op_ffi_ptr_value.out b/ops/optimizer_tests/op_ffi_ptr_value.out
new file mode 100644
index 000000000..bfbbd5ae7
--- /dev/null
+++ b/ops/optimizer_tests/op_ffi_ptr_value.out
@@ -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
+}
diff --git a/ops/optimizer_tests/op_ffi_ptr_value.rs b/ops/optimizer_tests/op_ffi_ptr_value.rs
new file mode 100644
index 000000000..4c3364507
--- /dev/null
+++ b/ops/optimizer_tests/op_ffi_ptr_value.rs
@@ -0,0 +1,3 @@
+pub fn op_ffi_ptr_value(ptr: *mut c_void, out: &mut [u32]) {
+ // ...
+}
diff --git a/serde_v8/error.rs b/serde_v8/error.rs
index 2cd8eab65..145524abb 100644
--- a/serde_v8/error.rs
+++ b/serde_v8/error.rs
@@ -22,6 +22,7 @@ pub enum Error {
ExpectedObject,
ExpectedBuffer,
ExpectedDetachable,
+ ExpectedExternal,
ExpectedUtf8,
ExpectedLatin1,
diff --git a/serde_v8/lib.rs b/serde_v8/lib.rs
index c15ca715a..b857acbe8 100644
--- a/serde_v8/lib.rs
+++ b/serde_v8/lib.rs
@@ -20,6 +20,7 @@ pub use magic::bytestring::ByteString;
pub use magic::detached_buffer::DetachedBuffer;
pub use magic::string_or_buffer::StringOrBuffer;
pub use magic::u16string::U16String;
+pub use magic::ExternalPointer;
pub use magic::Global;
pub use magic::Value;
pub use ser::to_v8;
diff --git a/serde_v8/magic/external_pointer.rs b/serde_v8/magic/external_pointer.rs
new file mode 100644
index 000000000..fca6028d6
--- /dev/null
+++ b/serde_v8/magic/external_pointer.rs
@@ -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)
+ }
+}
diff --git a/serde_v8/magic/mod.rs b/serde_v8/magic/mod.rs
index fe4577672..f96e422b1 100644
--- a/serde_v8/magic/mod.rs
+++ b/serde_v8/magic/mod.rs
@@ -2,6 +2,7 @@
pub mod buffer;
pub mod bytestring;
pub mod detached_buffer;
+mod external_pointer;
mod global;
pub(super) mod rawbytes;
pub mod string_or_buffer;
@@ -9,5 +10,6 @@ pub mod transl8;
pub mod u16string;
pub mod v8slice;
mod value;
+pub use external_pointer::ExternalPointer;
pub use global::Global;
pub use value::Value;
diff --git a/serde_v8/ser.rs b/serde_v8/ser.rs
index 834efa78a..fa4cfecde 100644
--- a/serde_v8/ser.rs
+++ b/serde_v8/ser.rs
@@ -16,6 +16,7 @@ use crate::magic::transl8::ToV8;
use crate::magic::transl8::MAGIC_FIELD;
use crate::ByteString;
use crate::DetachedBuffer;
+use crate::ExternalPointer;
use crate::StringOrBuffer;
use crate::U16String;
use crate::ZeroCopyBuf;
@@ -269,6 +270,7 @@ impl<'a, 'b, 'c, T: MagicType + ToV8> ser::SerializeStruct
// Dispatches between magic and regular struct serializers
pub enum StructSerializers<'a, 'b, 'c> {
+ ExternalPointer(MagicalSerializer<'a, 'b, 'c, magic::ExternalPointer>),
Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>),
ZeroCopyBuf(MagicalSerializer<'a, 'b, 'c, ZeroCopyBuf>),
MagicDetached(MagicalSerializer<'a, 'b, 'c, DetachedBuffer>),
@@ -288,6 +290,7 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
value: &T,
) -> Result<()> {
match self {
+ StructSerializers::ExternalPointer(s) => s.serialize_field(key, value),
StructSerializers::Magic(s) => s.serialize_field(key, value),
StructSerializers::ZeroCopyBuf(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> {
match self {
+ StructSerializers::ExternalPointer(s) => s.end(),
StructSerializers::Magic(s) => s.end(),
StructSerializers::ZeroCopyBuf(s) => s.end(),
StructSerializers::MagicDetached(s) => s.end(),
@@ -385,8 +389,8 @@ macro_rules! forward_to {
};
}
-const MAX_SAFE_INTEGER: i64 = (1 << 53) - 1;
-const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER;
+pub(crate) const MAX_SAFE_INTEGER: i64 = (1 << 53) - 1;
+pub(crate) const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER;
impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
type Ok = v8::Local<'a, v8::Value>;
@@ -564,6 +568,10 @@ impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
len: usize,
) -> Result<Self::SerializeStruct> {
match name {
+ magic::ExternalPointer::MAGIC_NAME => {
+ let m = MagicalSerializer::<ExternalPointer>::new(self.scope);
+ Ok(StructSerializers::ExternalPointer(m))
+ }
ByteString::MAGIC_NAME => {
let m = MagicalSerializer::<ByteString>::new(self.scope);
Ok(StructSerializers::MagicByteString(m))
diff --git a/test_ffi/tests/ffi_types.ts b/test_ffi/tests/ffi_types.ts
index d7c66203c..c04c13d79 100644
--- a/test_ffi/tests/ffi_types.ts
+++ b/test_ffi/tests/ffi_types.ts
@@ -140,30 +140,31 @@ remote.symbols.method14(0);
remote.symbols.method15("foo");
// @ts-expect-error: Invalid argument
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();
// @ts-expect-error: Invalid argument
let r_0: string = result;
-let r_1: Deno.PointerValue = result;
+let r_1: number | bigint = result;
const result2 = remote.symbols.method17();
// @ts-expect-error: Invalid argument
result2.then((_0: string) => {});
-result2.then((_1: Deno.PointerValue) => {});
+result2.then((_1: number | bigint) => {});
const result3 = remote.symbols.method18();
// @ts-expect-error: Invalid argument
let r3_0: Deno.BufferSource = result3;
-let r3_1: Deno.UnsafePointer = result3;
+let r3_1: null | Deno.UnsafePointer = result3;
const result4 = remote.symbols.method19();
// @ts-expect-error: Invalid argument
result4.then((_0: Deno.BufferSource) => {});
-result4.then((_1: Deno.UnsafePointer) => {});
+result4.then((_1: null | Deno.UnsafePointer) => {});
const fnptr = new Deno.UnsafeFnPointer(
- 0n,
+ {} as NonNullable<Deno.PointerValue>,
{
parameters: ["u32", "pointer"],
result: "void",
@@ -210,7 +211,7 @@ const unsafe_callback_right1 = new Deno.UnsafeCallback(
parameters: ["u8", "u32", "pointer"],
result: "void",
},
- (_1: number, _2: number, _3: Deno.PointerValue) => {},
+ (_1: number, _2: number, _3: null | Deno.PointerValue) => {},
);
const unsafe_callback_right2 = new Deno.UnsafeCallback(
{
@@ -232,14 +233,14 @@ const unsafe_callback_right4 = new Deno.UnsafeCallback(
parameters: ["u8", "u32", "pointer"],
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(
{
parameters: ["u8", "i32", "pointer"],
result: "void",
},
- (_1: number, _2: number, _3: Deno.PointerValue) => {},
+ (_1: number, _2: number, _3: null | Deno.PointerValue) => {},
);
// @ts-expect-error: Must pass callback
@@ -255,9 +256,9 @@ remote.symbols.method23(new Uint32Array(1));
remote.symbols.method23(new Uint8Array(1));
// @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.
-remote.symbols.method23(0n);
+remote.symbols.method23({});
remote.symbols.method23(null);
// @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
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
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
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
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
const static5_wrong: null = 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;
// @ts-expect-error: Invalid member type
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
const static9_wrong: null = 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;
// @ts-expect-error: Invalid member type
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
const static13_wrong: null = remote.symbols.static13;
const static13_right: number = remote.symbols.static13;
@@ -376,8 +377,8 @@ type __Tests__ = [
symbols: {
pushBuf: (
buf: BufferSource | null,
- ptr: Deno.PointerValue | null,
- func: Deno.PointerValue | null,
+ ptr: Deno.PointerValue,
+ func: Deno.PointerValue,
) => Deno.PointerValue;
};
close(): void;
@@ -395,8 +396,8 @@ type __Tests__ = [
{
symbols: {
foo: (
- ...args: (Deno.PointerValue | null)[]
- ) => Deno.PointerValue;
+ ...args: (number | Deno.PointerValue | null)[]
+ ) => number | bigint;
};
close(): void;
},
diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs
index c213f8295..fdfb5d895 100644
--- a/test_ffi/tests/integration_tests.rs
+++ b/test_ffi/tests/integration_tests.rs
@@ -97,12 +97,8 @@ fn basic() {
-9007199254740992n\n\
579.9119873046875\n\
579.912\n\
- After sleep_blocking\n\
- true\n\
Before\n\
- true\n\
After\n\
- true\n\
logCallback\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\
@@ -119,23 +115,12 @@ fn basic() {
78\n\
STORED_FUNCTION cleared\n\
STORED_FUNCTION_2 cleared\n\
- Thread safe call counter: 0\n\
logCallback\n\
- Thread safe call counter: 1\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: 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\
- 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\
Correct number of resources\n";
diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js
index 15900e72c..788faa93e 100644
--- a/test_ffi/tests/test.js
+++ b/test_ffi/tests/test.js
@@ -1,12 +1,15 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// 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 {
assertThrows,
assert,
+ assertNotEquals,
+ assertInstanceOf,
+ assertEquals,
+ assertFalse,
} from "../../test_util/std/testing/asserts.ts";
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
// 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.
-assertEquals(Deno.UnsafePointer.of(externalZeroBuffer), 0, "Deno.UnsafePointer.of(externalZeroBuffer) !== 0");
-assertNotEquals(Deno.UnsafePointer.of(externalOneBuffer), 0, "Deno.UnsafePointer.of(externalOneBuffer) !== 0");
+assertEquals(Deno.UnsafePointer.of(externalZeroBuffer), null, "Deno.UnsafePointer.of(externalZeroBuffer) !== null");
+assertNotEquals(Deno.UnsafePointer.of(externalOneBuffer), null, "Deno.UnsafePointer.of(externalOneBuffer) === null");
const addU32Ptr = dylib.symbols.get_add_u32_ptr();
const addU32 = new Deno.UnsafeFnPointer(addU32Ptr, {
@@ -486,16 +489,15 @@ await promise;
let start = performance.now();
dylib.symbols.sleep_blocking(100);
-console.log("After sleep_blocking");
-console.log(performance.now() - start >= 100);
+assert(performance.now() - start >= 100);
start = performance.now();
const promise_2 = dylib.symbols.sleep_nonblocking(100).then(() => {
console.log("After");
- console.log(performance.now() - start >= 100);
+ assert(performance.now() - start >= 100);
});
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 promise_2;
@@ -532,7 +534,7 @@ const returnU8Callback = new Deno.UnsafeCallback(
);
const returnBufferCallback = new Deno.UnsafeCallback({
parameters: [],
- result: "pointer",
+ result: "buffer",
}, () => {
return buffer;
});
@@ -617,27 +619,29 @@ const addToFooCallback = new Deno.UnsafeCallback({
}, () => counter++);
// Test thread safe callbacks
-console.log("Thread safe call counter:", counter);
+assertEquals(counter, 0);
addToFooCallback.ref();
await dylib.symbols.call_fn_ptr_thread_safe(addToFooCallback.pointer);
addToFooCallback.unref();
logCallback.ref();
await dylib.symbols.call_fn_ptr_thread_safe(logCallback.pointer);
logCallback.unref();
-console.log("Thread safe call counter:", counter);
+assertEquals(counter, 1);
returnU8Callback.ref();
await dylib.symbols.call_fn_ptr_return_u8_thread_safe(returnU8Callback.pointer);
// Purposefully do not unref returnU8Callback: Instead use it to test close() unrefing.
// Test statics
-console.log("Static u32:", dylib.symbols.static_u32);
-console.log("Static i64:", dylib.symbols.static_i64);
-console.log(
- "Static ptr:",
- typeof dylib.symbols.static_ptr === "number",
+assertEquals(dylib.symbols.static_u32, 42);
+assertEquals(dylib.symbols.static_i64, -1242464576485);
+assert(
+ typeof dylib.symbols.static_ptr === "object"
+);
+assertEquals(
+ Object.keys(dylib.symbols.static_ptr).length, 0
);
const view = new Deno.UnsafePointerView(dylib.symbols.static_ptr);
-console.log("Static ptr value:", view.getUint32());
+assertEquals(view.getUint32(), 42);
// Test struct returning
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]);
// 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(Array.from(mixedStruct.subarray(0, 4)), [3, 0, 0, 0]);
assertEquals(new Float32Array(mixedStruct.buffer, 4, 1)[0], 12.515000343322754);
@@ -681,12 +685,31 @@ cb.close();
const arrayBuffer = view.getArrayBuffer(4);
const uint32Array = new Uint32Array(arrayBuffer);
-console.log("arrayBuffer.byteLength:", arrayBuffer.byteLength);
-console.log("uint32Array.length:", uint32Array.length);
-console.log("uint32Array[0]:", uint32Array[0]);
+assertEquals(arrayBuffer.byteLength, 4);
+assertEquals(uint32Array.length, 1);
+assertEquals(uint32Array[0], 42);
uint32Array[0] = 55; // MUTATES!
-console.log("uint32Array[0] after mutation:", uint32Array[0]);
-console.log("Static ptr value after mutation:", view.getUint32());
+assertEquals(uint32Array[0], 55);
+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