]> granicus.if.org Git - php/commitdiff
Refactored recursion pretection
authorDmitry Stogov <dmitry@zend.com>
Thu, 5 Oct 2017 22:34:50 +0000 (01:34 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 5 Oct 2017 22:34:50 +0000 (01:34 +0300)
31 files changed:
UPGRADING
UPGRADING.INTERNALS
Zend/tests/gc_032.phpt
Zend/zend.c
Zend/zend_builtin_functions.c
Zend/zend_exceptions.c
Zend/zend_hash.c
Zend/zend_hash.h
Zend/zend_object_handlers.c
Zend/zend_operators.c
Zend/zend_ts_hash.c
Zend/zend_ts_hash.h
Zend/zend_types.h
ext/filter/filter.c
ext/json/json_encoder.c
ext/mbstring/mbstring.c
ext/mbstring/tests/bug66964.phpt
ext/opcache/zend_accelerator_util_funcs.c
ext/opcache/zend_persist.c
ext/soap/php_encoding.c
ext/spl/spl_observer.c
ext/standard/array.c
ext/standard/http.c
ext/standard/php_array.h
ext/standard/tests/array/compact_variation1.phpt
ext/standard/tests/array/count_variation3.phpt
ext/standard/tests/general_functions/debug_zval_dump_o.phpt
ext/standard/var.c
ext/wddx/wddx.c
ext/xmlrpc/xmlrpc-epi-php.c
sapi/phpdbg/phpdbg_utils.c

index de2dc910161a22308934a349d48f90b06f3b0dfb..f9d59167050184ebbb902140ac2cd6e7312b3ecf 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -55,6 +55,9 @@ BCMath:
 5. Changed Functions
 ========================================
 
+  . debug_zval_dump() was changed to display recursive arrays and objects
+    in the same way as var_dump(). Now, it doesn't display them twice.
+
 ========================================
 6. New Functions
 ========================================
index efb9920bfb9c0f824259162efe6b15346ddcb407..1b6ba2adbba3f9357a7ff8718a273421c136545d 100644 (file)
@@ -11,6 +11,7 @@ PHP 7.2 INTERNALS UPGRADE NOTES
   h. valid_symbol_table removed
   i. array_init() and array_init_size()
   j. Run-time constant operand addressing
+  k. Array/Object recursion protection
 
 2. Build system changes
   a. Unix build system changes
@@ -70,6 +71,19 @@ PHP 7.2 INTERNALS UPGRADE NOTES
      RT_CONSTANT_EX, EX_CONSTANT should be substituted by RT_CONSTANT than now
      use "opline" (instead of "op_array") as first argument.
 
+  k. Protection from recursion during processing circular data structures was
+     refactored. HashTable.nApplyCount and IS_OBJ_APPLY_COUNT are replaced by
+     single flag GC_PROTECTED. Corresponding macros Z_OBJ_APPLY_COUNT,
+     Z_OBJ_INC_APPLY_COUNT, Z_OBJ_DEC_APPLY_COUNT, ZEND_HASH_GET_APPLY_COUNT,
+     ZEND_HASH_INC_APPLY_COUNT, ZEND_HASH_DEC_APPLY_COUNT are replaced with
+     GC_IS_RECURSIVE, GC_PROTECT_RECURSION, GC_UNPROTECT_RECURSION,
+     Z_IS_RECURSIVE, Z_PROTECT_RECURSION, Z_UNPROTECT_RECURSION.
+
+     HASH_FLAG_APPLY_PROTECTION flag and ZEND_HASH_APPLY_PROTECTION() macro
+     are removed. All mutable arrays should use recursion protection.
+     Corresponding checks should be replaced by Z_REFCOUNTED() or
+     !(GC_GLAGS(p) & GC_IMMUTABLE).
+
 
 ========================
 2. Build system changes
index cd30ed7cb6ab13c2dd81e9cb00a8a945d706dd64..b11a6655f166d071dcbeb05aa218d55fb85af85f 100644 (file)
@@ -18,10 +18,7 @@ array(1) refcount(%d){
   [0]=>
   array(1) refcount(%d){
     [0]=>
-    array(1) refcount(%d){
-      [0]=>
-      *RECURSION*
-    }
+    *RECURSION*
   }
 }
 array(1) refcount(%d){
@@ -30,13 +27,7 @@ array(1) refcount(%d){
     [0]=>
     array(1) refcount(%d){
       [0]=>
-      array(1) refcount(%d){
-        [0]=>
-        array(1) refcount(%d){
-          [0]=>
-          *RECURSION*
-        }
-      }
+      *RECURSION*
     }
   }
 }
index 014b9744d7a170ac4726d906b785f5ef6ece6dc2..2621851941f5051ae3d251f982c2b66d9e87158f 100644 (file)
@@ -334,16 +334,17 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
        switch (Z_TYPE_P(expr)) {
                case IS_ARRAY:
                        ZEND_PUTS("Array (");
-                       if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) &&
-                           ++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) {
-                               ZEND_PUTS(" *RECURSION*");
-                               Z_ARRVAL_P(expr)->u.v.nApplyCount--;
-                               return;
+                       if (Z_REFCOUNTED_P(expr)) {
+                               if (Z_IS_RECURSIVE_P(expr)) {
+                                       ZEND_PUTS(" *RECURSION*");
+                                       return;
+                               }
+                               Z_PROTECT_RECURSION_P(expr);
                        }
                        print_flat_hash(Z_ARRVAL_P(expr));
                        ZEND_PUTS(")");
-                       if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) {
-                               Z_ARRVAL_P(expr)->u.v.nApplyCount--;
+                       if (Z_REFCOUNTED_P(expr)) {
+                               Z_UNPROTECT_RECURSION_P(expr);
                        }
                        break;
                case IS_OBJECT:
@@ -353,7 +354,7 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
                        zend_printf("%s Object (", ZSTR_VAL(class_name));
                        zend_string_release(class_name);
 
-                       if (Z_OBJ_APPLY_COUNT_P(expr) > 0) {
+                       if (Z_IS_RECURSIVE_P(expr)) {
                                ZEND_PUTS(" *RECURSION*");
                                return;
                        }
@@ -362,9 +363,9 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
                                properties = Z_OBJPROP_P(expr);
                        }
                        if (properties) {
-                               Z_OBJ_INC_APPLY_COUNT_P(expr);
+                               Z_PROTECT_RECURSION_P(expr);
                                print_flat_hash(properties);
-                               Z_OBJ_DEC_APPLY_COUNT_P(expr);
+                               Z_UNPROTECT_RECURSION_P(expr);
                        }
                        ZEND_PUTS(")");
                        break;
@@ -384,15 +385,16 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
        switch (Z_TYPE_P(expr)) {
                case IS_ARRAY:
                        smart_str_appends(buf, "Array\n");
-                       if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) &&
-                           ++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) {
-                               smart_str_appends(buf, " *RECURSION*");
-                               Z_ARRVAL_P(expr)->u.v.nApplyCount--;
-                               return;
+                       if (Z_REFCOUNTED_P(expr)) {
+                               if (Z_IS_RECURSIVE_P(expr)) {
+                                       smart_str_appends(buf, " *RECURSION*");
+                                       return;
+                               }
+                               Z_PROTECT_RECURSION_P(expr);
                        }
                        print_hash(buf, Z_ARRVAL_P(expr), indent, 0);
-                       if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) {
-                               Z_ARRVAL_P(expr)->u.v.nApplyCount--;
+                       if (Z_REFCOUNTED_P(expr)) {
+                               Z_UNPROTECT_RECURSION_P(expr);
                        }
                        break;
                case IS_OBJECT:
@@ -405,7 +407,7 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
                                zend_string_release(class_name);
 
                                smart_str_appends(buf, " Object\n");
-                               if (Z_OBJ_APPLY_COUNT_P(expr) > 0) {
+                               if (Z_IS_RECURSIVE_P(expr)) {
                                        smart_str_appends(buf, " *RECURSION*");
                                        return;
                                }
@@ -413,9 +415,9 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
                                        break;
                                }
 
-                               Z_OBJ_INC_APPLY_COUNT_P(expr);
+                               Z_PROTECT_RECURSION_P(expr);
                                print_hash(buf, properties, indent, 1);
-                               Z_OBJ_DEC_APPLY_COUNT_P(expr);
+                               Z_UNPROTECT_RECURSION_P(expr);
 
                                if (is_temp) {
                                        zend_hash_destroy(properties);
index d0f0b7cf6932b530f8a95b7c81971aba4daefb37..73b1b64c827d8a3157b248212b689f11144a6521 100644 (file)
@@ -727,13 +727,13 @@ static int validate_constant_array(HashTable *ht) /* {{{ */
        int ret = 1;
        zval *val;
 
-       ht->u.v.nApplyCount++;
+       GC_PROTECT_RECURSION(ht);
        ZEND_HASH_FOREACH_VAL_IND(ht, val) {
                ZVAL_DEREF(val);
                if (Z_REFCOUNTED_P(val)) {
                        if (Z_TYPE_P(val) == IS_ARRAY) {
                                if (Z_REFCOUNTED_P(val)) {
-                                       if (Z_ARRVAL_P(val)->u.v.nApplyCount > 0) {
+                                       if (Z_IS_RECURSIVE_P(val)) {
                                                zend_error(E_WARNING, "Constants cannot be recursive arrays");
                                                ret = 0;
                                                break;
@@ -749,7 +749,7 @@ static int validate_constant_array(HashTable *ht) /* {{{ */
                        }
                }
        } ZEND_HASH_FOREACH_END();
-       ht->u.v.nApplyCount--;
+       GC_UNPROTECT_RECURSION(ht);
        return ret;
 }
 /* }}} */
@@ -1137,7 +1137,7 @@ ZEND_FUNCTION(get_object_vars)
 
        zobj = Z_OBJ_P(obj);
 
-       if (!zobj->ce->default_properties_count && properties == zobj->properties && !ZEND_HASH_GET_APPLY_COUNT(properties)) {
+       if (!zobj->ce->default_properties_count && properties == zobj->properties && !GC_IS_RECURSIVE(properties)) {
                /* fast copy */
                if (EXPECTED(zobj->handlers == &std_object_handlers)) {
                        RETURN_ARR(zend_proptable_to_symtable(properties, 0));
index 4bf2b746cb031efd79d9c29dccb6662757ff0235..d9ea885a62a7deed50f1c4533006ad6f9f5e5fbd 100644 (file)
@@ -711,9 +711,9 @@ ZEND_METHOD(exception, __toString)
                zend_string_release(file);
                zval_ptr_dtor(&trace);
 
-               Z_OBJPROP_P(exception)->u.v.nApplyCount++;
+               Z_PROTECT_RECURSION_P(exception);
                exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS);
-               if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_OBJPROP_P(exception)->u.v.nApplyCount > 0) {
+               if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_IS_RECURSIVE_P(exception)) {
                        break;
                }
        }
@@ -722,8 +722,8 @@ ZEND_METHOD(exception, __toString)
        exception = getThis();
        /* Reset apply counts */
        while (exception && Z_TYPE_P(exception) == IS_OBJECT && (base_ce = i_get_exception_base(exception)) && instanceof_function(Z_OBJCE_P(exception), base_ce)) {
-               if (Z_OBJPROP_P(exception)->u.v.nApplyCount) {
-                       Z_OBJPROP_P(exception)->u.v.nApplyCount--;
+               if (Z_IS_RECURSIVE_P(exception)) {
+                       Z_UNPROTECT_RECURSION_P(exception);
                } else {
                        break;
                }
index 5f03ce86b04c412716bb35a329bbbdead0013e72..92f8358a0061dad750bf4272ea76ef8abc2138dd 100644 (file)
@@ -72,19 +72,6 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
 #define SET_INCONSISTENT(n)
 #endif
 
-#define HASH_PROTECT_RECURSION(ht)                                                                                                             \
-       if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) {                                                                       \
-               if (((ht)->u.flags & ZEND_HASH_APPLY_COUNT_MASK) >= (3 << ZEND_HASH_APPLY_SHIFT)) {\
-                       zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");\
-               }                                                                                                                                                               \
-               ZEND_HASH_INC_APPLY_COUNT(ht);                                                                                                  \
-       }
-
-#define HASH_UNPROTECT_RECURSION(ht)                                                                                                   \
-       if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) {                                                                       \
-               ZEND_HASH_DEC_APPLY_COUNT(ht);                                                                                                  \
-       }
-
 #define ZEND_HASH_IF_FULL_DO_RESIZE(ht)                                \
        if ((ht)->nNumUsed >= (ht)->nTableSize) {               \
                zend_hash_do_resize(ht);                                        \
@@ -170,11 +157,11 @@ static zend_always_inline void zend_hash_check_init(HashTable *ht, int packed)
 static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
        {HT_INVALID_IDX, HT_INVALID_IDX};
 
-static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
+static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
 {
        GC_REFCOUNT(ht) = 1;
        GC_TYPE_INFO(ht) = IS_ARRAY | (persistent ? 0 : (GC_COLLECTABLE << GC_FLAGS_SHIFT));
-       ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | (bApplyProtection ? HASH_FLAG_APPLY_PROTECTION : 0) | HASH_FLAG_STATIC_KEYS;
+       ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | HASH_FLAG_STATIC_KEYS;
        ht->nTableMask = HT_MIN_MASK;
        HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
        ht->nNumUsed = 0;
@@ -187,13 +174,13 @@ static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize
 
 ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
 {
-       _zend_hash_init_int(ht, nSize, pDestructor, persistent, 1);
+       _zend_hash_init_int(ht, nSize, pDestructor, persistent);
 }
 
 ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize ZEND_FILE_LINE_DC)
 {
        HashTable *ht = emalloc(sizeof(HashTable));
-       _zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, 0, 1);
+       _zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, 0);
        return ht;
 }
 
@@ -245,11 +232,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht)
        pefree(old_data, (ht)->u.flags & HASH_FLAG_PERSISTENT);
 }
 
-ZEND_API void ZEND_FASTCALL _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
-{
-       _zend_hash_init_int(ht, nSize, pDestructor, persistent, bApplyProtection);
-}
-
 ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, zend_bool packed)
 {
        HT_ASSERT_RC1(ht);
@@ -317,15 +299,6 @@ ZEND_API uint32_t zend_array_count(HashTable *ht)
 }
 /* }}} */
 
-ZEND_API void ZEND_FASTCALL zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
-{
-       if (bApplyProtection) {
-               ht->u.flags |= HASH_FLAG_APPLY_PROTECTION;
-       } else {
-               ht->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
-       }
-}
-
 ZEND_API uint32_t ZEND_FASTCALL zend_hash_iterator_add(HashTable *ht, HashPosition pos)
 {
        HashTableIterator *iter = EG(ht_iterators);
@@ -1508,7 +1481,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_fu
 
        IS_CONSISTENT(ht);
 
-       HASH_PROTECT_RECURSION(ht);
        for (idx = 0; idx < ht->nNumUsed; idx++) {
                p = ht->arData + idx;
                if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@@ -1522,7 +1494,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_fu
                        break;
                }
        }
-       HASH_UNPROTECT_RECURSION(ht);
 }
 
 
@@ -1534,7 +1505,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_f
 
        IS_CONSISTENT(ht);
 
-       HASH_PROTECT_RECURSION(ht);
        for (idx = 0; idx < ht->nNumUsed; idx++) {
                p = ht->arData + idx;
                if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@@ -1548,7 +1518,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_f
                        break;
                }
        }
