FFI: Nested structs

This commit is contained in:
Adrián Arroyo Calle
2023-02-26 20:48:00 +01:00
parent 92b262d599
commit f94294dbd9
2 changed files with 184 additions and 139 deletions

View File

@@ -30,15 +30,16 @@ pub struct ForeignFunctionTable {
structs: HashMap<String, StructImpl>, structs: HashMap<String, StructImpl>,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
struct StructImpl { struct StructImpl {
ffi_type: ffi_type, ffi_type: ffi_type,
fields: Vec<*mut ffi_type>, fields: Vec<*mut ffi_type>,
atom_fields: Vec<Atom>,
} }
struct PointerArgs { struct PointerArgs {
pointers: Vec<*mut c_void>, pointers: Vec<*mut c_void>,
memory: Vec<Box<dyn Any>>, _memory: Vec<Box<dyn Any>>,
} }
impl ForeignFunctionTable { impl ForeignFunctionTable {
@@ -46,13 +47,13 @@ impl ForeignFunctionTable {
self.table.extend(other.table); self.table.extend(other.table);
} }
pub fn define_struct(&mut self, name: &str, fields: Vec<Atom>) { pub fn define_struct(&mut self, name: &str, atom_fields: Vec<Atom>) {
let mut fields: Vec<_> = fields.iter().map(|x| self.map_type_ffi(&x)).collect(); let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(&x)).collect();
fields.push(std::ptr::null_mut::<ffi_type>()); fields.push(std::ptr::null_mut::<ffi_type>());
let mut struct_type: ffi_type = Default::default(); let mut struct_type: ffi_type = Default::default();
struct_type.type_ = type_tag::STRUCT; struct_type.type_ = type_tag::STRUCT;
struct_type.elements = fields.as_mut_ptr(); struct_type.elements = fields.as_mut_ptr();
self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields}); self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields, atom_fields});
} }
fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type { fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type {
@@ -122,7 +123,7 @@ impl ForeignFunctionTable {
fn build_pointer_args(args: &mut Vec<Value>, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap<String, StructImpl>) -> Result<PointerArgs, FFIError> { fn build_pointer_args(args: &mut Vec<Value>, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap<String, StructImpl>) -> Result<PointerArgs, FFIError> {
let mut pointers = Vec::with_capacity(args.len()); let mut pointers = Vec::with_capacity(args.len());
let mut memory = Vec::new(); let mut _memory = Vec::new();
for i in 0..args.len() { for i in 0..args.len() {
let field_type = type_args[i]; let field_type = type_args[i];
unsafe { unsafe {
@@ -132,7 +133,7 @@ impl ForeignFunctionTable {
let n: $type = <$type>::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; let n: $type = <$type>::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?;
let mut box_value = Box::new(n) as Box<dyn Any>; let mut box_value = Box::new(n) as Box<dyn Any>;
pointers.push(&mut *box_value as *mut _ as *mut c_void); pointers.push(&mut *box_value as *mut _ as *mut c_void);
memory.push(box_value); _memory.push(box_value);
} }
} }
} }
@@ -150,23 +151,41 @@ impl ForeignFunctionTable {
let n: f32 = args[i].as_float()? as f32; let n: f32 = args[i].as_float()? as f32;
let mut box_value = Box::new(n) as Box<dyn Any>; let mut box_value = Box::new(n) as Box<dyn Any>;
pointers.push(&mut *box_value as *mut _ as *mut c_void); pointers.push(&mut *box_value as *mut _ as *mut c_void);
memory.push(box_value); _memory.push(box_value);
}, },
libffi::raw::FFI_TYPE_DOUBLE => { libffi::raw::FFI_TYPE_DOUBLE => {
let n: f64 = args[i].as_float()?; let n: f64 = args[i].as_float()?;
let mut box_value = Box::new(n) as Box<dyn Any>; let mut box_value = Box::new(n) as Box<dyn Any>;
pointers.push(&mut *box_value as *mut _ as *mut c_void); pointers.push(&mut *box_value as *mut _ as *mut c_void);
memory.push(box_value); _memory.push(box_value);
}, },
libffi::raw::FFI_TYPE_POINTER => { libffi::raw::FFI_TYPE_POINTER => {
let ptr: *mut c_void = args[i].as_ptr()?; let ptr: *mut c_void = args[i].as_ptr()?;
pointers.push(ptr); pointers.push(ptr);
}, },
libffi::raw::FFI_TYPE_STRUCT => { libffi::raw::FFI_TYPE_STRUCT => {
match args[i] { let (mut ptr, _size, _align) = Self::build_struct(&mut args[i], structs_table)?;
pointers.push(&mut *ptr as *mut _ as *mut c_void);
_memory.push(ptr);
},
_ => return Err(FFIError::InvalidFFIType)
}
}
}
Ok(PointerArgs {
pointers,
_memory
})
}
fn build_struct(arg: &mut Value, structs_table: &mut HashMap<String, StructImpl>) -> Result<(Box<dyn Any>, usize, usize), FFIError> {
unsafe {
match arg {
Value::Struct(ref name, ref mut struct_args) => { Value::Struct(ref name, ref mut struct_args) => {
if let Some(ref mut struct_type) = structs_table.get_mut(name) { if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) {
let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap();
let align = struct_type.ffi_type.alignment as usize;
let size = struct_type.ffi_type.size;
let ptr = alloc(layout) as *mut c_void; let ptr = alloc(layout) as *mut c_void;
let mut field_ptr = ptr; let mut field_ptr = ptr;
@@ -174,6 +193,7 @@ impl ForeignFunctionTable {
macro_rules! try_write_int { macro_rules! try_write_int {
($type:ty) => { ($type:ty) => {
{ {
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
let n: $type = <$type>::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; let n: $type = <$type>::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?;
std::ptr::write(field_ptr as *mut $type, n); std::ptr::write(field_ptr as *mut $type, n);
field_ptr = field_ptr.add(std::mem::size_of::<$type>()); field_ptr = field_ptr.add(std::mem::size_of::<$type>());
@@ -186,7 +206,7 @@ impl ForeignFunctionTable {
{ {
let data: $type = $value; let data: $type = $value;
std::ptr::write(field_ptr as *mut $type, data); std::ptr::write(field_ptr as *mut $type, data);
field_ptr = field_ptr.add(std::mem::size_of::<$type>()); field_ptr = field_ptr.add(align);
} }
} }
} }
@@ -204,29 +224,27 @@ impl ForeignFunctionTable {
libffi::raw::FFI_TYPE_POINTER => write!(*mut c_void, struct_args[i].as_ptr()?), libffi::raw::FFI_TYPE_POINTER => write!(*mut c_void, struct_args[i].as_ptr()?),
libffi::raw::FFI_TYPE_FLOAT => write!(f32, struct_args[i].as_float()? as f32), libffi::raw::FFI_TYPE_FLOAT => write!(f32, struct_args[i].as_float()? as f32),
libffi::raw::FFI_TYPE_DOUBLE => write!(f64, struct_args[i].as_float()?), libffi::raw::FFI_TYPE_DOUBLE => write!(f64, struct_args[i].as_float()?),
libffi::raw::FFI_TYPE_STRUCT => {
let (struct_ptr, struct_size, struct_align) = Self::build_struct(&mut struct_args[i], structs_table)?;
field_ptr = field_ptr.add(field_ptr.align_offset(struct_align));
std::ptr::copy(& *struct_ptr as *const _ as *const c_void, field_ptr as *mut c_void, struct_size);
field_ptr = field_ptr.add(struct_size);
},
_ => { _ => {
unreachable!() unreachable!()
} }
} }
} }
pointers.push(ptr); return Ok((Box::from_raw(ptr), size, align));
memory.push(Box::from_raw(ptr));
} else { } else {
return Err(FFIError::InvalidStructName); return Err(FFIError::InvalidStructName);
} }
} }
_ => return Err(FFIError::ValueCast) _ => return Err(FFIError::ValueCast)
} }
},
_ => return Err(FFIError::InvalidFFIType)
} }
} }
}
Ok(PointerArgs {
pointers,
memory
})
}
pub fn exec(&mut self, name: &str, mut args: Vec<Value>) -> Result<Value, FFIError> { pub fn exec(&mut self, name: &str, mut args: Vec<Value>) -> Result<Value, FFIError> {
let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?; let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?;
@@ -288,22 +306,38 @@ impl ForeignFunctionTable {
Ok(Value::Float(*n)) Ok(Value::Float(*n))
}, },
libffi::raw::FFI_TYPE_STRUCT => { libffi::raw::FFI_TYPE_STRUCT => {
let mut returns = Vec::new(); let name = &function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?;
let struct_type = self.structs.get_mut(&function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?).ok_or(FFIError::StructNotFound)?; let struct_type = self.structs.get(name).ok_or(FFIError::StructNotFound)?;
let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap();
let ptr = alloc(layout) as *mut c_void; let ptr = alloc(layout) as *mut c_void;
libffi::raw::ffi_call( libffi::raw::ffi_call(
&mut function_impl.cif, &mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()), Some(*function_impl.code_ptr.as_safe_fun()),
&mut *ptr as *mut _ as *mut c_void, &mut *ptr as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
); );
let struct_val = self.read_struct(ptr, name, struct_type);
drop(Box::from_raw(ptr));
struct_val
}
_ => unreachable!()
}
};
}
fn read_struct(&self, ptr: *mut c_void, name: &str, struct_type: &StructImpl) -> Result<Value, FFIError> {
unsafe {
let mut returns = Vec::new();
let mut field_ptr = ptr; let mut field_ptr = ptr;
for i in 0..(struct_type.fields.len()-1) {
let field = struct_type.fields[i];
macro_rules! read_and_push_int { macro_rules! read_and_push_int {
($type:ty) => { ($type:ty) => {
{ {
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
let n = std::ptr::read(field_ptr as *mut $type); let n = std::ptr::read(field_ptr as *mut $type);
returns.push(Value::Int(i64::from(n))); returns.push(Value::Int(i64::from(n)));
field_ptr = field_ptr.add(std::mem::size_of::<$type>()); field_ptr = field_ptr.add(std::mem::size_of::<$type>());
@@ -311,8 +345,6 @@ impl ForeignFunctionTable {
} }
} }
for i in 0..(struct_type.fields.len()-1) {
let field = struct_type.fields[i];
match (*field).type_ as u32 { match (*field).type_ as u32 {
libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8), libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8),
libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8), libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8),
@@ -321,22 +353,27 @@ impl ForeignFunctionTable {
libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32), libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32),
libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32), libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32),
libffi::raw::FFI_TYPE_UINT64 => { libffi::raw::FFI_TYPE_UINT64 => {
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<u64>()));
let n = std::ptr::read(field_ptr as *mut u64); let n = std::ptr::read(field_ptr as *mut u64);
returns.push(Value::Int(i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?)); returns.push(Value::Int(i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?));
field_ptr = field_ptr.add(std::mem::size_of::<u64>()); field_ptr = field_ptr.add(std::mem::size_of::<u64>());
}, },
libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64), libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64),
libffi::raw::FFI_TYPE_STRUCT => {
let substruct = struct_type.atom_fields[i].as_str();
let struct_type = self.structs.get(substruct).ok_or(FFIError::StructNotFound)?;
field_ptr = field_ptr.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize));
let struct_val = self.read_struct(field_ptr, substruct, struct_type);
returns.push(struct_val?);
field_ptr = field_ptr.add(struct_type.ffi_type.size);
},
_ => { _ => {
unreachable!() unreachable!()
} }
} }
} }
drop(Box::from_raw(ptr)); Ok(Value::Struct(name.into(), returns))
Ok(Value::Struct("texture".into(), returns))
},
_ => unreachable!()
} }
};
} }
} }

