mirror of
https://github.com/icewind1991/ivory.git
synced 2026-06-03 18:54:07 +02:00
return type casting for arrays
This commit is contained in:
parent
1ec6938bfa
commit
f20c2b5fd3
6 changed files with 108 additions and 15 deletions
|
|
@ -7,7 +7,9 @@ mod cache;
|
||||||
use proc_macro2::{Span, TokenStream, TokenTree};
|
use proc_macro2::{Span, TokenStream, TokenTree};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{parse_macro_input, AttributeArgs, FnArg, Ident, Item, ItemFn, LitStr, Pat, Type};
|
use syn::{
|
||||||
|
parse_macro_input, AttributeArgs, FnArg, Ident, Item, ItemFn, LitStr, Pat, ReturnType, Type,
|
||||||
|
};
|
||||||
|
|
||||||
/// See the [crate documentation](index.html) for details
|
/// See the [crate documentation](index.html) for details
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
|
|
@ -76,6 +78,12 @@ fn export_fn(item: ItemFn) -> TokenStream {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let return_type = decl.output;
|
||||||
|
let return_type = match return_type {
|
||||||
|
ReturnType::Default => quote!(),
|
||||||
|
ReturnType::Type(_, return_type) => quote!(: #return_type),
|
||||||
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn #name(data: *const ::ivory::zend::ExecuteData, retval: *mut ::ivory::zend::ZVal) {
|
pub unsafe extern "C" fn #name(data: *const ::ivory::zend::ExecuteData, retval: *mut ::ivory::zend::ZVal) {
|
||||||
|
|
@ -88,7 +96,7 @@ fn export_fn(item: ItemFn) -> TokenStream {
|
||||||
}
|
}
|
||||||
let mut args = data.args();
|
let mut args = data.args();
|
||||||
#(#arg_cast);*
|
#(#arg_cast);*
|
||||||
let result = #body;
|
let result #return_type = #body;
|
||||||
|
|
||||||
let php_val = ::ivory::PhpVal::from(result);
|
let php_val = ::ivory::PhpVal::from(result);
|
||||||
let zval = ::ivory::zend::ZVal::from(php_val);
|
let zval = ::ivory::zend::ZVal::from(php_val);
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,12 @@ impl From<PhpVal> for Result<PhpVal, CastError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_from_phpval {
|
macro_rules! impl_from_phpval {
|
||||||
($type:ty, $variant:ident) => {
|
($type:ty, $variant:ident, $type2:ty) => {
|
||||||
// non nullable version
|
// non nullable version
|
||||||
impl From<PhpVal> for Result<$type, CastError> {
|
impl From<PhpVal> for Result<$type2, CastError> {
|
||||||
fn from(val: PhpVal) -> Self {
|
fn from(val: PhpVal) -> Self {
|
||||||
match val {
|
match val {
|
||||||
PhpVal::$variant(val) => Ok(val),
|
PhpVal::$variant(val) => Ok(val as $type2),
|
||||||
_ => Err(CastError {
|
_ => Err(CastError {
|
||||||
actual: val.get_type(),
|
actual: val.get_type(),
|
||||||
}),
|
}),
|
||||||
|
|
@ -87,12 +87,12 @@ macro_rules! impl_from_phpval {
|
||||||
}
|
}
|
||||||
|
|
||||||
// nullable version
|
// nullable version
|
||||||
impl From<PhpVal> for Result<Option<$type>, CastError> {
|
impl From<PhpVal> for Result<Option<$type2>, CastError> {
|
||||||
fn from(val: PhpVal) -> Self {
|
fn from(val: PhpVal) -> Self {
|
||||||
match val {
|
match val {
|
||||||
PhpVal::Null => Ok(None),
|
PhpVal::Null => Ok(None),
|
||||||
PhpVal::Undef => Ok(None),
|
PhpVal::Undef => Ok(None),
|
||||||
PhpVal::$variant(val) => Ok(Some(val)),
|
PhpVal::$variant(val) => Ok(Some(val as $type2)),
|
||||||
_ => Err(CastError {
|
_ => Err(CastError {
|
||||||
actual: val.get_type(),
|
actual: val.get_type(),
|
||||||
}),
|
}),
|
||||||
|
|
@ -100,18 +100,26 @@ macro_rules! impl_from_phpval {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<$type> for PhpVal {
|
impl From<$type2> for PhpVal {
|
||||||
fn from(input: $type) -> Self {
|
fn from(input: $type2) -> Self {
|
||||||
PhpVal::$variant(input)
|
PhpVal::$variant(input as $type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_from_phpval!(i64, Long);
|
impl_from_phpval!(i64, Long, i64);
|
||||||
impl_from_phpval!(f64, Double);
|
impl_from_phpval!(i64, Long, i32);
|
||||||
impl_from_phpval!(bool, Bool);
|
impl_from_phpval!(i64, Long, i16);
|
||||||
impl_from_phpval!(String, String);
|
impl_from_phpval!(i64, Long, i8);
|
||||||
|
impl_from_phpval!(i64, Long, u64);
|
||||||
|
impl_from_phpval!(i64, Long, u32);
|
||||||
|
impl_from_phpval!(i64, Long, u16);
|
||||||
|
impl_from_phpval!(i64, Long, u8);
|
||||||
|
impl_from_phpval!(f64, Double, f64);
|
||||||
|
impl_from_phpval!(f64, Double, f32);
|
||||||
|
impl_from_phpval!(bool, Bool, bool);
|
||||||
|
impl_from_phpval!(String, String, String);
|
||||||
|
|
||||||
impl From<()> for PhpVal {
|
impl From<()> for PhpVal {
|
||||||
fn from(_input: ()) -> Self {
|
fn from(_input: ()) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,36 @@ impl From<PhpVal> for ZVal {
|
||||||
u1: ty.into(),
|
u1: ty.into(),
|
||||||
u2: _zval_struct__bindgen_ty_2 { extra: 0 },
|
u2: _zval_struct__bindgen_ty_2 { extra: 0 },
|
||||||
}),
|
}),
|
||||||
|
PhpVal::Array(vec) => {
|
||||||
|
// unlike reading, when creating a zval array all the extra bits actually matter
|
||||||
|
// so we just use zend's builtin methods for creating arrays
|
||||||
|
unsafe {
|
||||||
|
let map: *mut _zend_array = _zend_new_array(vec.len() as u32);
|
||||||
|
let mut arr = zval {
|
||||||
|
value: zend_value { arr: map },
|
||||||
|
u1: ty.into(),
|
||||||
|
u2: _zval_struct__bindgen_ty_2 { extra: 0 },
|
||||||
|
};
|
||||||
|
let arr_ptr: *mut zval = &mut arr;
|
||||||
|
for (key, val) in vec.into_iter() {
|
||||||
|
let val_ptr: *mut zval =
|
||||||
|
Box::into_raw(Box::new(ZVal::from(val))) as *mut zval;
|
||||||
|
match key {
|
||||||
|
ArrayKey::Int(index) => {
|
||||||
|
add_index_zval(arr_ptr, index, val_ptr);
|
||||||
|
}
|
||||||
|
ArrayKey::String(key) => {
|
||||||
|
let key_len = key.len();
|
||||||
|
let bytes =
|
||||||
|
Box::into_raw(key.into_bytes().into_boxed_slice()) as *const i8;
|
||||||
|
add_assoc_zval_ex(arr_ptr, bytes, key_len, val_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZVal(arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,9 @@ fn main() {
|
||||||
.whitelist_function("php_info_print_table_row")
|
.whitelist_function("php_info_print_table_row")
|
||||||
.whitelist_function("php_info_print_table_end")
|
.whitelist_function("php_info_print_table_end")
|
||||||
.whitelist_function("php_printf")
|
.whitelist_function("php_printf")
|
||||||
|
.whitelist_function("_zend_new_array")
|
||||||
|
.whitelist_function("add_index_zval")
|
||||||
|
.whitelist_function("add_assoc_zval_ex")
|
||||||
.whitelist_type("zval")
|
.whitelist_type("zval")
|
||||||
.whitelist_type("zend_execute_data")
|
.whitelist_type("zend_execute_data")
|
||||||
.whitelist_type("zend_module_entry")
|
.whitelist_type("zend_module_entry")
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::imported::imported_fn;
|
use crate::imported::imported_fn;
|
||||||
use ivory::externs::printf;
|
use ivory::externs::printf;
|
||||||
use ivory::PhpVal;
|
|
||||||
use ivory::{ivory_export, ivory_module};
|
use ivory::{ivory_export, ivory_module};
|
||||||
|
use ivory::{ArrayKey, PhpVal};
|
||||||
|
|
||||||
mod imported;
|
mod imported;
|
||||||
|
|
||||||
|
|
@ -66,6 +66,30 @@ fn return_string() -> String {
|
||||||
"some string data".to_string()
|
"some string data".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn return_array_simple() -> Vec<i32> {
|
||||||
|
vec![-10, 10, 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn return_array_gap() -> Vec<(u32, i32)> {
|
||||||
|
vec![(0u32, -10), (1, 10), (10, 0)]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn return_array_mixed() -> Vec<(ArrayKey, PhpVal)> {
|
||||||
|
vec![
|
||||||
|
(0u32.into(), PhpVal::from(-10)),
|
||||||
|
("foo".to_string().into(), PhpVal::from(10)),
|
||||||
|
("bar".to_string().into(), PhpVal::from(0.5)),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn return_array_nested() -> Vec<Vec<i32>> {
|
||||||
|
vec![vec![1, 2], vec![3, 4]]
|
||||||
|
}
|
||||||
|
|
||||||
ivory_module!({
|
ivory_module!({
|
||||||
name: "tests",
|
name: "tests",
|
||||||
version: "0.0.1",
|
version: "0.0.1",
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,26 @@ test_return!(
|
||||||
"return_string",
|
"return_string",
|
||||||
"Command line code:1:\nstring(16) \"some string data\"\n"
|
"Command line code:1:\nstring(16) \"some string data\"\n"
|
||||||
);
|
);
|
||||||
|
test_return!(
|
||||||
|
test_return_array_simple,
|
||||||
|
"return_array_simple",
|
||||||
|
"Command line code:1:\narray(3) {\n [0] =>\n int(-10)\n [1] =>\n int(10)\n [2] =>\n int(0)\n}\n"
|
||||||
|
);
|
||||||
|
test_return!(
|
||||||
|
test_return_array_gap,
|
||||||
|
"return_array_gap",
|
||||||
|
"Command line code:1:\narray(3) {\n [0] =>\n int(-10)\n [1] =>\n int(10)\n [10] =>\n int(0)\n}\n"
|
||||||
|
);
|
||||||
|
test_return!(
|
||||||
|
test_return_array_mixed,
|
||||||
|
"return_array_mixed",
|
||||||
|
"Command line code:1:\narray(3) {\n [0] =>\n int(-10)\n \'foo\' =>\n int(10)\n \'bar\' =>\n double(0.5)\n}\n"
|
||||||
|
);
|
||||||
|
test_return!(
|
||||||
|
test_return_array_nested,
|
||||||
|
"return_array_nested",
|
||||||
|
"Command line code:1:\narray(2) {\n [0] =>\n array(2) {\n [0] =>\n int(1)\n [1] =>\n int(2)\n }\n [1] =>\n array(2) {\n [0] =>\n int(3)\n [1] =>\n int(4)\n }\n}\n"
|
||||||
|
);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_imported() {
|
fn test_imported() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue