diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index 2cfd5cef0c..731460af94 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -278,7 +278,7 @@ where permissions.check(None)?; }; - let symbol = PtrSymbol::new(pointer, &def); + let symbol = PtrSymbol::new(pointer, &def)?; let call_args = ffi_parse_args(scope, parameters, &def.parameters)?; let def_result = def.result.clone(); @@ -379,7 +379,7 @@ where permissions.check(None)?; }; - let symbol = PtrSymbol::new(pointer, &def); + let symbol = PtrSymbol::new(pointer, &def)?; let call_args = ffi_parse_args(scope, parameters, &def.parameters)?; let out_buffer = out_buffer diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index d6ef518232..1558d950ee 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -40,18 +40,19 @@ pub struct PtrSymbol { } impl PtrSymbol { - pub fn new(fn_ptr: usize, def: &ForeignFunction) -> Self { + pub fn new(fn_ptr: usize, def: &ForeignFunction) -> Result { let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); let cif = libffi::middle::Cif::new( def .parameters .clone() .into_iter() - .map(libffi::middle::Type::from), - def.result.clone().into(), + .map(libffi::middle::Type::try_from) + .collect::, _>>()?, + def.result.clone().try_into()?, ); - Self { cif, ptr } + Ok(Self { cif, ptr }) } } @@ -578,8 +579,12 @@ where waker: None, })); let cif = Cif::new( - args.parameters.into_iter().map(libffi::middle::Type::from), - libffi::middle::Type::from(args.result), + args + .parameters + .into_iter() + .map(libffi::middle::Type::try_from) + .collect::, _>>()?, + libffi::middle::Type::try_from(args.result)?, ); // SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists. diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs index eeff2c8a75..a6b870c301 100644 --- a/ext/ffi/dlfcn.rs +++ b/ext/ffi/dlfcn.rs @@ -166,8 +166,9 @@ where .parameters .clone() .into_iter() - .map(libffi::middle::Type::from), - foreign_fn.result.clone().into(), + .map(libffi::middle::Type::try_from) + .collect::, _>>()?, + foreign_fn.result.clone().try_into()?, ); let func_key = v8::String::new(scope, &symbol_key).unwrap(); diff --git a/ext/ffi/symbol.rs b/ext/ffi/symbol.rs index bccef79b1d..bfe150f784 100644 --- a/ext/ffi/symbol.rs +++ b/ext/ffi/symbol.rs @@ -1,5 +1,8 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use deno_core::error::type_error; +use deno_core::error::AnyError; + /// Defines the accepted types that can be used as /// parameters and return values in FFI. #[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq)] @@ -25,9 +28,11 @@ pub enum NativeType { Struct(Box<[NativeType]>), } -impl From for libffi::middle::Type { - fn from(native_type: NativeType) -> Self { - match native_type { +impl TryFrom for libffi::middle::Type { + type Error = AnyError; + + fn try_from(native_type: NativeType) -> Result { + Ok(match native_type { NativeType::Void => libffi::middle::Type::void(), NativeType::U8 | NativeType::Bool => libffi::middle::Type::u8(), NativeType::I8 => libffi::middle::Type::i8(), @@ -44,10 +49,18 @@ impl From for libffi::middle::Type { NativeType::Pointer | NativeType::Buffer | NativeType::Function => { libffi::middle::Type::pointer() } - NativeType::Struct(fields) => libffi::middle::Type::structure( - fields.iter().map(|field| field.clone().into()), - ), - } + NativeType::Struct(fields) => { + libffi::middle::Type::structure(match fields.len() > 0 { + true => fields + .iter() + .map(|field| field.clone().try_into()) + .collect::, _>>()?, + false => { + return Err(type_error("Struct must have at least one field")) + } + }) + } + }) } } diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 0ada7dc0ac..e45c5895b3 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -37,6 +37,40 @@ assertThrows( "Failed to register symbol non_existent_symbol", ); +assertThrows(() => { + Deno.dlopen(libPath, { + print_something: { + parameters: [], + result: { struct: [] } + }, + }), + TypeError, + "Struct must have at least one field" +}); + +assertThrows(() => { + Deno.dlopen(libPath, { + print_something: { + parameters: [ { struct: [] } ], + result: "void", + }, + }), + TypeError, + "Struct must have at least one field" +}); + +const Empty = { struct: [] } +assertThrows(() => { + Deno.dlopen(libPath, { + print_something: { + parameters: [ { struct: [Empty] } ], + result: "void", + }, + }), + TypeError, + "Struct must have at least one field" +}); + const Point = ["f64", "f64"]; const Size = ["f64", "f64"]; const Rect = ["f64", "f64", "f64", "f64"]; @@ -720,4 +754,4 @@ function testOptimized(fn, callback) { console.log(r2); } assertIsOptimized(fn); -} \ No newline at end of file +}