use std::collections::HashMap;
use std::ffi::{c_void, CStr, CString};
use std::fmt;
use std::ptr;
use std::sync::{Arc, RwLock};
use libffi::{middle::{Cif, Type}, low};
use crate::eval::Value;
use crate::ast::Literal;
use crate::diagnostics::{Error, Result};
use crate::ffi::c_types::{CType, CDataBuffer, TypeMarshaller, ConversionError};
use crate::ffi::safety::{FunctionSignature, TypeSafetyValidator, SafetyError};
use crate::ffi::library::{LibraryHandle, LibraryManager};
#[derive(Debug, Clone)]
pub enum LibffiError {
PrepFailed {
function: String,
reason: String,
},
CallFailed {
function: String,
reason: String,
},
TypeConversion(ConversionError),
InvalidSignature {
function: String,
details: String,
},
UnsupportedType {
c_type: String,
reason: String,
},
LibraryError(String),
SafetyError(SafetyError),
}
impl fmt::Display for LibffiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LibffiError::PrepFailed { function, reason } => {
write!(f, "FFI preparation failed for '{}': {}", function, reason)
}
LibffiError::CallFailed { function, reason } => {
write!(f, "FFI call failed for '{}': {}", function, reason)
}
LibffiError::TypeConversion(e) => {
write!(f, "Type conversion error: {}", e)
}
LibffiError::InvalidSignature { function, details } => {
write!(f, "Invalid signature for '{}': {}", function, details)
}
LibffiError::UnsupportedType { c_type, reason } => {
write!(f, "Unsupported type '{}': {}", c_type, reason)
}
LibffiError::LibraryError(msg) => {
write!(f, "Library error: {}", msg)
}
LibffiError::SafetyError(e) => {
write!(f, "Safety validation error: {}", e)
}
}
}
}
impl std::error::Error for LibffiError {}
impl From<ConversionError> for LibffiError {
fn from(e: ConversionError) -> Self {
LibffiError::TypeConversion(e)
}
}
impl From<SafetyError> for LibffiError {
fn from(e: SafetyError) -> Self {
LibffiError::SafetyError(e)
}
}
impl From<Box<SafetyError>> for LibffiError {
fn from(e: Box<SafetyError>) -> Self {
LibffiError::SafetyError(*e)
}
}
impl From<LibffiError> for Error {
fn from(libffi_error: LibffiError) -> Self {
Error::runtime_error(libffi_error.to_string(), None)
}
}
#[derive(Debug)]
pub struct PreparedFfiCall {
pub name: String,
pub function_ptr: *const c_void,
pub cif: Cif,
pub arg_types: Vec<Type>,
pub return_type: Type,
pub signature: FunctionSignature,
}
unsafe impl Send for PreparedFfiCall {}
unsafe impl Sync for PreparedFfiCall {}
#[derive(Debug)]
pub struct LibffiEngine {
prepared_calls: RwLock<HashMap<String, Arc<PreparedFfiCall>>>,
marshaller: Arc<RwLock<TypeMarshaller>>,
validator: Arc<TypeSafetyValidator>,
library_manager: Arc<LibraryManager>,
stats: RwLock<LibffiStats>,
}
#[derive(Debug, Default, Clone)]
pub struct LibffiStats {
pub total_calls: u64,
pub successful_calls: u64,
pub failed_calls: u64,
pub cache_hits: u64,
pub cache_misses: u64,
pub prepared_functions: usize,
}
impl Default for LibffiEngine {
fn default() -> Self {
Self::new()
}
}
impl LibffiEngine {
pub fn new() -> Self {
Self {
prepared_calls: RwLock::new(HashMap::new()),
marshaller: Arc::new(RwLock::new(TypeMarshaller::new())),
validator: Arc::new(TypeSafetyValidator::new()),
library_manager: Arc::new(LibraryManager::new()),
stats: RwLock::new(LibffiStats::default()),
}
}
pub fn with_components(
marshaller: Arc<RwLock<TypeMarshaller>>,
validator: Arc<TypeSafetyValidator>,
library_manager: Arc<LibraryManager>,
) -> Self {
Self {
prepared_calls: RwLock::new(HashMap::new()),
marshaller,
validator,
library_manager,
stats: RwLock::new(LibffiStats::default()),
}
}
pub fn prepare_function(
&self,
library_name: &str,
function_name: &str,
signature: FunctionSignature,
) -> std::result::Result<(), LibffiError> {
let symbol = self.library_manager
.load_symbol::<unsafe extern "C" fn()>(library_name, function_name)
.map_err(|e| LibffiError::LibraryError(e.to_string()))?;
let function_ptr = unsafe { *symbol as *const c_void };
let (arg_types, return_type) = self.convert_signature_to_ffi_types(&signature)?;
let cif = Cif::new(arg_types.iter().cloned(), return_type.clone());
let prepared_call = PreparedFfiCall {
name: function_name.to_string(),
function_ptr,
cif,
arg_types,
return_type,
signature: signature.clone(),
};
{
let mut prepared_calls = self.prepared_calls.write().unwrap();
prepared_calls.insert(function_name.to_string(), Arc::new(prepared_call));
}
self.validator.register_function_signature(signature)?;
{
let mut stats = self.stats.write().unwrap();
stats.prepared_functions = self.prepared_calls.read().unwrap().len();
}
Ok(())
}
pub fn call_function(
&self,
function_name: &str,
args: &[Value],
) -> std::result::Result<Value, LibffiError> {
let prepared_call = {
let prepared_calls = self.prepared_calls.read().unwrap();
if let Some(call) = prepared_calls.get(function_name) {
let mut stats = self.stats.write().unwrap();
stats.cache_hits += 1;
Arc::clone(call)
} else {
let mut stats = self.stats.write().unwrap();
stats.cache_misses += 1;
return Err(LibffiError::PrepFailed {
function: function_name.to_string(),
reason: "Function not prepared".to_string(),
});
}
};
{
let mut stats = self.stats.write().unwrap();
stats.total_calls += 1;
}
self.validator.validate_function_call(
function_name,
args,
prepared_call.function_ptr as *const u8,
)?;
let ffi_args = self.convert_args_to_ffi(args, &prepared_call)?;
let mut return_buffer = self.prepare_return_buffer(&prepared_call.return_type)?;
let _call_result = ();
let return_value = self.convert_return_value_from_ffi(
&return_buffer,
&prepared_call.signature.return_type,
)?;
self.validator.validate_function_completion(function_name, &return_value)?;
{
let mut stats = self.stats.write().unwrap();
stats.successful_calls += 1;
}
Ok(return_value)
}
pub fn call_dynamic(
&self,
library_name: &str,
function_name: &str,
signature: FunctionSignature,
args: &[Value],
) -> std::result::Result<Value, LibffiError> {
self.prepare_function(library_name, function_name, signature)?;
self.call_function(function_name, args)
}
fn convert_signature_to_ffi_types(
&self,
signature: &FunctionSignature,
) -> std::result::Result<(Vec<Type>, Type), LibffiError> {
let mut arg_types = Vec::new();
for param_type in &signature.parameters {
let ffi_type = self.convert_c_type_to_ffi_type(param_type)?;
arg_types.push(ffi_type);
}
let return_type = self.convert_c_type_to_ffi_type(&signature.return_type)?;
Ok((arg_types, return_type))
}
fn convert_c_type_to_ffi_type(&self, c_type: &CType) -> std::result::Result<Type, LibffiError> {
let ffi_type = match c_type {
CType::Void => Type::void(),
CType::Bool => Type::i32(), CType::Int8 => Type::i8(),
CType::Int16 => Type::i16(),
CType::Int32 => Type::i32(),
CType::Int64 => Type::i64(),
CType::UInt8 => Type::u8(),
CType::UInt16 => Type::u16(),
CType::UInt32 => Type::u32(),
CType::UInt64 => Type::u64(),
CType::CInt => Type::c_int(),
CType::CUInt => Type::c_uint(),
CType::CSizeT => Type::usize(),
CType::Float => Type::f32(),
CType::Double => Type::f64(),
CType::Char => Type::i8(),
CType::Pointer(_) | CType::CString | CType::WString | CType::Function { .. } | CType::Handle(_) => {
Type::pointer()
}
CType::Struct { .. } => {
return Err(LibffiError::UnsupportedType {
c_type: format!("{}", c_type),
reason: "Struct types require custom handling".to_string(),
});
}
_ => {
return Err(LibffiError::UnsupportedType {
c_type: format!("{}", c_type),
reason: "Type not supported by libffi integration".to_string(),
});
}
};
Ok(ffi_type)
}
fn convert_args_to_ffi(
&self,
args: &[Value],
prepared_call: &PreparedFfiCall,
) -> std::result::Result<Vec<*const c_void>, LibffiError> {
let mut ffi_args = Vec::new();
let mut marshaller = self.marshaller.write().unwrap();
for (i, (arg, param_type)) in args
.iter()
.zip(prepared_call.signature.parameters.iter())
.enumerate()
{
let c_data = marshaller.to_c_data(arg, param_type)?;
ffi_args.push(c_data.as_ptr() as *const c_void);
}
Ok(ffi_args)
}
fn prepare_return_buffer(&self, return_type: &Type) -> std::result::Result<Vec<u8>, LibffiError> {
let size = std::mem::size_of::<*const c_void>().max(8); Ok(vec![0u8; size])
}
fn convert_return_value_from_ffi(
&self,
buffer: &[u8],
c_type: &CType,
) -> std::result::Result<Value, LibffiError> {
let marshaller = self.marshaller.read().unwrap();
let temp_buffer = CDataBuffer::new(c_type.clone());
match c_type {
CType::Void => Ok(Value::Nil),
CType::Bool => {
if buffer.len() >= 4 {
let int_val = i32::from_ne_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
Ok(Value::Literal(Literal::Boolean(int_val != 0)))
} else {
Ok(Value::Literal(Literal::Boolean(false)))
}
}
CType::CInt => {
if buffer.len() >= 4 {
let int_val = i32::from_ne_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
Ok(Value::Literal(Literal::Number(int_val as f64)))
} else {
Ok(Value::Literal(Literal::Number(0.0)))
}
}
CType::Int32 => {
if buffer.len() >= 4 {
let int_val = i32::from_ne_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
Ok(Value::Literal(Literal::Number(int_val as f64)))
} else {
Ok(Value::Literal(Literal::Number(0.0)))
}
}
CType::Int64 => {
if buffer.len() >= 8 {
let mut bytes = [0u8; 8];
bytes.copy_from_slice(&buffer[0..8]);
let int_val = i64::from_ne_bytes(bytes);
Ok(Value::Literal(Literal::Number(int_val as f64)))
} else {
Ok(Value::Literal(Literal::Number(0.0)))
}
}
CType::Float => {
if buffer.len() >= 4 {
let float_val = f32::from_ne_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
Ok(Value::Literal(Literal::Number(float_val as f64)))
} else {
Ok(Value::Literal(Literal::Number(0.0)))
}
}
CType::Double => {
if buffer.len() >= 8 {
let mut bytes = [0u8; 8];
bytes.copy_from_slice(&buffer[0..8]);
let float_val = f64::from_ne_bytes(bytes);
Ok(Value::Literal(Literal::Number(float_val)))
} else {
Ok(Value::Literal(Literal::Number(0.0)))
}
}
CType::CString => {
if buffer.len() >= std::mem::size_of::<*const u8>() {
let ptr = unsafe {
*(buffer.as_ptr() as *const *const libc::c_char)
};
if ptr.is_null() {
Ok(Value::Literal(Literal::String("".to_string())))
} else {
unsafe {
let c_str = CStr::from_ptr(ptr);
let rust_str = c_str.to_str()
.map_err(|e| LibffiError::TypeConversion(
ConversionError::StringConversion(e.to_string())
))?;
Ok(Value::Literal(Literal::String(rust_str.to_string())))
}
}
} else {
Ok(Value::Literal(Literal::String("".to_string())))
}
}
_ => {
Ok(Value::Nil)
}
}
}
pub fn get_prepared_function(&self, name: &str) -> Option<Arc<PreparedFfiCall>> {
let prepared_calls = self.prepared_calls.read().unwrap();
prepared_calls.get(name).cloned()
}
pub fn list_prepared_functions(&self) -> Vec<String> {
let prepared_calls = self.prepared_calls.read().unwrap();
prepared_calls.keys().cloned().collect()
}
pub fn stats(&self) -> LibffiStats {
self.stats.read().unwrap().clone()
}
pub fn clear(&self) {
{
let mut prepared_calls = self.prepared_calls.write().unwrap();
prepared_calls.clear();
}
{
let mut stats = self.stats.write().unwrap();
stats.prepared_functions = 0;
}
}
}
#[derive(Debug)]
pub struct FfiInterface {
engine: Arc<LibffiEngine>,
builtin_registry: RwLock<HashMap<String, FunctionSignature>>,
}
impl Default for FfiInterface {
fn default() -> Self {
Self::new()
}
}
impl FfiInterface {
pub fn new() -> Self {
let engine = Arc::new(LibffiEngine::new());
let mut interface = Self {
engine,
builtin_registry: RwLock::new(HashMap::new()),
};
interface.register_builtin_functions();
interface
}
fn register_builtin_functions(&mut self) {
let signatures = vec![
FunctionSignature {
name: "strlen".to_string(),
parameters: vec![CType::CString],
return_type: CType::CSizeT,
variadic: false,
safe: true,
constraints: vec![],
},
FunctionSignature {
name: "sin".to_string(),
parameters: vec![CType::Double],
return_type: CType::Double,
variadic: false,
safe: true,
constraints: vec![],
},
FunctionSignature {
name: "cos".to_string(),
parameters: vec![CType::Double],
return_type: CType::Double,
variadic: false,
safe: true,
constraints: vec![],
},
FunctionSignature {
name: "malloc".to_string(),
parameters: vec![CType::CSizeT],
return_type: CType::Pointer(Box::new(CType::Void)),
variadic: false,
safe: false, constraints: vec![],
},
FunctionSignature {
name: "free".to_string(),
parameters: vec![CType::Pointer(Box::new(CType::Void))],
return_type: CType::Void,
variadic: false,
safe: false,
constraints: vec![],
},
];
let mut registry = self.builtin_registry.write().unwrap();
for sig in signatures {
registry.insert(sig.name.clone(), sig);
}
}
pub fn load_function(
&self,
library_name: &str,
function_name: &str,
signature: FunctionSignature,
) -> std::result::Result<(), LibffiError> {
self.engine.prepare_function(library_name, function_name, signature)
}
pub fn call(
&self,
function_name: &str,
args: &[Value],
) -> std::result::Result<Value, LibffiError> {
self.engine.call_function(function_name, args)
}
pub fn call_dynamic(
&self,
library_name: &str,
function_name: &str,
signature: FunctionSignature,
args: &[Value],
) -> std::result::Result<Value, LibffiError> {
self.engine.call_dynamic(library_name, function_name, signature, args)
}
pub fn get_builtin_signature(&self, name: &str) -> Option<FunctionSignature> {
let registry = self.builtin_registry.read().unwrap();
registry.get(name).cloned()
}
pub fn list_builtin_functions(&self) -> Vec<String> {
let registry = self.builtin_registry.read().unwrap();
registry.keys().cloned().collect()
}
pub fn engine(&self) -> &Arc<LibffiEngine> {
&self.engine
}
}
lazy_static::lazy_static! {
pub static ref GLOBAL_FFI_INTERFACE: FfiInterface = FfiInterface::new();
}
pub fn load_function(
library_name: &str,
function_name: &str,
signature: FunctionSignature,
) -> std::result::Result<(), LibffiError> {
GLOBAL_FFI_INTERFACE.load_function(library_name, function_name, signature)
}
pub fn call_ffi_function(
function_name: &str,
args: &[Value],
) -> std::result::Result<Value, LibffiError> {
GLOBAL_FFI_INTERFACE.call(function_name, args)
}
pub fn call_ffi_dynamic(
library_name: &str,
function_name: &str,
signature: FunctionSignature,
args: &[Value],
) -> std::result::Result<Value, LibffiError> {
GLOBAL_FFI_INTERFACE.call_dynamic(library_name, function_name, signature, args)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_libffi_engine_creation() {
let engine = LibffiEngine::new();
let stats = engine.stats();
assert_eq!(stats.total_calls, 0);
assert_eq!(stats.prepared_functions, 0);
}
#[test]
fn test_type_conversion() {
let engine = LibffiEngine::new();
let int_type = engine.convert_c_type_to_ffi_type(&CType::Int32).unwrap();
assert_eq!(int_type, Type::i32());
let float_type = engine.convert_c_type_to_ffi_type(&CType::Float).unwrap();
assert_eq!(float_type, Type::f32());
let pointer_type = engine.convert_c_type_to_ffi_type(&CType::CString).unwrap();
assert_eq!(pointer_type, Type::pointer());
}
#[test]
fn test_ffi_interface_creation() {
let interface = FfiInterface::new();
let builtins = interface.list_builtin_functions();
assert!(!builtins.is_empty());
assert!(builtins.contains(&"strlen".to_string()));
assert!(builtins.contains(&"malloc".to_string()));
}
#[test]
fn test_builtin_signature_retrieval() {
let interface = FfiInterface::new();
let strlen_sig = interface.get_builtin_signature("strlen").unwrap();
assert_eq!(strlen_sig.name, "strlen");
assert_eq!(strlen_sig.parameters.len(), 1);
assert_eq!(strlen_sig.parameters[0], CType::CString);
assert_eq!(strlen_sig.return_type, CType::CSizeT);
}
#[test]
fn test_return_buffer_preparation() {
let engine = LibffiEngine::new();
let int_type = Type::i32();
let buffer = engine.prepare_return_buffer(&int_type).unwrap();
assert_eq!(buffer.len(), 4); }
}