14
14
+----------------------------------------------------------------------+
15
15
| Authors: Levi Morrison <[email protected] > |
16
16
| Sammy Kaye Powers <[email protected] > |
17
+ | Bob Weinand <[email protected] > |
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_USER_CODE((function)->type) \
28
- ? zend_observer_fcall_op_array_extension : zend_observer_fcall_internal_function_extension)
29
-
30
27
#define ZEND_OBSERVER_NOT_OBSERVED ((void *) 2)
31
28
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
-
35
29
static zend_llist zend_observers_fcall_list ;
36
30
static zend_llist zend_observer_function_declared_callbacks ;
37
31
static zend_llist zend_observer_class_linked_callbacks ;
@@ -46,8 +40,6 @@ bool zend_observer_errors_observed;
46
40
bool zend_observer_function_declared_observed ;
47
41
bool zend_observer_class_linked_observed ;
48
42
49
- ZEND_TLS zend_execute_data * current_observed_frame ;
50
-
51
43
// Call during minit/startup ONLY
52
44
ZEND_API void zend_observer_fcall_register (zend_observer_fcall_init init )
53
45
{
@@ -107,7 +99,7 @@ ZEND_API void zend_observer_post_startup(void)
107
99
108
100
ZEND_API void zend_observer_activate (void )
109
101
{
110
- current_observed_frame = NULL ;
102
+ EG ( current_observed_frame ) = NULL ;
111
103
}
112
104
113
105
ZEND_API void zend_observer_shutdown (void )
@@ -127,21 +119,24 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
127
119
zend_function * function = execute_data -> func ;
128
120
129
121
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 ;
131
123
zend_observer_fcall_end_handler * end_handlers = (zend_observer_fcall_end_handler * )begin_handlers + list -> count , * end_handlers_start = end_handlers ;
132
124
133
125
* begin_handlers = ZEND_OBSERVER_NOT_OBSERVED ;
134
126
* end_handlers = ZEND_OBSERVER_NOT_OBSERVED ;
127
+ bool has_handlers = false;
135
128
136
129
for (zend_llist_element * element = list -> head ; element ; element = element -> next ) {
137
130
zend_observer_fcall_init init ;
138
131
memcpy (& init , element -> data , sizeof init );
139
132
zend_observer_fcall_handlers handlers = init (execute_data );
140
133
if (handlers .begin ) {
141
134
* (begin_handlers ++ ) = handlers .begin ;
135
+ has_handlers = true;
142
136
}
143
137
if (handlers .end ) {
144
138
* (end_handlers ++ ) = handlers .end ;
139
+ has_handlers = true;
145
140
}
146
141
}
147
142
@@ -151,6 +146,10 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
151
146
* end_handlers = * end_handlers_start ;
152
147
* end_handlers_start = tmp ;
153
148
}
149
+
150
+ if (!has_handlers ) {
151
+ * begin_handlers_start = ZEND_OBSERVER_NONE_OBSERVED ;
152
+ }
154
153
}
155
154
156
155
/* 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
182
181
183
182
ZEND_API void zend_observer_add_begin_handler (zend_function * function , zend_observer_fcall_begin_handler begin ) {
184
183
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 ) {
187
186
* first_handler = begin ;
188
187
} else {
189
188
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
198
197
}
199
198
200
199
ZEND_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;
202
212
}
203
213
204
214
ZEND_API void zend_observer_add_end_handler (zend_function * function , zend_observer_fcall_end_handler end ) {
205
215
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 ;
207
218
// to allow to preserve the invariant that end handlers are in reverse order of begin handlers, push the new end handler in front
208
219
if (* end_handler != ZEND_OBSERVER_NOT_OBSERVED ) {
209
220
// there's no space for new handlers, then it's forbidden to call this function
210
221
ZEND_ASSERT (end_handler [registered_observers - 1 ] == NULL );
211
222
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 ;
212
225
}
213
226
* end_handler = end ;
214
227
}
215
228
216
229
ZEND_API bool zend_observer_remove_end_handler (zend_function * function , zend_observer_fcall_end_handler end , zend_observer_fcall_end_handler * next ) {
217
230
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;
219
241
}
220
242
221
243
static 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
224
246
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 ));
225
247
}
226
248
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 ) {
229
250
if (!ZEND_OBSERVER_ENABLED ) {
230
251
return ;
231
252
}
232
253
233
- zend_function * function = execute_data -> func ;
254
+ zend_observer_fcall_begin_specialized (execute_data , true);
255
+ }
234
256
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 ;
238
260
239
- zend_observer_fcall_begin_handler * handler = (zend_observer_fcall_begin_handler * )& ZEND_OBSERVER_DATA (function );
240
261
if (!* handler ) {
241
262
zend_observer_fcall_install (execute_data );
263
+ if (zend_observer_handler_is_unobserved (handler )) {
264
+ return ;
265
+ }
242
266
}
243
267
244
- zend_observer_fcall_begin_handler * possible_handlers_end = handler + zend_observers_fcall_list .count ;
245
-
246
268
zend_observer_fcall_end_handler * end_handler = (zend_observer_fcall_end_handler * )possible_handlers_end ;
247
269
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 ;
251
272
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
+ }
254
276
}
255
277
256
278
do {
@@ -265,17 +287,17 @@ ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(zend_execute_data *ex
265
287
266
288
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin (zend_execute_data * execute_data )
267
289
{
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 )) {
270
292
_zend_observe_fcall_begin (execute_data );
271
293
}
272
294
}
273
295
274
296
static 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 ) ;
276
298
ZEND_ASSERT (func );
277
299
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 ;
279
301
// TODO: Fix exceptions from generators
280
302
// ZEND_ASSERT(fcall_data);
281
303
if (!* handler || * handler == ZEND_OBSERVER_NOT_OBSERVED ) {
@@ -288,19 +310,16 @@ static inline void call_end_observers(zend_execute_data *execute_data, zval *ret
288
310
} while (++ handler != possible_handlers_end && * handler != NULL );
289
311
}
290
312
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 )
292
314
{
293
- if (execute_data != current_observed_frame ) {
294
- return ;
295
- }
296
315
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 );
298
317
}
299
318
300
319
ZEND_API void zend_observer_fcall_end_all (void )
301
320
{
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 ;
304
323
while (execute_data ) {
305
324
EG (current_execute_data ) = execute_data ;
306
325
call_end_observers (execute_data , NULL );
@@ -401,8 +420,8 @@ ZEND_API void ZEND_FASTCALL zend_observer_fiber_switch_notify(zend_fiber_context
401
420
callback (from , to );
402
421
}
403
422
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 ;
406
425
}
407
426
408
427
ZEND_API void ZEND_FASTCALL zend_observer_fiber_destroy_notify (zend_fiber_context * destroying )
0 commit comments