Fix lazy object foreach edge cases

- Fix foreach on a object whose initialization failed before
 - Do not allow to reset an object during iteration
This commit is contained in:
Arnaud Le Blanc 2024-09-19 17:13:09 +02:00
parent 5cc8db8c4b
commit 720ed56929
No known key found for this signature in database
GPG Key ID: 0098C05DD15ABC13
9 changed files with 484 additions and 59 deletions

View File

@ -0,0 +1,108 @@
--TEST--
Lazy Objects: resetAsLazy() during foreach() is not allowed
--FILE--
<?php
class NoHooks {
public int $a = 1;
}
class Hooks {
public int $a = 1;
public int $b { get { return 2; } }
}
function test(string $name, object $obj) {
printf("# %s\n", $name);
$reflector = new ReflectionClass($obj::class);
foreach ($obj as $key => $value) {
var_dump($key);
try {
$reflector->resetAsLazyProxy($obj, function () {});
} catch (Error $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}
}
}
$nohooks = new ReflectionClass(NoHooks::class);
$hooks = new ReflectionClass(Hooks::class);
$obj = $nohooks->newLazyGhost(function () {});
test('Ghost', $obj);
$obj = $hooks->newLazyGhost(function () {});
test('Ghost (hooks)', $obj);
$obj = $nohooks->newLazyProxy(function () {
return new NoHooks();
});
test('Proxy', $obj);
$obj = $hooks->newLazyProxy(function () {
return new Hooks();
});
test('Proxy', $obj);
$obj = new NoHooks();
test('Non lazy', $obj);
$obj = new Hooks();
test('Non lazy (hooks)', $obj);
$obj = new NoHooks();
ob_start();
var_dump($obj);
ob_end_clean();
test('Non lazy with ht', $obj);
$obj = new Hooks();
ob_start();
var_dump($obj);
ob_end_clean();
test('Non lazy with ht (hooks)', $obj);
?>
==DONE==
--EXPECT--
# Ghost
string(1) "a"
Error: Can not reset an object during property iteration
# Ghost (hooks)
string(1) "a"
Error: Can not reset an object during property iteration
string(1) "b"
Error: Can not reset an object during property iteration
# Proxy
string(1) "a"
Error: Can not reset an object during property iteration
# Proxy
string(1) "a"
Error: Can not reset an object during property iteration
string(1) "b"
Error: Can not reset an object during property iteration
# Non lazy
string(1) "a"
Error: Can not reset an object during property iteration
# Non lazy (hooks)
string(1) "a"
Error: Can not reset an object during property iteration
string(1) "b"
Error: Can not reset an object during property iteration
# Non lazy with ht
string(1) "a"
Error: Can not reset an object during property iteration
# Non lazy with ht (hooks)
string(1) "a"
Error: Can not reset an object during property iteration
string(1) "b"
Error: Can not reset an object during property iteration
==DONE==

View File

@ -5,9 +5,11 @@ Lazy objects: Foreach initializes object
class C {
public int $a;
public int $b;
public function __construct() {
var_dump(__METHOD__);
$this->a = 1;
$this->b = 2;
}
}
@ -24,6 +26,17 @@ foreach ($obj as $prop => $value) {
var_dump($prop, $value);
}
print "# Ghost (by ref):\n";
$obj = $reflector->newLazyGhost(function ($obj) {
var_dump("initializer");
$obj->__construct();
});
foreach ($obj as $prop => &$value) {
var_dump($prop, $value);
}
print "# Proxy:\n";
$obj = $reflector->newLazyProxy(function ($obj) {
@ -35,14 +48,110 @@ foreach ($obj as $prop => $value) {
var_dump($prop, $value);
}
--EXPECTF--
print "# Proxy (by ref):\n";
$obj = $reflector->newLazyProxy(function ($obj) {
var_dump("initializer");
return new C();
});
foreach ($obj as $prop => &$value) {
var_dump($prop, $value);
}
print "# Ghost (init failure)\n";
$fail = true;
$obj = $reflector->newLazyGhost(function ($obj) use (&$fail) {
if ($fail) {
throw new Exception("initializer");
} else {
var_dump("initializer");
$obj->__construct();
}
});
try {
foreach ($obj as $prop => $value) {
var_dump($prop, $value);
}
} catch (Exception $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}
$fail = false;
foreach ($obj as $prop => $value) {
var_dump($prop, $value);
}
print "# Ghost (init failure, by ref)\n";
$fail = true;
$obj = $reflector->newLazyGhost(function ($obj) use (&$fail) {
if ($fail) {
throw new Exception("initializer");
} else {
var_dump("initializer");
$obj->__construct();
}
});
try {
foreach ($obj as $prop => &$value) {
var_dump($prop, $value);
}
} catch (Exception $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}
$fail = false;
foreach ($obj as $prop => &$value) {
var_dump($prop, $value);
}
?>
--EXPECT--
# Ghost:
string(11) "initializer"
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)
# Ghost (by ref):
string(11) "initializer"
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)
# Proxy:
string(11) "initializer"
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)
# Proxy (by ref):
string(11) "initializer"
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)
# Ghost (init failure)
Exception: initializer
string(11) "initializer"
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)
# Ghost (init failure, by ref)
Exception: initializer
string(11) "initializer"
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)

