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::externs::printf;
use ivory::zend::{ExecuteData, ZVal, PhpVal};
use ivory::zend::{ExecuteData, PhpVal, ZVal};
#[ivory_export]
fn hello_other(_other: String) {
printf(format!("Hello ", ));
fn hello_other(other: String) {
printf(format!("Hello {}", other));
}
#[ivory_export]
@ -14,24 +14,9 @@ fn hello_world() {
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!({
name: "demo",
version: "0.0.1",
functions: &[hello_world, hello_other, dump],
functions: &[hello_world, hello_other],
info: &[("demo extension", "enabled")]
});

View file

@ -45,15 +45,41 @@ fn export_fn(item: ItemFn) -> TokenStream {
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(|(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))
});
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! {
#[no_mangle]
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;
}
@ -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 {
FnArg::Captured(cap) => {
let arg_type = cap.ty;
match cap.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!(),
_ => panic!()

View file

@ -53,7 +53,7 @@ impl Iterator for IntoArgIterator {
fn next(&mut self) -> Option<Self::Item> {
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;
Some(val)
} else {
@ -101,7 +101,7 @@ impl ZVal {
} else {
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 {
PhpVal::Undef => {}
_ => result.push((key, val))
@ -109,6 +109,20 @@ impl ZVal {
}
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)]
@ -162,25 +176,38 @@ impl Default for PhpVal {
}
}
impl From<*const ZVal> for PhpVal {
fn from(val: *const ZVal) -> Self {
let val = unsafe { &*val };
val.into()
impl From<PhpVal> for Option<i64> {
fn from(val: PhpVal) -> Self {
match val {
PhpVal::Long(val) => Some(val),
_ => None
}
}
}
impl From<&ZVal> for PhpVal {
fn from(val: &ZVal) -> Self {
match val.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 { val.as_i64() }),
ZValType::Double => PhpVal::Double(unsafe { val.as_f64() }),
ZValType::String => PhpVal::String(unsafe { val.as_str() }),
ZValType::Array => PhpVal::Array(unsafe { val.as_array() }),
_ => PhpVal::Undef
impl From<PhpVal> for Option<f64> {
fn from(val: PhpVal) -> Self {
match val {
PhpVal::Double(val) => Some(val),
_ => None
}
}
}
impl From<PhpVal> for Option<bool> {
fn from(val: PhpVal) -> Self {
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
}
}
}