Skip to content

Commit 625f164

Browse files
committed
Include internal functions in the observer API
There are two main motivations to this: a) The logic for handling internal and userland observation can be unified. b) Unwinding of observed functions on a bailout does notably not include observers. Even if users of observers were to ensure such handling themselves, it would be impossible to retain the relative ordering - either the user has to unwind all internal observed frames before the automatic unwinding (zend_observer_fcall_end_all) or afterwards, but not properly interleaved. Signed-off-by: Bob Weinand <[email protected]>
1 parent 0c225a2 commit 625f164

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1672
-1254
lines changed

UPGRADING.INTERNALS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ PHP 8.2 INTERNALS UPGRADE NOTES
5151
are deprecated (see main UPGRADING notes). To suppress the notice, e.g. to
5252
avoid duplicates when processing the same value multiple times, pass or add
5353
IS_CALLABLE_SUPPRESS_DEPRECATIONS to the check_flags parameter.
54+
* Registered zend_observer_fcall_init handlers are now also called for internal functions.
5455

5556
========================
5657
2. Build system changes

Zend/zend.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,7 @@ ZEND_API void zend_activate(void) /* {{{ */
12261226
if (CG(map_ptr_last)) {
12271227
memset(CG(map_ptr_real_base), 0, CG(map_ptr_last) * sizeof(void*));
12281228
}
1229+
zend_init_internal_run_time_cache();
12291230
zend_observer_activate();
12301231
}
12311232
/* }}} */

