14
14
+----------------------------------------------------------------------+
15
15
| Authors: Levi Morrison <levim@php.net> |
16
16
| Sammy Kaye Powers <sammyk@php.net> |
17
+ | Bob Weinand <bobwei9@hotmail.com> |
17
18
+----------------------------------------------------------------------+
18
19
*/
19
20
23
24
#include "zend_llist.h"
24
25
#include "zend_vm.h"
25
26
26
- #define ZEND_OBSERVER_DATA (function ) \
27
- ZEND_OP_ARRAY_EXTENSION((&(function)->common), zend_observer_fcall_op_array_extension)
28
-
29
27
#define ZEND_OBSERVER_NOT_OBSERVED ((void *) 2)
30
28
31
- #define ZEND_OBSERVABLE_FN (function ) \
32
- (ZEND_MAP_PTR(function->common.run_time_cache) && !(function->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
33
-
34
29
zend_llist zend_observers_fcall_list ;
35
30
zend_llist zend_observer_function_declared_callbacks ;
36
31
zend_llist zend_observer_class_linked_callbacks ;
@@ -44,7 +39,7 @@ bool zend_observer_errors_observed;
44
39
bool zend_observer_function_declared_observed ;
45
40
bool zend_observer_class_linked_observed ;
46
41
47
- ZEND_TLS zend_execute_data * current_observed_frame ;
42
+ ZEND_EXT_TLS zend_execute_data * zend_observer_current_frame ;
48
43
49
44
// Call during minit/startup ONLY
50
45
ZEND_API void zend_observer_fcall_register (zend_observer_fcall_init init )
@@ -101,7 +96,7 @@ ZEND_API void zend_observer_post_startup(void)
101
96
102
97
ZEND_API void zend_observer_activate (void )
103
98
{
104
- current_observed_frame = NULL ;
99
+ zend_observer_current_frame = NULL ;
105
100
}
106
101
107
102
ZEND_API void zend_observer_shutdown (void )
@@ -121,21 +116,24 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
121
116
zend_function * function = execute_data -> func ;
122
117
123
118
ZEND_ASSERT (RUN_TIME_CACHE (& function -> common ));
124
- zend_observer_fcall_begin_handler * begin_handlers = ( zend_observer_fcall_begin_handler * ) & ZEND_OBSERVER_DATA (function );
119
+ zend_observer_fcall_begin_handler * begin_handlers = ZEND_OBSERVER_DATA (function ), * begin_handlers_start = begin_handlers ;
125
120
zend_observer_fcall_end_handler * end_handlers = (zend_observer_fcall_end_handler * )begin_handlers + list -> count , * end_handlers_start = end_handlers ;
126
121
127
122
* begin_handlers = ZEND_OBSERVER_NOT_OBSERVED ;
128
123
* end_handlers = ZEND_OBSERVER_NOT_OBSERVED ;
124
+ bool has_handlers = false;
129
125
130
126
for (zend_llist_element * element = list -> head ; element ; element = element -> next ) {
131
127
zend_observer_fcall_init init ;
132
128
memcpy (& init , element -> data , sizeof init );
133
129
zend_observer_fcall_handlers handlers = init (execute_data );
134
130
if (handlers .begin ) {
135
131
* (begin_handlers ++ ) = handlers .begin ;
132
+ has_handlers = true;
136
133
}
137
134
if (handlers .end ) {
138
135
* (end_handlers ++ ) = handlers .end ;
136
+ has_handlers = true;
139
137
}
140
138
}
141
139
@@ -145,6 +143,10 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
145
143
* end_handlers = * end_handlers_start ;
146
144
* end_handlers_start = tmp ;
147
145
}
146
+
147
+ if (!has_handlers ) {
148
+ * begin_handlers_start = ZEND_OBSERVER_NONE_OBSERVED ;
149
+ }
148
150
}
149
151
150
152
static bool zend_observer_remove_handler (void * * first_handler , void * old_handler ) {
@@ -169,8 +171,8 @@ static bool zend_observer_remove_handler(void **first_handler, void *old_handler
169
171
170
172
ZEND_API void zend_observer_add_begin_handler (zend_function * function , zend_observer_fcall_begin_handler begin ) {
171
173
size_t registered_observers = zend_observers_fcall_list .count ;
172
- zend_observer_fcall_begin_handler * first_handler = ( void * ) & ZEND_OBSERVER_DATA (function ), * last_handler = first_handler + registered_observers - 1 ;
173
- if (* first_handler == ZEND_OBSERVER_NOT_OBSERVED ) {
174
+ zend_observer_fcall_begin_handler * first_handler = ZEND_OBSERVER_DATA (function ), * last_handler = first_handler + registered_observers - 1 ;
175
+ if (* first_handler == ZEND_OBSERVER_NOT_OBSERVED || * first_handler == ZEND_OBSERVER_NONE_OBSERVED ) {
174
176
* first_handler = begin ;
175
177
} else {
176
178
for (zend_observer_fcall_begin_handler * cur_handler = first_handler + 1 ; cur_handler <= last_handler ; ++ cur_handler ) {
@@ -185,24 +187,45 @@ ZEND_API void zend_observer_add_begin_handler(zend_function *function, zend_obse
185
187
}
186
188
187
189
ZEND_API bool zend_observer_remove_begin_handler (zend_function * function , zend_observer_fcall_begin_handler begin ) {
188
- return zend_observer_remove_handler ((void * * )& ZEND_OBSERVER_DATA (function ), begin );
190
+ void * * begin_handlers = (void * * )ZEND_OBSERVER_DATA (function );
191
+ if (zend_observer_remove_handler (begin_handlers , begin )) {
192
+ if (* begin_handlers == ZEND_OBSERVER_NOT_OBSERVED ) {
193
+ size_t registered_observers = zend_observers_fcall_list .count ;
194
+ if (begin_handlers [registered_observers ] /* first end handler */ == ZEND_OBSERVER_NOT_OBSERVED ) {
195
+ * begin_handlers = ZEND_OBSERVER_NONE_OBSERVED ;
196
+ }
197
+ }
198
+ return true;
199
+ }
200
+ return false;
189
201
}
190
202
191
203
ZEND_API void zend_observer_add_end_handler (zend_function * function , zend_observer_fcall_end_handler end ) {
192
204
size_t registered_observers = zend_observers_fcall_list .count ;
193
- zend_observer_fcall_end_handler * end_handler = (zend_observer_fcall_end_handler * )& ZEND_OBSERVER_DATA (function ) + registered_observers ;
205
+ void * * begin_handler = (void * * )ZEND_OBSERVER_DATA (function );
206
+ zend_observer_fcall_end_handler * end_handler = (zend_observer_fcall_end_handler * )begin_handler + registered_observers ;
194
207
// to allow to preserve the invariant that end handlers are in reverse order of begin handlers, push the new end handler in front
195
208
if (* end_handler != ZEND_OBSERVER_NOT_OBSERVED ) {
196
209
// there's no space for new handlers, then it's forbidden to call this function
197
210
ZEND_ASSERT (end_handler [registered_observers - 1 ] == NULL );
198
211
memmove (end_handler + 1 , end_handler , sizeof (end_handler ) * (registered_observers - 1 ));
212
+ } else if (* begin_handler == ZEND_OBSERVER_NONE_OBSERVED ) {
213
+ * begin_handler = ZEND_OBSERVER_NOT_OBSERVED ;
199
214
}
200
215
* end_handler = end ;
201
216
}
202
217
203
218
ZEND_API bool zend_observer_remove_end_handler (zend_function * function , zend_observer_fcall_end_handler end ) {
204
219
size_t registered_observers = zend_observers_fcall_list .count ;
205
- return zend_observer_remove_handler ((void * * )& ZEND_OBSERVER_DATA (function ) + registered_observers , end );
220
+ void * * begin_handlers = (void * * )ZEND_OBSERVER_DATA (function );
221
+ void * * end_handlers = begin_handlers + registered_observers ;
222
+ if (zend_observer_remove_handler (end_handlers , end )) {
223
+ if (* begin_handlers == ZEND_OBSERVER_NOT_OBSERVED && * end_handlers == ZEND_OBSERVER_NOT_OBSERVED ) {
224
+ * begin_handlers = ZEND_OBSERVER_NONE_OBSERVED ;
225
+ }
226
+ return true;
227
+ }
228
+ return false;
206
229
}
207
230
208
231
static inline zend_execute_data * * prev_observed_frame (zend_execute_data * execute_data ) {
@@ -211,33 +234,33 @@ static inline zend_execute_data **prev_observed_frame(zend_execute_data *execute
211
234
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 ));
212
235
}
213
236
214
- static void ZEND_FASTCALL _zend_observe_fcall_begin (zend_execute_data * execute_data )
215
- {
237
+ static void ZEND_FASTCALL _zend_observe_fcall_begin (zend_execute_data * execute_data ) {
216
238
if (!ZEND_OBSERVER_ENABLED ) {
217
239
return ;
218
240
}
219
241
220
- zend_function * function = execute_data -> func ;
242
+ zend_observer_fcall_begin_specialized (execute_data , true);
243
+ }
221
244
222
- if (! ZEND_OBSERVABLE_FN ( function )) {
223
- return ;
224
- }
245
+ void ZEND_FASTCALL zend_observer_fcall_begin_prechecked ( zend_execute_data * execute_data , zend_observer_fcall_begin_handler * handler )
246
+ {
247
+ zend_observer_fcall_begin_handler * possible_handlers_end = handler + zend_observers_fcall_list . count ;
225
248
226
- zend_observer_fcall_begin_handler * handler = (zend_observer_fcall_begin_handler * )& ZEND_OBSERVER_DATA (function );
227
249
if (!* handler ) {
228
250
zend_observer_fcall_install (execute_data );
251
+ if (zend_observer_handler_is_unobserved (handler )) {
252
+ return ;
253
+ }
229
254
}
230
255
231
- zend_observer_fcall_begin_handler * possible_handlers_end = handler + zend_observers_fcall_list .count ;
232
-
233
256
zend_observer_fcall_end_handler * end_handler = (zend_observer_fcall_end_handler * )possible_handlers_end ;
234
257
if (* end_handler != ZEND_OBSERVER_NOT_OBSERVED ) {
235
- * prev_observed_frame (execute_data ) = current_observed_frame ;
236
- current_observed_frame = execute_data ;
237
- }
258
+ * prev_observed_frame (execute_data ) = zend_observer_current_frame ;
259
+ zend_observer_current_frame = execute_data ;
238
260
239
- if (* handler == ZEND_OBSERVER_NOT_OBSERVED ) {
240
- return ;
261
+ if (* handler == ZEND_OBSERVER_NOT_OBSERVED ) { // this function must not be called if ZEND_OBSERVER_NONE_OBSERVED, hence sufficient to check
262
+ return ;
263
+ }
241
264
}
242
265
243
266
do {
@@ -252,17 +275,17 @@ ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(zend_execute_data *ex
252
275
253
276
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin (zend_execute_data * execute_data )
254
277
{
255
- ZEND_ASSUME (execute_data -> func );
256
- if (!(execute_data -> func -> common .fn_flags & ZEND_ACC_GENERATOR )) {
278
+ ZEND_ASSUME (EX ( func ) );
279
+ if (!(EX ( func ) -> common .fn_flags & ZEND_ACC_GENERATOR )) {
257
280
_zend_observe_fcall_begin (execute_data );
258
281
}
259
282
}
260
283
261
284
static inline void call_end_observers (zend_execute_data * execute_data , zval * return_value ) {
262
- zend_function * func = execute_data -> func ;
285
+ zend_function * func = EX ( func ) ;
263
286
ZEND_ASSERT (func );
264
287
265
- zend_observer_fcall_end_handler * handler = (zend_observer_fcall_end_handler * )& ZEND_OBSERVER_DATA (func ) + zend_observers_fcall_list .count ;
288
+ zend_observer_fcall_end_handler * handler = (zend_observer_fcall_end_handler * )ZEND_OBSERVER_DATA (func ) + zend_observers_fcall_list .count ;
266
289
// TODO: Fix exceptions from generators
267
290
// ZEND_ASSERT(fcall_data);
268
291
if (!* handler || * handler == ZEND_OBSERVER_NOT_OBSERVED ) {
@@ -275,19 +298,16 @@ static inline void call_end_observers(zend_execute_data *execute_data, zval *ret
275
298
} while (++ handler != possible_handlers_end && * handler != NULL );
276
299
}
277
300
278
- ZEND_API void ZEND_FASTCALL zend_observer_fcall_end (zend_execute_data * execute_data , zval * return_value )
301
+ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end_prechecked (zend_execute_data * execute_data , zval * return_value )
279
302
{
280
- if (execute_data != current_observed_frame ) {
281
- return ;
282
- }
283
303
call_end_observers (execute_data , return_value );
284
- current_observed_frame = * prev_observed_frame (execute_data );
304
+ zend_observer_current_frame = * prev_observed_frame (execute_data );
285
305
}
286
306
287
307
ZEND_API void zend_observer_fcall_end_all (void )
288
308
{
289
- zend_execute_data * execute_data = current_observed_frame , * original_execute_data = EG (current_execute_data );
290
- current_observed_frame = NULL ;
309
+ zend_execute_data * execute_data = zend_observer_current_frame , * original_execute_data = EG (current_execute_data );
310
+ zend_observer_current_frame = NULL ;
291
311
while (execute_data ) {
292
312
EG (current_execute_data ) = execute_data ;
293
313
call_end_observers (execute_data , NULL );
@@ -388,8 +408,8 @@ ZEND_API void ZEND_FASTCALL zend_observer_fiber_switch_notify(zend_fiber_context
388
408
callback (from , to );
389
409
}
390
410
391
- from -> top_observed_frame = current_observed_frame ;
392
- current_observed_frame = to -> top_observed_frame ;
411
+ from -> top_observed_frame = zend_observer_current_frame ;
412
+ zend_observer_current_frame = to -> top_observed_frame ;
393
413
}
394
414
395
415
ZEND_API void ZEND_FASTCALL zend_observer_fiber_destroy_notify (zend_fiber_context * destroying )
0 commit comments