1
0
Fork 0
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:
DjDeveloper 2022-01-05 12:55:31 +05:30 committed by Bartek Iwańczuk
parent 91cbd1ab52
commit 8806e4165e
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750
3 changed files with 113 additions and 52 deletions

View file

@ -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

View file

@ -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\

View file

@ -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));