Skip to content

Commit dec4d8c

Browse files
Fix alignment of class objects (#66)
* Fix alignment of class objects * Clippy
1 parent f506a41 commit dec4d8c

File tree

2 files changed

+44
-29
lines changed

2 files changed

+44
-29
lines changed

src/php/execution_data.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
//! Functions for interacting with the execution data passed to PHP functions\
22
//! introduced in Rust.
33
4-
use std::mem;
5-
64
use crate::{
75
bindings::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK},
86
errors::{Error, Result},
@@ -30,10 +28,8 @@ impl ExecutionData {
3028
/// 1. Contains an object.
3129
/// 2. The object was originally derived from `T`.
3230
pub unsafe fn get_object<T: RegisteredClass>(&self) -> Option<ClassObject<'static, T>> {
33-
let ptr = self.This.object()? as *const ZendObject as *mut u8;
34-
let offset = mem::size_of::<T>();
35-
let ptr = ptr.offset(0 - offset as isize) as *mut ZendClassObject<T>;
36-
Some(ClassObject::from_zend_class_object(&mut *ptr, false))
31+
let ptr = ZendClassObject::<T>::from_zend_obj_ptr(self.This.object()?)?;
32+
Some(ClassObject::from_zend_class_object(ptr, false))
3733
}
3834

3935
/// Attempts to retrieve the 'this' object, which can be used in class methods

src/php/types/object.rs

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
//! allowing users to store Rust data inside a PHP object.
33
44
use std::{
5-
alloc::Layout,
65
convert::TryInto,
76
fmt::Debug,
87
marker::PhantomData,
98
mem::{self, MaybeUninit},
109
ops::{Deref, DerefMut},
11-
ptr,
10+
ptr::{self, NonNull},
1211
sync::atomic::{AtomicBool, AtomicPtr, Ordering},
1312
};
1413

@@ -347,31 +346,19 @@ impl<'a, T: RegisteredClass> FromZval<'a> for &'a T {
347346
const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
348347

349348
fn from_zval(zval: &'a Zval) -> Option<Self> {
350-
let obj = zval.object()?;
349+
let cobj = ZendClassObject::<T>::from_zend_obj_ptr(zval.object()?)?;
351350

352-
if obj.is_instance::<T>() {
353-
// SAFETY: If the zend object is an instance of `T`, we can guarantee that the memory before
354-
// it is occupied by an instance of `T`.
355-
unsafe { ((obj as *mut ZendObject) as *mut T).offset(-1).as_ref() }
356-
} else {
357-
None
358-
}
351+
Some(unsafe { &*cobj.obj.as_mut_ptr() })
359352
}
360353
}
361354

362355
impl<'a, T: RegisteredClass> FromZval<'a> for &'a mut T {
363356
const TYPE: DataType = DataType::Object(Some(T::CLASS_NAME));
364357

365358
fn from_zval(zval: &'a Zval) -> Option<Self> {
366-
let obj = zval.object()?;
359+
let cobj = ZendClassObject::<T>::from_zend_obj_ptr(zval.object()?)?;
367360

368-
if obj.is_instance::<T>() {
369-
// SAFETY: If the zend object is an instance of `T`, we can guarantee that the memory before
370-
// it is occupied by an instance of `T`.
371-
unsafe { ((obj as *mut ZendObject) as *mut T).offset(-1).as_mut() }
372-
} else {
373-
None
374-
}
361+
Some(unsafe { &mut *cobj.obj.as_mut_ptr() })
375362
}
376363
}
377364

@@ -439,7 +426,7 @@ where
439426
/// Representation of a Zend class object in memory. Usually seen through its managed variant
440427
/// of [`ClassObject`].
441428
#[repr(C)]
442-
pub(crate) struct ZendClassObject<T: RegisteredClass> {
429+
pub(crate) struct ZendClassObject<T> {
443430
obj: MaybeUninit<T>,
444431
std: zend_object,
445432
}
@@ -486,13 +473,46 @@ impl<T: RegisteredClass> ZendClassObject<T> {
486473
}
487474
}
488475

476+
/// Returns a reference to the [`ZendClassObject`] of a given zend object `obj`. Returns [`None`]
477+
/// if the given object is not of the type `T`.
478+
///
479+
/// # Parameters
480+
///
481+
/// * `obj` - The zend object to get the [`ZendClassObject`] for.
482+
pub(crate) fn from_zend_obj_ptr(obj: &zend_object) -> Option<&mut Self> {
483+
let ptr = obj as *const zend_object as *const i8;
484+
let ptr = unsafe {
485+
let ptr = ptr.offset(0 - Self::std_offset() as isize) as *const Self;
486+
(ptr as *mut Self).as_mut()?
487+
};
488+
489+
if ptr.std.is_instance::<T>() {
490+
Some(ptr)
491+
} else {
492+
None
493+
}
494+
}
495+
489496
/// Returns a mutable reference to the underlying Zend object.
490497
pub(crate) fn get_mut_zend_obj(&mut self) -> &mut zend_object {
491498
&mut self.std
492499
}
493500
}
494501

495-
impl<T: RegisteredClass> Drop for ZendClassObject<T> {
502+
impl<T> ZendClassObject<T> {
503+
/// Returns the offset of the `std` property in the class object.
504+
pub(crate) fn std_offset() -> usize {
505+
unsafe {
506+
let null = NonNull::<Self>::dangling();
507+
let base = null.as_ref() as *const Self;
508+
let std = &null.as_ref().std as *const zend_object;
509+
510+
(std as usize) - (base as usize)
511+
}
512+
}
513+
}
514+
515+
impl<T> Drop for ZendClassObject<T> {
496516
fn drop(&mut self) {
497517
// SAFETY: All constructors guarantee that `obj` is valid.
498518
unsafe { std::ptr::drop_in_place(self.obj.as_mut_ptr()) };
@@ -585,8 +605,7 @@ impl ZendObjectHandlers {
585605
/// Caller must guarantee that the `ptr` given is a valid memory location.
586606
pub unsafe fn init<T>(ptr: *mut ZendObjectHandlers) {
587607
pub unsafe extern "C" fn free_obj<T>(object: *mut zend_object) {
588-
let layout = Layout::new::<T>();
589-
let offset = layout.size();
608+
let offset = ZendClassObject::<T>::std_offset();
590609

591610
// Cast to *mut u8 to work in byte offsets
592611
let ptr = (object as *mut u8).offset(0 - offset as isize) as *mut T;
@@ -599,7 +618,7 @@ impl ZendObjectHandlers {
599618
}
600619

601620
std::ptr::copy_nonoverlapping(&std_object_handlers, ptr, 1);
602-
let offset = std::mem::size_of::<T>();
621+
let offset = ZendClassObject::<T>::std_offset();
603622
(*ptr).offset = offset as _;
604623
(*ptr).free_obj = Some(free_obj::<T>);
605624
}

0 commit comments

Comments
 (0)