From 5dc52e488048d93767b78fe7fce34a6a0febc5e0 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 14 Apr 2014 13:24:43 +0400 Subject: [PATCH] Fixed interface constants inheritance. Now we use IS_REFERENCE for inhereted class constants. I might miss some edje cases. --- Zend/zend_compile.c | 54 ++++++++++++++++++----- Zend/zend_constants.c | 2 + Zend/zend_objects.c | 2 +- Zend/zend_types.h | 9 ++++ Zend/zend_variables.c | 21 ++++++--- Zend/zend_vm_def.h | 3 ++ Zend/zend_vm_execute.h | 9 ++++ ext/opcache/Optimizer/pass1_5.c | 1 + ext/opcache/zend_accelerator_util_funcs.c | 2 +- ext/reflection/php_reflection.c | 2 +- 10 files changed, 85 insertions(+), 20 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 818ecf3632..d54e705a81 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3089,7 +3089,7 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */ ALLOC_HASHTABLE(op_array->static_variables); zend_hash_init(op_array->static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(op_array->static_variables, static_variables, zval_add_ref_unref); + zend_hash_copy(op_array->static_variables, static_variables, zval_add_ref); } op_array->run_time_cache = NULL; } else if (function->type == ZEND_INTERNAL_FUNCTION) { @@ -3743,6 +3743,27 @@ ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_ zval_add_ref #endif +static int do_inherit_class_constant(zval *zv TSRMLS_DC, int num_args, va_list args, const zend_hash_key *hash_key) /* {{{ */ +{ + zend_class_entry *ce = va_arg(args, zend_class_entry *); + zend_class_entry *parent_ce = va_arg(args, zend_class_entry *); + + if (hash_key->key) { + if (!Z_ISREF_P(zv)) { + if (parent_ce->type == ZEND_INTERNAL_CLASS) { + ZVAL_NEW_PERSISTENT_REF(zv, zv); + } else { + ZVAL_NEW_REF(zv, zv); + } + } + if (zend_hash_add(&ce->constants_table, hash_key->key, zv)) { + Z_ADDREF_P(zv); + } + } + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC) /* {{{ */ { zend_property_info *property_info; @@ -3846,7 +3867,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent zend_hash_merge_ex(&ce->properties_info, &parent_ce->properties_info, (ce->type & ZEND_INTERNAL_CLASS ? zend_duplicate_property_info_internal_zval : zend_duplicate_property_info_zval), (merge_checker_func_t) do_inherit_property_access_check, ce); - zend_hash_merge(&ce->constants_table, &parent_ce->constants_table, zval_property_ctor(parent_ce, ce), 0); + zend_hash_apply_with_arguments(&parent_ce->constants_table TSRMLS_CC, (apply_func_args_t)do_inherit_class_constant, 2, ce, parent_ce); zend_hash_merge_ex(&ce->function_table, &parent_ce->function_table, do_inherit_method, (merge_checker_func_t) do_inherit_method_check, ce); do_inherit_parent_constructor(ce); @@ -3865,14 +3886,9 @@ static zend_bool do_inherit_constant_check(HashTable *child_constants_table, con zval *old_constant; if ((old_constant = zend_hash_find(child_constants_table, hash_key->key)) != NULL) { -//??? if (old_constant != parent_constant) { -//??? see Zend/tests/errmsg_025.phpt - if ((Z_TYPE_P(old_constant) != Z_TYPE_P(parent_constant)) || - (Z_TYPE_P(old_constant) == IS_LONG && Z_LVAL_P(old_constant) != Z_LVAL_P(parent_constant)) || - (Z_TYPE_P(old_constant) == IS_BOOL && Z_LVAL_P(old_constant) != Z_LVAL_P(parent_constant)) || - (Z_TYPE_P(old_constant) == IS_DOUBLE && Z_DVAL_P(old_constant) != Z_DVAL_P(parent_constant)) || - (Z_TYPE_P(old_constant) == IS_STRING && Z_STR_P(old_constant) != Z_STR_P(parent_constant)) || - (Z_REFCOUNTED_P(old_constant) && Z_COUNTED_P(old_constant) != Z_COUNTED_P(parent_constant))) { + if (!Z_ISREF_P(old_constant) || + !Z_ISREF_P(parent_constant) || + Z_REFVAL_P(old_constant) != Z_REFVAL_P(parent_constant)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", hash_key->key->val, iface->name->val); } return 0; @@ -3891,6 +3907,22 @@ static int do_interface_constant_check(zval *val TSRMLS_DC, int num_args, va_lis } /* }}} */ +static int do_inherit_iface_constant(zval *zv TSRMLS_DC, int num_args, va_list args, const zend_hash_key *hash_key) /* {{{ */ +{ + zend_class_entry *ce = va_arg(args, zend_class_entry *); + zend_class_entry *iface = va_arg(args, zend_class_entry *); + + if (hash_key->key && do_inherit_constant_check(&ce->constants_table, zv, hash_key, iface)) { + if (!Z_ISREF_P(zv)) { + ZVAL_NEW_REF(zv, zv); + } + Z_ADDREF_P(zv); + zend_hash_update(&ce->constants_table, hash_key->key, zv); + } + return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) /* {{{ */ { zend_uint i, ignore = 0; @@ -3922,7 +3954,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } ce->interfaces[ce->num_interfaces++] = iface; - zend_hash_merge_ex(&ce->constants_table, &iface->constants_table, (copy_ctor_func_t) zval_add_ref, (merge_checker_func_t) do_inherit_constant_check, iface); + zend_hash_apply_with_arguments(&iface->constants_table TSRMLS_CC, (apply_func_args_t)do_inherit_iface_constant, 2, ce, iface); zend_hash_merge_ex(&ce->function_table, &iface->function_table, do_inherit_method, (merge_checker_func_t) do_inherit_method_check, ce); do_implement_interface(ce, iface TSRMLS_CC); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 8227f185d4..964c7f8165 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -379,6 +379,8 @@ ZEND_API int zend_get_constant_ex(const char *name, uint name_len, zval *result, if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { zend_error(E_ERROR, "Undefined class constant '%s::%s'", class_name->val, constant_name->val); } + } else if (Z_ISREF_P(ret_constant)) { + ret_constant = Z_REFVAL_P(ret_constant); } } else if (!ce) { retval = 0; diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 37423b3dd8..390d6fc026 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -165,7 +165,7 @@ ZEND_API void zend_objects_clone_members(zend_object *new_object, zend_object *o ZVAL_INDIRECT(&new_prop, new_object->properties_table + (Z_INDIRECT_P(prop) - old_object->properties_table)); } else { ZVAL_COPY_VALUE(&new_prop, prop); - zval_add_ref_unref(&new_prop); + zval_add_ref(&new_prop); } switch (zend_hash_get_current_key_ex(old_object->properties, &key, &num_key, 0, &pos)) { case HASH_KEY_IS_STRING: diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 8baf8643af..4390d99232 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -568,6 +568,15 @@ static inline zend_uchar zval_get_type(const zval* pz) { Z_TYPE_INFO_P(z) = IS_REFERENCE_EX; \ } while (0) +#define ZVAL_NEW_PERSISTENT_REF(z, r) do { \ + zend_reference *_ref = malloc(sizeof(zend_reference)); \ + GC_REFCOUNT(_ref) = 1; \ + GC_TYPE_INFO(_ref) = IS_REFERENCE; \ + ZVAL_COPY_VALUE(&_ref->val, r); \ + Z_REF_P(z) = _ref; \ + Z_TYPE_INFO_P(z) = IS_REFERENCE_EX; \ + } while (0) + #define ZVAL_NEW_AST(z, a) do { \ zval *__z = (z); \ zend_ast_ref *_ast = emalloc(sizeof(zend_ast_ref)); \ diff --git a/Zend/zend_variables.c b/Zend/zend_variables.c index 24e347a15b..9661af1773 100644 --- a/Zend/zend_variables.c +++ b/Zend/zend_variables.c @@ -161,6 +161,13 @@ ZEND_API void _zval_internal_dtor(zval *zvalue ZEND_FILE_LINE_DC) case IS_RESOURCE: zend_error(E_CORE_ERROR, "Internal zval's can't be arrays, objects or resources"); break; + case IS_REFERENCE: { + zend_reference *ref = (zend_reference*)Z_REF_P(zvalue); + + zval_internal_ptr_dtor(&ref->val); + free(ref); + break; + } case IS_LONG: case IS_DOUBLE: case IS_BOOL: @@ -185,6 +192,13 @@ ZEND_API void _zval_internal_dtor_for_ptr(zval *zvalue ZEND_FILE_LINE_DC) case IS_RESOURCE: zend_error(E_CORE_ERROR, "Internal zval's can't be arrays, objects or resources"); break; + case IS_REFERENCE: { + zend_reference *ref = (zend_reference*)Z_REF_P(zvalue); + + zval_internal_ptr_dtor(&ref->val); + free(ref); + break; + } case IS_LONG: case IS_DOUBLE: case IS_BOOL: @@ -210,12 +224,7 @@ ZEND_API void zval_add_ref_unref(zval *p) { if (Z_REFCOUNTED_P(p)) { if (Z_ISREF_P(p)) { - if (Z_REFCOUNT_P(p) == 1) { - zval *q = Z_REFVAL_P(p); - ZVAL_DUP(p, q); - } else { - Z_ADDREF_P(p); - } + ZVAL_DUP(p, Z_REFVAL_P(p)); } else { Z_ADDREF_P(p); } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 3f714703ba..610b46c58e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3693,6 +3693,9 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST) } if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(opline->op2.zv))) != NULL)) { + if (Z_ISREF_P(value)) { + value = Z_REFVAL_P(value); + } if (Z_CONSTANT_P(value)) { zend_class_entry *old_scope = EG(scope); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 427ae46b62..a6d1be2270 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3890,6 +3890,9 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO } if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(opline->op2.zv))) != NULL)) { + if (Z_ISREF_P(value)) { + value = Z_REFVAL_P(value); + } if (Z_CONSTANT_P(value)) { zend_class_entry *old_scope = EG(scope); @@ -15161,6 +15164,9 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE } if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(opline->op2.zv))) != NULL)) { + if (Z_ISREF_P(value)) { + value = Z_REFVAL_P(value); + } if (Z_CONSTANT_P(value)) { zend_class_entry *old_scope = EG(scope); @@ -24594,6 +24600,9 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC } if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(opline->op2.zv))) != NULL)) { + if (Z_ISREF_P(value)) { + value = Z_REFVAL_P(value); + } if (Z_CONSTANT_P(value)) { zend_class_entry *old_scope = EG(scope); diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 3bf59be84b..9edff53d39 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -294,6 +294,7 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { if ((c = zend_hash_find(&ce->constants_table, Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL) { + ZVAL_DEREF(c); if (ZEND_IS_CONSTANT_TYPE(Z_TYPE_P(c))) { if (!zend_get_persistent_constant(Z_STR_P(c), &t, 1 TSRMLS_CC) || ZEND_IS_CONSTANT_TYPE(Z_TYPE(t))) { diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 6df81e6ee6..61d662a4f8 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -692,7 +692,7 @@ static void zend_class_copy_ctor(zend_class_entry **pce) zend_hash_clone_prop_info(&ce->properties_info, &old_ce->properties_info, old_ce, ce TSRMLS_CC); /* constants table */ - zend_hash_clone_zval(&ce->constants_table, &old_ce->constants_table, 0); + zend_hash_clone_zval(&ce->constants_table, &old_ce->constants_table, 1); ce->name = STR_DUP(ce->name, 0); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 1ef849f92a..224bec75ba 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4010,7 +4010,7 @@ ZEND_METHOD(reflection_class, getConstants) GET_REFLECTION_OBJECT_PTR(ce); array_init(return_value); zend_hash_apply_with_argument(&ce->constants_table, (apply_func_arg_t)zval_update_constant_inline_change, ce TSRMLS_CC); - zend_hash_copy(Z_ARRVAL_P(return_value), &ce->constants_table, zval_add_ref); + zend_hash_copy(Z_ARRVAL_P(return_value), &ce->constants_table, zval_add_ref_unref); } /* }}} */ -- 2.50.1