diff --git a/phper/Cargo.toml b/phper/Cargo.toml index eea6e1a..689bc41 100644 --- a/phper/Cargo.toml +++ b/phper/Cargo.toml @@ -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 } diff --git a/phper/src/classes/entity.rs b/phper/src/classes/entity.rs index 308c2af..04f9411 100644 --- a/phper/src/classes/entity.rs +++ b/phper/src/classes/entity.rs @@ -1,3 +1,4 @@ +use smallvec::SmallVec; use std::{any::Any, marker::PhantomData, mem::zeroed, ptr::null_mut, rc::Rc}; use phper_sys::{ @@ -5,12 +6,7 @@ use phper_sys::{ 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, @@ -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 { class: zend_class_entry, state_constructor: Rc, - method_entities: Vec, + method_entities: SmallVec<[FunctionEntry; 16]>, property_entities: Vec, parent: Option &'static ClassEntry>>, interfaces: Vec &'static ClassEntry>>, - bind_class: Option<&'static StaticStateClass>, + bind_class: Option<&'static StaticStateClass<()>>, state_cloner: Option>, - _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) -> Self { - Self::new_with_state_constructor::<()>(class_name, || ()) + Self::new_with_state_constructor(class_name, || ()) } } -impl ClassEntity { +impl ClassEntity { /// Construct a new `ClassEntity` with class name and default state /// constructor. - pub fn new_with_default_state_constructor(class_name: impl AsRef) -> Self + pub fn new_with_default_state_constructor(class_name: impl AsRef) -> Self where T: Default + 'static, { @@ -61,10 +57,10 @@ impl Handler for dyn Fn(&mut StateObj, &mut [ZVal]) -> Result } } -impl ClassEntity { +impl ClassEntity { /// Construct a new `ClassEntity` with class name and the constructor to /// build state. - pub fn new_with_state_constructor( + pub fn new_with_state_constructor( class_name: impl AsRef, state_constructor: impl Fn() -> T + 'static, ) -> Self @@ -81,7 +77,7 @@ impl ClassEntity { let boxed = Box::new(state) as Box; Box::into_raw(boxed) }), - method_entities: Vec::new(), + method_entities: SmallVec::default(), property_entities: Vec::new(), parent: None, interfaces: Vec::new(), @@ -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()); @@ -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) { + self.bind_class = Some(unsafe { + std::mem::transmute::<&'static StaticStateClass, &'static StaticStateClass<()>>(cls) + }); } /// Add the state clone function, called when cloning PHP object. @@ -244,13 +241,16 @@ impl ClassEntity { /// ``` /// use phper::classes::ClassEntity; /// - /// fn make_foo_class() -> ClassEntity { + /// fn make_foo_class() -> ClassEntity { /// let mut class = ClassEntity::new_with_state_constructor("Foo", || 123456); /// class.state_cloner(Clone::clone); /// class /// } /// ``` - pub fn state_cloner(&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() @@ -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::>(); - - methods.push(zeroed::()); - - // 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::(); - 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::(); - 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> { +impl crate::modules::Registerer for ClassEntity { + fn register(mut self, _: i32) -> Result<(), Box> { 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::(); + 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::(); + 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); } diff --git a/phper/src/classes/entry.rs b/phper/src/classes/entry.rs index 9d68b1f..600e2d9 100644 --- a/phper/src/classes/entry.rs +++ b/phper/src/classes/entry.rs @@ -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. @@ -74,6 +76,7 @@ impl ClassEntry { } /// Returns a raw pointer wrapped. + #[inline] pub const fn as_ptr(&self) -> *const zend_class_entry { &self.inner } @@ -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) -> crate::Result<&'static Self> { let name = class_name.as_ref(); let ptr: *mut Self = find_global_class_entry_ptr(name).cast(); @@ -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 { let mut object = self.init_object()?; object.call_construct(arguments)?; @@ -117,6 +122,7 @@ impl ClassEntry { /// Create the object from class, without calling `__construct`. /// /// **Be careful when `__construct` is necessary.** + #[inline] pub fn init_object(&self) -> crate::Result { unsafe { let ptr = self.as_ptr() as *mut _; @@ -124,7 +130,7 @@ impl ClassEntry { 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()); @@ -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); @@ -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()) } } @@ -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) -> Option<&ZVal> { let ptr = self.as_ptr() as *mut _; let prop = Self::inner_get_static_property(ptr, name); @@ -166,6 +181,7 @@ 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, val: impl Into) -> Option { let ptr = self.as_ptr() as *mut _; let prop = Self::inner_get_static_property(ptr, name); @@ -173,13 +189,11 @@ impl ClassEntry { prop.map(|prop| replace(prop, val.into())) } + #[inline] fn inner_get_static_property(scope: *mut zend_class_entry, name: impl AsRef) -> *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()) } } } diff --git a/phper/src/classes/methods.rs b/phper/src/classes/methods.rs new file mode 100644 index 0000000..2eaf896 --- /dev/null +++ b/phper/src/classes/methods.rs @@ -0,0 +1,85 @@ +use std::ffi::CString; +use std::rc::Rc; +use phper_sys::{ZEND_ACC_ABSTRACT, ZEND_ACC_FINAL, ZEND_ACC_PRIVATE, ZEND_ACC_PROMOTED, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, zend_internal_arg_info}; +use crate::classes::{RawVisibility, Visibility}; +use crate::functions::{Callable, FunctionEntry}; +use crate::utils::ensure_end_with_zero; + +/// Builder for registering class method. +pub struct MethodEntity { + pub(crate) name: CString, + pub(crate) handler: Option>, + pub(crate) arguments: &'static [zend_internal_arg_info], + pub(crate) visibility: RawVisibility, +} + +#[derive(Default)] +pub struct MethodEntityBuilder { + name: CString, + handler: Option>, + arguments: &'static [zend_internal_arg_info], + visibility: RawVisibility, +} + +impl Into for MethodEntity { + fn into(self) -> FunctionEntry { + unsafe { FunctionEntry::from_method_entity(self) } + } +} + +impl MethodEntityBuilder { + #[inline] + fn new(name: impl AsRef, arguments: &'static [zend_internal_arg_info]) -> Self { + Self { + name: ensure_end_with_zero(name), + handler: None, + arguments, + visibility: ZEND_ACC_PUBLIC, + } + } + + #[inline] + pub fn set_handler(mut self, handler: impl Into>) -> Self { + self.handler = Some(handler.into()); + self + } + + #[inline] + pub fn set_final(mut self) -> Self { + self.visibility |= ZEND_ACC_FINAL; + self + } + + #[inline] + pub fn set_abstract(mut self) -> Self { + self.visibility |= ZEND_ACC_ABSTRACT; + self + } + + #[inline] + pub fn set_private(mut self) -> Self { + self.visibility |= ZEND_ACC_PRIVATE; + self + } + + #[inline] + pub fn set_protected(mut self) -> Self { + self.visibility |= ZEND_ACC_PROTECTED; + self + } + + #[inline] + pub fn set_public(mut self) -> Self { + self.visibility |= ZEND_ACC_PUBLIC; + self + } + + pub(crate) fn build(self) -> MethodEntity { + MethodEntity { + name: self.name, + handler: self.handler, + visibility: self.visibility, + arguments: self.arguments, + } + } +} diff --git a/phper/src/classes/mod.rs b/phper/src/classes/mod.rs index e248fda..7b06199 100644 --- a/phper/src/classes/mod.rs +++ b/phper/src/classes/mod.rs @@ -13,19 +13,23 @@ pub mod entity; /// Zend Class Entry pub mod entry; +pub mod methods; pub mod zend_classes; +pub use entity::*; +pub use methods::MethodEntity; + +use crate::classes::methods::MethodEntityBuilder; use crate::{ - functions::{FunctionEntry, MethodEntity}, + functions::FunctionEntry, objects::{StateObj, StateObject, ZObject}, sys::*, types::Scalar, values::ZVal, }; -use std::ptr::null; +use smallvec::SmallVec; use std::{ any::Any, - convert::TryInto, mem::{size_of, zeroed}, os::raw::c_int, ptr::null_mut, @@ -35,7 +39,7 @@ use std::{ use self::entry::ClassEntry; -#[allow(clippy::useless_conversion)] +#[inline] fn find_global_class_entry_ptr(name: impl AsRef) -> *mut zend_class_entry { let name = name.as_ref(); let name = name.to_lowercase(); @@ -43,7 +47,7 @@ fn find_global_class_entry_ptr(name: impl AsRef) -> *mut zend_class_entry { phper_zend_hash_str_find_ptr( compiler_globals.class_table, name.as_ptr().cast(), - name.len().try_into().unwrap(), + name.len(), ) .cast() } @@ -76,27 +80,24 @@ fn find_global_class_entry_ptr(name: impl AsRef) -> *mut zend_class_entry { /// class /// } /// ``` + #[repr(transparent)] -pub struct StaticStateClass { - inner: AtomicPtr, -} +pub struct StaticStateClass(AtomicPtr, std::marker::PhantomData); -impl StaticStateClass { +impl StaticStateClass { /// Create empty [StaticStateClass], with null /// [zend_class_entry]. pub const fn null() -> Self { - Self { - inner: AtomicPtr::new(null_mut()), - } + Self(AtomicPtr::new(null_mut()), std::marker::PhantomData) } fn bind(&'static self, ptr: *mut zend_class_entry) { - self.inner.store(ptr, Ordering::Relaxed); + self.0.store(ptr, Ordering::Relaxed); } /// Converts to class entry. pub fn as_class_entry(&'static self) -> &'static ClassEntry { - unsafe { ClassEntry::from_mut_ptr(self.inner.load(Ordering::Relaxed)) } + unsafe { ClassEntry::from_mut_ptr(self.0.load(Ordering::Relaxed)) } } /// Create the object from class and call `__construct` with arguments. @@ -121,7 +122,7 @@ impl StaticStateClass { } } -unsafe impl Sync for StaticStateClass {} +unsafe impl Sync for StaticStateClass {} /// The [StaticInterface] holds /// [zend_class_entry], always as the static @@ -147,26 +148,23 @@ unsafe impl Sync for StaticStateClass {} /// } /// ``` #[repr(transparent)] -pub struct StaticInterface { - inner: AtomicPtr, -} +pub struct StaticInterface(AtomicPtr); impl StaticInterface { /// Create empty [StaticInterface], with null /// [zend_class_entry]. pub const fn null() -> Self { - Self { - inner: AtomicPtr::new(null_mut()), - } + Self(AtomicPtr::new(null_mut())) + } fn bind(&'static self, ptr: *mut zend_class_entry) { - self.inner.store(ptr, Ordering::Relaxed); + self.0.store(ptr, Ordering::Relaxed); } /// Converts to class entry. pub fn as_class_entry(&'static self) -> &'static ClassEntry { - unsafe { ClassEntry::from_mut_ptr(self.inner.load(Ordering::Relaxed)) } + unsafe { ClassEntry::from_mut_ptr(self.0.load(Ordering::Relaxed)) } } } @@ -177,8 +175,8 @@ pub(crate) type StateCloner = dyn Fn(*const dyn Any) -> *mut dyn Any; /// Builder for registering interface. pub struct InterfaceEntity { interface: zend_class_entry, - method_entities: Vec, - extends: Vec &'static ClassEntry>>, + methods: SmallVec<[FunctionEntry; 16]>, + extends: SmallVec<[Box &'static ClassEntry>; 1]>, bind_interface: Option<&'static StaticInterface>, } @@ -192,27 +190,20 @@ impl InterfaceEntity { interface: unsafe { phper_init_interface_entry(interface_name.as_ptr().cast(), interface_name_len) }, - method_entities: Vec::new(), - extends: Vec::new(), + methods: SmallVec::default(), + extends: SmallVec::default(), bind_interface: None, } } /// Add member method to interface, with mandatory visibility public /// abstract. - pub fn add_method( - &mut self, - name: impl AsRef, - args: &'static [zend_internal_arg_info], - ) -> &mut MethodEntity { - let mut entity = MethodEntity::new(name, None, Visibility::Public, args); - entity.set_vis_abstract(); - self.method_entities.push(entity); - self.method_entities.last_mut().unwrap() + pub fn add_method(&mut self, builder: MethodEntityBuilder) { + self.methods.push(builder.set_abstract().build().into()); } /// Register interface to `extends` the interfaces, due to the interface can - /// extends multi interface, so this method can be called multi time. + /// extend multi interface, so this method can be called multi time. /// /// *Because in the `MINIT` phase, the class starts to register, so the* /// *closure is used to return the `ClassEntry` to delay the acquisition of* @@ -238,25 +229,15 @@ impl InterfaceEntity { pub fn bind(&mut self, i: &'static StaticInterface) { self.bind_interface = Some(i); } - - unsafe fn function_entries(&self) -> *const zend_function_entry { - let mut methods = self - .method_entities - .iter() - .map(|method| FunctionEntry::from_method_entity(method)) - .collect::>(); - - methods.push(zeroed::()); - - Box::into_raw(methods.into_boxed_slice()).cast() - } } impl crate::modules::Registerer for InterfaceEntity { - fn register(&mut self, _: i32) -> Result<(), Box> { + fn register(mut self, _: i32) -> Result<(), Box> { unsafe { + self.methods.push(zeroed::()); + let class_ce = - phper_register_interface_entry(&mut self.interface, self.function_entries()); + phper_register_interface_entry(&mut self.interface, self.methods.as_ptr().cast()); if let Some(bind_interface) = self.bind_interface { bind_interface.bind(class_ce); diff --git a/phper/src/constants.rs b/phper/src/constants.rs index 7f85cbf..c15eecc 100644 --- a/phper/src/constants.rs +++ b/phper/src/constants.rs @@ -54,7 +54,7 @@ impl Constant { } impl Registerer for Constant { - fn register(&mut self, module_number: i32) -> Result<(), Box> { + fn register(mut self, module_number: i32) -> Result<(), Box> { let result = unsafe { phper_register_constant(&mut self.constant, self.flags, module_number) }; diff --git a/phper/src/functions.rs b/phper/src/functions.rs index 4091b57..2d15240 100644 --- a/phper/src/functions.rs +++ b/phper/src/functions.rs @@ -22,6 +22,7 @@ use crate::{ values::{ExecuteData, ZVal}, }; use phper_alloc::ToRefOwned; +use std::mem::zeroed; use std::{ ffi::{CStr, CString}, marker::PhantomData, @@ -29,6 +30,7 @@ use std::{ ptr::{self, null_mut}, rc::Rc, }; +use crate::classes::MethodEntity; pub(crate) trait Callable { fn call(&self, execute_data: &mut ExecuteData, arguments: &mut [ZVal], return_value: &mut ZVal); @@ -99,14 +101,15 @@ where } /// Wrapper of [`zend_function_entry`]. -#[repr(transparent)] -pub struct FunctionEntry { - #[allow(dead_code)] - inner: zend_function_entry, -} +#[repr(C)] +pub struct FunctionEntry(pub(crate) zend_function_entry); impl FunctionEntry { - pub(crate) unsafe fn from_function_entity(entity: &FunctionEntity) -> zend_function_entry { + pub(crate) fn empty() -> Self { + Self(unsafe { zeroed::() }) + } + + pub(crate) unsafe fn from_function_entity(entity: FunctionEntity) -> FunctionEntry { Self::entry( &entity.name, entity.arguments, @@ -115,7 +118,7 @@ impl FunctionEntry { ) } - pub(crate) unsafe fn from_method_entity(entity: &MethodEntity) -> zend_function_entry { + pub(crate) unsafe fn from_method_entity(entity: MethodEntity) -> FunctionEntry { Self::entry( &entity.name, entity.arguments, @@ -129,7 +132,7 @@ impl FunctionEntry { arguments: &'static [zend_internal_arg_info], handler: Option>, visibility: Option, - ) -> zend_function_entry { + ) -> FunctionEntry { let raw_handler = handler.as_ref().map(|_| invoke as _); if let Some(handler) = handler { @@ -142,13 +145,13 @@ impl FunctionEntry { let flags = visibility.unwrap_or(Visibility::default() as u32); - zend_function_entry { + FunctionEntry(zend_function_entry { fname: name.as_ptr().cast(), handler: raw_handler, arg_info: null_mut(), num_args: 0u32, flags, - } + }) } } @@ -174,42 +177,6 @@ impl FunctionEntity { } } -/// Builder for registering class method. -pub struct MethodEntity { - name: CString, - handler: Option>, - arguments: &'static [zend_internal_arg_info], - visibility: RawVisibility, -} - -impl MethodEntity { - #[inline] - pub(crate) fn new( - name: impl AsRef, - handler: Option>, - visibility: Visibility, - arguments: &'static [zend_internal_arg_info], - ) -> Self { - Self { - name: ensure_end_with_zero(name), - handler, - visibility: visibility as RawVisibility, - arguments, - } - } - - #[inline] - pub(crate) fn set_vis_static(&mut self) -> &mut Self { - self.visibility |= ZEND_ACC_STATIC; - self - } - - #[inline] - pub(crate) fn set_vis_abstract(&mut self) -> &mut Self { - self.visibility |= ZEND_ACC_ABSTRACT; - self - } -} /// Wrapper of [`zend_function`]. #[repr(transparent)] diff --git a/phper/src/modules.rs b/phper/src/modules.rs index 7cd30fc..bfe1076 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -19,9 +19,9 @@ use crate::{ functions::{Function, FunctionEntity, FunctionEntry}, ini, sys::*, - utils::ensure_end_with_zero, values::ZVal, }; +use smallvec::SmallVec; use std::{ collections::HashMap, ffi::CString, @@ -42,6 +42,15 @@ unsafe fn get_module() -> &'static mut Module { unsafe { GLOBAL_MODULE.as_mut().unwrap_unchecked() } } +/// Safety: This is used as a global variable, initialization is always +/// guaranteed by PHP to be from one thread in ZTS, and on NTS its always one thread +struct FEntry(SmallVec<[FunctionEntry; 64]>); + +unsafe impl Send for FEntry {} +unsafe impl Sync for FEntry {} + +static mut PHP_FUNCTIONS: FEntry = FEntry(SmallVec::new_const()); + /// PHP Module information pub struct ModuleInfo { /// Module Type @@ -52,7 +61,7 @@ pub struct ModuleInfo { } pub(crate) trait Registerer { - fn register(&mut self, module_number: i32) -> Result<(), Box>; + fn register(self, module_number: i32) -> Result<(), Box>; } unsafe extern "C" fn module_startup(_type: c_int, module_number: c_int) -> c_int { @@ -61,7 +70,7 @@ unsafe extern "C" fn module_startup(_type: c_int, module_number: c_int) -> c_int ini::register(take(&mut module.ini_entities), module_number); - for mut entity in take(&mut module.entities).into_iter() { + for entity in take(&mut module.entities).into_iter() { if let Err(err) = entity.register(module_number) { crate::output::log( crate::output::LogLevel::Error, @@ -157,7 +166,6 @@ pub struct Module { module_shutdown: Option>, request_init: Option<&'static dyn Fn(ModuleInfo)>, request_shutdown: Option<&'static dyn Fn(ModuleInfo)>, - function_entities: Vec, entities: Vec, ini_entities: Vec, infos: HashMap, @@ -165,12 +173,12 @@ pub struct Module { pub(crate) enum Entities { Constant(Constant), - Class(ClassEntity), + Class(ClassEntity<()>), Interface(InterfaceEntity), } impl Registerer for Entities { - fn register(&mut self, module_number: i32) -> Result<(), Box> { + fn register(self, module_number: i32) -> Result<(), Box> { match self { Entities::Constant(con) => con.register(module_number), Entities::Class(class) => class.register(module_number), @@ -183,17 +191,12 @@ impl Module { /// Construct the `Module` with base metadata. pub fn new(name: impl AsRef, version: impl AsRef, author: impl AsRef) -> Self { Self { - name: ensure_end_with_zero(name), - version: ensure_end_with_zero(version), - author: ensure_end_with_zero(author), - module_init: None, - module_shutdown: None, - request_init: None, - request_shutdown: None, - function_entities: vec![], - entities: Default::default(), - ini_entities: Default::default(), - infos: Default::default(), + name: CString::new(name.as_ref()).expect("Failed to allocate CString, param: name"), + version: CString::new(version.as_ref()) + .expect("Failed to allocate CString, param: version"), + author: CString::new(author.as_ref()) + .expect("Failed to allocate CString, param: author"), + ..Default::default() } } @@ -218,7 +221,12 @@ impl Module { } /// Register function to module. - pub fn add_function(&mut self, name: impl AsRef, arguments: &'static [zend_internal_arg_info], handler: F) -> &mut Self + pub fn add_function( + &mut self, + name: impl AsRef, + arguments: &'static [zend_internal_arg_info], + handler: F, + ) -> &mut Self where F: Fn(&mut [ZVal]) -> Result + 'static, Z: Into + 'static, @@ -226,14 +234,20 @@ impl Module { { let entry = FunctionEntity::new(name, Rc::new(Function::new(handler)), arguments); - self.function_entities.push(entry); + unsafe { + PHP_FUNCTIONS + .0 + .push(FunctionEntry::from_function_entity(entry)); + } self } /// Register class to module. - pub fn add_class(&mut self, class: ClassEntity) -> &mut Self { - self.entities.push(Entities::Class(class)); + pub fn add_class(&mut self, class: ClassEntity) -> &mut Self { + self.entities.push(Entities::Class(unsafe { + std::mem::transmute::, ClassEntity<()>>(class) + })); self } @@ -295,12 +309,6 @@ impl Module { return GLOBAL_MODULE_ENTRY; } - assert!(!self.name.as_bytes().is_empty(), "module name must be set"); - assert!( - !self.version.as_bytes().is_empty(), - "module version must be set" - ); - let module = Box::leak(Box::new(self)); let entry = Box::new(zend_module_entry { @@ -347,17 +355,13 @@ impl Module { GLOBAL_MODULE_ENTRY } - fn function_entries(&self) -> *const zend_function_entry { - if self.function_entities.is_empty() { + unsafe fn function_entries(&self) -> *const zend_function_entry { + if PHP_FUNCTIONS.0.is_empty() { return null(); } - let mut entries = Vec::new(); - for f in &self.function_entities { - entries.push(unsafe { FunctionEntry::from_function_entity(f) }); - } - entries.push(unsafe { zeroed::() }); + PHP_FUNCTIONS.0.push(zeroed::()); - Box::into_raw(entries.into_boxed_slice()).cast() + PHP_FUNCTIONS.0.as_ptr() as *const zend_function_entry } } diff --git a/phper/src/strings.rs b/phper/src/strings.rs index 96d0503..340adbe 100644 --- a/phper/src/strings.rs +++ b/phper/src/strings.rs @@ -109,6 +109,12 @@ impl ZStr { self.inner.val.as_ptr() } + #[inline] + pub unsafe fn as_str(&self) -> &str { + let val: &[u8] = from_raw_parts(self.inner.val.as_ptr() as *const u8, self.inner.len); + std::str::from_utf8_unchecked(val) + } + /// Gets the inner C string length. #[inline] pub fn len(&self) -> usize {