Skip to content

Commit

Permalink
Move further with Type system
Browse files Browse the repository at this point in the history
Signed-off-by: Dusan Malusev <dusan@dusanmalusev.dev>
  • Loading branch information
CodeLieutenant committed Jun 19, 2024
1 parent acd85c6 commit f5e5b5e
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 203 deletions.
1 change: 1 addition & 0 deletions phper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ once_cell = "1.19.0"
thiserror = "1.0.61"
bitflags = "2.5.0"
memoffset = "0.9.1"
smallvec = { version = "1.13.2", features = ["const_generics", "const_new", "union"] }


phper-alloc = { workspace = true }
Expand Down
114 changes: 50 additions & 64 deletions phper/src/classes/entity.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
use smallvec::SmallVec;
use std::{any::Any, marker::PhantomData, mem::zeroed, ptr::null_mut, rc::Rc};

use phper_sys::{
phper_init_class_entry, phper_register_class_entry, zend_class_entry, zend_class_implements,
zend_function_entry,
};

use crate::{
functions::{FunctionEntry, MethodEntity},
objects::StateObj,
types::Scalar,
values::ZVal,
};
use crate::{functions::FunctionEntry, objects::StateObj, types::Scalar, values::ZVal};

use super::{
create_object, entry::ClassEntry, PropertyEntity, StateCloner, StateConstructor,
Expand All @@ -21,29 +17,29 @@ use super::{
///
/// *It is a common practice for PHP extensions to use PHP objects to package
/// third-party resources.*
pub struct ClassEntity {
pub struct ClassEntity<T> {
class: zend_class_entry,
state_constructor: Rc<StateConstructor>,
method_entities: Vec<MethodEntity>,
method_entities: SmallVec<[FunctionEntry; 16]>,
property_entities: Vec<PropertyEntity>,
parent: Option<Box<dyn Fn() -> &'static ClassEntry>>,
interfaces: Vec<Box<dyn Fn() -> &'static ClassEntry>>,
bind_class: Option<&'static StaticStateClass>,
bind_class: Option<&'static StaticStateClass<()>>,
state_cloner: Option<Rc<StateCloner>>,
_p: PhantomData<*mut ()>,
_p: PhantomData<*mut T>,
}

impl ClassEntity {
impl ClassEntity<()> {
/// Construct a new `ClassEntity` with class name, do not own state.
pub fn new(class_name: impl AsRef<str>) -> Self {
Self::new_with_state_constructor::<()>(class_name, || ())
Self::new_with_state_constructor(class_name, || ())
}
}

impl ClassEntity {
impl<T> ClassEntity<T> {
/// Construct a new `ClassEntity` with class name and default state
/// constructor.
pub fn new_with_default_state_constructor<T>(class_name: impl AsRef<str>) -> Self
pub fn new_with_default_state_constructor(class_name: impl AsRef<str>) -> Self
where
T: Default + 'static,
{
Expand All @@ -61,10 +57,10 @@ impl<Z, E> Handler<Z, E> for dyn Fn(&mut StateObj, &mut [ZVal]) -> Result<Z, E>
}
}

impl ClassEntity {
impl<T> ClassEntity<T> {
/// Construct a new `ClassEntity` with class name and the constructor to
/// build state.
pub fn new_with_state_constructor<T>(
pub fn new_with_state_constructor(
class_name: impl AsRef<str>,
state_constructor: impl Fn() -> T + 'static,
) -> Self
Expand All @@ -81,7 +77,7 @@ impl ClassEntity {
let boxed = Box::new(state) as Box<dyn Any>;
Box::into_raw(boxed)
}),
method_entities: Vec::new(),
method_entities: SmallVec::default(),
property_entities: Vec::new(),
parent: None,
interfaces: Vec::new(),
Expand Down Expand Up @@ -183,7 +179,6 @@ impl ClassEntity {
///
/// ```no_run
/// use phper::classes::{ClassEntity, ClassEntry};
/// use phper::classes::entity::ClassEntity;
///
/// let mut class = ClassEntity::new("MyException");
/// class.extends(|| ClassEntry::from_globals("Exception").unwrap());
Expand Down Expand Up @@ -219,8 +214,10 @@ impl ClassEntity {
///
/// When the class registered, the [StaticStateClass] will be initialized,
/// so you can use the [StaticStateClass] to new stateful object, etc.
pub fn bind(&mut self, cls: &'static StaticStateClass) {
self.bind_class = Some(cls);
pub fn bind(&mut self, cls: &'static StaticStateClass<T>) {
self.bind_class = Some(unsafe {
std::mem::transmute::<&'static StaticStateClass<T>, &'static StaticStateClass<()>>(cls)
});
}

/// Add the state clone function, called when cloning PHP object.
Expand All @@ -244,13 +241,16 @@ impl ClassEntity {
/// ```
/// use phper::classes::ClassEntity;
///
/// fn make_foo_class() -> ClassEntity<i64> {
/// fn make_foo_class<T>() -> ClassEntity<T> {
/// let mut class = ClassEntity::new_with_state_constructor("Foo", || 123456);
/// class.state_cloner(Clone::clone);
/// class
/// }
/// ```
pub fn state_cloner<T: 'static>(&mut self, clone_fn: impl Fn(&T) -> T + 'static) {
pub fn state_cloner(&mut self, clone_fn: impl Fn(&T) -> T + 'static)
where
T: 'static,
{
self.state_cloner = Some(Rc::new(move |src| {
let src = unsafe {
src.as_ref()
Expand All @@ -263,62 +263,48 @@ impl ClassEntity {
Box::into_raw(boxed)
}));
}

unsafe fn function_entries(&self) -> *const zend_function_entry {
let mut methods = self
.method_entities
.iter()
.map(|method| FunctionEntry::from_method_entity(method))
.collect::<Vec<_>>();

methods.push(zeroed::<zend_function_entry>());

// Store the state constructor pointer to zend_class_entry.
methods.push(self.take_state_constructor_into_function_entry());

// Store the state cloner pointer to zend_class_entry.
methods.push(self.take_state_cloner_into_function_entry());

Box::into_raw(methods.into_boxed_slice()).cast()
}

unsafe fn take_state_constructor_into_function_entry(&self) -> zend_function_entry {
let mut entry = zeroed::<zend_function_entry>();
let ptr = &mut entry as *mut _ as *mut *const StateConstructor;
let state_constructor = Rc::into_raw(self.state_constructor.clone());
ptr.write(state_constructor);
entry
}

unsafe fn take_state_cloner_into_function_entry(&self) -> zend_function_entry {
let mut entry = zeroed::<zend_function_entry>();
let ptr = &mut entry as *mut _ as *mut *const StateCloner;
if let Some(state_cloner) = &self.state_cloner {
let state_constructor = Rc::into_raw(state_cloner.clone());
ptr.write(state_constructor);
}
entry
}
}

impl crate::modules::Registerer for ClassEntity {
fn register(&mut self, _: i32) -> Result<(), Box<dyn std::error::Error>> {
impl<T> crate::modules::Registerer for ClassEntity<T> {
fn register(mut self, _: i32) -> Result<(), Box<dyn std::error::Error>> {
unsafe {
let parent: *mut zend_class_entry = self
.parent
.as_ref()
.map(|parent| parent())
.map(|entry| entry.as_ptr() as *mut _)
.map(|parent| parent().as_ptr() as *mut _)
.unwrap_or(null_mut());

let mut methods = std::mem::take(&mut self.method_entities);
methods.push(FunctionEntry::empty());

{
let mut entry = zeroed::<zend_function_entry>();
let ptr = &mut entry as *mut _ as *mut *const StateConstructor;
let state_constructor = Rc::into_raw(self.state_constructor);
ptr.write(state_constructor);
methods.push(FunctionEntry(entry));
}

// Store the state constructor pointer to zend_class_entry.

if let Some(state_cloner) = self.state_cloner {
let mut entry = zeroed::<zend_function_entry>();
let ptr = &mut entry as *mut _ as *mut *const StateCloner;
let state_constructor = Rc::into_raw(state_cloner.clone());
ptr.write(state_constructor);
methods.push(FunctionEntry(entry));
}

// Store the state cloner pointer to zend_class_entry.

let class_ce =
phper_register_class_entry(&mut self.class, parent, self.function_entries());
phper_register_class_entry(&mut self.class, parent, methods.as_ptr().cast());

if let Some(bind_class) = self.bind_class {
bind_class.bind(class_ce);
}

for interface in &self.interfaces {
for interface in self.interfaces {
let interface_ce = interface().as_ptr();
zend_class_implements(class_ce, 1, interface_ce);
}
Expand Down
26 changes: 20 additions & 6 deletions phper/src/classes/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ impl ClassEntry {
/// Panics if pointer is null.
#[inline]
pub unsafe fn from_ptr<'a>(ptr: *const zend_class_entry) -> &'a Self {
(ptr as *const Self).as_ref().expect("ptr should't be null")
(ptr as *const Self)
.as_ref()
.expect("ptr shouldn't be null")
}

/// Wraps a raw pointer, return None if pointer is null.
Expand Down Expand Up @@ -74,6 +76,7 @@ impl ClassEntry {
}

/// Returns a raw pointer wrapped.
#[inline]
pub const fn as_ptr(&self) -> *const zend_class_entry {
&self.inner
}
Expand All @@ -94,6 +97,7 @@ impl ClassEntry {
/// let std_class = ClassEntry::from_globals("stdClass").unwrap();
/// let _obj = std_class.new_object([]).unwrap();
/// ```
#[inline]
pub fn from_globals(class_name: impl AsRef<str>) -> crate::Result<&'static Self> {
let name = class_name.as_ref();
let ptr: *mut Self = find_global_class_entry_ptr(name).cast();
Expand All @@ -108,6 +112,7 @@ impl ClassEntry {
///
/// If the `__construct` is private, or protected and the called scope isn't
/// parent class, it will throw PHP Error.
#[inline]
pub fn new_object(&self, arguments: impl AsMut<[ZVal]>) -> crate::Result<ZObject> {
let mut object = self.init_object()?;
object.call_construct(arguments)?;
Expand All @@ -117,14 +122,15 @@ impl ClassEntry {
/// Create the object from class, without calling `__construct`.
///
/// **Be careful when `__construct` is necessary.**
#[inline]
pub fn init_object(&self) -> crate::Result<ZObject> {
unsafe {
let ptr = self.as_ptr() as *mut _;
let mut val = ZVal::default();
if !phper_object_init_ex(val.as_mut_ptr(), ptr) {
Err(InitializeObjectError::new(self.get_name().to_str()?.to_owned()).into())
} else {
// Can't drop val here! Otherwise the object will be dropped too (wasting me a
// Can't drop val here! Otherwise, the object will be dropped too (wasting me a
// day of debugging time here).
let mut val = ManuallyDrop::new(val);
let ptr = phper_z_obj_p(val.as_mut_ptr());
Expand All @@ -134,11 +140,18 @@ impl ClassEntry {
}

/// Get the class name.
#[inline]
pub fn get_name(&self) -> &ZStr {
unsafe { ZStr::from_ptr(self.inner.name) }
}

#[inline]
pub fn get_name_str(&self) -> &str {
unsafe { ZStr::from_ptr(self.inner.name).as_str() }
}

/// Detect if the method is exists in class.
#[inline]
pub fn has_method(&self, method_name: &str) -> bool {
unsafe {
let function_table = ZArr::from_ptr(&self.inner.function_table);
Expand All @@ -147,6 +160,7 @@ impl ClassEntry {
}

/// Detect if the class is instance of parent class.
#[inline]
pub fn is_instance_of(&self, parent: &ClassEntry) -> bool {
unsafe { phper_instanceof_function(self.as_ptr(), parent.as_ptr()) }
}
Expand All @@ -155,6 +169,7 @@ impl ClassEntry {
///
/// Return None when static property hasn't register by
/// [ClassEntity::add_static_property].
#[inline]
pub fn get_static_property(&self, name: impl AsRef<str>) -> Option<&ZVal> {
let ptr = self.as_ptr() as *mut _;
let prop = Self::inner_get_static_property(ptr, name);
Expand All @@ -166,20 +181,19 @@ impl ClassEntry {
/// Return `Some(x)` where `x` is the previous value of static property, or
/// return `None` when static property hasn't register by
/// [ClassEntity::add_static_property].
#[inline]
pub fn set_static_property(&self, name: impl AsRef<str>, val: impl Into<ZVal>) -> Option<ZVal> {
let ptr = self.as_ptr() as *mut _;
let prop = Self::inner_get_static_property(ptr, name);
let prop = unsafe { ZVal::try_from_mut_ptr(prop) };
prop.map(|prop| replace(prop, val.into()))
}

#[inline]
fn inner_get_static_property(scope: *mut zend_class_entry, name: impl AsRef<str>) -> *mut zval {
let name = name.as_ref();

unsafe {
#[allow(clippy::useless_conversion)]
zend_read_static_property(scope, name.as_ptr().cast(), name.len(), true.into())
}
unsafe { zend_read_static_property(scope, name.as_ptr().cast(), name.len(), true.into()) }
}
}

Expand Down
Loading

0 comments on commit f5e5b5e

Please sign in to comment.