View File

@ -6,49 +6,79 @@ Lazy objects: Foreach initializes object
#[AllowDynamicProperties]
class C {
public int $a;
private int $_b;
public int $b {
get { return $this->b; }
set(int $value) { $this->b = $value; }
&get { $ref = &$this->_b; return $ref; }
}
public int $c {
get { return $this->a + 2; }
}
public function __construct() {
public function __construct(bool $addDynamic = true) {
var_dump(__METHOD__);
$this->a = 1;
$this->b = 2;
$this->d = 4;
$this->_b = 2;
if ($addDynamic) {
$this->c = 3;
$this->d = 4;
unset($this->c);
}
}
}
$reflector = new ReflectionClass(C::class);
print "# Ghost:\n";
function test(string $name, object $obj) {
printf("# %s:\n", $name);
foreach ($obj as $prop => $value) {
var_dump($prop, $value);
}
foreach ($obj as $prop => &$value) {
var_dump($prop, $value);
}
}
$obj = $reflector->newLazyGhost(function ($obj) {
var_dump("initializer");
$obj->__construct();
});
foreach ($obj as $prop => $value) {
var_dump($prop, $value);
}
print "# Proxy:\n";
test('Ghost', $obj);
$obj = $reflector->newLazyProxy(function ($obj) {
var_dump("initializer");
return new C();
});
foreach ($obj as $prop => $value) {
var_dump($prop, $value);
}
test('Proxy', $obj);
$obj = $reflector->newLazyGhost(function ($obj) {
var_dump("initializer");
$obj->__construct(addDynamic: false);
});
test('Ghost (no dynamic)', $obj);
$obj = $reflector->newLazyProxy(function ($obj) {
var_dump("initializer");
return new C(addDynamic: false);
});
test('Proxy (no dynamic)', $obj);
print "# Proxy of proxy (initialization)\n";
$obj = $reflector->newLazyProxy(function ($obj) use (&$obj2, $reflector) {
var_dump("initializer");
return $obj2 = new C();
});
$reflector->initializeLazyObject($obj);
$reflector->resetAsLazyProxy($obj2, function () {
return new C();
});
test('Proxy of proxy', $obj);
print "# Ghost (init exception):\n";
$obj = $reflector->newLazyGhost(function ($obj) {
throw new \Exception();
throw new \Exception("initializer");
});
try {
@ -60,7 +90,7 @@ try {
print "# Proxy (init exception):\n";
$obj = $reflector->newLazyProxy(function ($obj) {
throw new \Exception();
throw new \Exception("initializer");
});
try {
@ -77,8 +107,12 @@ string(1) "a"
int(1)
string(1) "b"
int(2)
string(1) "c"
int(3)
string(1) "d"
int(4)
string(1) "a"
int(1)
string(1) "b"
int(2)
string(1) "d"
int(4)
# Proxy:
@ -88,9 +122,54 @@ string(1) "a"
int(1)
string(1) "b"
int(2)
string(1) "c"
int(3)
string(1) "d"
int(4)
string(1) "a"
int(1)
string(1) "b"
int(2)
string(1) "d"
int(4)
# Ghost (no dynamic):
string(11) "initializer"
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)
string(1) "a"
int(1)
string(1) "b"
int(2)
# Proxy (no dynamic):
string(11) "initializer"
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)
string(1) "a"
int(1)
string(1) "b"
int(2)
# Proxy of proxy (initialization)
string(11) "initializer"
string(14) "C::__construct"
# Proxy of proxy:
string(14) "C::__construct"
string(1) "a"
int(1)
string(1) "b"
int(2)
string(1) "d"
int(4)
string(1) "a"
int(1)
string(1) "b"
int(2)
string(1) "d"
int(4)
# Ghost (init exception):
Exception:
Exception: initializer
# Proxy (init exception):
Exception:
Exception: initializer

View File

@ -47,7 +47,7 @@ var_dump($obj);
--EXPECTF--
# Ghost:
string(18) "Canary::__destruct"
lazy ghost object(C)#%d (0) {
lazy ghost object(C)#%d (%d) {
}
string(11) "initializer"
object(Canary)#%d (0) {
@ -62,7 +62,7 @@ object(C)#%d (2) {
# Proxy:
string(18) "Canary::__destruct"
string(18) "Canary::__destruct"
lazy proxy object(C)#%d (0) {
lazy proxy object(C)#%d (%d) {
}
string(11) "initializer"
object(Canary)#%d (0) {