View File

@@ -4257,6 +4257,7 @@ impl Machine {
} }
} }
} }
match self.machine_st.try_from_list(args_reg, stub_gen) { match self.machine_st.try_from_list(args_reg, stub_gen) {
Ok(args) => { Ok(args) => {
let args: Vec<_> = args.into_iter().map(|x| map_arg(&mut self.machine_st, x)).collect(); let args: Vec<_> = args.into_iter().map(|x| map_arg(&mut self.machine_st, x)).collect();
@@ -4264,22 +4265,9 @@ impl Machine {
Ok(result) => { Ok(result) => {
match result { match result {
Value::Int(n) => self.machine_st.unify_fixnum(Fixnum::build_with(n), return_value), Value::Int(n) => self.machine_st.unify_fixnum(Fixnum::build_with(n), return_value),
Value::Struct(name, mut args) => { Value::Struct(name, args) => {
args.insert(0, Value::CString(CString::new(name).unwrap())); let struct_value = self.build_struct(&name, args);
let struct_list = heap_loc_as_cell!( unify!(self.machine_st, return_value, struct_value);
iter_to_heap_list(
&mut self.machine_st.heap,
args.into_iter()
.map(|val| {
match val {
Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)),
Value::CString(cstr) => atom_as_cell!(self.machine_st.atom_tbl.build_with(&cstr.into_string().unwrap())),
_ => unreachable!()
}
}),
)
);
unify!(self.machine_st, return_value, struct_list);
} }
_ => { _ => {
unreachable!(); unreachable!();
@@ -4302,6 +4290,26 @@ impl Machine {
Ok(()) Ok(())
} }
fn build_struct(&mut self, name: &str, mut args: Vec<Value>) -> HeapCellValue {
args.insert(0, Value::CString(CString::new(name).unwrap()));
let cells: Vec<_> = args.into_iter()
.map(|val| {
match val {
Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)),
Value::CString(cstr) => atom_as_cell!(self.machine_st.atom_tbl.build_with(&cstr.into_string().unwrap())),
Value::Struct(name, struct_args) => self.build_struct(&name, struct_args),
_ => unreachable!()
}
}).collect();
heap_loc_as_cell!(
iter_to_heap_list(
&mut self.machine_st.heap,
cells.into_iter()
)
)
}
#[inline(always)] #[inline(always)]
pub(crate) fn define_foreign_struct(&mut self) -> CallResult { pub(crate) fn define_foreign_struct(&mut self) -> CallResult {
let struct_name = self.deref_register(1); let struct_name = self.deref_register(1);