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 std::sync::Mutex;
use crate::FunctionDefinition;
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) {
FUNCTION_NAMES.lock().unwrap().push(name);
pub(crate) fn cache_function(func: FunctionDefinition) {
FUNCTION_NAMES.lock().unwrap().push(func);
}
pub fn get_functions() -> Vec<String> {
pub(crate) fn get_functions() -> Vec<FunctionDefinition> {
FUNCTION_NAMES.lock().unwrap().clone()
}

View file

@ -29,27 +29,39 @@ pub fn ivory_export(
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 {
let span = item.span();
let name = item.ident;
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 decl = item.decl;
if decl.generics.gt_token.is_some() {
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_defs = args.iter().map(|(name, _type, is_ref, _)| {
quote!(::ivory::zend::ArgInfo::new(::ivory::c_str!(#name), false, false, #is_ref))
});
let arg_cast = args.iter().enumerate().map(|(_index, (name, ty, _is_ref, span))| {
let arg_ident = Ident::new(name, span.clone());
let arg_cast = args.iter().enumerate().map(|(_index, (arg, ty))| {
let arg_ident = Ident::new(&arg.name, span);
quote!(
let #arg_ident: #ty = {
let result: Result<#ty, ::ivory::CastError> = args.next().unwrap().into();
@ -78,25 +90,20 @@ fn export_fn(item: ItemFn) -> TokenStream {
#(#arg_cast);*
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 {
FnArg::Captured(cap) => {
let arg_type = cap.ty;
match cap.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,
ident_pat.by_ref.is_some(),
ident_pat.span(),
),
Pat::Ref(_ref_pat) => unimplemented!(),
_ => 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);
}
#funcs
#[no_mangle]
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));
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(funcs);
entry.set_functions(&IVORY_FUCTIONS);
Box::into_raw(entry)
}
@ -188,17 +191,44 @@ fn into_c_str(input: TokenStream) -> TokenStream {
output
}
fn get_funcs(names: Vec<String>, span: Span) -> TokenStream {
let definitions = names.into_iter().map(|name| {
let meta_name = Ident::new(&format!("FUNCTION_META_{}", name.to_uppercase()), span);
quote! {
#meta_name.as_function()
fn get_funcs(funcs: Vec<FunctionDefinition>, span: Span) -> TokenStream {
let func_count = funcs.len() + 1;
let definitions = funcs.into_iter().map(|func| {
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! {
let funcs = vec![
#(#definitions),*
const IVORY_FUCTIONS: [::ivory::zend::Function; #func_count] = [
#(#definitions),*,
::ivory::zend::Function::end()
];
}
}

View file

@ -1,17 +1,15 @@
use std;
use std::os::raw::{c_char, c_uchar};
use crate::zend::HandlerFunc;
use std::os::raw::{c_char, c_uchar, c_void};
#[derive(Clone)]
#[repr(C)]
pub struct ArgInfo {
name: *const c_char,
class_name: *const c_char,
type_hint: c_uchar,
pass_by_reference: c_uchar,
allow_null: c_uchar,
is_variadic: c_uchar,
pub name: *const c_char,
pub class_name: *const c_char,
pub type_hint: c_uchar,
pub pass_by_reference: c_uchar,
pub allow_null: c_uchar,
pub is_variadic: c_uchar,
}
impl ArgInfo {
@ -35,50 +33,44 @@ impl ArgInfo {
#[repr(C)]
pub struct Function {
fname: *const c_char,
handler: Option<HandlerFunc>,
handler: *const c_void,
arg_info: *const ArgInfo,
num_args: u32,
flags: u32,
}
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 {
fname: name,
handler: Some(handler),
handler,
arg_info: std::ptr::null(),
num_args: 0,
flags: 0,
}
}
pub fn new_with_args(
pub const fn new_with_args(
name: *const c_char,
handler: HandlerFunc,
handler: *const c_void,
args: &'static [ArgInfo],
num_args: u32,
) -> Function {
let num_args = args.len() as u32;
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;
let arg_ptr = args.as_ptr();
Function {
fname: name,
handler: Some(handler),
handler,
arg_info: arg_ptr,
num_args,
flags: 0,
}
}
pub(crate) fn end() -> Function {
pub const fn end() -> Function {
Function {
fname: std::ptr::null(),
handler: None,
handler: std::ptr::null(),
arg_info: std::ptr::null(),
num_args: 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 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 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 GlobalsDtorFunc = extern "C" fn(global: *const c_void) -> c_void;
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 {}
@ -87,24 +85,24 @@ impl ModuleInternal {
self.info_func = Some(func);
}
pub fn set_functions(&mut self, mut funcs: Vec<Function>) {
funcs.push(Function::end());
self.functions = Box::into_raw(funcs.into_boxed_slice()) as *const Function;
pub fn set_functions(&mut self, funcs: &'static [Function]) {
self.functions = funcs.as_ptr();
}
}
pub struct FunctionMeta {
pub name: *const c_char,
pub func: HandlerFunc,
pub func: *const c_void,
pub args: &'static [ArgInfo],
}
impl FunctionMeta {
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)
} 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 crate::imported::imported_fn;
use ivory::externs::printf;
use ivory::PhpVal;
use ivory::{ivory_export, ivory_module};
mod imported;
fn dump<T: Debug>(arg: T) {
printf(format!("{:?}", arg));
}
@ -37,7 +40,6 @@ fn expect_bool(arg: bool) {
fn expect_option_bool(arg: Option<bool>) {
dump(arg);
}
ivory_module!({
name: "tests",
version: "0.0.1",