feat(sapi): split try_catch / try_catch_first, global feature for test

This commit is contained in:
Joel Wurtz 2023-10-31 10:31:53 +01:00
parent 4f74bfcd8a
commit 411ee6c176
No known key found for this signature in database
GPG Key ID: ED264D1967A51B0D
5 changed files with 70 additions and 82 deletions

View File

@ -90,7 +90,7 @@ impl Embed {
zend_stream_init_filename(&mut file_handle, path.as_ptr());
}
let exec_result = try_catch(|| unsafe { php_execute_script(&mut file_handle) }, false);
let exec_result = try_catch(|| unsafe { php_execute_script(&mut file_handle) });
match exec_result {
Err(_) => Err(EmbedError::CatchError),
@ -184,16 +184,13 @@ impl Embed {
let mut result = Zval::new();
let exec_result = try_catch(
|| unsafe {
zend_eval_string(
cstr.as_ptr() as *const c_char,
&mut result,
b"run\0".as_ptr() as *const _,
)
},
false,
);
let exec_result = try_catch(|| unsafe {
zend_eval_string(
cstr.as_ptr() as *const c_char,
&mut result,
b"run\0".as_ptr() as *const _,
)
});
match exec_result {
Err(_) => Err(EmbedError::CatchError),

View File

@ -25,7 +25,7 @@ pub use ini_entry_def::IniEntryDef;
pub use module::ModuleEntry;
#[cfg(feature = "embed")]
pub(crate) use try_catch::panic_wrapper;
pub use try_catch::{bailout, try_catch};
pub use try_catch::{bailout, try_catch, try_catch_first};
// Used as the format string for `php_printf`.
const FORMAT_STR: &[u8] = b"%s\0";

View File

@ -28,10 +28,28 @@ pub(crate) unsafe extern "C" fn panic_wrapper<R, F: FnMut() -> R + RefUnwindSafe
///
/// * `Ok(R)` - The result of the function
/// * `Err(CatchError)` - A bailout occurred during the execution
pub fn try_catch<R, F: FnMut() -> R + RefUnwindSafe>(
func: F,
first: bool,
) -> Result<R, CatchError> {
pub fn try_catch<R, F: FnMut() -> R + RefUnwindSafe>(func: F) -> Result<R, CatchError> {
do_try_catch(func, false)
}
/// PHP propose a try catch mechanism in C using setjmp and longjmp (bailout)
/// It store the arg of setjmp into the bailout field of the global executor
/// If a bailout is triggered, the executor will jump to the setjmp and restore the previous setjmp
///
/// try_catch_first allow to use this mechanism
///
/// This functions differs from ['try_catch'] as it also initialize the bailout mechanism
/// for the first time
///
/// # Returns
///
/// * `Ok(R)` - The result of the function
/// * `Err(CatchError)` - A bailout occurred during the execution
pub fn try_catch_first<R, F: FnMut() -> R + RefUnwindSafe>(func: F) -> Result<R, CatchError> {
do_try_catch(func, true)
}
fn do_try_catch<R, F: FnMut() -> R + RefUnwindSafe>(func: F, first: bool) -> Result<R, CatchError> {
let mut panic_ptr = null_mut();
let has_bailout = unsafe {
if first {
@ -91,19 +109,16 @@ mod tests {
#[test]
fn test_catch() {
Embed::run(|| {
let catch = try_catch(
|| {
unsafe {
bailout();
}
let catch = try_catch(|| {
unsafe {
bailout();
}
#[allow(unreachable_code)]
{
assert!(false);
}
},
false,
);
#[allow(unreachable_code)]
{
assert!(false);
}
});
assert!(catch.is_err());
});
@ -112,12 +127,9 @@ mod tests {
#[test]
fn test_no_catch() {
Embed::run(|| {
let catch = try_catch(
|| {
assert!(true);
},
false,
);
let catch = try_catch(|| {
assert!(true);
});
assert!(catch.is_ok());
});
@ -141,24 +153,18 @@ mod tests {
#[should_panic]
fn test_panic() {
Embed::run(|| {
let _ = try_catch(
|| {
panic!("should panic");
},
false,
);
let _ = try_catch(|| {
panic!("should panic");
});
});
}
#[test]
fn test_return() {
let foo = Embed::run(|| {
let result = try_catch(
|| {
return "foo";
},
false,
);
let result = try_catch(|| {
return "foo";
});
assert!(result.is_ok());
@ -172,17 +178,14 @@ mod tests {
fn test_memory_leak() {
let mut ptr = null_mut();
let _ = try_catch(
|| {
let mut result = "foo".to_string();
ptr = &mut result;
let _ = try_catch(|| {
let mut result = "foo".to_string();
ptr = &mut result;
unsafe {
bailout();
}
},
false,
);
unsafe {
bailout();
}
});
// Check that the string is never released
let result = unsafe { &*ptr as &str };

View File

@ -1,14 +1,12 @@
#![cfg_attr(windows, feature(abi_vectorcall))]
#![cfg(feature = "embed")]
extern crate ext_php_rs;
#[cfg(feature = "embed")]
use ext_php_rs::embed::Embed;
#[cfg(feature = "embed")]
use ext_php_rs::ffi::zend_register_module_ex;
use ext_php_rs::prelude::*;
#[test]
#[cfg(feature = "embed")]
fn test_module() {
Embed::run(|| {
// Allow to load the module

View File

@ -1,25 +1,19 @@
#![cfg_attr(windows, feature(abi_vectorcall))]
#![cfg(feature = "embed")]
extern crate ext_php_rs;
#[cfg(feature = "embed")]
use std::ffi::c_char;
#[cfg(feature = "embed")]
use ext_php_rs::builders::SapiBuilder;
#[cfg(feature = "embed")]
use ext_php_rs::embed::{ext_php_rs_sapi_startup, Embed};
#[cfg(feature = "embed")]
use ext_php_rs::ffi::{
php_module_shutdown, php_module_startup, php_request_shutdown, php_request_startup,
sapi_shutdown, sapi_startup, ZEND_RESULT_CODE_SUCCESS,
};
use ext_php_rs::prelude::*;
#[cfg(feature = "embed")]
use ext_php_rs::zend::try_catch;
use ext_php_rs::zend::try_catch_first;
use std::ffi::c_char;
#[cfg(feature = "embed")]
static mut LAST_OUTPUT: String = String::new();
#[cfg(feature = "embed")]
extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize {
let char = unsafe { std::slice::from_raw_parts(str as *const u8, str_length) };
let string = String::from_utf8_lossy(char);
@ -34,7 +28,6 @@ extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize {
}
#[test]
#[cfg(feature = "embed")]
fn test_sapi() {
let mut builder = SapiBuilder::new("test", "Test");
builder = builder.ub_write_function(output_tester);
@ -58,26 +51,23 @@ fn test_sapi() {
assert_eq!(result, ZEND_RESULT_CODE_SUCCESS);
let _ = try_catch(
|| {
let result = Embed::eval("$foo = hello_world('foo');");
let _ = try_catch_first(|| {
let result = Embed::eval("$foo = hello_world('foo');");
assert!(result.is_ok());
assert!(result.is_ok());
let zval = result.unwrap();
let zval = result.unwrap();
assert!(zval.is_string());
assert!(zval.is_string());
let string = zval.string().unwrap();
let string = zval.string().unwrap();
assert_eq!(string.to_string(), "Hello, foo!");
assert_eq!(string.to_string(), "Hello, foo!");
let result = Embed::eval("var_dump($foo);");
let result = Embed::eval("var_dump($foo);");
assert!(result.is_ok());
},
true,
);
assert!(result.is_ok());
});
unsafe {
php_request_shutdown(std::ptr::null_mut());