-       HASH_UNPROTECT_RECURSION(ht);
 }
 
 
@@ -1562,8 +1531,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_
 
        IS_CONSISTENT(ht);
 
-       HASH_PROTECT_RECURSION(ht);
-
        for (idx = 0; idx < ht->nNumUsed; idx++) {
                p = ht->arData + idx;
                if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@@ -1583,8 +1550,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_
                }
                va_end(args);
        }
-
-       HASH_UNPROTECT_RECURSION(ht);
 }
 
 
@@ -1596,7 +1561,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
 
        IS_CONSISTENT(ht);
 
-       HASH_PROTECT_RECURSION(ht);
        idx = ht->nNumUsed;
        while (idx > 0) {
                idx--;
@@ -1613,7 +1577,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
                        break;
                }
        }
-       HASH_UNPROTECT_RECURSION(ht);
 }
 
 
@@ -1776,7 +1739,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
        target->pDestructor = source->pDestructor;
 
        if (source->nNumUsed == 0) {
-               target->u.flags = (source->u.flags & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED|HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS;
+               target->u.flags = (source->u.flags & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED|HASH_FLAG_PERSISTENT)) | HASH_FLAG_STATIC_KEYS;
                target->nTableMask = HT_MIN_MASK;
                target->nNumUsed = 0;
                target->nNumOfElements = 0;
@@ -1784,7 +1747,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
                target->nInternalPointer = HT_INVALID_IDX;
                HT_SET_DATA_ADDR(target, &uninitialized_bucket);
        } else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) {
-               target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT) | HASH_FLAG_APPLY_PROTECTION;
+               target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
                target->nTableMask = source->nTableMask;
                target->nNumUsed = source->nNumUsed;
                target->nNumOfElements = source->nNumOfElements;
@@ -1801,7 +1764,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
                        target->nInternalPointer = idx;
                }
        } else if (source->u.flags & HASH_FLAG_PACKED) {
-               target->u.flags = (source->u.flags & ~(HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION;
+               target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
                target->nTableMask = source->nTableMask;
                target->nNumUsed = source->nNumUsed;
                target->nNumOfElements = source->nNumOfElements;
@@ -1824,7 +1787,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
                        target->nInternalPointer = idx;
                }
        } else {
-               target->u.flags = (source->u.flags & ~(HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION;
+               target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
                target->nTableMask = source->nTableMask;
                target->nNextFreeElement = source->nNextFreeElement;
                target->nInternalPointer = source->nInternalPointer;
@@ -2402,11 +2365,28 @@ ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t co
        IS_CONSISTENT(ht1);
        IS_CONSISTENT(ht2);
 
-       HASH_PROTECT_RECURSION(ht1);
-       HASH_PROTECT_RECURSION(ht2);
+       if (ht1 == ht2) {
+               return 0;
+       }
+
+       /* use bitwise OR to make only one conditional jump */
+       if (UNEXPECTED(GC_IS_RECURSIVE(ht1) | GC_IS_RECURSIVE(ht2))) {
+               zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");
+       }
+
+       if (!(GC_FLAGS(ht1) & GC_IMMUTABLE)) {
+               GC_PROTECT_RECURSION(ht1);
+       }
+       if (!(GC_FLAGS(ht2) & GC_IMMUTABLE)) {
+               GC_PROTECT_RECURSION(ht2);
+       }
        result = zend_hash_compare_impl(ht1, ht2, compar, ordered);
-       HASH_UNPROTECT_RECURSION(ht1);
-       HASH_UNPROTECT_RECURSION(ht2);
+       if (!(GC_FLAGS(ht1) & GC_IMMUTABLE)) {
+               GC_UNPROTECT_RECURSION(ht1);
+       }
+       if (!(GC_FLAGS(ht2) & GC_IMMUTABLE)) {
+               GC_UNPROTECT_RECURSION(ht2);
+       }
 
        return result;
 }
index e362b5557b4234a8322934018a41d0b7633f6535..e99224a0b4e51ac61efe8f3169880a0042017c05 100644 (file)
@@ -36,7 +36,6 @@
 #define HASH_ADD_NEXT                  (1<<4)
 
 #define HASH_FLAG_PERSISTENT       (1<<0)
-#define HASH_FLAG_APPLY_PROTECTION (1<<1)
 #define HASH_FLAG_PACKED           (1<<2)
 #define HASH_FLAG_INITIALIZED      (1<<3)
 #define HASH_FLAG_STATIC_KEYS      (1<<4) /* long and interned strings */
@@ -69,14 +68,13 @@ BEGIN_EXTERN_C()
 
 /* startup/shutdown */
 ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent);
-ZEND_API void ZEND_FASTCALL _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection);
 ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht);
 ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht);
 
 #define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent) \
        _zend_hash_init((ht), (nSize), (pDestructor), (persistent))
 #define zend_hash_init_ex(ht, nSize, pHashFunction, pDestructor, persistent, bApplyProtection) \
-       _zend_hash_init_ex((ht), (nSize), (pDestructor), (persistent), (bApplyProtection))
+       _zend_hash_init((ht), (nSize), (pDestructor), (persistent))
 
 ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed);
 ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht);
