mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Avoid double checks on early binding
This commit is contained in:
parent
798fed3b0d
commit
83b99527df
@ -1157,19 +1157,8 @@ ZEND_API void zend_do_delayed_early_binding(const zend_op_array *op_array, uint3
|
||||
zend_string *lc_parent_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
zend_class_entry *parent_ce = zend_hash_find_ex_ptr(EG(class_table), lc_parent_name, 1);
|
||||
|
||||
if (parent_ce && zend_can_early_bind(ce, parent_ce)) {
|
||||
zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, Z_STR_P(lcname));
|
||||
if (UNEXPECTED(!zv)) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
|
||||
} else {
|
||||
zend_do_inheritance(ce, parent_ce);
|
||||
zend_build_properties_info_table(ce);
|
||||
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
|
||||
zend_verify_abstract_class(ce);
|
||||
}
|
||||
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
|
||||
ce->ce_flags |= ZEND_ACC_LINKED;
|
||||
}
|
||||
if (parent_ce) {
|
||||
zend_try_early_bind(ce, parent_ce, Z_STR_P(lcname), zv);
|
||||
}
|
||||
}
|
||||
opline_num = op_array->opcodes[opline_num].result.opline_num;
|
||||
@ -6447,22 +6436,15 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
|
||||
if (parent_ce
|
||||
&& !(CG(compiler_options) & ZEND_COMPILE_PRELOAD) /* delay inheritance till preloading */
|
||||
&& ((parent_ce->type != ZEND_INTERNAL_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES))
|
||||
&& ((parent_ce->type != ZEND_USER_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) || (parent_ce->info.user.filename == ce->info.user.filename))
|
||||
&& zend_can_early_bind(ce, parent_ce)
|
||||
) {
|
||||
if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
|
||||
CG(zend_lineno) = decl->end_lineno;
|
||||
zend_do_inheritance(ce, parent_ce);
|
||||
zend_build_properties_info_table(ce);
|
||||
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
|
||||
zend_verify_abstract_class(ce);
|
||||
}
|
||||
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
|
||||
ce->ce_flags |= ZEND_ACC_LINKED;
|
||||
&& ((parent_ce->type != ZEND_USER_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) || (parent_ce->info.user.filename == ce->info.user.filename))) {
|
||||
|
||||
CG(zend_lineno) = decl->end_lineno;
|
||||
if (zend_try_early_bind(ce, parent_ce, lcname, NULL)) {
|
||||
CG(zend_lineno) = ast->lineno;
|
||||
zend_string_release(lcname);
|
||||
return NULL;
|
||||
}
|
||||
CG(zend_lineno) = ast->lineno;
|
||||
}
|
||||
} else {
|
||||
if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
|
||||
|
@ -720,13 +720,16 @@ static void perform_delayable_implementation_check(
|
||||
}
|
||||
}
|
||||
|
||||
static void do_inheritance_check_on_method(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv) /* {{{ */
|
||||
static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv, zend_bool check_only, zend_bool checked) /* {{{ */
|
||||
{
|
||||
uint32_t child_flags;
|
||||
uint32_t parent_flags = parent->common.fn_flags;
|
||||
zend_function *proto;
|
||||
|
||||
if (UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) {
|
||||
if (!checked && UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) {
|
||||
if (check_only) {
|
||||
return INHERITANCE_ERROR;
|
||||
}
|
||||
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
|
||||
"Cannot override final method %s::%s()",
|
||||
ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name));
|
||||
@ -735,7 +738,10 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
|
||||
child_flags = child->common.fn_flags;
|
||||
/* You cannot change from static to non static and vice versa.
|
||||
*/
|
||||
if (UNEXPECTED((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC))) {
|
||||
if (!checked && UNEXPECTED((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC))) {
|
||||
if (check_only) {
|
||||
return INHERITANCE_ERROR;
|
||||
}
|
||||
if (child_flags & ZEND_ACC_STATIC) {
|
||||
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
|
||||
"Cannot make non static method %s::%s() static in class %s",
|
||||
@ -748,18 +754,21 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
|
||||
}
|
||||
|
||||
/* Disallow making an inherited method abstract. */
|
||||
if (UNEXPECTED((child_flags & ZEND_ACC_ABSTRACT) > (parent_flags & ZEND_ACC_ABSTRACT))) {
|
||||
if (!checked && UNEXPECTED((child_flags & ZEND_ACC_ABSTRACT) > (parent_flags & ZEND_ACC_ABSTRACT))) {
|
||||
if (check_only) {
|
||||
return INHERITANCE_ERROR;
|
||||
}
|
||||
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
|
||||
"Cannot make non abstract method %s::%s() abstract in class %s",
|
||||
ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name), ZEND_FN_SCOPE_NAME(child));
|
||||
}
|
||||
|
||||
if (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED)) {
|
||||
if (!check_only && (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED))) {
|
||||
child->common.fn_flags |= ZEND_ACC_CHANGED;
|
||||
}
|
||||
|
||||
if (parent_flags & ZEND_ACC_PRIVATE) {
|
||||
return;
|
||||
return INHERITANCE_SUCCESS;
|
||||
}
|
||||
|
||||
proto = parent->common.prototype ?
|
||||
@ -769,12 +778,12 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
|
||||
/* ctors only have a prototype if is abstract (or comes from an interface) */
|
||||
/* and if that is the case, we want to check inheritance against it */
|
||||
if (!(proto->common.fn_flags & ZEND_ACC_ABSTRACT)) {
|
||||
return;
|
||||
return INHERITANCE_SUCCESS;
|
||||
}
|
||||
parent = proto;
|
||||
}
|
||||
|
||||
if (child_zv && child->common.prototype != proto) {
|
||||
if (!check_only && child_zv && child->common.prototype != proto) {
|
||||
do {
|
||||
if (child->common.scope != ce
|
||||
&& child->type == ZEND_USER_FUNCTION
|
||||
@ -794,14 +803,32 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
|
||||
}
|
||||
|
||||
/* Prevent derived classes from restricting access that was available in parent classes (except deriving from non-abstract ctors) */
|
||||
if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
|
||||
if (!checked && (child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
|
||||
if (check_only) {
|
||||
return INHERITANCE_ERROR;
|
||||
}
|
||||
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
|
||||
"Access level to %s::%s() must be %s (as in class %s)%s",
|
||||
ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
|
||||
}
|
||||
|
||||
perform_delayable_implementation_check(
|
||||
ce, child, parent, /*always_error*/0);
|
||||
if (!checked) {
|
||||
if (check_only) {
|
||||
zend_string *unresolved_class;
|
||||
|
||||
return zend_do_perform_implementation_check(
|
||||
&unresolved_class, child, parent);
|
||||
}
|
||||
perform_delayable_implementation_check(
|
||||
ce, child, parent, /*always_error*/0);
|
||||
}
|
||||
return INHERITANCE_SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void do_inheritance_check_on_method(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv) /* {{{ */
|
||||
{
|
||||
do_inheritance_check_on_method_ex(child, parent, ce, child_zv, 0, 0);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -829,6 +856,30 @@ static zend_function *do_inherit_method(zend_string *key, zend_function *parent,
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void do_inherit_checked_methods(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
|
||||
{
|
||||
zend_string *key;
|
||||
zend_function *parent;
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent) {
|
||||
zval *child = zend_hash_find_ex(&ce->function_table, key, 1);
|
||||
|
||||
if (child) {
|
||||
zend_function *func = (zend_function*)Z_PTR_P(child);
|
||||
do_inheritance_check_on_method_ex(func, parent, ce, child, 0, 1);
|
||||
} else {
|
||||
|
||||
if (parent->common.fn_flags & (ZEND_ACC_ABSTRACT)) {
|
||||
ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
|
||||
}
|
||||
|
||||
parent = zend_duplicate_function(parent, ce);
|
||||
_zend_hash_append_ptr(&ce->function_table, key, parent);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
zend_string* zend_resolve_property_type(zend_string *type, zend_class_entry *scope) /* {{{ */
|
||||
{
|
||||
if (zend_string_equals_literal_ci(type, "parent")) {
|
||||
@ -1049,7 +1100,7 @@ void zend_build_properties_info_table(zend_class_entry *ce)
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
|
||||
ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, zend_bool checked) /* {{{ */
|
||||
{
|
||||
zend_property_info *property_info;
|
||||
zend_function *func;
|
||||
@ -1259,13 +1310,17 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
|
||||
zend_hash_num_elements(&ce->function_table) +
|
||||
zend_hash_num_elements(&parent_ce->function_table), 0);
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) {
|
||||
zend_function *new_func = do_inherit_method(key, func, ce);
|
||||
if (checked) {
|
||||
do_inherit_checked_methods(ce, parent_ce);
|
||||
} else {
|
||||
ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) {
|
||||
zend_function *new_func = do_inherit_method(key, func, ce);
|
||||
|
||||
if (new_func) {
|
||||
_zend_hash_append_ptr(&ce->function_table, key, new_func);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
if (new_func) {
|
||||
_zend_hash_append_ptr(&ce->function_table, key, new_func);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
}
|
||||
|
||||
do_inherit_parent_constructor(ce);
|
||||
@ -2385,43 +2440,60 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */
|
||||
zend_bool zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) {
|
||||
static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
|
||||
{
|
||||
inheritance_status ret = INHERITANCE_SUCCESS;
|
||||
zend_string *key;
|
||||
zend_function *parent_func;
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) {
|
||||
uint32_t parent_flags = parent_func->common.fn_flags;
|
||||
zval *zv;
|
||||
zend_string *unresolved_class;
|
||||
|
||||
if (parent_flags & ZEND_ACC_PRIVATE) {
|
||||
continue;
|
||||
} else if (parent_flags & ZEND_ACC_CTOR) {
|
||||
zend_function *proto = parent_func->common.prototype ?
|
||||
parent_func->common.prototype : parent_func;
|
||||
|
||||
/* ctors only have a prototype if is abstract (or comes from an interface) */
|
||||
/* and if that is the case, we want to check inheritance against it */
|
||||
if (!(proto->common.fn_flags & ZEND_ACC_ABSTRACT)) {
|
||||
continue;
|
||||
}
|
||||
parent_func = proto;
|
||||
}
|
||||
|
||||
zv = zend_hash_find_ex(&ce->function_table, key, 1);
|
||||
zval *zv = zend_hash_find_ex(&ce->function_table, key, 1);
|
||||
if (zv) {
|
||||
zend_function *child_func = Z_FUNC_P(zv);
|
||||
inheritance_status status;
|
||||
inheritance_status status =
|
||||
do_inheritance_check_on_method_ex(child_func, parent_func, ce, NULL, 1, 0);
|
||||
|
||||
status = zend_do_perform_implementation_check(
|
||||
&unresolved_class, child_func, parent_func);
|
||||
|
||||
if (UNEXPECTED(status == INHERITANCE_UNRESOLVED)) {
|
||||
return 0;
|
||||
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
|
||||
if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
|
||||
return INHERITANCE_UNRESOLVED;
|
||||
}
|
||||
ZEND_ASSERT(status == INHERITANCE_ERROR);
|
||||
ret = INHERITANCE_ERROR;
|
||||
}
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
return 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
zend_bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */
|
||||
{
|
||||
inheritance_status status = zend_can_early_bind(ce, parent_ce);
|
||||
|
||||
if (EXPECTED(status != INHERITANCE_UNRESOLVED)) {
|
||||
if (delayed_early_binding) {
|
||||
if (UNEXPECTED(zend_hash_set_bucket_key(EG(class_table), (Bucket*)delayed_early_binding, lcname) == NULL)) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (UNEXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) == NULL)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
|
||||
zend_build_properties_info_table(ce);
|
||||
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
|
||||
zend_verify_abstract_class(ce);
|
||||
}
|
||||
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
|
||||
ce->ce_flags |= ZEND_ACC_LINKED;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
@ -25,14 +25,17 @@
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface);
|
||||
ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce);
|
||||
ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, zend_bool checked);
|
||||
|
||||
#define zend_do_inheritance(ce, parent_ce) \
|
||||
zend_do_inheritance_ex(ce, parent_ce, 0)
|
||||
|
||||
ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name);
|
||||
|
||||
void zend_verify_abstract_class(zend_class_entry *ce);
|
||||
void zend_check_deprecated_constructor(const zend_class_entry *ce);
|
||||
void zend_build_properties_info_table(zend_class_entry *ce);
|
||||
zend_bool zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce);
|
||||
zend_bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user