From 4638f7b91407c48710007af82a68da0007c820f2 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 07:43:28 +0300 Subject: 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 21eaae9444..65e32533ef 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 374f73b75e..8a679654d5 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 7476d475de..e37fdd778c 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 7c82c67d6d..2d71ca2571 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 377f508658..85f1890302 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 a6d041f131..c1e3adb3ec 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 5c00cb133f..aa5404139b 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 24bb3fd3d6..aae951eeed 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 9a7b23d87a..a51941195c 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 c3006e6036..295600c6fd 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 f0fc7c7182..9cb1679e98 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 7948990f81..3df0faa0ef 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 0bb8cce17f..dccd010ae4 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 27ba6bad8e..ac5389ed2b 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 348fcb8efb..341e80501b 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 c3a55b319f..3019b274e9 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -328,7 +328,8 @@ continue_jmp_ex_optimization: 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 @@ continue_jmp_ex_optimization: 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 54063d0ed3..a8506fb078 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 3d63f96e34..5b32baa64b 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 4ddb82e8c6..f8778fffe3 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 b24f14e81d..c35b9b44e5 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 4586a35a10..df51fd6be4 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 671cfaf354..8527a8833e 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 d32ea635fd..9b179ab22a 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 8218b44dab..5204aca9ca 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" -- cgit v1.2.1 From dd2a36a2074bbb0cb31de00b66dcf2812d6d753f Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 10:02:34 +0300 Subject: 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 aae951eeed..b04324c4ed 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 a51941195c..3eead75c73 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); -- cgit v1.2.1 From 92e90c09f085c22707ff4a59201f016f56e0ef8b Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 12:44:57 +0300 Subject: 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 0000000000..71b0f2a5a3 --- /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 0000000000..1f754a77ed --- /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 b04324c4ed..52a24d0859 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 3eead75c73..3d0dd2e97d 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)); } } -- cgit v1.2.1 From 61e739187391661e2d541947bec25d7dcc4479f3 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 14:59:54 +0300 Subject: 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 0000000000..6ed9fe940b --- /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 ac5389ed2b..7ff94ddae5 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 */ -- cgit v1.2.1 From eef80c583762d1e98d177cdbb27e3a8a6b0c4539 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 28 Jan 2015 16:52:21 +0300 Subject: 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 0000000000..65d6fdc52c --- /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 52a24d0859..5ddc903888 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 3d0dd2e97d..362f450390 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) { -- cgit v1.2.1 From 10a3260b1f16b6075fd8140f673dfef4d5efea91 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 29 Jan 2015 21:04:44 +0300 Subject: 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 0000000000..b99bc73ebe --- /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 -- cgit v1.2.1 From 15a23b1218b3e38630d677751a975907daa2cd54 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 29 Jan 2015 21:05:02 +0300 Subject: 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 e37fdd778c..080b31562a 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 2d71ca2571..5239f565b3 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 7a79defb05..d59aef1d1b 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 c9da46d819..c6a0d8f0c9 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 f19bb85013..1f25d07ea9 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 beca5ad631..abebb19ae3 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 480ac64dd1..dd175c7c00 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 @@ add_to_hash: 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 @@ add_to_packed: } 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 @@ add_to_hash: 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 85f1890302..c8218f1112 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 aa5404139b..dbe8c616df 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 5ddc903888..245bc0b68a 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 362f450390..4244f5f750 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 295600c6fd..d7c5db6966 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 9cb1679e98..aa4afbd984 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 3df0faa0ef..4db48944e1 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 7ff94ddae5..dc93ce2f4c 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 5be1e30c9f..b578592493 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 966ea0f1f6..397bb88f0b 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 b0c5e8dcf5..10f43f3998 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 5b12a2b0c2..a56249ee0b 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): )--- -- cgit v1.2.1 From 721fc9e80d2ee8f2cd79c8c3cdceffae2c72de92 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 29 Jan 2015 21:43:28 +0300 Subject: 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 0000000000..c68bcd89da --- /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 -- cgit v1.2.1 From 4c5b385ff53ae9f0b52572e98c4db801f56603b0 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 07:56:37 +0300 Subject: 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 0000000000..6ce8384642 --- /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 dd175c7c00..82617edf18 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 @@ add_to_hash: 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 @@ add_to_packed: } 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 @@ add_to_hash: 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 c8218f1112..749846aaf9 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 397bb88f0b..49c6cd0789 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))); -- cgit v1.2.1 From 5aa9712b0a30303aadfe3bdd8ae1f072ca3e6ba1 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 09:49:35 +0300 Subject: 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 0000000000..6ba7e7e9fd --- /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 080b31562a..79293971e8 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 d59aef1d1b..1a81c298a8 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 1f25d07ea9..971d4e7bc8 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 245bc0b68a..064c756c6e 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 4244f5f750..8ae97d2bf8 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); -- cgit v1.2.1 From cc4b7be41e2e2b9b0d7a3c8e98466b8886692e6e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 12:24:31 +0300 Subject: 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 0000000000..e91426fb27 --- /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 0000000000..5e5538cd4d --- /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 0000000000..cfbb3d7f79 --- /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 0000000000..8d0ac582a9 --- /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 0000000000..adc8085f34 --- /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 0000000000..423c8dd0a6 --- /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 49c6cd0789..526c9210c1 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 10f43f3998..ea728156f0 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 a56249ee0b..a4ee09d6a9 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" } -- cgit v1.2.1 From 08302c0d6d1cab279b9f2129df03a057baddf2ff Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 14:20:46 +0300 Subject: 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 0000000000..780d2e90fc --- /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 82617edf18..8a2f6a26c3 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 749846aaf9..d244995a9a 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 526c9210c1..a7e60899dd 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); } /* }}} */ -- cgit v1.2.1 From fb2d079645829b12ed4e55a461034df6400bc430 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 18:08:05 +0300 Subject: 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 8a2f6a26c3..815444bbf5 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 @@ add_to_hash: 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 @@ add_to_packed: 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 @@ add_to_hash: 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 d244995a9a..982c63b492 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() -- cgit v1.2.1 From b37f1d58d2a141b6e1d980a461ccb588d4317d2e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 18:08:30 +0300 Subject: 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 780d2e90fc..55eeeb0891 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: 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 a7e60899dd..f923ad9e7b 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(); } -- cgit v1.2.1 From 1e41295097576dbce6c197ddb7507c07ccae3cbe Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Sat, 31 Jan 2015 07:28:58 +0300 Subject: 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 815444bbf5..195b5e48f0 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 982c63b492..602cd7a2e4 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 064c756c6e..80f7ef3c14 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 8ae97d2bf8..471f73aa32 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(); -- cgit v1.2.1