@@ -985,16 +983,6 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht,
        _key = _p->key; \
        _val = _z;
 
-#define ZEND_HASH_APPLY_PROTECTION(ht) \
-       ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION)
-
-#define ZEND_HASH_APPLY_SHIFT         8
-#define ZEND_HASH_APPLY_COUNT_MASK    0xff00
-#define ZEND_HASH_GET_APPLY_COUNT(ht) (((ht)->u.flags & ZEND_HASH_APPLY_COUNT_MASK) >> ZEND_HASH_APPLY_SHIFT)
-#define ZEND_HASH_INC_APPLY_COUNT(ht) ((ht)->u.flags += (1 << ZEND_HASH_APPLY_SHIFT))
-#define ZEND_HASH_DEC_APPLY_COUNT(ht) ((ht)->u.flags -= (1 << ZEND_HASH_APPLY_SHIFT))
-
-
 /* The following macros are useful to insert a sequence of new elements
  * of packed array. They may be use insted of series of
  * zend_hash_next_index_insert_new()
index a11664ff0e0ecebb9dfb404c28c766c9b806f12a..f1412ef0d240d1dbff578ad82704e158e54ccf24 100644 (file)
 #define IN_UNSET       (1<<2)
 #define IN_ISSET       (1<<3)
 
-#define Z_OBJ_PROTECT_RECURSION(zval_p) \
-       do { \
-               if (Z_OBJ_APPLY_COUNT_P(zval_p) >= 3) { \
-                       zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?"); \
-               } \
-               Z_OBJ_INC_APPLY_COUNT_P(zval_p); \
-       } while (0)
-
-
-#define Z_OBJ_UNPROTECT_RECURSION(zval_p) \
-       Z_OBJ_DEC_APPLY_COUNT_P(zval_p)
-
 /*
   __X accessors explanation:
 
@@ -1444,6 +1432,9 @@ static int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
        zobj1 = Z_OBJ_P(o1);
        zobj2 = Z_OBJ_P(o2);
 
+       if (zobj1 == zobj2) {
+               return 0; /* the same object */
+       }
        if (zobj1->ce != zobj2->ce) {
                return 1; /* different classes */
        }
@@ -1456,40 +1447,45 @@ static int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
                p1 = zobj1->properties_table;
                p2 = zobj2->properties_table;
                end = p1 + zobj1->ce->default_properties_count;
-               Z_OBJ_PROTECT_RECURSION(o1);
-               Z_OBJ_PROTECT_RECURSION(o2);
+
+               /* use bitwise OR to make only one conditional jump */
+               if (UNEXPECTED(Z_IS_RECURSIVE_P(o1) | Z_IS_RECURSIVE_P(o2))) {
+                       zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");
+               }
+               Z_PROTECT_RECURSION_P(o1);
+               Z_PROTECT_RECURSION_P(o2);
                do {
                        if (Z_TYPE_P(p1) != IS_UNDEF) {
                                if (Z_TYPE_P(p2) != IS_UNDEF) {
                                        zval result;
 
                                        if (compare_function(&result, p1, p2)==FAILURE) {
-                                               Z_OBJ_UNPROTECT_RECURSION(o1);
-                                               Z_OBJ_UNPROTECT_RECURSION(o2);
+                                               Z_UNPROTECT_RECURSION_P(o1);
+                                               Z_UNPROTECT_RECURSION_P(o2);
                                                return 1;
                                        }
                                        if (Z_LVAL(result) != 0) {
-                                               Z_OBJ_UNPROTECT_RECURSION(o1);
-                                               Z_OBJ_UNPROTECT_RECURSION(o2);
+                                               Z_UNPROTECT_RECURSION_P(o1);
+                                               Z_UNPROTECT_RECURSION_P(o2);
                                                return Z_LVAL(result);
                                        }
                                } else {
-                                       Z_OBJ_UNPROTECT_RECURSION(o1);
-                                       Z_OBJ_UNPROTECT_RECURSION(o2);
+                                       Z_UNPROTECT_RECURSION_P(o1);
+                                       Z_UNPROTECT_RECURSION_P(o2);
                                        return 1;
                                }
                        } else {
                                if (Z_TYPE_P(p2) != IS_UNDEF) {
-                                       Z_OBJ_UNPROTECT_RECURSION(o1);
-                                       Z_OBJ_UNPROTECT_RECURSION(o2);
+                                       Z_UNPROTECT_RECURSION_P(o1);
+                                       Z_UNPROTECT_RECURSION_P(o2);
                                        return 1;
                                }
                        }
                        p1++;
                        p2++;
                } while (p1 != end);
-               Z_OBJ_UNPROTECT_RECURSION(o1);
-               Z_OBJ_UNPROTECT_RECURSION(o2);
+               Z_UNPROTECT_RECURSION_P(o1);
+               Z_UNPROTECT_RECURSION_P(o2);
                return 0;
        } else {
                if (!zobj1->properties) {
index 781a1825e97cfc1ae3e8b8c722e7a5b30747a95b..8a20d1ca28c69dc3ee18839e487c74493c93487b 100644 (file)
@@ -607,7 +607,7 @@ try_again:
                                                /* fast copy */
                                                if (!Z_OBJCE_P(op)->default_properties_count &&
                                                        obj_ht == Z_OBJ_P(op)->properties &&
-                                                       !ZEND_HASH_GET_APPLY_COUNT(Z_OBJ_P(op)->properties) &&
+                                                       !GC_IS_RECURSIVE(obj_ht) &&
                                                        EXPECTED(Z_OBJ_P(op)->handlers == &std_object_handlers)) {
                                                        arr = zend_proptable_to_symtable(obj_ht, 0);
                                                } else {
index 66af862884521f76999bb90cf416dd2ceca6b83e..931236936d3a3993a24cfd35d3516de807f41dea 100644 (file)
@@ -69,16 +69,6 @@ ZEND_API void _zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pD
        _zend_hash_init(TS_HASH(ht), nSize, pDestructor, persistent);
 }
 
-ZEND_API void _zend_ts_hash_init_ex(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
-{
-#ifdef ZTS
-       ht->mx_reader = tsrm_mutex_alloc();
-       ht->mx_writer = tsrm_mutex_alloc();
-       ht->reader = 0;
-#endif
-       _zend_hash_init_ex(TS_HASH(ht), nSize, pDestructor, persistent, bApplyProtection);
-}
-
 ZEND_API void zend_ts_hash_destroy(TsHashTable *ht)
 {
        begin_write(ht);
index 3128096cd3832d97bace11e73eddd5a03c07273b..20e66d3890b57a565ad6ab32bee49a62a3cbbe2f 100644 (file)
@@ -38,14 +38,13 @@ BEGIN_EXTERN_C()
 
 /* startup/shutdown */
 ZEND_API void _zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent);
-ZEND_API void _zend_ts_hash_init_ex(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection);
 ZEND_API void zend_ts_hash_destroy(TsHashTable *ht);
 ZEND_API void zend_ts_hash_clean(TsHashTable *ht);
 
 #define zend_ts_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)   \
        _zend_ts_hash_init(ht, nSize, pDestructor, persistent)
 #define zend_ts_hash_init_ex(ht, nSize, pHashFunction, pDestructor, persistent, bApplyProtection)      \
-       _zend_ts_hash_init_ex(ht, nSize, pDestructor, persistent, bApplyProtection)
+       _zend_ts_hash_init(ht, nSize, pDestructor, persistent)
 
 
 /* additions/updates/changes */
index 4118154289d44056b64f43a912a3273157769edd..d7381ab2d7d557a8a01225883aedebe7ea9f3324 100644 (file)
@@ -239,7 +239,7 @@ struct _zend_array {
                struct {
                        ZEND_ENDIAN_LOHI_4(
                                zend_uchar    flags,
-                               zend_uchar    nApplyCount,
+                               zend_uchar    _unused,
                                zend_uchar    nIteratorsCount,
                                zend_uchar    consistency)
                } v;
@@ -455,7 +455,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
 #define GC_INFO_SHIFT                          16
 #define GC_INFO_MASK                           0xffff0000
 
-/* zval.value->gc.u.v.flags */
+/* zval.value->gc.u.v.flags (common flags) */
+#define GC_PROTECTED                (1<<0) /* used for array/object recursion detection */
+#define GC_IMMUTABLE                (1<<1) /* string/array can't be canged in place */
 #define GC_COLLECTABLE                         (1<<7)
 
 #define GC_ARRAY                                       (IS_ARRAY          | (GC_COLLECTABLE << GC_FLAGS_SHIFT))
@@ -490,37 +492,38 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
 
 /* string flags (zval.value->gc.u.flags) */
 #define IS_STR_PERSISTENT                      (1<<0) /* allocated using malloc   */
-#define IS_STR_INTERNED                                (1<<1) /* interned string          */
+#define IS_STR_INTERNED                                GC_IMMUTABLE /* interned string */
 #define IS_STR_PERMANENT               (1<<2) /* relives request boundary */
 
 /* array flags */
-#define IS_ARRAY_IMMUTABLE                     (1<<1)
+#define IS_ARRAY_PROTECTED                     GC_PROTECTED
+#define IS_ARRAY_IMMUTABLE                     GC_IMMUTABLE
 
 /* object flags (zval.value->gc.u.flags) */
-#define IS_OBJ_APPLY_COUNT                     0x07
+#define IS_OBJ_PROTECTED                       GC_PROTECTED
 #define IS_OBJ_DESTRUCTOR_CALLED       (1<<3)
 #define IS_OBJ_FREE_CALLED                     (1<<4)
 #define IS_OBJ_USE_GUARDS           (1<<5)
 #define IS_OBJ_HAS_GUARDS           (1<<6)
 
-#define Z_OBJ_APPLY_COUNT(zval) \
-       (Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT)
+/* Recursion protection macros must be used only for arrays and objects */
+#define GC_IS_RECURSIVE(p) \
+       (GC_FLAGS(p) & GC_PROTECTED)
 
-#define Z_OBJ_INC_APPLY_COUNT(zval) do { \
-               Z_GC_FLAGS(zval) = \
-                       (Z_GC_FLAGS(zval) & ~IS_OBJ_APPLY_COUNT) | \
-                       ((Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT) + 1); \
+#define GC_PROTECT_RECURSION(p) do { \
+               GC_FLAGS(p) |= GC_PROTECTED; \
        } while (0)
 
-#define Z_OBJ_DEC_APPLY_COUNT(zval) do { \
-               Z_GC_FLAGS(zval) = \
-                       (Z_GC_FLAGS(zval) & ~IS_OBJ_APPLY_COUNT) | \
-                       ((Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT) - 1); \
+#define GC_UNPROTECT_RECURSION(p) do { \
+               GC_FLAGS(p) &= ~GC_PROTECTED; \
        } while (0)
 
-#define Z_OBJ_APPLY_COUNT_P(zv)     Z_OBJ_APPLY_COUNT(*(zv))
-#define Z_OBJ_INC_APPLY_COUNT_P(zv) Z_OBJ_INC_APPLY_COUNT(*(zv))
-#define Z_OBJ_DEC_APPLY_COUNT_P(zv) Z_OBJ_DEC_APPLY_COUNT(*(zv))
+#define Z_IS_RECURSIVE(zval)        GC_IS_RECURSIVE(Z_COUNTED(zval))
+#define Z_PROTECT_RECURSION(zval)   GC_PROTECT_RECURSION(Z_COUNTED(zval))
+#define Z_UNPROTECT_RECURSION(zval) GC_UNPROTECT_RECURSION(Z_COUNTED(zval))
+#define Z_IS_RECURSIVE_P(zv)        Z_IS_RECURSIVE(*(zv))
+#define Z_PROTECT_RECURSION_P(zv)   Z_PROTECT_RECURSION(*(zv))
+#define Z_UNPROTECT_RECURSION_P(zv) Z_UNPROTECT_RECURSION(*(zv))
 
 /* All data types < IS_STRING have their constructor/destructors skipped */
 #define Z_CONSTANT(zval)                       ((Z_TYPE_FLAGS(zval) & IS_TYPE_CONSTANT) != 0)
index cdc5e15bb616f10210b94ecd8789e66c927a375f..17fc500a9d8d3d63d6cf08f62dc488b508c29fb0 100644 (file)
@@ -503,21 +503,21 @@ static void php_zval_filter_recursive(zval *value, zend_long filter, zend_long f
        if (Z_TYPE_P(value) == IS_ARRAY) {
                zval *element;
 
-               if (Z_ARRVAL_P(value)->u.v.nApplyCount > 1) {
+               if (Z_IS_RECURSIVE_P(value)) {
                        return;
                }
+               Z_PROTECT_RECURSION_P(value);
 
                ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(value), element) {
                        ZVAL_DEREF(element);
                        SEPARATE_ZVAL_NOREF(element);
                        if (Z_TYPE_P(element) == IS_ARRAY) {
-                               Z_ARRVAL_P(element)->u.v.nApplyCount++;
                                php_zval_filter_recursive(element, filter, flags, options, charset, copy);
-                               Z_ARRVAL_P(element)->u.v.nApplyCount--;
                        } else {
                                php_zval_filter(element, filter, flags, options, charset, copy);
                        }
                } ZEND_HASH_FOREACH_END();
+               Z_UNPROTECT_RECURSION_P(value);
        } else {
                php_zval_filter(value, filter, flags, options, charset, copy);
        }
index 1fa234420416ac04824c1aef9a9e895b9eb257e3..2c3843fdd2adc5a6d2cf4faf1521540d90df3ea9 100644 (file)
@@ -113,17 +113,17 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options)
 }
 /* }}} */
 
-#define PHP_JSON_HASH_APPLY_PROTECTION_INC(_tmp_ht) \
+#define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \
        do { \
-               if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
-                       ZEND_HASH_INC_APPLY_COUNT(_tmp_ht); \
+               if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \
+                       GC_PROTECT_RECURSION(_tmp_ht); \
                } \
        } while (0)
 
