use crate::{
cg,
classes::{ClassEntry, Visibility},
errors::{throw, ArgumentCountError, ExceptionGuard, ThrowObject, Throwable},
objects::{StateObj, ZObj, ZObject},
strings::{ZStr, ZString},
sys::*,
utils::ensure_end_with_zero,
values::{ExecuteData, ZVal},
};
use phper_alloc::ToRefOwned;
use std::{
ffi::{CStr, CString},
marker::PhantomData,
mem::{transmute, zeroed},
ptr::{self, null_mut},
rc::Rc,
};
pub(crate) trait Callable {
fn call(&self, execute_data: &mut ExecuteData, arguments: &mut [ZVal], return_value: &mut ZVal);
}
pub(crate) struct Function<F, Z, E>(F, PhantomData<(Z, E)>);
impl<F, Z, E> Function<F, Z, E> {
pub fn new(f: F) -> Self {
Self(f, PhantomData)
}
}
impl<F, Z, E> Callable for Function<F, Z, E>
where
F: Fn(&mut [ZVal]) -> Result<Z, E>,
Z: Into<ZVal>,
E: Throwable,
{
fn call(&self, _: &mut ExecuteData, arguments: &mut [ZVal], return_value: &mut ZVal) {
match (self.0)(arguments) {
Ok(z) => {
*return_value = z.into();
}
Err(e) => {
unsafe {
throw(e);
}
*return_value = ().into();
}
}
}
}
pub(crate) struct Method<F, Z, E, T>(F, PhantomData<(Z, E, T)>);
impl<F, Z, E, T> Method<F, Z, E, T> {
pub(crate) fn new(f: F) -> Self {
Self(f, PhantomData)
}
}
impl<F, Z, E, T: 'static> Callable for Method<F, Z, E, T>
where
F: Fn(&mut StateObj<T>, &mut [ZVal]) -> Result<Z, E>,
Z: Into<ZVal>,
E: Throwable,
{
fn call(
&self, execute_data: &mut ExecuteData, arguments: &mut [ZVal], return_value: &mut ZVal,
) {
let this = unsafe { execute_data.get_this_mut().unwrap().as_mut_state_obj() };
match (self.0)(this, arguments) {
Ok(z) => {
*return_value = z.into();
}
Err(e) => {
unsafe {
throw(e);
}
*return_value = ().into();
}
}
}
}
#[repr(transparent)]
pub struct FunctionEntry {
#[allow(dead_code)]
inner: zend_function_entry,
}
impl FunctionEntry {
pub(crate) unsafe fn from_function_entity(entity: &FunctionEntity) -> zend_function_entry {
Self::entry(
&entity.name,
&entity.arguments,
entity.handler.clone(),
None,
None,
)
}
pub(crate) unsafe fn from_method_entity(entity: &MethodEntity) -> zend_function_entry {
Self::entry(
&entity.name,
&entity.arguments,
entity.handler.clone(),
Some(entity.visibility),
Some(entity.r#static),
)
}
unsafe fn entry(
name: &CStr, arguments: &[Argument], handler: Rc<dyn Callable>,
visibility: Option<Visibility>, r#static: Option<bool>,
) -> zend_function_entry {
let mut infos = Vec::new();
let require_arg_count = arguments.iter().filter(|arg| arg.required).count();
infos.push(phper_zend_begin_arg_info_ex(false, require_arg_count));
for arg in arguments {
infos.push(phper_zend_arg_info(
arg.pass_by_ref,
arg.name.as_ptr().cast(),
));
}
infos.push(zeroed::<zend_internal_arg_info>());
let translator = CallableTranslator {
callable: Rc::into_raw(handler),
};
let last_arg_info: zend_internal_arg_info = translator.internal_arg_info;
infos.push(last_arg_info);
let flags = visibility.map(|v| v as u32).unwrap_or_default()
| r#static
.and_then(|v| if v { Some(ZEND_ACC_STATIC) } else { None })
.unwrap_or_default();
zend_function_entry {
fname: name.as_ptr().cast(),
handler: Some(invoke),
arg_info: Box::into_raw(infos.into_boxed_slice()).cast(),
num_args: arguments.len() as u32,
flags,
}
}
}
pub struct FunctionEntity {
name: CString,
handler: Rc<dyn Callable>,
arguments: Vec<Argument>,
}
impl FunctionEntity {
#[inline]
pub(crate) fn new(name: impl Into<String>, handler: Rc<dyn Callable>) -> Self {
FunctionEntity {
name: ensure_end_with_zero(name),
handler,
arguments: Default::default(),
}
}
#[inline]
pub fn argument(&mut self, argument: Argument) -> &mut Self {
self.arguments.push(argument);
self
}
#[inline]
pub fn arguments(&mut self, arguments: impl IntoIterator<Item = Argument>) -> &mut Self {
self.arguments.extend(arguments);
self
}
}
pub struct MethodEntity {
name: CString,
handler: Rc<dyn Callable>,
arguments: Vec<Argument>,
visibility: Visibility,
r#static: bool,
}
impl MethodEntity {
#[inline]
pub(crate) fn new(
name: impl Into<String>, handler: Rc<dyn Callable>, visibility: Visibility,
) -> Self {
Self {
name: ensure_end_with_zero(name),
handler,
visibility,
arguments: Default::default(),
r#static: false,
}
}
#[inline]
pub(crate) fn r#static(&mut self, s: bool) -> &mut Self {
self.r#static = s;
self
}
#[inline]
pub fn argument(&mut self, argument: Argument) -> &mut Self {
self.arguments.push(argument);
self
}
#[inline]
pub fn arguments(&mut self, arguments: impl IntoIterator<Item = Argument>) -> &mut Self {
self.arguments.extend(arguments);
self
}
}
pub struct Argument {
name: CString,
pass_by_ref: bool,
required: bool,
}
impl Argument {
pub fn by_val(name: impl Into<String>) -> Self {
let name = ensure_end_with_zero(name);
Self {
name,
pass_by_ref: false,
required: true,
}
}
pub fn by_ref(name: impl Into<String>) -> Self {
let name = ensure_end_with_zero(name);
Self {
name,
pass_by_ref: true,
required: true,
}
}
pub fn by_val_optional(name: impl Into<String>) -> Self {
let name = ensure_end_with_zero(name);
Self {
name,
pass_by_ref: false,
required: false,
}
}
pub fn by_ref_optional(name: impl Into<String>) -> Self {
let name = ensure_end_with_zero(name);
Self {
name,
pass_by_ref: true,
required: false,
}
}
}
#[repr(transparent)]
pub struct ZendFunc {
inner: zend_function,
}
impl ZendFunc {
pub(crate) unsafe fn from_mut_ptr<'a>(ptr: *mut zend_function) -> &'a mut ZendFunc {
let ptr = ptr as *mut Self;
ptr.as_mut().expect("ptr shouldn't be null")
}
pub const fn as_ptr(&self) -> *const zend_function {
&self.inner
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut zend_function {
&mut self.inner
}
pub fn get_function_name(&self) -> Option<&ZStr> {
unsafe {
let s = phper_get_function_name(self.as_ptr());
ZStr::try_from_ptr(s)
}
}
pub fn get_function_or_method_name(&self) -> ZString {
unsafe {
let s = phper_get_function_or_method_name(self.as_ptr());
ZString::from_raw(s)
}
}
pub fn get_class(&self) -> Option<&ClassEntry> {
unsafe {
let ptr = self.inner.common.scope;
if ptr.is_null() {
None
} else {
Some(ClassEntry::from_ptr(self.inner.common.scope))
}
}
}
#[allow(clippy::useless_conversion)]
pub(crate) fn call(
&mut self, mut object: Option<&mut ZObj>, mut arguments: impl AsMut<[ZVal]>,
) -> crate::Result<ZVal> {
let arguments = arguments.as_mut();
let function_handler = self.as_mut_ptr();
let object_ptr = object
.as_mut()
.map(|o| o.as_mut_ptr())
.unwrap_or(null_mut());
call_raw_common(|ret| unsafe {
#[cfg(phper_major_version = "8")]
{
let class_ptr = object
.as_mut()
.map(|o| o.get_mut_class().as_mut_ptr())
.unwrap_or(null_mut());
zend_call_known_function(
function_handler,
object_ptr,
class_ptr,
ret.as_mut_ptr(),
arguments.len() as u32,
arguments.as_mut_ptr().cast(),
null_mut(),
);
}
#[cfg(phper_major_version = "7")]
{
use std::mem::size_of;
let called_scope = {
let mut called_scope = object
.as_mut()
.map(|o| o.get_class().as_ptr() as *mut zend_class_entry)
.unwrap_or(null_mut());
if called_scope.is_null() {
called_scope = self.inner.common.scope;
}
called_scope
};
let mut fci = zend_fcall_info {
size: size_of::<zend_fcall_info>().try_into().unwrap(),
function_name: ZVal::from(()).into_inner(),
retval: ret.as_mut_ptr(),
params: arguments.as_mut_ptr().cast(),
object: object_ptr,
param_count: arguments.len() as u32,
no_separation: 1,
#[cfg(all(phper_major_version = "7", phper_minor_version = "0"))]
function_table: null_mut(),
#[cfg(all(phper_major_version = "7", phper_minor_version = "0"))]
symbol_table: null_mut(),
};
let mut fcc = zend_fcall_info_cache {
function_handler,
calling_scope: null_mut(),
called_scope,
object: object_ptr,
#[cfg(all(
phper_major_version = "7",
any(
phper_minor_version = "2",
phper_minor_version = "1",
phper_minor_version = "0",
)
))]
initialized: 1,
};
zend_call_function(&mut fci, &mut fcc);
}
})
}
}
pub(crate) union CallableTranslator {
pub(crate) callable: *const dyn Callable,
pub(crate) internal_arg_info: zend_internal_arg_info,
pub(crate) arg_info: zend_arg_info,
}
unsafe extern "C" fn invoke(execute_data: *mut zend_execute_data, return_value: *mut zval) {
let execute_data = ExecuteData::from_mut_ptr(execute_data);
let return_value = ZVal::from_mut_ptr(return_value);
let num_args = execute_data.common_num_args();
let arg_info = execute_data.common_arg_info();
let last_arg_info = arg_info.offset((num_args + 1) as isize);
let translator = CallableTranslator {
arg_info: *last_arg_info,
};
let handler = translator.callable;
let handler = handler.as_ref().expect("handler is null");
let num_args = execute_data.num_args();
let required_num_args = execute_data.common_required_num_args();
if num_args < required_num_args {
let func_name = execute_data.func().get_function_or_method_name();
let err: crate::Error = match func_name.to_str() {
Ok(func_name) => {
ArgumentCountError::new(func_name.to_owned(), required_num_args, num_args).into()
}
Err(e) => e.into(),
};
throw(err);
*return_value = ().into();
return;
}
let mut arguments = execute_data.get_parameters_array();
let arguments = arguments.as_mut_slice();
handler.call(execute_data, transmute(arguments), return_value);
}
pub fn call(callable: impl Into<ZVal>, arguments: impl AsMut<[ZVal]>) -> crate::Result<ZVal> {
let mut func = callable.into();
call_internal(&mut func, None, arguments)
}
pub(crate) fn call_internal(
func: &mut ZVal, mut object: Option<&mut ZObj>, mut arguments: impl AsMut<[ZVal]>,
) -> crate::Result<ZVal> {
let func_ptr = func.as_mut_ptr();
let arguments = arguments.as_mut();
let mut object_val = object.as_mut().map(|obj| ZVal::from(obj.to_ref_owned()));
call_raw_common(|ret| unsafe {
phper_call_user_function(
cg!(function_table),
object_val
.as_mut()
.map(|o| o.as_mut_ptr())
.unwrap_or(null_mut()),
func_ptr,
ret.as_mut_ptr(),
arguments.len() as u32,
arguments.as_mut_ptr().cast(),
);
})
}
pub(crate) fn call_raw_common(call_fn: impl FnOnce(&mut ZVal)) -> crate::Result<ZVal> {
let _guard = ExceptionGuard::default();
let mut ret = ZVal::default();
call_fn(&mut ret);
if ret.get_type_info().is_undef() {
ret = ().into();
}
unsafe {
if !eg!(exception).is_null() {
let e = ptr::replace(&mut eg!(exception), null_mut());
let obj = ZObject::from_raw(e);
match ThrowObject::new(obj) {
Ok(e) => return Err(e.into()),
Err(e) => return Err(e.into()),
}
}
}
Ok(ret)
}