1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-01 16:51:13 -05:00

feat(ext/ffi): Safe number pointers (#15173)

This commit is contained in:
Aapo Alasuutari 2022-07-24 13:41:11 +03:00 committed by crowlkats
parent f6f0215d87
commit 3893ce848c
No known key found for this signature in database
GPG key ID: A82C9D461FC483E8
8 changed files with 713 additions and 443 deletions

View file

@ -366,9 +366,9 @@ declare namespace Deno {
type ToNativeTypeMap =
& Record<NativeNumberType, number>
& Record<NativeBigIntType, bigint | number>
& Record<NativePointerType, TypedArray | bigint | null>
& Record<NativeFunctionType, bigint | null>;
& Record<NativeBigIntType, PointerValue>
& Record<NativePointerType, TypedArray | PointerValue | null>
& Record<NativeFunctionType, PointerValue | null>;
/** Type conversion for foreign symbol parameters and unsafe callback return types */
type ToNativeType<T extends NativeType = NativeType> = ToNativeTypeMap[T];
@ -391,9 +391,9 @@ declare namespace Deno {
type FromNativeTypeMap =
& Record<NativeNumberType, number>
& Record<NativeBigIntType, bigint>
& Record<NativePointerType, bigint>
& Record<NativeFunctionType, bigint>;
& Record<NativeBigIntType, PointerValue>
& Record<NativePointerType, PointerValue>
& Record<NativeFunctionType, PointerValue>;
/** Type conversion for foreign symbol return types and unsafe callback parameters */
type FromNativeType<T extends NativeType = NativeType> = FromNativeTypeMap[T];
@ -481,6 +481,15 @@ declare namespace Deno {
| BigInt64Array
| BigUint64Array;
/**
* Pointer type depends on the architecture and actual pointer value.
*
* On a 32 bit system all pointer values are plain numbers. On a 64 bit
* system pointer values are represented as numbers if the value is below
* `Number.MAX_SAFE_INTEGER`.
*/
export type PointerValue = number | bigint;
/** **UNSTABLE**: Unsafe and new API, beware!
*
* An unsafe pointer to a memory location for passing and returning pointers to and from the ffi
@ -489,7 +498,7 @@ declare namespace Deno {
/**
* Return the direct memory pointer to the typed array in memory
*/
static of(value: Deno.UnsafeCallback | TypedArray): bigint;
static of(value: Deno.UnsafeCallback | TypedArray): PointerValue;
}
/** **UNSTABLE**: Unsafe and new API, beware!
@ -517,9 +526,9 @@ declare namespace Deno {
/** Gets a signed 32-bit integer at the specified byte offset from the pointer. */
getInt32(offset?: number): number;
/** Gets an unsigned 64-bit integer at the specified byte offset from the pointer. */
getBigUint64(offset?: number): bigint;
getBigUint64(offset?: number): PointerValue;
/** Gets a signed 64-bit integer at the specified byte offset from the pointer. */
getBigInt64(offset?: number): bigint;
getBigInt64(offset?: number): PointerValue;
/** 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. */

View file

@ -12,11 +12,19 @@
TypeError,
} = window.__bootstrap.primordials;
function unpackU64([hi, lo]) {
function unpackU64(returnValue) {
if (typeof returnValue === "number") {
return returnValue;
}
const [hi, lo] = returnValue;
return BigInt(hi) << 32n | BigInt(lo);
}
function unpackI64([hi, lo]) {
function unpackI64(returnValue) {
if (typeof returnValue === "number") {
return returnValue;
}
const [hi, lo] = returnValue;
const u64 = unpackU64([hi, lo]);
return u64 >> 63n ? u64 - 0x10000000000000000n : u64;
}
@ -31,77 +39,77 @@
getUint8(offset = 0) {
return core.opSync(
"op_ffi_read_u8",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getInt8(offset = 0) {
return core.opSync(
"op_ffi_read_i8",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getUint16(offset = 0) {
return core.opSync(
"op_ffi_read_u16",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getInt16(offset = 0) {
return core.opSync(
"op_ffi_read_i16",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getUint32(offset = 0) {
return core.opSync(
"op_ffi_read_u32",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getInt32(offset = 0) {
return core.opSync(
"op_ffi_read_i32",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getBigUint64(offset = 0) {
return core.opSync(
"op_ffi_read_u64",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getBigInt64(offset = 0) {
return core.opSync(
"op_ffi_read_u64",
offset ? this.pointer + BigInt(offset) : this.pointer,
"op_ffi_read_i64",
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getFloat32(offset = 0) {
return core.opSync(
"op_ffi_read_f32",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getFloat64(offset = 0) {
return core.opSync(
"op_ffi_read_f64",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
getCString(offset = 0) {
return core.opSync(
"op_ffi_cstr_read",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
);
}
@ -116,7 +124,7 @@
copyInto(destination, offset = 0) {
core.opSync(
"op_ffi_buf_copy_into",
offset ? this.pointer + BigInt(offset) : this.pointer,
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
destination,
destination.byteLength,
);

View file

@ -5,6 +5,9 @@ use crate::{tcc::Compiler, Symbol};
use std::ffi::c_void;
use std::ffi::CString;
use std::fmt::Write as _;
use std::mem::size_of;
const _: () = assert!(size_of::<fn()>() == size_of::<usize>());
pub(crate) struct Allocation {
pub addr: *mut c_void,
@ -22,12 +25,14 @@ fn native_arg_to_c(ty: &NativeType) -> &'static str {
match ty {
NativeType::U8 | NativeType::U16 | NativeType::U32 => "uint32_t",
NativeType::I8 | NativeType::I16 | NativeType::I32 => "int32_t",
NativeType::U64 | NativeType::USize => "uint64_t",
NativeType::I64 | NativeType::ISize => "int64_t",
NativeType::Void => "void",
NativeType::F32 => "float",
NativeType::F64 => "double",
_ => unimplemented!(),
NativeType::U64 => "uint64_t",
NativeType::I64 => "int64_t",
NativeType::ISize => "intptr_t",
NativeType::USize => "uintptr_t",
NativeType::Pointer | NativeType::Function => "void*",
}
}
@ -42,9 +47,11 @@ fn native_to_c(ty: &NativeType) -> &'static str {
NativeType::Void => "void",
NativeType::F32 => "float",
NativeType::F64 => "double",
NativeType::U64 | NativeType::USize => "uint64_t",
NativeType::I64 | NativeType::ISize => "int64_t",
_ => unimplemented!(),
NativeType::U64 => "uint64_t",
NativeType::I64 => "int64_t",
NativeType::ISize => "intptr_t",
NativeType::USize => "uintptr_t",
NativeType::Pointer | NativeType::Function => "void*",
}
}
@ -180,16 +187,16 @@ mod tests {
assert_eq!(
codegen(vec![NativeType::ISize, NativeType::U64], NativeType::Void),
"#include <stdint.h>\n\n\
extern void func(int64_t p0, uint64_t p1);\n\n\
void func_trampoline(void* recv, int64_t p0, uint64_t p1) {\
extern void func(intptr_t p0, uint64_t p1);\n\n\
void func_trampoline(void* recv, intptr_t p0, uint64_t p1) {\
\n return func(p0, p1);\n\
}\n\n"
);
assert_eq!(
codegen(vec![NativeType::USize, NativeType::USize], NativeType::U32),
"#include <stdint.h>\n\n\
extern uint32_t func(uint64_t p0, uint64_t p1);\n\n\
uint32_t func_trampoline(void* recv, uint64_t p0, uint64_t p1) {\
extern uint32_t func(uintptr_t p0, uintptr_t p1);\n\n\
uint32_t func_trampoline(void* recv, uintptr_t p0, uintptr_t p1) {\
\n return func(p0, p1);\n\
}\n\n"
);

File diff suppressed because it is too large Load diff

View file

@ -305,6 +305,11 @@ Deno.bench("nop_buffer()", () => {
nop_buffer(buffer);
});
const buffer_ptr = Deno.UnsafePointer.of(buffer);
Deno.bench("nop_buffer() number", () => {
nop_buffer(buffer_ptr);
});
const { return_u8 } = dylib.symbols;
Deno.bench("return_u8()", () => {
return_u8();
@ -442,6 +447,10 @@ Deno.bench("nop_buffer_nonblocking()", async () => {
await nop_buffer_nonblocking(buffer);
});
Deno.bench("nop_buffer_nonblocking() number", async () => {
await nop_buffer_nonblocking(buffer_ptr);
});
const { return_u8_nonblocking } = dylib.symbols;
Deno.bench("return_u8_nonblocking()", async () => {
await return_u8_nonblocking();
@ -540,6 +549,38 @@ Deno.bench("nop_many_parameters()", () => {
);
});
const buffer2_ptr = Deno.UnsafePointer.of(buffer2);
Deno.bench("nop_many_parameters() number", () => {
nop_many_parameters(
135,
47,
356,
-236,
7457,
-1356,
16471468,
-1334748136,
132658769535,
-42745856824,
13567.26437,
7.686234e-3,
buffer_ptr,
64,
-42,
83,
-136,
3657,
-2376,
3277918,
-474628146,
344657895,
-2436732,
135.26437e3,
264.3576468623546834,
buffer2_ptr,
);
});
const { nop_many_parameters_nonblocking } = dylib.symbols;
Deno.bench("nop_many_parameters_nonblocking()", () => {
nop_many_parameters_nonblocking(

View file

@ -131,7 +131,7 @@ remote.symbols.method14(null);
remote.symbols.method14(0);
// @ts-expect-error: Invalid argument
remote.symbols.method15(0);
remote.symbols.method15("foo");
remote.symbols.method15(new Uint16Array(1));
remote.symbols.method15(0n);
@ -245,16 +245,16 @@ remote.symbols.method20(unsafe_callback_right1.pointer);
// @ts-expect-error: Invalid member type
const static1_wrong: null = remote.symbols.static1;
const static1_right: bigint = remote.symbols.static1;
const static1_right: Deno.PointerValue = remote.symbols.static1;
// @ts-expect-error: Invalid member type
const static2_wrong: null = remote.symbols.static2;
const static2_right: Deno.UnsafePointer = remote.symbols.static2;
// @ts-expect-error: Invalid member type
const static3_wrong: null = remote.symbols.static3;
const static3_right: bigint = remote.symbols.static3;
const static3_right: Deno.PointerValue = remote.symbols.static3;
// @ts-expect-error: Invalid member type
const static4_wrong: null = remote.symbols.static4;
const static4_right: bigint = remote.symbols.static4;
const static4_right: Deno.PointerValue = remote.symbols.static4;
// @ts-expect-error: Invalid member type
const static5_wrong: null = remote.symbols.static5;
const static5_right: number = remote.symbols.static5;
@ -266,7 +266,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: bigint = remote.symbols.static8;
const static8_right: Deno.PointerValue = remote.symbols.static8;
// @ts-expect-error: Invalid member type
const static9_wrong: null = remote.symbols.static9;
const static9_right: number = remote.symbols.static9;
@ -278,7 +278,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: bigint = remote.symbols.static12;
const static12_right: Deno.PointerValue = remote.symbols.static12;
// @ts-expect-error: Invalid member type
const static13_wrong: null = remote.symbols.static13;
const static13_right: number = remote.symbols.static13;
@ -331,7 +331,10 @@ type __Tests__ = [
higher_order_params: AssertEqual<
{
symbols: {
pushBuf: (ptr: bigint | TypedArray | null, func: bigint | null) => void;
pushBuf: (
ptr: number | bigint | TypedArray | null,
func: number | bigint | null,
) => void;
};
close(): void;
},
@ -343,9 +346,9 @@ type __Tests__ = [
{
symbols: {
pushBuf: (
ptr: bigint | TypedArray | null,
func: bigint | null,
) => bigint;
ptr: number | bigint | TypedArray | null,
func: number | bigint | null,
) => number | bigint;
};
close(): void;
},
@ -356,7 +359,9 @@ type __Tests__ = [
non_exact_params: AssertEqual<
{
symbols: {
foo: (...args: (number | bigint | TypedArray | null)[]) => bigint;
foo: (
...args: (number | bigint | TypedArray | null)[]
) => number | bigint;
};
close(): void;
},

View file

@ -66,17 +66,29 @@ fn basic() {
5\n\
5\n\
579\n\
8589934590n\n\
-8589934590n\n\
8589934590n\n\
-8589934590n\n\
8589934590\n\
-8589934590\n\
8589934590\n\
-8589934590\n\
9007199254740992n\n\
9007199254740992n\n\
-9007199254740992n\n\
9007199254740992n\n\
9007199254740992n\n\
-9007199254740992n\n\
579.9119873046875\n\
579.912\n\
579\n\
8589934590n\n\
-8589934590n\n\
8589934590n\n\
-8589934590n\n\
8589934590\n\
-8589934590\n\
8589934590\n\
-8589934590\n\
9007199254740992n\n\
9007199254740992n\n\
-9007199254740992n\n\
9007199254740992n\n\
9007199254740992n\n\
-9007199254740992n\n\
579.9119873046875\n\
579.912\n\
After sleep_blocking\n\
@ -86,7 +98,7 @@ fn basic() {
After\n\
true\n\
logCallback\n\
1 -1 2 -2 3 -3 4n -4n 0.5 -0.5 1 2 3 4 5 6 7 8\n\
1 -1 2 -2 3 -3 4 -4 0.5 -0.5 1 2 3 4 5 6 7 8\n\
u8: 8\n\
buf: [1, 2, 3, 4, 5, 6, 7, 8]\n\
logCallback\n\
@ -98,7 +110,7 @@ fn basic() {
Thread safe call counter: 1\n\
u8: 8\n\
Static u32: 42\n\
Static i64: -1242464576485n\n\
Static i64: -1242464576485\n\
Static ptr: true\n\
Static ptr value: 42\n\
arrayBuffer.byteLength: 4\n\
@ -116,7 +128,7 @@ fn symbol_types() {
build();
let output = deno_cmd()
.arg("cache")
.arg("check")
.arg("--unstable")
.arg("--quiet")
.arg("tests/ffi_types.ts")

View file

@ -265,6 +265,12 @@ console.log(dylib.symbols.add_u64(0xffffffffn, 0xffffffffn));
console.log(dylib.symbols.add_i64(-0xffffffffn, -0xffffffffn));
console.log(dylib.symbols.add_usize(0xffffffffn, 0xffffffffn));
console.log(dylib.symbols.add_isize(-0xffffffffn, -0xffffffffn));
console.log(dylib.symbols.add_u64(Number.MAX_SAFE_INTEGER, 1));
console.log(dylib.symbols.add_i64(Number.MAX_SAFE_INTEGER, 1));
console.log(dylib.symbols.add_i64(Number.MIN_SAFE_INTEGER, -1));
console.log(dylib.symbols.add_usize(Number.MAX_SAFE_INTEGER, 1));
console.log(dylib.symbols.add_isize(Number.MAX_SAFE_INTEGER, 1));
console.log(dylib.symbols.add_isize(Number.MIN_SAFE_INTEGER, -1));
console.log(dylib.symbols.add_f32(123.123, 456.789));
console.log(dylib.symbols.add_f64(123.123, 456.789));
@ -280,6 +286,12 @@ console.log(
console.log(
await dylib.symbols.add_isize_nonblocking(-0xffffffffn, -0xffffffffn),
);
console.log(await dylib.symbols.add_u64_nonblocking(Number.MAX_SAFE_INTEGER, 1));
console.log(await dylib.symbols.add_i64_nonblocking(Number.MAX_SAFE_INTEGER, 1));
console.log(await dylib.symbols.add_i64_nonblocking(Number.MIN_SAFE_INTEGER, -1));
console.log(await dylib.symbols.add_usize_nonblocking(Number.MAX_SAFE_INTEGER, 1));
console.log(await dylib.symbols.add_isize_nonblocking(Number.MAX_SAFE_INTEGER, 1));
console.log(await dylib.symbols.add_isize_nonblocking(Number.MIN_SAFE_INTEGER, -1));
console.log(await dylib.symbols.add_f32_nonblocking(123.123, 456.789));
console.log(await dylib.symbols.add_f64_nonblocking(123.123, 456.789));
@ -439,7 +451,7 @@ 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 === "bigint",
typeof dylib.symbols.static_ptr === "number",
);
const view = new Deno.UnsafePointerView(dylib.symbols.static_ptr);
console.log("Static ptr value:", view.getUint32());