-#define PHP_JSON_HASH_APPLY_PROTECTION_DEC(_tmp_ht) \
+#define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \
        do { \
-               if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
-                       ZEND_HASH_DEC_APPLY_COUNT(_tmp_ht); \
+               if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \
+                       GC_UNPROTECT_RECURSION(_tmp_ht); \
                } \
        } while (0)
 
@@ -140,13 +140,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
                r = PHP_JSON_OUTPUT_OBJECT;
        }
 
-       if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) {
+       if (myht && GC_IS_RECURSIVE(myht)) {
                encoder->error_code = PHP_JSON_ERROR_RECURSION;
                smart_str_appendl(buf, "null", 4);
                return FAILURE;
        }
 
-       PHP_JSON_HASH_APPLY_PROTECTION_INC(myht);
+       PHP_JSON_HASH_PROTECT_RECURSION(myht);
 
        if (r == PHP_JSON_OUTPUT_ARRAY) {
                smart_str_appendc(buf, '[');
@@ -212,13 +212,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
 
                        if (php_json_encode_zval(buf, data, options, encoder) == FAILURE &&
                                        !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
-                               PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+                               PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
                                return FAILURE;
                        }
                } ZEND_HASH_FOREACH_END();
        }
 
-       PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+       PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
 
        if (encoder->depth > encoder->max_depth) {
                encoder->error_code = PHP_JSON_ERROR_DEPTH;
@@ -445,7 +445,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
        zval retval, fname;
        int return_code;
 
-       if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) {
+       if (myht && GC_IS_RECURSIVE(myht)) {
                encoder->error_code = PHP_JSON_ERROR_RECURSION;
                if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
                        smart_str_appendl(buf, "null", 4);
@@ -453,7 +453,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
                return FAILURE;
        }
 
-       PHP_JSON_HASH_APPLY_PROTECTION_INC(myht);
+       PHP_JSON_HASH_PROTECT_RECURSION(myht);
 
        ZVAL_STRING(&fname, "jsonSerialize");
 
@@ -466,7 +466,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
                if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
                        smart_str_appendl(buf, "null", 4);
                }
-               PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+               PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
                return FAILURE;
        }
 
@@ -478,19 +478,19 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
                if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
                        smart_str_appendl(buf, "null", 4);
                }
-               PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+               PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
                return FAILURE;
        }
 
        if ((Z_TYPE(retval) == IS_OBJECT) &&
                (Z_OBJ(retval) == Z_OBJ_P(val))) {
                /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
-               PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+               PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
                return_code = php_json_encode_array(buf, &retval, options, encoder);
        } else {
                /* All other types, encode as normal */
                return_code = php_json_encode_zval(buf, &retval, options, encoder);
-               PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+               PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
        }
 
        zval_ptr_dtor(&retval);
index 0bb18e9b574101d03c1288e3422dcb90c7e5a878..6d1103aff7d6f3e9332cad3ae89a11ea8481d38c 100644 (file)
@@ -3129,11 +3129,12 @@ MBSTRING_API HashTable *php_mb_convert_encoding_recursive(HashTable *input, cons
                return NULL;
        }
 
-       if (input->u.v.nApplyCount++ > 1) {
-               input->u.v.nApplyCount--;
+       if (GC_IS_RECURSIVE(input)) {
+               GC_UNPROTECT_RECURSION(input);
                php_error_docref(NULL, E_WARNING, "Cannot convert recursively referenced values");
                return NULL;
        }
+       GC_PROTECT_RECURSION(input);
        output = zend_new_array(zend_hash_num_elements(input));
        ZEND_HASH_FOREACH_KEY_VAL(input, idx, key, entry) {
                /* convert key */
@@ -3177,7 +3178,7 @@ MBSTRING_API HashTable *php_mb_convert_encoding_recursive(HashTable *input, cons
                        zend_hash_index_add(output, idx, &entry_tmp);
                }
        } ZEND_HASH_FOREACH_END();
-       input->u.v.nApplyCount--;
+       GC_UNPROTECT_RECURSION(input);
 
        return output;
 }
