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

View File

@ -25,7 +25,7 @@ pub use ini_entry_def::IniEntryDef;
pub use module::ModuleEntry; pub use module::ModuleEntry;
#[cfg(feature = "embed")] #[cfg(feature = "embed")]
pub(crate) use try_catch::panic_wrapper; 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`. // Used as the format string for `php_printf`.
const FORMAT_STR: &[u8] = b"%s\0"; 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 /// * `Ok(R)` - The result of the function
/// * `Err(CatchError)` - A bailout occurred during the execution /// * `Err(CatchError)` - A bailout occurred during the execution
pub fn try_catch<R, F: FnMut() -> R + RefUnwindSafe>( pub fn try_catch<R, F: FnMut() -> R + RefUnwindSafe>(func: F) -> Result<R, CatchError> {
func: F, do_try_catch(func, false)
first: bool, }
) -> Result<R, CatchError> {
/// 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 mut panic_ptr = null_mut();
let has_bailout = unsafe { let has_bailout = unsafe {
if first { if first {
@ -91,8 +109,7 @@ mod tests {
#[test] #[test]
fn test_catch() { fn test_catch() {
Embed::run(|| { Embed::run(|| {
let catch = try_catch( let catch = try_catch(|| {
|| {
unsafe { unsafe {
bailout(); bailout();
} }
@ -101,9 +118,7 @@ mod tests {
{ {
assert!(false); assert!(false);
} }
}, });
false,
);
assert!(catch.is_err()); assert!(catch.is_err());
}); });
@ -112,12 +127,9 @@ mod tests {
#[test] #[test]
fn test_no_catch() { fn test_no_catch() {
Embed::run(|| { Embed::run(|| {
let catch = try_catch( let catch = try_catch(|| {
|| {
assert!(true); assert!(true);
}, });
false,
);
assert!(catch.is_ok()); assert!(catch.is_ok());
}); });
@ -141,24 +153,18 @@ mod tests {
#[should_panic] #[should_panic]
fn test_panic() { fn test_panic() {
Embed::run(|| { Embed::run(|| {
let _ = try_catch( let _ = try_catch(|| {
|| {
panic!("should panic"); panic!("should panic");
}, });
false,
);
}); });
} }
#[test] #[test]
fn test_return() { fn test_return() {
let foo = Embed::run(|| { let foo = Embed::run(|| {
let result = try_catch( let result = try_catch(|| {
|| {
return "foo"; return "foo";
}, });
false,
);
assert!(result.is_ok()); assert!(result.is_ok());
@ -172,17 +178,14 @@ mod tests {
fn test_memory_leak() { fn test_memory_leak() {
let mut ptr = null_mut(); let mut ptr = null_mut();
let _ = try_catch( let _ = try_catch(|| {
|| {
let mut result = "foo".to_string(); let mut result = "foo".to_string();
ptr = &mut result; ptr = &mut result;
unsafe { unsafe {
bailout(); bailout();
} }
}, });
false,
);
// Check that the string is never released // Check that the string is never released
let result = unsafe { &*ptr as &str }; let result = unsafe { &*ptr as &str };

View File

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

View File

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