Skip to content

Instantly share code, notes, and snippets.

@dstogov
Created April 22, 2024 18:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dstogov/e58c5c1ae04ecce6dd963d708639f722 to your computer and use it in GitHub Desktop.
Save dstogov/e58c5c1ae04ecce6dd963d708639f722 to your computer and use it in GitHub Desktop.
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 491d68714b3..20c830db03f 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -1081,13 +1081,13 @@ static void perform_delayable_implementation_check(
/**
* @param check_only Set to false to throw compile errors on incompatible methods, or true to return INHERITANCE_ERROR.
* @param checked Whether the compatibility check has already succeeded in zend_can_early_bind().
- * @param force_mutable Whether we know that child may be modified, i.e. doesn't live in shm.
+ * @param child_zv the zval where the "child" may be cloned. It's NULL if we don't need cloning.
*/
static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
zend_function *child, zend_class_entry *child_scope,
zend_function *parent, zend_class_entry *parent_scope,
zend_class_entry *ce, zval *child_zv,
- bool check_visibility, bool check_only, bool checked, bool force_mutable) /* {{{ */
+ bool check_visibility, bool check_only, bool checked) /* {{{ */
{
uint32_t child_flags;
uint32_t parent_flags = parent->common.fn_flags;
@@ -1095,6 +1095,12 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
if (UNEXPECTED((parent_flags & ZEND_ACC_PRIVATE) && !(parent_flags & ZEND_ACC_ABSTRACT) && !(parent_flags & ZEND_ACC_CTOR))) {
if (!check_only) {
+ if (child_scope != ce && child->type == ZEND_USER_FUNCTION && child_zv) {
+ /* op_array wasn't duplicated yet */
+ zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+ memcpy(new_function, child, sizeof(zend_op_array));
+ Z_PTR_P(child_zv) = child = new_function;
+ }
child->common.fn_flags |= ZEND_ACC_CHANGED;
}
/* The parent method is private and not an abstract so we don't need to check any inheritance rules */
@@ -1139,6 +1145,13 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
}
if (!check_only && (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED))) {
+ if (child_scope != ce && child->type == ZEND_USER_FUNCTION && child_zv) {
+ /* op_array wasn't duplicated yet */
+ zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+ memcpy(new_function, child, sizeof(zend_op_array));
+ Z_PTR_P(child_zv) = child = new_function;
+ child_zv = NULL;
+ }
child->common.fn_flags |= ZEND_ACC_CHANGED;
}
@@ -1154,17 +1167,18 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
parent = proto;
}
- if (!check_only && child->common.prototype != proto && child_zv) {
+ if (!check_only && child->common.prototype != proto) {
do {
- if (child->common.scope != ce && child->type == ZEND_USER_FUNCTION) {
+ if (child_scope != ce && child->type == ZEND_USER_FUNCTION) {
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
/* Few parent interfaces contain the same method */
break;
- } else {
+ } else if (child_zv) {
/* op_array wasn't duplicated yet */
zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
memcpy(new_function, child, sizeof(zend_op_array));
Z_PTR_P(child_zv) = child = new_function;
+ child_zv = NULL;
}
}
child->common.prototype = proto;
@@ -1189,7 +1203,14 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
perform_delayable_implementation_check(ce, child, child_scope, parent, parent_scope);
}
- if (!check_only && (child->common.scope == ce || force_mutable)) {
+ if (!check_only && (child->common.fn_flags & ZEND_ACC_OVERRIDE)) {
+ if (child_scope != ce && child->type == ZEND_USER_FUNCTION && child_zv) {
+ /* op_array wasn't duplicated yet */
+ zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+ memcpy(new_function, child, sizeof(zend_op_array));
+ Z_PTR_P(child_zv) = child = new_function;
+ child_zv = NULL;
+ }
child->common.fn_flags &= ~ZEND_ACC_OVERRIDE;
}
@@ -1202,7 +1223,7 @@ static zend_never_inline void do_inheritance_check_on_method(
zend_function *parent, zend_class_entry *parent_scope,
zend_class_entry *ce, zval *child_zv, bool check_visibility)
{
- do_inheritance_check_on_method_ex(child, child_scope, parent, parent_scope, ce, child_zv, check_visibility, 0, 0, /* force_mutable */ false);
+ do_inheritance_check_on_method_ex(child, child_scope, parent, parent_scope, ce, child_zv, check_visibility, 0, 0);
}
static zend_always_inline void do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce, bool is_interface, bool checked) /* {{{ */
@@ -1220,7 +1241,7 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function
if (checked) {
do_inheritance_check_on_method_ex(
func, func->common.scope, parent, parent->common.scope, ce, child,
- /* check_visibility */ 1, 0, checked, /* force_mutable */ false);
+ /* check_visibility */ 1, 0, checked);
} else {
do_inheritance_check_on_method(
func, func->common.scope, parent, parent->common.scope, ce, child,
@@ -1948,11 +1969,13 @@ static zend_class_entry *fixup_trait_scope(const zend_function *fn, zend_class_e
static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_string *key, zend_function *fn) /* {{{ */
{
+ zval *existing_zv = zend_hash_find(&ce->function_table, key);
zend_function *existing_fn = NULL;
zend_function *new_fn;
bool check_inheritance = false;
- if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) {
+ if (existing_zv != NULL) {
+ existing_fn = Z_PTR_P(existing_zv);
/* if it is the same function with the same visibility and has not been assigned a class scope yet, regardless
* of where it is coming from there is no conflict and we do not need to add it again */
if (existing_fn->op_array.opcodes == fn->op_array.opcodes &&
@@ -1970,7 +1993,7 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
*/
do_inheritance_check_on_method(
existing_fn, fixup_trait_scope(existing_fn, ce), fn, fixup_trait_scope(fn, ce),
- ce, NULL, /* check_visibility */ 0);
+ ce, existing_zv, /* check_visibility */ 0);
return;
}
@@ -2011,7 +2034,7 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
* Check whether the trait method fulfills the inheritance requirements. */
do_inheritance_check_on_method_ex(
fn, fixup_trait_scope(fn, ce), existing_fn, fixup_trait_scope(existing_fn, ce),
- ce, NULL, /* check_visibility */ 1, false, false, /* force_mutable */ true);
+ ce, NULL, /* check_visibility */ 1, false, false);
}
}
/* }}} */
@@ -3267,7 +3290,7 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
do_inheritance_check_on_method_ex(
child_func, child_func->common.scope,
parent_func, parent_func->common.scope,
- ce, NULL, /* check_visibility */ 1, 1, 0, /* force_mutable */ false);
+ ce, NULL, /* check_visibility */ 1, 1, 0);
if (UNEXPECTED(status == INHERITANCE_WARNING)) {
overall_status = INHERITANCE_WARNING;
} else if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment