Skip to content

Commit 2b878ab

Browse files
committed
Modify Object and Ebox, implement SetVal for EBox<Object>.
1 parent 69c34b1 commit 2b878ab

File tree

11 files changed

+216
-36
lines changed

11 files changed

+216
-36
lines changed

phper-alloc/src/lib.rs

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,53 @@ Alloc related items for [phper](https://crates.io/crates/phper).
77
[Unlicense](https://github.com/jmjoy/phper/blob/master/LICENSE).
88
*/
99

10+
use phper_sys::*;
11+
use std::{
12+
mem::{forget, size_of},
13+
ops::{Deref, DerefMut},
14+
};
15+
1016
/// The Box which use php `emalloc` and `efree` to manage memory.
1117
///
12-
/// TODO now feature `allocator_api` is still unstable, using global allocator instead.
13-
pub type EBox<T> = Box<T>;
18+
/// TODO now feature `allocator_api` is still unstable, implement myself.
19+
pub struct EBox<T> {
20+
ptr: *mut T,
21+
}
1422

15-
/// The Vec which use php `emalloc` and `efree` to manage memory.
16-
///
17-
/// TODO now feature `allocator_api` is still unstable, using global allocator instead.
18-
pub type EVec<T> = Vec<T>;
23+
impl<T> EBox<T> {
24+
pub fn new(x: T) -> Self {
25+
unsafe {
26+
let ptr: *mut T = _emalloc(size_of::<T>()).cast();
27+
ptr.write(x);
28+
Self { ptr }
29+
}
30+
}
31+
32+
pub fn into_raw(b: EBox<T>) -> *mut T {
33+
let ptr = b.ptr;
34+
forget(b);
35+
ptr
36+
}
37+
}
38+
39+
impl<T> Deref for EBox<T> {
40+
type Target = T;
41+
42+
fn deref(&self) -> &Self::Target {
43+
unsafe { self.ptr.as_ref().unwrap() }
44+
}
45+
}
46+
47+
impl<T> DerefMut for EBox<T> {
48+
fn deref_mut(&mut self) -> &mut Self::Target {
49+
unsafe { self.ptr.as_mut().unwrap() }
50+
}
51+
}
52+
53+
impl<T> Drop for EBox<T> {
54+
fn drop(&mut self) {
55+
unsafe {
56+
_efree(self.ptr.cast());
57+
}
58+
}
59+
}

phper/src/arrays.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl Array {
4444
let key = key.as_ref();
4545
unsafe {
4646
let value = zend_hash_str_find(&mut self.inner, key.as_ptr().cast(), key.len());
47-
Val::from_mut(value)
47+
Val::from_mut_ptr(value)
4848
}
4949
}
5050

phper/src/classes.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
use std::{
2-
mem::zeroed,
3-
os::raw::c_int,
4-
ptr::null_mut,
5-
sync::atomic::{AtomicPtr, Ordering},
6-
};
7-
8-
use once_cell::sync::OnceCell;
9-
101
use crate::{
2+
arrays::Array,
113
functions::{Argument, Callable, FunctionEntity, FunctionEntry, Method},
124
sys::*,
5+
utils::ensure_end_with_zero,
136
values::SetVal,
147
};
8+
use once_cell::sync::OnceCell;
9+
use std::{
10+
mem::zeroed,
11+
os::raw::c_int,
12+
ptr::{null, null_mut},
13+
sync::atomic::{AtomicPtr, Ordering},
14+
};
1515

1616
pub trait Class: Send + Sync {
1717
fn methods(&mut self) -> &mut [FunctionEntity];
@@ -82,7 +82,15 @@ pub struct ClassEntry {
8282
}
8383

8484
impl ClassEntry {
85-
pub fn as_mut(&mut self) -> *mut zend_class_entry {
85+
pub fn from_ptr<'a>(ptr: *const zend_class_entry) -> &'a Self {
86+
unsafe { (ptr as *const Self).as_ref() }.expect("ptr should not be null")
87+
}
88+
89+
pub fn as_ptr(&self) -> *const zend_class_entry {
90+
&self.inner
91+
}
92+
93+
pub fn as_mut_ptr(&mut self) -> *mut zend_class_entry {
8694
&mut self.inner
8795
}
8896
}
@@ -185,3 +193,15 @@ pub enum Visibility {
185193
Protected = ZEND_ACC_PROTECTED,
186194
Private = ZEND_ACC_PRIVATE,
187195
}
196+
197+
pub(crate) fn get_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {
198+
let name = name.as_ref();
199+
unsafe {
200+
zend_hash_str_find_ptr_lc(
201+
compiler_globals.class_table,
202+
name.as_ptr().cast(),
203+
name.len(),
204+
)
205+
.cast()
206+
}
207+
}

phper/src/errors.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ pub enum Error {
2020
#[error(transparent)]
2121
TypeError(#[from] TypeError),
2222

23+
#[error(transparent)]
24+
ClassNotFound(#[from] ClassNotFoundError),
25+
2326
#[error(transparent)]
2427
Other(#[from] anyhow::Error),
2528
}
@@ -34,7 +37,7 @@ impl Error {
3437

3538
/// PHP type error.
3639
#[derive(thiserror::Error, Debug)]
37-
#[error("{message}")]
40+
#[error("type error: {message}")]
3841
pub struct TypeError {
3942
message: String,
4043
}
@@ -45,6 +48,18 @@ impl TypeError {
4548
}
4649
}
4750

51+
#[derive(thiserror::Error, Debug)]
52+
#[error("class `{class_name}` not found")]
53+
pub struct ClassNotFoundError {
54+
class_name: String,
55+
}
56+
57+
impl ClassNotFoundError {
58+
pub fn new(class_name: String) -> Self {
59+
Self { class_name }
60+
}
61+
}
62+
4863
/// PHP Throwable, can cause throwing an exception when setting to [crate::values::Val].
4964
pub trait Throwable: error::Error {
5065
fn class_entity(&self) -> *const ClassEntity;

phper/src/functions.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ pub(crate) unsafe extern "C" fn invoke(
151151
return_value: *mut zval,
152152
) {
153153
let execute_data = ExecuteData::from_mut(execute_data);
154-
let return_value = Val::from_mut(return_value);
154+
let return_value = Val::from_mut_ptr(return_value);
155155

156156
let num_args = execute_data.common_num_args();
157157
let arg_info = execute_data.common_arg_info();
@@ -178,8 +178,14 @@ pub(crate) unsafe extern "C" fn invoke(
178178
f.call(&mut arguments, return_value);
179179
}
180180
Callable::Method(m, class) => {
181-
let mut this = Object::new(execute_data.get_this(), class.load(Ordering::SeqCst));
182-
m.call(&mut this, &mut arguments, return_value);
181+
let mut this = execute_data.get_this();
182+
let this = this.as_mut().expect("this should not be null");
183+
assert!(this.is_object());
184+
m.call(
185+
Object::from_mut_ptr(this.inner.value.obj),
186+
&mut arguments,
187+
return_value,
188+
);
183189
}
184190
}
185191
}

phper/src/objects.rs

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,53 @@
1-
use crate::{classes::ClassEntry, sys::*, values::Val};
2-
use std::ptr::null_mut;
1+
use crate::{
2+
classes::{get_global_class_entry_ptr, ClassEntry},
3+
sys::*,
4+
values::{SetVal, Val},
5+
ClassNotFoundError,
6+
};
7+
use std::{
8+
mem::zeroed,
9+
ptr::null_mut,
10+
sync::atomic::{AtomicPtr, Ordering},
11+
};
312

13+
/// Wrapper of [crate::sys::zend_object].
14+
#[repr(transparent)]
415
pub struct Object {
5-
val: *mut Val,
6-
class: *mut ClassEntry,
16+
inner: zend_object,
717
}
818

919
impl Object {
10-
pub(crate) fn new<'a>(val: *mut Val, class: *mut ClassEntry) -> Object {
11-
assert!(!val.is_null());
12-
assert!(!class.is_null());
13-
Self { val, class }
20+
pub fn new(class_name: impl AsRef<str>) -> Result<Self, ClassNotFoundError> {
21+
unsafe {
22+
let mut object = zeroed::<Object>();
23+
let class_name = class_name.as_ref();
24+
let ce = get_global_class_entry_ptr(class_name);
25+
if ce.is_null() {
26+
Err(ClassNotFoundError::new(class_name.to_string()))
27+
} else {
28+
zend_object_std_init(object.as_mut_ptr(), ce);
29+
object.inner.handlers = &std_object_handlers;
30+
Ok(object)
31+
}
32+
}
33+
}
34+
35+
pub fn new_std() -> Self {
36+
Self::new("stdClass").expect("stdClass not found")
37+
}
38+
39+
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zend_object) -> &'a mut Object {
40+
(ptr as *mut Object)
41+
.as_mut()
42+
.expect("ptr should not be null")
43+
}
44+
45+
pub fn as_ptr(&self) -> *const zend_object {
46+
&self.inner
47+
}
48+
49+
pub fn as_mut_ptr(&mut self) -> *mut zend_object {
50+
&mut self.inner
1451
}
1552

1653
pub fn get_property(&self, name: impl AsRef<str>) -> &mut Val {
@@ -20,8 +57,8 @@ impl Object {
2057
#[cfg(phper_major_version = "8")]
2158
{
2259
zend_read_property(
23-
self.class as *mut _,
24-
(*self.val).inner.value.obj,
60+
self.inner.ce,
61+
&self.inner as *const _ as *mut _,
2562
name.as_ptr().cast(),
2663
name.len(),
2764
false.into(),
@@ -32,8 +69,8 @@ impl Object {
3269
#[cfg(phper_major_version = "7")]
3370
{
3471
zend_read_property(
35-
self.class as *mut _,
36-
self.val as *mut _,
72+
self.inner.ce,
73+
&self.inner as *const _ as *mut _,
3774
name.as_ptr().cast(),
3875
name.len(),
3976
false.into(),
@@ -42,6 +79,28 @@ impl Object {
4279
}
4380
};
4481

45-
unsafe { Val::from_mut(prop) }
82+
unsafe { Val::from_mut_ptr(prop) }
83+
}
84+
85+
pub fn set_property(&mut self, name: impl AsRef<str>, value: impl SetVal) {
86+
let name = name.as_ref();
87+
let mut val = Val::new(value);
88+
unsafe {
89+
zend_update_property(
90+
self.inner.ce,
91+
&mut self.inner as *mut _,
92+
name.as_ptr().cast(),
93+
name.len(),
94+
val.as_mut_ptr(),
95+
)
96+
}
97+
}
98+
}
99+
100+
impl Drop for Object {
101+
fn drop(&mut self) {
102+
unsafe {
103+
zend_objects_destroy_object(&mut self.inner);
104+
}
46105
}
47106
}

phper/src/types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub enum Type {
1717
Array = IS_ARRAY,
1818
ArrayEx = IS_ARRAY_EX,
1919
Object = IS_OBJECT,
20+
ObjectEx = IS_OBJECT_EX,
2021
Resource = IS_RESOURCE,
2122
Reference = IS_REFERENCE,
2223
ConstantAst = IS_CONSTANT_AST,

phper/src/values.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{
2+
alloc::EBox,
23
arrays::Array,
34
errors::Throwable,
45
objects::Object,
@@ -82,7 +83,7 @@ impl Val {
8283
Self { inner }
8384
}
8485

85-
pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self {
86+
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zval) -> &'a mut Self {
8687
assert!(!ptr.is_null(), "ptr should not be null");
8788
&mut *(ptr as *mut Self)
8889
}
@@ -133,6 +134,10 @@ impl Val {
133134
self.inner.u1.type_info = t as u32;
134135
}
135136

137+
pub fn is_object(&self) -> bool {
138+
matches!(self.get_type(), Type::Object | Type::ObjectEx)
139+
}
140+
136141
pub fn as_bool(&self) -> crate::Result<bool> {
137142
match self.get_type() {
138143
Type::True => Ok(true),
@@ -353,6 +358,16 @@ impl SetVal for Array {
353358
}
354359
}
355360

361+
impl SetVal for EBox<Object> {
362+
fn set_val(mut self, val: &mut Val) {
363+
unsafe {
364+
let object = EBox::into_raw(self);
365+
val.inner.value.obj = object.cast();
366+
val.set_type(Type::ObjectEx);
367+
}
368+
}
369+
}
370+
356371
impl<T: SetVal> SetVal for Option<T> {
357372
fn set_val(self, val: &mut Val) {
358373
match self {

tests/integration/src/values.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use indexmap::map::IndexMap;
2-
use phper::{arrays::Array, modules::Module, values::Val};
2+
use phper::{alloc::EBox, arrays::Array, modules::Module, objects::Object, values::Val};
33
use std::collections::HashMap;
44

55
pub fn integrate(module: &mut Module) {
@@ -82,6 +82,11 @@ fn integrate_returns(module: &mut Module) {
8282
integration_values_return_array,
8383
vec![],
8484
);
85+
module.add_function(
86+
"integration_values_return_object",
87+
integration_values_return_object,
88+
vec![],
89+
);
8590
module.add_function(
8691
"integration_values_return_option_i64_some",
8792
integration_values_return_option_i64_some,
@@ -177,6 +182,12 @@ fn integration_values_return_array(_: &mut [Val]) -> Array {
177182
arr
178183
}
179184

185+
fn integration_values_return_object(_: &mut [Val]) -> EBox<Object> {
186+
let mut object = EBox::new(Object::new_std());
187+
object.set_property("foo", "bar");
188+
object
189+
}
190+
180191
fn integration_values_return_option_i64_some(_: &mut [Val]) -> Option<i64> {
181192
Some(64)
182193
}

0 commit comments

Comments
 (0)