diff --git a/Zend/zend.c b/Zend/zend.c index 2d8a0f455f8b4..462649e2cd902 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -724,6 +724,9 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{ zend_hash_init(compiler_globals->class_table, 64, NULL, ZEND_CLASS_DTOR, 1); zend_hash_copy(compiler_globals->class_table, global_class_table, zend_class_add_ref); + compiler_globals->constraint_cache = (HashTable *) malloc(sizeof(HashTable)); + zend_hash_init(compiler_globals->constraint_cache, 64, NULL, NULL, 1); + zend_set_default_compile_time_values(); compiler_globals->auto_globals = (HashTable *) malloc(sizeof(HashTable)); @@ -794,6 +797,13 @@ static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{ pefree(compiler_globals->internal_run_time_cache, 1); compiler_globals->internal_run_time_cache = NULL; } +// HashTable *cache; +// ZEND_HASH_FOREACH_PTR(compiler_globals->constraint_cache, cache) { +// zend_hash_destroy(cache); +// free(cache); +// } ZEND_HASH_FOREACH_END(); +// zend_hash_destroy(compiler_globals->constraint_cache); +// free(compiler_globals->constraint_cache); } /* }}} */ @@ -1029,6 +1039,7 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ compiler_globals->in_compilation = 0; compiler_globals->function_table = (HashTable *) malloc(sizeof(HashTable)); compiler_globals->class_table = (HashTable *) malloc(sizeof(HashTable)); + compiler_globals->constraint_cache = (HashTable *) malloc(sizeof(HashTable)); *compiler_globals->function_table = *GLOBAL_FUNCTION_TABLE; *compiler_globals->class_table = *GLOBAL_CLASS_TABLE; @@ -1124,6 +1135,8 @@ zend_result zend_post_startup(void) /* {{{ */ compiler_globals->function_table = NULL; free(compiler_globals->class_table); compiler_globals->class_table = NULL; + free(compiler_globals->constraint_cache); + compiler_globals->constraint_cache = NULL; if (compiler_globals->map_ptr_real_base) { free(compiler_globals->map_ptr_real_base); } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2e405de30ea62..9cd9e2f1c4017 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7359,6 +7359,7 @@ static zend_type zend_compile_typename_ex( } ast->attr = orig_ast_attr; + return type; } /* }}} */ @@ -8370,10 +8371,10 @@ static zend_op_array *zend_compile_func_decl_ex( "nodiscard", sizeof("nodiscard")-1 ); - + if (nodiscard_attribute) { op_array->fn_flags |= ZEND_ACC_NODISCARD; - } + } } /* Do not leak the class scope into free standing functions, even if they are dynamically diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4fed2af3a92fd..b8f97e13ef2ab 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1140,7 +1140,7 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( return ce; } -static bool zend_check_intersection_type_from_cache_slot(zend_type_list *intersection_type_list, +static bool zend_check_intersection_type_from_cache_slot(zend_type *original_type, zend_type_list *intersection_type_list, zend_class_entry *arg_ce, void ***cache_slot_ptr) { void **cache_slot = *cache_slot_ptr; @@ -1162,6 +1162,9 @@ static bool zend_check_intersection_type_from_cache_slot(zend_type_list *interse if (HAVE_CACHE_SLOT) { *cache_slot_ptr = cache_slot; } + if (status) { + zend_type_satisfied_by_class(original_type, arg_ce); + } return status; } @@ -1171,15 +1174,18 @@ static zend_always_inline bool zend_check_type_slow( { uint32_t type_mask; if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { - zend_class_entry *ce; + zend_class_entry *ce = Z_OBJCE_P(arg); + if (EXPECTED(zend_type_is_satisfied_by_class(type, ce))) { + return 1; + } if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) { zend_type *list_type; if (ZEND_TYPE_IS_INTERSECTION(*type)) { - return zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg), &cache_slot); + return zend_check_intersection_type_from_cache_slot(type, ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg), &cache_slot); } else { ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { - if (zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg), &cache_slot)) { + if (zend_check_intersection_type_from_cache_slot(type, ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg), &cache_slot)) { return true; } /* The cache_slot is progressed in zend_check_intersection_type_from_cache_slot() */ @@ -1188,6 +1194,7 @@ static zend_always_inline bool zend_check_type_slow( ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type); /* Instance of a single type part of a union is sufficient to pass the type check */ if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + zend_type_satisfied_by_class(type, ce); return true; } PROGRESS_CACHE_SLOT(); @@ -1199,6 +1206,7 @@ static zend_always_inline bool zend_check_type_slow( /* If we have a CE we check if it satisfies the type constraint, * otherwise it will check if a standard type satisfies it. */ if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) { + zend_type_satisfied_by_class(type, ce); return true; } } diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 1f55521fb72f1..d64ddc52397cc 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -148,6 +148,7 @@ void init_executor(void) /* {{{ */ EG(function_table) = CG(function_table); EG(class_table) = CG(class_table); + EG(constraint_cache) = CG(constraint_cache); EG(in_autoload) = NULL; EG(error_handling) = EH_NORMAL; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 079bfb99caccf..e0577fc85b95a 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -94,6 +94,7 @@ struct _zend_compiler_globals { HashTable *function_table; /* function symbol table */ HashTable *class_table; /* class table */ + HashTable *constraint_cache; /* constraint cache */ HashTable *auto_globals; @@ -191,6 +192,7 @@ struct _zend_executor_globals { HashTable *function_table; /* function symbol table */ HashTable *class_table; /* class table */ HashTable *zend_constants; /* constants table */ + HashTable *constraint_cache; /* constraint cache */ zval *vm_stack_top; zval *vm_stack_end; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 8747e11d151fc..6018d138f7856 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2194,6 +2194,31 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry * } /* }}} */ +ZEND_API void zend_type_satisfied_by_class(const zend_type *type, const zend_class_entry *ce) { + const zend_ulong hash = zend_hash_type(type); + HashTable *constraint = zend_hash_index_find_ptr(EG(constraint_cache), hash); + if (UNEXPECTED(constraint == NULL)) { + constraint = malloc(sizeof(HashTable)); + zend_hash_init(constraint, 8, NULL, NULL, 1); + zend_hash_index_add_new_ptr(EG(constraint_cache), hash, constraint); + } + zend_hash_add_empty_element(constraint, ce->name); +} + +ZEND_API bool zend_type_is_satisfied_by_class(const zend_type *type, const zend_class_entry *ce) { + const zend_ulong hash = zend_hash_type(type); + const HashTable *constraint = zend_hash_index_find_ptr(EG(constraint_cache), hash); + if (UNEXPECTED(constraint == NULL)) { + return false; + } + return zend_hash_exists(constraint, ce->name); +} + +ZEND_API ZEND_FASTCALL zend_ulong zend_hash_type(const zend_type *type) { + const uintptr_t hash = (uintptr_t)type; + return (zend_ulong)hash; +} + ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface) /* {{{ */ { uint32_t i, ignore = 0; diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h index 477874181e416..d6c91c5c208f6 100644 --- a/Zend/zend_inheritance.h +++ b/Zend/zend_inheritance.h @@ -27,6 +27,9 @@ BEGIN_EXTERN_C() ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface); ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked); +ZEND_API ZEND_FASTCALL zend_ulong zend_hash_type(const zend_type *type); +ZEND_API bool zend_type_is_satisfied_by_class(const zend_type *type, const zend_class_entry *ce); +ZEND_API void zend_type_satisfied_by_class(const zend_type *type, const zend_class_entry *ce); static zend_always_inline void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) { zend_do_inheritance_ex(ce, parent_ce, 0);