Zend/zend_API.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2694,6 +2694,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
26942694
internal_function->scope = scope;
26952695
internal_function->prototype = NULL;
26962696
internal_function->attributes = NULL;
2697+
if (EG(active)) { // at run-time: this ought to only happen if registered with dl() or somehow temporarily at runtime
2698+
ZEND_MAP_PTR_INIT(internal_function->run_time_cache, zend_arena_alloc(&CG(arena), zend_internal_run_time_cache_reserved_size()));
2699+
} else {
2700+
ZEND_MAP_PTR_NEW(internal_function->run_time_cache);
2701+
}
26972702
if (ptr->flags) {
26982703
if (!(ptr->flags & ZEND_ACC_PPP_MASK)) {
26992704
if (ptr->flags != ZEND_ACC_DEPRECATED && scope) {

Zend/zend_compile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "zend_inheritance.h"
3434
#include "zend_vm.h"
3535
#include "zend_enum.h"
36+
#include "zend_observer.h"
3637

3738
#define SET_NODE(target, src) do { \
3839
target ## _type = (src)->op_type; \

Zend/zend_compile.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ struct _zend_op_array {
448448
uint32_t required_num_args;
449449
zend_arg_info *arg_info;
450450
HashTable *attributes;
451+
ZEND_MAP_PTR_DEF(void **, run_time_cache);
451452
/* END of common elements */
452453

453454
int cache_size; /* number of run_time_cache_slots * sizeof(void*) */
@@ -456,7 +457,6 @@ struct _zend_op_array {
456457
uint32_t last; /* number of opcodes */
457458

458459
zend_op *opcodes;
459-
ZEND_MAP_PTR_DEF(void **, run_time_cache);
460460
ZEND_MAP_PTR_DEF(HashTable *, static_variables_ptr);
461461
HashTable *static_variables;
462462
zend_string **vars; /* names of CV variables */
@@ -503,6 +503,7 @@ typedef struct _zend_internal_function {
503503
uint32_t required_num_args;
504504
zend_internal_arg_info *arg_info;
505505
HashTable *attributes;
506+
ZEND_MAP_PTR_DEF(void **, run_time_cache);
506507
/* END of common elements */
507508

508509
zif_handler handler;
@@ -527,6 +528,7 @@ union _zend_function {
527528
uint32_t required_num_args;
528529
zend_arg_info *arg_info; /* index -1 represents the return value info, if any */
529530
HashTable *attributes;
531+
ZEND_MAP_PTR_DEF(void **, run_time_cache);
530532
} common;
531533

532534
zend_op_array op_array;

Zend/zend_enum.c

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "zend_enum_arginfo.h"
2323
#include "zend_interfaces.h"
2424
#include "zend_enum.h"
25+
#include "zend_extensions.h"
2526

2627
#define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \
2728
do { \
@@ -401,59 +402,48 @@ static ZEND_NAMED_FUNCTION(zend_enum_try_from_func)
401402
zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
402403
}
403404

405+
static void zend_enum_register_func(zend_class_entry *ce, zend_known_string_id name_id, zend_internal_function *zif) {
406+
zend_string *name = ZSTR_KNOWN(name_id);
407+
zif->type = ZEND_INTERNAL_FUNCTION;
408+
zif->module = EG(current_module);
409+
zif->scope = ce;
410+
ZEND_MAP_PTR_NEW(zif->run_time_cache);
411+
ZEND_MAP_PTR_SET(zif->run_time_cache, zend_arena_alloc(&CG(arena), zend_internal_run_time_cache_reserved_size()));
412+
413+
if (!zend_hash_add_ptr(&ce->function_table, name, zif)) {
414+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(name));
415+
}
416+
}
417+
404418
void zend_enum_register_funcs(zend_class_entry *ce)
405419
{
406420
const uint32_t fn_flags =
407421
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_ARENA_ALLOCATED;
408-
zend_internal_function *cases_function =
409-
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
410-
memset(cases_function, 0, sizeof(zend_internal_function));
411-
cases_function->type = ZEND_INTERNAL_FUNCTION;
412-
cases_function->module = EG(current_module);
422+
zend_internal_function *cases_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
413423
cases_function->handler = zend_enum_cases_func;
414424
cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES);
415-
cases_function->scope = ce;
416425
cases_function->fn_flags = fn_flags;
417426
cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1);
418-
if (!zend_hash_add_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_CASES), cases_function)) {
419-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::cases()", ZSTR_VAL(ce->name));
420-
}
427+
zend_enum_register_func(ce, ZEND_STR_CASES, cases_function);
421428

422429
if (ce->enum_backing_type != IS_UNDEF) {
423-
zend_internal_function *from_function =
424-
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
425-
memset(from_function, 0, sizeof(zend_internal_function));
426-
from_function->type = ZEND_INTERNAL_FUNCTION;
427-
from_function->module = EG(current_module);
430+
zend_internal_function *from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
428431
from_function->handler = zend_enum_from_func;
429432
from_function->function_name = ZSTR_KNOWN(ZEND_STR_FROM);
430-
from_function->scope = ce;
431433
from_function->fn_flags = fn_flags;
432434
from_function->num_args = 1;
433435
from_function->required_num_args = 1;
434436
from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_from + 1);
435-
if (!zend_hash_add_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_FROM), from_function)) {
436-
zend_error_noreturn(E_COMPILE_ERROR,
437-
"Cannot redeclare %s::from()", ZSTR_VAL(ce->name));
438-
}
437+
zend_enum_register_func(ce, ZEND_STR_FROM, from_function);
439438

440-
zend_internal_function *try_from_function =
441-
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
442-
memset(try_from_function, 0, sizeof(zend_internal_function));
443-
try_from_function->type = ZEND_INTERNAL_FUNCTION;
444-
try_from_function->module = EG(current_module);
439+
zend_internal_function *try_from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
445440
try_from_function->handler = zend_enum_try_from_func;
446441
try_from_function->function_name = ZSTR_KNOWN(ZEND_STR_TRYFROM);
447-
try_from_function->scope = ce;
448442
try_from_function->fn_flags = fn_flags;
449443
try_from_function->num_args = 1;
450444
try_from_function->required_num_args = 1;
451445
try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1);
452-
if (!zend_hash_add_ptr(
453-
&ce->function_table, ZSTR_KNOWN(ZEND_STR_TRYFROM_LOWERCASE), try_from_function)) {
454-
zend_error_noreturn(E_COMPILE_ERROR,
455-
"Cannot redeclare %s::tryFrom()", ZSTR_VAL(ce->name));
456-
}
446+
zend_enum_register_func(ce, ZEND_STR_TRYFROM_LOWERCASE, try_from_function);
457447
}
458448
}
459449

