From f6e49bb8efe7f1872a72cc0000c0d53c70515db6 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 12 Dec 2024 19:00:41 +0100 Subject: [PATCH 1/4] Implement acyclic object tracking Acyclic objects don't need to be added to the GC. We know an object is acyclic if all properties are typed as acyclic, and the object does not have any dynamic properties. This is possible to track at runtime with minimal overhead. Fixes GH-17127 --- Zend/tests/gc/gc_045.phpt | 6 ++++ Zend/tests/weakrefs/gh10043-008.phpt | 3 ++ Zend/zend_API.c | 27 ++++++++++++++++ Zend/zend_closures.c | 3 ++ Zend/zend_compile.h | 5 ++- Zend/zend_fibers.c | 1 + Zend/zend_generators.c | 1 + Zend/zend_inheritance.c | 4 +++ Zend/zend_object_handlers.c | 2 ++ Zend/zend_objects.c | 3 ++ Zend/zend_weakrefs.c | 1 + ext/curl/interface.c | 1 + ext/curl/multi.c | 1 + ext/dom/php_dom.c | 2 ++ ext/pdo/pdo_dbh.c | 1 + ext/pdo/pdo_stmt.c | 1 + ext/reflection/php_reflection.c | 32 +++++++++++++++++++ ext/reflection/php_reflection.stub.php | 2 ++ ext/reflection/php_reflection_arginfo.h | 6 +++- .../tests/ReflectionClass_toString_001.phpt | 9 +++++- ext/simplexml/simplexml.c | 1 + ext/sockets/sockets.c | 1 + ext/spl/spl_array.c | 2 ++ ext/spl/spl_dllist.c | 1 + ext/spl/spl_fixedarray.c | 1 + ext/spl/spl_heap.c | 2 ++ ext/spl/spl_iterators.c | 2 ++ ext/spl/spl_observer.c | 2 ++ ext/sqlite3/sqlite3.c | 1 + ext/xml/xml.c | 1 + ext/xsl/php_xsl.c | 1 + 31 files changed, 123 insertions(+), 3 deletions(-) diff --git a/Zend/tests/gc/gc_045.phpt b/Zend/tests/gc/gc_045.phpt index 1762be5db1ad9..cbb1fc71e79f9 100644 --- a/Zend/tests/gc/gc_045.phpt +++ b/Zend/tests/gc/gc_045.phpt @@ -11,6 +11,9 @@ class GlobalData class Value { + /* Force object to be added to GC, even though it is acyclic. */ + public $dummy; + public function __destruct() { new Bar(); @@ -19,6 +22,9 @@ class Value class Bar { + /* Force object to be added to GC, even though it is acyclic. */ + public $dummy; + public function __construct() { GlobalData::$bar = $this; diff --git a/Zend/tests/weakrefs/gh10043-008.phpt b/Zend/tests/weakrefs/gh10043-008.phpt index f39c2ddbe1c2e..91645b8ff36b0 100644 --- a/Zend/tests/weakrefs/gh10043-008.phpt +++ b/Zend/tests/weakrefs/gh10043-008.phpt @@ -5,6 +5,9 @@ Self-referencing map entry GC - 008 class Canary extends stdClass { + /* Force object to be added to GC, even though it is acyclic. */ + public $dummy; + public function __construct(public string $name) { } diff --git a/Zend/zend_API.c b/Zend/zend_API.c index fd5b7c8db7966..6f7f0f1b323c0 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4494,6 +4494,27 @@ static zend_always_inline bool is_persistent_class(zend_class_entry *ce) { && ce->info.internal.module->type == MODULE_PERSISTENT; } +static bool zend_type_may_be_cyclic(zend_type type) +{ + if (!ZEND_TYPE_IS_SET(type)) { + return true; + } + + if (!ZEND_TYPE_IS_COMPLEX(type)) { + return ZEND_TYPE_PURE_MASK(type) & (MAY_BE_OBJECT|MAY_BE_ARRAY); + } else if (ZEND_TYPE_IS_UNION(type)) { + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + if (zend_type_may_be_cyclic(*list_type)) { + return true; + } + } ZEND_TYPE_LIST_FOREACH_END(); + return false; + } + + return true; +} + ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type) /* {{{ */ { zend_property_info *property_info, *property_info_ptr; @@ -4506,6 +4527,12 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z } } + if (!(access_type & ZEND_ACC_STATIC) + && !(ce->ce_flags & ZEND_ACC_MAY_BE_CYCLIC) + && zend_type_may_be_cyclic(type)) { + ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; + } + if (ce->type == ZEND_INTERNAL_CLASS) { property_info = pemalloc(sizeof(zend_property_info), 1); } else { diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 629aa7d51a840..54899f25945a6 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -705,6 +705,9 @@ void zend_register_closure_ce(void) /* {{{ */ zend_ce_closure = register_class_Closure(); zend_ce_closure->create_object = zend_closure_new; zend_ce_closure->default_object_handlers = &closure_handlers; + /* FIXME: Potentially improve during construction of closure? static closures + * not binding by references can't be cyclic. */ + zend_ce_closure->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&closure_handlers, &std_object_handlers, sizeof(zend_object_handlers)); closure_handlers.free_obj = zend_closure_free_storage; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 1eaf3ef686e79..5f3634ae4b894 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -267,7 +267,7 @@ typedef struct _zend_oparray_context { #define ZEND_ACC_PROTECTED_SET (1 << 11) /* | | X | */ #define ZEND_ACC_PRIVATE_SET (1 << 12) /* | | X | */ /* | | | */ -/* Class Flags (unused: 30,31) | | | */ +/* Class Flags (unused: 31) | | | */ /* =========== | | | */ /* | | | */ /* Special class types | | | */ @@ -333,6 +333,9 @@ typedef struct _zend_oparray_context { /* Class cannot be serialized or unserialized | | | */ #define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */ /* | | | */ +/* Object may be the root of a cycle | | | */ +#define ZEND_ACC_MAY_BE_CYCLIC (1 << 30) /* X | | | */ +/* | | | */ /* Function Flags (unused: 29-30) | | | */ /* ============== | | | */ /* | | | */ diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 97b7cdcc911b7..da0a2a5f16eb1 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -1107,6 +1107,7 @@ void zend_register_fiber_ce(void) zend_ce_fiber = register_class_Fiber(); zend_ce_fiber->create_object = zend_fiber_object_create; zend_ce_fiber->default_object_handlers = &zend_fiber_handlers; + zend_ce_fiber->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; zend_fiber_handlers = std_object_handlers; zend_fiber_handlers.dtor_obj = zend_fiber_object_destroy; diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index a6ea91a7425b9..d847da5e93942 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -1215,6 +1215,7 @@ void zend_register_generator_ce(void) /* {{{ */ /* get_iterator has to be assigned *after* implementing the interface */ zend_ce_generator->get_iterator = zend_generator_get_iterator; zend_ce_generator->default_object_handlers = &zend_generator_handlers; + zend_ce_generator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&zend_generator_handlers, &std_object_handlers, sizeof(zend_object_handlers)); zend_generator_handlers.free_obj = zend_generator_free_storage; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 3557b14bb9740..76aed3fea04c2 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1810,6 +1810,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par ce->parent = parent_ce; ce->default_object_handlers = parent_ce->default_object_handlers; ce->ce_flags |= ZEND_ACC_RESOLVED_PARENT; + ce->ce_flags |= (parent_ce->ce_flags & ZEND_ACC_MAY_BE_CYCLIC); /* Inherit properties */ if (parent_ce->default_properties_count) { @@ -2832,6 +2833,9 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent if (!traits[i]) { continue; } + + ce->ce_flags |= (traits[i]->ce_flags & ZEND_ACC_MAY_BE_CYCLIC); + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&traits[i]->properties_info, prop_name, property_info) { uint32_t flags = property_info->flags; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 0709d05580dc8..8aa5c243110d5 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -70,6 +70,8 @@ ZEND_API HashTable *rebuild_object_properties_internal(zend_object *zobj) /* {{{ zend_class_entry *ce = zobj->ce; int i; + GC_TYPE_INFO(zobj) &= ~(GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT); + zobj->properties = zend_new_array(ce->default_properties_count); if (ce->default_properties_count) { zend_hash_real_init_mixed(zobj->properties); diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index fd0e97c5f4131..5dbd411c623f8 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -31,6 +31,9 @@ static zend_always_inline void _zend_object_std_init(zend_object *object, zend_c { GC_SET_REFCOUNT(object, 1); GC_TYPE_INFO(object) = GC_OBJECT; + if (!(ce->ce_flags & ZEND_ACC_MAY_BE_CYCLIC)) { + GC_TYPE_INFO(object) |= (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT); + } object->ce = ce; object->extra_flags = 0; object->handlers = ce->default_object_handlers; diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index cf363cd12595c..2dce20257e689 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -788,6 +788,7 @@ void zend_register_weakref_ce(void) /* {{{ */ zend_ce_weakmap->create_object = zend_weakmap_create_object; zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator; zend_ce_weakmap->default_object_handlers = &zend_weakmap_handlers; + zend_ce_weakmap->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&zend_weakmap_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); zend_weakmap_handlers.offset = XtOffsetOf(zend_weakmap, std); diff --git a/ext/curl/interface.c b/ext/curl/interface.c index aba5273d5496c..328ea4e0a674a 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -395,6 +395,7 @@ PHP_MINIT_FUNCTION(curl) curl_ce = register_class_CurlHandle(); curl_ce->create_object = curl_create_object; curl_ce->default_object_handlers = &curl_object_handlers; + curl_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&curl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); curl_object_handlers.offset = XtOffsetOf(php_curl, std); diff --git a/ext/curl/multi.c b/ext/curl/multi.c index 6456cf6f813e4..244c3b078a752 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -606,6 +606,7 @@ static zend_object_handlers curl_multi_handlers; void curl_multi_register_handlers(void) { curl_multi_ce->create_object = curl_multi_create_object; curl_multi_ce->default_object_handlers = &curl_multi_handlers; + curl_multi_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&curl_multi_handlers, &std_object_handlers, sizeof(zend_object_handlers)); curl_multi_handlers.offset = XtOffsetOf(php_curlm, std); diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 21741166c61aa..b92f2d843d4b6 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1292,6 +1292,7 @@ PHP_MINIT_FUNCTION(dom) dom_xpath_class_entry = register_class_DOMXPath(); dom_xpath_class_entry->create_object = dom_xpath_objects_new; dom_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers; + dom_xpath_class_entry->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; zend_hash_init(&dom_xpath_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "document", dom_xpath_document_read, NULL); @@ -1301,6 +1302,7 @@ PHP_MINIT_FUNCTION(dom) dom_modern_xpath_class_entry = register_class_Dom_XPath(); dom_modern_xpath_class_entry->create_object = dom_xpath_objects_new; dom_modern_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers; + dom_modern_xpath_class_entry->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; zend_hash_add_new_ptr(&classes, dom_modern_xpath_class_entry->name, &dom_xpath_prop_handlers); #endif diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 782639be0758e..611c8a490b96f 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1449,6 +1449,7 @@ void pdo_dbh_init(int module_number) pdo_dbh_ce = register_class_PDO(); pdo_dbh_ce->create_object = pdo_dbh_new; pdo_dbh_ce->default_object_handlers = &pdo_dbh_object_handlers; + pdo_dbh_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&pdo_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); pdo_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std); diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index a39b7a3b06804..df131685a16a1 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2491,6 +2491,7 @@ void pdo_stmt_init(void) pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get; pdo_dbstmt_ce->create_object = pdo_dbstmt_new; pdo_dbstmt_ce->default_object_handlers = &pdo_dbstmt_object_handlers; + pdo_dbstmt_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index f5e463699b1b5..9b60374f8de89 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4343,6 +4343,16 @@ ZEND_METHOD(ReflectionClass, getAttributes) } /* }}} */ +ZEND_METHOD(ReflectionClass, mayBeCyclic) +{ + reflection_object *intern; + zend_class_entry *ce; + + GET_REFLECTION_OBJECT_PTR(ce); + + RETURN_BOOL(ce->ce_flags & ZEND_ACC_MAY_BE_CYCLIC); +} + /* {{{ Returns the class' constructor if there is one, NULL otherwise */ ZEND_METHOD(ReflectionClass, getConstructor) { @@ -7682,91 +7692,113 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ reflection_function_abstract_ptr = register_class_ReflectionFunctionAbstract(reflector_ptr); reflection_function_abstract_ptr->default_object_handlers = &reflection_object_handlers; + reflection_function_abstract_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_function_abstract_ptr->create_object = reflection_objects_new; reflection_function_ptr = register_class_ReflectionFunction(reflection_function_abstract_ptr); reflection_function_ptr->create_object = reflection_objects_new; reflection_function_ptr->default_object_handlers = &reflection_object_handlers; + reflection_function_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_generator_ptr = register_class_ReflectionGenerator(); reflection_generator_ptr->create_object = reflection_objects_new; reflection_generator_ptr->default_object_handlers = &reflection_object_handlers; + reflection_generator_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_parameter_ptr = register_class_ReflectionParameter(reflector_ptr); reflection_parameter_ptr->create_object = reflection_objects_new; reflection_parameter_ptr->default_object_handlers = &reflection_object_handlers; + reflection_parameter_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_type_ptr = register_class_ReflectionType(zend_ce_stringable); reflection_type_ptr->create_object = reflection_objects_new; reflection_type_ptr->default_object_handlers = &reflection_object_handlers; + reflection_type_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_named_type_ptr = register_class_ReflectionNamedType(reflection_type_ptr); reflection_named_type_ptr->create_object = reflection_objects_new; reflection_named_type_ptr->default_object_handlers = &reflection_object_handlers; + reflection_named_type_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_union_type_ptr = register_class_ReflectionUnionType(reflection_type_ptr); reflection_union_type_ptr->create_object = reflection_objects_new; reflection_union_type_ptr->default_object_handlers = &reflection_object_handlers; + reflection_union_type_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_intersection_type_ptr = register_class_ReflectionIntersectionType(reflection_type_ptr); reflection_intersection_type_ptr->create_object = reflection_objects_new; reflection_intersection_type_ptr->default_object_handlers = &reflection_object_handlers; + reflection_intersection_type_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_method_ptr = register_class_ReflectionMethod(reflection_function_abstract_ptr); reflection_method_ptr->create_object = reflection_objects_new; reflection_method_ptr->default_object_handlers = &reflection_object_handlers; + reflection_method_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_class_ptr = register_class_ReflectionClass(reflector_ptr); reflection_class_ptr->create_object = reflection_objects_new; reflection_class_ptr->default_object_handlers = &reflection_object_handlers; + reflection_class_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_object_ptr = register_class_ReflectionObject(reflection_class_ptr); reflection_object_ptr->create_object = reflection_objects_new; reflection_object_ptr->default_object_handlers = &reflection_object_handlers; + reflection_object_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_property_ptr = register_class_ReflectionProperty(reflector_ptr); reflection_property_ptr->create_object = reflection_objects_new; reflection_property_ptr->default_object_handlers = &reflection_object_handlers; + reflection_property_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_class_constant_ptr = register_class_ReflectionClassConstant(reflector_ptr); reflection_class_constant_ptr->create_object = reflection_objects_new; reflection_class_constant_ptr->default_object_handlers = &reflection_object_handlers; + reflection_class_constant_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_extension_ptr = register_class_ReflectionExtension(reflector_ptr); reflection_extension_ptr->create_object = reflection_objects_new; reflection_extension_ptr->default_object_handlers = &reflection_object_handlers; + reflection_extension_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_zend_extension_ptr = register_class_ReflectionZendExtension(reflector_ptr); reflection_zend_extension_ptr->create_object = reflection_objects_new; reflection_zend_extension_ptr->default_object_handlers = &reflection_object_handlers; + reflection_zend_extension_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_reference_ptr = register_class_ReflectionReference(); reflection_reference_ptr->create_object = reflection_objects_new; reflection_reference_ptr->default_object_handlers = &reflection_object_handlers; + reflection_reference_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_attribute_ptr = register_class_ReflectionAttribute(reflector_ptr); reflection_attribute_ptr->create_object = reflection_objects_new; reflection_attribute_ptr->default_object_handlers = &reflection_object_handlers; + reflection_attribute_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_enum_ptr = register_class_ReflectionEnum(reflection_class_ptr); reflection_enum_ptr->create_object = reflection_objects_new; reflection_enum_ptr->default_object_handlers = &reflection_object_handlers; + reflection_enum_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_enum_unit_case_ptr = register_class_ReflectionEnumUnitCase(reflection_class_constant_ptr); reflection_enum_unit_case_ptr->create_object = reflection_objects_new; reflection_enum_unit_case_ptr->default_object_handlers = &reflection_object_handlers; + reflection_enum_unit_case_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_enum_backed_case_ptr = register_class_ReflectionEnumBackedCase(reflection_enum_unit_case_ptr); reflection_enum_backed_case_ptr->create_object = reflection_objects_new; reflection_enum_backed_case_ptr->default_object_handlers = &reflection_object_handlers; + reflection_enum_backed_case_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_fiber_ptr = register_class_ReflectionFiber(); reflection_fiber_ptr->create_object = reflection_objects_new; reflection_fiber_ptr->default_object_handlers = &reflection_object_handlers; + reflection_fiber_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_constant_ptr = register_class_ReflectionConstant(reflector_ptr); reflection_constant_ptr->create_object = reflection_objects_new; reflection_constant_ptr->default_object_handlers = &reflection_object_handlers; + reflection_constant_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_property_hook_type_ptr = register_class_PropertyHookType(); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index be511d7ee14cd..b33c27b2af2d1 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -432,6 +432,8 @@ public function getNamespaceName(): string {} public function getShortName(): string {} public function getAttributes(?string $name = null, int $flags = 0): array {} + + public function mayBeCyclic(): bool {} } class ReflectionObject extends ReflectionClass diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index d78a685dde9c9..7bc073f1f5459 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3c6be99bb36965139464925a618cb0bf03affa62 */ + * Stub hash: e2086e92426bd71218575f5a4c6dd0fea7049008 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -366,6 +366,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes +#define arginfo_class_ReflectionClass_mayBeCyclic arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionObject___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) ZEND_END_ARG_INFO() @@ -847,6 +849,7 @@ ZEND_METHOD(ReflectionClass, inNamespace); ZEND_METHOD(ReflectionClass, getNamespaceName); ZEND_METHOD(ReflectionClass, getShortName); ZEND_METHOD(ReflectionClass, getAttributes); +ZEND_METHOD(ReflectionClass, mayBeCyclic); ZEND_METHOD(ReflectionObject, __construct); ZEND_METHOD(ReflectionProperty, __construct); ZEND_METHOD(ReflectionProperty, __toString); @@ -1139,6 +1142,7 @@ static const zend_function_entry class_ReflectionClass_methods[] = { ZEND_ME(ReflectionClass, getNamespaceName, arginfo_class_ReflectionClass_getNamespaceName, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getShortName, arginfo_class_ReflectionClass_getShortName, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getAttributes, arginfo_class_ReflectionClass_getAttributes, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, mayBeCyclic, arginfo_class_ReflectionClass_mayBeCyclic, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index fd5d83e917419..67828502b3bdb 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -30,7 +30,7 @@ Class [ class ReflectionClass implements Stringable, Refle Property [ public string $name ] } - - Methods [64] { + - Methods [65] { Method [ private method __clone ] { - Parameters [0] { @@ -514,5 +514,12 @@ Class [ class ReflectionClass implements Stringable, Refle } - Return [ array ] } + + Method [ public method mayBeCyclic ] { + + - Parameters [0] { + } + - Return [ bool ] + } } } diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index a962028d30865..5a627b43324e5 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -2654,6 +2654,7 @@ PHP_MINIT_FUNCTION(simplexml) ce_SimpleXMLElement = register_class_SimpleXMLElement(zend_ce_stringable, zend_ce_countable, spl_ce_RecursiveIterator); ce_SimpleXMLElement->create_object = sxe_object_new; ce_SimpleXMLElement->default_object_handlers = &sxe_object_handlers; + ce_SimpleXMLElement->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; ce_SimpleXMLElement->get_iterator = php_sxe_get_iterator; memcpy(&sxe_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 940b1621f75d0..e31cc7cf4b44c 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -455,6 +455,7 @@ static PHP_MINIT_FUNCTION(sockets) socket_ce = register_class_Socket(); socket_ce->create_object = socket_create_object; socket_ce->default_object_handlers = &socket_object_handlers; + socket_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&socket_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); socket_object_handlers.offset = XtOffsetOf(php_socket, std); diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index af886944bc49e..a77f110c47e55 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -1883,6 +1883,7 @@ PHP_MINIT_FUNCTION(spl_array) spl_ce_ArrayObject = register_class_ArrayObject(zend_ce_aggregate, zend_ce_arrayaccess, zend_ce_serializable, zend_ce_countable); spl_ce_ArrayObject->create_object = spl_array_object_new; spl_ce_ArrayObject->default_object_handlers = &spl_handler_ArrayObject; + spl_ce_ArrayObject->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&spl_handler_ArrayObject, &std_object_handlers, sizeof(zend_object_handlers)); @@ -1909,6 +1910,7 @@ PHP_MINIT_FUNCTION(spl_array) spl_ce_ArrayIterator = register_class_ArrayIterator(spl_ce_SeekableIterator, zend_ce_arrayaccess, zend_ce_serializable, zend_ce_countable); spl_ce_ArrayIterator->create_object = spl_array_object_new; spl_ce_ArrayIterator->default_object_handlers = &spl_handler_ArrayObject; + spl_ce_ArrayIterator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator; spl_ce_RecursiveArrayIterator = register_class_RecursiveArrayIterator(spl_ce_ArrayIterator, spl_ce_RecursiveIterator); diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 5a78db2921a81..cf82c8000c450 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -1256,6 +1256,7 @@ PHP_MINIT_FUNCTION(spl_dllist) /* {{{ */ ); spl_ce_SplDoublyLinkedList->create_object = spl_dllist_object_new; spl_ce_SplDoublyLinkedList->default_object_handlers = &spl_handler_SplDoublyLinkedList; + spl_ce_SplDoublyLinkedList->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_SplDoublyLinkedList->get_iterator = spl_dllist_get_iterator; memcpy(&spl_handler_SplDoublyLinkedList, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index 5d7949308a303..a25e389861cf1 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -964,6 +964,7 @@ PHP_MINIT_FUNCTION(spl_fixedarray) zend_ce_aggregate, zend_ce_arrayaccess, zend_ce_countable, php_json_serializable_ce); spl_ce_SplFixedArray->create_object = spl_fixedarray_new; spl_ce_SplFixedArray->default_object_handlers = &spl_handler_SplFixedArray; + spl_ce_SplFixedArray->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_SplFixedArray->get_iterator = spl_fixedarray_get_iterator; memcpy(&spl_handler_SplFixedArray, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index d4450da42009c..52d5c9783a074 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -1141,6 +1141,7 @@ PHP_MINIT_FUNCTION(spl_heap) /* {{{ */ spl_ce_SplHeap = register_class_SplHeap(zend_ce_iterator, zend_ce_countable); spl_ce_SplHeap->create_object = spl_heap_object_new; spl_ce_SplHeap->default_object_handlers = &spl_handler_SplHeap; + spl_ce_SplHeap->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_SplHeap->get_iterator = spl_heap_get_iterator; memcpy(&spl_handler_SplHeap, &std_object_handlers, sizeof(zend_object_handlers)); @@ -1162,6 +1163,7 @@ PHP_MINIT_FUNCTION(spl_heap) /* {{{ */ spl_ce_SplPriorityQueue = register_class_SplPriorityQueue(zend_ce_iterator, zend_ce_countable); spl_ce_SplPriorityQueue->create_object = spl_heap_object_new; spl_ce_SplPriorityQueue->default_object_handlers = &spl_handler_SplPriorityQueue; + spl_ce_SplPriorityQueue->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_SplPriorityQueue->get_iterator = spl_pqueue_get_iterator; memcpy(&spl_handler_SplPriorityQueue, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index d4e40bf6b684a..a0360ef556d62 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -3108,6 +3108,7 @@ PHP_MINIT_FUNCTION(spl_iterators) spl_ce_RecursiveIteratorIterator = register_class_RecursiveIteratorIterator(spl_ce_OuterIterator); spl_ce_RecursiveIteratorIterator->create_object = spl_RecursiveIteratorIterator_new; spl_ce_RecursiveIteratorIterator->default_object_handlers = &spl_handlers_rec_it_it; + spl_ce_RecursiveIteratorIterator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator; memcpy(&spl_handlers_rec_it_it, &std_object_handlers, sizeof(zend_object_handlers)); @@ -3127,6 +3128,7 @@ PHP_MINIT_FUNCTION(spl_iterators) spl_ce_IteratorIterator = register_class_IteratorIterator(spl_ce_OuterIterator); spl_ce_IteratorIterator->create_object = spl_dual_it_new; spl_ce_IteratorIterator->default_object_handlers = &spl_handlers_dual_it; + spl_ce_IteratorIterator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_FilterIterator = register_class_FilterIterator(spl_ce_IteratorIterator); spl_ce_FilterIterator->create_object = spl_dual_it_new; diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 5222fe2a7fa4d..a3847bed048e8 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -1379,6 +1379,7 @@ PHP_MINIT_FUNCTION(spl_observer) spl_ce_SplObjectStorage = register_class_SplObjectStorage(zend_ce_countable, spl_ce_SeekableIterator, zend_ce_serializable, zend_ce_arrayaccess); spl_ce_SplObjectStorage->create_object = spl_SplObjectStorage_new; spl_ce_SplObjectStorage->default_object_handlers = &spl_handler_SplObjectStorage; + spl_ce_SplObjectStorage->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&spl_handler_SplObjectStorage, &std_object_handlers, sizeof(zend_object_handlers)); @@ -1395,6 +1396,7 @@ PHP_MINIT_FUNCTION(spl_observer) spl_ce_MultipleIterator = register_class_MultipleIterator(zend_ce_iterator); spl_ce_MultipleIterator->create_object = spl_SplObjectStorage_new; spl_ce_MultipleIterator->default_object_handlers = &spl_handler_SplObjectStorage; + spl_ce_MultipleIterator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; return SUCCESS; } diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 01b8af435b633..e7200b65355cd 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -2411,6 +2411,7 @@ PHP_MINIT_FUNCTION(sqlite3) php_sqlite3_sc_entry = register_class_SQLite3(); php_sqlite3_sc_entry->create_object = php_sqlite3_object_new; php_sqlite3_sc_entry->default_object_handlers = &sqlite3_object_handlers; + php_sqlite3_sc_entry->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; /* Register SQLite 3 Prepared Statement Class */ sqlite3_stmt_object_handlers.offset = XtOffsetOf(php_sqlite3_stmt, zo); diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 7eca5c0795e2a..d034898f861c9 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -228,6 +228,7 @@ PHP_MINIT_FUNCTION(xml) xml_parser_ce = register_class_XMLParser(); xml_parser_ce->create_object = xml_parser_create_object; xml_parser_ce->default_object_handlers = &xml_parser_object_handlers; + xml_parser_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&xml_parser_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); xml_parser_object_handlers.offset = XtOffsetOf(xml_parser, std); diff --git a/ext/xsl/php_xsl.c b/ext/xsl/php_xsl.c index dec7eb501eb2f..8c45429269d72 100644 --- a/ext/xsl/php_xsl.c +++ b/ext/xsl/php_xsl.c @@ -278,6 +278,7 @@ PHP_MINIT_FUNCTION(xsl) xsl_xsltprocessor_class_entry = register_class_XSLTProcessor(); xsl_xsltprocessor_class_entry->create_object = xsl_objects_new; xsl_xsltprocessor_class_entry->default_object_handlers = &xsl_object_handlers; + xsl_xsltprocessor_class_entry->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; #ifdef HAVE_XSL_EXSLT exsltRegisterAll(); From 969a472f72692d1654eb669570d2cdef837c395d Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 13 Dec 2024 10:23:33 +0100 Subject: [PATCH 2/4] Automatically handle ZEND_ACC_MAY_BE_CYCLIC for extensions If get_gc is customized, assume cyclic classes for now. --- Zend/zend_API.c | 15 +++++++++++++++ Zend/zend_closures.c | 5 ++--- Zend/zend_fibers.c | 1 - Zend/zend_generators.c | 1 - Zend/zend_weakrefs.c | 1 - ext/curl/interface.c | 1 - ext/curl/multi.c | 1 - ext/dom/php_dom.c | 2 -- ext/pdo/pdo_dbh.c | 1 - ext/pdo/pdo_stmt.c | 1 - ext/reflection/php_reflection.c | 22 ---------------------- ext/simplexml/simplexml.c | 1 - ext/sockets/sockets.c | 1 - ext/spl/spl_array.c | 2 -- ext/spl/spl_dllist.c | 1 - ext/spl/spl_fixedarray.c | 1 - ext/spl/spl_heap.c | 2 -- ext/spl/spl_iterators.c | 2 -- ext/spl/spl_observer.c | 2 -- ext/sqlite3/sqlite3.c | 1 - ext/xml/xml.c | 1 - ext/xsl/php_xsl.c | 1 - 22 files changed, 17 insertions(+), 49 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 6f7f0f1b323c0..76faf07580488 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2390,6 +2390,8 @@ ZEND_API zend_result zend_startup_module_ex(zend_module_entry *module) /* {{{ */ } module->module_started = 1; + uint32_t prev_class_count = zend_hash_num_elements(CG(class_table)); + /* Check module dependencies */ if (module->deps) { const zend_module_dep *dep = module->deps; @@ -2434,6 +2436,19 @@ ZEND_API zend_result zend_startup_module_ex(zend_module_entry *module) /* {{{ */ } EG(current_module) = NULL; } + + /* Mark classes with custom get_gc handler as potentially cyclic, even if + * their properties don't indicate so. */ + if (prev_class_count != zend_hash_num_elements(CG(class_table))) { + Bucket *p; + ZEND_HASH_MAP_FOREACH_BUCKET_FROM(CG(class_table), p, prev_class_count) { + zend_class_entry *ce = Z_PTR(p->val); + if (ce->default_object_handlers->get_gc != zend_std_get_gc) { + ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; + } + } ZEND_HASH_FOREACH_END(); + } + return SUCCESS; } /* }}} */ diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 54899f25945a6..f3bc55541cf5c 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -705,9 +705,8 @@ void zend_register_closure_ce(void) /* {{{ */ zend_ce_closure = register_class_Closure(); zend_ce_closure->create_object = zend_closure_new; zend_ce_closure->default_object_handlers = &closure_handlers; - /* FIXME: Potentially improve during construction of closure? static closures - * not binding by references can't be cyclic. */ - zend_ce_closure->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; + /* FIXME: Potentially infer ZEND_ACC_MAY_BE_CYCLIC during construction of + * closure? static closures not binding by references can't be cyclic. */ memcpy(&closure_handlers, &std_object_handlers, sizeof(zend_object_handlers)); closure_handlers.free_obj = zend_closure_free_storage; diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index da0a2a5f16eb1..97b7cdcc911b7 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -1107,7 +1107,6 @@ void zend_register_fiber_ce(void) zend_ce_fiber = register_class_Fiber(); zend_ce_fiber->create_object = zend_fiber_object_create; zend_ce_fiber->default_object_handlers = &zend_fiber_handlers; - zend_ce_fiber->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; zend_fiber_handlers = std_object_handlers; zend_fiber_handlers.dtor_obj = zend_fiber_object_destroy; diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index d847da5e93942..a6ea91a7425b9 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -1215,7 +1215,6 @@ void zend_register_generator_ce(void) /* {{{ */ /* get_iterator has to be assigned *after* implementing the interface */ zend_ce_generator->get_iterator = zend_generator_get_iterator; zend_ce_generator->default_object_handlers = &zend_generator_handlers; - zend_ce_generator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&zend_generator_handlers, &std_object_handlers, sizeof(zend_object_handlers)); zend_generator_handlers.free_obj = zend_generator_free_storage; diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 2dce20257e689..cf363cd12595c 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -788,7 +788,6 @@ void zend_register_weakref_ce(void) /* {{{ */ zend_ce_weakmap->create_object = zend_weakmap_create_object; zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator; zend_ce_weakmap->default_object_handlers = &zend_weakmap_handlers; - zend_ce_weakmap->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&zend_weakmap_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); zend_weakmap_handlers.offset = XtOffsetOf(zend_weakmap, std); diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 328ea4e0a674a..aba5273d5496c 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -395,7 +395,6 @@ PHP_MINIT_FUNCTION(curl) curl_ce = register_class_CurlHandle(); curl_ce->create_object = curl_create_object; curl_ce->default_object_handlers = &curl_object_handlers; - curl_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&curl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); curl_object_handlers.offset = XtOffsetOf(php_curl, std); diff --git a/ext/curl/multi.c b/ext/curl/multi.c index 244c3b078a752..6456cf6f813e4 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -606,7 +606,6 @@ static zend_object_handlers curl_multi_handlers; void curl_multi_register_handlers(void) { curl_multi_ce->create_object = curl_multi_create_object; curl_multi_ce->default_object_handlers = &curl_multi_handlers; - curl_multi_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&curl_multi_handlers, &std_object_handlers, sizeof(zend_object_handlers)); curl_multi_handlers.offset = XtOffsetOf(php_curlm, std); diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index b92f2d843d4b6..21741166c61aa 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1292,7 +1292,6 @@ PHP_MINIT_FUNCTION(dom) dom_xpath_class_entry = register_class_DOMXPath(); dom_xpath_class_entry->create_object = dom_xpath_objects_new; dom_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers; - dom_xpath_class_entry->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; zend_hash_init(&dom_xpath_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_xpath_prop_handlers, "document", dom_xpath_document_read, NULL); @@ -1302,7 +1301,6 @@ PHP_MINIT_FUNCTION(dom) dom_modern_xpath_class_entry = register_class_Dom_XPath(); dom_modern_xpath_class_entry->create_object = dom_xpath_objects_new; dom_modern_xpath_class_entry->default_object_handlers = &dom_xpath_object_handlers; - dom_modern_xpath_class_entry->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; zend_hash_add_new_ptr(&classes, dom_modern_xpath_class_entry->name, &dom_xpath_prop_handlers); #endif diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 611c8a490b96f..782639be0758e 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1449,7 +1449,6 @@ void pdo_dbh_init(int module_number) pdo_dbh_ce = register_class_PDO(); pdo_dbh_ce->create_object = pdo_dbh_new; pdo_dbh_ce->default_object_handlers = &pdo_dbh_object_handlers; - pdo_dbh_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&pdo_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); pdo_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std); diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index df131685a16a1..a39b7a3b06804 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2491,7 +2491,6 @@ void pdo_stmt_init(void) pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get; pdo_dbstmt_ce->create_object = pdo_dbstmt_new; pdo_dbstmt_ce->default_object_handlers = &pdo_dbstmt_object_handlers; - pdo_dbstmt_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9b60374f8de89..aca7467efd75b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -7692,113 +7692,91 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ reflection_function_abstract_ptr = register_class_ReflectionFunctionAbstract(reflector_ptr); reflection_function_abstract_ptr->default_object_handlers = &reflection_object_handlers; - reflection_function_abstract_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_function_abstract_ptr->create_object = reflection_objects_new; reflection_function_ptr = register_class_ReflectionFunction(reflection_function_abstract_ptr); reflection_function_ptr->create_object = reflection_objects_new; reflection_function_ptr->default_object_handlers = &reflection_object_handlers; - reflection_function_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_generator_ptr = register_class_ReflectionGenerator(); reflection_generator_ptr->create_object = reflection_objects_new; reflection_generator_ptr->default_object_handlers = &reflection_object_handlers; - reflection_generator_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_parameter_ptr = register_class_ReflectionParameter(reflector_ptr); reflection_parameter_ptr->create_object = reflection_objects_new; reflection_parameter_ptr->default_object_handlers = &reflection_object_handlers; - reflection_parameter_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_type_ptr = register_class_ReflectionType(zend_ce_stringable); reflection_type_ptr->create_object = reflection_objects_new; reflection_type_ptr->default_object_handlers = &reflection_object_handlers; - reflection_type_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_named_type_ptr = register_class_ReflectionNamedType(reflection_type_ptr); reflection_named_type_ptr->create_object = reflection_objects_new; reflection_named_type_ptr->default_object_handlers = &reflection_object_handlers; - reflection_named_type_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_union_type_ptr = register_class_ReflectionUnionType(reflection_type_ptr); reflection_union_type_ptr->create_object = reflection_objects_new; reflection_union_type_ptr->default_object_handlers = &reflection_object_handlers; - reflection_union_type_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_intersection_type_ptr = register_class_ReflectionIntersectionType(reflection_type_ptr); reflection_intersection_type_ptr->create_object = reflection_objects_new; reflection_intersection_type_ptr->default_object_handlers = &reflection_object_handlers; - reflection_intersection_type_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_method_ptr = register_class_ReflectionMethod(reflection_function_abstract_ptr); reflection_method_ptr->create_object = reflection_objects_new; reflection_method_ptr->default_object_handlers = &reflection_object_handlers; - reflection_method_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_class_ptr = register_class_ReflectionClass(reflector_ptr); reflection_class_ptr->create_object = reflection_objects_new; reflection_class_ptr->default_object_handlers = &reflection_object_handlers; - reflection_class_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_object_ptr = register_class_ReflectionObject(reflection_class_ptr); reflection_object_ptr->create_object = reflection_objects_new; reflection_object_ptr->default_object_handlers = &reflection_object_handlers; - reflection_object_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_property_ptr = register_class_ReflectionProperty(reflector_ptr); reflection_property_ptr->create_object = reflection_objects_new; reflection_property_ptr->default_object_handlers = &reflection_object_handlers; - reflection_property_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_class_constant_ptr = register_class_ReflectionClassConstant(reflector_ptr); reflection_class_constant_ptr->create_object = reflection_objects_new; reflection_class_constant_ptr->default_object_handlers = &reflection_object_handlers; - reflection_class_constant_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_extension_ptr = register_class_ReflectionExtension(reflector_ptr); reflection_extension_ptr->create_object = reflection_objects_new; reflection_extension_ptr->default_object_handlers = &reflection_object_handlers; - reflection_extension_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_zend_extension_ptr = register_class_ReflectionZendExtension(reflector_ptr); reflection_zend_extension_ptr->create_object = reflection_objects_new; reflection_zend_extension_ptr->default_object_handlers = &reflection_object_handlers; - reflection_zend_extension_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_reference_ptr = register_class_ReflectionReference(); reflection_reference_ptr->create_object = reflection_objects_new; reflection_reference_ptr->default_object_handlers = &reflection_object_handlers; - reflection_reference_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_attribute_ptr = register_class_ReflectionAttribute(reflector_ptr); reflection_attribute_ptr->create_object = reflection_objects_new; reflection_attribute_ptr->default_object_handlers = &reflection_object_handlers; - reflection_attribute_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_enum_ptr = register_class_ReflectionEnum(reflection_class_ptr); reflection_enum_ptr->create_object = reflection_objects_new; reflection_enum_ptr->default_object_handlers = &reflection_object_handlers; - reflection_enum_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_enum_unit_case_ptr = register_class_ReflectionEnumUnitCase(reflection_class_constant_ptr); reflection_enum_unit_case_ptr->create_object = reflection_objects_new; reflection_enum_unit_case_ptr->default_object_handlers = &reflection_object_handlers; - reflection_enum_unit_case_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_enum_backed_case_ptr = register_class_ReflectionEnumBackedCase(reflection_enum_unit_case_ptr); reflection_enum_backed_case_ptr->create_object = reflection_objects_new; reflection_enum_backed_case_ptr->default_object_handlers = &reflection_object_handlers; - reflection_enum_backed_case_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_fiber_ptr = register_class_ReflectionFiber(); reflection_fiber_ptr->create_object = reflection_objects_new; reflection_fiber_ptr->default_object_handlers = &reflection_object_handlers; - reflection_fiber_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_constant_ptr = register_class_ReflectionConstant(reflector_ptr); reflection_constant_ptr->create_object = reflection_objects_new; reflection_constant_ptr->default_object_handlers = &reflection_object_handlers; - reflection_constant_ptr->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; reflection_property_hook_type_ptr = register_class_PropertyHookType(); diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 5a627b43324e5..a962028d30865 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -2654,7 +2654,6 @@ PHP_MINIT_FUNCTION(simplexml) ce_SimpleXMLElement = register_class_SimpleXMLElement(zend_ce_stringable, zend_ce_countable, spl_ce_RecursiveIterator); ce_SimpleXMLElement->create_object = sxe_object_new; ce_SimpleXMLElement->default_object_handlers = &sxe_object_handlers; - ce_SimpleXMLElement->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; ce_SimpleXMLElement->get_iterator = php_sxe_get_iterator; memcpy(&sxe_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index e31cc7cf4b44c..940b1621f75d0 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -455,7 +455,6 @@ static PHP_MINIT_FUNCTION(sockets) socket_ce = register_class_Socket(); socket_ce->create_object = socket_create_object; socket_ce->default_object_handlers = &socket_object_handlers; - socket_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&socket_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); socket_object_handlers.offset = XtOffsetOf(php_socket, std); diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index a77f110c47e55..af886944bc49e 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -1883,7 +1883,6 @@ PHP_MINIT_FUNCTION(spl_array) spl_ce_ArrayObject = register_class_ArrayObject(zend_ce_aggregate, zend_ce_arrayaccess, zend_ce_serializable, zend_ce_countable); spl_ce_ArrayObject->create_object = spl_array_object_new; spl_ce_ArrayObject->default_object_handlers = &spl_handler_ArrayObject; - spl_ce_ArrayObject->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&spl_handler_ArrayObject, &std_object_handlers, sizeof(zend_object_handlers)); @@ -1910,7 +1909,6 @@ PHP_MINIT_FUNCTION(spl_array) spl_ce_ArrayIterator = register_class_ArrayIterator(spl_ce_SeekableIterator, zend_ce_arrayaccess, zend_ce_serializable, zend_ce_countable); spl_ce_ArrayIterator->create_object = spl_array_object_new; spl_ce_ArrayIterator->default_object_handlers = &spl_handler_ArrayObject; - spl_ce_ArrayIterator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator; spl_ce_RecursiveArrayIterator = register_class_RecursiveArrayIterator(spl_ce_ArrayIterator, spl_ce_RecursiveIterator); diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index cf82c8000c450..5a78db2921a81 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -1256,7 +1256,6 @@ PHP_MINIT_FUNCTION(spl_dllist) /* {{{ */ ); spl_ce_SplDoublyLinkedList->create_object = spl_dllist_object_new; spl_ce_SplDoublyLinkedList->default_object_handlers = &spl_handler_SplDoublyLinkedList; - spl_ce_SplDoublyLinkedList->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_SplDoublyLinkedList->get_iterator = spl_dllist_get_iterator; memcpy(&spl_handler_SplDoublyLinkedList, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index a25e389861cf1..5d7949308a303 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -964,7 +964,6 @@ PHP_MINIT_FUNCTION(spl_fixedarray) zend_ce_aggregate, zend_ce_arrayaccess, zend_ce_countable, php_json_serializable_ce); spl_ce_SplFixedArray->create_object = spl_fixedarray_new; spl_ce_SplFixedArray->default_object_handlers = &spl_handler_SplFixedArray; - spl_ce_SplFixedArray->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_SplFixedArray->get_iterator = spl_fixedarray_get_iterator; memcpy(&spl_handler_SplFixedArray, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 52d5c9783a074..d4450da42009c 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -1141,7 +1141,6 @@ PHP_MINIT_FUNCTION(spl_heap) /* {{{ */ spl_ce_SplHeap = register_class_SplHeap(zend_ce_iterator, zend_ce_countable); spl_ce_SplHeap->create_object = spl_heap_object_new; spl_ce_SplHeap->default_object_handlers = &spl_handler_SplHeap; - spl_ce_SplHeap->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_SplHeap->get_iterator = spl_heap_get_iterator; memcpy(&spl_handler_SplHeap, &std_object_handlers, sizeof(zend_object_handlers)); @@ -1163,7 +1162,6 @@ PHP_MINIT_FUNCTION(spl_heap) /* {{{ */ spl_ce_SplPriorityQueue = register_class_SplPriorityQueue(zend_ce_iterator, zend_ce_countable); spl_ce_SplPriorityQueue->create_object = spl_heap_object_new; spl_ce_SplPriorityQueue->default_object_handlers = &spl_handler_SplPriorityQueue; - spl_ce_SplPriorityQueue->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_SplPriorityQueue->get_iterator = spl_pqueue_get_iterator; memcpy(&spl_handler_SplPriorityQueue, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index a0360ef556d62..d4e40bf6b684a 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -3108,7 +3108,6 @@ PHP_MINIT_FUNCTION(spl_iterators) spl_ce_RecursiveIteratorIterator = register_class_RecursiveIteratorIterator(spl_ce_OuterIterator); spl_ce_RecursiveIteratorIterator->create_object = spl_RecursiveIteratorIterator_new; spl_ce_RecursiveIteratorIterator->default_object_handlers = &spl_handlers_rec_it_it; - spl_ce_RecursiveIteratorIterator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator; memcpy(&spl_handlers_rec_it_it, &std_object_handlers, sizeof(zend_object_handlers)); @@ -3128,7 +3127,6 @@ PHP_MINIT_FUNCTION(spl_iterators) spl_ce_IteratorIterator = register_class_IteratorIterator(spl_ce_OuterIterator); spl_ce_IteratorIterator->create_object = spl_dual_it_new; spl_ce_IteratorIterator->default_object_handlers = &spl_handlers_dual_it; - spl_ce_IteratorIterator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; spl_ce_FilterIterator = register_class_FilterIterator(spl_ce_IteratorIterator); spl_ce_FilterIterator->create_object = spl_dual_it_new; diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index a3847bed048e8..5222fe2a7fa4d 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -1379,7 +1379,6 @@ PHP_MINIT_FUNCTION(spl_observer) spl_ce_SplObjectStorage = register_class_SplObjectStorage(zend_ce_countable, spl_ce_SeekableIterator, zend_ce_serializable, zend_ce_arrayaccess); spl_ce_SplObjectStorage->create_object = spl_SplObjectStorage_new; spl_ce_SplObjectStorage->default_object_handlers = &spl_handler_SplObjectStorage; - spl_ce_SplObjectStorage->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&spl_handler_SplObjectStorage, &std_object_handlers, sizeof(zend_object_handlers)); @@ -1396,7 +1395,6 @@ PHP_MINIT_FUNCTION(spl_observer) spl_ce_MultipleIterator = register_class_MultipleIterator(zend_ce_iterator); spl_ce_MultipleIterator->create_object = spl_SplObjectStorage_new; spl_ce_MultipleIterator->default_object_handlers = &spl_handler_SplObjectStorage; - spl_ce_MultipleIterator->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; return SUCCESS; } diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index e7200b65355cd..01b8af435b633 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -2411,7 +2411,6 @@ PHP_MINIT_FUNCTION(sqlite3) php_sqlite3_sc_entry = register_class_SQLite3(); php_sqlite3_sc_entry->create_object = php_sqlite3_object_new; php_sqlite3_sc_entry->default_object_handlers = &sqlite3_object_handlers; - php_sqlite3_sc_entry->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; /* Register SQLite 3 Prepared Statement Class */ sqlite3_stmt_object_handlers.offset = XtOffsetOf(php_sqlite3_stmt, zo); diff --git a/ext/xml/xml.c b/ext/xml/xml.c index d034898f861c9..7eca5c0795e2a 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -228,7 +228,6 @@ PHP_MINIT_FUNCTION(xml) xml_parser_ce = register_class_XMLParser(); xml_parser_ce->create_object = xml_parser_create_object; xml_parser_ce->default_object_handlers = &xml_parser_object_handlers; - xml_parser_ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; memcpy(&xml_parser_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); xml_parser_object_handlers.offset = XtOffsetOf(xml_parser, std); diff --git a/ext/xsl/php_xsl.c b/ext/xsl/php_xsl.c index 8c45429269d72..dec7eb501eb2f 100644 --- a/ext/xsl/php_xsl.c +++ b/ext/xsl/php_xsl.c @@ -278,7 +278,6 @@ PHP_MINIT_FUNCTION(xsl) xsl_xsltprocessor_class_entry = register_class_XSLTProcessor(); xsl_xsltprocessor_class_entry->create_object = xsl_objects_new; xsl_xsltprocessor_class_entry->default_object_handlers = &xsl_object_handlers; - xsl_xsltprocessor_class_entry->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; #ifdef HAVE_XSL_EXSLT exsltRegisterAll(); From 4eddbc52eec93a3be9227c0d5ba23fcdfeabd04d Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 13 Dec 2024 11:12:33 +0100 Subject: [PATCH 3/4] Fix leak in Zend/tests/bug64720.phpt Don't rely on the cycle collector for objects released in destructor. --- Zend/zend_objects_API.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 8a6b714c8b3fd..5c7c51ddf32c1 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -55,7 +55,9 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_sto || obj->ce->destructor) { GC_ADDREF(obj); obj->handlers->dtor_obj(obj); - GC_DELREF(obj); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } } } From 786c14a21ae35f8a8db129a95138ec52204bbfc8 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 16 Dec 2024 20:02:23 +0100 Subject: [PATCH 4/4] Fixes and improvements * Actually skip non-cyclic objects in GC. * Add MAY_BE_CYCLIC to more internal classes * Fix dynamic property creation trigger And also breaking GH-10932 for now. :) I will try to fix this later. --- Zend/zend_API.c | 7 ++- Zend/zend_gc.c | 91 +++++++++++++++++++------------------ Zend/zend_iterators.c | 1 + Zend/zend_object_handlers.c | 4 +- Zend/zend_types.h | 2 +- 5 files changed, 57 insertions(+), 48 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 76faf07580488..9edcc11bce929 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1770,6 +1770,7 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) ZSTR_VAL(object->ce->name), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); } + GC_TYPE_INFO(object) &= ~(GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT); prop = zend_hash_update(zend_std_get_properties_ex(object), key, prop); zval_add_ref(prop); } @@ -1782,6 +1783,7 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) ZSTR_VAL(object->ce->name), h); } + GC_TYPE_INFO(object) &= ~(GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT); prop = zend_hash_index_update(zend_std_get_properties_ex(object), h, prop); zval_add_ref(prop); } @@ -2443,7 +2445,10 @@ ZEND_API zend_result zend_startup_module_ex(zend_module_entry *module) /* {{{ */ Bucket *p; ZEND_HASH_MAP_FOREACH_BUCKET_FROM(CG(class_table), p, prev_class_count) { zend_class_entry *ce = Z_PTR(p->val); - if (ce->default_object_handlers->get_gc != zend_std_get_gc) { + if ((ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES) + || ce->create_object + || ce->default_object_handlers->get_gc != zend_std_get_gc + || ce->default_object_handlers->get_properties != zend_std_get_properties) { ce->ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; } } ZEND_HASH_FOREACH_END(); diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index a966a106def33..b996e7e294ed8 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -242,6 +242,9 @@ #define GC_FETCH_NEXT_UNUSED() \ gc_fetch_next_unused() +#define GC_COLLECTABLE(ref) \ + (!(GC_TYPE_INFO(ref) & (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT))) + ZEND_API int (*gc_collect_cycles)(void); typedef struct _gc_root_buffer { @@ -818,7 +821,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) zval *entry = (zval*) Z_PTR_P(zv); zval *weakmap = zv+1; ZEND_ASSERT(Z_REFCOUNTED_P(weakmap)); - if (Z_OPT_REFCOUNTED_P(entry)) { + if (Z_OPT_REFCOUNTED_P(entry) && GC_COLLECTABLE(Z_COUNTED_P(entry))) { GC_UNSET_FROM_WEAKMAP_KEY(entry); if (GC_REF_CHECK_COLOR(Z_COUNTED_P(weakmap), GC_GREY)) { /* Weakmap was scanned in gc_mark_roots, we must @@ -855,7 +858,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) ZEND_ASSERT(Z_TYPE_P(zv+1) == IS_PTR); zval *key = zv; zval *entry = (zval*) Z_PTR_P(zv+1); - if (Z_OPT_REFCOUNTED_P(entry)) { + if (Z_OPT_REFCOUNTED_P(entry) && GC_COLLECTABLE(Z_COUNTED_P(entry))) { GC_UNSET_FROM_WEAKMAP(entry); if (GC_REF_CHECK_COLOR(Z_COUNTED_P(key), GC_GREY)) { /* Key was scanned in gc_mark_roots, we must @@ -888,12 +891,12 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) ht = obj->handlers->get_gc(obj, &table, &len); n = len; zv = table; - if (UNEXPECTED(ht)) { + if (UNEXPECTED(ht) && GC_COLLECTABLE(ht)) { GC_ADDREF(ht); if (!GC_REF_CHECK_COLOR(ht, GC_BLACK)) { GC_REF_SET_BLACK(ht); for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) { @@ -909,14 +912,14 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) handle_zvals: for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) { GC_REF_SET_BLACK(ref); zv++; while (--n) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) { @@ -948,7 +951,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) { @@ -959,7 +962,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) { @@ -975,7 +978,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) p++; } } else if (GC_TYPE(ref) == IS_REFERENCE) { - if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { + if (Z_REFCOUNTED(((zend_reference*)ref)->val) && GC_COLLECTABLE(Z_COUNTED(((zend_reference*)ref)->val))) { ref = Z_COUNTED(((zend_reference*)ref)->val); GC_ADDREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) { @@ -1019,7 +1022,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) zval *entry = (zval*) Z_PTR_P(zv); zval *weakmap = zv+1; ZEND_ASSERT(Z_REFCOUNTED_P(weakmap)); - if (Z_REFCOUNTED_P(entry)) { + if (Z_REFCOUNTED_P(entry) && GC_COLLECTABLE(Z_COUNTED_P(entry))) { GC_SET_FROM_WEAKMAP_KEY(entry); ref = Z_COUNTED_P(entry); /* Only DELREF if the contribution from the weakmap has @@ -1043,7 +1046,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) for (; n != 0; n--) { ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); zval *entry = (zval*) Z_PTR_P(zv); - if (Z_REFCOUNTED_P(entry)) { + if (Z_REFCOUNTED_P(entry) && GC_COLLECTABLE(Z_COUNTED_P(entry))) { GC_SET_FROM_WEAKMAP(entry); ref = Z_COUNTED_P(entry); /* Only DELREF if the contribution from the weakmap key @@ -1064,12 +1067,12 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) ht = obj->handlers->get_gc(obj, &table, &len); n = len; zv = table; - if (UNEXPECTED(ht)) { + if (UNEXPECTED(ht) && GC_COLLECTABLE(ht)) { GC_DELREF(ht); if (!GC_REF_CHECK_COLOR(ht, GC_GREY)) { GC_REF_SET_COLOR(ht, GC_GREY); for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { @@ -1084,14 +1087,14 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) } handle_zvals: for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_GREY); zv++; while (--n) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { @@ -1123,7 +1126,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { @@ -1134,7 +1137,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { @@ -1150,7 +1153,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) p++; } } else if (GC_TYPE(ref) == IS_REFERENCE) { - if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { + if (Z_REFCOUNTED(((zend_reference*)ref)->val) && GC_COLLECTABLE(Z_COUNTED(((zend_reference*)ref)->val))) { ref = Z_COUNTED(((zend_reference*)ref)->val); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { @@ -1263,7 +1266,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack) for (; n != 0; n--) { ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); zval *entry = (zval*) Z_PTR_P(zv); - if (Z_OPT_REFCOUNTED_P(entry)) { + if (Z_OPT_REFCOUNTED_P(entry) && GC_COLLECTABLE(Z_COUNTED_P(entry))) { ref = Z_COUNTED_P(entry); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); @@ -1277,12 +1280,12 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack) ht = obj->handlers->get_gc(obj, &table, &len); n = len; zv = table; - if (UNEXPECTED(ht)) { + if (UNEXPECTED(ht) && GC_COLLECTABLE(ht)) { if (GC_REF_CHECK_COLOR(ht, GC_GREY)) { GC_REF_SET_COLOR(ht, GC_WHITE); GC_STACK_PUSH((zend_refcounted *) ht); for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); @@ -1297,13 +1300,13 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack) handle_zvals: for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); zv++; while (--n) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); @@ -1335,7 +1338,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack) if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); @@ -1345,7 +1348,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack) if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); @@ -1360,7 +1363,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack) p++; } } else if (GC_TYPE(ref) == IS_REFERENCE) { - if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { + if (Z_REFCOUNTED(((zend_reference*)ref)->val) && GC_COLLECTABLE(Z_COUNTED(((zend_reference*)ref)->val))) { ref = Z_COUNTED(((zend_reference*)ref)->val); if (GC_REF_CHECK_COLOR(ref, GC_GREY)) { GC_REF_SET_COLOR(ref, GC_WHITE); @@ -1473,7 +1476,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta for (; n != 0; n--) { ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); zval *entry = (zval*) Z_PTR_P(zv); - if (Z_REFCOUNTED_P(entry) && GC_FROM_WEAKMAP_KEY(entry)) { + if (Z_REFCOUNTED_P(entry) && GC_COLLECTABLE(Z_COUNTED_P(entry)) && GC_FROM_WEAKMAP_KEY(entry)) { GC_UNSET_FROM_WEAKMAP_KEY(entry); GC_UNSET_FROM_WEAKMAP(entry); ref = Z_COUNTED_P(entry); @@ -1494,7 +1497,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta for (; n != 0; n--) { ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); zval *entry = (zval*) Z_PTR_P(zv); - if (Z_REFCOUNTED_P(entry) && GC_FROM_WEAKMAP(entry)) { + if (Z_REFCOUNTED_P(entry) && GC_COLLECTABLE(Z_COUNTED_P(entry)) && GC_FROM_WEAKMAP(entry)) { GC_UNSET_FROM_WEAKMAP_KEY(entry); GC_UNSET_FROM_WEAKMAP(entry); ref = Z_COUNTED_P(entry); @@ -1512,12 +1515,12 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta ht = obj->handlers->get_gc(obj, &table, &len); n = len; zv = table; - if (UNEXPECTED(ht)) { + if (UNEXPECTED(ht) && GC_COLLECTABLE(ht)) { GC_ADDREF(ht); if (GC_REF_CHECK_COLOR(ht, GC_WHITE)) { GC_REF_SET_BLACK(ht); for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { @@ -1533,14 +1536,14 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta handle_zvals: for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { GC_REF_SET_BLACK(ref); zv++; while (--n) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { @@ -1576,7 +1579,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { @@ -1587,7 +1590,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { @@ -1603,7 +1606,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta p++; } } else if (GC_TYPE(ref) == IS_REFERENCE) { - if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { + if (Z_REFCOUNTED(((zend_reference*)ref)->val) && GC_COLLECTABLE(Z_COUNTED(((zend_reference*)ref)->val))) { ref = Z_COUNTED(((zend_reference*)ref)->val); GC_ADDREF(ref); if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) { @@ -1681,7 +1684,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe GC_REMOVE_FROM_BUFFER(ref); count++; } else if (GC_TYPE(ref) == IS_REFERENCE) { - if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { + if (Z_REFCOUNTED(((zend_reference*)ref)->val) && GC_COLLECTABLE(Z_COUNTED(((zend_reference*)ref)->val))) { ref = Z_COUNTED(((zend_reference*)ref)->val); goto tail_call; } @@ -1704,7 +1707,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe for (; n != 0; n--) { ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); zval *entry = (zval*) Z_PTR_P(zv); - if (Z_OPT_REFCOUNTED_P(entry)) { + if (Z_OPT_REFCOUNTED_P(entry) && GC_COLLECTABLE(Z_COUNTED_P(entry))) { ref = Z_COUNTED_P(entry); GC_STACK_PUSH(ref); } @@ -1715,9 +1718,9 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe ht = obj->handlers->get_gc(obj, &table, &len); n = len; zv = table; - if (UNEXPECTED(ht)) { + if (UNEXPECTED(ht) && GC_COLLECTABLE(ht)) { for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); GC_STACK_PUSH(ref); } @@ -1732,11 +1735,11 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe handle_zvals: for (; n != 0; n--) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); zv++; while (--n) { - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); GC_STACK_PUSH(ref); } @@ -1763,7 +1766,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { ref = Z_COUNTED_P(zv); p++; while (--n) { @@ -1771,7 +1774,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } - if (Z_REFCOUNTED_P(zv)) { + if (Z_REFCOUNTED_P(zv) && GC_COLLECTABLE(Z_COUNTED_P(zv))) { zend_refcounted *ref = Z_COUNTED_P(zv); GC_STACK_PUSH(ref); } @@ -2205,7 +2208,7 @@ static void zend_gc_remove_root_tmpvars(void) { if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) { uint32_t var_num = range->var & ~ZEND_LIVE_MASK; zval *var = ZEND_CALL_VAR(ex, var_num); - if (Z_REFCOUNTED_P(var)) { + if (Z_REFCOUNTED_P(var) && GC_COLLECTABLE(Z_COUNTED_P(var))) { GC_REMOVE_FROM_BUFFER(Z_COUNTED_P(var)); } } diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c index f67033b11161c..6ff9c64857afc 100644 --- a/Zend/zend_iterators.c +++ b/Zend/zend_iterators.c @@ -58,6 +58,7 @@ ZEND_API void zend_register_iterator_wrapper(void) { INIT_CLASS_ENTRY(zend_iterator_class_entry, "__iterator_wrapper", NULL); zend_iterator_class_entry.default_object_handlers = &iterator_object_handlers; + zend_iterator_class_entry.ce_flags |= ZEND_ACC_MAY_BE_CYCLIC; } static void iter_wrapper_free(zend_object *object) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 8aa5c243110d5..519beaabac54d 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -70,8 +70,6 @@ ZEND_API HashTable *rebuild_object_properties_internal(zend_object *zobj) /* {{{ zend_class_entry *ce = zobj->ce; int i; - GC_TYPE_INFO(zobj) &= ~(GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT); - zobj->properties = zend_new_array(ce->default_properties_count); if (ce->default_properties_count) { zend_hash_real_init_mixed(zobj->properties); @@ -1171,6 +1169,7 @@ found:; variable_ptr = &EG(error_zval); goto exit; } + GC_TYPE_INFO(zobj) &= ~(GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT); if (UNEXPECTED(!(zobj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { if (UNEXPECTED(!zend_deprecated_dynamic_property(zobj, name))) { variable_ptr = &EG(error_zval); @@ -1389,6 +1388,7 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam return &EG(error_zval); } } + GC_TYPE_INFO(zobj) &= ~(GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT); if (UNEXPECTED(zend_lazy_object_must_init(zobj))) { zobj = zend_lazy_object_init(zobj); if (!zobj) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index f7d234c92c622..f79c3df5a8f3e 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -786,7 +786,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define GC_ARRAY IS_ARRAY #define GC_OBJECT IS_OBJECT #define GC_RESOURCE (IS_RESOURCE | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)) -#define GC_REFERENCE (IS_REFERENCE | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)) +#define GC_REFERENCE IS_REFERENCE #define GC_CONSTANT_AST (IS_CONSTANT_AST | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)) /* zval.u1.v.type_flags */