Skip to content

Commit f7fd7c2

Browse files
committed
Refactor Handler of ArgumentCountError and TypeError.
1 parent aee0a13 commit f7fd7c2

File tree

13 files changed

+224
-69
lines changed

13 files changed

+224
-69
lines changed

phper/src/classes.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::{
22
functions::{Argument, Callable, FunctionEntity, FunctionEntry, Method},
33
sys::*,
44
utils::ensure_end_with_zero,
5+
ClassNotFoundError,
56
};
67
use once_cell::sync::OnceCell;
78
use std::{
@@ -76,6 +77,15 @@ pub struct ClassEntry {
7677
}
7778

7879
impl ClassEntry {
80+
pub fn from_globals<'a>(class_name: impl AsRef<str>) -> Result<&'a Self, ClassNotFoundError> {
81+
let name = class_name.as_ref();
82+
let ptr: *mut Self = find_global_class_entry_ptr(name).cast();
83+
unsafe {
84+
ptr.as_ref()
85+
.ok_or_else(|| ClassNotFoundError::new(name.to_string()))
86+
}
87+
}
88+
7989
pub fn from_ptr<'a>(ptr: *const zend_class_entry) -> &'a Self {
8090
unsafe { (ptr as *const Self).as_ref() }.expect("ptr should not be null")
8191
}
@@ -89,6 +99,19 @@ impl ClassEntry {
8999
}
90100
}
91101

102+
fn find_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {
103+
let name = name.as_ref();
104+
let name = name.to_lowercase();
105+
unsafe {
106+
phper_zend_hash_str_find_ptr(
107+
compiler_globals.class_table,
108+
name.as_ptr().cast(),
109+
name.len(),
110+
)
111+
.cast()
112+
}
113+
}
114+
92115
pub struct ClassEntity {
93116
pub(crate) name: String,
94117
pub(crate) entry: AtomicPtr<ClassEntry>,
@@ -186,15 +209,3 @@ pub enum Visibility {
186209
Protected = ZEND_ACC_PROTECTED,
187210
Private = ZEND_ACC_PRIVATE,
188211
}
189-
190-
pub(crate) fn get_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {
191-
let name = name.as_ref();
192-
unsafe {
193-
phper_zend_hash_str_find_ptr(
194-
compiler_globals.class_table,
195-
name.as_ptr().cast(),
196-
name.len(),
197-
)
198-
.cast()
199-
}
200-
}

phper/src/errors.rs

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
1-
use crate::{classes::ClassEntity, modules::read_global_module, Error::Other};
1+
use crate::{
2+
classes::{ClassEntity, ClassEntry},
3+
modules::read_global_module,
4+
Error::{ClassNotFound, Other},
5+
};
26
use anyhow::anyhow;
37
use std::{error, ffi::FromBytesWithNulError, io, str::Utf8Error};
48

9+
pub(crate) const EXCEPTION_CLASS_NAME: &'static str = "Phper\\OtherException";
10+
11+
/// PHP Throwable, can cause throwing an exception when setting to [crate::values::Val].
12+
pub trait Throwable: error::Error {
13+
fn class_entry(&self) -> &ClassEntry;
14+
15+
fn code(&self) -> u64 {
16+
0
17+
}
18+
19+
fn message(&self) -> String {
20+
self.to_string()
21+
}
22+
}
23+
524
/// Type of [std::result::Result]<T, [crate::Error]>.
625
pub type Result<T> = std::result::Result<T, self::Error>;
726

@@ -23,6 +42,9 @@ pub enum Error {
2342
#[error(transparent)]
2443
ClassNotFound(#[from] ClassNotFoundError),
2544

45+
#[error(transparent)]
46+
ArgumentCountError(#[from] ArgumentCountError),
47+
2648
#[error(transparent)]
2749
Other(#[from] anyhow::Error),
2850
}
@@ -35,6 +57,27 @@ impl Error {
3557
}
3658
}
3759

60+
// TODO Add message() implement.
61+
impl Throwable for Error {
62+
fn class_entry(&self) -> &ClassEntry {
63+
match self {
64+
Self::TypeError(e) => e.class_entry(),
65+
Self::ClassNotFound(e) => e.class_entry(),
66+
Self::ArgumentCountError(e) => e.class_entry(),
67+
_ => ClassEntry::from_globals(EXCEPTION_CLASS_NAME).unwrap(),
68+
}
69+
}
70+
71+
fn code(&self) -> u64 {
72+
match self {
73+
Self::TypeError(e) => e.code(),
74+
Self::ClassNotFound(e) => e.code(),
75+
Self::ArgumentCountError(e) => e.code(),
76+
_ => 0,
77+
}
78+
}
79+
}
80+
3881
/// PHP type error.
3982
#[derive(thiserror::Error, Debug)]
4083
#[error("type error: {message}")]
@@ -48,8 +91,14 @@ impl TypeError {
4891
}
4992
}
5093

94+
impl Throwable for TypeError {
95+
fn class_entry(&self) -> &ClassEntry {
96+
ClassEntry::from_globals("TypeError").unwrap()
97+
}
98+
}
99+
51100
#[derive(thiserror::Error, Debug)]
52-
#[error("class `{class_name}` not found")]
101+
#[error("Class '{class_name}' not found")]
53102
pub struct ClassNotFoundError {
54103
class_name: String,
55104
}
@@ -60,25 +109,32 @@ impl ClassNotFoundError {
60109
}
61110
}
62111

63-
/// PHP Throwable, can cause throwing an exception when setting to [crate::values::Val].
64-
pub trait Throwable: error::Error {
65-
fn class_entity(&self) -> *const ClassEntity;
66-
fn code(&self) -> u64;
112+
impl Throwable for ClassNotFoundError {
113+
fn class_entry(&self) -> &ClassEntry {
114+
ClassEntry::from_globals("Error").unwrap()
115+
}
67116
}
68117

69-
pub(crate) const EXCEPTION_CLASS_NAME: &'static str = "Phper\\OtherException";
118+
#[derive(thiserror::Error, Debug)]
119+
#[error("{function_name}(): expects at least {expect_count} parameter(s), {given_count} given")]
120+
pub struct ArgumentCountError {
121+
function_name: String,
122+
expect_count: usize,
123+
given_count: usize,
124+
}
70125

71-
impl Throwable for Error {
72-
fn class_entity(&self) -> *const ClassEntity {
73-
read_global_module(|module| {
74-
module
75-
.class_entities
76-
.get(EXCEPTION_CLASS_NAME)
77-
.expect("Must be called after module init") as *const _
78-
})
126+
impl ArgumentCountError {
127+
pub fn new(function_name: String, expect_count: usize, given_count: usize) -> Self {
128+
Self {
129+
function_name,
130+
expect_count,
131+
given_count,
132+
}
79133
}
134+
}
80135

81-
fn code(&self) -> u64 {
82-
500
136+
impl Throwable for ArgumentCountError {
137+
fn class_entry(&self) -> &ClassEntry {
138+
ClassEntry::from_globals("ArgumentCountError").unwrap()
83139
}
84140
}

phper/src/functions.rs

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ use std::{mem::zeroed, os::raw::c_char, sync::atomic::AtomicPtr};
22

33
use crate::{
44
classes::ClassEntry,
5+
errors::ArgumentCountError,
56
objects::Object,
67
sys::*,
78
utils::ensure_end_with_zero,
89
values::{ExecuteData, SetVal, Val},
910
};
11+
use std::{slice::from_raw_parts, str, str::Utf8Error};
1012

1113
pub trait Function: Send + Sync {
1214
fn call(&self, arguments: &mut [Val], return_value: &mut Val);
@@ -142,6 +144,38 @@ impl Argument {
142144
}
143145
}
144146

147+
#[repr(transparent)]
148+
pub struct ZendFunction {
149+
inner: zend_function,
150+
}
151+
152+
impl ZendFunction {
153+
pub(crate) unsafe fn from_mut_ptr<'a>(ptr: *mut zend_function) -> &'a mut ZendFunction {
154+
let ptr = ptr as *mut Self;
155+
ptr.as_mut().expect("ptr shouldn't be null")
156+
}
157+
158+
#[inline]
159+
pub fn as_ptr(&self) -> *const zend_function {
160+
&self.inner
161+
}
162+
163+
#[inline]
164+
pub fn as_mut_ptr(&mut self) -> *mut zend_function {
165+
&mut self.inner
166+
}
167+
168+
pub fn get_name(&self) -> Result<String, Utf8Error> {
169+
unsafe {
170+
let s = get_function_or_method_name(self.as_ptr());
171+
let buf = from_raw_parts(&(*s).val as *const c_char as *const u8, (*s).len);
172+
let result = str::from_utf8(buf).map(ToString::to_string);
173+
phper_zend_string_release(s);
174+
result
175+
}
176+
}
177+
}
178+
145179
pub(crate) unsafe extern "C" fn invoke(
146180
execute_data: *mut zend_execute_data,
147181
return_value: *mut zval,
@@ -157,13 +191,20 @@ pub(crate) unsafe extern "C" fn invoke(
157191
let handler = handler.as_ref().expect("handler is null");
158192

159193
// Check arguments count.
160-
if execute_data.num_args() < execute_data.common_required_num_args() {
161-
warning!(
162-
"expects at least {} parameter(s), {} given\0",
163-
execute_data.common_required_num_args(),
164-
execute_data.num_args()
165-
);
166-
SetVal::set_val((), return_value);
194+
let num_args = execute_data.num_args() as usize;
195+
let required_num_args = execute_data.common_required_num_args() as usize;
196+
if num_args < required_num_args {
197+
let func_name = execute_data.func().get_name();
198+
let result = func_name
199+
.map(|func_name| {
200+
Err::<(), _>(ArgumentCountError::new(
201+
func_name,
202+
required_num_args,
203+
num_args,
204+
))
205+
})
206+
.map_err(crate::Error::Utf8);
207+
SetVal::set_val(result, return_value);
167208
return;
168209
}
169210

phper/src/objects.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
classes::get_global_class_entry_ptr,
2+
classes::ClassEntry,
33
sys::*,
44
values::{SetVal, Val},
55
ClassNotFoundError,
@@ -16,24 +16,22 @@ pub struct Object {
1616
}
1717

1818
impl Object {
19-
pub fn new(class_name: impl AsRef<str>) -> Result<Self, ClassNotFoundError> {
19+
pub fn new(class_entry: &ClassEntry) -> Self {
2020
unsafe {
2121
let mut object = zeroed::<Object>();
22-
let class_name = class_name.as_ref();
23-
let ce = get_global_class_entry_ptr(class_name);
24-
if ce.is_null() {
25-
forget(object);
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-
}
22+
zend_object_std_init(object.as_mut_ptr(), class_entry.as_ptr() as *mut _);
23+
object.inner.handlers = &std_object_handlers;
24+
object
3225
}
3326
}
3427

35-
pub fn new_std() -> Self {
36-
Self::new("stdclass").expect("stdClass not found")
28+
pub fn new_by_class_name(class_name: impl AsRef<str>) -> Result<Self, ClassNotFoundError> {
29+
let class_entry = ClassEntry::from_globals(class_name)?;
30+
Ok(Self::new(class_entry))
31+
}
32+
33+
pub fn new_by_std_class() -> Self {
34+
Self::new_by_class_name("stdclass").unwrap()
3735
}
3836

3937
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zend_object) -> &'a mut Object {

phper/src/strings.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::sys::*;
1+
use crate::{alloc::EBox, sys::*};
2+
use std::{slice::from_raw_parts, str::Utf8Error};
23

34
/// Wrapper of [crate::sys::zend_string].
45
#[repr(transparent)]
@@ -9,7 +10,7 @@ pub struct ZendString {
910
impl ZendString {
1011
// TODO Remove dead_code tag
1112
#[allow(dead_code)]
12-
pub(crate) unsafe fn from_mut_ptr<'a>(ptr: *mut zend_array) -> &'a mut Self {
13+
pub(crate) unsafe fn from_mut_ptr<'a>(ptr: *mut zend_string) -> &'a mut Self {
1314
let ptr = ptr as *mut Self;
1415
ptr.as_mut().expect("ptr shouldn't be null")
1516
}
@@ -21,4 +22,8 @@ impl ZendString {
2122
pub fn as_mut_ptr(&mut self) -> *mut zend_string {
2223
&mut self.inner
2324
}
25+
26+
pub unsafe fn from_raw(s: *mut zend_string) -> EBox<ZendString> {
27+
EBox::from_raw(s.cast())
28+
}
2429
}

0 commit comments

Comments
 (0)