make imported functions work

This commit is contained in:
Robin Appelman 2019-04-05 14:24:29 +02:00
commit 56d69458a8
6 changed files with 102 additions and 71 deletions

View file

@ -1,14 +1,16 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::sync::Mutex; use std::sync::Mutex;
use crate::FunctionDefinition;
lazy_static! { lazy_static! {
static ref FUNCTION_NAMES: Mutex<Vec<String>> = Mutex::new(Vec::new()); static ref FUNCTION_NAMES: Mutex<Vec<FunctionDefinition>> = Mutex::new(Vec::new());
} }
pub fn cache_function(name: String) { pub(crate) fn cache_function(func: FunctionDefinition) {
FUNCTION_NAMES.lock().unwrap().push(name); FUNCTION_NAMES.lock().unwrap().push(func);
} }
pub fn get_functions() -> Vec<String> { pub(crate) fn get_functions() -> Vec<FunctionDefinition> {
FUNCTION_NAMES.lock().unwrap().clone() FUNCTION_NAMES.lock().unwrap().clone()
} }

View file

@ -29,27 +29,39 @@ pub fn ivory_export(
output output
} }
#[derive(Clone)]
pub(crate) struct ArgumentDefinition {
name: String,
is_ref: bool,
}
#[derive(Clone)]
pub(crate) struct FunctionDefinition {
name: String,
args: Vec<ArgumentDefinition>,
}
fn export_fn(item: ItemFn) -> TokenStream { fn export_fn(item: ItemFn) -> TokenStream {
let span = item.span(); let span = item.span();
let name = item.ident; let name = item.ident;
let name_str = name.to_string(); let name_str = name.to_string();
cache::cache_function(name_str.clone());
let meta_name = Ident::new(&format!("FUNCTION_META_{}", name_str.to_uppercase()), span);
let body = item.block; let body = item.block;
let decl = item.decl; let decl = item.decl;
if decl.generics.gt_token.is_some() { if decl.generics.gt_token.is_some() {
unimplemented!("generics are not supported for exported functions"); unimplemented!("generics are not supported for exported functions");
} }
let args: Vec<(String, Type, bool, Span)> = decl.inputs.into_iter().map(get_arg_info).collect(); let args: Vec<(ArgumentDefinition, Type)> = decl.inputs.into_iter().map(get_arg_info).collect();
let arg_defs: Vec<ArgumentDefinition> = args.clone().into_iter().map(|(def, _)| def).collect();
let func_def = FunctionDefinition {
name: name_str.clone(),
args: arg_defs,
};
cache::cache_function(func_def);
let arg_count = args.len() as u32; let arg_count = args.len() as u32;
let arg_defs = args.iter().map(|(name, _type, is_ref, _)| { let arg_cast = args.iter().enumerate().map(|(_index, (arg, ty))| {
quote!(::ivory::zend::ArgInfo::new(::ivory::c_str!(#name), false, false, #is_ref)) let arg_ident = Ident::new(&arg.name, span);
});
let arg_cast = args.iter().enumerate().map(|(_index, (name, ty, _is_ref, span))| {
let arg_ident = Ident::new(name, span.clone());
quote!( quote!(
let #arg_ident: #ty = { let #arg_ident: #ty = {
let result: Result<#ty, ::ivory::CastError> = args.next().unwrap().into(); let result: Result<#ty, ::ivory::CastError> = args.next().unwrap().into();
@ -78,25 +90,20 @@ fn export_fn(item: ItemFn) -> TokenStream {
#(#arg_cast);* #(#arg_cast);*
let result = #body; let result = #body;
} }
const #meta_name: ::ivory::zend::FunctionMeta = ::ivory::zend::FunctionMeta{
name: {concat!(#name_str, "\0").as_ptr() as *const ::std::os::raw::c_char},
func: #name,
args: &[ #(#arg_defs),*]
};
} }
} }
fn get_arg_info(arg: FnArg) -> (String, Type, bool, Span) { fn get_arg_info(arg: FnArg) -> (ArgumentDefinition, Type) {
match arg { match arg {
FnArg::Captured(cap) => { FnArg::Captured(cap) => {
let arg_type = cap.ty; let arg_type = cap.ty;
match cap.pat { match cap.pat {
Pat::Ident(ident_pat) => ( Pat::Ident(ident_pat) => (
ident_pat.ident.to_string(), ArgumentDefinition {
name: ident_pat.ident.to_string(),
is_ref: ident_pat.by_ref.is_some(),
},
arg_type, arg_type,
ident_pat.by_ref.is_some(),
ident_pat.span(),
), ),
Pat::Ref(_ref_pat) => unimplemented!(), Pat::Ref(_ref_pat) => unimplemented!(),
_ => panic!(), _ => panic!(),
@ -134,20 +141,16 @@ pub fn ivory_module(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
::ivory::info::php_print_module_info(&MODULE_INFO.info); ::ivory::info::php_print_module_info(&MODULE_INFO.info);
} }
#funcs
#[no_mangle] #[no_mangle]
pub extern "C" fn get_module() -> *mut ::ivory::zend::ModuleInternal { pub extern "C" fn get_module() -> *mut ::ivory::zend::ModuleInternal {
let mut entry = Box::new(::ivory::zend::ModuleInternal::new(MODULE_INFO.name, MODULE_INFO.version)); let mut entry = Box::new(::ivory::zend::ModuleInternal::new(MODULE_INFO.name, MODULE_INFO.version));
entry.set_info_func(php_module_info); entry.set_info_func(php_module_info);
let args = vec![
::ivory::zend::ArgInfo::new(::ivory::c_str!("name"), false, false, false),
::ivory::zend::ArgInfo::new(::ivory::c_str!("foo"), false, false, false),
];
#funcs; entry.set_functions(&IVORY_FUCTIONS);
entry.set_functions(funcs);
Box::into_raw(entry) Box::into_raw(entry)
} }
@ -188,17 +191,44 @@ fn into_c_str(input: TokenStream) -> TokenStream {
output output
} }
fn get_funcs(names: Vec<String>, span: Span) -> TokenStream { fn get_funcs(funcs: Vec<FunctionDefinition>, span: Span) -> TokenStream {
let definitions = names.into_iter().map(|name| { let func_count = funcs.len() + 1;
let meta_name = Ident::new(&format!("FUNCTION_META_{}", name.to_uppercase()), span);
quote! { let definitions = funcs.into_iter().map(|func| {
#meta_name.as_function() let name = func.name;
let name_ident = Ident::new(&name, span.clone());
let num_args = func.args.len();
let arg_defs = func.args.iter().map(|arg| {
let name = &arg.name;
let is_ref = &arg.is_ref;
quote!(::ivory::zend::ArgInfo::new(::ivory::c_str!(#name), false, false, #is_ref))
});
if num_args > 0 {
quote! {
::ivory::zend::Function::new_with_args(
{concat!(#name, "\0").as_ptr() as *const ::std::os::raw::c_char},
#name_ident as *const ::std::os::raw::c_void,
&[::ivory::zend::ArgInfo::new(#num_args as *const ::std::os::raw::c_char, false, false, false),
#(#arg_defs),*
],
#num_args as u32
)
}
} else {
quote! {
::ivory::zend::Function::new(
{concat!(#name, "\0").as_ptr() as *const ::std::os::raw::c_char},
#name_ident as *const ::std::os::raw::c_void,
)
}
} }
}); });
quote! { quote! {
let funcs = vec![ const IVORY_FUCTIONS: [::ivory::zend::Function; #func_count] = [
#(#definitions),* #(#definitions),*,
::ivory::zend::Function::end()
]; ];
} }
} }

View file

@ -1,17 +1,15 @@
use std; use std;
use std::os::raw::{c_char, c_uchar}; use std::os::raw::{c_char, c_uchar, c_void};
use crate::zend::HandlerFunc;
#[derive(Clone)] #[derive(Clone)]
#[repr(C)] #[repr(C)]
pub struct ArgInfo { pub struct ArgInfo {
name: *const c_char, pub name: *const c_char,
class_name: *const c_char, pub class_name: *const c_char,
type_hint: c_uchar, pub type_hint: c_uchar,
pass_by_reference: c_uchar, pub pass_by_reference: c_uchar,
allow_null: c_uchar, pub allow_null: c_uchar,
is_variadic: c_uchar, pub is_variadic: c_uchar,
} }
impl ArgInfo { impl ArgInfo {
@ -35,50 +33,44 @@ impl ArgInfo {
#[repr(C)] #[repr(C)]
pub struct Function { pub struct Function {
fname: *const c_char, fname: *const c_char,
handler: Option<HandlerFunc>, handler: *const c_void,
arg_info: *const ArgInfo, arg_info: *const ArgInfo,
num_args: u32, num_args: u32,
flags: u32, flags: u32,
} }
impl Function { impl Function {
pub fn new(name: *const c_char, handler: HandlerFunc) -> Function { pub const fn new(name: *const c_char, handler: *const c_void) -> Function {
Function { Function {
fname: name, fname: name,
handler: Some(handler), handler,
arg_info: std::ptr::null(), arg_info: std::ptr::null(),
num_args: 0, num_args: 0,
flags: 0, flags: 0,
} }
} }
pub fn new_with_args( pub const fn new_with_args(
name: *const c_char, name: *const c_char,
handler: HandlerFunc, handler: *const c_void,
args: &'static [ArgInfo], args: &'static [ArgInfo],
num_args: u32,
) -> Function { ) -> Function {
let num_args = args.len() as u32; let arg_ptr = args.as_ptr();
let mut args_vec = Vec::new();
let arg_count = ArgInfo::new(num_args as *const c_char, false, false, false);
args_vec.push(arg_count);
args_vec.extend_from_slice(args);
let arg_ptr = Box::into_raw(args_vec.into_boxed_slice()) as *const ArgInfo;
Function { Function {
fname: name, fname: name,
handler: Some(handler), handler,
arg_info: arg_ptr, arg_info: arg_ptr,
num_args, num_args,
flags: 0, flags: 0,
} }
} }
pub(crate) fn end() -> Function { pub const fn end() -> Function {
Function { Function {
fname: std::ptr::null(), fname: std::ptr::null(),
handler: None, handler: std::ptr::null(),
arg_info: std::ptr::null(), arg_info: std::ptr::null(),
num_args: 0, num_args: 0,
flags: 0, flags: 0,

View file

@ -3,7 +3,6 @@ use std::mem;
use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void}; use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void};
use crate::zend::function::{ArgInfo, Function}; use crate::zend::function::{ArgInfo, Function};
use crate::zend::{ExecuteData, ZVal};
pub(crate) type StartupFunc = extern "C" fn(type_: c_int, module_number: c_int) -> c_int; pub(crate) type StartupFunc = extern "C" fn(type_: c_int, module_number: c_int) -> c_int;
pub(crate) type ShutdownFunc = extern "C" fn(type_: c_int, module_number: c_int) -> c_int; pub(crate) type ShutdownFunc = extern "C" fn(type_: c_int, module_number: c_int) -> c_int;
@ -11,7 +10,6 @@ pub(crate) type InfoFunc = extern "C" fn();
pub(crate) type GlobalsCtorFunc = extern "C" fn(global: *const c_void) -> c_void; pub(crate) type GlobalsCtorFunc = extern "C" fn(global: *const c_void) -> c_void;
pub(crate) type GlobalsDtorFunc = extern "C" fn(global: *const c_void) -> c_void; pub(crate) type GlobalsDtorFunc = extern "C" fn(global: *const c_void) -> c_void;
pub(crate) type PostDeactivateFunc = extern "C" fn() -> c_int; pub(crate) type PostDeactivateFunc = extern "C" fn() -> c_int;
pub(crate) type HandlerFunc = extern "C" fn(execute_data: *const ExecuteData, retval: *mut ZVal);
pub struct ModuleDep {} pub struct ModuleDep {}
@ -87,24 +85,24 @@ impl ModuleInternal {
self.info_func = Some(func); self.info_func = Some(func);
} }
pub fn set_functions(&mut self, mut funcs: Vec<Function>) { pub fn set_functions(&mut self, funcs: &'static [Function]) {
funcs.push(Function::end()); self.functions = funcs.as_ptr();
self.functions = Box::into_raw(funcs.into_boxed_slice()) as *const Function;
} }
} }
pub struct FunctionMeta { pub struct FunctionMeta {
pub name: *const c_char, pub name: *const c_char,
pub func: HandlerFunc, pub func: *const c_void,
pub args: &'static [ArgInfo], pub args: &'static [ArgInfo],
} }
impl FunctionMeta { impl FunctionMeta {
pub fn as_function(&self) -> Function { pub fn as_function(&self) -> Function {
if self.args.len() == 0 { if self.args.len() == 1 {
// first arg is argument count which is always added
Function::new(self.name, self.func) Function::new(self.name, self.func)
} else { } else {
Function::new_with_args(self.name, self.func, self.args) Function::new_with_args(self.name, self.func, self.args, self.args.len() as u32 - 1)
} }
} }
} }

7
tests/src/imported.rs Normal file
View file

@ -0,0 +1,7 @@
use ivory::externs::printf;
use ivory::ivory_export;
#[ivory_export]
fn imported_fn() {
printf("imported");
}

View file

@ -1,9 +1,12 @@
use std::fmt::Debug; use std::fmt::Debug;
use crate::imported::imported_fn;
use ivory::externs::printf; use ivory::externs::printf;
use ivory::PhpVal; use ivory::PhpVal;
use ivory::{ivory_export, ivory_module}; use ivory::{ivory_export, ivory_module};
mod imported;
fn dump<T: Debug>(arg: T) { fn dump<T: Debug>(arg: T) {
printf(format!("{:?}", arg)); printf(format!("{:?}", arg));
} }
@ -37,7 +40,6 @@ fn expect_bool(arg: bool) {
fn expect_option_bool(arg: Option<bool>) { fn expect_option_bool(arg: Option<bool>) {
dump(arg); dump(arg);
} }
ivory_module!({ ivory_module!({
name: "tests", name: "tests",
version: "0.0.1", version: "0.0.1",