1414 +----------------------------------------------------------------------+
1515 | Authors: Levi Morrison <[email protected] > | 1616 | Sammy Kaye Powers <[email protected] > | 17+ | Bob Weinand <[email protected] > | 1718 +----------------------------------------------------------------------+
1819*/
1920
2324#include "zend_llist.h"
2425#include "zend_vm.h"
2526
26- #define ZEND_OBSERVER_DATA (function ) \
27- ZEND_OP_ARRAY_EXTENSION((&(function)->common), ZEND_USER_CODE((function)->type) \
28- ? zend_observer_fcall_op_array_extension : zend_observer_fcall_internal_function_extension)
29-
3027#define ZEND_OBSERVER_NOT_OBSERVED ((void *) 2)
3128
32- #define ZEND_OBSERVABLE_FN (function ) \
33- (ZEND_MAP_PTR(function->common.run_time_cache) && !(function->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
34-
3529static zend_llist zend_observers_fcall_list ;
3630static zend_llist zend_observer_function_declared_callbacks ;
3731static zend_llist zend_observer_class_linked_callbacks ;
@@ -46,8 +40,6 @@ bool zend_observer_errors_observed;
4640bool zend_observer_function_declared_observed ;
4741bool zend_observer_class_linked_observed ;
4842
49- ZEND_TLS zend_execute_data * current_observed_frame ;
50-
5143// Call during minit/startup ONLY
5244ZEND_API void zend_observer_fcall_register (zend_observer_fcall_init init )
5345{
@@ -107,7 +99,7 @@ ZEND_API void zend_observer_post_startup(void)
10799
108100ZEND_API void zend_observer_activate (void )
109101{
110- current_observed_frame = NULL ;
102+ EG ( current_observed_frame ) = NULL ;
111103}
112104
113105ZEND_API void zend_observer_shutdown (void )
@@ -127,21 +119,24 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
127119 zend_function * function = execute_data -> func ;
128120
129121 ZEND_ASSERT (RUN_TIME_CACHE (& function -> common ));
130- zend_observer_fcall_begin_handler * begin_handlers = ( zend_observer_fcall_begin_handler * ) & ZEND_OBSERVER_DATA (function );
122+ zend_observer_fcall_begin_handler * begin_handlers = ZEND_OBSERVER_DATA (function ), * begin_handlers_start = begin_handlers ;
131123 zend_observer_fcall_end_handler * end_handlers = (zend_observer_fcall_end_handler * )begin_handlers + list -> count , * end_handlers_start = end_handlers ;
132124
133125 * begin_handlers = ZEND_OBSERVER_NOT_OBSERVED ;
134126 * end_handlers = ZEND_OBSERVER_NOT_OBSERVED ;
127+ bool has_handlers = false;
135128
136129 for (zend_llist_element * element = list -> head ; element ; element = element -> next ) {
137130 zend_observer_fcall_init init ;
138131 memcpy (& init , element -> data , sizeof init );
139132 zend_observer_fcall_handlers handlers = init (execute_data );
140133 if (handlers .begin ) {
141134 * (begin_handlers ++ ) = handlers .begin ;
135+ has_handlers = true;
142136 }
143137 if (handlers .end ) {
144138 * (end_handlers ++ ) = handlers .end ;
139+ has_handlers = true;
145140 }
146141 }
147142
@@ -151,6 +146,10 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
151146 * end_handlers = * end_handlers_start ;
152147 * end_handlers_start = tmp ;
153148 }
149+
150+ if (!has_handlers ) {
151+ * begin_handlers_start = ZEND_OBSERVER_NONE_OBSERVED ;
152+ }
154153}
155154
156155/* We need to provide the ability to retrieve the handler which will move onto the position the current handler was.
@@ -182,8 +181,8 @@ static bool zend_observer_remove_handler(void **first_handler, void *old_handler
182181
183182ZEND_API void zend_observer_add_begin_handler (zend_function * function , zend_observer_fcall_begin_handler begin ) {
184183 size_t registered_observers = zend_observers_fcall_list .count ;
185- zend_observer_fcall_begin_handler * first_handler = ( void * ) & ZEND_OBSERVER_DATA (function ), * last_handler = first_handler + registered_observers - 1 ;
186- if (* first_handler == ZEND_OBSERVER_NOT_OBSERVED ) {
184+ zend_observer_fcall_begin_handler * first_handler = ZEND_OBSERVER_DATA (function ), * last_handler = first_handler + registered_observers - 1 ;
185+ if (* first_handler == ZEND_OBSERVER_NOT_OBSERVED || * first_handler == ZEND_OBSERVER_NONE_OBSERVED ) {
187186 * first_handler = begin ;
188187 } else {
189188 for (zend_observer_fcall_begin_handler * cur_handler = first_handler + 1 ; cur_handler <= last_handler ; ++ cur_handler ) {
@@ -198,24 +197,47 @@ ZEND_API void zend_observer_add_begin_handler(zend_function *function, zend_obse
198197}
199198
200199ZEND_API bool zend_observer_remove_begin_handler (zend_function * function , zend_observer_fcall_begin_handler begin , zend_observer_fcall_begin_handler * next ) {
201- return zend_observer_remove_handler ((void * * )& ZEND_OBSERVER_DATA (function ), begin , (void * * )next );
200+ void * * begin_handlers = (void * * )ZEND_OBSERVER_DATA (function );
201+ if (zend_observer_remove_handler (begin_handlers , begin , (void * * )next )) {
202+ // Ensure invariant: ZEND_OBSERVER_NONE_OBSERVED in begin_handlers if both are not observed
203+ if (* begin_handlers == ZEND_OBSERVER_NOT_OBSERVED ) {
204+ size_t registered_observers = zend_observers_fcall_list .count ;
205+ if (begin_handlers [registered_observers ] /* first end handler */ == ZEND_OBSERVER_NOT_OBSERVED ) {
206+ * begin_handlers = ZEND_OBSERVER_NONE_OBSERVED ;
207+ }
208+ }
209+ return true;
210+ }
211+ return false;
202212}
203213
204214ZEND_API void zend_observer_add_end_handler (zend_function * function , zend_observer_fcall_end_handler end ) {
205215 size_t registered_observers = zend_observers_fcall_list .count ;
206- zend_observer_fcall_end_handler * end_handler = (zend_observer_fcall_end_handler * )& ZEND_OBSERVER_DATA (function ) + registered_observers ;
216+ void * * begin_handler = (void * * )ZEND_OBSERVER_DATA (function );
217+ zend_observer_fcall_end_handler * end_handler = (zend_observer_fcall_end_handler * )begin_handler + registered_observers ;
207218 // to allow to preserve the invariant that end handlers are in reverse order of begin handlers, push the new end handler in front
208219 if (* end_handler != ZEND_OBSERVER_NOT_OBSERVED ) {
209220 // there's no space for new handlers, then it's forbidden to call this function
210221 ZEND_ASSERT (end_handler [registered_observers - 1 ] == NULL );
211222 memmove (end_handler + 1 , end_handler , sizeof (end_handler ) * (registered_observers - 1 ));
223+ } else if (* begin_handler == ZEND_OBSERVER_NONE_OBSERVED ) {
224+ * begin_handler = ZEND_OBSERVER_NOT_OBSERVED ;
212225 }
213226 * end_handler = end ;
214227}
215228
216229ZEND_API bool zend_observer_remove_end_handler (zend_function * function , zend_observer_fcall_end_handler end , zend_observer_fcall_end_handler * next ) {
217230 size_t registered_observers = zend_observers_fcall_list .count ;
218- return zend_observer_remove_handler ((void * * )& ZEND_OBSERVER_DATA (function ) + registered_observers , end , (void * * )next );
231+ void * * begin_handlers = (void * * )ZEND_OBSERVER_DATA (function );
232+ void * * end_handlers = begin_handlers + registered_observers ;
233+ if (zend_observer_remove_handler (end_handlers , end , (void * * )next )) {
234+ // Ensure invariant: ZEND_OBSERVER_NONE_OBSERVED in begin_handlers if both are not observed
235+ if (* begin_handlers == ZEND_OBSERVER_NOT_OBSERVED && * end_handlers == ZEND_OBSERVER_NOT_OBSERVED ) {
236+ * begin_handlers = ZEND_OBSERVER_NONE_OBSERVED ;
237+ }
238+ return true;
239+ }
240+ return false;
219241}
220242
221243static inline zend_execute_data * * prev_observed_frame (zend_execute_data * execute_data ) {
@@ -224,33 +246,33 @@ static inline zend_execute_data **prev_observed_frame(zend_execute_data *execute
224246 return (zend_execute_data * * )& Z_PTR_P (EX_VAR_NUM ((ZEND_USER_CODE (func -> type ) ? func -> op_array .last_var : ZEND_CALL_NUM_ARGS (execute_data )) + func -> common .T - 1 ));
225247}
226248
227- static void ZEND_FASTCALL _zend_observe_fcall_begin (zend_execute_data * execute_data )
228- {
249+ static void ZEND_FASTCALL _zend_observe_fcall_begin (zend_execute_data * execute_data ) {
229250 if (!ZEND_OBSERVER_ENABLED ) {
230251 return ;
231252 }
232253
233- zend_function * function = execute_data -> func ;
254+ zend_observer_fcall_begin_specialized (execute_data , true);
255+ }
234256
235- if (! ZEND_OBSERVABLE_FN ( function )) {
236- return ;
237- }
257+ ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin_prechecked ( zend_execute_data * execute_data , zend_observer_fcall_begin_handler * handler )
258+ {
259+ zend_observer_fcall_begin_handler * possible_handlers_end = handler + zend_observers_fcall_list . count ;
238260
239- zend_observer_fcall_begin_handler * handler = (zend_observer_fcall_begin_handler * )& ZEND_OBSERVER_DATA (function );
240261 if (!* handler ) {
241262 zend_observer_fcall_install (execute_data );
263+ if (zend_observer_handler_is_unobserved (handler )) {
264+ return ;
265+ }
242266 }
243267
244- zend_observer_fcall_begin_handler * possible_handlers_end = handler + zend_observers_fcall_list .count ;
245-
246268 zend_observer_fcall_end_handler * end_handler = (zend_observer_fcall_end_handler * )possible_handlers_end ;
247269 if (* end_handler != ZEND_OBSERVER_NOT_OBSERVED ) {
248- * prev_observed_frame (execute_data ) = current_observed_frame ;
249- current_observed_frame = execute_data ;
250- }
270+ * prev_observed_frame (execute_data ) = EG (current_observed_frame );
271+ EG (current_observed_frame ) = execute_data ;
251272
252- if (* handler == ZEND_OBSERVER_NOT_OBSERVED ) {
253- return ;
273+ if (* handler == ZEND_OBSERVER_NOT_OBSERVED ) { // this function must not be called if ZEND_OBSERVER_NONE_OBSERVED, hence sufficient to check
274+ return ;
275+ }
254276 }
255277
256278 do {
@@ -265,17 +287,17 @@ ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(zend_execute_data *ex
265287
266288ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin (zend_execute_data * execute_data )
267289{
268- ZEND_ASSUME (execute_data -> func );
269- if (!(execute_data -> func -> common .fn_flags & ZEND_ACC_GENERATOR )) {
290+ ZEND_ASSUME (EX ( func ) );
291+ if (!(EX ( func ) -> common .fn_flags & ZEND_ACC_GENERATOR )) {
270292 _zend_observe_fcall_begin (execute_data );
271293 }
272294}
273295
274296static inline void call_end_observers (zend_execute_data * execute_data , zval * return_value ) {
275- zend_function * func = execute_data -> func ;
297+ zend_function * func = EX ( func ) ;
276298 ZEND_ASSERT (func );
277299
278- zend_observer_fcall_end_handler * handler = (zend_observer_fcall_end_handler * )& ZEND_OBSERVER_DATA (func ) + zend_observers_fcall_list .count ;
300+ zend_observer_fcall_end_handler * handler = (zend_observer_fcall_end_handler * )ZEND_OBSERVER_DATA (func ) + zend_observers_fcall_list .count ;
279301 // TODO: Fix exceptions from generators
280302 // ZEND_ASSERT(fcall_data);
281303 if (!* handler || * handler == ZEND_OBSERVER_NOT_OBSERVED ) {
@@ -288,19 +310,16 @@ static inline void call_end_observers(zend_execute_data *execute_data, zval *ret
288310 } while (++ handler != possible_handlers_end && * handler != NULL );
289311}
290312
291- ZEND_API void ZEND_FASTCALL zend_observer_fcall_end (zend_execute_data * execute_data , zval * return_value )
313+ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end_prechecked (zend_execute_data * execute_data , zval * return_value )
292314{
293- if (execute_data != current_observed_frame ) {
294- return ;
295- }
296315 call_end_observers (execute_data , return_value );
297- current_observed_frame = * prev_observed_frame (execute_data );
316+ EG ( current_observed_frame ) = * prev_observed_frame (execute_data );
298317}
299318
300319ZEND_API void zend_observer_fcall_end_all (void )
301320{
302- zend_execute_data * execute_data = current_observed_frame , * original_execute_data = EG (current_execute_data );
303- current_observed_frame = NULL ;
321+ zend_execute_data * execute_data = EG ( current_observed_frame ) , * original_execute_data = EG (current_execute_data );
322+ EG ( current_observed_frame ) = NULL ;
304323 while (execute_data ) {
305324 EG (current_execute_data ) = execute_data ;
306325 call_end_observers (execute_data , NULL );
@@ -401,8 +420,8 @@ ZEND_API void ZEND_FASTCALL zend_observer_fiber_switch_notify(zend_fiber_context
401420 callback (from , to );
402421 }
403422
404- from -> top_observed_frame = current_observed_frame ;
405- current_observed_frame = to -> top_observed_frame ;
423+ from -> top_observed_frame = EG ( current_observed_frame ) ;
424+ EG ( current_observed_frame ) = to -> top_observed_frame ;
406425}
407426
408427ZEND_API void ZEND_FASTCALL zend_observer_fiber_destroy_notify (zend_fiber_context * destroying )
0 commit comments