View File

@ -23,7 +23,9 @@ class ByRef {
}
}
public function __construct() {
$this->undef = 'dynamic';
$this->dynamic = 'dynamic';
unset($this->undef);
}
}

View File

@ -199,6 +199,27 @@ ZEND_API bool zend_class_can_be_lazy(zend_class_entry *ce)
return true;
}
static int zlo_hash_remove_dyn_props_func(zval *pDest)
{
if (Z_TYPE_P(pDest) == IS_INDIRECT) {
return ZEND_HASH_APPLY_STOP;
}
return ZEND_HASH_APPLY_REMOVE;
}
static bool zlo_is_iterating(zend_object *object)
{
if (object->properties && object->properties->u.v.nIteratorsCount) {
return true;
}
if (zend_object_is_lazy_proxy(object)
&& zend_lazy_object_initialized(object)) {
return zlo_is_iterating(zend_lazy_object_get_instance(object));
}
return false;
}
/* Make object 'obj' lazy. If 'obj' is NULL, create a lazy instance of
* class 'reflection_ce' */
ZEND_API zend_object *zend_object_make_lazy(zend_object *obj,
@ -251,6 +272,10 @@ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj,
}
}
} else {
if (zlo_is_iterating(obj)) {
zend_throw_error(NULL, "Can not reset an object during property iteration");
return NULL;
}
if (zend_object_is_lazy(obj)) {
ZEND_ASSERT(zend_object_is_lazy_proxy(obj) && zend_lazy_object_initialized(obj));
OBJ_EXTRA_FLAGS(obj) &= ~(IS_OBJ_LAZY_UNINITIALIZED|IS_OBJ_LAZY_PROXY);
@ -278,9 +303,17 @@ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj,
GC_DEL_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
/* unset() dynamic properties */
zend_object_dtor_dynamic_properties(obj);
obj->properties = NULL;
/* unset() dynamic properties. Do not NULL out obj->properties, as this
* would be unexpected. */
if (obj->properties) {
if (UNEXPECTED(GC_REFCOUNT(obj->properties) > 1)) {
if (EXPECTED(!(GC_FLAGS(obj->properties) & IS_ARRAY_IMMUTABLE))) {
GC_DELREF(obj->properties);
}
obj->properties = zend_array_dup(obj->properties);
}
zend_hash_reverse_apply(obj->properties, zlo_hash_remove_dyn_props_func);
}
/* unset() declared properties */
for (int i = 0; i < reflection_ce->default_properties_count; i++) {
@ -513,6 +546,9 @@ ZEND_API zend_object *zend_lazy_object_init(zend_object *obj)
ZEND_ASSERT(zend_object_is_lazy_proxy(obj));
zend_lazy_object_info *info = zend_lazy_object_get_info(obj);
ZEND_ASSERT(info->flags & ZEND_LAZY_OBJECT_INITIALIZED);
if (zend_object_is_lazy(info->u.instance)) {
return zend_lazy_object_init(info->u.instance);
}
return info->u.instance;
}

View File

@ -51,13 +51,6 @@ static uint32_t zho_num_backed_props(zend_object *zobj)
static zend_array *zho_build_properties_ex(zend_object *zobj, bool check_access, bool include_dynamic_props)
{
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(!zobj)) {
return zend_new_array(0);
}
}
zend_class_entry *ce = zobj->ce;
zend_array *properties = zend_new_array(ce->default_properties_count);
zend_hash_real_init_mixed(properties);
@ -93,23 +86,22 @@ static zend_array *zho_build_properties_ex(zend_object *zobj, bool check_access,
ZEND_API zend_array *zend_hooked_object_build_properties(zend_object *zobj)
{
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(!zobj)) {
return (zend_array*) &zend_empty_array;
}
}
return zho_build_properties_ex(zobj, false, true);
}
static bool zho_dynamic_it_init(zend_hooked_object_iterator *hooked_iter)
static void zho_dynamic_it_init(zend_hooked_object_iterator *hooked_iter)
{
if (hooked_iter->dynamic_prop_it != (uint32_t) -1) {
return true;
}
zend_object *zobj = Z_OBJ_P(&hooked_iter->it.data);
if (!zobj->properties || zho_num_backed_props(zobj) == zobj->properties->nNumUsed) {
hooked_iter->dynamic_props_done = true;
return false;
}
hooked_iter->dynamic_prop_it = zend_hash_iterator_add(zobj->properties, zho_num_backed_props(zobj));
return true;
zend_array *properties = zobj->handlers->get_properties(zobj);
hooked_iter->dynamic_props_done = false;
hooked_iter->dynamic_prop_it = zend_hash_iterator_add(properties, zho_num_backed_props(zobj));
}
static void zho_it_get_current_key(zend_object_iterator *iter, zval *key);
@ -168,7 +160,17 @@ static void zho_dynamic_it_fetch_current(zend_object_iterator *iter)
zend_array *properties = Z_OBJ(iter->data)->properties;
HashPosition pos = zend_hash_iterator_pos(hooked_iter->dynamic_prop_it, properties);
if (pos >= properties->nNumUsed) {
hooked_iter->dynamic_props_done = true;
return;
}
Bucket *bucket = properties->arData + pos;
if (UNEXPECTED(Z_TYPE(bucket->val) == IS_UNDEF)) {
return;
}
if (hooked_iter->by_ref && Z_TYPE(bucket->val) != IS_REFERENCE) {
ZEND_ASSERT(Z_TYPE(bucket->val) != IS_UNDEF);
ZVAL_MAKE_REF(&bucket->val);
@ -192,7 +194,7 @@ static void zho_it_fetch_current(zend_object_iterator *iter)
while (true) {
if (!hooked_iter->declared_props_done) {
zho_declared_it_fetch_current(iter);
} else if (!hooked_iter->dynamic_props_done && zho_dynamic_it_init(hooked_iter)) {
} else if (!hooked_iter->dynamic_props_done) {
zho_dynamic_it_fetch_current(iter);
} else {
break;
@ -252,14 +254,11 @@ static void zho_it_move_forward(zend_object_iterator *iter)
if (zend_hash_has_more_elements(properties) != SUCCESS) {
hooked_iter->declared_props_done = true;
}
} else if (!hooked_iter->dynamic_props_done && zho_dynamic_it_init(hooked_iter)) {
} else if (!hooked_iter->dynamic_props_done) {
zend_array *properties = Z_OBJ(iter->data)->properties;
HashPosition pos = zend_hash_iterator_pos(hooked_iter->dynamic_prop_it, properties);
pos++;
EG(ht_iterators)[hooked_iter->dynamic_prop_it].pos = pos;
if (pos >= properties->nNumUsed) {
hooked_iter->dynamic_props_done = true;
}
}
}
@ -305,17 +304,24 @@ static const zend_object_iterator_funcs zend_hooked_object_it_funcs = {
ZEND_API zend_object_iterator *zend_hooked_object_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
{
zend_object *zobj = Z_OBJ_P(object);
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(!zobj)) {
return NULL;
}
}
zend_hooked_object_iterator *iterator = emalloc(sizeof(zend_hooked_object_iterator));
zend_iterator_init(&iterator->it);
ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
ZVAL_OBJ_COPY(&iterator->it.data, zobj);
iterator->it.funcs = &zend_hooked_object_it_funcs;
iterator->by_ref = by_ref;
iterator->declared_props_done = false;
zend_array *properties = zho_build_properties_ex(Z_OBJ_P(object), true, false);
zend_array *properties = zho_build_properties_ex(zobj, true, false);
ZVAL_ARR(&iterator->declared_props, properties);
iterator->dynamic_props_done = false;
iterator->dynamic_prop_it = (uint32_t) -1;
zho_dynamic_it_init(iterator);
ZVAL_UNDEF(&iterator->current_key);
ZVAL_UNDEF(&iterator->current_data);

View File

@ -6821,6 +6821,14 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR)
} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
zend_object *zobj = Z_OBJ_P(array_ptr);
if (!zobj->ce->get_iterator) {
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
FREE_OP1_IF_VAR();
HANDLE_EXCEPTION();
}
}
HashTable *properties = zobj->properties;
if (properties) {
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
@ -6909,7 +6917,16 @@ ZEND_VM_COLD_CONST_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR)
ZEND_VM_NEXT_OPCODE();
} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
zend_object *zobj = Z_OBJ_P(array_ptr);
HashTable *properties;
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
FREE_OP1_IF_VAR();
HANDLE_EXCEPTION();
}
}
if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
if (array_ptr == array_ref) {
ZVAL_NEW_REF(array_ref, array_ref);

68
Zend/zend_vm_execute.h generated
View File

@ -5411,6 +5411,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(
} else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
zend_object *zobj = Z_OBJ_P(array_ptr);
if (!zobj->ce->get_iterator) {
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
}
HashTable *properties = zobj->properties;
if (properties) {
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
@ -5497,7 +5505,16 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_
ZEND_VM_NEXT_OPCODE();
} else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
zend_object *zobj = Z_OBJ_P(array_ptr);
HashTable *properties;
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
}
if (IS_CONST == IS_VAR || IS_CONST == IS_CV) {
if (array_ptr == array_ref) {
ZVAL_NEW_REF(array_ref, array_ref);
@ -20117,6 +20134,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZE
} else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
zend_object *zobj = Z_OBJ_P(array_ptr);
if (!zobj->ce->get_iterator) {
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
}
HashTable *properties = zobj->properties;
if (properties) {
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
@ -20204,7 +20229,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(Z
ZEND_VM_NEXT_OPCODE();
} else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
zend_object *zobj = Z_OBJ_P(array_ptr);
HashTable *properties;
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
}
if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) {
if (array_ptr == array_ref) {
ZVAL_NEW_REF(array_ref, array_ref);
@ -22769,6 +22803,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZE
} else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
zend_object *zobj = Z_OBJ_P(array_ptr);
if (!zobj->ce->get_iterator) {
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
}
HashTable *properties = zobj->properties;
if (properties) {
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
@ -22857,7 +22899,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(Z
ZEND_VM_NEXT_OPCODE();
} else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
zend_object *zobj = Z_OBJ_P(array_ptr);
HashTable *properties;
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
}
if (IS_VAR == IS_VAR || IS_VAR == IS_CV) {
if (array_ptr == array_ref) {
ZVAL_NEW_REF(array_ref, array_ref);
@ -41067,6 +41118,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEN
} else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
zend_object *zobj = Z_OBJ_P(array_ptr);
if (!zobj->ce->get_iterator) {
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
}
HashTable *properties = zobj->properties;
if (properties) {
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
@ -41153,7 +41212,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZE
ZEND_VM_NEXT_OPCODE();
} else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
zend_object *zobj = Z_OBJ_P(array_ptr);
HashTable *properties;
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
zobj = zend_lazy_object_init(zobj);
if (UNEXPECTED(EG(exception))) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
}
if (IS_CV == IS_VAR || IS_CV == IS_CV) {
if (array_ptr == array_ref) {
ZVAL_NEW_REF(array_ref, array_ref);