mirror of
https://github.com/denoland/deno.git
synced 2024-11-30 16:40:57 -05:00
fix(ext/ffi): throw errors instead of panic (#13283)
This commit is contained in:
parent
91cbd1ab52
commit
8806e4165e
3 changed files with 113 additions and 52 deletions
128
ext/ffi/lib.rs
128
ext/ffi/lib.rs
|
@ -1,7 +1,9 @@
|
||||||
// Copyright 2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use deno_core::error::bad_resource_id;
|
use deno_core::error::bad_resource_id;
|
||||||
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::range_error;
|
use deno_core::error::range_error;
|
||||||
|
use deno_core::error::type_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::include_js_files;
|
use deno_core::include_js_files;
|
||||||
use deno_core::op_async;
|
use deno_core::op_async;
|
||||||
|
@ -79,7 +81,17 @@ impl DynamicLibraryResource {
|
||||||
symbol: String,
|
symbol: String,
|
||||||
foreign_fn: ForeignFunction,
|
foreign_fn: ForeignFunction,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
let fn_ptr = unsafe { self.lib.symbol::<*const c_void>(&symbol) }?;
|
// 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.
|
||||||
|
let fn_ptr = match unsafe { self.lib.symbol::<*const c_void>(&symbol) } {
|
||||||
|
Ok(value) => Ok(value),
|
||||||
|
Err(err) => Err(generic_error(format!(
|
||||||
|
"Failed to register symbol {}: {}",
|
||||||
|
symbol,
|
||||||
|
err.to_string()
|
||||||
|
))),
|
||||||
|
}?;
|
||||||
let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
|
let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
|
||||||
let cif = libffi::middle::Cif::new(
|
let cif = libffi::middle::Cif::new(
|
||||||
foreign_fn
|
foreign_fn
|
||||||
|
@ -194,44 +206,44 @@ union NativeValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeValue {
|
impl NativeValue {
|
||||||
fn new(native_type: NativeType, value: Value) -> Self {
|
fn new(native_type: NativeType, value: Value) -> Result<Self, AnyError> {
|
||||||
match native_type {
|
let value = match native_type {
|
||||||
NativeType::Void => Self { void_value: () },
|
NativeType::Void => Self { void_value: () },
|
||||||
NativeType::U8 => Self {
|
NativeType::U8 => Self {
|
||||||
u8_value: value_as_uint::<u8>(value),
|
u8_value: value_as_uint::<u8>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::I8 => Self {
|
NativeType::I8 => Self {
|
||||||
i8_value: value_as_int::<i8>(value),
|
i8_value: value_as_int::<i8>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::U16 => Self {
|
NativeType::U16 => Self {
|
||||||
u16_value: value_as_uint::<u16>(value),
|
u16_value: value_as_uint::<u16>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::I16 => Self {
|
NativeType::I16 => Self {
|
||||||
i16_value: value_as_int::<i16>(value),
|
i16_value: value_as_int::<i16>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::U32 => Self {
|
NativeType::U32 => Self {
|
||||||
u32_value: value_as_uint::<u32>(value),
|
u32_value: value_as_uint::<u32>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::I32 => Self {
|
NativeType::I32 => Self {
|
||||||
i32_value: value_as_int::<i32>(value),
|
i32_value: value_as_int::<i32>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::U64 => Self {
|
NativeType::U64 => Self {
|
||||||
u64_value: value_as_uint::<u64>(value),
|
u64_value: value_as_uint::<u64>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::I64 => Self {
|
NativeType::I64 => Self {
|
||||||
i64_value: value_as_int::<i64>(value),
|
i64_value: value_as_int::<i64>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::USize => Self {
|
NativeType::USize => Self {
|
||||||
usize_value: value_as_uint::<usize>(value),
|
usize_value: value_as_uint::<usize>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::ISize => Self {
|
NativeType::ISize => Self {
|
||||||
isize_value: value_as_int::<isize>(value),
|
isize_value: value_as_int::<isize>(value)?,
|
||||||
},
|
},
|
||||||
NativeType::F32 => Self {
|
NativeType::F32 => Self {
|
||||||
f32_value: value_as_f32(value),
|
f32_value: value_as_f32(value)?,
|
||||||
},
|
},
|
||||||
NativeType::F64 => Self {
|
NativeType::F64 => Self {
|
||||||
f64_value: value_as_f64(value),
|
f64_value: value_as_f64(value)?,
|
||||||
},
|
},
|
||||||
NativeType::Pointer => {
|
NativeType::Pointer => {
|
||||||
if value.is_null() {
|
if value.is_null() {
|
||||||
|
@ -240,14 +252,13 @@ impl NativeValue {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Self {
|
Self {
|
||||||
pointer: u64::from(
|
pointer: u64::from(serde_json::from_value::<U32x2>(value)?)
|
||||||
serde_json::from_value::<U32x2>(value)
|
as *const u8,
|
||||||
.expect("Expected ffi arg value to be a tuple of the low and high bits of a pointer address")
|
|
||||||
) as *const u8,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buffer(ptr: *const u8) -> Self {
|
fn buffer(ptr: *const u8) -> Self {
|
||||||
|
@ -274,28 +285,38 @@ impl NativeValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_as_uint<T: TryFrom<u64>>(value: Value) -> T {
|
fn value_as_uint<T: TryFrom<u64>>(value: Value) -> Result<T, AnyError> {
|
||||||
value
|
match value.as_u64().and_then(|v| T::try_from(v).ok()) {
|
||||||
.as_u64()
|
Some(value) => Ok(value),
|
||||||
.and_then(|v| T::try_from(v).ok())
|
None => Err(type_error(format!(
|
||||||
.expect("Expected ffi arg value to be an unsigned integer")
|
"Expected FFI argument to be an unsigned integer, but got {:?}",
|
||||||
|
value
|
||||||
|
))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_as_int<T: TryFrom<i64>>(value: Value) -> T {
|
fn value_as_int<T: TryFrom<i64>>(value: Value) -> Result<T, AnyError> {
|
||||||
value
|
match value.as_i64().and_then(|v| T::try_from(v).ok()) {
|
||||||
.as_i64()
|
Some(value) => Ok(value),
|
||||||
.and_then(|v| T::try_from(v).ok())
|
None => Err(type_error(format!(
|
||||||
.expect("Expected ffi arg value to be a signed integer")
|
"Expected FFI argument to be a signed integer, but got {:?}",
|
||||||
|
value
|
||||||
|
))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_as_f32(value: Value) -> f32 {
|
fn value_as_f32(value: Value) -> Result<f32, AnyError> {
|
||||||
value_as_f64(value) as f32
|
Ok(value_as_f64(value)? as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_as_f64(value: Value) -> f64 {
|
fn value_as_f64(value: Value) -> Result<f64, AnyError> {
|
||||||
value
|
match value.as_f64() {
|
||||||
.as_f64()
|
Some(value) => Ok(value),
|
||||||
.expect("Expected ffi arg value to be a float")
|
None => Err(type_error(format!(
|
||||||
|
"Expected FFI argument to be a double, but got {:?}",
|
||||||
|
value
|
||||||
|
))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
@ -450,22 +471,35 @@ fn ffi_call(args: FfiCallArgs, symbol: &Symbol) -> Result<Value, AnyError> {
|
||||||
.map(|buffer| buffer.as_ref().map(|buffer| &buffer[..]))
|
.map(|buffer| buffer.as_ref().map(|buffer| &buffer[..]))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let native_values = symbol
|
let mut native_values: Vec<NativeValue> = vec![];
|
||||||
|
|
||||||
|
for (&native_type, value) in symbol
|
||||||
.parameter_types
|
.parameter_types
|
||||||
.iter()
|
.iter()
|
||||||
.zip(args.parameters.into_iter())
|
.zip(args.parameters.into_iter())
|
||||||
.map(|(&native_type, value)| {
|
{
|
||||||
if let NativeType::Pointer = native_type {
|
match native_type {
|
||||||
if let Some(idx) = value.as_u64() {
|
NativeType::Pointer => match value.as_u64() {
|
||||||
if let Some(&Some(buf)) = buffers.get(idx as usize) {
|
Some(idx) => {
|
||||||
return NativeValue::buffer(buf.as_ptr());
|
let buf = buffers
|
||||||
}
|
.get(idx as usize)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
generic_error(format!("No buffer present at index {}", idx))
|
||||||
|
})?
|
||||||
|
.unwrap();
|
||||||
|
native_values.push(NativeValue::buffer(buf.as_ptr()));
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
let value = NativeValue::new(native_type, value)?;
|
||||||
|
native_values.push(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let value = NativeValue::new(native_type, value)?;
|
||||||
|
native_values.push(value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
NativeValue::new(native_type, value)
|
}
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let call_args = symbol
|
let call_args = symbol
|
||||||
.parameter_types
|
.parameter_types
|
||||||
|
|
|
@ -24,6 +24,7 @@ fn basic() {
|
||||||
.arg("--allow-ffi")
|
.arg("--allow-ffi")
|
||||||
.arg("--allow-read")
|
.arg("--allow-read")
|
||||||
.arg("--unstable")
|
.arg("--unstable")
|
||||||
|
.arg("--quiet")
|
||||||
.arg("tests/test.js")
|
.arg("tests/test.js")
|
||||||
.env("NO_COLOR", "1")
|
.env("NO_COLOR", "1")
|
||||||
.output()
|
.output()
|
||||||
|
@ -37,7 +38,6 @@ fn basic() {
|
||||||
println!("{:?}", output.status);
|
println!("{:?}", output.status);
|
||||||
assert!(output.status.success());
|
assert!(output.status.success());
|
||||||
let expected = "\
|
let expected = "\
|
||||||
dlopen doesn't panic\n\
|
|
||||||
something\n\
|
something\n\
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8]\n\
|
[1, 2, 3, 4, 5, 6, 7, 8]\n\
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8] [9, 10]\n\
|
[1, 2, 3, 4, 5, 6, 7, 8] [9, 10]\n\
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
// deno-lint-ignore-file
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
|
import { assertThrows } from "../../test_util/std/testing/asserts.ts";
|
||||||
|
|
||||||
const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
|
const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
|
||||||
const [libPrefix, libSuffix] = {
|
const [libPrefix, libSuffix] = {
|
||||||
darwin: ["lib", "dylib"],
|
darwin: ["lib", "dylib"],
|
||||||
|
@ -12,11 +14,22 @@ const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`;
|
||||||
const resourcesPre = Deno.resources();
|
const resourcesPre = Deno.resources();
|
||||||
|
|
||||||
// dlopen shouldn't panic
|
// dlopen shouldn't panic
|
||||||
try {
|
assertThrows(() => {
|
||||||
Deno.dlopen("cli/src/main.rs", {});
|
Deno.dlopen("cli/src/main.rs", {});
|
||||||
} catch (_) {
|
});
|
||||||
console.log("dlopen doesn't panic");
|
|
||||||
}
|
assertThrows(
|
||||||
|
() => {
|
||||||
|
Deno.dlopen(libPath, {
|
||||||
|
non_existent_symbol: {
|
||||||
|
parameters: [],
|
||||||
|
result: "void",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
"Failed to register symbol non_existent_symbol",
|
||||||
|
);
|
||||||
|
|
||||||
const dylib = Deno.dlopen(libPath, {
|
const dylib = Deno.dlopen(libPath, {
|
||||||
"print_something": { parameters: [], result: "void" },
|
"print_something": { parameters: [], result: "void" },
|
||||||
|
@ -75,6 +88,20 @@ console.log(Boolean(dylib.symbols.is_null_ptr(ptr)));
|
||||||
console.log(Boolean(dylib.symbols.is_null_ptr(null)));
|
console.log(Boolean(dylib.symbols.is_null_ptr(null)));
|
||||||
console.log(Boolean(dylib.symbols.is_null_ptr(Deno.UnsafePointer.of(into))));
|
console.log(Boolean(dylib.symbols.is_null_ptr(Deno.UnsafePointer.of(into))));
|
||||||
console.log(dylib.symbols.add_u32(123, 456));
|
console.log(dylib.symbols.add_u32(123, 456));
|
||||||
|
assertThrows(
|
||||||
|
() => {
|
||||||
|
dylib.symbols.add_u32(-1, 100);
|
||||||
|
},
|
||||||
|
TypeError,
|
||||||
|
"Expected FFI argument to be an unsigned integer, but got Number(-1)",
|
||||||
|
);
|
||||||
|
assertThrows(
|
||||||
|
() => {
|
||||||
|
dylib.symbols.add_u32(null, 100);
|
||||||
|
},
|
||||||
|
TypeError,
|
||||||
|
"Expected FFI argument to be an unsigned integer, but got Null",
|
||||||
|
);
|
||||||
console.log(dylib.symbols.add_i32(123, 456));
|
console.log(dylib.symbols.add_i32(123, 456));
|
||||||
console.log(dylib.symbols.add_u64(123, 456));
|
console.log(dylib.symbols.add_u64(123, 456));
|
||||||
console.log(dylib.symbols.add_i64(123, 456));
|
console.log(dylib.symbols.add_i64(123, 456));
|
||||||
|
|
Loading…
Reference in a new issue