extern crate libffi_sys;
use libffi_sys::*;
use std::ptr::{null, null_mut};
use libc::c_void;
use std::ffi::{CString, CStr};
use crate::vmbindings::vm::Vm;
use crate::vmbindings::vmerror::VmError;
use crate::vmbindings::value::Value;
use crate::vmbindings::carray::CArray;
use crate::vmbindings::record::Record;
extern crate num_derive;
use num_traits::cast::FromPrimitive;
#[repr(i64)]
#[derive(FromPrimitive)]
enum FFI_Type {
UInt8, Int8,
UInt16, Int16,
UInt32, Int32,
UInt64, Int64,
Float32, Float64,
Pointer,
String,
Void
}
impl FFI_Type {
unsafe fn to_libffi_type(&self) -> *mut ffi_type {
match self {
FFI_Type::UInt8 => &mut ffi_type_uint8,
FFI_Type::Int8 => &mut ffi_type_sint8,
FFI_Type::UInt16 => &mut ffi_type_uint16,
FFI_Type::Int16 => &mut ffi_type_sint16,
FFI_Type::UInt32 => &mut ffi_type_uint32,
FFI_Type::Int32 => &mut ffi_type_sint32,
FFI_Type::UInt64 => &mut ffi_type_uint64,
FFI_Type::Int64 => &mut ffi_type_sint64,
FFI_Type::Float32 => &mut ffi_type_float,
FFI_Type::Float64 => &mut ffi_type_double,
FFI_Type::Pointer => &mut ffi_type_pointer,
FFI_Type::String => &mut ffi_type_pointer,
FFI_Type::Void => &mut ffi_type_void
}
}
}
struct FFIFunction {
sym: unsafe extern fn(),
cif: ffi_cif,
argtypes: CArray<FFI_Type>,
ffi_argtypes: CArray<*mut ffi_type>,
rettype: FFI_Type,
ffi_rettype: *mut ffi_type,
}
pub mod function {
use super::*;
#[hana_function()]
fn constructor(name_or_addr: Value::Any, argtypes: Value::Array, rettype: Value::Int) -> Value {
let argtypes = argtypes.as_ref();
let mut inst_argtypes = CArray::new();
let mut ffi_argtypes : CArray<*mut ffi_type> = CArray::new();
for arg in argtypes.iter() {
let ffi_type = FFI_Type::from_i64(arg.unwrap().int()).unwrap();
ffi_argtypes.push(unsafe{ ffi_type.to_libffi_type() });
inst_argtypes.push(ffi_type);
}
let rettype = FFI_Type::from_i64(rettype).unwrap();
let ffi_fn = unsafe {
let mut cif: ffi_cif = Default::default();
let ffi_rettype = unsafe{ rettype.to_libffi_type() };
ffi_prep_cif(&mut cif, ffi_abi_FFI_DEFAULT_ABI,
argtypes.len() as u32,
ffi_rettype,
ffi_argtypes.as_mut_ptr());
fn invalid_symbol(vm: &Vm, filename: Value) -> Value {
let rec = vm.malloc(Record::new());
rec.as_mut().insert(
"prototype",
Value::Record(vm.stdlib.as_ref().unwrap().invalid_argument_error.clone())
.wrap(),
);
rec.as_mut().insert(
"why",
Value::Str(
vm.malloc("Specified symbol doesn't exist".to_string()),
)
.wrap(),
);
rec.as_mut().insert("where", filename.wrap());
Value::Record(rec)
}
let dl = libc::dlopen(null(), libc::RTLD_LAZY);
FFIFunction {
sym: {
match &name_or_addr {
Value::Int(addr) => {
if *addr == 0 {
let err = invalid_symbol(vm, Value::Str(vm.malloc("[nil]".to_string())));
hana_raise!(vm, err);
} else {
std::mem::transmute::<i64, unsafe extern fn()>(*addr)
}
},
Value::Str(sym) => {
let cstr = if let Some(cstr) = CString::new(sym.as_ref().clone()) {
cstr
} else {
let err = invalid_symbol(vm, Value::Str(sym.clone()));
hana_raise!(vm, err);
};
let dlsym = libc::dlsym(dl, cstr.as_c_str().as_ptr());
if dlsym.is_null() {
let err = invalid_symbol(vm, Value::Str(sym.clone()));
hana_raise!(vm, err);
}
else { std::mem::transmute::<*mut c_void, unsafe extern fn()>(dlsym) }
}
_ => panic!("expected symbol address or name")
}
},
cif,
argtypes: inst_argtypes,
ffi_argtypes,
rettype,
ffi_rettype
}
};
let rec = vm.malloc(Record::new());
rec.as_mut().native_field = Some(Box::new(ffi_fn));
rec.as_mut().insert("prototype",
vm.global().get("Cffi").unwrap().unwrap().record().get("Function").unwrap().clone());
Value::Record(rec)
}
#[hana_function()]
fn call(ffi_fn_rec: Value::Record, args: Value::Array) {
let field = ffi_fn_rec.as_mut().native_field.as_mut().unwrap();
let mut ffi_fn = field.downcast_mut::<FFIFunction>().unwrap();
use libc::c_void;
use std::any::Any;
use std::convert::TryInto;
use std::mem::transmute;
let mut managed_strs : Vec<Box<CStr>> = Vec::new();
let mut aref : CArray<*mut c_void> = CArray::new();
let mut argtypes_iter = ffi_fn.argtypes.iter();
let slice = args.as_mut().as_mut_slice();
for arg in slice {
let argtype = &argtypes_iter.next().unwrap();
unsafe {
let ptr = transmute::<&u64, *mut c_void>(&arg.data);
#[allow(safe_packed_borrows)]
match argtype {
FFI_Type::String => {
let cstr : Box<CStr> = CString::new(arg.unwrap().string().clone()).unwrap().into_boxed_c_str();
aref.push(transmute::<*const *const libc::c_char, *mut c_void>(&cstr.as_ptr()));
managed_strs.push(cstr);
},
_ => { aref.push(transmute::<&mut u64, *mut c_void>(&mut arg.data)); },
}
}
}
let sym = Some(ffi_fn.sym);
unsafe { match &ffi_fn.rettype {
FFI_Type::UInt8 | FFI_Type::Int8 |
FFI_Type::UInt16 | FFI_Type::Int16 |
FFI_Type::UInt32 | FFI_Type::Int32 |
FFI_Type::UInt64 | FFI_Type::Int64 |
FFI_Type::Pointer
=> {
let mut rvalue = 0;
ffi_call(&mut ffi_fn.cif, sym, transmute::<&i64, *mut c_void>(&rvalue), aref.as_mut_ptr());
Value::Int(rvalue)
},
FFI_Type::Float32
=> {
let mut rvalue = 0.0f32;
ffi_call(&mut ffi_fn.cif, sym, transmute::<&f32, *mut c_void>(&rvalue), aref.as_mut_ptr());
Value::Float(rvalue as f64)
},
FFI_Type::Float64
=> {
let mut rvalue = 0.0f64;
ffi_call(&mut ffi_fn.cif, sym, transmute::<&f64, *mut c_void>(&rvalue), aref.as_mut_ptr());
Value::Float(rvalue)
},
FFI_Type::String
=> {
let mut rvalue : *const libc::c_char = null_mut();
ffi_call(&mut ffi_fn.cif, sym, transmute::<&*const libc::c_char, *mut c_void>(&rvalue), aref.as_mut_ptr());
assert!(!rvalue.is_null());
Value::Str(vm.malloc(CStr::from_ptr(rvalue).to_str().unwrap().to_string()))
},
FFI_Type::Void
=> {
ffi_call(&mut ffi_fn.cif, sym, null_mut(), aref.as_mut_ptr());
Value::Nil
}
} }
}
}
pub mod gc_pointer {
use super::*;
struct GcPointer {
data: *mut libc::c_void,
free: unsafe extern fn(*mut libc::c_void),
}
impl std::ops::Drop for GcPointer {
fn drop(&mut self) {
unsafe{ (self.free)(self.data) }
}
}
#[hana_function()]
fn constructor(addr: Value::Int, cffi_free: Value::Record) -> Value {
let field = cffi_free.as_mut().native_field.as_mut().unwrap();
let mut cffi_free = field.downcast_mut::<FFIFunction>().unwrap();
let rec = vm.malloc(Record::new());
unsafe{
use std::mem::transmute;
rec.as_mut().native_field = Some(Box::new(GcPointer {
data: transmute::<i64, *mut libc::c_void>(addr),
free: transmute::<*mut libc::c_void, unsafe extern fn(*mut libc::c_void)>(cffi_free.sym as *mut libc::c_void),
}));
}
rec.as_mut().insert("prototype",
vm.global().get("Cffi").unwrap().unwrap().record().get("GcPointer").unwrap().clone());
Value::Record(rec)
}
#[hana_function()]
fn addr(pointer: Value::Record) -> Value {
let field = pointer.as_mut().native_field.as_mut().unwrap();
let mut gc_pointer = field.downcast_mut::<GcPointer>().unwrap();
Value::Int(unsafe { std::mem::transmute::<*mut libc::c_void, i64>(gc_pointer.data) })
}
}
pub mod library {
use super::*;
struct Library {
dl: *mut libc::c_void,
}
impl std::ops::Drop for Library {
fn drop(&mut self) {
unsafe {
libc::dlclose(self.dl);
}
}
}
#[hana_function()]
fn constructor(filename: Value::Str) -> Value {
let rec = vm.malloc(Record::new());
unsafe {
rec.as_mut().native_field = Some(Box::new({
let cstr = CString::new(filename.as_ref().clone()).unwrap();
let dl = libc::dlopen(cstr.as_ptr(), libc::RTLD_LAZY);
if dl.is_null() {
let rec = vm.malloc(Record::new());
rec.as_mut().insert(
"prototype",
Value::Record(vm.stdlib.as_ref().unwrap().invalid_argument_error.clone())
.wrap(),
);
rec.as_mut().insert(
"why",
Value::Str(
vm.malloc("Specified library doesn't exist".to_string()),
)
.wrap(),
);
rec.as_mut().insert("where", Value::Str(filename).wrap());
hana_raise!(vm, Value::Record(rec));
}
Library {
dl
}
}));
}
rec.as_mut().insert("prototype",
vm.global().get("Cffi").unwrap().unwrap().record().get("Library").unwrap().clone());
Value::Record(rec)
}
#[hana_function()]
fn sym(library: Value::Record, sym: Value::Str) -> Value {
let field = library.as_mut().native_field.as_mut().unwrap();
let mut dl = field.downcast_mut::<Library>().unwrap();
unsafe {
let cstr = CString::new(sym.as_ref().clone()).unwrap();
let sym = libc::dlsym(dl.dl, cstr.as_c_str().as_ptr());
Value::Int(sym as i64)
}
}
}
pub fn load(vm: &mut Vm) {
macro_rules! set_var {
($x:literal, $y:expr) => (vm.mut_global().insert($x.to_string(), $y.wrap()));
}
macro_rules! set_obj_var {
($o: expr, $x:literal, $y:expr) => ($o.as_mut().insert($x.to_string(), $y.wrap()));
}
let cffi_mod = vm.malloc(Record::new());
set_obj_var!(cffi_mod, "UInt8", Value::Int(FFI_Type::UInt8 as i64));
set_obj_var!(cffi_mod, "Int8", Value::Int(FFI_Type::Int8 as i64));
set_obj_var!(cffi_mod, "UInt16", Value::Int(FFI_Type::UInt16 as i64));
set_obj_var!(cffi_mod, "Int16", Value::Int(FFI_Type::Int16 as i64));
set_obj_var!(cffi_mod, "UInt32", Value::Int(FFI_Type::UInt32 as i64));
set_obj_var!(cffi_mod, "Int32", Value::Int(FFI_Type::Int32 as i64));
set_obj_var!(cffi_mod, "UInt64", Value::Int(FFI_Type::UInt64 as i64));
set_obj_var!(cffi_mod, "Int64", Value::Int(FFI_Type::Int64 as i64));
set_obj_var!(cffi_mod, "Float32", Value::Int(FFI_Type::Float32 as i64));
set_obj_var!(cffi_mod, "Float64", Value::Int(FFI_Type::Float64 as i64));
set_obj_var!(cffi_mod, "Pointer", Value::Int(FFI_Type::Pointer as i64));
set_obj_var!(cffi_mod, "String", Value::Int(FFI_Type::String as i64));
set_obj_var!(cffi_mod, "Void", Value::Int(FFI_Type::Void as i64));
let library = vm.malloc(Record::new());
set_obj_var!(library, "constructor", Value::NativeFn(library::constructor));
set_obj_var!(library, "sym", Value::NativeFn(library::sym));
set_obj_var!(cffi_mod, "Library", Value::Record(library));
let function = vm.malloc(Record::new());
set_obj_var!(function, "constructor", Value::NativeFn(function::constructor));
set_obj_var!(function, "call", Value::NativeFn(function::call));
set_obj_var!(cffi_mod, "Function", Value::Record(function));
let gc_pointer = vm.malloc(Record::new());
set_obj_var!(gc_pointer, "constructor", Value::NativeFn(gc_pointer::constructor));
set_obj_var!(gc_pointer, "addr", Value::NativeFn(gc_pointer::addr));
set_obj_var!(cffi_mod, "GcPointer", Value::Record(gc_pointer));
set_var!("Cffi", Value::Record(cffi_mod));
}