diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 1dda0ee9cd..7a633cae03 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -28,6 +28,7 @@ bind! { _efree, _emalloc, _zend_executor_globals, + _zend_compiler_globals, _sapi_globals_struct, _sapi_module_struct, _zend_expected_type, @@ -43,6 +44,7 @@ bind! { _zval_struct__bindgen_ty_2, _zend_known_string_id, // ext_php_rs_executor_globals, + // ext_php_rs_compiler_globals, // ext_php_rs_php_build_id, // ext_php_rs_zend_object_alloc, // ext_php_rs_zend_object_release, @@ -254,6 +256,7 @@ bind! { zend_class_serialize_deny, zend_class_unserialize_deny, zend_executor_globals, + zend_compiler_globals, sapi_module_struct, zend_objects_store_del, zend_hash_move_forward_ex, @@ -265,6 +268,7 @@ bind! { gc_possible_root, ZEND_ACC_NOT_SERIALIZABLE, executor_globals, + compiler_globals, php_core_globals, core_globals, sapi_globals_struct, @@ -274,6 +278,7 @@ bind! { __zend_malloc, tsrm_get_ls_cache, executor_globals_offset, + compiler_globals_offset, core_globals_offset, sapi_globals_offset, php_file_globals, diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 16a3d11697..8802958d0f 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -293,6 +293,7 @@ pub type zend_object = _zend_object; pub type zend_resource = _zend_resource; pub type zend_reference = _zend_reference; pub type zend_ast_ref = _zend_ast_ref; +pub type zend_ast = _zend_ast; pub type dtor_func_t = ::std::option::Option; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -697,6 +698,16 @@ extern "C" { len: usize, ) -> *mut ::std::os::raw::c_void; } +pub type zend_ast_kind = u16; +pub type zend_ast_attr = u16; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_ast { + pub kind: zend_ast_kind, + pub attr: zend_ast_attr, + pub lineno: u32, + pub child: [*mut zend_ast; 1usize], +} extern "C" { pub fn gc_possible_root(ref_: *mut zend_refcounted); } @@ -1266,6 +1277,24 @@ pub union _znode_op { } pub type znode_op = _znode_op; #[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_declarables { + pub ticks: zend_long, +} +pub type zend_declarables = _zend_declarables; +#[repr(C)] +pub struct _zend_file_context { + pub declarables: zend_declarables, + pub current_namespace: *mut zend_string, + pub in_namespace: bool, + pub has_bracketed_namespaces: bool, + pub imports: *mut HashTable, + pub imports_function: *mut HashTable, + pub imports_const: *mut HashTable, + pub seen_symbols: HashTable, +} +pub type zend_file_context = _zend_file_context; +#[repr(C)] #[derive(Copy, Clone)] pub struct _zend_op { pub handler: *const ::std::os::raw::c_void, @@ -1281,6 +1310,16 @@ pub struct _zend_op { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct _zend_brk_cont_element { + pub start: ::std::os::raw::c_int, + pub cont: ::std::os::raw::c_int, + pub brk: ::std::os::raw::c_int, + pub parent: ::std::os::raw::c_int, + pub is_switch: bool, +} +pub type zend_brk_cont_element = _zend_brk_cont_element; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct _zend_try_catch_element { pub try_op: u32, pub catch_op: u32, @@ -1298,6 +1337,20 @@ pub struct _zend_live_range { pub type zend_live_range = _zend_live_range; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct _zend_oparray_context { + pub opcodes_size: u32, + pub vars_size: ::std::os::raw::c_int, + pub literals_size: ::std::os::raw::c_int, + pub fast_call_var: u32, + pub try_catch_offset: u32, + pub current_brk_cont: ::std::os::raw::c_int, + pub last_brk_cont: ::std::os::raw::c_int, + pub brk_cont_array: *mut zend_brk_cont_element, + pub labels: *mut HashTable, +} +pub type zend_oparray_context = _zend_oparray_context; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct _zend_property_info { pub offset: u32, pub flags: u32, @@ -1429,7 +1482,60 @@ pub struct __jmp_buf_tag { pub __saved_mask: __sigset_t, } pub type jmp_buf = [__jmp_buf_tag; 1usize]; +pub type zend_compiler_globals = _zend_compiler_globals; pub type zend_executor_globals = _zend_executor_globals; +#[repr(C)] +pub struct _zend_compiler_globals { + pub loop_var_stack: zend_stack, + pub active_class_entry: *mut zend_class_entry, + pub compiled_filename: *mut zend_string, + pub zend_lineno: ::std::os::raw::c_int, + pub active_op_array: *mut zend_op_array, + pub function_table: *mut HashTable, + pub class_table: *mut HashTable, + pub auto_globals: *mut HashTable, + pub parse_error: u8, + pub in_compilation: bool, + pub short_tags: bool, + pub unclean_shutdown: bool, + pub ini_parser_unbuffered_errors: bool, + pub open_files: zend_llist, + pub ini_parser_param: *mut _zend_ini_parser_param, + pub skip_shebang: bool, + pub increment_lineno: bool, + pub variable_width_locale: bool, + pub ascii_compatible_locale: bool, + pub doc_comment: *mut zend_string, + pub extra_fn_flags: u32, + pub compiler_options: u32, + pub context: zend_oparray_context, + pub file_context: zend_file_context, + pub arena: *mut zend_arena, + pub interned_strings: HashTable, + pub script_encoding_list: *mut *const zend_encoding, + pub script_encoding_list_size: usize, + pub multibyte: bool, + pub detect_unicode: bool, + pub encoding_declared: bool, + pub ast: *mut zend_ast, + pub ast_arena: *mut zend_arena, + pub delayed_oplines_stack: zend_stack, + pub memoized_exprs: *mut HashTable, + pub memoize_mode: zend_memoize_mode, + pub map_ptr_real_base: *mut ::std::os::raw::c_void, + pub map_ptr_base: *mut ::std::os::raw::c_void, + pub map_ptr_size: usize, + pub map_ptr_last: usize, + pub delayed_variance_obligations: *mut HashTable, + pub delayed_autoloads: *mut HashTable, + pub unlinked_uses: *mut HashTable, + pub current_linking_class: *mut zend_class_entry, + pub rtd_key_counter: u32, + pub short_circuiting_opnums: zend_stack, +} +extern "C" { + pub static mut compiler_globals: _zend_compiler_globals; +} extern "C" { pub static mut executor_globals: zend_executor_globals; } @@ -1477,6 +1583,20 @@ extern "C" { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct _zend_encoding { + _unused: [u8; 0], +} +pub type zend_encoding = _zend_encoding; +pub type zend_arena = _zend_arena; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_arena { + pub ptr: *mut ::std::os::raw::c_char, + pub end: *mut ::std::os::raw::c_char, + pub prev: *mut zend_arena, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct _zend_call_stack { pub base: *mut ::std::os::raw::c_void, pub max_size: usize, @@ -1496,6 +1616,10 @@ pub struct _zend_fiber { _unused: [u8; 0], } pub type zend_fiber = _zend_fiber; +pub const zend_memoize_mode_ZEND_MEMOIZE_NONE: zend_memoize_mode = 0; +pub const zend_memoize_mode_ZEND_MEMOIZE_COMPILE: zend_memoize_mode = 1; +pub const zend_memoize_mode_ZEND_MEMOIZE_FETCH: zend_memoize_mode = 2; +pub type zend_memoize_mode = ::std::os::raw::c_uint; #[repr(C)] pub struct _zend_executor_globals { pub uninitialized_zval: zval, @@ -2391,6 +2515,21 @@ extern "C" { module_number: ::std::os::raw::c_int, ) -> zend_result; } +pub type zend_ini_parser_cb_t = ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut zval, + arg2: *mut zval, + arg3: *mut zval, + callback_type: ::std::os::raw::c_int, + arg: *mut ::std::os::raw::c_void, + ), +>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_ini_parser_param { + pub ini_parser_cb: zend_ini_parser_cb_t, + pub arg: *mut ::std::os::raw::c_void, +} extern "C" { pub fn zend_register_bool_constant( name: *const ::std::os::raw::c_char, diff --git a/src/ffi.rs b/src/ffi.rs index 52004638c0..83817cb48e 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -26,6 +26,7 @@ extern "C" { pub fn ext_php_rs_zend_object_alloc(obj_size: usize, ce: *mut zend_class_entry) -> *mut c_void; pub fn ext_php_rs_zend_object_release(obj: *mut zend_object); pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals; + pub fn ext_php_rs_compiler_globals() -> *mut zend_compiler_globals; pub fn ext_php_rs_process_globals() -> *mut php_core_globals; pub fn ext_php_rs_sapi_globals() -> *mut sapi_globals_struct; pub fn ext_php_rs_file_globals() -> *mut php_file_globals; diff --git a/src/wrapper.c b/src/wrapper.c index 78ca02c2f3..745262afb7 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -40,6 +40,18 @@ zend_executor_globals *ext_php_rs_executor_globals() { #endif } +zend_compiler_globals *ext_php_rs_compiler_globals() { +#ifdef ZTS +#ifdef ZEND_ENABLE_STATIC_TSRMLS_CACHE + return TSRMG_FAST_BULK_STATIC(compiler_globals_offset, zend_compiler_globals); +#else + return TSRMG_FAST_BULK(compiler_globals_offset, zend_compiler_globals *); +#endif +#else + return &compiler_globals; +#endif +} + php_core_globals *ext_php_rs_process_globals() { #ifdef ZTS #ifdef ZEND_ENABLE_STATIC_TSRMLS_CACHE diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 9cbd6ef4d4..b0193c9a44 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -13,12 +13,12 @@ use crate::exception::PhpResult; #[cfg(php82)] use crate::ffi::zend_atomic_bool_store; use crate::ffi::{ - _sapi_module_struct, _zend_executor_globals, ext_php_rs_executor_globals, - ext_php_rs_file_globals, ext_php_rs_process_globals, ext_php_rs_sapi_globals, - ext_php_rs_sapi_module, php_core_globals, php_file_globals, sapi_globals_struct, - sapi_header_struct, sapi_headers_struct, sapi_request_info, zend_ini_entry, - zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, - TRACK_VARS_POST, TRACK_VARS_SERVER, + _sapi_module_struct, _zend_compiler_globals, _zend_executor_globals, + ext_php_rs_compiler_globals, ext_php_rs_executor_globals, ext_php_rs_file_globals, + ext_php_rs_process_globals, ext_php_rs_sapi_globals, ext_php_rs_sapi_module, php_core_globals, + php_file_globals, sapi_globals_struct, sapi_header_struct, sapi_headers_struct, + sapi_request_info, zend_ini_entry, zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, + TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, TRACK_VARS_SERVER, }; #[cfg(not(php81))] use crate::ffi::{_zend_hash_find_known_hash, _zend_string}; @@ -35,9 +35,6 @@ use super::linked_list::ZendLinkedListIterator; /// Stores global variables used in the PHP executor. pub type ExecutorGlobals = _zend_executor_globals; -/// Stores the SAPI module used in the PHP executor. -pub type SapiModule = _sapi_module_struct; - impl ExecutorGlobals { /// Returns a reference to the PHP executor globals. /// @@ -226,6 +223,69 @@ impl ExecutorGlobals { } } +pub type CompilerGlobals = _zend_compiler_globals; + +impl CompilerGlobals { + /// Returns a reference to the PHP compiler globals. + /// + /// The compiler globals are guarded by a [`RwLock`]. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + /// + /// # Panics + /// + /// * If static executor globals are not set + pub fn get() -> GlobalReadGuard { + // SAFETY: PHP compiler globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { ext_php_rs_compiler_globals().as_ref() } + .expect("Static compiler globals were invalid"); + + cfg_if::cfg_if! { + if #[cfg(php_zts)] { + let guard = lock::GLOBALS_LOCK.with(|l| l.read_arc()); + } else { + let guard = lock::GLOBALS_LOCK.read_arc(); + } + } + + GlobalReadGuard { globals, guard } + } + + /// Returns a mutable reference to the PHP compiler globals. + /// + /// The compiler globals are guarded by a [`RwLock`]. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + /// + /// # Panics + /// + /// * If static compiler globals are not set + pub fn get_mut() -> GlobalWriteGuard { + // SAFETY: PHP compiler globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { ext_php_rs_compiler_globals().as_mut() } + .expect("Static compiler globals were invalid"); + + cfg_if::cfg_if! { + if #[cfg(php_zts)] { + let guard = lock::GLOBALS_LOCK.with(|l| l.write_arc()); + } else { + let guard = lock::GLOBALS_LOCK.write_arc(); + } + } + + GlobalWriteGuard { globals, guard } + } +} + +/// Stores the SAPI module used in the PHP executor. +pub type SapiModule = _sapi_module_struct; + impl SapiModule { /// Returns a reference to the PHP SAPI module. /// @@ -839,11 +899,10 @@ impl DerefMut for GlobalWriteGuard { #[cfg(feature = "embed")] #[cfg(test)] mod embed_tests { + use super::*; use crate::embed::Embed; use std::os::raw::c_char; - use super::SapiHeader; - #[test] fn test_sapi_header() { Embed::run(|| { @@ -867,4 +926,22 @@ mod embed_tests { } }); } + + #[test] + fn test_executor_globals() { + let state = ExecutorGlobals::get().active; + ExecutorGlobals::get_mut().active = !state; + let changed = ExecutorGlobals::get().active; + ExecutorGlobals::get_mut().active = state; + assert_eq!(changed, !state); + } + + #[test] + fn test_compiler_globals() { + let state = CompilerGlobals::get().in_compilation; + CompilerGlobals::get_mut().in_compilation = !state; + let changed = CompilerGlobals::get().in_compilation; + CompilerGlobals::get_mut().in_compilation = state; + assert_eq!(changed, !state); + } }