@@ -3764,11 +3765,11 @@ PHP_FUNCTION(mb_convert_variables)
                                        if (target_hash != NULL) {
                                                while ((hash_entry = zend_hash_get_current_data(target_hash)) != NULL) {
                                                        if (Z_REFCOUNTED_P(var)) {
-                                                               if (++target_hash->u.v.nApplyCount > 1) {
-                                                                       --target_hash->u.v.nApplyCount;
+                                                               if (GC_IS_RECURSIVE(target_hash)) {
                                                                        recursion_error = 1;
                                                                        goto detect_end;
                                                                }
+                                                               GC_PROTECT_RECURSION(target_hash);
                                                        }
                                                        zend_hash_move_forward(target_hash);
                                                        if (Z_TYPE_P(hash_entry) == IS_INDIRECT) {
@@ -3813,9 +3814,7 @@ detect_end:
                if (recursion_error) {
                        while(stack_level-- && (var = &stack[stack_level])) {
                                if (Z_REFCOUNTED_P(var)) {
-                                       if (HASH_OF(var)->u.v.nApplyCount > 1) {
-                                               HASH_OF(var)->u.v.nApplyCount--;
-                                       }
+                                       Z_UNPROTECT_RECURSION_P(var);
                                }
                        }
                        efree(stack);
@@ -3880,11 +3879,11 @@ detect_end:
                                                ZVAL_DEREF(hash_entry);
                                                if (Z_TYPE_P(hash_entry) == IS_ARRAY || Z_TYPE_P(hash_entry) == IS_OBJECT) {
                                                        if (Z_REFCOUNTED_P(hash_entry)) {
-                                                               if (++(HASH_OF(hash_entry)->u.v.nApplyCount) > 1) {
-                                                                       --(HASH_OF(hash_entry)->u.v.nApplyCount);
+                                                               if (Z_IS_RECURSIVE_P(hash_entry)) {
                                                                        recursion_error = 1;
                                                                        goto conv_end;
                                                                }
+                                                               Z_PROTECT_RECURSION_P(hash_entry);
                                                        }
                                                        if (stack_level >= stack_max) {
                                                                stack_max += PHP_MBSTR_STACK_BLOCK_SIZE;
@@ -3933,9 +3932,7 @@ conv_end:
                if (recursion_error) {
                        while(stack_level-- && (var = &stack[stack_level])) {
                                if (Z_REFCOUNTED_P(var)) {
-                                       if (HASH_OF(var)->u.v.nApplyCount > 1) {
-                                               HASH_OF(var)->u.v.nApplyCount--;
-                                       }
+                                       Z_UNPROTECT_RECURSION_P(var);
                                }
                        }
                        efree(stack);
@@ -4787,12 +4784,12 @@ MBSTRING_API int php_mb_check_encoding_recursive(HashTable *vars, const zend_str
                return 0;
        }
 
-       if (vars->u.v.nApplyCount++ > 1) {
-               vars->u.v.nApplyCount--;
+       if (GC_IS_RECURSIVE(vars)) {
                mbfl_buffer_converter_delete(convd);
                php_error_docref(NULL, E_WARNING, "Cannot not handle circular references");
                return 0;
        }
+       GC_PROTECT_RECURSION(vars);
        ZEND_HASH_FOREACH_KEY_VAL(vars, idx, key, entry) {
                ZVAL_DEREF(entry);
                if (key) {
@@ -4826,7 +4823,7 @@ MBSTRING_API int php_mb_check_encoding_recursive(HashTable *vars, const zend_str
                                break;
                }
        } ZEND_HASH_FOREACH_END();
-       vars->u.v.nApplyCount--;
+       GC_UNPROTECT_RECURSION(vars);
        mbfl_buffer_converter_delete(convd);
        return valid;
 }
index e982aa2e019ce3385d4b324794e9041cd2663d1e..c33bb67c49e20b009371244b0c2adc33aad19e72 100644 (file)
@@ -49,5 +49,16 @@ array(5) {
   [3]=>
   string(21) "日本語テキスト"
   [4]=>
-  *RECURSION*
-}
\ No newline at end of file
+  &array(5) {
+    [0]=>
+    string(21) "日本語テキスト"
+    [1]=>
+    string(21) "日本語テキスト"
+    [2]=>
+    string(21) "日本語テキスト"
+    [3]=>
+    string(21) "日本語テキスト"
+    [4]=>
+    *RECURSION*
+  }
+}
index 930bcceaf2e2facf947f623fbd8430137dadc136..6926798ab6e30460613cdfecf5e7e2decb9ad61c 100644 (file)
@@ -181,7 +181,7 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source)
        ht->nNumOfElements = source->nNumOfElements;
        ht->nNextFreeElement = source->nNextFreeElement;
        ht->pDestructor = ZVAL_PTR_DTOR;
-       ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED) | HASH_FLAG_APPLY_PROTECTION;
+       ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED);
        ht->nInternalPointer = source->nNumOfElements ? 0 : HT_INVALID_IDX;
 
        if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
@@ -378,7 +378,6 @@ static void zend_class_copy_ctor(zend_class_entry **pce)
 
        /* constants table */
        zend_hash_clone_constants(&ce->constants_table, &old_ce->constants_table);
-       ce->constants_table.u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
 
        /* interfaces aren't really implemented, so we create a new table */
        if (ce->num_interfaces) {
index 2ce4c05ba3a14a548b0bfa53524028b102464d50..cd4077279fef6bce5f7de2755e7791d41a7e6a81 100644 (file)
@@ -305,7 +305,6 @@ static void zend_persist_zval(zval *z)
                                        GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
                                        GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
                                        Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS;
-                                       Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
                                }
                        }
                        break;
@@ -374,7 +373,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
                        GC_REFCOUNT(op_array->static_variables) = 2;
                        GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8);
                        op_array->static_variables->u.flags |= HASH_FLAG_STATIC_KEYS;
-                       op_array->static_variables->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
                }
        }
 
index a88d14fb1a09a213a658a7033dd8d9361d24af93..6d69d81c338430798ce9c1c718c30e41c17e96f1 100644 (file)
@@ -453,7 +453,7 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml
        } else {
                if (check_class_map && SOAP_GLOBAL(class_map) && data &&
                    Z_TYPE_P(data) == IS_OBJECT &&
-                   !ZEND_HASH_GET_APPLY_COUNT(Z_OBJPROP_P(data))) {
+                   !GC_IS_RECURSIVE(Z_OBJPROP_P(data))) {
                        zend_class_entry *ce = Z_OBJCE_P(data);
                        zval *tmp;
                        zend_string *type_name;
@@ -1859,9 +1859,9 @@ static xmlNodePtr to_xml_object(encodeTypePtr type, zval *data, int style, xmlNo
                            sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_LIST &&
                            sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_UNION) {
 
-                               if (prop) ZEND_HASH_INC_APPLY_COUNT(prop);
+                               if (prop) {GC_PROTECT_RECURSION(prop);}
                                xmlParam = master_to_xml(sdlType->encode, data, style, parent);
-                               if (prop) ZEND_HASH_DEC_APPLY_COUNT(prop);
+                               if (prop) {GC_UNPROTECT_RECURSION(prop);}
                        } else {
                                zval rv;
                                zval *tmp = get_zval_property(data, "_", &rv);
index c6d366515b3b98ecca612939bf29b7f638996cdf..e9dc418b53d83fdc63296e55c0adc11506eb7dd2 100644 (file)
@@ -569,12 +569,13 @@ SPL_METHOD(SplObjectStorage, count)
        }
 
        if (mode == COUNT_RECURSIVE) {
-               zend_long ret = zend_hash_num_elements(&intern->storage);
-               zval *element;
+               zend_long ret;
 
-               ZEND_HASH_FOREACH_VAL(&intern->storage, element) {
-                       ret += php_count_recursive(element, mode);
-               } ZEND_HASH_FOREACH_END();
+               if (mode != COUNT_RECURSIVE) {
+                       ret = zend_hash_num_elements(&intern->storage);
+               } else {
+                       ret = php_count_recursive(&intern->storage);
+               }
 
                RETURN_LONG(ret);
                return;
index e55924c66b39fdcec5234aa26be1aba23e48e4ca..18a8a2e77fb99ae6549aff4b94354a88be877d3d 100644 (file)
@@ -743,30 +743,29 @@ PHP_FUNCTION(ksort)
 }
 /* }}} */
 
-PHPAPI zend_long php_count_recursive(zval *array, zend_long mode) /* {{{ */
+PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */
 {
        zend_long cnt = 0;
        zval *element;
 
-       if (Z_TYPE_P(array) == IS_ARRAY) {
-               if (Z_ARRVAL_P(array)->u.v.nApplyCount > 1) {
+       if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+               if (GC_IS_RECURSIVE(ht)) {
                        php_error_docref(NULL, E_WARNING, "recursion detected");
                        return 0;
                }
+               GC_PROTECT_RECURSION(ht);
+       }
 
-               cnt = zend_array_count(Z_ARRVAL_P(array));
-               if (mode == COUNT_RECURSIVE) {
-                   if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) {
-                               Z_ARRVAL_P(array)->u.v.nApplyCount++;
-                       }
-                       ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) {
-                               ZVAL_DEREF(element);
-                               cnt += php_count_recursive(element, COUNT_RECURSIVE);
-                       } ZEND_HASH_FOREACH_END();
-                   if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) {
-                               Z_ARRVAL_P(array)->u.v.nApplyCount--;
-                       }
+       cnt = zend_array_count(ht);
+       ZEND_HASH_FOREACH_VAL(ht, element) {
+               ZVAL_DEREF(element);
+               if (Z_TYPE_P(element) == IS_ARRAY) {
+                       cnt += php_count_recursive(Z_ARRVAL_P(element));
                }
+       } ZEND_HASH_FOREACH_END();
+
+       if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+               GC_UNPROTECT_RECURSION(ht);
        }
 
        return cnt;
@@ -794,12 +793,10 @@ PHP_FUNCTION(count)
                        RETURN_LONG(0);
                        break;
                case IS_ARRAY:
-                       cnt = zend_array_count(Z_ARRVAL_P(array));
-                       if (mode == COUNT_RECURSIVE) {
-                               ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) {
-                                       ZVAL_DEREF(element);
-                                       cnt += php_count_recursive(element, COUNT_RECURSIVE);
-                               } ZEND_HASH_FOREACH_END();
+                       if (mode != COUNT_RECURSIVE) {
+                               cnt = zend_array_count(Z_ARRVAL_P(array));
+                       } else {
+                               cnt = php_count_recursive(Z_ARRVAL_P(array));
                        }
                        RETURN_LONG(cnt);
                        break;
@@ -1431,7 +1428,7 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
                        ZVAL_DEREF(zv);
                        SEPARATE_ARRAY(zv);
                        thash = Z_ARRVAL_P(zv);
-                       if (thash->u.v.nApplyCount > 1) {
+                       if (GC_IS_RECURSIVE(thash)) {
                                php_error_docref(NULL, E_WARNING, "recursion detected");
                                result = FAILURE;
                                break;
@@ -1442,12 +1439,12 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
                        orig_array_walk_fci_cache = BG(array_walk_fci_cache);
 
                        Z_ADDREF(ref);
-                       thash->u.v.nApplyCount++;
+                       GC_PROTECT_RECURSION(thash);
                        result = php_array_walk(zv, userdata, recursive);
                        if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
                                /* If the hashtable changed in the meantime, we'll "leak" this apply count
                                 * increment -- our reference to thash is no longer valid. */
-                               thash->u.v.nApplyCount--;
+                               GC_UNPROTECT_RECURSION(thash);
                        }
                        zval_ptr_dtor(&ref);
 
@@ -2577,19 +2574,18 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu
                        }
                }
        } else if (Z_TYPE_P(entry) == IS_ARRAY) {
-               if ((Z_ARRVAL_P(entry)->u.v.nApplyCount > 1)) {
-                       php_error_docref(NULL, E_WARNING, "recursion detected");
-                       return;
-               }
-
-           if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) {
-                       Z_ARRVAL_P(entry)->u.v.nApplyCount++;
+           if (Z_REFCOUNTED_P(entry)) {
+                       if (Z_IS_RECURSIVE_P(entry)) {
+                               php_error_docref(NULL, E_WARNING, "recursion detected");
+                               return;
+                       }
+                       Z_PROTECT_RECURSION_P(entry);
                }
                ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) {
                        php_compact_var(eg_active_symbol_table, return_value, value_ptr);
                } ZEND_HASH_FOREACH_END();
-           if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) {
-                       Z_ARRVAL_P(entry)->u.v.nApplyCount--;
+           if (Z_REFCOUNTED_P(entry)) {
+                       Z_UNPROTECT_RECURSION_P(entry);
                }
        }
 }
@@ -3636,7 +3632,7 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
                                ZVAL_DEREF(src_zval);
                                ZVAL_DEREF(dest_zval);
                                thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
