summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-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
9 files changed, 339 insertions, 278 deletions
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)