]> Repositorios git - scryer-prolog.git/commitdiff
MVP of Foreign Function Interface
authorAdrián Arroyo Calle <[email protected]>
Wed, 22 Feb 2023 22:10:03 +0000 (23:10 +0100)
committerAdrián Arroyo Calle <[email protected]>
Wed, 22 Feb 2023 22:10:03 +0000 (23:10 +0100)
Cargo.lock
Cargo.toml
build/instructions_template.rs
src/ffi.rs [new file with mode: 0644]
src/lib.rs
src/lib/ffi.pl [new file with mode: 0644]
src/machine/dispatch.rs
src/machine/mock_wam.rs
src/machine/mod.rs
src/machine/system_calls.rs

index 63784cd92200cec371633a02611331e2fa82a0b6..b7c06dbc74709e9f75a67b3dc4d038e8d9cf49d3 100644 (file)
@@ -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",
index 6c98ea43421438d51db22250efca7e768dce97e7..206b7150fd95b40ec4281753726bcda82dc980f1 100644 (file)
@@ -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"
index 48166239d1153953bb2f40d8104b98aa37ab5817..806148158b2f529e22dcd099c8e309027b2490c4 100644 (file)
@@ -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 (file)
index 0000000..e8f2bc4
--- /dev/null
@@ -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<Atom>,
+}
+
+#[derive(Debug)]
+pub struct FunctionImpl {
+    cif: ffi_cif,
+    args: Vec<*mut ffi_type>,
+    code_ptr: CodePtr,
+    return_struct_name: Option<String>,
+}
+
+#[derive(Debug, Default)]
+pub struct ForeignFunctionTable {
+    table: HashMap<String, FunctionImpl>,
+    structs: HashMap<String, StructImpl>,
+}
+
+#[derive(Debug)]
+struct StructImpl {
+    ffi_type: ffi_type,
+    fields: Vec<*mut ffi_type>,
+}
+
+struct PointerArgs {
+    pointers: Vec<*mut c_void>,
+    memory: Vec<Box<dyn Any>>,
+}
+
+impl ForeignFunctionTable {
+    pub fn merge(&mut self, other: ForeignFunctionTable) {
+       self.table.extend(other.table);
+    }
+
+    pub fn define_struct(&mut self, name: &str, fields: Vec<Atom>) {
+       let mut fields: Vec<_> = fields.iter().map(|x| self.map_type_ffi(&x)).collect();
+       fields.push(std::ptr::null_mut::<ffi_type>());
+       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<FunctionDefinition>) -> Result<(), Box<dyn Error>> {
+       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<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 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<dyn Any>;
+                       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<dyn Any>;
+                       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<dyn Any>;
+                       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<dyn Any>;
+                       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<dyn Any>;
+                       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<dyn Any>;
+                       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<dyn Any>;
+                       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<dyn Any>;
+                       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<dyn Any>;
+                       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<dyn Any>;
+                       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::<u8>());
+                                           },
+                                           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::<u32>());
+                                           },
+                                           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::<u32>());
+                                           },                                      
+                                           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::<f32>());
+                                           },
+                                           _ => {
+                                               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<Value>) -> Result<Value, FFIError> {
+       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<i32> = 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<i8> = 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<i32> = 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::<u8>());
+                           },
+                           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::<i32>());
+                           },
+                           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::<u32>());
+                           },
+                           _ => {
+                               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<Value>),
+}
+
+impl Value {
+    fn as_int(&self) -> Result<i64, FFIError> {
+       match self {
+           Value::Int(n) => Ok(*n),
+           _ => Err(FFIError::ValueCast),
+       }
+    }
+
+    fn as_float(&self) -> Result<f64, FFIError> {
+       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,
+}
index 2846fd0e64448dbfe4ad778e2cb26cf801153a2e..45dc23856cd9fa0dd73c4f00c4df6c54707275e9 100644 (file)
@@ -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 (file)
index 0000000..c34cb4a
--- /dev/null
@@ -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
index 89fa348a8f5166ec3ab4647fff9d2791f362e04d..d8ace62d6886d682e64fb2834976f5601350f39b 100644 (file)
@@ -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);
index f18d957589f34aea98b511090a7ecfaed242d251..761590fbb38ad776e2648618a9b30274edca0e49 100644 (file)
@@ -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();
index 09883b99bbd60ac872a0da9ce9827002b1b7518b..77eb9c8b1182f758619bd4ca9f348ddf98410bb3 100644 (file)
@@ -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<LoadContext>,
     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();
index 93dcdb092fd08e93a8f009af05cb716bd2d2af4d..c68a532e9779090f456c35c481312de664208343 100644 (file)
@@ -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<Atom> = 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<Atom> = 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());