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