From 0d7cde8034b8805dcd9d562130c56f300b34ea25 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 23 Nov 2024 02:19:59 +0100 Subject: [PATCH 1/7] compare by value --- Zend/tests/data_class_001.phpt | 30 +++++++++++++++++++++++++ Zend/tests/data_class_002.phpt | 29 ++++++++++++++++++++++++ Zend/tests/data_class_003.phpt | 17 ++++++++++++++ Zend/tests/data_class_004.phpt | 21 +++++++++++++++++ Zend/zend_ast.c | 3 +++ Zend/zend_attributes.c | 5 +++++ Zend/zend_compile.c | 10 +++++++++ Zend/zend_compile.h | 5 ++++- Zend/zend_inheritance.c | 7 ++++++ Zend/zend_language_parser.y | 4 +++- Zend/zend_language_scanner.l | 4 ++++ Zend/zend_operators.c | 4 ++++ ext/reflection/php_reflection.c | 14 +++++++++++- ext/reflection/php_reflection.stub.php | 2 ++ ext/reflection/php_reflection_arginfo.h | 6 ++++- ext/standard/var.c | 4 ++++ ext/tokenizer/tokenizer_data.c | 1 + ext/tokenizer/tokenizer_data.stub.php | 5 +++++ ext/tokenizer/tokenizer_data_arginfo.h | 3 ++- 19 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 Zend/tests/data_class_001.phpt create mode 100644 Zend/tests/data_class_002.phpt create mode 100644 Zend/tests/data_class_003.phpt create mode 100644 Zend/tests/data_class_004.phpt diff --git a/Zend/tests/data_class_001.phpt b/Zend/tests/data_class_001.phpt new file mode 100644 index 0000000000000..92a9801a1ec99 --- /dev/null +++ b/Zend/tests/data_class_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +Data classes can be defined and compared. +--FILE-- + +--EXPECT-- +bool(true) +bool(false) +bool(false) +bool(true) +data object(Point)#1 (2) { + ["x"]=> + int(1) + ["y"]=> + int(2) +} diff --git a/Zend/tests/data_class_002.phpt b/Zend/tests/data_class_002.phpt new file mode 100644 index 0000000000000..848f49519349d --- /dev/null +++ b/Zend/tests/data_class_002.phpt @@ -0,0 +1,29 @@ +--TEST-- +Data classes can be combined with inheritance and other features. +--FILE-- + +--EXPECT-- +bool(true) +bool(false) +bool(false) +bool(true) diff --git a/Zend/tests/data_class_003.phpt b/Zend/tests/data_class_003.phpt new file mode 100644 index 0000000000000..676e01976fddd --- /dev/null +++ b/Zend/tests/data_class_003.phpt @@ -0,0 +1,17 @@ +--TEST-- +Data class inheritance rules are enforced. +--FILE-- + +--EXPECTF-- +Fatal error: Data class Point3D cannot extend non-data class Point in %s on line %d diff --git a/Zend/tests/data_class_004.phpt b/Zend/tests/data_class_004.phpt new file mode 100644 index 0000000000000..dcfc2ce1717e6 --- /dev/null +++ b/Zend/tests/data_class_004.phpt @@ -0,0 +1,21 @@ +--TEST-- +Reflection should work +--FILE-- +isDataClass()); +var_dump($bar->isDataClass()); +?> +--EXPECT-- +bool(false) +bool(true) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index d33747412ef0a..de2c67e7ad7da 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1898,6 +1898,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio if (decl->flags & ZEND_ACC_READONLY_CLASS) { smart_str_appends(str, "readonly "); } + if (decl->flags & ZEND_ACC_DATA_CLASS) { + smart_str_appends(str, "data "); + } smart_str_appends(str, "class "); } smart_str_appendl(str, ZSTR_VAL(decl->name), ZSTR_LEN(decl->name)); diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index d7bcb1f54e889..7009049780529 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -86,6 +86,11 @@ static void validate_allow_dynamic_properties( ZSTR_VAL(scope->name) ); } + if (scope->ce_flags & ZEND_ACC_DATA_CLASS) { + zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to data class %s", + ZSTR_VAL(scope->name) + ); + } if (scope->ce_flags & ZEND_ACC_ENUM) { zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to enum %s", ZSTR_VAL(scope->name) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7e5351ddfafdb..130659fe2cb1c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -862,6 +862,8 @@ static const char *zend_modifier_token_to_string(uint32_t token) return "final"; case T_READONLY: return "readonly"; + case T_DATA: + return "data"; case T_ABSTRACT: return "abstract"; case T_PUBLIC_SET: @@ -995,6 +997,10 @@ uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */ zend_throw_exception(zend_ce_compile_error, "Multiple readonly modifiers are not allowed", 0); return 0; } + if ((flags & ZEND_ACC_DATA_CLASS) && (new_flag & ZEND_ACC_DATA_CLASS)) { + zend_throw_exception(zend_ce_compile_error, "Multiple data modifiers are not allowed", 0); + return 0; + } if ((new_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flags & ZEND_ACC_FINAL)) { zend_throw_exception(zend_ce_compile_error, "Cannot use the final modifier on an abstract class", 0); @@ -1020,6 +1026,10 @@ uint32_t zend_add_anonymous_class_modifier(uint32_t flags, uint32_t new_flag) zend_throw_exception(zend_ce_compile_error, "Multiple readonly modifiers are not allowed", 0); return 0; } + if ((flags & ZEND_ACC_DATA_CLASS) && (new_flag & ZEND_ACC_DATA_CLASS)) { + zend_throw_exception(zend_ce_compile_error, "Multiple data modifiers are not allowed", 0); + return 0; + } return new_flags; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 1eaf3ef686e79..ee46f742a1168 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -267,7 +267,7 @@ typedef struct _zend_oparray_context { #define ZEND_ACC_PROTECTED_SET (1 << 11) /* | | X | */ #define ZEND_ACC_PRIVATE_SET (1 << 12) /* | | X | */ /* | | | */ -/* Class Flags (unused: 30,31) | | | */ +/* Class Flags (unused: 31) | | | */ /* =========== | | | */ /* | | | */ /* Special class types | | | */ @@ -333,6 +333,9 @@ typedef struct _zend_oparray_context { /* Class cannot be serialized or unserialized | | | */ #define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */ /* | | | */ +/* Class is compared by value | | | */ +#define ZEND_ACC_DATA_CLASS (1 << 30) /* X | | | */ +/* | | | */ /* Function Flags (unused: 29-30) | | | */ /* ============== | | | */ /* | | | */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d85bfeb6ae2ab..91102364d8e35 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1783,6 +1783,13 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par ); } + if (UNEXPECTED((ce->ce_flags & ZEND_ACC_DATA_CLASS) != (parent_ce->ce_flags & ZEND_ACC_DATA_CLASS))) { + zend_error_noreturn(E_COMPILE_ERROR, "%s class %s cannot extend %s class %s", + ce->ce_flags & ZEND_ACC_DATA_CLASS ? "Data" : "Non-data", ZSTR_VAL(ce->name), + parent_ce->ce_flags & ZEND_ACC_DATA_CLASS ? "data" : "non-data", ZSTR_VAL(parent_ce->name) + ); + } + if (ce->parent_name) { zend_string_release_ex(ce->parent_name, 0); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index d2a29e670d8bf..9c1adc4186e7f 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -158,6 +158,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_PROTECTED_SET "'protected(set)'" %token T_PUBLIC_SET "'public(set)'" %token T_READONLY "'readonly'" +%token T_DATA "'data'" %token T_VAR "'var'" %token T_UNSET "'unset'" %token T_ISSET "'isset'" @@ -314,7 +315,7 @@ reserved_non_modifiers: semi_reserved: reserved_non_modifiers - | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY + | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY | T_DATA ; ampersand: @@ -624,6 +625,7 @@ class_modifier: T_ABSTRACT { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } | T_FINAL { $$ = ZEND_ACC_FINAL; } | T_READONLY { $$ = ZEND_ACC_READONLY_CLASS|ZEND_ACC_NO_DYNAMIC_PROPERTIES; } + | T_DATA { $$ = ZEND_ACC_DATA_CLASS; } ; trait_declaration_statement: diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 7ae73875926eb..6bb8ea887a189 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1745,6 +1745,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_READONLY); } +"data" { + RETURN_TOKEN_WITH_IDENT(T_DATA); +} + "unset" { RETURN_TOKEN_WITH_IDENT(T_UNSET); } diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index c2fbc0ee110c9..d20bb803240ac 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2409,6 +2409,10 @@ ZEND_API bool ZEND_FASTCALL zend_is_identical(const zval *op1, const zval *op2) return (Z_ARRVAL_P(op1) == Z_ARRVAL_P(op2) || zend_hash_compare(Z_ARRVAL_P(op1), Z_ARRVAL_P(op2), (compare_func_t) hash_zval_identical_function, 1) == 0); case IS_OBJECT: + // check the class entry for a ACC_DATA_CLASS flag + if (Z_OBJ_P(op1)->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + return !Z_OBJ_HANDLER_P(op1, compare)((zval *)op1, (zval *)op2); + } return (Z_OBJ_P(op1) == Z_OBJ_P(op2)); default: return 0; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ecfa20fe0c0c8..f8514f3d4ab42 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -354,6 +354,9 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const if (ce->ce_flags & ZEND_ACC_READONLY_CLASS) { smart_str_append_printf(str, "readonly "); } + if (ce->ce_flags & ZEND_ACC_DATA_CLASS) { + smart_str_append_printf(str, "data "); + } smart_str_append_printf(str, "class "); } smart_str_append_printf(str, "%s", ZSTR_VAL(ce->name)); @@ -1622,6 +1625,10 @@ ZEND_METHOD(Reflection, getModifierNames) if (modifiers & (ZEND_ACC_READONLY | ZEND_ACC_READONLY_CLASS)) { add_next_index_stringl(return_value, "readonly", sizeof("readonly")-1); } + + if (modifiers & ZEND_ACC_DATA_CLASS) { + add_next_index_stringl(return_value, "data", sizeof("data")-1); + } } /* }}} */ @@ -4877,6 +4884,11 @@ ZEND_METHOD(ReflectionClass, isReadOnly) _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_READONLY_CLASS); } +ZEND_METHOD(ReflectionClass, isDataClass) +{ + _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_DATA_CLASS); +} + /* {{{ Returns whether this class is abstract */ ZEND_METHOD(ReflectionClass, isAbstract) { @@ -4889,7 +4901,7 @@ ZEND_METHOD(ReflectionClass, getModifiers) { reflection_object *intern; zend_class_entry *ce; - uint32_t keep_flags = ZEND_ACC_FINAL | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_READONLY_CLASS; + uint32_t keep_flags = ZEND_ACC_FINAL | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_READONLY_CLASS | ZEND_ACC_DATA_CLASS; ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index be511d7ee14cd..6397d76d58428 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -355,6 +355,8 @@ public function isFinal(): bool {} public function isReadOnly(): bool {} + public function isDataClass(): bool {} + /** @tentative-return-type */ public function getModifiers(): int {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index d78a685dde9c9..f1d09f285cddb 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3c6be99bb36965139464925a618cb0bf03affa62 */ + * Stub hash: 4877c4510cf80bbfe4d39fd671b84e0663b65773 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -272,6 +272,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_isReadOnly arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClass_isDataClass arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + #define arginfo_class_ReflectionClass_getModifiers arginfo_class_ReflectionFunctionAbstract_getNumberOfParameters ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionClass_isInstance, 0, 1, _IS_BOOL, 0) @@ -820,6 +822,7 @@ ZEND_METHOD(ReflectionClass, isEnum); ZEND_METHOD(ReflectionClass, isAbstract); ZEND_METHOD(ReflectionClass, isFinal); ZEND_METHOD(ReflectionClass, isReadOnly); +ZEND_METHOD(ReflectionClass, isDataClass); ZEND_METHOD(ReflectionClass, getModifiers); ZEND_METHOD(ReflectionClass, isInstance); ZEND_METHOD(ReflectionClass, newInstance); @@ -1111,6 +1114,7 @@ static const zend_function_entry class_ReflectionClass_methods[] = { ZEND_ME(ReflectionClass, isAbstract, arginfo_class_ReflectionClass_isAbstract, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isFinal, arginfo_class_ReflectionClass_isFinal, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isReadOnly, arginfo_class_ReflectionClass_isReadOnly, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, isDataClass, arginfo_class_ReflectionClass_isDataClass, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getModifiers, arginfo_class_ReflectionClass_getModifiers, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, isInstance, arginfo_class_ReflectionClass_isInstance, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, newInstance, arginfo_class_ReflectionClass_newInstance, ZEND_ACC_PUBLIC) diff --git a/ext/standard/var.c b/ext/standard/var.c index 1c2b0eb164a1c..14b8c405326a6 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -89,6 +89,10 @@ static void php_object_property_dump(zend_property_info *prop_info, zval *zv, ze /* }}} */ static const char *php_var_dump_object_prefix(zend_object *obj) { + if (obj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + return "data "; + } + if (EXPECTED(!zend_object_is_lazy(obj))) { return ""; } diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index a046ab50e1498..7aa58ee1d4ada 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -96,6 +96,7 @@ char *get_token_type_name(int token_type) case T_PROTECTED_SET: return "T_PROTECTED_SET"; case T_PUBLIC_SET: return "T_PUBLIC_SET"; case T_READONLY: return "T_READONLY"; + case T_DATA: return "T_DATA"; case T_VAR: return "T_VAR"; case T_UNSET: return "T_UNSET"; case T_ISSET: return "T_ISSET"; diff --git a/ext/tokenizer/tokenizer_data.stub.php b/ext/tokenizer/tokenizer_data.stub.php index 45f3c89f2de3a..fa9adfbc69ded 100644 --- a/ext/tokenizer/tokenizer_data.stub.php +++ b/ext/tokenizer/tokenizer_data.stub.php @@ -357,6 +357,11 @@ * @cvalue T_READONLY */ const T_READONLY = UNKNOWN; +/** + * @var int + * @cvalue T_DATA + */ +const T_DATA = UNKNOWN; /** * @var int * @cvalue T_VAR diff --git a/ext/tokenizer/tokenizer_data_arginfo.h b/ext/tokenizer/tokenizer_data_arginfo.h index 61f6ac1ec3659..8f5298f4493f9 100644 --- a/ext/tokenizer/tokenizer_data_arginfo.h +++ b/ext/tokenizer/tokenizer_data_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d917cab61a2b436a16d2227cdb438add45e42d69 */ + * Stub hash: dc57c500646c3ce4426e8a5821297d24381e11cf */ static void register_tokenizer_data_symbols(int module_number) { @@ -74,6 +74,7 @@ static void register_tokenizer_data_symbols(int module_number) REGISTER_LONG_CONSTANT("T_PROTECTED_SET", T_PROTECTED_SET, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_PUBLIC_SET", T_PUBLIC_SET, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_READONLY", T_READONLY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_DATA", T_DATA, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_VAR", T_VAR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_UNSET", T_UNSET, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_ISSET", T_ISSET, CONST_PERSISTENT); From 103628e22b62af117269eb1941b4b4dc31a1395f Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 23 Nov 2024 05:43:39 +0100 Subject: [PATCH 2/7] implement CoW semantics --- Zend/tests/data_class_005.phpt | 52 ++++++++++++++++++++++++++++++++ Zend/zend_vm_def.h | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 Zend/tests/data_class_005.phpt diff --git a/Zend/tests/data_class_005.phpt b/Zend/tests/data_class_005.phpt new file mode 100644 index 0000000000000..14d27fec3606a --- /dev/null +++ b/Zend/tests/data_class_005.phpt @@ -0,0 +1,52 @@ +--TEST-- +Copy on write +--FILE-- +x += $other->x; + $this->y += $other->y; + return $this; + } + public function __toString(): string { + return "({$this->x},{$this->y})"; + } +} + +$p1 = new Point(1, 2); +$p2 = $p1; + +// p2 is copy on write (3,2) +$p2->x = 3; +echo "p1: $p1\n"; +echo "p2 (x+3): $p2\n"; + +echo "p1->add(p2): " . $p1->add($p2) . "\n"; +echo "p1: $p1\n"; +$po = new Point(1,1); +$p2 = $po; +$p2->x++; +echo "p2: $p2\n"; +echo "po: $po\n"; + +function modify(Point $p): Point { + $p->y++; + return $p; +} + +$p1 = new Point(1,1); +$p2 = modify($p1); +echo "p1: $p1\n"; +echo "p2: $p2\n"; + +--EXPECT-- +p1: (1,2) +p2 (x+3): (3,2) +p1->add(p2): (4,4) +p1: (1,2) +p2: (2,1) +po: (1,1) +p1: (1,1) +p2: (1,2) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7e471b5acd8b6..84f54e2cfebb8 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1041,6 +1041,19 @@ ZEND_VM_HANDLER(28, ZEND_ASSIGN_OBJ_OP, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, OP) ZEND_VM_C_LABEL(assign_op_object): /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (OP2_TYPE == IS_CONST) { name = Z_STR_P(property); } else { @@ -1310,6 +1323,19 @@ ZEND_VM_HANDLER(132, ZEND_PRE_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACH ZEND_VM_C_LABEL(pre_incdec_object): /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (OP2_TYPE == IS_CONST) { name = Z_STR_P(property); } else { @@ -1380,6 +1406,19 @@ ZEND_VM_HANDLER(134, ZEND_POST_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CAC ZEND_VM_C_LABEL(post_incdec_object): /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (OP2_TYPE == IS_CONST) { name = Z_STR_P(property); } else { @@ -2560,6 +2599,22 @@ ZEND_VM_C_LABEL(fast_assign_obj): ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (OP2_TYPE != IS_CONST) { From 23e0ec9a882ba9c84f229f04b7526a806472bcf5 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 23 Nov 2024 10:21:04 +0100 Subject: [PATCH 3/7] squash --- Zend/zend_vm_execute.h | 927 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 927 insertions(+) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9209399a5cdbf..4d4a51e6002d9 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -23548,6 +23548,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CONST_H assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -23768,6 +23781,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CONST_HAN pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -23832,6 +23858,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CONST_HA post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -24151,6 +24190,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -24305,6 +24360,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -24459,6 +24530,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -24613,6 +24700,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -26534,6 +26637,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_TMPVAR_ assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -26756,6 +26872,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HA pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -26821,6 +26950,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_H post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -27142,6 +27284,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -27296,6 +27454,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -27450,6 +27624,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -27604,6 +27794,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -30892,6 +31098,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV_HAND assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -31112,6 +31331,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CV_HANDLE pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -31176,6 +31408,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CV_HANDL post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -31495,6 +31740,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -31649,6 +31910,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -31803,6 +32080,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -31957,6 +32250,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -33561,6 +33870,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONS assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -33651,6 +33973,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CONST_ pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -33715,6 +34050,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CONST post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -34242,6 +34590,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -34396,6 +34760,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -34550,6 +34930,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -34704,6 +35100,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -35732,6 +36144,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_TMPV assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -35822,6 +36247,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -35887,6 +36325,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVA post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -36410,6 +36861,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -36564,6 +37031,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -36718,6 +37201,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -36872,6 +37371,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -38380,6 +38895,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV_H assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -38470,6 +38998,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CV_HAN pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -38534,6 +39075,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CV_HA post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -39056,6 +39610,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -39210,6 +39780,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -39364,6 +39950,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -39518,6 +40120,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -42533,6 +43151,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CONST_HA assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -42753,6 +43384,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CONST_HAND pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -42817,6 +43461,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CONST_HAN post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CONST == IS_CONST) { name = Z_STR_P(property); } else { @@ -43456,6 +44113,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -43610,6 +44283,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -43764,6 +44453,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -43918,6 +44623,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CONST != IS_CONST) { @@ -46486,6 +47207,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_TMPVAR_H assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -46708,6 +47442,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HAN pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -46773,6 +47520,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HA post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { name = Z_STR_P(property); } else { @@ -47408,6 +48168,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -47562,6 +48338,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -47716,6 +48508,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -47870,6 +48678,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -51989,6 +52813,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CV_HANDL assign_op_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -52209,6 +53046,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CV_HANDLER pre_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -52273,6 +53123,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CV_HANDLE post_incdec_object: /* here we are sure we are dealing with an object */ zobj = Z_OBJ_P(object); + + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + if (IS_CV == IS_CONST) { name = Z_STR_P(property); } else { @@ -52907,6 +53770,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -53061,6 +53940,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -53215,6 +54110,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { @@ -53369,6 +54280,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ ZVAL_DEREF(value); } + // if this is a data class, we may need to CoW + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + // skip if in a constructor + if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { + // skip + } else + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(object, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + value = zobj->handlers->write_property(zobj, name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL); if (IS_CV != IS_CONST) { From c18ea453e9697ddf39420d0771fecddad517a8eb Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 23 Nov 2024 11:02:06 +0100 Subject: [PATCH 4/7] add a convoluted test --- Zend/tests/data_class_006.phpt | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Zend/tests/data_class_006.phpt diff --git a/Zend/tests/data_class_006.phpt b/Zend/tests/data_class_006.phpt new file mode 100644 index 0000000000000..cd9b5386f3b64 --- /dev/null +++ b/Zend/tests/data_class_006.phpt @@ -0,0 +1,42 @@ +--TEST-- +A convoluted path +--FILE-- +x = $x; + $this->l = new Length($x + $y); + echo "$previous === $this\n"; + } + + public function addNoisily(Point $other): Point { + echo "Adding $this and $other\n"; + $previous = $this; + $this->x += $other->x; + $this->y += $other->y; + $this->l = new Length($this->x + $this->y); + echo "$previous !== $this\n"; + return $this; + } + + public function __toString(): string { + return "({$this->x},{$this->y}|{$this->l->length})"; + } +} + +$p1 = new Point(1, 2); +$p2 = $p1->addNoisily($p1); + +?> +--EXPECT-- +(1,2|3) === (1,2|3) +Adding (1,2|3) and (1,2|3) +(1,2|3) !== (2,4|6) From 349db0e795d20a39cfe5a3ac290d872fcdb54a30 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 23 Nov 2024 12:16:47 +0100 Subject: [PATCH 5/7] add unset support and more tests --- Zend/tests/data_class_007.phpt | 18 +++++ Zend/tests/data_class_008.phpt | 16 +++++ Zend/tests/data_class_009.phpt | 20 ++++++ Zend/zend_vm_def.h | 14 +++- Zend/zend_vm_execute.h | 126 ++++++++++++++++++++++++++++++--- 5 files changed, 184 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/data_class_007.phpt create mode 100644 Zend/tests/data_class_008.phpt create mode 100644 Zend/tests/data_class_009.phpt diff --git a/Zend/tests/data_class_007.phpt b/Zend/tests/data_class_007.phpt new file mode 100644 index 0000000000000..e725f93ffc202 --- /dev/null +++ b/Zend/tests/data_class_007.phpt @@ -0,0 +1,18 @@ +--TEST-- +Private properties are included in equality checks +--FILE-- + +--EXPECT-- +bool(true) +bool(true) diff --git a/Zend/tests/data_class_008.phpt b/Zend/tests/data_class_008.phpt new file mode 100644 index 0000000000000..011c13ea08c37 --- /dev/null +++ b/Zend/tests/data_class_008.phpt @@ -0,0 +1,16 @@ +--TEST-- +Data classes can be anonymous +--FILE-- + +--EXPECT-- +data object(class@anonymous)#1 (1) { + ["value":"class@anonymous":private]=> + int(1) +} diff --git a/Zend/tests/data_class_009.phpt b/Zend/tests/data_class_009.phpt new file mode 100644 index 0000000000000..9975da8027adf --- /dev/null +++ b/Zend/tests/data_class_009.phpt @@ -0,0 +1,20 @@ +--TEST-- +unset triggers copy-on-write +--FILE-- +value); +var_dump($data !== $copy); +var_dump($data); +?> +--EXPECT-- +bool(true) +data object(class@anonymous)#1 (1) { + ["value"]=> + int(1) +} diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 84f54e2cfebb8..636e0b546876c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6863,7 +6863,19 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_S break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (OP2_TYPE != IS_CONST) { zend_tmp_string_release(tmp_name); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 4d4a51e6002d9..f7820600cc994 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -26273,7 +26273,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CONST_HANDL break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CONST != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -28892,7 +28904,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HAND break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -33443,7 +33467,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CV_HANDLER( break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CV != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -35803,7 +35839,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HA break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CONST != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -37902,7 +37950,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_H break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -40650,7 +40710,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDL break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CV != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -46064,7 +46136,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLE break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CONST != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -49921,7 +50005,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDL break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { zend_tmp_string_release(tmp_name); } @@ -55618,7 +55714,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER(Z break; } } - Z_OBJ_HT_P(container)->unset_property(Z_OBJ_P(container), name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); + // if this is a data class, we may need to CoW + zend_object *zobj = Z_OBJ_P(container); + if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { + if (GC_REFCOUNT(zobj) > 1) { + // clone the object + zend_object *new_obj = zend_objects_clone_obj(zobj); + // set the object zval to the new object + ZVAL_OBJ(container, new_obj); + GC_DELREF(zobj); + zobj = new_obj; + } + } + Z_OBJ_HT_P(container)->unset_property(zobj, name, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL)); if (IS_CV != IS_CONST) { zend_tmp_string_release(tmp_name); } From f90b45546cb2f9597719bdcf56398d574daab06e Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 23 Nov 2024 12:20:47 +0100 Subject: [PATCH 6/7] ensure we always skip CoW in constructors --- Zend/zend_vm_def.h | 19 +-- Zend/zend_vm_execute.h | 360 +++++++++++++++++------------------------ 2 files changed, 154 insertions(+), 225 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 636e0b546876c..67db052e6493e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1044,7 +1044,8 @@ ZEND_VM_C_LABEL(assign_op_object): // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -1326,7 +1327,8 @@ ZEND_VM_C_LABEL(pre_incdec_object): // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -1409,7 +1411,8 @@ ZEND_VM_C_LABEL(post_incdec_object): // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -2601,11 +2604,8 @@ ZEND_VM_C_LABEL(fast_assign_obj): // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -6866,7 +6866,8 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_S // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index f7820600cc994..7848323925781 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -23551,7 +23551,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CONST_H // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -23784,7 +23785,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CONST_HAN // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -23861,7 +23863,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CONST_HA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -24192,11 +24195,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -24362,11 +24362,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -24532,11 +24529,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -24702,11 +24696,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -26276,7 +26267,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CONST_HANDL // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -26652,7 +26644,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_TMPVAR_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -26887,7 +26880,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -26965,7 +26959,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_H // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -27298,11 +27293,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -27468,11 +27460,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -27638,11 +27627,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -27808,11 +27794,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -28907,7 +28890,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HAND // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -31125,7 +31109,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV_HAND // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -31358,7 +31343,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CV_HANDLE // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -31435,7 +31421,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CV_HANDL // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -31766,11 +31753,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -31936,11 +31920,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -32106,11 +32087,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -32276,11 +32254,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -33470,7 +33445,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CV_HANDLER( // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -33909,7 +33885,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONS // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -34012,7 +33989,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CONST_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -34089,7 +34067,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CONST // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -34628,11 +34607,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -34798,11 +34774,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -34968,11 +34941,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -35138,11 +35108,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_O // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -35842,7 +35809,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HA // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -36195,7 +36163,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_TMPV // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -36298,7 +36267,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -36376,7 +36346,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -36911,11 +36882,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -37081,11 +37049,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -37251,11 +37216,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -37421,11 +37383,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -37953,7 +37912,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_H // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -38958,7 +38918,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV_H // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -39061,7 +39022,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CV_HAN // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -39138,7 +39100,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CV_HA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -39672,11 +39635,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -39842,11 +39802,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -40012,11 +39969,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -40182,11 +40136,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -40713,7 +40664,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDL // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -43226,7 +43178,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CONST_HA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -43459,7 +43412,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CONST_HAND // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -43536,7 +43490,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CONST_HAN // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -44187,11 +44142,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -44357,11 +44309,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -44527,11 +44476,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -44697,11 +44643,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -46139,7 +46082,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLE // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -47294,7 +47238,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_TMPVAR_H // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -47529,7 +47474,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HAN // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -47607,7 +47553,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HA // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -48254,11 +48201,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -48424,11 +48368,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -48594,11 +48535,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -48764,11 +48702,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_D // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -50008,7 +49943,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDL // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -52912,7 +52848,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_CV_CV_HANDL // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -53145,7 +53082,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CV_HANDLER // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -53222,7 +53160,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CV_HANDLE // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -53868,11 +53807,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -54038,11 +53974,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -54208,11 +54141,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -54378,11 +54308,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_ // if this is a data class, we may need to CoW if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - // skip if in a constructor - if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) { - // skip - } else - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object @@ -55717,7 +55644,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER(Z // if this is a data class, we may need to CoW zend_object *zobj = Z_OBJ_P(container); if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) { - if (GC_REFCOUNT(zobj) > 1) { + // skip if in a constructor or if the object is not shared + if (!(EX(func)->common.fn_flags & ZEND_ACC_CTOR) && GC_REFCOUNT(zobj) > 1) { // clone the object zend_object *new_obj = zend_objects_clone_obj(zobj); // set the object zval to the new object From 31ce62c6f7fe3410c3ac59de41e4f5e80fd54015 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 23 Nov 2024 13:12:22 +0100 Subject: [PATCH 7/7] add more tests --- Zend/tests/data_class_010.phpt | 18 +++++++++++++++++ Zend/tests/data_class_011.phpt | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 Zend/tests/data_class_010.phpt create mode 100644 Zend/tests/data_class_011.phpt diff --git a/Zend/tests/data_class_010.phpt b/Zend/tests/data_class_010.phpt new file mode 100644 index 0000000000000..6ca2f4cb139e6 --- /dev/null +++ b/Zend/tests/data_class_010.phpt @@ -0,0 +1,18 @@ +--TEST-- +Serialization works +--FILE-- + +--EXPECT-- +string(40) "O:5:"Point":2:{s:1:"x";i:1;s:1:"y";i:2;}" +bool(true) diff --git a/Zend/tests/data_class_011.phpt b/Zend/tests/data_class_011.phpt new file mode 100644 index 0000000000000..a7e11062d0383 --- /dev/null +++ b/Zend/tests/data_class_011.phpt @@ -0,0 +1,37 @@ +--TEST-- +Other features work with data classes +--FILE-- +length = sqrt($this->x ** 2 + $this->y ** 2); + } +} + +final readonly data class Point2D implements Point { + use PythagoreanTheorem; // contains implementation of $length + + public function __construct(public int $x, public int $y) { + $this->memoizeLength(); // from the trait + } + + public function add(Point $point): Point { + return new static($this->x + $point->x, $this->y + $point->y); + } +} + +$p1 = new Point2D(1, 1); +$p2 = new Point2D(2, 2); +$p3 = $p1->add($p2); +echo $p3->length, "\n"; +?> +--EXPECT-- +6