From 9d52d2a653c5fe2fb64cd6d9eb555733f46d8d02 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 22 Feb 2023 23:10:03 +0100 Subject: [PATCH] MVP of Foreign Function Interface --- Cargo.lock | 31 +++ Cargo.toml | 2 + build/instructions_template.rs | 12 ++ src/ffi.rs | 369 +++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/lib/ffi.pl | 83 ++++++++ src/machine/dispatch.rs | 24 +++ src/machine/mock_wam.rs | 3 +- src/machine/mod.rs | 3 + src/machine/system_calls.rs | 152 ++++++++++++++ 10 files changed, 679 insertions(+), 1 deletion(-) create mode 100644 src/ffi.rs create mode 100644 src/lib/ffi.pl diff --git a/Cargo.lock b/Cargo.lock index 63784cd9..b7c06dbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -952,6 +952,35 @@ version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "libffi" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb06d5b4c428f3cd682943741c39ed4157ae989fffe1094a08eaf7c4014cf60" +dependencies = [ + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c6f11e063a27ffe040a9d15f0b661bf41edc2383b7ae0e0ad5a7e7d53d9da3" +dependencies = [ + "cc", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libsodium-sys" version = "0.2.7" @@ -1833,6 +1862,8 @@ dependencies = [ "lazy_static", "lexical", "libc", + "libffi", + "libloading", "modular-bitfield", "native-tls", "ordered-float", diff --git a/Cargo.toml b/Cargo.toml index 6c98ea43..206b7150 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,8 @@ hyper = { version = "0.14", features = ["full"] } hyper-tls = "0.5.0" tokio = { version = "1.24.2", features = ["full"] } futures = "0.3" +libffi = "3.1.0" +libloading = "0.7" [dev-dependencies] assert_cmd = "1.0.3" diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 48166239..80614815 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -552,6 +552,12 @@ enum SystemClauseType { HttpAccept, #[strum_discriminants(strum(props(Arity = "4", Name = "$http_answer")))] HttpAnswer, + #[strum_discriminants(strum(props(Arity = "2", Name = "$load_foreign_lib")))] + LoadForeignLib, + #[strum_discriminants(strum(props(Arity = "3", Name = "$foreign_call")))] + ForeignCall, + #[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))] + DefineForeignStruct, #[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))] PredicateDefined, #[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))] @@ -1701,6 +1707,9 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallHttpListen(_) | &Instruction::CallHttpAccept(_) | &Instruction::CallHttpAnswer(_) | + &Instruction::CallLoadForeignLib(_) | + &Instruction::CallForeignCall(_) | + &Instruction::CallDefineForeignStruct(_) | &Instruction::CallPredicateDefined(_) | &Instruction::CallStripModule(_) | &Instruction::CallCurrentTime(_) | @@ -1916,6 +1925,9 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteHttpListen(_) | &Instruction::ExecuteHttpAccept(_) | &Instruction::ExecuteHttpAnswer(_) | + &Instruction::ExecuteLoadForeignLib(_) | + &Instruction::ExecuteForeignCall(_) | + &Instruction::ExecuteDefineForeignStruct(_) | &Instruction::ExecutePredicateDefined(_) | &Instruction::ExecuteStripModule(_) | &Instruction::ExecuteCurrentTime(_) | diff --git a/src/ffi.rs b/src/ffi.rs new file mode 100644 index 00000000..e8f2bc4b --- /dev/null +++ b/src/ffi.rs @@ -0,0 +1,369 @@ +use crate::atom_table::Atom; + +use std::alloc::{alloc, Layout}; +use std::any::Any; +use std::collections::HashMap; +use std::error::Error; +use std::ffi::{CString, c_void}; +use std::convert::TryFrom; + +use libffi::low::{ffi_cif, types, CodePtr, ffi_abi_FFI_DEFAULT_ABI, prep_cif, ffi_type, type_tag}; +use libloading::{Symbol, Library}; + +pub struct FunctionDefinition { + pub name: String, + pub return_value: Atom, + pub args: Vec, +} + +#[derive(Debug)] +pub struct FunctionImpl { + cif: ffi_cif, + args: Vec<*mut ffi_type>, + code_ptr: CodePtr, + return_struct_name: Option, +} + +#[derive(Debug, Default)] +pub struct ForeignFunctionTable { + table: HashMap, + structs: HashMap, +} + +#[derive(Debug)] +struct StructImpl { + ffi_type: ffi_type, + fields: Vec<*mut ffi_type>, +} + +struct PointerArgs { + pointers: Vec<*mut c_void>, + memory: Vec>, +} + +impl ForeignFunctionTable { + pub fn merge(&mut self, other: ForeignFunctionTable) { + self.table.extend(other.table); + } + + pub fn define_struct(&mut self, name: &str, fields: Vec) { + let mut fields: Vec<_> = fields.iter().map(|x| self.map_type_ffi(&x)).collect(); + fields.push(std::ptr::null_mut::()); + let mut struct_type: ffi_type = Default::default(); + struct_type.type_ = type_tag::STRUCT; + struct_type.elements = fields.as_mut_ptr(); + self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields}); + } + + fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type { + unsafe { + match source { + atom!("sint64") => &mut types::sint64, + atom!("sint32") => &mut types::sint32, + atom!("sint16") => &mut types::sint16, + atom!("sint8") => &mut types::sint8, + atom!("uint64") => &mut types::uint64, + atom!("uint32") => &mut types::uint32, + atom!("uint16") => &mut types::uint16, + atom!("uint8") => &mut types::uint8, + atom!("bool") => &mut types::sint8, + atom!("void") => &mut types::void, + atom!("cstr") => &mut types::pointer, + atom!("ptr") => &mut types::pointer, + atom!("f32") => &mut types::float, + atom!("f64") => &mut types::double, + struct_name => { + match self.structs.get_mut(struct_name.as_str()) { + Some(ref mut struct_type) => { + &mut struct_type.ffi_type + }, + None => unreachable!() + } + } + } + } + } + + pub(crate) fn load_library(&mut self, library_name: &str, functions: &Vec) -> Result<(), Box> { + let mut ff_table: ForeignFunctionTable = Default::default(); + unsafe { + let library = Library::new(library_name)?; + for function in functions { + let symbol_name: CString = CString::new(function.name.clone())?; + let code_ptr: Symbol<*mut c_void> = library.get(&symbol_name.into_bytes_with_nul())?; + let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(&x)).collect(); + let mut cif: ffi_cif = Default::default(); + prep_cif( + &mut cif, + ffi_abi_FFI_DEFAULT_ABI, + args.len(), + self.map_type_ffi(&function.return_value), + args.as_mut_ptr() + ).unwrap(); + + let return_struct_name = if (*self.map_type_ffi(&function.return_value)).type_ as u32 == libffi::raw::FFI_TYPE_STRUCT { + Some(function.return_value.as_str().to_string()) + } else { + None + }; + + ff_table.table.insert(function.name.clone(), FunctionImpl { + cif, + args, + code_ptr: CodePtr(code_ptr.into_raw().into_raw() as *mut _), + return_struct_name, + }); + } + std::mem::forget(library); + } + self.merge(ff_table); + Ok(()) + } + + fn build_pointer_args(mut args: &mut Vec, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap) -> Result { + let mut pointers = Vec::with_capacity(args.len()); + let mut memory = Vec::new(); + for i in 0..args.len() { + let field_type = type_args[i]; + unsafe { + match (*field_type).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => { + let n: u8 = u8::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_SINT8 => { + let n: i8 = i8::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_UINT16 => { + let n: u16 = u16::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_SINT16 => { + let n: i16 = i16::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_UINT32 => { + let n: u32 = u32::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_SINT32 => { + let n: i32 = i32::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_UINT64 => { + let n: u64 = u64::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_SINT64 => { + let n: i64 = args[i].as_int()?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_FLOAT => { + let n: f32 = args[i].as_float()? as f32; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_DOUBLE => { + let n: f64 = args[i].as_float()?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_POINTER => { + let ptr: *mut c_void = args[i].as_ptr()?; + pointers.push(ptr); + }, + libffi::raw::FFI_TYPE_STRUCT => { + match args[i] { + Value::Struct(ref name, ref struct_args) => { + if let Some(ref mut struct_type) = structs_table.get_mut(name) { + 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 mut field_ptr = ptr; + for i in 0..(struct_type.fields.len()-1) { + let field = struct_type.fields[i]; + match (*field).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => { + let n: u8 = u8::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut u8, n); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_UINT32 => { + let n: u32 = u32::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut u32, n); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_SINT32 => { + let n: u32 = u32::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut u32, n); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_FLOAT => { + let n: f32 = struct_args[i].as_float()? as f32; + std::ptr::write(field_ptr as *mut f32, n); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + _ => { + unreachable!() + } + } + } + pointers.push(ptr); + memory.push(Box::from_raw(ptr)); + } else { + return Err(FFIError::InvalidStructName); + } + } + _ => return Err(FFIError::ValueCast) + } + }, + _ => return Err(FFIError::InvalidFFIType) + } + } + } + Ok(PointerArgs { + pointers, + memory + }) + } + + pub fn exec(&mut self, name: &str, mut args: Vec) -> Result { + let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?; + let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs).unwrap(); + return unsafe { + match (*function_impl.cif.rtype).type_ as u32 { + libffi::raw::FFI_TYPE_VOID => { + let mut _n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *_n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(0)) + }, + libffi::raw::FFI_TYPE_SINT8 => { + let mut n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(i64::from(*n))) + }, + libffi::raw::FFI_TYPE_SINT32 => { + let mut n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(i64::from(*n))) + }, + libffi::raw::FFI_TYPE_STRUCT => { + let mut returns = Vec::new(); + let mut struct_type = self.structs.get_mut(&function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?).ok_or(FFIError::StructNotFound)?; + 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; + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *ptr as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + + let mut field_ptr = ptr; + for i in 0..(struct_type.fields.len()-1) { + let field = struct_type.fields[i]; + match (*field).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => { + let n = std::ptr::read(field_ptr as *mut u8); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_SINT32 => { + let n = std::ptr::read(field_ptr as *mut i32); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_UINT32 => { + let n = std::ptr::read(field_ptr as *mut u32); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + _ => { + unreachable!() + } + } + } + drop(Box::from_raw(ptr)); + Ok(Value::Struct("texture".into(), returns)) + }, + _ => unreachable!() + } + }; + } +} + +#[derive(Clone, Debug)] +pub enum Value { + Int(i64), + Float(f64), + CString(CString), + Struct(String, Vec), +} + +impl Value { + fn as_int(&self) -> Result { + match self { + Value::Int(n) => Ok(*n), + _ => Err(FFIError::ValueCast), + } + } + + fn as_float(&self) -> Result { + match self { + Value::Float(n) => Ok(*n), + Value::Int(n) => Ok(*n as f64), + _ => Err(FFIError::ValueCast), + } + } + + fn as_ptr(&mut self) -> Result<*mut c_void, FFIError> { + match self { + Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void), + Value::Int(n) => Ok(*n as *mut c_void), + _ => Err(FFIError::ValueCast) + } + } +} + +#[derive(Debug)] +pub enum FFIError { + ValueCast, + ValueDontFit, + InvalidFFIType, + InvalidStructName, + FunctionNotFound, + StructNotFound, +} diff --git a/src/lib.rs b/src/lib.rs index 2846fd0e..45dc2385 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ mod allocator; mod arithmetic; pub mod codegen; mod debray_allocator; +mod ffi; mod fixtures; mod forms; mod heap_iter; diff --git a/src/lib/ffi.pl b/src/lib/ffi.pl new file mode 100644 index 00000000..c34cb4ad --- /dev/null +++ b/src/lib/ffi.pl @@ -0,0 +1,83 @@ +:- module(ffi, [use_foreign_module/2, foreign_struct/2]). + +:- use_module(library(lists)). +:- use_module(library(error)). + +foreign_struct(Name, Elements) :- + '$define_foreign_struct'(Name, Elements). + +use_foreign_module(LibName, Predicates) :- + '$load_foreign_lib'(LibName, Predicates), + maplist(assert_predicate, Predicates). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, void], + length(Inputs, NumInputs), + functor(Head, Name, NumInputs), + term_variables(Head, TermList), + Body = ( + lists:maplist(ffi:check_input, Inputs, TermList), + '$foreign_call'(Name, TermList, _),! + ), + Predicate =.. [:-, Head, Body], + assertz(ffi:Predicate). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, bool], + length(Inputs, NumInputs), + functor(Head, Name, NumInputs), + term_variables(Head, TermList), + Body = ( + lists:maplist(ffi:check_input, Inputs, TermList), + '$foreign_call'(Name, TermList, 1),! + ), + Predicate =.. [:-, Head, Body], + assertz(ffi:Predicate). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, Return], + \+ member(Return, [void, bool]), + length(Inputs, NumInputs), + NumArgs is NumInputs + 1, + functor(Head, Name, NumArgs), + term_variables(Head, TermList), + Body = ( + lists:append(TermListInputs, [TermListReturn], TermList), + lists:maplist(ffi:check_input, Inputs, TermListInputs), + '$foreign_call'(Name, TermListInputs, TermListReturn),! + ), + Predicate =.. [:-, Head, Body], + assertz(ffi:Predicate). + +check_input(sint8, Var) :- + must_be(integer, Var), + ( + (Var > -129, Var < 128) -> + true + ; domain_error(integer_does_not_fit, Var, foreign_call/3) + ). +check_input(sint16, Var) :- + must_be(integer, Var), + ( + (Var > -32769, Var < 32768) -> + true + ; domain_error(integer_does_not_fit, Var, foreign_call/3) + ). +check_input(sint32, Var) :- + must_be(integer, Var), + ( + (Var > -2147483649, Var < 2147483648) -> + true + ; domain_error(integer_does_not_fit, Var, foreign_call/3) + ). +check_input(sint64, Var) :- + must_be(integer, Var). +check_input(f32, _Var). +check_input(f64, _Var). +check_input(cstr, Var) :- + must_be(chars, Var). +check_input(_, Var). +% must_be(list, Var). + +% TODO: assert native predicates. +% They MUST validate types diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 89fa348a..d8ace62d 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4221,6 +4221,30 @@ impl Machine { try_or_throw!(self.machine_st, self.http_answer()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallLoadForeignLib(_) => { + try_or_throw!(self.machine_st, self.load_foreign_lib()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteLoadForeignLib(_) => { + try_or_throw!(self.machine_st, self.load_foreign_lib()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallForeignCall(_) => { + try_or_throw!(self.machine_st, self.foreign_call()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteForeignCall(_) => { + try_or_throw!(self.machine_st, self.foreign_call()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallDefineForeignStruct(_) => { + try_or_throw!(self.machine_st, self.define_foreign_struct()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteDefineForeignStruct(_) => { + try_or_throw!(self.machine_st, self.define_foreign_struct()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallCurrentTime(_) => { self.current_time(); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index f18d9575..761590fb 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -236,7 +236,8 @@ impl Machine { user_output, user_error, load_contexts: vec![], - runtime + runtime, + foreign_function_table: Default::default(), }; let mut lib_path = current_dir(); diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 09883b99..77eb9c8b 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -26,6 +26,7 @@ use crate::arena::*; use crate::arithmetic::*; use crate::atom_table::*; use crate::forms::*; +use crate::ffi::ForeignFunctionTable; use crate::instructions::*; use crate::machine::args::*; use crate::machine::compile::*; @@ -65,6 +66,7 @@ pub struct Machine { pub(super) user_error: Stream, pub(super) load_contexts: Vec, pub(super) runtime: Runtime, + pub(super) foreign_function_table: ForeignFunctionTable, } #[derive(Debug)] @@ -443,6 +445,7 @@ impl Machine { user_error, load_contexts: vec![], runtime, + foreign_function_table: Default::default(), }; let mut lib_path = current_dir(); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 93dcdb09..c68a532e 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -6,6 +6,7 @@ use lazy_static::lazy_static; use crate::arena::*; use crate::atom_table::*; use crate::forms::*; +use crate::ffi::*; use crate::heap_iter::*; use crate::heap_print::*; use crate::http::{self, HttpListener, HttpResponse}; @@ -40,6 +41,7 @@ use std::cmp::Ordering; use std::collections::BTreeSet; use std::convert::{TryFrom, Infallible}; use std::env; +use std::ffi::CString; use std::fs; use std::hash::{BuildHasher, BuildHasherDefault}; use std::io::{ErrorKind, Read, Write}; @@ -4172,6 +4174,156 @@ impl Machine { Ok(()) } + #[inline(always)] + pub(crate) fn load_foreign_lib(&mut self) -> CallResult { + let library_name = self.deref_register(1); + let args_reg = self.deref_register(2); + if let Some(library_name) = self.machine_st.value_to_str_like(library_name) { + let stub_gen = || functor_stub(atom!("use_foreign_module"), 2); + match self.machine_st.try_from_list(args_reg, stub_gen) { + Ok(addrs) => { + let mut functions = Vec::new(); + for heap_cell in addrs { + read_heap_cell!(heap_cell, + (HeapCellValueTag::Str, s) => { + let name = cell_as_atom_cell!(self.machine_st.heap[s]).get_name(); + let args: Vec = match self.machine_st.try_from_list(self.machine_st.heap[s + 1], stub_gen) { + Ok(addrs) => { + let mut args = Vec::new(); + for heap_cell in addrs { + args.push(cell_as_atom_cell!(heap_cell).get_name()); + } + args + } + Err(e) => return Err(e) + }; + let return_value = cell_as_atom_cell!(self.machine_st.heap[s + 2]); + functions.push(FunctionDefinition { + name: name.as_str().to_string(), + args, + return_value: return_value.get_name(), + }); + } + _ => { + unreachable!() + } + ) + } + if let Ok(_) = self.foreign_function_table.load_library(library_name.as_str(), &functions) { + return Ok(()); + } + } + Err(e) => return Err(e) + }; + } + self.machine_st.fail = true; + Ok(()) + } + + #[inline(always)] + pub(crate) fn foreign_call(&mut self) -> CallResult { + let function_name = self.deref_register(1); + let args_reg = self.deref_register(2); + let return_value = self.deref_register(3); + if let Some(function_name) = self.machine_st.value_to_str_like(function_name) { + let stub_gen = || functor_stub(atom!("foreign_call"), 3); + fn map_arg(mut machine_st: &mut MachineState, source: HeapCellValue) -> crate::ffi::Value { + match Number::try_from(source) { + Ok(Number::Fixnum(n)) => { + Value::Int(n.get_num()) + }, + Ok(Number::Float(n)) => { + Value::Float(n.into_inner()) + }, + _ => { + let stub_gen = || functor_stub(atom!("foreign_call"), 3); + if let Some(string) = machine_st.value_to_str_like(source) { + Value::CString(CString::new(string.as_str()).unwrap()) + } else { + match machine_st.try_from_list(source, stub_gen) { + Ok(args) => { + let mut iter = args.into_iter(); + if let Some(struct_name) = machine_st.value_to_str_like(iter.next().unwrap()) { + Value::Struct(struct_name.as_str().to_string(), iter.map(|x| map_arg(&mut machine_st, x)).collect()) + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + } + } + } + } + } + match self.machine_st.try_from_list(args_reg, stub_gen) { + Ok(args) => { + let args: Vec<_> = args.into_iter().map(|x| map_arg(&mut self.machine_st, x)).collect(); + match self.foreign_function_table.exec(function_name.as_str(), args) { + Ok(result) => { + match result { + Value::Int(n) => self.machine_st.unify_fixnum(Fixnum::build_with(n), return_value), + Value::Struct(name, mut args) => { + args.insert(0, Value::CString(CString::new(name).unwrap())); + let struct_list = heap_loc_as_cell!( + 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!(); + } + } + return Ok(()); + }, + Err(e) => { + // throw error + self.machine_st.fail = true; + return Ok(()); + } + } + } + Err(e) => return Err(e) + } + } + self.machine_st.fail = true; + Ok(()) + } + + #[inline(always)] + pub(crate) fn define_foreign_struct(&mut self) -> CallResult { + let struct_name = self.deref_register(1); + let fields_reg = self.deref_register(2); + if let Some(struct_name) = self.machine_st.value_to_str_like(struct_name) { + let stub_gen = || functor_stub(atom!("define_foreign_struct"), 2); + let fields: Vec = match self.machine_st.try_from_list(fields_reg, stub_gen) { + Ok(addrs) => { + let mut args = Vec::new(); + for heap_cell in addrs { + args.push(cell_as_atom_cell!(heap_cell).get_name()); + } + args + } + Err(e) => return Err(e) + }; + self.foreign_function_table.define_struct(struct_name.as_str(), fields); + return Ok(()) + } + self.machine_st.fail = true; + Ok(()) + } + #[inline(always)] pub(crate) fn current_time(&mut self) { let timestamp = self.systemtime_to_timestamp(SystemTime::now()); -- 2.54.0