3131#define ZEND_OBSERVABLE_FN (fn_flags ) \
3232 (!(fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
3333
34- typedef struct _zend_observer_fcall_data {
35- // points after the last handler
36- zend_observer_fcall_handlers * end ;
37- // a variadic array using "struct hack"
38- zend_observer_fcall_handlers handlers [1 ];
39- } zend_observer_fcall_data ;
40-
4134zend_llist zend_observers_fcall_list ;
4235zend_llist zend_observer_error_callbacks ;
4336
4437int zend_observer_fcall_op_array_extension ;
4538
46- ZEND_TLS zend_arena * fcall_handlers_arena ;
4739ZEND_TLS zend_execute_data * first_observed_frame ;
4840ZEND_TLS zend_execute_data * current_observed_frame ;
4941
5042// Call during minit/startup ONLY
51- ZEND_API void zend_observer_fcall_register (zend_observer_fcall_init init ) {
52- if (!ZEND_OBSERVER_ENABLED ) {
53- /* We don't want to get an extension handle unless an ext installs an observer */
43+ ZEND_API void zend_observer_fcall_register (zend_observer_fcall_init init )
44+ {
45+ zend_llist_add_element (& zend_observers_fcall_list , & init );
46+ }
47+
48+ // Called by engine before MINITs
49+ ZEND_API void zend_observer_startup (void )
50+ {
51+ zend_llist_init (& zend_observers_fcall_list , sizeof (zend_observer_fcall_init ), NULL , 1 );
52+ zend_llist_init (& zend_observer_error_callbacks , sizeof (zend_observer_error_cb ), NULL , 1 );
53+
54+ zend_observer_fcall_op_array_extension = -1 ;
55+ }
56+
57+ ZEND_API void zend_observer_post_startup (void )
58+ {
59+ if (zend_observers_fcall_list .count ) {
60+ /* We don't want to get an extension handle unless an ext installs an observer
61+ * Allocate each a begin and an end pointer */
5462 zend_observer_fcall_op_array_extension =
55- zend_get_op_array_extension_handle ("Zend Observer" );
63+ zend_get_op_array_extension_handles ("Zend Observer" , ( int ) zend_observers_fcall_list . count * 2 );
5664
5765 /* ZEND_CALL_TRAMPOLINE has SPEC(OBSERVER) but zend_init_call_trampoline_op()
5866 * is called before any extensions have registered as an observer. So we
@@ -62,123 +70,98 @@ ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init) {
6270 /* ZEND_HANDLE_EXCEPTION also has SPEC(OBSERVER) and no observer extensions
6371 * exist when zend_init_exception_op() is called. */
6472 ZEND_VM_SET_OPCODE_HANDLER (EG (exception_op ));
65- ZEND_VM_SET_OPCODE_HANDLER (EG (exception_op )+ 1 );
66- ZEND_VM_SET_OPCODE_HANDLER (EG (exception_op )+ 2 );
73+ ZEND_VM_SET_OPCODE_HANDLER (EG (exception_op ) + 1 );
74+ ZEND_VM_SET_OPCODE_HANDLER (EG (exception_op ) + 2 );
6775 }
68- zend_llist_add_element (& zend_observers_fcall_list , & init );
69- }
70-
71- // Called by engine before MINITs
72- ZEND_API void zend_observer_startup (void ) {
73- zend_llist_init (& zend_observers_fcall_list , sizeof (zend_observer_fcall_init ), NULL , 1 );
74- zend_llist_init (& zend_observer_error_callbacks , sizeof (zend_observer_error_cb ), NULL , 1 );
75-
76- zend_observer_fcall_op_array_extension = -1 ;
7776}
7877
79- ZEND_API void zend_observer_activate (void ) {
80- if (ZEND_OBSERVER_ENABLED ) {
81- fcall_handlers_arena = zend_arena_create (4096 );
82- } else {
83- fcall_handlers_arena = NULL ;
84- }
78+ ZEND_API void zend_observer_activate (void )
79+ {
8580 first_observed_frame = NULL ;
8681 current_observed_frame = NULL ;
8782}
8883
89- ZEND_API void zend_observer_deactivate (void ) {
90- if (fcall_handlers_arena ) {
91- zend_arena_destroy (fcall_handlers_arena );
92- }
84+ ZEND_API void zend_observer_deactivate (void )
85+ {
86+ // now empty and unused, but kept for ABI compatibility
9387}
9488
95- ZEND_API void zend_observer_shutdown (void ) {
89+ ZEND_API void zend_observer_shutdown (void )
90+ {
9691 zend_llist_destroy (& zend_observers_fcall_list );
9792 zend_llist_destroy (& zend_observer_error_callbacks );
9893}
9994
100- static void zend_observer_fcall_install (zend_execute_data * execute_data ) {
101- zend_llist_element * element ;
95+ static void zend_observer_fcall_install (zend_execute_data * execute_data )
96+ {
10297 zend_llist * list = & zend_observers_fcall_list ;
10398 zend_function * function = execute_data -> func ;
10499 zend_op_array * op_array = & function -> op_array ;
105100
106- if (fcall_handlers_arena == NULL ) {
107- return ;
108- }
109-
110101 ZEND_ASSERT (function -> type != ZEND_INTERNAL_FUNCTION );
111102
112- zend_llist handlers_list ;
113- zend_llist_init (& handlers_list , sizeof (zend_observer_fcall_handlers ), NULL , 0 );
114- for (element = list -> head ; element ; element = element -> next ) {
103+ ZEND_ASSERT (RUN_TIME_CACHE (op_array ));
104+ zend_observer_fcall_begin_handler * begin_handlers = (zend_observer_fcall_begin_handler * )& ZEND_OBSERVER_DATA (op_array );
105+ zend_observer_fcall_end_handler * end_handlers = (zend_observer_fcall_end_handler * )begin_handlers + list -> count , * end_handlers_start = end_handlers ;
106+
107+ * begin_handlers = ZEND_OBSERVER_NOT_OBSERVED ;
108+ * end_handlers = ZEND_OBSERVER_NOT_OBSERVED ;
109+
110+ for (zend_llist_element * element = list -> head ; element ; element = element -> next ) {
115111 zend_observer_fcall_init init ;
116112 memcpy (& init , element -> data , sizeof init );
117113 zend_observer_fcall_handlers handlers = init (execute_data );
118- if (handlers .begin || handlers . end ) {
119- zend_llist_add_element ( & handlers_list , & handlers ) ;
114+ if (handlers .begin ) {
115+ * ( begin_handlers ++ ) = handlers . begin ;
120116 }
121- }
122-
123- ZEND_ASSERT (RUN_TIME_CACHE (op_array ));
124- void * ext ;
125- if (handlers_list .count ) {
126- size_t size = sizeof (zend_observer_fcall_data ) + (handlers_list .count - 1 ) * sizeof (zend_observer_fcall_handlers );
127- zend_observer_fcall_data * fcall_data = zend_arena_alloc (& fcall_handlers_arena , size );
128- zend_observer_fcall_handlers * handlers = fcall_data -> handlers ;
129- for (element = handlers_list .head ; element ; element = element -> next ) {
130- memcpy (handlers ++ , element -> data , sizeof * handlers );
117+ if (handlers .end ) {
118+ * (end_handlers ++ ) = handlers .end ;
131119 }
132- fcall_data -> end = handlers ;
133- ext = fcall_data ;
134- } else {
135- ext = ZEND_OBSERVER_NOT_OBSERVED ;
136120 }
137-
138- ZEND_OBSERVER_DATA (op_array ) = ext ;
139- zend_llist_destroy (& handlers_list );
121+
122+ // end handlers are executed in reverse order
123+ for (-- end_handlers ; end_handlers_start < end_handlers ; -- end_handlers , ++ end_handlers_start ) {
124+ zend_observer_fcall_end_handler tmp = * end_handlers ;
125+ * end_handlers = * end_handlers_start ;
126+ * end_handlers_start = tmp ;
127+ }
140128}
141129
142130static void ZEND_FASTCALL _zend_observe_fcall_begin (zend_execute_data * execute_data )
143131{
144- zend_op_array * op_array ;
145- uint32_t fn_flags ;
146- zend_observer_fcall_data * fcall_data ;
147- zend_observer_fcall_handlers * handlers , * end ;
148-
149132 if (!ZEND_OBSERVER_ENABLED ) {
150133 return ;
151134 }
152135
153- op_array = & execute_data -> func -> op_array ;
154- fn_flags = op_array -> fn_flags ;
136+ zend_op_array * op_array = & execute_data -> func -> op_array ;
137+ uint32_t fn_flags = op_array -> fn_flags ;
155138
156139 if (!ZEND_OBSERVABLE_FN (fn_flags )) {
157140 return ;
158141 }
159142
160- fcall_data = ZEND_OBSERVER_DATA (op_array );
161- if (!fcall_data ) {
143+ zend_observer_fcall_begin_handler * handler = ( zend_observer_fcall_begin_handler * ) & ZEND_OBSERVER_DATA (op_array );
144+ if (!* handler ) {
162145 zend_observer_fcall_install (execute_data );
163- fcall_data = ZEND_OBSERVER_DATA (op_array );
164146 }
165147
166- ZEND_ASSERT (fcall_data );
167- if (fcall_data == ZEND_OBSERVER_NOT_OBSERVED ) {
168- return ;
169- }
148+ zend_observer_fcall_begin_handler * possible_handlers_end = handler + zend_observers_fcall_list .count ;
170149
171- if (first_observed_frame == NULL ) {
172- first_observed_frame = execute_data ;
150+ zend_observer_fcall_end_handler * end_handler = (zend_observer_fcall_end_handler * )possible_handlers_end ;
151+ if (* end_handler != ZEND_OBSERVER_NOT_OBSERVED ) {
152+ if (first_observed_frame == NULL ) {
153+ first_observed_frame = execute_data ;
154+ }
155+ current_observed_frame = execute_data ;
173156 }
174- current_observed_frame = execute_data ;
175157
176- end = fcall_data -> end ;
177- for (handlers = fcall_data -> handlers ; handlers != end ; ++ handlers ) {
178- if (handlers -> begin ) {
179- handlers -> begin (execute_data );
180- }
158+ if (* handler == ZEND_OBSERVER_NOT_OBSERVED ) {
159+ return ;
181160 }
161+
162+ do {
163+ (* handler )(execute_data );
164+ } while (++ handler != possible_handlers_end && * handler != NULL );
182165}
183166
184167ZEND_API void ZEND_FASTCALL zend_observer_generator_resume (zend_execute_data * execute_data )
@@ -194,43 +177,48 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(zend_execute_data *execute
194177 }
195178}
196179
197- ZEND_API void ZEND_FASTCALL zend_observer_fcall_end (
198- zend_execute_data * execute_data ,
199- zval * return_value )
180+ static inline bool zend_observer_is_skipped_frame (zend_execute_data * execute_data ) {
181+ zend_function * func = execute_data -> func ;
182+
183+ if (!func || func -> type == ZEND_INTERNAL_FUNCTION || !ZEND_OBSERVABLE_FN (func -> common .fn_flags )) {
184+ return true;
185+ }
186+
187+ zend_observer_fcall_end_handler end_handler = (& ZEND_OBSERVER_DATA (& func -> op_array ))[zend_observers_fcall_list .count ];
188+ if (end_handler == NULL || end_handler == ZEND_OBSERVER_NOT_OBSERVED ) {
189+ return true;
190+ }
191+
192+ return false;
193+ }
194+
195+ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end (zend_execute_data * execute_data , zval * return_value )
200196{
201197 zend_function * func = execute_data -> func ;
202- zend_observer_fcall_data * fcall_data ;
203- zend_observer_fcall_handlers * handlers , * end ;
204198
205199 if (!ZEND_OBSERVER_ENABLED
206200 || !ZEND_OBSERVABLE_FN (func -> common .fn_flags )) {
207201 return ;
208202 }
209203
210- fcall_data = (zend_observer_fcall_data * ) ZEND_OBSERVER_DATA (& func -> op_array );
204+ zend_observer_fcall_end_handler * handler = (zend_observer_fcall_end_handler * ) & ZEND_OBSERVER_DATA (& func -> op_array ) + zend_observers_fcall_list . count ;
211205 // TODO: Fix exceptions from generators
212206 // ZEND_ASSERT(fcall_data);
213- if (!fcall_data || fcall_data == ZEND_OBSERVER_NOT_OBSERVED ) {
207+ if (!* handler || * handler == ZEND_OBSERVER_NOT_OBSERVED ) {
214208 return ;
215209 }
216210
217- handlers = fcall_data -> end ;
218- end = fcall_data -> handlers ;
219- while (handlers -- != end ) {
220- if (handlers -> end ) {
221- handlers -> end (execute_data , return_value );
222- }
223- }
211+ zend_observer_fcall_end_handler * possible_handlers_end = handler + zend_observers_fcall_list .count ;
212+ do {
213+ (* handler )(execute_data , return_value );
214+ } while (++ handler != possible_handlers_end && * handler != NULL );
224215
225216 if (first_observed_frame == execute_data ) {
226217 first_observed_frame = NULL ;
227218 current_observed_frame = NULL ;
228219 } else {
229220 zend_execute_data * ex = execute_data -> prev_execute_data ;
230- while (ex && (!ex -> func || ex -> func -> type == ZEND_INTERNAL_FUNCTION
231- || !ZEND_OBSERVABLE_FN (ex -> func -> common .fn_flags )
232- || !ZEND_OBSERVER_DATA (& ex -> func -> op_array )
233- || ZEND_OBSERVER_DATA (& ex -> func -> op_array ) == ZEND_OBSERVER_NOT_OBSERVED )) {
221+ while (ex && zend_observer_is_skipped_frame (ex )) {
234222 ex = ex -> prev_execute_data ;
235223 }
236224 current_observed_frame = ex ;
@@ -246,7 +234,6 @@ ZEND_API void zend_observer_fcall_end_all(void)
246234 }
247235 ex = ex -> prev_execute_data ;
248236 }
249- current_observed_frame = NULL ;
250237}
251238
252239ZEND_API void zend_observer_error_register (zend_observer_error_cb cb )
@@ -256,11 +243,8 @@ ZEND_API void zend_observer_error_register(zend_observer_error_cb cb)
256243
257244void zend_observer_error_notify (int type , const char * error_filename , uint32_t error_lineno , zend_string * message )
258245{
259- zend_llist_element * element ;
260- zend_observer_error_cb callback ;
261-
262- for (element = zend_observer_error_callbacks .head ; element ; element = element -> next ) {
263- callback = * (zend_observer_error_cb * ) (element -> data );
246+ for (zend_llist_element * element = zend_observer_error_callbacks .head ; element ; element = element -> next ) {
247+ zend_observer_error_cb callback = * (zend_observer_error_cb * ) (element -> data );
264248 callback (type , error_filename , error_lineno , message );
265249 }
266250}
0 commit comments