Skip to content

Commit 103628e

Browse files
committed
implement CoW semantics
1 parent 0d7cde8 commit 103628e

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

Zend/tests/data_class_005.phpt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--TEST--
2+
Copy on write
3+
--FILE--
4+
<?php
5+
6+
data class Point {
7+
public function __construct(public int $x, public int $y) {}
8+
public function add(Point $other): Point {
9+
$this->x += $other->x;
10+
$this->y += $other->y;
11+
return $this;
12+
}
13+
public function __toString(): string {
14+
return "({$this->x},{$this->y})";
15+
}
16+
}
17+
18+
$p1 = new Point(1, 2);
19+
$p2 = $p1;
20+
21+
// p2 is copy on write (3,2)
22+
$p2->x = 3;
23+
echo "p1: $p1\n";
24+
echo "p2 (x+3): $p2\n";
25+
26+
echo "p1->add(p2): " . $p1->add($p2) . "\n";
27+
echo "p1: $p1\n";
28+
$po = new Point(1,1);
29+
$p2 = $po;
30+
$p2->x++;
31+
echo "p2: $p2\n";
32+
echo "po: $po\n";
33+
34+
function modify(Point $p): Point {
35+
$p->y++;
36+
return $p;
37+
}
38+
39+
$p1 = new Point(1,1);
40+
$p2 = modify($p1);
41+
echo "p1: $p1\n";
42+
echo "p2: $p2\n";
43+
44+
--EXPECT--
45+
p1: (1,2)
46+
p2 (x+3): (3,2)
47+
p1->add(p2): (4,4)
48+
p1: (1,2)
49+
p2: (2,1)
50+
po: (1,1)
51+
p1: (1,1)
52+
p2: (1,2)

Zend/zend_vm_def.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,19 @@ ZEND_VM_HANDLER(28, ZEND_ASSIGN_OBJ_OP, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, OP)
10411041
ZEND_VM_C_LABEL(assign_op_object):
10421042
/* here we are sure we are dealing with an object */
10431043
zobj = Z_OBJ_P(object);
1044+
1045+
// if this is a data class, we may need to CoW
1046+
if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) {
1047+
if (GC_REFCOUNT(zobj) > 1) {
1048+
// clone the object
1049+
zend_object *new_obj = zend_objects_clone_obj(zobj);
1050+
// set the object zval to the new object
1051+
ZVAL_OBJ(object, new_obj);
1052+
GC_DELREF(zobj);
1053+
zobj = new_obj;
1054+
}
1055+
}
1056+
10441057
if (OP2_TYPE == IS_CONST) {
10451058
name = Z_STR_P(property);
10461059
} else {
@@ -1310,6 +1323,19 @@ ZEND_VM_HANDLER(132, ZEND_PRE_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACH
13101323
ZEND_VM_C_LABEL(pre_incdec_object):
13111324
/* here we are sure we are dealing with an object */
13121325
zobj = Z_OBJ_P(object);
1326+
1327+
// if this is a data class, we may need to CoW
1328+
if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) {
1329+
if (GC_REFCOUNT(zobj) > 1) {
1330+
// clone the object
1331+
zend_object *new_obj = zend_objects_clone_obj(zobj);
1332+
// set the object zval to the new object
1333+
ZVAL_OBJ(object, new_obj);
1334+
GC_DELREF(zobj);
1335+
zobj = new_obj;
1336+
}
1337+
}
1338+
13131339
if (OP2_TYPE == IS_CONST) {
13141340
name = Z_STR_P(property);
13151341
} else {
@@ -1380,6 +1406,19 @@ ZEND_VM_HANDLER(134, ZEND_POST_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CAC
13801406
ZEND_VM_C_LABEL(post_incdec_object):
13811407
/* here we are sure we are dealing with an object */
13821408
zobj = Z_OBJ_P(object);
1409+
1410+
// if this is a data class, we may need to CoW
1411+
if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) {
1412+
if (GC_REFCOUNT(zobj) > 1) {
1413+
// clone the object
1414+
zend_object *new_obj = zend_objects_clone_obj(zobj);
1415+
// set the object zval to the new object
1416+
ZVAL_OBJ(object, new_obj);
1417+
GC_DELREF(zobj);
1418+
zobj = new_obj;
1419+
}
1420+
}
1421+
13831422
if (OP2_TYPE == IS_CONST) {
13841423
name = Z_STR_P(property);
13851424
} else {
@@ -2560,6 +2599,22 @@ ZEND_VM_C_LABEL(fast_assign_obj):
25602599
ZVAL_DEREF(value);
25612600
}
25622601

2602+
// if this is a data class, we may need to CoW
2603+
if (zobj->ce->ce_flags & ZEND_ACC_DATA_CLASS) {
2604+
// skip if in a constructor
2605+
if (EX(func)->common.fn_flags & ZEND_ACC_CTOR) {
2606+
// skip
2607+
} else
2608+
if (GC_REFCOUNT(zobj) > 1) {
2609+
// clone the object
2610+
zend_object *new_obj = zend_objects_clone_obj(zobj);
2611+
// set the object zval to the new object
2612+
ZVAL_OBJ(object, new_obj);
2613+
GC_DELREF(zobj);
2614+
zobj = new_obj;
2615+
}
2616+
}
2617+
25632618
value = zobj->handlers->write_property(zobj, name, value, (OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL);
25642619

25652620
if (OP2_TYPE != IS_CONST) {

0 commit comments

Comments
 (0)