2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2022-12-12 09:14:20 -05:00
|
|
|
|
|
|
|
use crate::dlfcn::DynamicLibraryResource;
|
|
|
|
use crate::symbol::NativeType;
|
2023-09-27 09:54:43 -04:00
|
|
|
use deno_core::op2;
|
2022-12-12 09:14:20 -05:00
|
|
|
use deno_core::v8;
|
2023-09-27 09:54:43 -04:00
|
|
|
use deno_core::OpState;
|
2022-12-12 09:14:20 -05:00
|
|
|
use deno_core::ResourceId;
|
|
|
|
use std::ptr;
|
|
|
|
|
2024-10-14 18:05:49 -04:00
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub enum StaticError {
|
|
|
|
#[error(transparent)]
|
|
|
|
Dlfcn(super::DlfcnError),
|
|
|
|
#[error("Invalid FFI static type 'void'")]
|
|
|
|
InvalidTypeVoid,
|
|
|
|
#[error("Invalid FFI static type 'struct'")]
|
|
|
|
InvalidTypeStruct,
|
|
|
|
#[error(transparent)]
|
|
|
|
Resource(deno_core::error::AnyError),
|
|
|
|
}
|
|
|
|
|
2023-09-27 09:54:43 -04:00
|
|
|
#[op2]
|
2022-12-12 09:14:20 -05:00
|
|
|
pub fn op_ffi_get_static<'scope>(
|
|
|
|
scope: &mut v8::HandleScope<'scope>,
|
2023-09-27 09:54:43 -04:00
|
|
|
state: &mut OpState,
|
|
|
|
#[smi] rid: ResourceId,
|
|
|
|
#[string] name: String,
|
|
|
|
#[serde] static_type: NativeType,
|
2023-04-03 14:32:21 -04:00
|
|
|
optional: bool,
|
2024-10-14 18:05:49 -04:00
|
|
|
) -> Result<v8::Local<'scope, v8::Value>, StaticError> {
|
|
|
|
let resource = state
|
|
|
|
.resource_table
|
|
|
|
.get::<DynamicLibraryResource>(rid)
|
|
|
|
.map_err(StaticError::Resource)?;
|
2022-12-12 09:14:20 -05:00
|
|
|
|
2023-04-03 14:32:21 -04:00
|
|
|
let data_ptr = match resource.get_static(name) {
|
2024-10-14 18:05:49 -04:00
|
|
|
Ok(data_ptr) => data_ptr,
|
2023-04-03 14:32:21 -04:00
|
|
|
Err(err) => {
|
|
|
|
if optional {
|
|
|
|
let null: v8::Local<v8::Value> = v8::null(scope).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
return Ok(null);
|
2023-04-03 14:32:21 -04:00
|
|
|
} else {
|
2024-10-14 18:05:49 -04:00
|
|
|
return Err(StaticError::Dlfcn(err));
|
2023-04-03 14:32:21 -04:00
|
|
|
}
|
|
|
|
}
|
2024-10-14 18:05:49 -04:00
|
|
|
};
|
2022-12-12 09:14:20 -05:00
|
|
|
|
|
|
|
Ok(match static_type {
|
|
|
|
NativeType::Void => {
|
2024-10-14 18:05:49 -04:00
|
|
|
return Err(StaticError::InvalidTypeVoid);
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::Bool => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const bool) };
|
|
|
|
let boolean: v8::Local<v8::Value> =
|
|
|
|
v8::Boolean::new(scope, result).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
boolean
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::U8 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const u8) };
|
|
|
|
let number: v8::Local<v8::Value> =
|
|
|
|
v8::Integer::new_from_unsigned(scope, result as u32).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
number
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::I8 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const i8) };
|
|
|
|
let number: v8::Local<v8::Value> =
|
|
|
|
v8::Integer::new(scope, result as i32).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
number
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::U16 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const u16) };
|
|
|
|
let number: v8::Local<v8::Value> =
|
|
|
|
v8::Integer::new_from_unsigned(scope, result as u32).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
number
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::I16 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const i16) };
|
|
|
|
let number: v8::Local<v8::Value> =
|
|
|
|
v8::Integer::new(scope, result as i32).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
number
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::U32 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const u32) };
|
|
|
|
let number: v8::Local<v8::Value> =
|
|
|
|
v8::Integer::new_from_unsigned(scope, result).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
number
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::I32 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const i32) };
|
|
|
|
let number: v8::Local<v8::Value> = v8::Integer::new(scope, result).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
number
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::U64 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const u64) };
|
BREAKING(ffi/unstable): always return u64 as bigint (#23981)
The mixed `number | bigint` representation was useful optimization for
pointers. Now, pointers are represented as V8 externals. As part of the
FFI stabilization effort we want to make `bigint` the only
representation for `u64` and `i64`.
BigInt representation performance is almost on par with mixed
representation with the added benefit that its less confusing and users
don't need manual checks and conversions for doing operations on the
value.
```
cpu: AMD Ryzen 5 7530U with Radeon Graphics
runtime: deno 1.43.6+92a8d09 (x86_64-unknown-linux-gnu)
file:///home/divy/gh/ffi/main.ts
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------- -----------------------------
nop 4.01 ns/iter 249,533,690.5 (3.97 ns … 10.8 ns) 3.97 ns 4.36 ns 9.03 ns
ret bigint 7.74 ns/iter 129,127,186.8 (7.72 ns … 10.46 ns) 7.72 ns 8.11 ns 8.82 ns
ret i32 7.81 ns/iter 128,087,100.5 (7.77 ns … 12.72 ns) 7.78 ns 8.57 ns 9.75 ns
ret bigint (add op) 15.02 ns/iter 66,588,253.2 (14.64 ns … 24.99 ns) 14.76 ns 19.13 ns 19.44 ns
ret i32 (add op) 12.02 ns/iter 83,209,131.8 (11.95 ns … 18.18 ns) 11.98 ns 13.11 ns 14.5 ns
```
2024-05-28 00:01:09 -04:00
|
|
|
let integer: v8::Local<v8::Value> =
|
|
|
|
v8::BigInt::new_from_u64(scope, result).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
integer
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::I64 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const i64) };
|
BREAKING(ffi/unstable): always return u64 as bigint (#23981)
The mixed `number | bigint` representation was useful optimization for
pointers. Now, pointers are represented as V8 externals. As part of the
FFI stabilization effort we want to make `bigint` the only
representation for `u64` and `i64`.
BigInt representation performance is almost on par with mixed
representation with the added benefit that its less confusing and users
don't need manual checks and conversions for doing operations on the
value.
```
cpu: AMD Ryzen 5 7530U with Radeon Graphics
runtime: deno 1.43.6+92a8d09 (x86_64-unknown-linux-gnu)
file:///home/divy/gh/ffi/main.ts
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------- -----------------------------
nop 4.01 ns/iter 249,533,690.5 (3.97 ns … 10.8 ns) 3.97 ns 4.36 ns 9.03 ns
ret bigint 7.74 ns/iter 129,127,186.8 (7.72 ns … 10.46 ns) 7.72 ns 8.11 ns 8.82 ns
ret i32 7.81 ns/iter 128,087,100.5 (7.77 ns … 12.72 ns) 7.78 ns 8.57 ns 9.75 ns
ret bigint (add op) 15.02 ns/iter 66,588,253.2 (14.64 ns … 24.99 ns) 14.76 ns 19.13 ns 19.44 ns
ret i32 (add op) 12.02 ns/iter 83,209,131.8 (11.95 ns … 18.18 ns) 11.98 ns 13.11 ns 14.5 ns
```
2024-05-28 00:01:09 -04:00
|
|
|
let integer: v8::Local<v8::Value> =
|
|
|
|
v8::BigInt::new_from_i64(scope, result).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
integer
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::USize => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const usize) };
|
BREAKING(ffi/unstable): always return u64 as bigint (#23981)
The mixed `number | bigint` representation was useful optimization for
pointers. Now, pointers are represented as V8 externals. As part of the
FFI stabilization effort we want to make `bigint` the only
representation for `u64` and `i64`.
BigInt representation performance is almost on par with mixed
representation with the added benefit that its less confusing and users
don't need manual checks and conversions for doing operations on the
value.
```
cpu: AMD Ryzen 5 7530U with Radeon Graphics
runtime: deno 1.43.6+92a8d09 (x86_64-unknown-linux-gnu)
file:///home/divy/gh/ffi/main.ts
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------- -----------------------------
nop 4.01 ns/iter 249,533,690.5 (3.97 ns … 10.8 ns) 3.97 ns 4.36 ns 9.03 ns
ret bigint 7.74 ns/iter 129,127,186.8 (7.72 ns … 10.46 ns) 7.72 ns 8.11 ns 8.82 ns
ret i32 7.81 ns/iter 128,087,100.5 (7.77 ns … 12.72 ns) 7.78 ns 8.57 ns 9.75 ns
ret bigint (add op) 15.02 ns/iter 66,588,253.2 (14.64 ns … 24.99 ns) 14.76 ns 19.13 ns 19.44 ns
ret i32 (add op) 12.02 ns/iter 83,209,131.8 (11.95 ns … 18.18 ns) 11.98 ns 13.11 ns 14.5 ns
```
2024-05-28 00:01:09 -04:00
|
|
|
let integer: v8::Local<v8::Value> =
|
|
|
|
v8::BigInt::new_from_u64(scope, result as u64).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
integer
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::ISize => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const isize) };
|
|
|
|
let integer: v8::Local<v8::Value> =
|
BREAKING(ffi/unstable): always return u64 as bigint (#23981)
The mixed `number | bigint` representation was useful optimization for
pointers. Now, pointers are represented as V8 externals. As part of the
FFI stabilization effort we want to make `bigint` the only
representation for `u64` and `i64`.
BigInt representation performance is almost on par with mixed
representation with the added benefit that its less confusing and users
don't need manual checks and conversions for doing operations on the
value.
```
cpu: AMD Ryzen 5 7530U with Radeon Graphics
runtime: deno 1.43.6+92a8d09 (x86_64-unknown-linux-gnu)
file:///home/divy/gh/ffi/main.ts
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------- -----------------------------
nop 4.01 ns/iter 249,533,690.5 (3.97 ns … 10.8 ns) 3.97 ns 4.36 ns 9.03 ns
ret bigint 7.74 ns/iter 129,127,186.8 (7.72 ns … 10.46 ns) 7.72 ns 8.11 ns 8.82 ns
ret i32 7.81 ns/iter 128,087,100.5 (7.77 ns … 12.72 ns) 7.78 ns 8.57 ns 9.75 ns
ret bigint (add op) 15.02 ns/iter 66,588,253.2 (14.64 ns … 24.99 ns) 14.76 ns 19.13 ns 19.44 ns
ret i32 (add op) 12.02 ns/iter 83,209,131.8 (11.95 ns … 18.18 ns) 11.98 ns 13.11 ns 14.5 ns
```
2024-05-28 00:01:09 -04:00
|
|
|
v8::BigInt::new_from_i64(scope, result as i64).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
integer
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::F32 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const f32) };
|
|
|
|
let number: v8::Local<v8::Value> =
|
|
|
|
v8::Number::new(scope, result as f64).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
number
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::F64 => {
|
|
|
|
// SAFETY: ptr is user provided
|
|
|
|
let result = unsafe { ptr::read_unaligned(data_ptr as *const f64) };
|
|
|
|
let number: v8::Local<v8::Value> = v8::Number::new(scope, result).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
number
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
|
|
|
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
|
2023-02-22 12:32:38 -05:00
|
|
|
let external: v8::Local<v8::Value> =
|
2023-08-27 00:04:12 -04:00
|
|
|
v8::External::new(scope, data_ptr).into();
|
2023-09-27 09:54:43 -04:00
|
|
|
external
|
2022-12-12 09:14:20 -05:00
|
|
|
}
|
2023-01-07 22:58:10 -05:00
|
|
|
NativeType::Struct(_) => {
|
2024-10-14 18:05:49 -04:00
|
|
|
return Err(StaticError::InvalidTypeStruct);
|
2023-01-07 22:58:10 -05:00
|
|
|
}
|
2022-12-12 09:14:20 -05:00
|
|
|
})
|
|
|
|
}
|