summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-06-17 12:50:16 +0300
committerDmitry Stogov <dmitry@zend.com>2015-06-17 12:50:16 +0300
commit4a6e1345e2dfd1d1edfd18b783bc9000598a4d92 (patch)
tree05b52f5393e38d86e1a738696a63143289c8f25d
parente011e6fdf4a7b65d4cf92dcad57f1e4fe4b25b80 (diff)
downloadphp-git-4a6e1345e2dfd1d1edfd18b783bc9000598a4d92.tar.gz
Use COW to prevent unnecessary duplication of dynamic propertyes of stdClass (and other classes without predefined properties).
-rw-r--r--Zend/zend_builtin_functions.c6
-rw-r--r--Zend/zend_object_handlers.c54
-rw-r--r--Zend/zend_objects.c16
-rw-r--r--Zend/zend_operators.c21
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 {