-                               if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
+                               if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
                                        php_error_docref(NULL, E_WARNING, "recursion detected");
                                        return 0;
                                }
@@ -3662,12 +3658,12 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
                                        src_zval = &tmp;
                                }
                                if (Z_TYPE_P(src_zval) == IS_ARRAY) {
-                                       if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
-                                               thash->u.v.nApplyCount++;
+                                       if (thash && !(GC_FLAGS(thash) & GC_IMMUTABLE)) {
+                                               GC_PROTECT_RECURSION(thash);
                                        }
                                        ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
-                                       if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) {
-                                               thash->u.v.nApplyCount--;
+                                       if (thash && !(GC_FLAGS(thash) & GC_IMMUTABLE)) {
+                                               GC_UNPROTECT_RECURSION(thash);
                                        }
                                        if (!ret) {
                                                return 0;
@@ -3761,8 +3757,8 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ *
 
                dest_zval = dest_entry;
                ZVAL_DEREF(dest_zval);
-               if (Z_ARRVAL_P(dest_zval)->u.v.nApplyCount > 1 ||
-                   Z_ARRVAL_P(src_zval)->u.v.nApplyCount > 1 ||
+               if (Z_IS_RECURSIVE_P(dest_zval) ||
+                   Z_IS_RECURSIVE_P(src_zval) ||
                    (Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
                        php_error_docref(NULL, E_WARNING, "recursion detected");
                        return 0;
@@ -3772,20 +3768,20 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ *
                SEPARATE_ZVAL(dest_entry);
                dest_zval = dest_entry;
 
-               if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
-                       Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++;
+               if (Z_REFCOUNTED_P(dest_zval)) {
+                       Z_PROTECT_RECURSION_P(dest_zval);
                }
-               if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
-                       Z_ARRVAL_P(src_zval)->u.v.nApplyCount++;
+               if (Z_REFCOUNTED_P(src_zval)) {
+                       Z_PROTECT_RECURSION_P(src_zval);
                }
 
                ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
 
-               if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) {
-                       Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--;
+               if (Z_REFCOUNTED_P(dest_zval)) {
+                       Z_UNPROTECT_RECURSION_P(dest_zval);
                }
-               if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) {
-                       Z_ARRVAL_P(src_zval)->u.v.nApplyCount--;
+               if (Z_REFCOUNTED_P(src_zval)) {
+                       Z_UNPROTECT_RECURSION_P(src_zval);
                }
 
                if (!ret) {
index e270342c7bb263b98bdec8dc8a4f2313c666f37b..afbb77d1fa5b542b2f03dff0af3faabaaad9dbcb 100644 (file)
@@ -42,7 +42,7 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
                return FAILURE;
        }
 
-       if (ht->u.v.nApplyCount > 0) {
+       if (GC_IS_RECURSIVE(ht)) {
                /* Prevent recursion */
                return SUCCESS;
        }
@@ -136,12 +136,12 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
                                *(p++) = 'B';
                                *p = '\0';
                        }
-                       if (ZEND_HASH_APPLY_PROTECTION(ht)) {
-                               ht->u.v.nApplyCount++;
+                       if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+                               GC_PROTECT_RECURSION(ht);
                        }
                        php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, newprefix, newprefix_len, "%5D", 3, (Z_TYPE_P(zdata) == IS_OBJECT ? zdata : NULL), arg_sep, enc_type);
-                       if (ZEND_HASH_APPLY_PROTECTION(ht)) {
-                               ht->u.v.nApplyCount--;
+                       if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+                               GC_UNPROTECT_RECURSION(ht);
                        }
                        efree(newprefix);
                } else if (Z_TYPE_P(zdata) == IS_NULL || Z_TYPE_P(zdata) == IS_RESOURCE) {
index 2b58db71e33abae16d8a822b582291f0b371be08..d9c5eedc189581e2ea8eac6207af53ff7496fef0 100644 (file)
@@ -107,7 +107,7 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src);
 PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src);
 PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src);
 PHPAPI int php_multisort_compare(const void *a, const void *b);
-PHPAPI zend_long php_count_recursive(zval *array, zend_long mode);
+PHPAPI zend_long php_count_recursive(HashTable *ht);
 
 #define PHP_SORT_REGULAR            0
 #define PHP_SORT_NUMERIC            1
index ea481328573f845f455e7ae8f37c4395d0c1447a..d2b9fc45df0c70a87276842fd571ee501fa4aafb 100644 (file)
@@ -36,10 +36,6 @@ array(1) {
 
 Warning: compact(): recursion detected in %s on line %d
 
-Warning: compact(): recursion detected in %s on line %d
-
-Warning: compact(): recursion detected in %s on line %d
-
 Warning: compact(): recursion detected in %s on line %d
 array(2) {
   ["a"]=>
index e11b4c2449c1f888113f626ac5055314f8af3527..9c89bcd16bf5ccbb784ee5da80f6fee8bd8ab17c 100644 (file)
@@ -35,5 +35,5 @@ int(4)
 -- $mode = 1: --
 
 Warning: count(): recursion detected in %s on line %d
-int(12)
+int(4)
 Done
index c06dff556db7e5410b4ec1953ec806722f7d8fb1..e2c84dae9b8de2f431dbed83cba30446ee2435c4 100644 (file)
@@ -135,25 +135,7 @@ object(object_class)#%d (6) refcount(%d){
     int(3)
   }
   ["object_class1"]=>
-  object(object_class)#%d (6) refcount(%d){
-    ["value1"]=>
-    int(5)
-    ["value2":"object_class":private]=>
-    int(10)
-    ["value3":protected]=>
-    int(20)
-    ["value4"]=>
-    int(30)
-    ["array_var"]=>
-    array(2) refcount(%d){
-      ["key1"]=>
-      int(1)
-      ["key2 "]=>
-      int(3)
-    }
-    ["object_class1"]=>
-    *RECURSION*
-  }
+  *RECURSION*
 }
 -- Iteration 2 --
 object(no_member_class)#%d (0) refcount(%d){
@@ -184,25 +166,7 @@ object(contains_object_class)#%d (9) refcount(%d){
       int(3)
     }
     ["object_class1"]=>
-    object(object_class)#%d (6) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      *RECURSION*
-    }
+    *RECURSION*
   }
   ["class_object2"]=>
   object(object_class)#%d (6) refcount(%d){
@@ -222,25 +186,7 @@ object(contains_object_class)#%d (9) refcount(%d){
       int(3)
     }
     ["object_class1"]=>
-    object(object_class)#%d (6) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      *RECURSION*
-    }
+    *RECURSION*
   }
   ["class_object3":"contains_object_class":private]=>
   object(object_class)#%d (6) refcount(%d){
@@ -260,25 +206,7 @@ object(contains_object_class)#%d (9) refcount(%d){
       int(3)
     }
     ["object_class1"]=>
-    object(object_class)#%d (6) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      *RECURSION*
-    }
+    *RECURSION*
   }
   ["class_object4":protected]=>
   object(object_class)#%d (6) refcount(%d){
@@ -298,195 +226,13 @@ object(contains_object_class)#%d (9) refcount(%d){
       int(3)
     }
     ["object_class1"]=>
-    object(object_class)#%d (6) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      *RECURSION*
-    }
+    *RECURSION*
   }
   ["no_member_class_object"]=>
   object(no_member_class)#%d (0) refcount(%d){
   }
   ["class_object5"]=>
-  object(contains_object_class)#%d (9) refcount(%d){
-    ["p"]=>
-    int(30)
-    ["p1":protected]=>
-    int(40)
-    ["p2":"contains_object_class":private]=>
-    int(50)
-    ["class_object1"]=>
-    object(object_class)#%d (6) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      object(object_class)#%d (6) refcount(%d){
-        ["value1"]=>
-        int(5)
-        ["value2":"object_class":private]=>
-        int(10)
-        ["value3":protected]=>
-        int(20)
-        ["value4"]=>
-        int(30)
-        ["array_var"]=>
-        array(2) refcount(%d){
-          ["key1"]=>
-          int(1)
-          ["key2 "]=>
-          int(3)
-        }
-        ["object_class1"]=>
-        *RECURSION*
-      }
-    }
-    ["class_object2"]=>
-    object(object_class)#%d (6) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      object(object_class)#%d (6) refcount(%d){
-        ["value1"]=>
-        int(5)
-        ["value2":"object_class":private]=>
-        int(10)
-        ["value3":protected]=>
-        int(20)
-        ["value4"]=>
-        int(30)
-        ["array_var"]=>
-        array(2) refcount(%d){
-          ["key1"]=>
-          int(1)
-          ["key2 "]=>
-          int(3)
-        }
-        ["object_class1"]=>
-        *RECURSION*
-      }
-    }
-    ["class_object3":"contains_object_class":private]=>
-    object(object_class)#%d (6) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      object(object_class)#%d (6) refcount(%d){
-        ["value1"]=>
-        int(5)
-        ["value2":"object_class":private]=>
-        int(10)
-        ["value3":protected]=>
-        int(20)
-        ["value4"]=>
-        int(30)
-        ["array_var"]=>
-        array(2) refcount(%d){
-          ["key1"]=>
-          int(1)
-          ["key2 "]=>
-          int(3)
-        }
-        ["object_class1"]=>
-        *RECURSION*
-      }
-    }
-    ["class_object4":protected]=>
-    object(object_class)#%d (6) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      object(object_class)#%d (6) refcount(%d){
-        ["value1"]=>
-        int(5)
-        ["value2":"object_class":private]=>
-        int(10)
-        ["value3":protected]=>
-        int(20)
-        ["value4"]=>
-        int(30)
-        ["array_var"]=>
-        array(2) refcount(%d){
-          ["key1"]=>
-          int(1)
-          ["key2 "]=>
-          int(3)
-        }
-        ["object_class1"]=>
-        *RECURSION*
-      }
-    }
-    ["no_member_class_object"]=>
-    object(no_member_class)#%d (0) refcount(%d){
-    }
-    ["class_object5"]=>
-    *RECURSION*
-  }
+  *RECURSION*
 }
 -- Iteration 4 --
 object(object_class)#%d (6) refcount(%d){
@@ -506,25 +252,7 @@ object(object_class)#%d (6) refcount(%d){
     int(3)
   }
   ["object_class1"]=>
-  object(object_class)#%d (6) refcount(%d){
-    ["value1"]=>
-    int(5)
-    ["value2":"object_class":private]=>
-    int(10)
-    ["value3":protected]=>
-    int(20)
-    ["value4"]=>
-    int(30)
-    ["array_var"]=>
-    array(2) refcount(%d){
-      ["key1"]=>
-      int(1)
-      ["key2 "]=>
-      int(3)
-    }
-    ["object_class1"]=>
-    *RECURSION*
-  }
+  *RECURSION*
 }
 -- Iteration 5 --
 object(object_class)#%d (6) refcount(%d){
@@ -544,25 +272,7 @@ object(object_class)#%d (6) refcount(%d){
     int(3)
   }
   ["object_class1"]=>
-  object(object_class)#%d (6) refcount(%d){
-    ["value1"]=>
-    int(5)
-    ["value2":"object_class":private]=>
-    int(10)
-    ["value3":protected]=>
-    int(20)
-    ["value4"]=>
-    int(30)
-    ["array_var"]=>
-    array(2) refcount(%d){
-      ["key1"]=>
-      int(1)
-      ["key2 "]=>
-      int(3)
-    }
-    ["object_class1"]=>
-    *RECURSION*
-  }
+  *RECURSION*
 }
 -- Iteration 6 --
 object(no_member_class)#%d (0) refcount(%d){
@@ -587,25 +297,7 @@ object(object_class)#%d (6) refcount(%d){
     int(3)
   }
   ["object_class1"]=>
-  object(object_class)#%d (6) refcount(%d){
-    ["value1"]=>
-    int(5)
-    ["value2":"object_class":private]=>
-    int(10)
-    ["value3":protected]=>
-    int(20)
-    ["value4"]=>
-    int(30)
-    ["array_var"]=>
-    array(2) refcount(%d){
-      ["key1"]=>
-      int(1)
-      ["key2 "]=>
-      int(3)
-    }
-    ["object_class1"]=>
-    *RECURSION*
-  }
+  *RECURSION*
 }
 -- Iteration 9 --
 object(object_class)#%d (6) refcount(%d){
@@ -625,25 +317,7 @@ object(object_class)#%d (6) refcount(%d){
     int(3)
   }
   ["object_class1"]=>
-  object(object_class)#%d (6) refcount(%d){
-    ["value1"]=>
-    int(5)
-    ["value2":"object_class":private]=>
-    int(10)
-    ["value3":protected]=>
-    int(20)
-    ["value4"]=>
-    int(30)
-    ["array_var"]=>
-    array(2) refcount(%d){
-      ["key1"]=>
-      int(1)
-      ["key2 "]=>
-      int(3)
-    }
-    ["object_class1"]=>
-    *RECURSION*
-  }
+  *RECURSION*
 }
 -- Iteration 10 --
 int(30)
@@ -674,67 +348,7 @@ object(object_class)#%d (7) refcount(%d){
     int(3)
   }
   ["object_class1"]=>
-  object(object_class)#%d (7) refcount(%d){
-    ["value1"]=>
-    int(5)
-    ["value2":"object_class":private]=>
-    int(10)
-    ["value3":protected]=>
-    int(20)
-    ["value4"]=>
-    int(30)
-    ["array_var"]=>
-    array(2) refcount(%d){
-      ["key1"]=>
-      int(1)
-      ["key2 "]=>
-      int(3)
-    }
-    ["object_class1"]=>
-    *RECURSION*
-    ["obj"]=>
-    &object(object_class)#%d (7) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      object(object_class)#%d (7) refcount(%d){
-        ["value1"]=>
-        int(5)
-        ["value2":"object_class":private]=>
-        int(10)
-        ["value3":protected]=>
-        int(20)
-        ["value4"]=>
-        int(30)
-        ["array_var"]=>
-        array(2) refcount(%d){
-          ["key1"]=>
-          int(1)
-          ["key2 "]=>
-          int(3)
-        }
-        ["object_class1"]=>
-        *RECURSION*
-        ["obj"]=>
-        *RECURSION*
-      }
-      ["obj"]=>
-      *RECURSION*
-    }
-  }
+  *RECURSION*
   ["obj"]=>
   &object(object_class)#%d (7) refcount(%d){
     ["value1"]=>
@@ -753,89 +367,9 @@ object(object_class)#%d (7) refcount(%d){
       int(3)
     }
     ["object_class1"]=>
-    object(object_class)#%d (7) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      *RECURSION*
-      ["obj"]=>
-      &object(object_class)#%d (7) refcount(%d){
-        ["value1"]=>
-        int(5)
-        ["value2":"object_class":private]=>
-        int(10)
-        ["value3":protected]=>
-        int(20)
-        ["value4"]=>
-        int(30)
-        ["array_var"]=>
-        array(2) refcount(%d){
-          ["key1"]=>
-          int(1)
-          ["key2 "]=>
-          int(3)
-        }
-        ["object_class1"]=>
-        *RECURSION*
-        ["obj"]=>
-        *RECURSION*
-      }
-    }
+    *RECURSION*
     ["obj"]=>
-    &object(object_class)#%d (7) refcount(%d){
-      ["value1"]=>
-      int(5)
-      ["value2":"object_class":private]=>
-      int(10)
-      ["value3":protected]=>
-      int(20)
-      ["value4"]=>
-      int(30)
-      ["array_var"]=>
-      array(2) refcount(%d){
-        ["key1"]=>
-        int(1)
-        ["key2 "]=>
-        int(3)
-      }
-      ["object_class1"]=>
-      *RECURSION*
-      ["obj"]=>
-      &object(object_class)#%d (7) refcount(%d){
-        ["value1"]=>
-        int(5)
-        ["value2":"object_class":private]=>
-        int(10)
-        ["value3":protected]=>
-        int(20)
-        ["value4"]=>
-        int(30)
-        ["array_var"]=>
-        array(2) refcount(%d){
-          ["key1"]=>
-          int(1)
-          ["key2 "]=>
-          int(3)
-        }
-        ["object_class1"]=>
-        *RECURSION*
-        ["obj"]=>
-        *RECURSION*
-      }
-    }
+    *RECURSION*
   }
 }
 Done
index 0ae79914129259357cc730016b3b1f771e13d848..e28afe91776fab86ce1c4b65d5329b9169623a79 100644 (file)
@@ -119,10 +119,12 @@ again:
                        break;
                case IS_ARRAY:
                        myht = Z_ARRVAL_P(struc);
-                       if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht) && ++myht->u.v.nApplyCount > 1) {
-                               PUTS("*RECURSION*\n");
-                               --myht->u.v.nApplyCount;
-                               return;
+                       if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+                               if (GC_IS_RECURSIVE(myht)) {
+                                       PUTS("*RECURSION*\n");
+                                       return;
+                               }
+                               GC_PROTECT_RECURSION(myht);
                        }
                        count = zend_array_count(myht);
                        php_printf("%sarray(%d) {\n", COMMON, count);
@@ -131,8 +133,8 @@ again:
                        ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) {
                                php_array_element_dump(val, num, key, level);
                        } ZEND_HASH_FOREACH_END();
-                       if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht)) {
-                               --myht->u.v.nApplyCount;
+                       if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+                               GC_UNPROTECT_RECURSION(myht);
                        }
                        if (is_temp) {
                                zend_hash_destroy(myht);
@@ -144,11 +146,11 @@ again:
                        PUTS("}\n");
                        break;
                case IS_OBJECT:
-                       if (Z_OBJ_APPLY_COUNT_P(struc) > 0) {
+                       if (Z_IS_RECURSIVE_P(struc)) {
                                PUTS("*RECURSION*\n");
                                return;
                        }
-                       Z_OBJ_INC_APPLY_COUNT_P(struc);
+                       Z_PROTECT_RECURSION_P(struc);
 
                        myht = Z_OBJDEBUG_P(struc, is_temp);
                        class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
@@ -172,7 +174,7 @@ again:
                                php_printf("%*c", level-1, ' ');
                        }
                        PUTS("}\n");
-                       Z_OBJ_DEC_APPLY_COUNT_P(struc);
+                       Z_UNPROTECT_RECURSION_P(struc);
                        break;
                case IS_RESOURCE: {
                        const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
@@ -289,18 +291,20 @@ again:
                break;
        case IS_ARRAY:
                myht = Z_ARRVAL_P(struc);
-               if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 1) {
-                       myht->u.v.nApplyCount--;
-                       PUTS("*RECURSION*\n");
-                       return;
+               if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+                       if (GC_IS_RECURSIVE(myht)) {
+                               PUTS("*RECURSION*\n");
+                               return;
+                       }
+                       GC_PROTECT_RECURSION(myht);
                }
                count = zend_array_count(myht);
                php_printf("%sarray(%d) refcount(%u){\n", COMMON, count, Z_REFCOUNTED_P(struc) ? Z_REFCOUNT_P(struc) : 1);
                ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
                        zval_array_element_dump(val, index, key, level);
                } ZEND_HASH_FOREACH_END();
-               if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht)) {
-                       myht->u.v.nApplyCount--;
+               if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+                       GC_UNPROTECT_RECURSION(myht);
                }
                if (is_temp) {
                        zend_hash_destroy(myht);
@@ -314,12 +318,11 @@ again:
        case IS_OBJECT:
                myht = Z_OBJDEBUG_P(struc, is_temp);
                if (myht) {
-                       if (myht->u.v.nApplyCount > 1) {
+                       if (GC_IS_RECURSIVE(myht)) {
                                PUTS("*RECURSION*\n");
                                return;
-                       } else {
-                               myht->u.v.nApplyCount++;
                        }
+                       GC_PROTECT_RECURSION(myht);
                }
                class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
                php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0, Z_REFCOUNT_P(struc));
@@ -328,7 +331,7 @@ again:
                        ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
                                zval_object_property_dump(val, index, key, level);
                        } ZEND_HASH_FOREACH_END();
-                       myht->u.v.nApplyCount--;
+                       GC_UNPROTECT_RECURSION(myht);
                        if (is_temp) {
                                zend_hash_destroy(myht);
                                efree(myht);
@@ -487,11 +490,13 @@ again:
                        break;
                case IS_ARRAY:
                        myht = Z_ARRVAL_P(struc);
-                       if (ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 0) {
-                               myht->u.v.nApplyCount--;
-                               smart_str_appendl(buf, "NULL", 4);
-                               zend_error(E_WARNING, "var_export does not handle circular references");
-                               return;
+                       if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+                               if (GC_IS_RECURSIVE(myht)) {
+                                       smart_str_appendl(buf, "NULL", 4);
+                                       zend_error(E_WARNING, "var_export does not handle circular references");
+                                       return;
+                               }
+                               GC_PROTECT_RECURSION(myht);
                        }
                        if (level > 1) {
                                smart_str_appendc(buf, '\n');
@@ -501,8 +506,8 @@ again:
                        ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
                                php_array_element_export(val, index, key, level, buf);
                        } ZEND_HASH_FOREACH_END();
-                       if (ZEND_HASH_APPLY_PROTECTION(myht)) {
-                               myht->u.v.nApplyCount--;
+                       if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+                               GC_UNPROTECT_RECURSION(myht);
                        }
                        if (level > 1) {
                                buffer_append_spaces(buf, level - 1);
@@ -514,12 +519,12 @@ again:
                case IS_OBJECT:
                        myht = Z_OBJPROP_P(struc);
                        if (myht) {
-                               if (myht->u.v.nApplyCount > 0) {
+                               if (GC_IS_RECURSIVE(myht)) {
                                        smart_str_appendl(buf, "NULL", 4);
                                        zend_error(E_WARNING, "var_export does not handle circular references");
                                        return;
                                } else {
-                                       myht->u.v.nApplyCount++;
+                                       GC_PROTECT_RECURSION(myht);
                                }
                        }
                        if (level > 1) {
@@ -534,7 +539,7 @@ again:
                                ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
                                        php_object_element_export(val, index, key, level, buf);
                                } ZEND_HASH_FOREACH_END();
-                               myht->u.v.nApplyCount--;
+                               GC_UNPROTECT_RECURSION(myht);
                        }
                        if (level > 1) {
                                buffer_append_spaces(buf, level - 1);
@@ -951,18 +956,22 @@ again:
 
                                        /* we should still add element even if it's not OK,
                                         * since we already wrote the length of the array before */
-                                       if ((Z_TYPE_P(data) == IS_ARRAY && Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))
-                                               || (Z_TYPE_P(data) == IS_ARRAY && Z_ARRVAL_P(data)->u.v.nApplyCount > 1)
-                                       ) {
-                                               smart_str_appendl(buf, "N;", 2);
-                                       } else {
-                                               if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) {
-                                                       Z_ARRVAL_P(data)->u.v.nApplyCount++;
+                                       if (Z_TYPE_P(data) == IS_ARRAY) {
+                                               if (Z_TYPE_P(data) == IS_ARRAY
+                                                && (UNEXPECTED(Z_IS_RECURSIVE_P(data))
+                                                 || UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc)))) {
+                                                       smart_str_appendl(buf, "N;", 2);
+                                               } else {
+                                                       if (Z_REFCOUNTED_P(data)) {
+                                                               Z_PROTECT_RECURSION_P(data);
+                                                       }
+                                                       php_var_serialize_intern(buf, data, var_hash);
+                                                       if (Z_REFCOUNTED_P(data)) {
+                                                               Z_UNPROTECT_RECURSION_P(data);
+                                                       }
                                                }
+                                       } else {
                                                php_var_serialize_intern(buf, data, var_hash);
-                                               if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) {
-                                                       Z_ARRVAL_P(data)->u.v.nApplyCount--;
-                                               }
                                        }
                                } ZEND_HASH_FOREACH_END();
                        }
index b6af00edd7660941817ce5289bdc649d95e4fb3d..556af05112aee69294ddab350efcc57f8332a5ba 100644 (file)
@@ -640,28 +640,28 @@ void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name)
 
                case IS_ARRAY:
                        ht = Z_ARRVAL_P(var);
-                       if (ht->u.v.nApplyCount > 1) {
-                               zend_throw_error(NULL, "WDDX doesn't support circular references");
-                               return;
-                       }
-                       if (ZEND_HASH_APPLY_PROTECTION(ht)) {
-                               ht->u.v.nApplyCount++;
+                       if (Z_REFCOUNTED_P(var)) {
+                               if (GC_IS_RECURSIVE(ht)) {
+                                       zend_throw_error(NULL, "WDDX doesn't support circular references");
+                                       return;
+                               }
+                               GC_PROTECT_RECURSION(ht);
                        }
                        php_wddx_serialize_array(packet, var);
-                       if (ZEND_HASH_APPLY_PROTECTION(ht)) {
-                               ht->u.v.nApplyCount--;
+                       if (Z_REFCOUNTED_P(var)) {
+                               GC_UNPROTECT_RECURSION(ht);
                        }
                        break;
 
                case IS_OBJECT:
                        ht = Z_OBJPROP_P(var);
-                       if (ht->u.v.nApplyCount > 1) {
+                       if (GC_IS_RECURSIVE(ht)) {
                                zend_throw_error(NULL, "WDDX doesn't support circular references");
                                return;
                        }
-                       ht->u.v.nApplyCount++;
+                       GC_PROTECT_RECURSION(ht);
                        php_wddx_serialize_object(packet, var);
-                       ht->u.v.nApplyCount--;
+                       GC_UNPROTECT_RECURSION(ht);
                        break;
        }
 
@@ -691,28 +691,26 @@ static void php_wddx_add_var(wddx_packet *packet, zval *name_var)
 
                target_hash = HASH_OF(name_var);
 
-               if (is_array && target_hash->u.v.nApplyCount > 1) {
-                       php_error_docref(NULL, E_WARNING, "recursion detected");
-                       return;
-               }
-
                if (!Z_REFCOUNTED_P(name_var)) {
                        ZEND_HASH_FOREACH_VAL(target_hash, val) {
                                php_wddx_add_var(packet, val);
                        } ZEND_HASH_FOREACH_END();
                } else {
-                       ZEND_HASH_FOREACH_VAL(target_hash, val) {
-                               if (is_array) {
-                                       target_hash->u.v.nApplyCount++;
+                       if (is_array) {
+                               if (GC_IS_RECURSIVE(target_hash)) {
+                                       php_error_docref(NULL, E_WARNING, "recursion detected");
+                                       return;
                                }
-
+                               GC_PROTECT_RECURSION(target_hash);
+                       }
+                       ZEND_HASH_FOREACH_VAL(target_hash, val) {
                                ZVAL_DEREF(val);
                                php_wddx_add_var(packet, val);
 
-                               if (is_array) {
-                                       target_hash->u.v.nApplyCount--;
-                               }
                        } ZEND_HASH_FOREACH_END();
+                       if (is_array) {
+                               GC_UNPROTECT_RECURSION(target_hash);
+                       }
                }
        }
 }
index 2b1a642c05eaab7af59e6f18b8891c3f5e64262c..e20b6d68724aef889fbb10dbabd3b39af52a5c6f 100644 (file)
@@ -556,9 +556,12 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep
                                                XMLRPC_VECTOR_TYPE vtype;
 
                                                ht = HASH_OF(&val);
-                                               if (ht && ht->u.v.nApplyCount > 1) {
-                                                       zend_throw_error(NULL, "XML-RPC doesn't support circular references");
-                                                       return NULL;
+                                               if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+                                                       if (GC_IS_RECURSIVE(ht)) {
+                                                               zend_throw_error(NULL, "XML-RPC doesn't support circular references");
+                                                               return NULL;
+                                                       }
+                                                       GC_PROTECT_RECURSION(ht);
                                                }
 
                                                ZVAL_COPY(&val_arr, &val);
@@ -569,10 +572,6 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep
 
                                                ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(val_arr), num_index, my_key, pIter) {
                                                        ZVAL_DEREF(pIter);
-                                                       ht = HASH_OF(pIter);
-                                                       if (ht) {
-                                                               ht->u.v.nApplyCount++;
-                                                       }
                                                        if (my_key == NULL) {
                                                                char *num_str = NULL;
 
@@ -587,10 +586,10 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep
                                                        } else {
                                                                XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(ZSTR_VAL(my_key), pIter, depth++));
                                                        }
-                                                       if (ht) {
-                                                               ht->u.v.nApplyCount--;
-                                                       }
                                                } ZEND_HASH_FOREACH_END();
+                                               if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+                                                       GC_UNPROTECT_RECURSION(ht);
+                                               }
                                                zval_ptr_dtor(&val_arr);
                                        }
                                        break;
index 9326b79b6ccdc504123a2dfa33c92b332aeaaea4..bdcb173c24f7b57cea3c0258ca12d3bbc15206f0 100644 (file)
@@ -687,10 +687,12 @@ PHPDBG_API void phpdbg_xml_var_dump(zval *zv) {
                                break;
                        case IS_ARRAY:
                                myht = Z_ARRVAL_P(zv);
-                               if (ZEND_HASH_APPLY_PROTECTION(myht) && ++myht->u.v.nApplyCount > 1) {
-                                       phpdbg_xml("<recursion />");
-                                       --myht->u.v.nApplyCount;
-                                       break;
+                               if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+                                       if (GC_IS_RECURSIVE(myht)) {
+                                               phpdbg_xml("<recursion />");
+                                               break;
+                                       }
+                                       GC_PROTECT_RECURSION(myht);
                                }
                                phpdbg_xml("<array refstatus=\"%s\" num=\"%d\">", COMMON, zend_hash_num_elements(myht));
                                element_dump_func = phpdbg_xml_array_element_dump;
@@ -698,9 +700,8 @@ PHPDBG_API void phpdbg_xml_var_dump(zval *zv) {
                                goto head_done;
                        case IS_OBJECT:
                                myht = Z_OBJDEBUG_P(zv, is_temp);
-                               if (myht && ++myht->u.v.nApplyCount > 1) {
+                               if (myht && GC_IS_RECURSIVE(myht)) {
                                        phpdbg_xml("<recursion />");
-                                       --myht->u.v.nApplyCount;
                                        break;
                                }
 
@@ -715,7 +716,7 @@ head_done:
                                                element_dump_func(val, key, num);
                                        } ZEND_HASH_FOREACH_END();
                                        zend_hash_apply_with_arguments(myht, (apply_func_args_t) element_dump_func, 0);
-                                       --myht->u.v.nApplyCount;
+                                       GC_UNPROTECT_RECURSION(myht);
                                        if (is_temp) {
                                                zend_hash_destroy(myht);
                                                efree(myht);