Zend/zend_execute.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ ZEND_API const zend_internal_function zend_pass_function = {
144144
0, /* required_num_args */
145145
(zend_internal_arg_info *) zend_pass_function_arg_info + 1, /* arg_info */
146146
NULL, /* attributes */
147+
NULL, /* run_time_cache */
147148
ZEND_FN(pass), /* handler */
148149
NULL, /* module */
149150
{NULL,NULL,NULL,NULL} /* reserved */

Zend/zend_execute_API.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
935935
#if ZEND_DEBUG
936936
bool should_throw = zend_internal_call_should_throw(func, call);
937937
#endif
938+
ZEND_OBSERVER_FCALL_BEGIN(call);
938939
if (EXPECTED(zend_execute_internal == NULL)) {
939940
/* saves one function call if zend_execute_internal is not used */
940941
func->internal_function.handler(call, fci->retval);
@@ -953,6 +954,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
953954
? Z_ISREF_P(fci->retval) : !Z_ISREF_P(fci->retval));
954955
}
955956
#endif
957+
ZEND_OBSERVER_FCALL_END(call, fci->retval);
956958
EG(current_execute_data) = call->prev_execute_data;
957959
zend_vm_stack_free_args(call);
958960
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {

Zend/zend_extensions.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,40 @@ ZEND_API int zend_get_op_array_extension_handles(const char *module_name, int ha
280280
return handle;
281281
}
282282

283+
ZEND_API size_t zend_internal_run_time_cache_reserved_size() {
284+
return zend_op_array_extension_handles * sizeof(void *);
285+
}
286+
287+
ZEND_API void zend_init_internal_run_time_cache() {
288+
size_t rt_size = zend_internal_run_time_cache_reserved_size();
289+
if (rt_size) {
290+
size_t functions = zend_hash_num_elements(CG(function_table));
291+
zend_class_entry *ce;
292+
ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) {
293+
functions += zend_hash_num_elements(&ce->function_table);
294+
} ZEND_HASH_FOREACH_END();
295+
296+
char *ptr = zend_arena_calloc(&CG(arena), functions, rt_size);
297+
zend_internal_function *zif;
298+
ZEND_HASH_MAP_FOREACH_PTR(CG(function_table), zif) {
299+
if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL)
300+
{
301+
ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr);
302+
ptr += rt_size;
303+
}
304+
} ZEND_HASH_FOREACH_END();
305+
ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) {
306+
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, zif) {
307+
if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL)
308+
{
309+
ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr);
310+
ptr += rt_size;
311+
}
312+
} ZEND_HASH_FOREACH_END();
313+
} ZEND_HASH_FOREACH_END();
314+
}
315+
}
316+
283317
ZEND_API zend_extension *zend_get_extension(const char *extension_name)
284318
{
285319
zend_llist_element *element;

Zend/zend_extensions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ void zend_startup_extensions_mechanism(void);
145145
void zend_startup_extensions(void);
146146
void zend_shutdown_extensions(void);
147147

148+
ZEND_API size_t zend_internal_run_time_cache_reserved_size(void);
149+
ZEND_API void zend_init_internal_run_time_cache(void);
150+
148151
BEGIN_EXTERN_C()
149152
ZEND_API zend_result zend_load_extension(const char *path);
150153
ZEND_API zend_result zend_load_extension_handle(DL_HANDLE handle, const char *path);

0 commit comments

Comments
 (0)