From 4638f7b91407c48710007af82a68da0007c820f2 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 07:43:28 +0300 Subject: [PATCH 01/16] Change "foreach" statement behavior (this is just a PoC yet) - "foreach by value" don't relay on internal array/object pointer and doesnt perform array duplication. It just locks it incrementing reference counter. If the original array is modified by some code, the copy on write is performed and "foreach" still work with the old copy. - it makes no difference if array given to "foreach by value" is reference itself - "foreach by reference" still use internal array/object pointer and should work similar to PHP-5. (This id not completely implemented) --- Zend/tests/bug40509.phpt | 2 +- Zend/tests/bug40705.phpt | 2 +- Zend/zend_compile.c | 22 +- Zend/zend_compile.h | 3 - Zend/zend_hash.h | 7 - Zend/zend_opcode.c | 6 +- Zend/zend_types.h | 4 + Zend/zend_vm_def.h | 615 +++++--- Zend/zend_vm_execute.h | 1489 +++++++++--------- Zend/zend_vm_opcodes.c | 8 +- Zend/zend_vm_opcodes.h | 6 +- ext/opcache/Optimizer/block_pass.c | 14 +- ext/opcache/Optimizer/nop_removal.c | 6 +- ext/opcache/Optimizer/optimize_temp_vars_5.c | 35 +- ext/opcache/Optimizer/pass1_5.c | 6 +- ext/opcache/Optimizer/pass3.c | 6 +- ext/opcache/Optimizer/zend_optimizer.c | 12 +- ext/opcache/zend_persist.c | 6 +- tests/lang/bug23624.phpt | 2 +- tests/lang/foreachLoop.001.phpt | 2 +- tests/lang/foreachLoop.009.phpt | 6 +- tests/lang/foreachLoop.011.phpt | 7 +- tests/lang/foreachLoop.014.phpt | 139 +- tests/lang/foreachLoopObjects.006.phpt | 32 +- 24 files changed, 1245 insertions(+), 1192 deletions(-) diff --git a/Zend/tests/bug40509.phpt b/Zend/tests/bug40509.phpt index 21eaae94448c..65e32533ef11 100644 --- a/Zend/tests/bug40509.phpt +++ b/Zend/tests/bug40509.phpt @@ -23,4 +23,4 @@ var_dump(key($arr["v"])); int(0) int(0) int(0) -NULL +int(0) diff --git a/Zend/tests/bug40705.phpt b/Zend/tests/bug40705.phpt index 374f73b75eb6..8a679654d541 100644 --- a/Zend/tests/bug40705.phpt +++ b/Zend/tests/bug40705.phpt @@ -23,4 +23,4 @@ int(0) int(0) int(1) int(2) -NULL +int(0) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7476d475dea6..e37fdd778cb1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3396,30 +3396,22 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ } opnum_reset = get_next_op_number(CG(active_op_array)); - opline = zend_emit_op(&reset_node, ZEND_FE_RESET, &expr_node, NULL); - if (by_ref && is_variable) { - opline->extended_value = ZEND_FE_FETCH_BYREF; - } + opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); zend_stack_push(&CG(loop_var_stack), &reset_node); opnum_fetch = get_next_op_number(CG(active_op_array)); - opline = zend_emit_op(&value_node, ZEND_FE_FETCH, &reset_node, NULL); - if (by_ref) { - opline->extended_value |= ZEND_FE_FETCH_BYREF; - } + opline = zend_emit_op(&value_node, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); if (key_ast) { - opline->extended_value |= ZEND_FE_FETCH_WITH_KEY; + opline->extended_value = 1; } opline = zend_emit_op(NULL, ZEND_OP_DATA, NULL, NULL); - /* Allocate enough space to keep HashPointer on VM stack */ - opline->op1_type = IS_TMP_VAR; - opline->op1.var = get_temporary_variable(CG(active_op_array)); - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make sure 1 zval is enough for HashPointer (2 must be enough) */ - get_temporary_variable(CG(active_op_array)); + if (by_ref) { + /* Allocate temporary variable to keep HashTable value */ + opline->op1_type = IS_TMP_VAR; + opline->op1.var = get_temporary_variable(CG(active_op_array)); } if (key_ast) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 7c82c67d6dff..2d71ca25713b 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -828,9 +828,6 @@ int zend_add_literal(zend_op_array *op_array, zval *zv); #define ZEND_FETCH_ARG_MASK 0x000fffff -#define ZEND_FE_FETCH_BYREF 1 -#define ZEND_FE_FETCH_WITH_KEY 2 - #define EXT_TYPE_FREE_ON_RETURN (1<<2) #define ZEND_MEMBER_FUNC_CALL 1<<0 diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 377f508658ad..85f1890302a6 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -171,13 +171,6 @@ ZEND_API zval *zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos); ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos); ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos); -typedef struct _HashPointer { - HashPosition pos; - HashTable *ht; - zend_ulong h; - zend_string *key; -} HashPointer; - #define zend_hash_has_more_elements(ht) \ zend_hash_has_more_elements_ex(ht, &(ht)->nInternalPointer) #define zend_hash_move_forward(ht) \ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index a6d041f13120..c1e3adb3ec06 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -766,8 +766,10 @@ ZEND_API int pass_two(zend_op_array *op_array) case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2); break; case ZEND_VERIFY_RETURN_TYPE: diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 5c00cb133f49..aa5404139bd6 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -128,6 +128,7 @@ struct _zval_struct { uint32_t cache_slot; /* literal cache slot */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ + uint32_t fe_pos; /* foreach position */ } u2; }; @@ -261,6 +262,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) { #define Z_CACHE_SLOT(zval) (zval).u2.cache_slot #define Z_CACHE_SLOT_P(zval_p) Z_CACHE_SLOT(*(zval_p)) +#define Z_FE_POS(zval) (zval).u2.fe_pos +#define Z_FE_POS_P(zval_p) Z_FE_POS(*(zval_p)) + #define Z_COUNTED(zval) (zval).value.counted #define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 24bb3fd3d6b1..aae951eeed71 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4620,292 +4620,405 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) +ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R); - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { - Z_ADDREF_P(array_ptr); + array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); + ZVAL_DEREF(array_ptr); + if (OP1_TYPE != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + FREE_OP1_IF_VAR(); + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = GET_OP1_ZVAL_PTR(BP_VAR_R); - if (OP1_TYPE & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - if (OP1_TYPE == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); - } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); - } - } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (OP1_TYPE == IS_CV) { - Z_ADDREF_P(array_ref); - } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + FREE_OP1_IF_VAR(); + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (OP1_TYPE == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); - } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (OP1_TYPE == IS_CONST || - (OP1_TYPE == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (OP1_TYPE == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (OP1_TYPE == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (OP1_TYPE == IS_CV) { - Z_ADDREF_P(array_ref); - } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + FREE_OP1_IF_VAR(); + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + + FREE_OP1_IF_VAR(); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { + if (OP1_TYPE != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ptr)) { + Z_ADDREF_P(array_ptr); } } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); + Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + + FREE_OP1_IF_VAR(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + FREE_OP1(); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); +ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) +{ + USE_OPLINE + zend_free_op free_op1; + zval *array_ptr, *array_ref; + HashTable *fe_ht; - if (OP1_TYPE == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { - FREE_OP1_IF_VAR(); + SAVE_OPLINE(); + + if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { + array_ref = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R); + ZVAL_MAKE_REF(array_ref); + array_ptr = Z_REFVAL_P(array_ref); + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + SEPARATE_ARRAY(array_ptr); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; - } else { - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - FREE_OP1_VAR_PTR(); - } + } else { + array_ref = array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); + } + + if (OP1_TYPE != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + FREE_OP1_VAR_PTR(); if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - - if (iter) { iter->index = 0; if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - FREE_OP1_VAR_PTR(); - } + FREE_OP1_VAR_PTR(); + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } } + is_empty = iter->funcs->valid(iter) != SUCCESS; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - FREE_OP1_VAR_PTR(); - } + FREE_OP1_VAR_PTR(); + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + + FREE_OP1_VAR_PTR(); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); HashPosition pos = 0; Bucket *p; + if (OP1_TYPE != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ref)) { + Z_ADDREF_P(array_ref); + } + } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { - is_empty = 1; - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - FREE_OP1_VAR_PTR(); - } + FREE_OP1_VAR_PTR(); ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { - pos++; - continue; - } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array_ptr) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { break; } pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; - } else { - zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } + Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { FREE_OP1_VAR_PTR(); - } - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + FREE_OP1_VAR_PTR(); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } } -ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) +ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY) { USE_OPLINE zend_free_op free_op1; - zval *array, *array_ref; + zval *array; zval *value; HashTable *fe_ht; - HashPointer *ptr; HashPosition pos; Bucket *p; - array = array_ref = EX_VAR(opline->op1.var); - if (Z_ISREF_P(array)) { - array = Z_REFVAL_P(array); - // TODO: referenced value might be changed to different array ??? - if (Z_IMMUTABLE_P(array)) { - zval_copy_ctor_func(array); + array = EX_VAR(opline->op1.var); + SAVE_OPLINE(); + if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { + fe_ht = Z_ARRVAL_P(array); + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + /* reached end of iteration */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + value = &p->val; + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } else if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } + } + break; } + if (opline->extended_value) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { + zend_object_iterator *iter; + + if ((iter = zend_iterator_unwrap(array)) == NULL) { + /* plain object */ + zend_object *zobj = Z_OBJ_P(array); + + fe_ht = Z_OBJPROP_P(array); + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + /* reached end of iteration */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + + p = fe_ht->arData + pos; + value = &p->val; + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } else if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } + } + if (!p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) { + break; + } + pos++; + } + if (opline->extended_value) { + if (UNEXPECTED(!p->key)) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (p->key->val[0]) { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } else { + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex( + p->key, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } else { + /* !iter happens from exception */ + if (iter && ++iter->index > 0) { + /* This could cause an endless loop if index becomes zero again. + * In case that ever happens we need an additional flag. */ + iter->funcs->move_forward(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + } + /* If index is zero we come from FE_RESET and checked valid() already. */ + if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) { + /* reached end of iteration */ + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + value = iter->funcs->get_current_data(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + if (!value) { + /* failure in get_current_data */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + if (opline->extended_value) { + if (iter->funcs->get_current_key) { + iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + } else { + ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); + } + } + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} +ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) +{ + USE_OPLINE + zend_free_op free_op1; + zval *array; + zval *value; + HashTable *fe_ht; + HashPosition pos; + Bucket *p; + + array = EX_VAR(opline->op1.var); SAVE_OPLINE(); + ZVAL_DEREF(array); if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { fe_ht = Z_ARRVAL_P(array); - ptr = (HashPointer*)EX_VAR((opline+1)->op1.var); - pos = ptr->pos; + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); if (UNEXPECTED(pos == INVALID_IDX)) { /* reached end of iteration */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else if (UNEXPECTED(ptr->ht != fe_ht)) { - ptr->ht = fe_ht; - pos = 0; - } else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) { - if (fe_ht->u.flags & HASH_FLAG_PACKED) { - pos = ptr->h; - } else { - pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; - while (1) { - if (pos == INVALID_IDX) { - pos = fe_ht->nInternalPointer; - break; - } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { - break; - } - pos = Z_NEXT(fe_ht->arData[pos].val); - } + } else { + if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { + pos = fe_ht->nInternalPointer; } + SEPARATE_ARRAY(array); + fe_ht = Z_ARRVAL_P(array); + Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; } +//??? if (pos != fe_ht->nInternalPointer) { +//??? //... +//??? } while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ + fe_ht->nInternalPointer = INVALID_IDX; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; value = &p->val; - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; - } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + } else if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; } } - if (opline->extended_value & ZEND_FE_FETCH_BYREF) { - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + break; + } + if (opline->extended_value) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); } else { - ZVAL_COPY(EX_VAR(opline->result.var), value); + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); } - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - if (!p->key) { - ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); - } else { - ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); - } - } - break; } - do { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + while (1) { pos++; if (pos >= fe_ht->nNumUsed) { - fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); + pos = INVALID_IDX; + break; } p = fe_ht->arData + pos; - } while (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)); - fe_ht->nInternalPointer = ptr->pos = pos; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { + break; + } + } + Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -4916,93 +5029,77 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) zend_object *zobj = Z_OBJ_P(array); fe_ht = Z_OBJPROP_P(array); - ptr = (HashPointer*)EX_VAR((opline+1)->op1.var); - pos = ptr->pos; - if (pos == INVALID_IDX) { + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); + if (UNEXPECTED(pos == INVALID_IDX)) { /* reached end of iteration */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else if (UNEXPECTED(ptr->ht != fe_ht)) { - ptr->ht = fe_ht; - pos = 0; - } else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) { - if (fe_ht->u.flags & HASH_FLAG_PACKED) { - pos = ptr->h; - } else { - pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; - while (1) { - if (pos == INVALID_IDX) { - pos = fe_ht->nInternalPointer; - break; - } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { - break; - } - pos = Z_NEXT(fe_ht->arData[pos].val); - } + } else { + if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { + pos = fe_ht->nInternalPointer; } + Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; } +//??? if (pos != fe_ht->nInternalPointer) { +//??? +//??? } while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ + fe_ht->nInternalPointer = INVALID_IDX; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; value = &p->val; - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; - } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + } else if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; } } - - if (UNEXPECTED(!p->key)) { - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); - } - break; - } else if (zend_check_property_access(zobj, p->key) == SUCCESS) { - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - if (p->key->val[0]) { - ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); - } else { - const char *class_name, *prop_name; - size_t prop_name_len; - zend_unmangle_property_name_ex( - p->key, &class_name, &prop_name, &prop_name_len); - ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); - } - } + if (!p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) { break; } pos++; } - if (opline->extended_value & ZEND_FE_FETCH_BYREF) { - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); - } else { - ZVAL_COPY(EX_VAR(opline->result.var), value); + if (opline->extended_value) { + if (UNEXPECTED(!p->key)) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (p->key->val[0]) { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } else { + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex( + p->key, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } } - do { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + while (1) { pos++; if (pos >= fe_ht->nNumUsed) { - fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); + pos = INVALID_IDX; + break; } p = fe_ht->arData + pos; - } while (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) || - (EXPECTED(p->key != NULL) && - zend_check_property_access(zobj, p->key) == FAILURE)); - fe_ht->nInternalPointer = ptr->pos = pos; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + break; + } + } + Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -5012,7 +5109,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) * In case that ever happens we need an additional flag. */ iter->funcs->move_forward(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } @@ -5020,32 +5117,28 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) { /* reached end of iteration */ if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } value = iter->funcs->get_current_data(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } if (!value) { /* failure in get_current_data */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } - if (opline->extended_value & ZEND_FE_FETCH_BYREF) { - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); - } else { - ZVAL_COPY(EX_VAR(opline->result.var), value); - } - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + if (opline->extended_value) { if (iter->funcs->get_current_key) { iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } else { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9a7b23d87aae..a51941195c8f 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3039,194 +3039,178 @@ static int ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HA ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = NULL; - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { - Z_ADDREF_P(array_ptr); + array_ptr = EX_CONSTANT(opline->op1); + ZVAL_DEREF(array_ptr); + if (IS_CONST != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = EX_CONSTANT(opline->op1); - if (IS_CONST & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - if (IS_CONST == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); - } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); - } - } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (IS_CONST == IS_CV) { - Z_ADDREF_P(array_ref); - } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (IS_CONST == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); - } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (IS_CONST == IS_CONST || - (IS_CONST == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (IS_CONST == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (IS_CONST == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (IS_CONST == IS_CV || IS_CONST == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (IS_CONST == IS_CV) { - Z_ADDREF_P(array_ref); - } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { + if (IS_CONST != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ptr)) { + Z_ADDREF_P(array_ptr); } } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); + Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); +static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *array_ptr, *array_ref; + HashTable *fe_ht; - if (IS_CONST == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { + SAVE_OPLINE(); + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + array_ref = NULL; + ZVAL_MAKE_REF(array_ref); + array_ptr = Z_REFVAL_P(array_ref); + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + SEPARATE_ARRAY(array_ptr); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; - } else { - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + } else { + array_ref = array_ptr = EX_CONSTANT(opline->op1); + } + + if (IS_CONST != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - } if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - - if (iter) { iter->index = 0; if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } } + is_empty = iter->funcs->valid(iter) != SUCCESS; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); HashPosition pos = 0; Bucket *p; + if (IS_CONST != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ref)) { + Z_ADDREF_P(array_ref); + } + } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { - is_empty = 1; - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { - pos++; - continue; - } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array_ptr) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { break; } pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; + Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } + ZVAL_UNDEF(EX_VAR(opline->result.var)); - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - - } - if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } } @@ -8957,194 +8941,178 @@ static int ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = NULL; - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { - Z_ADDREF_P(array_ptr); + array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + ZVAL_DEREF(array_ptr); + if (IS_TMP_VAR != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); - if (IS_TMP_VAR & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - if (IS_TMP_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); - } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); - } - } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (IS_TMP_VAR == IS_CV) { - Z_ADDREF_P(array_ref); - } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (IS_TMP_VAR == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); - } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (IS_TMP_VAR == IS_CONST || - (IS_TMP_VAR == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (IS_TMP_VAR == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (IS_TMP_VAR == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (IS_TMP_VAR == IS_CV) { - Z_ADDREF_P(array_ref); - } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { + if (IS_TMP_VAR != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ptr)) { + Z_ADDREF_P(array_ptr); } } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); + Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + zval_ptr_dtor_nogc(free_op1); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); +static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *array_ptr, *array_ref; + HashTable *fe_ht; - if (IS_TMP_VAR == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { + SAVE_OPLINE(); + if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { + array_ref = NULL; + ZVAL_MAKE_REF(array_ref); + array_ptr = Z_REFVAL_P(array_ref); + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + SEPARATE_ARRAY(array_ptr); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; - } else { - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + } else { + array_ref = array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + } + + if (IS_TMP_VAR != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - } if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - if (iter) { iter->index = 0; if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } } + is_empty = iter->funcs->valid(iter) != SUCCESS; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); HashPosition pos = 0; Bucket *p; + if (IS_TMP_VAR != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ref)) { + Z_ADDREF_P(array_ref); + } + } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { - is_empty = 1; - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { - pos++; - continue; - } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array_ptr) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { break; } pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; + Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } - - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); - } - if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } } @@ -11776,245 +11744,200 @@ static int ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { - Z_ADDREF_P(array_ptr); + array_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + ZVAL_DEREF(array_ptr); + if (IS_VAR != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(free_op1); + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); - if (IS_VAR & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - if (IS_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); - } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); - } - } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (IS_VAR == IS_CV) { - Z_ADDREF_P(array_ref); - } - } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (IS_VAR == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); - } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (IS_VAR == IS_CONST || - (IS_VAR == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (IS_VAR == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (IS_VAR == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (IS_VAR == IS_CV || IS_VAR == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (IS_VAR == IS_CV) { - Z_ADDREF_P(array_ref); - } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor_nogc(free_op1); + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } } - } - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); + is_empty = iter->funcs->valid(iter) != SUCCESS; - if (IS_VAR == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { + if (UNEXPECTED(EG(exception) != NULL)) { zval_ptr_dtor_nogc(free_op1); + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + + zval_ptr_dtor_nogc(free_op1); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { + if (IS_VAR != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ptr)) { + Z_ADDREF_P(array_ptr); } + } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); + Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + + zval_ptr_dtor_nogc(free_op1); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + zval_ptr_dtor_nogc(free_op1); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } +} + +static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *array_ptr, *array_ref; + HashTable *fe_ht; + + SAVE_OPLINE(); + + if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { + array_ref = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); + ZVAL_MAKE_REF(array_ref); + array_ptr = Z_REFVAL_P(array_ref); + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + SEPARATE_ARRAY(array_ptr); + } + } else { + array_ref = array_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + } + + if (IS_VAR != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - if (iter) { iter->index = 0; if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - } + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } } + is_empty = iter->funcs->valid(iter) != SUCCESS; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - } + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); HashPosition pos = 0; Bucket *p; + if (IS_VAR != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ref)) { + Z_ADDREF_P(array_ref); + } + } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { - is_empty = 1; - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - } + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { - pos++; - continue; - } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array_ptr) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { break; } pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; - } else { - zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } + Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - } - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } } -static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *array, *array_ref; + zval *array; zval *value; HashTable *fe_ht; - HashPointer *ptr; HashPosition pos; Bucket *p; - array = array_ref = EX_VAR(opline->op1.var); - if (Z_ISREF_P(array)) { - array = Z_REFVAL_P(array); - // TODO: referenced value might be changed to different array ??? - if (Z_IMMUTABLE_P(array)) { - zval_copy_ctor_func(array); - } - } - + array = EX_VAR(opline->op1.var); SAVE_OPLINE(); - if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { fe_ht = Z_ARRVAL_P(array); - ptr = (HashPointer*)EX_VAR((opline+1)->op1.var); - pos = ptr->pos; - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else if (UNEXPECTED(ptr->ht != fe_ht)) { - ptr->ht = fe_ht; - pos = 0; - } else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) { - if (fe_ht->u.flags & HASH_FLAG_PACKED) { - pos = ptr->h; - } else { - pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; - while (1) { - if (pos == INVALID_IDX) { - pos = fe_ht->nInternalPointer; - break; - } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { - break; - } - pos = Z_NEXT(fe_ht->arData[pos].val); - } - } - } + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -12022,46 +11945,27 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG } p = fe_ht->arData + pos; value = &p->val; - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; - } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + } else if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; } } - if (opline->extended_value & ZEND_FE_FETCH_BYREF) { - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); - } else { - ZVAL_COPY(EX_VAR(opline->result.var), value); - } - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - if (!p->key) { - ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); - } else { - ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); - } - } break; } - do { - pos++; - if (pos >= fe_ht->nNumUsed) { - fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); + if (opline->extended_value) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); } - p = fe_ht->arData + pos; - } while (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)); - fe_ht->nInternalPointer = ptr->pos = pos; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -12072,30 +11976,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG zend_object *zobj = Z_OBJ_P(array); fe_ht = Z_OBJPROP_P(array); - ptr = (HashPointer*)EX_VAR((opline+1)->op1.var); - pos = ptr->pos; - if (pos == INVALID_IDX) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else if (UNEXPECTED(ptr->ht != fe_ht)) { - ptr->ht = fe_ht; - pos = 0; - } else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) { - if (fe_ht->u.flags & HASH_FLAG_PACKED) { - pos = ptr->h; - } else { - pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; - while (1) { - if (pos == INVALID_IDX) { - pos = fe_ht->nInternalPointer; - break; - } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { - break; - } - pos = Z_NEXT(fe_ht->arData[pos].val); - } - } - } + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -12104,61 +11985,37 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG p = fe_ht->arData + pos; value = &p->val; - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; - } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + } else if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; } } - - if (UNEXPECTED(!p->key)) { - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); - } - break; - } else if (zend_check_property_access(zobj, p->key) == SUCCESS) { - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - if (p->key->val[0]) { - ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); - } else { - const char *class_name, *prop_name; - size_t prop_name_len; - zend_unmangle_property_name_ex( - p->key, &class_name, &prop_name, &prop_name_len); - ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); - } - } + if (!p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) { break; } pos++; } - if (opline->extended_value & ZEND_FE_FETCH_BYREF) { - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); - } else { - ZVAL_COPY(EX_VAR(opline->result.var), value); - } - do { - pos++; - if (pos >= fe_ht->nNumUsed) { - fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); + if (opline->extended_value) { + if (UNEXPECTED(!p->key)) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (p->key->val[0]) { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } else { + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex( + p->key, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); } - p = fe_ht->arData + pos; - } while (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) || - (EXPECTED(p->key != NULL) && - zend_check_property_access(zobj, p->key) == FAILURE)); - fe_ht->nInternalPointer = ptr->pos = pos; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -12168,7 +12025,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG * In case that ever happens we need an additional flag. */ iter->funcs->move_forward(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } @@ -12176,32 +12033,236 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) { /* reached end of iteration */ if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } value = iter->funcs->get_current_data(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } if (!value) { /* failure in get_current_data */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } - if (opline->extended_value & ZEND_FE_FETCH_BYREF) { - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + ZVAL_COPY(EX_VAR(opline->result.var), value); + if (opline->extended_value) { + if (iter->funcs->get_current_key) { + iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + } else { + ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); + } + } + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } +} + +static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *array; + zval *value; + HashTable *fe_ht; + HashPosition pos; + Bucket *p; + + array = EX_VAR(opline->op1.var); + SAVE_OPLINE(); + + ZVAL_DEREF(array); + if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { + fe_ht = Z_ARRVAL_P(array); + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); + if (UNEXPECTED(pos == INVALID_IDX)) { + /* reached end of iteration */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { + pos = fe_ht->nInternalPointer; + } + SEPARATE_ARRAY(array); + fe_ht = Z_ARRVAL_P(array); + Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; + } +//??? if (pos != fe_ht->nInternalPointer) { +//??? //... +//??? } + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + /* reached end of iteration */ + fe_ht->nInternalPointer = INVALID_IDX; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + value = &p->val; + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } else if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } + } + break; + } + if (opline->extended_value) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); } else { - ZVAL_COPY(EX_VAR(opline->result.var), value); + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } + } + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + while (1) { + pos++; + if (pos >= fe_ht->nNumUsed) { + pos = INVALID_IDX; + break; + } + p = fe_ht->arData + pos; + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { + break; + } + } + Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { + zend_object_iterator *iter; + + if ((iter = zend_iterator_unwrap(array)) == NULL) { + /* plain object */ + zend_object *zobj = Z_OBJ_P(array); + + fe_ht = Z_OBJPROP_P(array); + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); + if (UNEXPECTED(pos == INVALID_IDX)) { + /* reached end of iteration */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { + pos = fe_ht->nInternalPointer; + } + Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; + } +//??? if (pos != fe_ht->nInternalPointer) { +//??? +//??? } + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + /* reached end of iteration */ + fe_ht->nInternalPointer = INVALID_IDX; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + + p = fe_ht->arData + pos; + value = &p->val; + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } else if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } + } + if (!p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) { + break; + } + pos++; + } + if (opline->extended_value) { + if (UNEXPECTED(!p->key)) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (p->key->val[0]) { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } else { + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex( + p->key, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } + } + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + while (1) { + pos++; + if (pos >= fe_ht->nNumUsed) { + pos = INVALID_IDX; + break; + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + break; + } + } + Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } else { + /* !iter happens from exception */ + if (iter && ++iter->index > 0) { + /* This could cause an endless loop if index becomes zero again. + * In case that ever happens we need an additional flag. */ + iter->funcs->move_forward(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + } + /* If index is zero we come from FE_RESET and checked valid() already. */ + if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) { + /* reached end of iteration */ + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + value = iter->funcs->get_current_data(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + if (!value) { + /* failure in get_current_data */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + if (opline->extended_value) { if (iter->funcs->get_current_key) { iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } else { @@ -24064,194 +24125,178 @@ static int ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDL ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((IS_CV == IS_CV || IS_CV == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { - Z_ADDREF_P(array_ptr); + array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + ZVAL_DEREF(array_ptr); + if (IS_CV != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - if (IS_CV & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - if (IS_CV == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); - } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); - } - } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (IS_CV == IS_CV) { - Z_ADDREF_P(array_ref); - } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (IS_CV == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); - } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (IS_CV == IS_CONST || - (IS_CV == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (IS_CV == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (IS_CV == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (IS_CV == IS_CV || IS_CV == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (IS_CV == IS_CV) { - Z_ADDREF_P(array_ref); - } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + + OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { + if (IS_CV != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ptr)) { + Z_ADDREF_P(array_ptr); } } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); + Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} + +static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); + zval *array_ptr, *array_ref; + HashTable *fe_ht; - if (IS_CV == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { + SAVE_OPLINE(); + if (IS_CV == IS_VAR || IS_CV == IS_CV) { + array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + ZVAL_MAKE_REF(array_ref); + array_ptr = Z_REFVAL_P(array_ref); + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + SEPARATE_ARRAY(array_ptr); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; - } else { - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + } else { + array_ref = array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + } + + if (IS_CV != IS_CONST && + Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - } if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - if (iter) { iter->index = 0; if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } } + is_empty = iter->funcs->valid(iter) != SUCCESS; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } + OBJ_RELEASE(&iter->std); HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); HashPosition pos = 0; Bucket *p; + if (IS_CV != IS_TMP_VAR) { + if (Z_REFCOUNTED_P(array_ref)) { + Z_ADDREF_P(array_ref); + } + } + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { - is_empty = 1; - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if (Z_TYPE(p->val) == IS_UNDEF || - (Z_TYPE(p->val) == IS_INDIRECT && - Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { - pos++; - continue; - } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array_ptr) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { break; } pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; + Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; + ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } + ZVAL_UNDEF(EX_VAR(opline->result.var)); - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - - } - if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } } @@ -38295,31 +38340,31 @@ void zend_init_opcodes_handlers(void) ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDLER, ZEND_NULL_HANDLER, ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -38330,11 +38375,11 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -39495,11 +39540,31 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNUSED_HANDLER, ZEND_NULL_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -39510,31 +39575,11 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index c3006e603693..295600c6fdfd 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -99,8 +99,8 @@ const char *zend_vm_opcodes_map[170] = { "ZEND_UNSET_VAR", "ZEND_UNSET_DIM", "ZEND_UNSET_OBJ", - "ZEND_FE_RESET", - "ZEND_FE_FETCH", + "ZEND_FE_RESET_R", + "ZEND_FE_FETCH_R", "ZEND_EXIT", "ZEND_FETCH_R", "ZEND_FETCH_DIM_R", @@ -147,8 +147,8 @@ const char *zend_vm_opcodes_map[170] = { "ZEND_DEFINED", "ZEND_TYPE_CHECK", "ZEND_VERIFY_RETURN_TYPE", - NULL, - NULL, + "ZEND_FE_RESET_RW", + "ZEND_FE_FETCH_RW", NULL, NULL, NULL, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index f0fc7c7182ee..9cb1679e9894 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -101,8 +101,8 @@ END_EXTERN_C() #define ZEND_UNSET_VAR 74 #define ZEND_UNSET_DIM 75 #define ZEND_UNSET_OBJ 76 -#define ZEND_FE_RESET 77 -#define ZEND_FE_FETCH 78 +#define ZEND_FE_RESET_R 77 +#define ZEND_FE_FETCH_R 78 #define ZEND_EXIT 79 #define ZEND_FETCH_R 80 #define ZEND_FETCH_DIM_R 81 @@ -149,6 +149,8 @@ END_EXTERN_C() #define ZEND_DEFINED 122 #define ZEND_TYPE_CHECK 123 #define ZEND_VERIFY_RETURN_TYPE 124 +#define ZEND_FE_RESET_RW 125 +#define ZEND_FE_FETCH_RW 126 #define ZEND_PRE_INC_OBJ 132 #define ZEND_PRE_DEC_OBJ 133 #define ZEND_POST_INC_OBJ 134 diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 7948990f8130..3df0faa0ef57 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -166,14 +166,16 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: START_BLOCK_OP(ZEND_OP2(opline).opline_num); START_BLOCK_OP(opno + 1); break; - case ZEND_FE_FETCH: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: START_BLOCK_OP(ZEND_OP2(opline).opline_num); START_BLOCK_OP(opno + 2); break; @@ -293,11 +295,13 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: - case ZEND_FE_FETCH: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; /* break missing intentionally */ default: @@ -619,7 +623,7 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */ opline->opcode != ZEND_FETCH_LIST && - opline->opcode != ZEND_FE_RESET && + (opline->opcode != ZEND_FE_RESET_R || opline->opcode != ZEND_FE_RESET_RW) && opline->opcode != ZEND_FREE ) { zend_op *src = VAR_SOURCE(opline->op1); diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index 0bb8cce17fbc..dccd010ae442 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -93,8 +93,10 @@ void zend_optimizer_nop_removal(zend_op_array *op_array) case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_FETCH: - case ZEND_FE_RESET: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index 27ba6bad8e54..ac5389ed2bca 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -66,15 +66,12 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline; } - /* special puprose variable to keep HashPointer on VM stack */ + /* special puprose variable to keep HashTable* on VM stack */ if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH && + (opline-1)->opcode == ZEND_FE_FETCH_RW && + (opline-2)->opcode == ZEND_FE_RESET_RW && opline->op1_type == IS_TMP_VAR) { - start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline; - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make shure 1 zval is enough for HashPointer (2 must be enough) */ - start_of_T[VAR_NUM(ZEND_OP1(opline).var) + 1 - offset] = opline; - } + start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline - 2; } opline--; } @@ -88,25 +85,13 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c while (opline >= end) { if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { - /* special puprose variable to keep HashPointer on VM stack */ - if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH && - opline->op1_type == IS_TMP_VAR) { - max++; - ZEND_OP1(opline).var = NUM_VAR(max + offset); - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make shure 1 zval is enough for HashPointer (2 must be enough) */ - max++; - } - } else { - currT = VAR_NUM(ZEND_OP1(opline).var) - offset; - if (!valid_T[currT]) { - GET_AVAILABLE_T(); - map_T[currT] = i; - valid_T[currT] = 1; - } - ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); + currT = VAR_NUM(ZEND_OP1(opline).var) - offset; + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; } + ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } /* Skip OP_DATA */ diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 348fcb8efbe0..341e80501b44 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -602,8 +602,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c index c3a55b319f19..3019b274e953 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -328,7 +328,8 @@ void zend_optimizer_pass3(zend_op_array *op_array) op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_FE_FETCH_R || + op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } @@ -363,7 +364,8 @@ void zend_optimizer_pass3(zend_op_array *op_array) op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_FE_FETCH_R || + op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 54063d0ed329..a8506fb0789a 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -450,8 +450,10 @@ static void zend_accel_optimize(zend_op_array *op_array, case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline)); break; } @@ -488,8 +490,10 @@ static void zend_accel_optimize(zend_op_array *op_array, case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline)); break; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 3d63f96e346d..5b32baa64b5e 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -378,8 +378,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes]; break; } diff --git a/tests/lang/bug23624.phpt b/tests/lang/bug23624.phpt index 4ddb82e8c688..f8778fffe380 100644 --- a/tests/lang/bug23624.phpt +++ b/tests/lang/bug23624.phpt @@ -9,4 +9,4 @@ Bug #23624 (foreach leaves current array key as null) ?> --EXPECT-- string(3) "one" -bool(false) +string(3) "one" diff --git a/tests/lang/foreachLoop.001.phpt b/tests/lang/foreachLoop.001.phpt index b24f14e81d6b..c35b9b44e51f 100644 --- a/tests/lang/foreachLoop.001.phpt +++ b/tests/lang/foreachLoop.001.phpt @@ -60,5 +60,5 @@ string(1) "f" int(2) string(1) "f" -bool(false) +string(1) "a" bool(false) diff --git a/tests/lang/foreachLoop.009.phpt b/tests/lang/foreachLoop.009.phpt index 4586a35a10f8..df51fd6be4f9 100644 --- a/tests/lang/foreachLoop.009.phpt +++ b/tests/lang/foreachLoop.009.phpt @@ -55,6 +55,7 @@ foreach ($refedArray as $k=>&$v4) { Remove elements from a referenced array during loop key: 0; value: original.0 key: 1; value: original.1 +key: 2; value: original.2 Remove elements from a referenced array during loop, using &$value key: 0; value: original.0 @@ -64,11 +65,6 @@ Add elements to a referenced array during loop key: 0; value: original.0 key: 1; value: original.1 key: 2; value: original.2 -key: 3; value: new.0 -key: 4; value: new.1 -key: 5; value: new.2 -key: 6; value: new.3 -Loop detected, as expected. Add elements to a referenced array during loop, using &$value key: 0; value: original.0 diff --git a/tests/lang/foreachLoop.011.phpt b/tests/lang/foreachLoop.011.phpt index 671cfaf354ed..8527a8833e4a 100644 --- a/tests/lang/foreachLoop.011.phpt +++ b/tests/lang/foreachLoop.011.phpt @@ -25,10 +25,9 @@ foreach ($a as $v) { Change from array to non iterable: int(1) - -Warning: Invalid argument supplied for foreach() in %s on line 5 +int(2) +int(3) Change from object to non iterable: int(1) - -Warning: Invalid argument supplied for foreach() in %s on line 15 +int(2) diff --git a/tests/lang/foreachLoop.014.phpt b/tests/lang/foreachLoop.014.phpt index d32ea635fda7..9b179ab22a7f 100644 --- a/tests/lang/foreachLoop.014.phpt +++ b/tests/lang/foreachLoop.014.phpt @@ -1,7 +1,5 @@ --TEST-- Directly modifying a REFERENCED array when foreach'ing over it. ---XFAIL-- -Needs major foreach changes to get sane behavior --FILE-- Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=v.0 + iteration 1: $k=1; $v=v.1 --> State of array after loop: array(0) { } @@ -114,10 +112,9 @@ array(3) { --> Do loop: iteration 0: $k=0; $v=v.0 iteration 1: $k=1; $v=v.1 + iteration 2: $k=2; $v=v.2 --> State of array after loop: -array(1) { - [0]=> - string(3) "v.0" +array(0) { } ---( Array with 4 element(s): )--- @@ -135,8 +132,8 @@ array(4) { --> Do loop: iteration 0: $k=0; $v=v.0 iteration 1: $k=1; $v=v.1 - iteration 2: $k=0; $v=v.0 - iteration 3: $k=0; $v=v.0 + iteration 2: $k=2; $v=v.2 + iteration 3: $k=3; $v=v.3 --> State of array after loop: array(0) { } @@ -166,7 +163,7 @@ array(2) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=v.1 + iteration 1: $k=1; $v=v.1 --> State of array after loop: array(0) { } @@ -183,8 +180,8 @@ array(3) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=v.1 - iteration 2: $k=0; $v=v.2 + iteration 1: $k=1; $v=v.1 + iteration 2: $k=2; $v=v.2 --> State of array after loop: array(0) { } @@ -203,9 +200,9 @@ array(4) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=v.1 - iteration 2: $k=0; $v=v.2 - iteration 3: $k=0; $v=v.3 + iteration 1: $k=1; $v=v.1 + iteration 2: $k=2; $v=v.2 + iteration 3: $k=3; $v=v.3 --> State of array after loop: array(0) { } @@ -309,13 +306,8 @@ array(2) { --> Do loop: iteration 0: $k=0; $v=v.0 iteration 1: $k=1; $v=v.1 - iteration 2: $k=2; $v=new.0 - iteration 3: $k=3; $v=new.1 - iteration 4: $k=4; $v=new.2 - iteration 5: $k=5; $v=new.3 - ** Stuck in a loop! ** --> State of array after loop: -array(8) { +array(4) { [0]=> string(3) "v.0" [1]=> @@ -324,14 +316,6 @@ array(8) { string(5) "new.0" [3]=> string(5) "new.1" - [4]=> - string(5) "new.2" - [5]=> - string(5) "new.3" - [6]=> - string(5) "new.4" - [7]=> - string(5) "new.5" } ---( Array with 3 element(s): )--- @@ -348,12 +332,8 @@ array(3) { iteration 0: $k=0; $v=v.0 iteration 1: $k=1; $v=v.1 iteration 2: $k=2; $v=v.2 - iteration 3: $k=3; $v=new.0 - iteration 4: $k=4; $v=new.1 - iteration 5: $k=5; $v=new.2 - ** Stuck in a loop! ** --> State of array after loop: -array(9) { +array(6) { [0]=> string(3) "v.0" [1]=> @@ -366,12 +346,6 @@ array(9) { string(5) "new.1" [5]=> string(5) "new.2" - [6]=> - string(5) "new.3" - [7]=> - string(5) "new.4" - [8]=> - string(5) "new.5" } ---( Array with 4 element(s): )--- @@ -391,11 +365,8 @@ array(4) { iteration 1: $k=1; $v=v.1 iteration 2: $k=2; $v=v.2 iteration 3: $k=3; $v=v.3 - iteration 4: $k=4; $v=new.0 - iteration 5: $k=5; $v=new.1 - ** Stuck in a loop! ** --> State of array after loop: -array(10) { +array(8) { [0]=> string(3) "v.0" [1]=> @@ -412,10 +383,6 @@ array(10) { string(5) "new.2" [7]=> string(5) "new.3" - [8]=> - string(5) "new.4" - [9]=> - string(5) "new.5" } @@ -447,29 +414,16 @@ array(2) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=1; $v=v.1 --> State of array after loop: -array(8) { +array(4) { [0]=> - string(5) "new.5" - [1]=> - string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" } @@ -485,31 +439,21 @@ array(3) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=1; $v=v.1 + iteration 2: $k=2; $v=v.2 --> State of array after loop: -array(9) { +array(6) { [0]=> - string(5) "new.5" - [1]=> - string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> string(5) "new.2" - [4]=> + [1]=> string(5) "new.1" - [5]=> + [2]=> string(5) "new.0" - [6]=> + [3]=> string(3) "v.0" - [7]=> + [4]=> string(3) "v.1" - [8]=> + [5]=> string(3) "v.2" } @@ -527,32 +471,25 @@ array(4) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=1; $v=v.1 + iteration 2: $k=2; $v=v.2 + iteration 3: $k=3; $v=v.3 --> State of array after loop: -array(10) { +array(8) { [0]=> - string(5) "new.5" - [1]=> - string(5) "new.4" - [2]=> string(5) "new.3" - [3]=> + [1]=> string(5) "new.2" - [4]=> + [2]=> string(5) "new.1" - [5]=> + [3]=> string(5) "new.0" - [6]=> + [4]=> string(3) "v.0" - [7]=> + [5]=> string(3) "v.1" - [8]=> + [6]=> string(3) "v.2" - [9]=> + [7]=> string(3) "v.3" } diff --git a/tests/lang/foreachLoopObjects.006.phpt b/tests/lang/foreachLoopObjects.006.phpt index 8218b44dab92..5204aca9caf1 100644 --- a/tests/lang/foreachLoopObjects.006.phpt +++ b/tests/lang/foreachLoopObjects.006.phpt @@ -70,16 +70,12 @@ var_dump($obj); ?> --EXPECTF-- - Substituting the iterated object for a different object. string(10) "Original a" string(10) "Original b" -string(5) "new a" -string(5) "new b" -string(5) "new c" -string(5) "new d" -string(5) "new e" -string(5) "new f" +string(10) "Original c" +string(10) "Original d" +string(10) "Original e" object(stdClass)#%d (6) { ["a"]=> string(5) "new a" @@ -98,14 +94,9 @@ object(stdClass)#%d (6) { Substituting the iterated object for an array. string(10) "Original a" string(10) "Original b" -int(1) -int(2) -int(3) -int(4) -int(5) -int(6) -int(7) -int(8) +string(10) "Original c" +string(10) "Original d" +string(10) "Original e" array(8) { [0]=> int(1) @@ -128,11 +119,12 @@ array(8) { Substituting the iterated array for an object. int(1) int(2) -string(10) "Original a" -string(10) "Original b" -string(10) "Original c" -string(10) "Original d" -string(10) "Original e" +int(3) +int(4) +int(5) +int(6) +int(7) +int(8) object(C)#%d (5) { ["a"]=> string(10) "Original a" From dd2a36a2074bbb0cb31de00b66dcf2812d6d753f Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 10:02:34 +0300 Subject: [PATCH 02/16] Use GET_OP1_ZVAL_PTR_DEREF() (IS_TMP_VAR and IS_CONST can't be IS_REFERENCE) --- Zend/zend_vm_def.h | 3 +-- Zend/zend_vm_execute.h | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index aae951eeed71..b04324c4ed3c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4629,8 +4629,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) SAVE_OPLINE(); - array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); - ZVAL_DEREF(array_ptr); + array_ptr = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); if (OP1_TYPE != IS_CONST && Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { zend_class_entry *ce = Z_OBJCE_P(array_ptr); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a51941195c8f..3eead75c730b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3049,7 +3049,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER SAVE_OPLINE(); array_ptr = EX_CONSTANT(opline->op1); - ZVAL_DEREF(array_ptr); if (IS_CONST != IS_CONST && Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { zend_class_entry *ce = Z_OBJCE_P(array_ptr); @@ -8951,7 +8950,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A SAVE_OPLINE(); array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); - ZVAL_DEREF(array_ptr); if (IS_TMP_VAR != IS_CONST && Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { zend_class_entry *ce = Z_OBJCE_P(array_ptr); @@ -11753,8 +11751,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A SAVE_OPLINE(); - array_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); - ZVAL_DEREF(array_ptr); + array_ptr = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); if (IS_VAR != IS_CONST && Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { zend_class_entry *ce = Z_OBJCE_P(array_ptr); @@ -24134,8 +24131,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR SAVE_OPLINE(); - array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - ZVAL_DEREF(array_ptr); + array_ptr = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); if (IS_CV != IS_CONST && Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { zend_class_entry *ce = Z_OBJCE_P(array_ptr); From 92e90c09f085c22707ff4a59201f016f56e0ef8b Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 12:44:57 +0300 Subject: [PATCH 03/16] Fixed operand destruction in case of exceptions in iterator --- Zend/tests/foreach_003.phpt | 71 ++++++++++++++ Zend/tests/foreach_004.phpt | 65 +++++++++++++ Zend/zend_vm_def.h | 56 ++++++++---- Zend/zend_vm_execute.h | 178 ++++++++++++++++++++++++++---------- 4 files changed, 305 insertions(+), 65 deletions(-) create mode 100644 Zend/tests/foreach_003.phpt create mode 100644 Zend/tests/foreach_004.phpt diff --git a/Zend/tests/foreach_003.phpt b/Zend/tests/foreach_003.phpt new file mode 100644 index 000000000000..71b0f2a5a3ab --- /dev/null +++ b/Zend/tests/foreach_003.phpt @@ -0,0 +1,71 @@ +--TEST-- +Iterator exceptions in foreach by value +--FILE-- +count = $count; + $this->trap = $trap; + } + + function trap($trap) { + if ($trap === $this->trap) { + throw new Exception($trap); + } + } + + function rewind() {$this->trap(__FUNCTION__); $this->n = 0;} + function valid() {$this->trap(__FUNCTION__); return $this->n < $this->count;} + function key() {$this->trap(__FUNCTION__); return $this->n;} + function current() {$this->trap(__FUNCTION__); return $this->n;} + function next() {$this->trap(__FUNCTION__); $this->n++;} +} + +foreach(['rewind', 'valid', 'key', 'current', 'next'] as $trap) { + $obj = new IT(3, $trap); + try { + // IS_CV + foreach ($obj as $key => $val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + unset($obj); + + try { + // IS_VAR + foreach (new IT(3, $trap) as $key => $val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + + try { + // IS_TMP_VAR + foreach ((object)new IT(2, $trap) as $key => $val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +rewind +rewind +rewind +valid +valid +valid +key +key +key +current +current +current +0 +next +0 +next +0 +next diff --git a/Zend/tests/foreach_004.phpt b/Zend/tests/foreach_004.phpt new file mode 100644 index 000000000000..1f754a77edd3 --- /dev/null +++ b/Zend/tests/foreach_004.phpt @@ -0,0 +1,65 @@ +--TEST-- +Iterator exceptions in foreach by reference +--FILE-- +trap = $trap; + } + + function trap($trap) { + if ($trap === $this->trap) { + throw new Exception($trap); + } + } + + function rewind() {$this->trap(__FUNCTION__); return parent::rewind();} + function valid() {$this->trap(__FUNCTION__); return parent::valid();} + function key() {$this->trap(__FUNCTION__); return parent::key();} + function next() {$this->trap(__FUNCTION__); return parent::next();} +} + +foreach(['rewind', 'valid', 'key', 'next'] as $trap) { + $obj = new IT($trap); + try { + // IS_CV + foreach ($obj as $key => &$val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + unset($obj); + + try { + // IS_VAR + foreach (new IT($trap) as $key => &$val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + + try { + // IS_TMP_VAR + foreach ((object)new IT($trap) as $key => &$val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +rewind +rewind +rewind +valid +valid +valid +key +key +key +0 +next +0 +next +0 +next diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b04324c4ed3c..52a24d0859d7 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4637,7 +4637,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) zend_bool is_empty; if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - FREE_OP1_IF_VAR(); + FREE_OP1(); if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } @@ -4649,8 +4649,8 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - FREE_OP1_IF_VAR(); OBJ_RELEASE(&iter->std); + FREE_OP1(); HANDLE_EXCEPTION(); } } @@ -4658,15 +4658,15 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - FREE_OP1_IF_VAR(); OBJ_RELEASE(&iter->std); + FREE_OP1(); HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - FREE_OP1_IF_VAR(); + FREE_OP1(); if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { @@ -4674,13 +4674,13 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) ZEND_VM_NEXT_OPCODE(); } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - if (OP1_TYPE != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ptr)) { - Z_ADDREF_P(array_ptr); - } + zval *result = EX_VAR(opline->result.var); + + ZVAL_COPY_VALUE(result, array_ptr); + if (OP1_TYPE != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); - Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + Z_FE_POS_P(result) = 0; FREE_OP1_IF_VAR(); CHECK_EXCEPTION(); @@ -4720,7 +4720,11 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) zend_bool is_empty; if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - FREE_OP1_VAR_PTR(); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } @@ -4732,8 +4736,12 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - FREE_OP1_VAR_PTR(); OBJ_RELEASE(&iter->std); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } HANDLE_EXCEPTION(); } } @@ -4741,8 +4749,12 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - FREE_OP1_VAR_PTR(); OBJ_RELEASE(&iter->std); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ @@ -4751,7 +4763,11 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); - FREE_OP1_VAR_PTR(); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { @@ -4793,7 +4809,11 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); - FREE_OP1_VAR_PTR(); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } } @@ -5130,9 +5150,6 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) /* failure in get_current_data */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); if (opline->extended_value) { if (iter->funcs->get_current_key) { iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); @@ -5144,6 +5161,9 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); } } + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3eead75c730b..3d0dd2e97d0b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3068,8 +3068,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } } @@ -3077,8 +3077,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ @@ -3092,13 +3092,13 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER ZEND_VM_NEXT_OPCODE(); } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - if (IS_CONST != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ptr)) { - Z_ADDREF_P(array_ptr); - } + zval *result = EX_VAR(opline->result.var); + + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CONST != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); - Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + Z_FE_POS_P(result) = 0; CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -3137,7 +3137,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE zend_bool is_empty; if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_CONST == IS_VAR) { + } else { + + } if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } @@ -3149,8 +3153,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + if (IS_CONST == IS_VAR) { + + } else { + + } HANDLE_EXCEPTION(); } } @@ -3158,8 +3166,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + if (IS_CONST == IS_VAR) { + + } else { + + } HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ @@ -3168,6 +3180,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + if (IS_CONST == IS_VAR) { + + } else { + + } if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { @@ -3208,7 +3225,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + if (IS_CONST == IS_VAR) { + + } else { + } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } } @@ -8957,7 +8978,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A zend_bool is_empty; if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - + zval_ptr_dtor_nogc(free_op1); if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } @@ -8969,8 +8990,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } } @@ -8978,14 +8999,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + zval_ptr_dtor_nogc(free_op1); if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { @@ -8993,13 +9015,13 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - if (IS_TMP_VAR != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ptr)) { - Z_ADDREF_P(array_ptr); - } + zval *result = EX_VAR(opline->result.var); + + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_TMP_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); - Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + Z_FE_POS_P(result) = 0; CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -9038,7 +9060,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ zend_bool is_empty; if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_TMP_VAR == IS_VAR) { + } else { + zval_ptr_dtor_nogc(free_op1); + } if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } @@ -9050,8 +9076,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } HANDLE_EXCEPTION(); } } @@ -9059,8 +9089,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ @@ -9069,6 +9103,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { @@ -9109,7 +9148,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + if (IS_TMP_VAR == IS_VAR) { + } else { + zval_ptr_dtor_nogc(free_op1); + } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } } @@ -11771,8 +11814,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(free_op1); OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } } @@ -11780,8 +11823,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(free_op1); OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ @@ -11796,13 +11839,13 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - if (IS_VAR != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ptr)) { - Z_ADDREF_P(array_ptr); - } + zval *result = EX_VAR(opline->result.var); + + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); - Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + Z_FE_POS_P(result) = 0; zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -11842,7 +11885,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ zend_bool is_empty; if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } @@ -11854,8 +11901,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; OBJ_RELEASE(&iter->std); + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } HANDLE_EXCEPTION(); } } @@ -11863,8 +11914,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; OBJ_RELEASE(&iter->std); + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ @@ -11873,7 +11928,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { @@ -11915,7 +11974,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } } @@ -12252,9 +12315,6 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ /* failure in get_current_data */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); if (opline->extended_value) { if (iter->funcs->get_current_key) { iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); @@ -12266,6 +12326,9 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); } } + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } @@ -24151,8 +24214,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } } @@ -24160,8 +24223,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ @@ -24175,13 +24238,13 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR ZEND_VM_NEXT_OPCODE(); } } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - if (IS_CV != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ptr)) { - Z_ADDREF_P(array_ptr); - } + zval *result = EX_VAR(opline->result.var); + + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CV != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ptr); - Z_FE_POS_P(EX_VAR(opline->result.var)) = 0; + Z_FE_POS_P(result) = 0; CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -24220,7 +24283,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A zend_bool is_empty; if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_CV == IS_VAR) { + + } else { + } if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } @@ -24232,8 +24299,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A if (iter->funcs->rewind) { iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + if (IS_CV == IS_VAR) { + + } else { + + } HANDLE_EXCEPTION(); } } @@ -24241,8 +24312,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A is_empty = iter->funcs->valid(iter) != SUCCESS; if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + if (IS_CV == IS_VAR) { + + } else { + + } HANDLE_EXCEPTION(); } iter->index = -1; /* will be set to 0 before using next handler */ @@ -24251,6 +24326,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + if (IS_CV == IS_VAR) { + + } else { + + } if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { @@ -24291,7 +24371,11 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + if (IS_CV == IS_VAR) { + + } else { + } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } } From 61e739187391661e2d541947bec25d7dcc4479f3 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 14:59:54 +0300 Subject: [PATCH 04/16] Fixed temporary variable re-allocation pass --- Zend/tests/foreach_005.phpt | 22 +++++++++++++++++++ ext/opcache/Optimizer/optimize_temp_vars_5.c | 23 +++++++++++++------- 2 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 Zend/tests/foreach_005.phpt diff --git a/Zend/tests/foreach_005.phpt b/Zend/tests/foreach_005.phpt new file mode 100644 index 000000000000..6ed9fe940bf2 --- /dev/null +++ b/Zend/tests/foreach_005.phpt @@ -0,0 +1,22 @@ +--TEST-- +Nested foreach by reference on the same array +--FILE-- + +--EXPECT-- +1-1 +2-2 +2-3 +3-2 +3-3 +4-4 +5-3 +5-4 +5-5 diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index ac5389ed2bca..7ff94ddae51c 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -69,9 +69,8 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c /* special puprose variable to keep HashTable* on VM stack */ if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_FE_FETCH_RW && - (opline-2)->opcode == ZEND_FE_RESET_RW && opline->op1_type == IS_TMP_VAR) { - start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline - 2; + start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline; } opline--; } @@ -85,13 +84,21 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c while (opline >= end) { if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { - currT = VAR_NUM(ZEND_OP1(opline).var) - offset; - if (!valid_T[currT]) { - GET_AVAILABLE_T(); - map_T[currT] = i; - valid_T[currT] = 1; + /* special puprose variable to keep HashPointer on VM stack */ + if (opline->opcode == ZEND_OP_DATA && + (opline-1)->opcode == ZEND_FE_FETCH_RW && + opline->op1_type == IS_TMP_VAR) { + max++; + ZEND_OP1(opline).var = NUM_VAR(max + offset); + } else { + currT = VAR_NUM(ZEND_OP1(opline).var) - offset; + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + } + ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } - ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } /* Skip OP_DATA */ From eef80c583762d1e98d177cdbb27e3a8a6b0c4539 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 16:52:21 +0300 Subject: [PATCH 05/16] Fixed foreach by reference iteration over constant array --- Zend/tests/foreach_006.phpt | 20 +++++++ Zend/zend_vm_def.h | 29 ++++++--- Zend/zend_vm_execute.h | 116 +++++++++++++++++++++++++----------- 3 files changed, 120 insertions(+), 45 deletions(-) create mode 100644 Zend/tests/foreach_006.phpt diff --git a/Zend/tests/foreach_006.phpt b/Zend/tests/foreach_006.phpt new file mode 100644 index 000000000000..65d6fdc52cc8 --- /dev/null +++ b/Zend/tests/foreach_006.phpt @@ -0,0 +1,20 @@ +--TEST-- +Foreach by reference on constant +--FILE-- + +--EXPECT-- +1 +2 +3 +1 +2 +3 +1 +2 +3 diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 52a24d0859d7..5ddc903888b6 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4703,11 +4703,9 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) SAVE_OPLINE(); if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { - array_ref = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R); - ZVAL_MAKE_REF(array_ref); - array_ptr = Z_REFVAL_P(array_ref); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); + array_ref = array_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R); + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); } } else { array_ref = array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); @@ -4778,12 +4776,25 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) HashPosition pos = 0; Bucket *p; - if (OP1_TYPE != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ref)) { - Z_ADDREF_P(array_ref); + if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + if (OP1_TYPE == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); } + fe_ht = Z_ARRVAL_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { FREE_OP1_VAR_PTR(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3d0dd2e97d0b..362f450390f8 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3120,11 +3120,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE SAVE_OPLINE(); if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { - array_ref = NULL; - ZVAL_MAKE_REF(array_ref); - array_ptr = Z_REFVAL_P(array_ref); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); + array_ref = array_ptr = NULL; + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); } } else { array_ref = array_ptr = EX_CONSTANT(opline->op1); @@ -3195,12 +3193,25 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE HashPosition pos = 0; Bucket *p; - if (IS_CONST != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ref)) { - Z_ADDREF_P(array_ref); + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + if (IS_CONST == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); } + fe_ht = Z_ARRVAL_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { @@ -9043,11 +9054,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ SAVE_OPLINE(); if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { - array_ref = NULL; - ZVAL_MAKE_REF(array_ref); - array_ptr = Z_REFVAL_P(array_ref); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); + array_ref = array_ptr = NULL; + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); } } else { array_ref = array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); @@ -9118,12 +9127,25 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ HashPosition pos = 0; Bucket *p; - if (IS_TMP_VAR != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ref)) { - Z_ADDREF_P(array_ref); + if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + if (IS_TMP_VAR == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { @@ -11868,11 +11890,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ SAVE_OPLINE(); if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { - array_ref = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); - ZVAL_MAKE_REF(array_ref); - array_ptr = Z_REFVAL_P(array_ref); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); + array_ref = array_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); } } else { array_ref = array_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -11943,12 +11963,25 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ HashPosition pos = 0; Bucket *p; - if (IS_VAR != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ref)) { - Z_ADDREF_P(array_ref); + if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + if (IS_VAR == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; @@ -24266,11 +24299,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A SAVE_OPLINE(); if (IS_CV == IS_VAR || IS_CV == IS_CV) { - array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - ZVAL_MAKE_REF(array_ref); - array_ptr = Z_REFVAL_P(array_ref); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); + array_ref = array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); } } else { array_ref = array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); @@ -24341,12 +24372,25 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A HashPosition pos = 0; Bucket *p; - if (IS_CV != IS_TMP_VAR) { - if (Z_REFCOUNTED_P(array_ref)) { - Z_ADDREF_P(array_ref); + if (IS_CV == IS_VAR || IS_CV == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (Z_TYPE_P(array_ptr) == IS_ARRAY) { + if (IS_CV == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); } + fe_ht = Z_ARRVAL_P(array_ptr); } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); while (1) { if (pos >= fe_ht->nNumUsed) { From 10a3260b1f16b6075fd8140f673dfef4d5efea91 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 29 Jan 2015 21:04:44 +0300 Subject: [PATCH 06/16] New test --- Zend/tests/foreach_007.phpt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Zend/tests/foreach_007.phpt diff --git a/Zend/tests/foreach_007.phpt b/Zend/tests/foreach_007.phpt new file mode 100644 index 000000000000..b99bc73ebe44 --- /dev/null +++ b/Zend/tests/foreach_007.phpt @@ -0,0 +1,13 @@ +--TEST-- +Foreach by reference and inserting new element when we are already at the end +--FILE-- + +--EXPECT-- +1 +2 From 15a23b1218b3e38630d677751a975907daa2cd54 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 29 Jan 2015 21:05:02 +0300 Subject: [PATCH 07/16] Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementation_details) --- Zend/zend_compile.c | 10 +- Zend/zend_compile.h | 3 +- Zend/zend_execute.c | 8 + Zend/zend_execute_API.c | 10 ++ Zend/zend_generators.c | 6 + Zend/zend_globals.h | 5 + Zend/zend_hash.c | 147 ++++++++++++++++++- Zend/zend_hash.h | 9 +- Zend/zend_types.h | 16 +- Zend/zend_vm_def.h | 76 +++++----- Zend/zend_vm_execute.h | 123 +++++++++------- Zend/zend_vm_opcodes.c | 2 +- Zend/zend_vm_opcodes.h | 1 + ext/opcache/Optimizer/block_pass.c | 6 +- ext/opcache/Optimizer/optimize_temp_vars_5.c | 26 +--- ext/opcache/Optimizer/pass2.c | 4 +- ext/standard/array.c | 8 + tests/lang/foreachLoop.013.phpt | 59 ++++++-- tests/lang/foreachLoop.015.phpt | 59 ++++++-- 19 files changed, 426 insertions(+), 152 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e37fdd778cb1..080b31562ac3 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -829,7 +829,7 @@ static int generate_free_loop_var(znode *var) /* {{{ */ { zend_op *opline = get_next_op(CG(active_op_array)); - opline->opcode = ZEND_FREE; + opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE; SET_NODE(opline->op1, var); SET_UNUSED(opline->op2); } @@ -3398,6 +3398,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opnum_reset = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); + reset_node.flag = by_ref; zend_stack_push(&CG(loop_var_stack), &reset_node); opnum_fetch = get_next_op_number(CG(active_op_array)); @@ -3408,12 +3409,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opline = zend_emit_op(NULL, ZEND_OP_DATA, NULL, NULL); - if (by_ref) { - /* Allocate temporary variable to keep HashTable value */ - opline->op1_type = IS_TMP_VAR; - opline->op1.var = get_temporary_variable(CG(active_op_array)); - } - if (key_ast) { zend_make_tmp_result(&key_node, opline); } @@ -3504,6 +3499,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); + expr_node.flag = 0; zend_stack_push(&CG(loop_var_stack), &expr_node); zend_begin_loop(); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 2d71ca25713b..5239f565b3c0 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -95,7 +95,8 @@ typedef union _znode_op { } znode_op; typedef struct _znode { /* used only during compilation */ - int op_type; + zend_uchar op_type; + zend_uchar flag; union { znode_op op; zval constant; /* replaced by literal/zv */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 7a79defb059d..d59aef1d1b16 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1584,6 +1584,14 @@ static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_of if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } } array_offset = jmp_to->parent; diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index c9da46d81938..c6a0d8f0c9ab 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -183,6 +183,11 @@ void init_executor(void) /* {{{ */ EG(scope) = NULL; + EG(ht_iterators_count) = sizeof(EG(ht_iterators_slots)) / sizeof(HashTableIterator); + EG(ht_iterators_used) = 0; + EG(ht_iterators) = EG(ht_iterators_slots); + memset(EG(ht_iterators), 0, sizeof(EG(ht_iterators_slots))); + EG(active) = 1; } /* }}} */ @@ -373,6 +378,11 @@ void shutdown_executor(void) /* {{{ */ zend_shutdown_fpu(); + EG(ht_iterators_used) = 0; + if (EG(ht_iterators) != EG(ht_iterators_slots)) { + efree(EG(ht_iterators)); + } + EG(active) = 0; } /* }}} */ diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index f19bb850138b..1f25d07ea9dd 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -60,6 +60,12 @@ static void zend_generator_cleanup_unfinished_execution(zend_generator *generato if (brk_opline->opcode == ZEND_FREE) { zval *var = EX_VAR(brk_opline->op1.var); zval_ptr_dtor_nogc(var); + } else if (brk_opline->opcode == ZEND_FE_FREE) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); } } } diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index beca5ad631b3..abebb19ae390 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -225,6 +225,11 @@ struct _zend_executor_globals { zend_bool active; zend_bool valid_symbol_table; + uint32_t ht_iterators_count; /* number of allocatd slots */ + uint32_t ht_iterators_used; /* number of used slots */ + HashTableIterator *ht_iterators; + HashTableIterator ht_iterators_slots[16]; + void *saved_fpu_cw_ptr; #if XPFPA_HAVE_CW XPFPA_CW_DATATYPE saved_fpu_cw; diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 480ac64dd16d..dd175c7c00e2 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -193,6 +193,144 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt } } +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_count); + uint32_t idx; + + if (EXPECTED(ht->u.v.nIteratorsCount != 255)) { + ht->u.v.nIteratorsCount++; + } + while (iter != end) { + if (iter->ht == NULL) { + iter->ht = ht; + iter->pos = ht->nInternalPointer; + idx = iter - EG(ht_iterators); + if (idx + 1 > EG(ht_iterators_used)) { + EG(ht_iterators_used) = idx + 1; + } + return idx; + } + iter++; + } + if (EG(ht_iterators) == EG(ht_iterators_slots)) { + EG(ht_iterators) = emalloc(sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8)); + memcpy(EG(ht_iterators), EG(ht_iterators_slots), sizeof(HashTableIterator) * EG(ht_iterators_count)); + } else { + EG(ht_iterators) = erealloc(EG(ht_iterators), sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8)); + } + iter = EG(ht_iterators) + EG(ht_iterators_count); + EG(ht_iterators_count) += 8; + iter->ht = ht; + iter->pos = ht->nInternalPointer; + memset(iter + 1, 0, sizeof(HashTableIterator) * 7); + idx = iter - EG(ht_iterators); + EG(ht_iterators_used) = idx + 1; + return idx; +} + +ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators) + idx; + + ZEND_ASSERT(idx != (uint32_t)-1); + if (iter->pos == INVALID_IDX) { + return INVALID_IDX; + } else if (UNEXPECTED(iter->ht != ht)) { + if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) { + iter->ht->u.v.nIteratorsCount--; + } + if (EXPECTED(ht->u.v.nIteratorsCount != 255)) { + ht->u.v.nIteratorsCount++; + } + iter->ht = ht; + iter->pos = ht->nInternalPointer; + } + return iter->pos; +} + +ZEND_API void zend_hash_iterator_del(uint32_t idx) +{ + HashTableIterator *iter = EG(ht_iterators) + idx; + + ZEND_ASSERT(idx != (uint32_t)-1); + + if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) { + iter->ht->u.v.nIteratorsCount--; + } + iter->ht = NULL; + + if (idx == EG(ht_iterators_used) - 1) { + while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) { + idx--; + } + EG(ht_iterators_used) = idx; + } +} + +static zend_never_inline void _iterators_del(HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + uint32_t idx; + + while (iter != end) { + if (iter->ht == ht) { + iter->ht = NULL; + } + iter++; + } + + idx = EG(ht_iterators_used); + while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) { + idx--; + } + EG(ht_iterators_used) = idx; +} + +static zend_always_inline void iterators_del(HashTable *ht) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _iterators_del(ht); + } +} + +static zend_never_inline void _iterators_update(HashTable *ht, HashPosition pos) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == ht->nInternalPointer) { + iter->pos = pos; + } + iter++; + } +} + +static zend_always_inline void iterators_update(HashTable *ht, HashPosition pos) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _iterators_update(ht, pos); + } +} + +ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == ht->nInternalPointer) { + iter->pos = pos; + } + iter++; + } + } +} + static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key) { zend_ulong h; @@ -303,6 +441,7 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, idx); ht->nInternalPointer = idx; } p = ht->arData + idx; @@ -470,6 +609,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, } ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, h); ht->nInternalPointer = h; } if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { @@ -512,6 +652,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, idx); ht->nInternalPointer = idx; } if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { @@ -600,6 +741,7 @@ ZEND_API int zend_hash_rehash(HashTable *ht) if (i != j) { ht->arData[j] = ht->arData[i]; if (ht->nInternalPointer == i) { + iterators_update(ht, j); ht->nInternalPointer = j; } } @@ -632,9 +774,11 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, while (1) { idx++; if (idx >= ht->nNumUsed) { + iterators_update(ht, INVALID_IDX); ht->nInternalPointer = INVALID_IDX; break; } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { + iterators_update(ht, idx); ht->nInternalPointer = idx; break; } @@ -893,6 +1037,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht) } while (++p != end); } } + iterators_del(ht); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; } @@ -933,7 +1078,7 @@ ZEND_API void zend_array_destroy(HashTable *ht) } } while (++p != end); } - + iterators_del(ht); SET_INCONSISTENT(HT_DESTROYED); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 85f1890302a6..c8218f1112bc 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -50,8 +50,6 @@ typedef struct _zend_hash_key { typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam); -typedef uint32_t HashPosition; - BEGIN_EXTERN_C() /* startup/shutdown */ @@ -227,6 +225,13 @@ void zend_hash_display(const HashTable *ht); ZEND_API int _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx); + +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht); +ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht); +ZEND_API void zend_hash_iterator_del(uint32_t idx); +ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos); + + END_EXTERN_C() #define ZEND_INIT_SYMTABLE(ht) \ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index aa5404139bd6..dbe8c616df54 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -129,6 +129,7 @@ struct _zval_struct { uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ + uint32_t fe_iter_idx; /* foreach iterator index */ } u2; }; @@ -161,10 +162,11 @@ typedef struct _Bucket { typedef struct _HashTable { union { struct { - ZEND_ENDIAN_LOHI_3( + ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, - uint16_t reserve) + zend_uchar nIteratorsCount, + zend_uchar reserve) } v; uint32_t flags; } u; @@ -179,6 +181,13 @@ typedef struct _HashTable { dtor_func_t pDestructor; } HashTable; +typedef uint32_t HashPosition; + +typedef struct _HashTableIterator { + HashTable *ht; + HashPosition pos; +} HashTableIterator; + struct _zend_array { zend_refcounted gc; HashTable ht; @@ -265,6 +274,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) { #define Z_FE_POS(zval) (zval).u2.fe_pos #define Z_FE_POS_P(zval_p) Z_FE_POS(*(zval_p)) +#define Z_FE_ITER(zval) (zval).u2.fe_iter_idx +#define Z_FE_ITER_P(zval_p) Z_FE_ITER(*(zval_p)) + #define Z_COUNTED(zval) (zval).value.counted #define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 5ddc903888b6..245bc0b68a1a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2138,6 +2138,21 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY) +{ + zval *var; + USE_OPLINE + + SAVE_OPLINE(); + var = EX_VAR(opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HANDLER(54, ZEND_ADD_CHAR, TMP|UNUSED, CONST) { USE_OPLINE @@ -3821,6 +3836,14 @@ ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST) if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1)); } @@ -4758,8 +4781,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (OP1_TYPE == IS_VAR) { FREE_OP1_VAR_PTR(); @@ -4798,6 +4820,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) while (1) { if (pos >= fe_ht->nNumUsed) { FREE_OP1_VAR_PTR(); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -4811,8 +4834,8 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); FREE_OP1_VAR_PTR(); CHECK_EXCEPTION(); @@ -4820,6 +4843,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (OP1_TYPE == IS_VAR) { FREE_OP1_VAR_PTR(); } else { @@ -4990,21 +5014,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) ZVAL_DEREF(array); if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { fe_ht = Z_ARRVAL_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { - pos = fe_ht->nInternalPointer; - } - SEPARATE_ARRAY(array); - fe_ht = Z_ARRVAL_P(array); - Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; - } -//??? if (pos != fe_ht->nInternalPointer) { -//??? //... -//??? } + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -5048,7 +5058,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) break; } } - Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -5059,19 +5070,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) zend_object *zobj = Z_OBJ_P(array); fe_ht = Z_OBJPROP_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { - pos = fe_ht->nInternalPointer; - } - Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; - } -//??? if (pos != fe_ht->nInternalPointer) { -//??? -//??? } + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -5129,7 +5128,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) break; } } - Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -5963,6 +5963,14 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } else if (brk_opline->opcode == ZEND_END_SILENCE) { /* restore previous error_reporting value */ if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 362f450390f8..4244f5f75027 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1347,6 +1347,14 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } else if (brk_opline->opcode == ZEND_END_SILENCE) { /* restore previous error_reporting value */ if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { @@ -1805,6 +1813,14 @@ static int ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1)); } @@ -3175,8 +3191,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_CONST == IS_VAR) { @@ -3215,6 +3230,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE while (1) { if (pos >= fe_ht->nNumUsed) { + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -3228,14 +3244,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_CONST == IS_VAR) { } else { @@ -9109,8 +9126,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_TMP_VAR == IS_VAR) { @@ -9149,6 +9165,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ while (1) { if (pos >= fe_ht->nNumUsed) { + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -9162,14 +9179,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_TMP_VAR == IS_VAR) { } else { @@ -11945,8 +11963,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_VAR == IS_VAR) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; @@ -11985,6 +12002,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ while (1) { if (pos >= fe_ht->nNumUsed) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -11998,8 +12016,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; CHECK_EXCEPTION(); @@ -12007,6 +12025,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_VAR == IS_VAR) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; } else { @@ -12177,21 +12196,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ ZVAL_DEREF(array); if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { fe_ht = Z_ARRVAL_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { - pos = fe_ht->nInternalPointer; - } - SEPARATE_ARRAY(array); - fe_ht = Z_ARRVAL_P(array); - Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; - } -//??? if (pos != fe_ht->nInternalPointer) { -//??? //... -//??? } + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -12235,7 +12240,8 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ break; } } - Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -12246,19 +12252,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ zend_object *zobj = Z_OBJ_P(array); fe_ht = Z_OBJPROP_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { - pos = fe_ht->nInternalPointer; - } - Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; - } -//??? if (pos != fe_ht->nInternalPointer) { -//??? -//??? } + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -12316,7 +12310,8 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ break; } } - Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -24354,8 +24349,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_CV == IS_VAR) { @@ -24394,6 +24388,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A while (1) { if (pos >= fe_ht->nNumUsed) { + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -24407,14 +24402,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_CV == IS_VAR) { } else { @@ -33144,6 +33140,21 @@ static int ZEND_FASTCALL ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *var; + USE_OPLINE + + SAVE_OPLINE(); + var = EX_VAR(opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_BOOL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -39719,16 +39730,16 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 295600c6fdfd..d7c5db696685 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -149,7 +149,7 @@ const char *zend_vm_opcodes_map[170] = { "ZEND_VERIFY_RETURN_TYPE", "ZEND_FE_RESET_RW", "ZEND_FE_FETCH_RW", - NULL, + "ZEND_FE_FREE", NULL, NULL, NULL, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 9cb1679e9894..aa4afbd984bb 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -151,6 +151,7 @@ END_EXTERN_C() #define ZEND_VERIFY_RETURN_TYPE 124 #define ZEND_FE_RESET_RW 125 #define ZEND_FE_FETCH_RW 126 +#define ZEND_FE_FREE 127 #define ZEND_PRE_INC_OBJ 132 #define ZEND_PRE_DEC_OBJ 133 #define ZEND_POST_INC_OBJ 134 diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 3df0faa0ef57..4db48944e170 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -207,12 +207,15 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { int parent = op_array->brk_cont_array[i].parent; while (parent >= 0 && op_array->brk_cont_array[parent].start < 0 && - op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE) { + (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE || + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) { parent = op_array->brk_cont_array[parent].parent; } op_array->brk_cont_array[i].parent = parent; @@ -227,6 +230,7 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { if (i != j) { op_array->brk_cont_array[j] = op_array->brk_cont_array[i]; diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index 7ff94ddae51c..dc93ce2f4c80 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -66,12 +66,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline; } - /* special puprose variable to keep HashTable* on VM stack */ - if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH_RW && - opline->op1_type == IS_TMP_VAR) { - start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline; - } opline--; } @@ -84,21 +78,13 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c while (opline >= end) { if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { - /* special puprose variable to keep HashPointer on VM stack */ - if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH_RW && - opline->op1_type == IS_TMP_VAR) { - max++; - ZEND_OP1(opline).var = NUM_VAR(max + offset); - } else { - currT = VAR_NUM(ZEND_OP1(opline).var) - offset; - if (!valid_T[currT]) { - GET_AVAILABLE_T(); - map_T[currT] = i; - valid_T[currT] = 1; - } - ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); + currT = VAR_NUM(ZEND_OP1(opline).var) - offset; + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; } + ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } /* Skip OP_DATA */ diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c index 5be1e30c9fc0..b5785924932f 100644 --- a/ext/opcache/Optimizer/pass2.c +++ b/ext/opcache/Optimizer/pass2.c @@ -203,7 +203,9 @@ void zend_optimizer_pass2(zend_op_array *op_array) jmp_to = &op_array->brk_cont_array[array_offset]; array_offset = jmp_to->parent; if (--nest_levels > 0) { - if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE) { + if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE || + op_array->opcodes[jmp_to->brk].opcode == ZEND_FE_FREE || + op_array->opcodes[jmp_to->brk].opcode == ZEND_END_SILENCE) { dont_optimize = 1; break; } diff --git a/ext/standard/array.c b/ext/standard/array.c index 966ea0f1f69d..397bb88f0b99 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2203,6 +2203,9 @@ PHP_FUNCTION(array_shift) q->key = NULL; ZVAL_COPY_VALUE(&q->val, &p->val); ZVAL_UNDEF(&p->val); + if (idx == Z_ARRVAL_P(stack)->nInternalPointer) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), k); + } } k++; } @@ -2265,9 +2268,14 @@ PHP_FUNCTION(array_unshift) } } ZEND_HASH_FOREACH_END(); + new_hash.nInternalPointer = Z_ARRVAL_P(stack)->nInternalPointer; + new_hash.u.v.nIteratorsCount = Z_ARRVAL_P(stack)->u.v.nIteratorsCount; + Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0; Z_ARRVAL_P(stack)->pDestructor = NULL; zend_hash_destroy(Z_ARRVAL_P(stack)); *Z_ARRVAL_P(stack) = new_hash; + zend_hash_iterators_update(Z_ARRVAL_P(stack), 0); + zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); /* Clean up and return the number of elements in the stack */ RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack))); diff --git a/tests/lang/foreachLoop.013.phpt b/tests/lang/foreachLoop.013.phpt index b0c5e8dcf51c..10f43f399865 100644 --- a/tests/lang/foreachLoop.013.phpt +++ b/tests/lang/foreachLoop.013.phpt @@ -1,7 +1,5 @@ --TEST-- Directly modifying an unreferenced array when foreach'ing over it while using &$value syntax. ---XFAIL-- -Needs major foreach changes to get sane behavior --FILE-- ---EXPECTF-- +--EXPECT-- Popping elements off end of an unreferenced array, using &$value. ---( Array with 1 element(s): )--- @@ -95,9 +93,10 @@ array(2) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=v.0 --> State of array after loop: -array(0) { +array(1) { + [0]=> + &string(3) "v.0" } ---( Array with 3 element(s): )--- @@ -134,10 +133,12 @@ array(4) { --> Do loop: iteration 0: $k=0; $v=v.0 iteration 1: $k=1; $v=v.1 - iteration 2: $k=0; $v=v.0 - iteration 3: $k=0; $v=v.0 --> State of array after loop: -array(0) { +array(2) { + [0]=> + string(3) "v.0" + [1]=> + &string(3) "v.1" } @@ -289,12 +290,28 @@ array(1) { } --> Do loop: iteration 0: $k=0; $v=v.0 + iteration 1: $k=1; $v=new.0 + iteration 2: $k=2; $v=new.1 + iteration 3: $k=3; $v=new.2 + iteration 4: $k=4; $v=new.3 + iteration 5: $k=5; $v=new.4 + ** Stuck in a loop! ** --> State of array after loop: -array(2) { +array(7) { [0]=> - &string(3) "v.0" + string(3) "v.0" [1]=> string(5) "new.0" + [2]=> + string(5) "new.1" + [3]=> + string(5) "new.2" + [4]=> + string(5) "new.3" + [5]=> + &string(5) "new.4" + [6]=> + string(5) "new.5" } ---( Array with 2 element(s): )--- @@ -428,12 +445,28 @@ array(1) { } --> Do loop: iteration 0: $k=0; $v=v.0 + iteration 1: $k=0; $v=new.0 + iteration 2: $k=0; $v=new.1 + iteration 3: $k=0; $v=new.2 + iteration 4: $k=0; $v=new.3 + iteration 5: $k=0; $v=new.4 + ** Stuck in a loop! ** --> State of array after loop: -array(2) { +array(7) { [0]=> - string(5) "new.0" + string(5) "new.5" [1]=> - &string(3) "v.0" + &string(5) "new.4" + [2]=> + string(5) "new.3" + [3]=> + string(5) "new.2" + [4]=> + string(5) "new.1" + [5]=> + string(5) "new.0" + [6]=> + string(3) "v.0" } ---( Array with 2 element(s): )--- diff --git a/tests/lang/foreachLoop.015.phpt b/tests/lang/foreachLoop.015.phpt index 5b12a2b0c269..a56249ee0b40 100644 --- a/tests/lang/foreachLoop.015.phpt +++ b/tests/lang/foreachLoop.015.phpt @@ -1,7 +1,5 @@ --TEST-- Directly modifying a REFERENCED array when foreach'ing over it while using &$value syntax. ---XFAIL-- -Needs major foreach changes to get sane behavior --FILE-- ---EXPECTF-- +--EXPECT-- Popping elements off end of a referenced array, using &$value ---( Array with 1 element(s): )--- @@ -97,9 +95,10 @@ array(2) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=v.0 --> State of array after loop: -array(0) { +array(1) { + [0]=> + &string(3) "v.0" } ---( Array with 3 element(s): )--- @@ -136,10 +135,12 @@ array(4) { --> Do loop: iteration 0: $k=0; $v=v.0 iteration 1: $k=1; $v=v.1 - iteration 2: $k=0; $v=v.0 - iteration 3: $k=0; $v=v.0 --> State of array after loop: -array(0) { +array(2) { + [0]=> + string(3) "v.0" + [1]=> + &string(3) "v.1" } @@ -291,12 +292,28 @@ array(1) { } --> Do loop: iteration 0: $k=0; $v=v.0 + iteration 1: $k=1; $v=new.0 + iteration 2: $k=2; $v=new.1 + iteration 3: $k=3; $v=new.2 + iteration 4: $k=4; $v=new.3 + iteration 5: $k=5; $v=new.4 + ** Stuck in a loop! ** --> State of array after loop: -array(2) { +array(7) { [0]=> - &string(3) "v.0" + string(3) "v.0" [1]=> string(5) "new.0" + [2]=> + string(5) "new.1" + [3]=> + string(5) "new.2" + [4]=> + string(5) "new.3" + [5]=> + &string(5) "new.4" + [6]=> + string(5) "new.5" } ---( Array with 2 element(s): )--- @@ -430,12 +447,28 @@ array(1) { } --> Do loop: iteration 0: $k=0; $v=v.0 + iteration 1: $k=0; $v=new.0 + iteration 2: $k=0; $v=new.1 + iteration 3: $k=0; $v=new.2 + iteration 4: $k=0; $v=new.3 + iteration 5: $k=0; $v=new.4 + ** Stuck in a loop! ** --> State of array after loop: -array(2) { +array(7) { [0]=> - string(5) "new.0" + string(5) "new.5" [1]=> - &string(3) "v.0" + &string(5) "new.4" + [2]=> + string(5) "new.3" + [3]=> + string(5) "new.2" + [4]=> + string(5) "new.1" + [5]=> + string(5) "new.0" + [6]=> + string(3) "v.0" } ---( Array with 2 element(s): )--- From 721fc9e80d2ee8f2cd79c8c3cdceffae2c72de92 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 29 Jan 2015 21:43:28 +0300 Subject: [PATCH 08/16] Added new test --- Zend/tests/foreach_008.phpt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Zend/tests/foreach_008.phpt diff --git a/Zend/tests/foreach_008.phpt b/Zend/tests/foreach_008.phpt new file mode 100644 index 000000000000..c68bcd89da76 --- /dev/null +++ b/Zend/tests/foreach_008.phpt @@ -0,0 +1,21 @@ +--TEST-- +Nested foreach by reference and array modification +--FILE-- + +--EXPECT-- +0 - 0 +0 - 1 +0 - 3 +3 - 0 +3 - 3 From 4c5b385ff53ae9f0b52572e98c4db801f56603b0 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 07:56:37 +0300 Subject: [PATCH 09/16] More careful iterators update. --- Zend/tests/foreach_009.phpt | 40 +++++++++++++++++++++++ Zend/zend_hash.c | 65 +++++++++++++++++++++++++++++-------- Zend/zend_hash.h | 3 +- ext/standard/array.c | 7 ++-- 4 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 Zend/tests/foreach_009.phpt diff --git a/Zend/tests/foreach_009.phpt b/Zend/tests/foreach_009.phpt new file mode 100644 index 000000000000..6ce838464250 --- /dev/null +++ b/Zend/tests/foreach_009.phpt @@ -0,0 +1,40 @@ +--TEST-- +Nested foreach by reference and array modification with resize +--FILE-- + +--EXPECT-- +4-4 +4-5 +4-6 +4-7 +5-4 +5-5 +5-6 +5-7 +5-8 +6-4 +6-5 +6-6 +6-7 +6-8 +7-4 +7-5 +7-6 +7-7 +7-8 +8-4 +8-5 +8-6 +8-7 +8-8 diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index dd175c7c00e2..82617edf187e 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -296,35 +296,73 @@ static zend_always_inline void iterators_del(HashTable *ht) } } -static zend_never_inline void _iterators_update(HashTable *ht, HashPosition pos) +static zend_never_inline void _iterators_update(HashTable *ht, HashPosition from, HashPosition to) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); while (iter != end) { - if (iter->ht == ht && iter->pos == ht->nInternalPointer) { - iter->pos = pos; + if (iter->ht == ht && iter->pos == from) { + iter->pos = to; } iter++; } } -static zend_always_inline void iterators_update(HashTable *ht, HashPosition pos) +static zend_always_inline void iterators_update(HashTable *ht, HashPosition from, HashPosition to) { if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - _iterators_update(ht, pos); + _iterators_update(ht, from, to); } } -ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos) +static zend_never_inline void _iterators_update_to_next(HashTable *ht, HashPosition from) +{ + uint32_t to = from; + + while (1) { + to++; + if (to >= ht->nNumUsed) { + to = INVALID_IDX; + break; + } else if (Z_TYPE(ht->arData[to].val) != IS_UNDEF) { + break; + } + } + _iterators_update(ht, from, to); +} + +static zend_always_inline void iterators_update_to_next(HashTable *ht, HashPosition from) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _iterators_update_to_next(ht, from); + } +} + +ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == from) { + iter->pos = to; + } + iter++; + } + } +} + +ZEND_API void zend_hash_iterators_reset(HashTable *ht) { if (UNEXPECTED(ht->u.v.nIteratorsCount)) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); while (iter != end) { - if (iter->ht == ht && iter->pos == ht->nInternalPointer) { - iter->pos = pos; + if (iter->ht == ht) { + iter->pos = ht->nInternalPointer; } iter++; } @@ -441,9 +479,9 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { - iterators_update(ht, idx); ht->nInternalPointer = idx; } + iterators_update(ht, INVALID_IDX, idx); p = ht->arData + idx; p->h = h = zend_string_hash_val(key); p->key = key; @@ -609,9 +647,9 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, } ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { - iterators_update(ht, h); ht->nInternalPointer = h; } + iterators_update(ht, INVALID_IDX, h); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -652,9 +690,9 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { - iterators_update(ht, idx); ht->nInternalPointer = idx; } + iterators_update(ht, INVALID_IDX, idx); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -741,9 +779,9 @@ ZEND_API int zend_hash_rehash(HashTable *ht) if (i != j) { ht->arData[j] = ht->arData[i]; if (ht->nInternalPointer == i) { - iterators_update(ht, j); ht->nInternalPointer = j; } + iterators_update(ht, i, j); } nIndex = ht->arData[j].h & ht->nTableMask; Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; @@ -770,15 +808,14 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, } while (ht->nNumUsed > 0 && (Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF)); } ht->nNumOfElements--; + iterators_update_to_next(ht, idx); if (ht->nInternalPointer == idx) { while (1) { idx++; if (idx >= ht->nNumUsed) { - iterators_update(ht, INVALID_IDX); ht->nInternalPointer = INVALID_IDX; break; } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { - iterators_update(ht, idx); ht->nInternalPointer = idx; break; } diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index c8218f1112bc..749846aaf9be 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -229,7 +229,8 @@ ZEND_API int _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ul ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht); ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht); ZEND_API void zend_hash_iterator_del(uint32_t idx); -ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos); +ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to); +ZEND_API void zend_hash_iterators_reset(HashTable *ht); END_EXTERN_C() diff --git a/ext/standard/array.c b/ext/standard/array.c index 397bb88f0b99..49c6cd0789bc 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2203,9 +2203,7 @@ PHP_FUNCTION(array_shift) q->key = NULL; ZVAL_COPY_VALUE(&q->val, &p->val); ZVAL_UNDEF(&p->val); - if (idx == Z_ARRVAL_P(stack)->nInternalPointer) { - zend_hash_iterators_update(Z_ARRVAL_P(stack), k); - } + zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); } k++; } @@ -2268,14 +2266,13 @@ PHP_FUNCTION(array_unshift) } } ZEND_HASH_FOREACH_END(); - new_hash.nInternalPointer = Z_ARRVAL_P(stack)->nInternalPointer; new_hash.u.v.nIteratorsCount = Z_ARRVAL_P(stack)->u.v.nIteratorsCount; Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0; Z_ARRVAL_P(stack)->pDestructor = NULL; zend_hash_destroy(Z_ARRVAL_P(stack)); *Z_ARRVAL_P(stack) = new_hash; - zend_hash_iterators_update(Z_ARRVAL_P(stack), 0); zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); + zend_hash_iterators_reset(Z_ARRVAL_P(stack)); /* Clean up and return the number of elements in the stack */ RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack))); From 5aa9712b0a30303aadfe3bdd8ae1f072ca3e6ba1 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 09:49:35 +0300 Subject: [PATCH 10/16] Implement consistent behavior for foreach by value over plain object --- Zend/tests/foreach_010.phpt | 40 ++ Zend/zend_compile.c | 2 +- Zend/zend_execute.c | 2 +- Zend/zend_generators.c | 2 +- Zend/zend_vm_def.h | 336 ++++++---- Zend/zend_vm_execute.h | 1239 +++++++++++++++++++++-------------- 6 files changed, 1007 insertions(+), 614 deletions(-) create mode 100644 Zend/tests/foreach_010.phpt diff --git a/Zend/tests/foreach_010.phpt b/Zend/tests/foreach_010.phpt new file mode 100644 index 000000000000..6ba7e7e9fd76 --- /dev/null +++ b/Zend/tests/foreach_010.phpt @@ -0,0 +1,40 @@ +--TEST-- +Nested foreach by value over object and object modification with resize +--FILE-- +0, 'b'=>1, 'c'=>2, 'd'=>3, 'e'=>4, 'f'=>5, 'g'=>6, 'h'=>7]; +unset($o->a, $o->b, $o->c, $o->d); +foreach ($o as $v1) { + foreach ($o as $v2) { + echo "$v1-$v2\n"; + if ($v1 == 5 && $v2 == 6) { + $o->i = 8; + } + } +} +?> +--EXPECT-- +4-4 +4-5 +4-6 +4-7 +5-4 +5-5 +5-6 +5-7 +5-8 +6-4 +6-5 +6-6 +6-7 +6-8 +7-4 +7-5 +7-6 +7-7 +7-8 +8-4 +8-5 +8-6 +8-7 +8-8 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 080b31562ac3..79293971e8f5 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3398,7 +3398,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opnum_reset = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); - reset_node.flag = by_ref; + reset_node.flag = 1; /* generate FE_FREE */ zend_stack_push(&CG(loop_var_stack), &reset_node); opnum_fetch = get_next_op_number(CG(active_op_array)); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index d59aef1d1b16..1a81c298a8fa 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1587,7 +1587,7 @@ static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_of } else if (brk_opline->opcode == ZEND_FE_FREE) { if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval *var = EX_VAR(brk_opline->op1.var); - if (Z_FE_ITER_P(var) != (uint32_t)-1) { + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 1f25d07ea9dd..971d4e7bc8c6 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -62,7 +62,7 @@ static void zend_generator_cleanup_unfinished_execution(zend_generator *generato zval_ptr_dtor_nogc(var); } else if (brk_opline->opcode == ZEND_FE_FREE) { zval *var = EX_VAR(brk_opline->op1.var); - if (Z_FE_ITER_P(var) != (uint32_t)-1) { + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 245bc0b68a1a..064c756c6e23 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2145,7 +2145,7 @@ ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY) SAVE_OPLINE(); var = EX_VAR(opline->op1.var); - if (Z_FE_ITER_P(var) != (uint32_t)-1) { + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); @@ -3839,7 +3839,7 @@ ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST) } else if (brk_opline->opcode == ZEND_FE_FREE) { if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval *var = EX_VAR(brk_opline->op1.var); - if (Z_FE_ITER_P(var) != (uint32_t)-1) { + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); @@ -4647,70 +4647,105 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) { USE_OPLINE zend_free_op free_op1; - zval *array_ptr; + zval *array_ptr, *result; HashTable *fe_ht; SAVE_OPLINE(); array_ptr = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); - if (OP1_TYPE != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); - zend_bool is_empty; + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (OP1_TYPE != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - FREE_OP1(); - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + FREE_OP1_IF_VAR(); + CHECK_EXCEPTION(); + 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) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (OP1_TYPE != IS_TMP_VAR) { + Z_ADDREF_P(array_ptr); + } + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + while (1) { + if (pos >= fe_ht->nNumUsed) { + FREE_OP1_IF_VAR(); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + + FREE_OP1_IF_VAR(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + FREE_OP1(); + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + FREE_OP1(); + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { OBJ_RELEASE(&iter->std); FREE_OP1(); HANDLE_EXCEPTION(); } - } - - is_empty = iter->funcs->valid(iter) != SUCCESS; - - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - FREE_OP1(); - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ + iter->index = -1; /* will be set to 0 before using next handler */ - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - FREE_OP1(); - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zval *result = EX_VAR(opline->result.var); - - ZVAL_COPY_VALUE(result, array_ptr); - if (OP1_TYPE != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { - Z_ADDREF_P(array_ptr); + FREE_OP1(); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } } - Z_FE_POS_P(result) = 0; - - FREE_OP1_IF_VAR(); - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; FREE_OP1(); ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } @@ -4722,6 +4757,8 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) zend_free_op free_op1; zval *array_ptr, *array_ref; HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; SAVE_OPLINE(); @@ -4734,70 +4771,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) array_ref = array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); } - if (OP1_TYPE != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); - zend_bool is_empty; - - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - if (OP1_TYPE == IS_VAR) { - FREE_OP1_VAR_PTR(); - } else { - FREE_OP1(); - } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); - } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } - - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - if (OP1_TYPE == IS_VAR) { - FREE_OP1_VAR_PTR(); - } else { - FREE_OP1(); - } - HANDLE_EXCEPTION(); - } - } - - is_empty = iter->funcs->valid(iter) != SUCCESS; - - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - if (OP1_TYPE == IS_VAR) { - FREE_OP1_VAR_PTR(); - } else { - FREE_OP1(); - } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - if (OP1_TYPE == IS_VAR) { - FREE_OP1_VAR_PTR(); - } else { - FREE_OP1(); - } - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPosition pos = 0; - Bucket *p; - + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { if (array_ptr == array_ref) { ZVAL_NEW_REF(array_ref, array_ref); @@ -4809,14 +4783,12 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) array_ptr = EX_VAR(opline->result.var); ZVAL_COPY_VALUE(array_ptr, array_ref); } - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - if (OP1_TYPE == IS_CONST) { - zval_copy_ctor_func(array_ptr); - } else { - SEPARATE_ARRAY(array_ptr); - } - fe_ht = Z_ARRVAL_P(array_ptr); + if (OP1_TYPE == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); } + fe_ht = Z_ARRVAL_P(array_ptr); while (1) { if (pos >= fe_ht->nNumUsed) { FREE_OP1_VAR_PTR(); @@ -4824,12 +4796,9 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if ((Z_TYPE(p->val) != IS_UNDEF && - (Z_TYPE(p->val) != IS_INDIRECT || - Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && - (Z_TYPE_P(array_ptr) != IS_OBJECT || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { break; } pos++; @@ -4840,6 +4809,102 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) FREE_OP1_VAR_PTR(); CHECK_EXCEPTION(); 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) { + if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + while (1) { + if (pos >= fe_ht->nNumUsed) { + FREE_OP1_VAR_PTR(); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + + FREE_OP1_VAR_PTR(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -4906,7 +4971,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY) zend_object *zobj = Z_OBJ_P(array); fe_ht = Z_OBJPROP_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -4945,7 +5010,24 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY) } } ZVAL_COPY(EX_VAR(opline->result.var), value); - Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1; + while (1) { + pos++; + if (pos >= fe_ht->nNumUsed) { + pos = INVALID_IDX; + break; + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + break; + } + } + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -5966,7 +6048,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) } else if (brk_opline->opcode == ZEND_FE_FREE) { if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval *var = EX_VAR(brk_opline->op1.var); - if (Z_FE_ITER_P(var) != (uint32_t)-1) { + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 4244f5f75027..8ae97d2bf807 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1350,7 +1350,7 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER } else if (brk_opline->opcode == ZEND_FE_FREE) { if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval *var = EX_VAR(brk_opline->op1.var); - if (Z_FE_ITER_P(var) != (uint32_t)-1) { + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); @@ -1816,7 +1816,7 @@ static int ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } else if (brk_opline->opcode == ZEND_FE_FREE) { if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval *var = EX_VAR(brk_opline->op1.var); - if (Z_FE_ITER_P(var) != (uint32_t)-1) { + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); @@ -3059,68 +3059,102 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER { USE_OPLINE - zval *array_ptr; + zval *array_ptr, *result; HashTable *fe_ht; SAVE_OPLINE(); array_ptr = EX_CONSTANT(opline->op1); - if (IS_CONST != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); - zend_bool is_empty; + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CONST != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + CHECK_EXCEPTION(); + 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) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CONST != IS_TMP_VAR) { + Z_ADDREF_P(array_ptr); + } + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + while (1) { + if (pos >= fe_ht->nNumUsed) { - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - is_empty = iter->funcs->valid(iter) != SUCCESS; + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); + } + } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ + is_empty = iter->funcs->valid(iter) != SUCCESS; - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zval *result = EX_VAR(opline->result.var); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ - ZVAL_COPY_VALUE(result, array_ptr); - if (IS_CONST != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { - Z_ADDREF_P(array_ptr); - } - Z_FE_POS_P(result) = 0; + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } @@ -3132,6 +3166,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE zval *array_ptr, *array_ref; HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; SAVE_OPLINE(); @@ -3144,70 +3180,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE array_ref = array_ptr = EX_CONSTANT(opline->op1); } - if (IS_CONST != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); - zend_bool is_empty; - - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - if (IS_CONST == IS_VAR) { - - } else { - - } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); - } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } - - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - if (IS_CONST == IS_VAR) { - - } else { - - } - HANDLE_EXCEPTION(); - } - } - - is_empty = iter->funcs->valid(iter) != SUCCESS; - - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - if (IS_CONST == IS_VAR) { - - } else { - - } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - if (IS_CONST == IS_VAR) { - - } else { - - } - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPosition pos = 0; - Bucket *p; - + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { if (array_ptr == array_ref) { ZVAL_NEW_REF(array_ref, array_ref); @@ -3219,14 +3192,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE array_ptr = EX_VAR(opline->result.var); ZVAL_COPY_VALUE(array_ptr, array_ref); } - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - if (IS_CONST == IS_CONST) { - zval_copy_ctor_func(array_ptr); - } else { - SEPARATE_ARRAY(array_ptr); - } - fe_ht = Z_ARRVAL_P(array_ptr); + if (IS_CONST == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); } + fe_ht = Z_ARRVAL_P(array_ptr); while (1) { if (pos >= fe_ht->nNumUsed) { @@ -3234,12 +3205,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if ((Z_TYPE(p->val) != IS_UNDEF && - (Z_TYPE(p->val) != IS_INDIRECT || - Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && - (Z_TYPE_P(array_ptr) != IS_OBJECT || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { break; } pos++; @@ -3249,6 +3217,101 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE CHECK_EXCEPTION(); 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) { + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + while (1) { + if (pos >= fe_ht->nNumUsed) { + + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_CONST == IS_VAR) { + + } else { + + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_CONST == IS_VAR) { + + } else { + + } + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_CONST == IS_VAR) { + + } else { + + } + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (IS_CONST == IS_VAR) { + + } else { + + } + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -8993,69 +9056,103 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A { USE_OPLINE zend_free_op free_op1; - zval *array_ptr; + zval *array_ptr, *result; HashTable *fe_ht; SAVE_OPLINE(); array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); - if (IS_TMP_VAR != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); - zend_bool is_empty; + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_TMP_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - zval_ptr_dtor_nogc(free_op1); - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + CHECK_EXCEPTION(); + 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) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_TMP_VAR != IS_TMP_VAR) { + Z_ADDREF_P(array_ptr); + } + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + while (1) { + if (pos >= fe_ht->nNumUsed) { + + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { zval_ptr_dtor_nogc(free_op1); + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - is_empty = iter->funcs->valid(iter) != SUCCESS; + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); + HANDLE_EXCEPTION(); + } + } - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - zval_ptr_dtor_nogc(free_op1); - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ + is_empty = iter->funcs->valid(iter) != SUCCESS; - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ - zval_ptr_dtor_nogc(free_op1); - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zval *result = EX_VAR(opline->result.var); + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - ZVAL_COPY_VALUE(result, array_ptr); - if (IS_TMP_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { - Z_ADDREF_P(array_ptr); + zval_ptr_dtor_nogc(free_op1); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } } - Z_FE_POS_P(result) = 0; - - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; zval_ptr_dtor_nogc(free_op1); ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } @@ -9067,6 +9164,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ zend_free_op free_op1; zval *array_ptr, *array_ref; HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; SAVE_OPLINE(); @@ -9079,70 +9178,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ array_ref = array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); } - if (IS_TMP_VAR != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); - zend_bool is_empty; - - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - if (IS_TMP_VAR == IS_VAR) { - - } else { - zval_ptr_dtor_nogc(free_op1); - } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); - } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } - - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - if (IS_TMP_VAR == IS_VAR) { - - } else { - zval_ptr_dtor_nogc(free_op1); - } - HANDLE_EXCEPTION(); - } - } - - is_empty = iter->funcs->valid(iter) != SUCCESS; - - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - if (IS_TMP_VAR == IS_VAR) { - - } else { - zval_ptr_dtor_nogc(free_op1); - } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - if (IS_TMP_VAR == IS_VAR) { - - } else { - zval_ptr_dtor_nogc(free_op1); - } - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPosition pos = 0; - Bucket *p; - + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { if (array_ptr == array_ref) { ZVAL_NEW_REF(array_ref, array_ref); @@ -9154,14 +9190,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ array_ptr = EX_VAR(opline->result.var); ZVAL_COPY_VALUE(array_ptr, array_ref); } - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - if (IS_TMP_VAR == IS_CONST) { - zval_copy_ctor_func(array_ptr); - } else { - SEPARATE_ARRAY(array_ptr); - } - fe_ht = Z_ARRVAL_P(array_ptr); + if (IS_TMP_VAR == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); } + fe_ht = Z_ARRVAL_P(array_ptr); while (1) { if (pos >= fe_ht->nNumUsed) { @@ -9169,12 +9203,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if ((Z_TYPE(p->val) != IS_UNDEF && - (Z_TYPE(p->val) != IS_INDIRECT || - Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && - (Z_TYPE_P(array_ptr) != IS_OBJECT || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { break; } pos++; @@ -9184,6 +9215,101 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ CHECK_EXCEPTION(); 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) { + if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + while (1) { + if (pos >= fe_ht->nNumUsed) { + + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -11829,70 +11955,105 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A { USE_OPLINE zend_free_op free_op1; - zval *array_ptr; + zval *array_ptr, *result; HashTable *fe_ht; SAVE_OPLINE(); array_ptr = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); - if (IS_VAR != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); - zend_bool is_empty; + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; + + zval_ptr_dtor_nogc(free_op1); + CHECK_EXCEPTION(); + 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) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_VAR != IS_TMP_VAR) { + Z_ADDREF_P(array_ptr); + } + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + while (1) { + if (pos >= fe_ht->nNumUsed) { + zval_ptr_dtor_nogc(free_op1); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { zval_ptr_dtor_nogc(free_op1); - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(free_op1); + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + if (UNEXPECTED(EG(exception) != NULL)) { OBJ_RELEASE(&iter->std); zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } - } + iter->index = -1; /* will be set to 0 before using next handler */ - is_empty = iter->funcs->valid(iter) != SUCCESS; + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); zval_ptr_dtor_nogc(free_op1); - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - - zval_ptr_dtor_nogc(free_op1); - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zval *result = EX_VAR(opline->result.var); - - ZVAL_COPY_VALUE(result, array_ptr); - if (IS_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { - Z_ADDREF_P(array_ptr); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } } - Z_FE_POS_P(result) = 0; - - zval_ptr_dtor_nogc(free_op1); - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; zval_ptr_dtor_nogc(free_op1); ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } @@ -11904,6 +12065,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ zend_free_op free_op1; zval *array_ptr, *array_ref; HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; SAVE_OPLINE(); @@ -11916,28 +12079,114 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ array_ref = array_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); } - if (IS_VAR != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); - zend_bool is_empty; - - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - if (IS_VAR == IS_VAR) { + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (IS_VAR == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); + while (1) { + if (pos >= fe_ht->nNumUsed) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - } else { - zval_ptr_dtor_nogc(free_op1); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + p = fe_ht->arData + pos; + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { + break; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); + pos++; } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + CHECK_EXCEPTION(); + 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) { + if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + while (1) { + if (pos >= fe_ht->nNumUsed) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { OBJ_RELEASE(&iter->std); if (IS_VAR == IS_VAR) { @@ -11947,81 +12196,23 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ } HANDLE_EXCEPTION(); } - } + iter->index = -1; /* will be set to 0 before using next handler */ - is_empty = iter->funcs->valid(iter) != SUCCESS; + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); if (IS_VAR == IS_VAR) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; } else { zval_ptr_dtor_nogc(free_op1); } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - if (IS_VAR == IS_VAR) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - } else { - zval_ptr_dtor_nogc(free_op1); - } - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPosition pos = 0; - Bucket *p; - - if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { - if (array_ptr == array_ref) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - Z_ADDREF_P(array_ref); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - } else { - array_ptr = EX_VAR(opline->result.var); - ZVAL_COPY_VALUE(array_ptr, array_ref); - } - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - if (IS_VAR == IS_CONST) { - zval_copy_ctor_func(array_ptr); - } else { - SEPARATE_ARRAY(array_ptr); - } - fe_ht = Z_ARRVAL_P(array_ptr); - } - while (1) { - if (pos >= fe_ht->nNumUsed) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - p = fe_ht->arData + pos; - if ((Z_TYPE(p->val) != IS_UNDEF && - (Z_TYPE(p->val) != IS_INDIRECT || - Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && - (Z_TYPE_P(array_ptr) != IS_OBJECT || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { - break; - } - pos++; } - fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); - - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -12088,7 +12279,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A zend_object *zobj = Z_OBJ_P(array); fe_ht = Z_OBJPROP_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -12127,7 +12318,24 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A } } ZVAL_COPY(EX_VAR(opline->result.var), value); - Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1; + while (1) { + pos++; + if (pos >= fe_ht->nNumUsed) { + pos = INVALID_IDX; + break; + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + break; + } + } + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -24217,68 +24425,102 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR { USE_OPLINE - zval *array_ptr; + zval *array_ptr, *result; HashTable *fe_ht; SAVE_OPLINE(); array_ptr = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); - if (IS_CV != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); - zend_bool is_empty; + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CV != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + CHECK_EXCEPTION(); + 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) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CV != IS_TMP_VAR) { + Z_ADDREF_P(array_ptr); + } + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + while (1) { + if (pos >= fe_ht->nNumUsed) { - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - is_empty = iter->funcs->valid(iter) != SUCCESS; + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); + HANDLE_EXCEPTION(); + } + } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ + is_empty = iter->funcs->valid(iter) != SUCCESS; - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zval *result = EX_VAR(opline->result.var); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ - ZVAL_COPY_VALUE(result, array_ptr); - if (IS_CV != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { - Z_ADDREF_P(array_ptr); - } - Z_FE_POS_P(result) = 0; + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } @@ -24290,6 +24532,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A zval *array_ptr, *array_ref; HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; SAVE_OPLINE(); @@ -24302,70 +24546,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A array_ref = array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); } - if (IS_CV != IS_CONST && - Z_TYPE_P(array_ptr) == IS_OBJECT && Z_OBJCE_P(array_ptr)->get_iterator) { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); - zend_bool is_empty; - - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - if (IS_CV == IS_VAR) { - - } else { - - } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); - } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } - - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - if (IS_CV == IS_VAR) { - - } else { - - } - HANDLE_EXCEPTION(); - } - } - - is_empty = iter->funcs->valid(iter) != SUCCESS; - - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - if (IS_CV == IS_VAR) { - - } else { - - } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - if (IS_CV == IS_VAR) { - - } else { - - } - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPosition pos = 0; - Bucket *p; - + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { if (IS_CV == IS_VAR || IS_CV == IS_CV) { if (array_ptr == array_ref) { ZVAL_NEW_REF(array_ref, array_ref); @@ -24377,14 +24558,12 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A array_ptr = EX_VAR(opline->result.var); ZVAL_COPY_VALUE(array_ptr, array_ref); } - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - if (IS_CV == IS_CONST) { - zval_copy_ctor_func(array_ptr); - } else { - SEPARATE_ARRAY(array_ptr); - } - fe_ht = Z_ARRVAL_P(array_ptr); + if (IS_CV == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); } + fe_ht = Z_ARRVAL_P(array_ptr); while (1) { if (pos >= fe_ht->nNumUsed) { @@ -24392,12 +24571,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; - if ((Z_TYPE(p->val) != IS_UNDEF && - (Z_TYPE(p->val) != IS_INDIRECT || - Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && - (Z_TYPE_P(array_ptr) != IS_OBJECT || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { break; } pos++; @@ -24407,6 +24583,101 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A CHECK_EXCEPTION(); 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) { + if (IS_CV == IS_VAR || IS_CV == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + while (1) { + if (pos >= fe_ht->nNumUsed) { + + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + p = fe_ht->arData + pos; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_CV == IS_VAR) { + + } else { + + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_CV == IS_VAR) { + + } else { + + } + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_CV == IS_VAR) { + + } else { + + } + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (IS_CV == IS_VAR) { + + } else { + + } + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + } } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -33147,7 +33418,7 @@ static int ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_A SAVE_OPLINE(); var = EX_VAR(opline->op1.var); - if (Z_FE_ITER_P(var) != (uint32_t)-1) { + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); From cc4b7be41e2e2b9b0d7a3c8e98466b8886692e6e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 12:24:31 +0300 Subject: [PATCH 11/16] Make internal function, operation on array passed by reference, to preserve foreach hash position --- Zend/tests/foreach_011.phpt | 19 +++++ Zend/tests/foreach_012.phpt | 18 +++++ Zend/tests/foreach_013.phpt | 17 ++++ Zend/tests/foreach_014.phpt | 15 ++++ Zend/tests/foreach_015.phpt | 18 +++++ Zend/tests/foreach_016.phpt | 18 +++++ ext/standard/array.c | 137 +++++++++++++++++++++++--------- tests/lang/foreachLoop.013.phpt | 103 ++++++------------------ tests/lang/foreachLoop.015.phpt | 103 ++++++------------------ 9 files changed, 253 insertions(+), 195 deletions(-) create mode 100644 Zend/tests/foreach_011.phpt create mode 100644 Zend/tests/foreach_012.phpt create mode 100644 Zend/tests/foreach_013.phpt create mode 100644 Zend/tests/foreach_014.phpt create mode 100644 Zend/tests/foreach_015.phpt create mode 100644 Zend/tests/foreach_016.phpt diff --git a/Zend/tests/foreach_011.phpt b/Zend/tests/foreach_011.phpt new file mode 100644 index 000000000000..e91426fb27df --- /dev/null +++ b/Zend/tests/foreach_011.phpt @@ -0,0 +1,19 @@ +--TEST-- +sort() functions precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +2 +1 +0 diff --git a/Zend/tests/foreach_012.phpt b/Zend/tests/foreach_012.phpt new file mode 100644 index 000000000000..5e5538cd4d30 --- /dev/null +++ b/Zend/tests/foreach_012.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_walk() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +14 +15 \ No newline at end of file diff --git a/Zend/tests/foreach_013.phpt b/Zend/tests/foreach_013.phpt new file mode 100644 index 000000000000..cfbb3d7f7915 --- /dev/null +++ b/Zend/tests/foreach_013.phpt @@ -0,0 +1,17 @@ +--TEST-- +array_push() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +4 diff --git a/Zend/tests/foreach_014.phpt b/Zend/tests/foreach_014.phpt new file mode 100644 index 000000000000..8d0ac582a9ec --- /dev/null +++ b/Zend/tests/foreach_014.phpt @@ -0,0 +1,15 @@ +--TEST-- +array_pop() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 diff --git a/Zend/tests/foreach_015.phpt b/Zend/tests/foreach_015.phpt new file mode 100644 index 000000000000..adc8085f3482 --- /dev/null +++ b/Zend/tests/foreach_015.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_shift() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +4 +array(0) { +} \ No newline at end of file diff --git a/Zend/tests/foreach_016.phpt b/Zend/tests/foreach_016.phpt new file mode 100644 index 000000000000..423c8dd0a624 --- /dev/null +++ b/Zend/tests/foreach_016.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_unshift() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +int(11) diff --git a/ext/standard/array.c b/ext/standard/array.c index 49c6cd0789bc..526c9210c1ec 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1877,26 +1877,49 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ hash = Z_ARRVAL_P(array); n_left = n_elems; - if (hash->nNumUsed != hash->nNumOfElements) { - for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { - p = hash->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (j != idx) { - hash->arData[j] = *p; + if (EXPECTED(hash->u.v.nIteratorsCount == 0)) { + if (hash->nNumUsed != hash->nNumOfElements) { + for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (j != idx) { + hash->arData[j] = *p; + } + j++; } - j++; } - } - while (--n_left) { - rnd_idx = php_rand(); - RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); - if (rnd_idx != n_left) { - temp = hash->arData[n_left]; - hash->arData[n_left] = hash->arData[rnd_idx]; - hash->arData[rnd_idx] = temp; + while (--n_left) { + rnd_idx = php_rand(); + RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); + if (rnd_idx != n_left) { + temp = hash->arData[n_left]; + hash->arData[n_left] = hash->arData[rnd_idx]; + hash->arData[rnd_idx] = temp; + } + } + } else { + if (hash->nNumUsed != hash->nNumOfElements) { + for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (j != idx) { + hash->arData[j] = *p; + zend_hash_iterators_update(hash, idx, j); + } + j++; + } + } + while (--n_left) { + rnd_idx = php_rand(); + RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); + if (rnd_idx != n_left) { + temp = hash->arData[n_left]; + hash->arData[n_left] = hash->arData[rnd_idx]; + hash->arData[rnd_idx] = temp; + zend_hash_iterators_update(hash, rnd_idx, n_left); + } } } - HANDLE_BLOCK_INTERRUPTIONS(); hash->nNumUsed = n_elems; hash->nInternalPointer = 0; @@ -2194,18 +2217,33 @@ PHP_FUNCTION(array_shift) if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) { uint32_t k = 0; - for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { - p = Z_ARRVAL_P(stack)->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (idx != k) { - Bucket *q = Z_ARRVAL_P(stack)->arData + k; - q->h = k; - q->key = NULL; - ZVAL_COPY_VALUE(&q->val, &p->val); - ZVAL_UNDEF(&p->val); - zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) { + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (idx != k) { + Bucket *q = Z_ARRVAL_P(stack)->arData + k; + q->h = k; + q->key = NULL; + ZVAL_COPY_VALUE(&q->val, &p->val); + ZVAL_UNDEF(&p->val); + } + k++; + } + } else { + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (idx != k) { + Bucket *q = Z_ARRVAL_P(stack)->arData + k; + q->h = k; + q->key = NULL; + ZVAL_COPY_VALUE(&q->val, &p->val); + ZVAL_UNDEF(&p->val); + zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + } + k++; } - k++; } Z_ARRVAL_P(stack)->nNumUsed = k; Z_ARRVAL_P(stack)->nNextFreeElement = k; @@ -2258,21 +2296,46 @@ PHP_FUNCTION(array_unshift) } zend_hash_next_index_insert_new(&new_hash, &args[i]); } - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { - if (key) { - zend_hash_add_new(&new_hash, key, value); - } else { - zend_hash_next_index_insert_new(&new_hash, value); - } - } ZEND_HASH_FOREACH_END(); + if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { + if (key) { + zend_hash_add_new(&new_hash, key, value); + } else { + zend_hash_next_index_insert_new(&new_hash, value); + } + } ZEND_HASH_FOREACH_END(); + } else { + uint32_t old_idx; + uint32_t new_idx = i; - new_hash.u.v.nIteratorsCount = Z_ARRVAL_P(stack)->u.v.nIteratorsCount; + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { + if (key) { + zend_hash_add_new(&new_hash, key, value); + } else { + zend_hash_next_index_insert_new(&new_hash, value); + } + old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData; + zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx); + new_idx++; + } ZEND_HASH_FOREACH_END(); + } + + /* replace HashTable data */ Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0; Z_ARRVAL_P(stack)->pDestructor = NULL; zend_hash_destroy(Z_ARRVAL_P(stack)); - *Z_ARRVAL_P(stack) = new_hash; + + Z_ARRVAL_P(stack)->u.v.flags = new_hash.u.v.flags; + Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize; + Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask; + Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed; + Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements; + Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement; + Z_ARRVAL_P(stack)->arData = new_hash.arData; + Z_ARRVAL_P(stack)->arHash = new_hash.arHash; + Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor; + zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); - zend_hash_iterators_reset(Z_ARRVAL_P(stack)); /* Clean up and return the number of elements in the stack */ RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack))); diff --git a/tests/lang/foreachLoop.013.phpt b/tests/lang/foreachLoop.013.phpt index 10f43f399865..ea728156f0f4 100644 --- a/tests/lang/foreachLoop.013.phpt +++ b/tests/lang/foreachLoop.013.phpt @@ -445,28 +445,12 @@ array(1) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** --> State of array after loop: -array(7) { +array(2) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> - string(5) "new.1" - [5]=> string(5) "new.0" - [6]=> - string(3) "v.0" + [1]=> + &string(3) "v.0" } ---( Array with 2 element(s): )--- @@ -479,30 +463,17 @@ array(2) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=2; $v=v.1 --> State of array after loop: -array(8) { +array(4) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> - string(3) "v.1" + [3]=> + &string(3) "v.1" } ---( Array with 3 element(s): )--- @@ -517,32 +488,19 @@ array(3) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=3; $v=v.2 --> State of array after loop: -array(9) { +array(5) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" - [8]=> - string(3) "v.2" + [4]=> + &string(3) "v.2" } ---( Array with 4 element(s): )--- @@ -559,32 +517,19 @@ array(4) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=4; $v=v.3 --> State of array after loop: -array(10) { +array(6) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" - [8]=> + [4]=> string(3) "v.2" - [9]=> - string(3) "v.3" + [5]=> + &string(3) "v.3" } diff --git a/tests/lang/foreachLoop.015.phpt b/tests/lang/foreachLoop.015.phpt index a56249ee0b40..a4ee09d6a969 100644 --- a/tests/lang/foreachLoop.015.phpt +++ b/tests/lang/foreachLoop.015.phpt @@ -447,28 +447,12 @@ array(1) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** --> State of array after loop: -array(7) { +array(2) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> - string(5) "new.1" - [5]=> string(5) "new.0" - [6]=> - string(3) "v.0" + [1]=> + &string(3) "v.0" } ---( Array with 2 element(s): )--- @@ -481,30 +465,17 @@ array(2) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=2; $v=v.1 --> State of array after loop: -array(8) { +array(4) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> - string(3) "v.1" + [3]=> + &string(3) "v.1" } ---( Array with 3 element(s): )--- @@ -519,32 +490,19 @@ array(3) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=3; $v=v.2 --> State of array after loop: -array(9) { +array(5) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" - [8]=> - string(3) "v.2" + [4]=> + &string(3) "v.2" } ---( Array with 4 element(s): )--- @@ -561,32 +519,19 @@ array(4) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=4; $v=v.3 --> State of array after loop: -array(10) { +array(6) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" - [8]=> + [4]=> string(3) "v.2" - [9]=> - string(3) "v.3" + [5]=> + &string(3) "v.3" } From 08302c0d6d1cab279b9f2129df03a057baddf2ff Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 14:20:46 +0300 Subject: [PATCH 12/16] Make array_splice() to preserve foreach hash position --- Zend/tests/foreach_017.phpt | 111 ++++++++++++++++++++++++++++++++++++ Zend/zend_hash.c | 15 ----- Zend/zend_hash.h | 1 - ext/standard/array.c | 33 +++++++++-- 4 files changed, 138 insertions(+), 22 deletions(-) create mode 100644 Zend/tests/foreach_017.phpt diff --git a/Zend/tests/foreach_017.phpt b/Zend/tests/foreach_017.phpt new file mode 100644 index 000000000000..780d2e90fc3a --- /dev/null +++ b/Zend/tests/foreach_017.phpt @@ -0,0 +1,111 @@ +--TEST-- +array_unshift() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +0 +1 +2 +3 +4 + +0 +1 +4 + +0 +1 +2 +4 + +0 +1 +2 +3 +4 + +0 +1 +x +y +z +4 + +0 +1 +2 +4 diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 82617edf187e..8a2f6a26c328 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -354,21 +354,6 @@ ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashP } } -ZEND_API void zend_hash_iterators_reset(HashTable *ht) -{ - if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - HashTableIterator *iter = EG(ht_iterators); - HashTableIterator *end = iter + EG(ht_iterators_used); - - while (iter != end) { - if (iter->ht == ht) { - iter->pos = ht->nInternalPointer; - } - iter++; - } - } -} - static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key) { zend_ulong h; diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 749846aaf9be..d244995a9ae0 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -230,7 +230,6 @@ ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht); ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht); ZEND_API void zend_hash_iterator_del(uint32_t idx); ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to); -ZEND_API void zend_hash_iterators_reset(HashTable *ht); END_EXTERN_C() diff --git a/ext/standard/array.c b/ext/standard/array.c index 526c9210c1ec..a7e60899dd1b 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1989,7 +1989,6 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) { p = in_hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - pos++; /* Get entry and increase reference count */ entry = &p->val; @@ -1999,6 +1998,10 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } else { zend_hash_add_new(&out_hash, p->key, entry); } + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + pos++; } /* If hash for removed entries exists, go until offset+length and copy the entries to it */ @@ -2024,10 +2027,12 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } } } else { /* otherwise just skip those entries */ - for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) { + int pos2 = pos; + + for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) { p = in_hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - pos++; + pos2++; if (p->key == NULL) { zend_hash_index_del(in_hash, p->h); } else { @@ -2045,6 +2050,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re ZEND_HASH_FOREACH_VAL_IND(replace, entry) { if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); zend_hash_next_index_insert_new(&out_hash, entry); + pos++; } ZEND_HASH_FOREACH_END(); } @@ -2058,13 +2064,28 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } else { zend_hash_add_new(&out_hash, p->key, entry); } + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + pos++; } - zend_hash_internal_pointer_reset(&out_hash); - + /* replace HashTable data */ + in_hash->u.v.nIteratorsCount = 0; in_hash->pDestructor = NULL; zend_hash_destroy(in_hash); - *in_hash = out_hash; + + in_hash->u.v.flags = out_hash.u.v.flags; + in_hash->nTableSize = out_hash.nTableSize; + in_hash->nTableMask = out_hash.nTableMask; + in_hash->nNumUsed = out_hash.nNumUsed; + in_hash->nNumOfElements = out_hash.nNumOfElements; + in_hash->nNextFreeElement = out_hash.nNextFreeElement; + in_hash->arData = out_hash.arData; + in_hash->arHash = out_hash.arHash; + in_hash->pDestructor = out_hash.pDestructor; + + zend_hash_internal_pointer_reset(in_hash); } /* }}} */ From fb2d079645829b12ed4e55a461034df6400bc430 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 18:08:05 +0300 Subject: [PATCH 13/16] API cleanup --- Zend/zend_hash.c | 137 +++++++++++++++++++++++------------------------ Zend/zend_hash.h | 10 +++- 2 files changed, 77 insertions(+), 70 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 8a2f6a26c328..815444bbf5d0 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -269,7 +269,7 @@ ZEND_API void zend_hash_iterator_del(uint32_t idx) } } -static zend_never_inline void _iterators_del(HashTable *ht) +static zend_never_inline void _zend_hash_iterators_remove(HashTable *ht) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); @@ -289,68 +289,41 @@ static zend_never_inline void _iterators_del(HashTable *ht) EG(ht_iterators_used) = idx; } -static zend_always_inline void iterators_del(HashTable *ht) +static zend_always_inline void zend_hash_iterators_remove(HashTable *ht) { if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - _iterators_del(ht); + _zend_hash_iterators_remove(ht); } } -static zend_never_inline void _iterators_update(HashTable *ht, HashPosition from, HashPosition to) +ZEND_API HashPosition zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); + HashPosition res = INVALID_IDX; + uint32_t idx; while (iter != end) { - if (iter->ht == ht && iter->pos == from) { - iter->pos = to; + if (iter->ht == ht) { + if (iter->pos >= start && iter->pos < res) { + res = iter->pos; + } } iter++; } + return res; } -static zend_always_inline void iterators_update(HashTable *ht, HashPosition from, HashPosition to) -{ - if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - _iterators_update(ht, from, to); - } -} - -static zend_never_inline void _iterators_update_to_next(HashTable *ht, HashPosition from) -{ - uint32_t to = from; - - while (1) { - to++; - if (to >= ht->nNumUsed) { - to = INVALID_IDX; - break; - } else if (Z_TYPE(ht->arData[to].val) != IS_UNDEF) { - break; - } - } - _iterators_update(ht, from, to); -} - -static zend_always_inline void iterators_update_to_next(HashTable *ht, HashPosition from) -{ - if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - _iterators_update_to_next(ht, from); - } -} - -ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) +ZEND_API void _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) { - if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - HashTableIterator *iter = EG(ht_iterators); - HashTableIterator *end = iter + EG(ht_iterators_used); + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); - while (iter != end) { - if (iter->ht == ht && iter->pos == from) { - iter->pos = to; - } - iter++; + while (iter != end) { + if (iter->ht == ht && iter->pos == from) { + iter->pos = to; } + iter++; } } @@ -466,7 +439,7 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = idx; } - iterators_update(ht, INVALID_IDX, idx); + zend_hash_iterators_update(ht, INVALID_IDX, idx); p = ht->arData + idx; p->h = h = zend_string_hash_val(key); p->key = key; @@ -634,7 +607,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = h; } - iterators_update(ht, INVALID_IDX, h); + zend_hash_iterators_update(ht, INVALID_IDX, h); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -677,7 +650,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = idx; } - iterators_update(ht, INVALID_IDX, idx); + zend_hash_iterators_update(ht, INVALID_IDX, idx); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -758,20 +731,42 @@ ZEND_API int zend_hash_rehash(HashTable *ht) } memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t)); - for (i = 0, j = 0; i < ht->nNumUsed; i++) { - p = ht->arData + i; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (i != j) { - ht->arData[j] = ht->arData[i]; - if (ht->nInternalPointer == i) { - ht->nInternalPointer = j; + if (EXPECTED(ht->u.v.nIteratorsCount == 0)) { + for (i = 0, j = 0; i < ht->nNumUsed; i++) { + p = ht->arData + i; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (i != j) { + ht->arData[j] = ht->arData[i]; + if (ht->nInternalPointer == i) { + ht->nInternalPointer = j; + } } - iterators_update(ht, i, j); + nIndex = ht->arData[j].h & ht->nTableMask; + Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; + ht->arHash[nIndex] = j; + j++; + } + } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(ht, 0); + + for (i = 0, j = 0; i < ht->nNumUsed; i++) { + p = ht->arData + i; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (i != j) { + ht->arData[j] = ht->arData[i]; + if (ht->nInternalPointer == i) { + ht->nInternalPointer = j; + } + if (i == iter_pos) { + zend_hash_iterators_update(ht, i, j); + iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1); + } + } + nIndex = ht->arData[j].h & ht->nTableMask; + Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; + ht->arHash[nIndex] = j; + j++; } - nIndex = ht->arData[j].h & ht->nTableMask; - Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; - ht->arHash[nIndex] = j; - j++; } ht->nNumUsed = j; return SUCCESS; @@ -793,18 +788,22 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, } while (ht->nNumUsed > 0 && (Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF)); } ht->nNumOfElements--; - iterators_update_to_next(ht, idx); - if (ht->nInternalPointer == idx) { + if (ht->nInternalPointer == idx || UNEXPECTED(ht->u.v.nIteratorsCount)) { + uint32_t new_idx = idx; + while (1) { - idx++; - if (idx >= ht->nNumUsed) { - ht->nInternalPointer = INVALID_IDX; + new_idx++; + if (new_idx >= ht->nNumUsed) { + new_idx = INVALID_IDX; break; - } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { - ht->nInternalPointer = idx; + } else if (Z_TYPE(ht->arData[new_idx].val) != IS_UNDEF) { break; } } + if (ht->nInternalPointer == idx) { + ht->nInternalPointer = new_idx; + } + zend_hash_iterators_update(ht, idx, new_idx); } if (p->key) { zend_string_release(p->key); @@ -1059,7 +1058,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht) } while (++p != end); } } - iterators_del(ht); + zend_hash_iterators_remove(ht); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; } @@ -1100,7 +1099,7 @@ ZEND_API void zend_array_destroy(HashTable *ht) } } while (++p != end); } - iterators_del(ht); + zend_hash_iterators_remove(ht); SET_INCONSISTENT(HT_DESTROYED); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index d244995a9ae0..982c63b492d2 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -229,7 +229,15 @@ ZEND_API int _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ul ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht); ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht); ZEND_API void zend_hash_iterator_del(uint32_t idx); -ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to); +ZEND_API HashPosition zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start); +ZEND_API void _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to); + +static zend_always_inline void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _zend_hash_iterators_update(ht, from, to); + } +} END_EXTERN_C() From b37f1d58d2a141b6e1d980a461ccb588d4317d2e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 18:08:30 +0300 Subject: [PATCH 14/16] Fixed test name --- Zend/tests/foreach_017.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/tests/foreach_017.phpt b/Zend/tests/foreach_017.phpt index 780d2e90fc3a..55eeeb0891b6 100644 --- a/Zend/tests/foreach_017.phpt +++ b/Zend/tests/foreach_017.phpt @@ -1,5 +1,5 @@ --TEST-- -array_unshift() function precerve foreach by reference iterator pointer +array_splice() function precerve foreach by reference iterator pointer --FILE-- Date: Fri, 30 Jan 2015 18:08:43 +0300 Subject: [PATCH 15/16] Reduced alghorithms complexity --- ext/standard/array.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index a7e60899dd1b..f923ad9e7b56 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1898,13 +1898,18 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ } } } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0); + if (hash->nNumUsed != hash->nNumOfElements) { for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { p = hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; if (j != idx) { hash->arData[j] = *p; - zend_hash_iterators_update(hash, idx, j); + if (idx == iter_pos) { + zend_hash_iterators_update(hash, idx, j); + iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1); + } } j++; } @@ -1964,6 +1969,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re uint idx; Bucket *p; /* Pointer to hash bucket */ zval *entry; /* Hash entry */ + uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0); /* Get number of entries in the input hash */ num_in = zend_hash_num_elements(in_hash); @@ -1998,8 +2004,11 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } else { zend_hash_add_new(&out_hash, p->key, entry); } - if (idx != pos) { - zend_hash_iterators_update(in_hash, idx, pos); + if (idx == iter_pos) { + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1); } pos++; } @@ -2044,6 +2053,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } } } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos); /* If there are entries to insert.. */ if (replace) { @@ -2064,8 +2074,11 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } else { zend_hash_add_new(&out_hash, p->key, entry); } - if (idx != pos) { - zend_hash_iterators_update(in_hash, idx, pos); + if (idx == iter_pos) { + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1); } pos++; } @@ -2252,6 +2265,8 @@ PHP_FUNCTION(array_shift) k++; } } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0); + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { p = Z_ARRVAL_P(stack)->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; @@ -2261,7 +2276,10 @@ PHP_FUNCTION(array_shift) q->key = NULL; ZVAL_COPY_VALUE(&q->val, &p->val); ZVAL_UNDEF(&p->val); - zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + if (idx == iter_pos) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1); + } } k++; } @@ -2328,6 +2346,7 @@ PHP_FUNCTION(array_unshift) } else { uint32_t old_idx; uint32_t new_idx = i; + uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { if (key) { @@ -2336,7 +2355,10 @@ PHP_FUNCTION(array_unshift) zend_hash_next_index_insert_new(&new_hash, value); } old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData; - zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx); + if (old_idx == iter_pos) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx); + iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1); + } new_idx++; } ZEND_HASH_FOREACH_END(); } From 1e41295097576dbce6c197ddb7507c07ccae3cbe Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Sat, 31 Jan 2015 07:28:58 +0300 Subject: [PATCH 16/16] Generalize HashTableIterator API to allows its usage without iinvolvement of HashTable.nInternalPonter --- Zend/zend_hash.c | 6 +++--- Zend/zend_hash.h | 2 +- Zend/zend_vm_def.h | 6 +++--- Zend/zend_vm_execute.h | 24 ++++++++++++------------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 815444bbf5d0..195b5e48f0e1 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -193,7 +193,7 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt } } -ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht, HashPosition pos) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_count); @@ -205,7 +205,7 @@ ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) while (iter != end) { if (iter->ht == NULL) { iter->ht = ht; - iter->pos = ht->nInternalPointer; + iter->pos = pos; idx = iter - EG(ht_iterators); if (idx + 1 > EG(ht_iterators_used)) { EG(ht_iterators_used) = idx + 1; @@ -223,7 +223,7 @@ ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) iter = EG(ht_iterators) + EG(ht_iterators_count); EG(ht_iterators_count) += 8; iter->ht = ht; - iter->pos = ht->nInternalPointer; + iter->pos = pos; memset(iter + 1, 0, sizeof(HashTableIterator) * 7); idx = iter - EG(ht_iterators); EG(ht_iterators_used) = idx + 1; diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 982c63b492d2..602cd7a2e457 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -226,7 +226,7 @@ void zend_hash_display(const HashTable *ht); ZEND_API int _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx); -ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht); +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht, HashPosition pos); ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht); ZEND_API void zend_hash_iterator_del(uint32_t idx); ZEND_API HashPosition zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 064c756c6e23..80f7ef3c1494 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4693,7 +4693,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); FREE_OP1_IF_VAR(); CHECK_EXCEPTION(); @@ -4804,7 +4804,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); FREE_OP1_VAR_PTR(); CHECK_EXCEPTION(); @@ -4840,7 +4840,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); FREE_OP1_VAR_PTR(); CHECK_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 8ae97d2bf807..471f73aa328d 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3104,7 +3104,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -3213,7 +3213,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -3248,7 +3248,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -9101,7 +9101,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -9211,7 +9211,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -9246,7 +9246,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -12001,7 +12001,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -12112,7 +12112,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; CHECK_EXCEPTION(); @@ -12148,7 +12148,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; CHECK_EXCEPTION(); @@ -24470,7 +24470,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -24579,7 +24579,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -24614,7 +24614,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A pos++; } fe_ht->nInternalPointer = pos; - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE();