From 4a6e1345e2dfd1d1edfd18b783bc9000598a4d92 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 17 Jun 2015 12:50:16 +0300 Subject: [PATCH] Use COW to prevent unnecessary duplication of dynamic propertyes of stdClass (and other classes without predefined properties). --- Zend/zend_builtin_functions.c | 6 ++++ Zend/zend_object_handlers.c | 54 +++++++++++++++++++++++++---------- Zend/zend_objects.c | 16 ++++++++++- Zend/zend_operators.c | 21 ++++++++++++-- 4 files changed, 78 insertions(+), 19 deletions(-) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 8989e93fa6..8ea758adcc 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1151,6 +1151,12 @@ ZEND_FUNCTION(get_object_vars) if (!zobj->ce->default_properties_count && properties == zobj->properties) { /* fast copy */ + if (EXPECTED(zobj->handlers == &std_object_handlers)) { + if (EXPECTED(!(GC_FLAGS(properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(properties)++; + } + RETURN_ARR(properties); + } RETURN_ARR(zend_array_dup(properties)); } else { array_init_size(return_value, zend_hash_num_elements(properties)); diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 74ae382df4..f284ca4b31 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -628,6 +628,12 @@ ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, v goto found; } } else if (EXPECTED(zobj->properties != NULL)) { + if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { + if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(zobj->properties)--; + } + zobj->properties = zend_array_dup(zobj->properties); + } if ((variable_ptr = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) { found: zend_assign_to_variable(variable_ptr, value, IS_CV); @@ -815,20 +821,31 @@ static zval *zend_std_get_property_ptr_ptr(zval *object, zval *member, int type, } } } else { - if (UNEXPECTED(!zobj->properties) || - UNEXPECTED((retval = zend_hash_find(zobj->properties, name)) == NULL)) { - if (EXPECTED(!zobj->ce->__get) || - UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) { - if (UNEXPECTED(!zobj->properties)) { - rebuild_object_properties(zobj); + if (EXPECTED(zobj->properties)) { + if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { + if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(zobj->properties)--; } - retval = zend_hash_update(zobj->properties, name, &EG(uninitialized_zval)); - /* Notice is thrown after creation of the property, to avoid EG(std_property_info) - * being overwritten in an error handler. */ - if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) { - zend_error(E_NOTICE, "Undefined property: %s::$%s", zobj->ce->name->val, name->val); + zobj->properties = zend_array_dup(zobj->properties); + } + if (EXPECTED((retval = zend_hash_find(zobj->properties, name)) != NULL)) { + if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) { + zend_string_release(name); } - } + return retval; + } + } + if (EXPECTED(!zobj->ce->__get) || + UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) { + if (UNEXPECTED(!zobj->properties)) { + rebuild_object_properties(zobj); + } + retval = zend_hash_update(zobj->properties, name, &EG(uninitialized_zval)); + /* Notice is thrown after creation of the property, to avoid EG(std_property_info) + * being overwritten in an error handler. */ + if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) { + zend_error(E_NOTICE, "Undefined property: %s::$%s", zobj->ce->name->val, name->val); + } } } } @@ -866,9 +883,16 @@ static void zend_std_unset_property(zval *object, zval *member, void **cache_slo ZVAL_UNDEF(slot); goto exit; } - } else if (EXPECTED(zobj->properties != NULL) && - EXPECTED(zend_hash_del(zobj->properties, Z_STR_P(member)) != FAILURE)) { - goto exit; + } else if (EXPECTED(zobj->properties != NULL)) { + if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { + if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(zobj->properties)--; + } + zobj->properties = zend_array_dup(zobj->properties); + } + if (EXPECTED(zend_hash_del(zobj->properties, Z_STR_P(member)) != FAILURE)) { + goto exit; + } } } else if (UNEXPECTED(EG(exception))) { goto exit; diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 9007661f03..2a0655b258 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -55,7 +55,11 @@ ZEND_API void zend_object_std_dtor(zend_object *object) zval *p, *end; if (object->properties) { - zend_array_destroy(object->properties); + if (EXPECTED(!(GC_FLAGS(object->properties) & IS_ARRAY_IMMUTABLE))) { + if (EXPECTED(--GC_REFCOUNT(object->properties) == 0)) { + zend_array_destroy(object->properties); + } + } } p = object->properties_table; if (EXPECTED(object->ce->default_properties_count)) { @@ -163,7 +167,17 @@ ZEND_API void zend_objects_clone_members(zend_object *new_object, zend_object *o src++; dst++; } while (src != end); + } else if (old_object->properties && !old_object->ce->clone) { + /* fast copy */ + if (EXPECTED(old_object->handlers == &std_object_handlers)) { + if (EXPECTED(!(GC_FLAGS(old_object->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(old_object->properties)++; + } + new_object->properties = old_object->properties; + return; + } } + if (old_object->properties && EXPECTED(zend_hash_num_elements(old_object->properties))) { zval *prop, new_prop; diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index d7d2591382..7e3b458bbe 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -588,9 +588,24 @@ try_again: HashTable *obj_ht = Z_OBJ_HT_P(op)->get_properties(op); if (obj_ht) { zval arr; - ZVAL_ARR(&arr, zend_array_dup(obj_ht)); - zval_dtor(op); - ZVAL_COPY_VALUE(op, &arr); + + if (!Z_OBJCE_P(op)->default_properties_count && obj_ht == Z_OBJ_P(op)->properties) { + /* fast copy */ + if (EXPECTED(Z_OBJ_P(op)->handlers == &std_object_handlers)) { + ZVAL_ARR(&arr, obj_ht); + if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(op)->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(Z_OBJ_P(op)->properties)++; + } + } else { + ZVAL_ARR(&arr, zend_array_dup(obj_ht)); + } + zval_dtor(op); + ZVAL_COPY_VALUE(op, &arr); + } else { + ZVAL_ARR(&arr, zend_array_dup(obj_ht)); + zval_dtor(op); + ZVAL_COPY_VALUE(op, &arr); + } return; } } else { -- 2.40.0