1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Functions for interacting with the execution data passed to PHP functions\
//! introduced in Rust.
use crate::bindings::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK};
use super::{
args::ArgParser,
types::{
object::{RegisteredClass, ZendClassObject, ZendObject},
zval::Zval,
},
};
/// Execution data passed when a function is called from Zend.
pub type ExecutionData = zend_execute_data;
impl ExecutionData {
/// Returns an [`ArgParser`] pre-loaded with the arguments contained inside `self`.
pub fn parser<'a>(&'a mut self) -> ArgParser<'a, '_> {
self.parser_object().0
}
/// Returns an [`ArgParser`] pre-loaded with the arguments contained inside `self`.
///
/// A reference to `$this` is also returned in an [`Option`], which resolves to [`None`]
/// if this function is not called inside a method.
pub fn parser_object<'a>(&'a mut self) -> (ArgParser<'a, '_>, Option<&'a mut ZendObject>) {
// SAFETY: All fields of the `u2` union are the same type.
let n_args = unsafe { self.This.u2.num_args };
let mut args = vec![];
for i in 0..n_args {
// SAFETY: Function definition ensures arg lifetime doesn't exceed execution data lifetime.
let arg = unsafe { self.zend_call_arg(i as usize) };
args.push(arg);
}
let obj = self.This.object_mut();
(ArgParser::new(args), obj)
}
/// Returns an [`ArgParser`] pre-loaded with the arguments contained inside `self`.
///
/// A reference to `$this` is also returned in an [`Option`], which resolves to [`None`]
/// if this function is not called inside a method.
///
/// This function differs from [`parse_object`] in the fact that it returns a reference to
/// a [`ZendClassObject`], which is an object that contains an arbitrary Rust type at the
/// start of the object. The object will also resolve to [`None`] if the function is called
/// inside a method that does not belong to an object with type `T`.
///
/// [`parse_object`]: #method.parse_object
pub fn parser_method<'a, T: RegisteredClass>(
&'a mut self,
) -> (ArgParser<'a, '_>, Option<&'a mut ZendClassObject<T>>) {
let (parser, obj) = self.parser_object();
(
parser,
obj.and_then(|obj| ZendClassObject::from_zend_obj_mut(obj)),
)
}
/// Attempts to retrieve a reference to the underlying class object of the Zend object.
///
/// Returns a [`ZendClassObject`] if the execution data contained a valid object of type `T`,
/// otherwise returns [`None`].
pub fn get_object<T: RegisteredClass>(&mut self) -> Option<&mut ZendClassObject<T>> {
// TODO(david): This should be a `&mut self` function but we need to fix arg parser first.
ZendClassObject::from_zend_obj_mut(self.get_self()?)
}
/// Attempts to retrieve the 'this' object, which can be used in class methods
/// to retrieve the underlying Zend object.
pub fn get_self(&mut self) -> Option<&mut ZendObject> {
// TODO(david): This should be a `&mut self` function but we need to fix arg parser first.
self.This.object_mut()
}
/// Translation of macro `ZEND_CALL_ARG(call, n)`
/// zend_compile.h:578
///
/// The resultant zval reference has a lifetime equal to the lifetime of `self`.
/// This isn't specified because when you attempt to get a reference to args and
/// the `$this` object, Rust doesnt't let you. Since this is a private method it's
/// up to the caller to ensure the lifetime isn't exceeded.
#[doc(hidden)]
unsafe fn zend_call_arg<'a>(&self, n: usize) -> Option<&'a mut Zval> {
let ptr = self.zend_call_var_num(n as isize);
ptr.as_mut()
}
/// Translation of macro `ZEND_CALL_VAR_NUM(call, n)`
/// zend_compile.h: 575
#[doc(hidden)]
unsafe fn zend_call_var_num(&self, n: isize) -> *mut Zval {
let ptr = self as *const Self as *mut Zval;
ptr.offset(Self::zend_call_frame_slot() + n as isize)
}
/// Translation of macro `ZEND_CALL_FRAME_SLOT`
/// zend_compile:573
#[doc(hidden)]
fn zend_call_frame_slot() -> isize {
(Self::zend_mm_aligned_size::<Self>() + Self::zend_mm_aligned_size::<Zval>() - 1)
/ Self::zend_mm_aligned_size::<Zval>()
}
/// Translation of macro `ZEND_MM_ALIGNED_SIZE(size)`
/// zend_alloc.h:41
#[doc(hidden)]
fn zend_mm_aligned_size<T>() -> isize {
let size = std::mem::size_of::<T>();
((size as isize) + ZEND_MM_ALIGNMENT as isize - 1) & ZEND_MM_ALIGNMENT_MASK as isize
}
}
#[cfg(test)]
mod tests {
use super::ExecutionData;
#[test]
fn test_zend_call_frame_slot() {
// PHP 8.0.2 (cli) (built: Feb 21 2021 11:51:33) ( NTS )
// Copyright (c) The PHP Group
// Zend Engine v4.0.2, Copyright (c) Zend Technologies
assert_eq!(ExecutionData::zend_call_frame_slot(), 5);
}
}