argument unpacking

This commit is contained in:
Robin Appelman 2019-03-27 22:27:33 +01:00
commit d1f2500b31
3 changed files with 79 additions and 41 deletions

View file

@ -2,11 +2,11 @@ use std::intrinsics::transmute;
use ivory::*; use ivory::*;
use ivory::externs::printf; use ivory::externs::printf;
use ivory::zend::{ExecuteData, ZVal, PhpVal}; use ivory::zend::{ExecuteData, PhpVal, ZVal};
#[ivory_export] #[ivory_export]
fn hello_other(_other: String) { fn hello_other(other: String) {
printf(format!("Hello ", )); printf(format!("Hello {}", other));
} }
#[ivory_export] #[ivory_export]
@ -14,24 +14,9 @@ fn hello_world() {
printf("Hello world, Rust2!"); printf("Hello world, Rust2!");
} }
#[no_mangle]
pub extern "C" fn dump(data: *const ExecuteData, retval: *mut ZVal) {
let data: &ExecuteData = unsafe { data.as_ref() }.unwrap();
for arg in data.args() {
printf(format!("{:?}\n", arg));
}
}
const FUNCTION_META_DUMP: ::ivory::zend::FunctionMeta = ::ivory::zend::FunctionMeta {
name: { concat!("dump", "\0").as_ptr() as *const ::libc::c_char },
func: dump,
args: &[],
};
ivory_module!({ ivory_module!({
name: "demo", name: "demo",
version: "0.0.1", version: "0.0.1",
functions: &[hello_world, hello_other, dump], functions: &[hello_world, hello_other],
info: &[("demo extension", "enabled")] info: &[("demo extension", "enabled")]
}); });

View file

@ -45,15 +45,41 @@ fn export_fn(item: ItemFn) -> TokenStream {
unimplemented!("generics are not supported for exported functions"); unimplemented!("generics are not supported for exported functions");
} }
let arg_defs = decl.inputs.into_iter() let args: Vec<(String, Type, bool, Span)> = decl.inputs.into_iter()
.map(get_arg_info) .map(get_arg_info)
.map(|(name, _type, is_ref)| { .collect();
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)) 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());
quote!(
let #arg_ident: #ty = {
let opt: Option<#ty> = args.remove(0).into();
match opt {
Some(val) => val,
None => {
::ivory::externs::printf("invalid argument type");
return;
}
}
};
)
});
quote! { quote! {
#[no_mangle] #[no_mangle]
pub extern "C" fn #name(data: *const ::ivory::zend::ExecuteData, retval: *mut ::ivory::zend::ZVal) { pub extern "C" fn #name(data: *const ::ivory::zend::ExecuteData, retval: *mut ::ivory::zend::ZVal) {
let data: &::ivory::zend::ExecuteData = unsafe { data.as_ref() }.unwrap();
if data.num_args() != #arg_count {
::ivory::externs::printf("unexpected number of arguments");
return;
}
let mut args: Vec<PhpVal> = data.args().collect();
#(#arg_cast);*
let result = #body; let result = #body;
} }
@ -65,13 +91,13 @@ fn export_fn(item: ItemFn) -> TokenStream {
} }
} }
fn get_arg_info(arg: FnArg) -> (String, Type, bool) { fn get_arg_info(arg: FnArg) -> (String, Type, bool, Span) {
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(), arg_type, ident_pat.by_ref.is_some()) (ident_pat.ident.to_string(), arg_type, ident_pat.by_ref.is_some(), ident_pat.span())
}, },
Pat::Ref(ref_pat) => unimplemented!(), Pat::Ref(ref_pat) => unimplemented!(),
_ => panic!() _ => panic!()

View file

@ -53,7 +53,7 @@ impl Iterator for IntoArgIterator {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.item < self.count { if self.item < self.count {
let val = unsafe { (self.base.add(self.item as usize)).into() }; let val = unsafe { (*self.base.add(self.item as usize)).as_php_val() };
self.item += 1; self.item += 1;
Some(val) Some(val)
} else { } else {
@ -101,7 +101,7 @@ impl ZVal {
} else { } else {
ArrayKey::String(zend_str_as_string(&*elem.key)) ArrayKey::String(zend_str_as_string(&*elem.key))
}; };
let val: PhpVal = (&ZVal(elem.val)).into(); let val: PhpVal = ZVal(elem.val).as_php_val();
match val { match val {
PhpVal::Undef => {} PhpVal::Undef => {}
_ => result.push((key, val)) _ => result.push((key, val))
@ -109,6 +109,20 @@ impl ZVal {
} }
result result
} }
pub fn as_php_val(&self) -> PhpVal {
match self.get_type() {
ZValType::Undef => PhpVal::Undef,
ZValType::Null => PhpVal::Null,
ZValType::False => PhpVal::Bool(false),
ZValType::True => PhpVal::Bool(true),
ZValType::Long => PhpVal::Long(unsafe { self.as_i64() }),
ZValType::Double => PhpVal::Double(unsafe { self.as_f64() }),
ZValType::String => PhpVal::String(unsafe { self.as_str() }),
ZValType::Array => PhpVal::Array(unsafe { self.as_array() }),
_ => PhpVal::Undef
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -162,25 +176,38 @@ impl Default for PhpVal {
} }
} }
impl From<*const ZVal> for PhpVal { impl From<PhpVal> for Option<i64> {
fn from(val: *const ZVal) -> Self { fn from(val: PhpVal) -> Self {
let val = unsafe { &*val }; match val {
val.into() PhpVal::Long(val) => Some(val),
_ => None
}
} }
} }
impl From<&ZVal> for PhpVal { impl From<PhpVal> for Option<f64> {
fn from(val: &ZVal) -> Self { fn from(val: PhpVal) -> Self {
match val.get_type() { match val {
ZValType::Undef => PhpVal::Undef, PhpVal::Double(val) => Some(val),
ZValType::Null => PhpVal::Null, _ => None
ZValType::False => PhpVal::Bool(false), }
ZValType::True => PhpVal::Bool(true), }
ZValType::Long => PhpVal::Long(unsafe { val.as_i64() }), }
ZValType::Double => PhpVal::Double(unsafe { val.as_f64() }),
ZValType::String => PhpVal::String(unsafe { val.as_str() }), impl From<PhpVal> for Option<bool> {
ZValType::Array => PhpVal::Array(unsafe { val.as_array() }), fn from(val: PhpVal) -> Self {
_ => PhpVal::Undef match val {
PhpVal::Bool(val) => Some(val),
_ => None
}
}
}
impl From<PhpVal> for Option<String> {
fn from(val: PhpVal) -> Self {
match val {
PhpVal::String(val) => Some(val),
_ => None
} }
} }
} }