From: Nikita Popov Date: Fri, 24 Aug 2012 11:29:40 +0000 (+0200) Subject: Merge remote-tracking branch 'php-src/master' into addGeneratorsSupport X-Git-Tag: php-5.5.0alpha1~20^2~24^2~24^2~9 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6517ed021520a608a18da4653cb9c6b414121f6f;p=php Merge remote-tracking branch 'php-src/master' into addGeneratorsSupport Conflicts: Zend/zend_vm_def.h Zend/zend_vm_execute.h --- 6517ed021520a608a18da4653cb9c6b414121f6f diff --cc Zend/zend_vm_def.h index f8955c5bf5,ce1674e4f3..975a2a7071 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@@ -1840,291 -1840,296 +1840,294 @@@ ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VA ZEND_VM_NEXT_OPCODE(); } - ZEND_VM_HANDLER(42, ZEND_JMP, ANY, ANY) + ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) { - USE_OPLINE - - #if DEBUG_ZEND>=2 - printf("Jumping to %d\n", opline->op1.opline_num); - #endif - ZEND_VM_SET_OPCODE(opline->op1.jmp_addr); - ZEND_VM_CONTINUE(); /* CHECK_ME */ - } + zend_bool nested; + zend_op_array *op_array = EX(op_array); - ZEND_VM_HANDLER(43, ZEND_JMPZ, CONST|TMP|VAR|CV, ANY) - { - USE_OPLINE - zend_free_op free_op1; - zval *val; - int ret; ++ /* Generators go throw a different cleanup process */ ++ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { ++ /* The generator object is stored in return_value_ptr_ptr */ ++ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + - SAVE_OPLINE(); - val = GET_OP1_ZVAL_PTR(BP_VAR_R); ++ /* Close the generator to free up resources */ ++ zend_generator_close(generator, 1 TSRMLS_CC); + - if (OP1_TYPE == IS_TMP_VAR && EXPECTED(Z_TYPE_P(val) == IS_BOOL)) { - ret = Z_LVAL_P(val); - } else { - ret = i_zend_is_true(val); - FREE_OP1(); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); - } - } - if (!ret) { - #if DEBUG_ZEND>=2 - printf("Conditional jmp to %d\n", opline->op2.opline_num); - #endif - ZEND_VM_SET_OPCODE(opline->op2.jmp_addr); - ZEND_VM_CONTINUE(); ++ /* Pass execution back to handling code */ ++ ZEND_VM_RETURN(); + } + - ZEND_VM_NEXT_OPCODE(); - } + EG(current_execute_data) = EX(prev_execute_data); + EG(opline_ptr) = NULL; + if (!EG(active_symbol_table)) { - zval ***cv = EX_CVs(); - zval ***end = cv + op_array->last_var; - while (cv != end) { - if (*cv) { - zval_ptr_dtor(*cv); - } - cv++; - } ++ zend_free_compiled_variables(EX_CVs(), op_array->last_var); + } - ZEND_VM_HANDLER(44, ZEND_JMPNZ, CONST|TMP|VAR|CV, ANY) - { - USE_OPLINE - zend_free_op free_op1; - zval *val; - int ret; + if ((op_array->fn_flags & ZEND_ACC_CLOSURE) && op_array->prototype) { + zval_ptr_dtor((zval**)&op_array->prototype); + } - SAVE_OPLINE(); - val = GET_OP1_ZVAL_PTR(BP_VAR_R); + nested = EX(nested); - if (OP1_TYPE == IS_TMP_VAR && EXPECTED(Z_TYPE_P(val) == IS_BOOL)) { - ret = Z_LVAL_P(val); - zend_vm_stack_free(execute_data TSRMLS_CC); ++ /* For generators the execute_data is stored on the heap, for everything ++ * else it is stored on the VM stack. */ ++ if (op_array->fn_flags & ZEND_ACC_GENERATOR) { ++ efree(execute_data); + } else { - ret = i_zend_is_true(val); - FREE_OP1(); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); - } ++ zend_vm_stack_free(execute_data TSRMLS_CC); + } - if (ret) { - #if DEBUG_ZEND>=2 - printf("Conditional jmp to %d\n", opline->op2.opline_num); - #endif - ZEND_VM_SET_OPCODE(opline->op2.jmp_addr); - ZEND_VM_CONTINUE(); + + if (nested) { + execute_data = EG(current_execute_data); } + if (nested) { + USE_OPLINE - ZEND_VM_NEXT_OPCODE(); - } + LOAD_REGS(); + LOAD_OPLINE(); + if (UNEXPECTED(opline->opcode == ZEND_INCLUDE_OR_EVAL)) { - ZEND_VM_HANDLER(45, ZEND_JMPZNZ, CONST|TMP|VAR|CV, ANY) - { - USE_OPLINE - zend_free_op free_op1; - zval *val; - int retval; + EX(function_state).function = (zend_function *) EX(op_array); + EX(function_state).arguments = NULL; + EX(object) = EX(current_object); - SAVE_OPLINE(); - val = GET_OP1_ZVAL_PTR(BP_VAR_R); + EG(opline_ptr) = &EX(opline); + EG(active_op_array) = EX(op_array); + EG(return_value_ptr_ptr) = EX(original_return_value); + destroy_op_array(op_array TSRMLS_CC); + efree(op_array); + if (UNEXPECTED(EG(exception) != NULL)) { + zend_throw_exception_internal(NULL TSRMLS_CC); + HANDLE_EXCEPTION_LEAVE(); + } else if (RETURN_VALUE_USED(opline)) { + if (!EX_T(opline->result.var).var.ptr) { /* there was no return statement */ + zval *retval; - if (OP1_TYPE == IS_TMP_VAR && EXPECTED(Z_TYPE_P(val) == IS_BOOL)) { - retval = Z_LVAL_P(val); - } else { - retval = i_zend_is_true(val); - FREE_OP1(); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); - } - } - if (EXPECTED(retval != 0)) { - #if DEBUG_ZEND>=2 - printf("Conditional jmp on true to %d\n", opline->extended_value); - #endif - ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->extended_value]); - ZEND_VM_CONTINUE(); /* CHECK_ME */ - } else { - #if DEBUG_ZEND>=2 - printf("Conditional jmp on false to %d\n", opline->op2.opline_num); - #endif - ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]); - ZEND_VM_CONTINUE(); /* CHECK_ME */ - } - } + ALLOC_ZVAL(retval); + ZVAL_BOOL(retval, 1); + INIT_PZVAL(retval); + EX_T(opline->result.var).var.ptr = retval; + } + } - ZEND_VM_HANDLER(46, ZEND_JMPZ_EX, CONST|TMP|VAR|CV, ANY) - { - USE_OPLINE - zend_free_op free_op1; - zval *val; - int retval; + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); + } else { - + EG(opline_ptr) = &EX(opline); + EG(active_op_array) = EX(op_array); + EG(return_value_ptr_ptr) = EX(original_return_value); + if (EG(active_symbol_table)) { - if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { - zend_hash_destroy(EG(active_symbol_table)); - FREE_HASHTABLE(EG(active_symbol_table)); - } else { - /* clean before putting into the cache, since clean - could call dtors, which could use cached hash */ - zend_hash_clean(EG(active_symbol_table)); - *(++EG(symtable_cache_ptr)) = EG(active_symbol_table); - } ++ zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); + } + EG(active_symbol_table) = EX(symbol_table); - SAVE_OPLINE(); - val = GET_OP1_ZVAL_PTR(BP_VAR_R); + EX(function_state).function = (zend_function *) EX(op_array); + EX(function_state).arguments = NULL; - if (OP1_TYPE == IS_TMP_VAR && EXPECTED(Z_TYPE_P(val) == IS_BOOL)) { - retval = Z_LVAL_P(val); - } else { - retval = i_zend_is_true(val); - FREE_OP1(); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); - } - } - Z_LVAL(EX_T(opline->result.var).tmp_var) = retval; - Z_TYPE(EX_T(opline->result.var).tmp_var) = IS_BOOL; - if (!retval) { - #if DEBUG_ZEND>=2 - printf("Conditional jmp to %d\n", opline->op2.opline_num); - #endif - ZEND_VM_SET_OPCODE(opline->op2.jmp_addr); - ZEND_VM_CONTINUE(); - } - ZEND_VM_NEXT_OPCODE(); - } + if (EG(This)) { + if (UNEXPECTED(EG(exception) != NULL) && IS_CTOR_CALL(EX(called_scope))) { + if (IS_CTOR_USED(EX(called_scope))) { + Z_DELREF_P(EG(This)); + } + if (Z_REFCOUNT_P(EG(This)) == 1) { + zend_object_store_ctor_failed(EG(This) TSRMLS_CC); + } + } + zval_ptr_dtor(&EG(This)); + } + EG(This) = EX(current_this); + EG(scope) = EX(current_scope); + EG(called_scope) = EX(current_called_scope); - ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMP|VAR|CV, ANY) - { - USE_OPLINE - zend_free_op free_op1; - zval *val; - int retval; + EX(object) = EX(current_object); + EX(called_scope) = DECODE_CTOR(EX(called_scope)); - SAVE_OPLINE(); - val = GET_OP1_ZVAL_PTR(BP_VAR_R); + zend_vm_stack_clear_multiple(TSRMLS_C); - if (OP1_TYPE == IS_TMP_VAR && EXPECTED(Z_TYPE_P(val) == IS_BOOL)) { - retval = Z_LVAL_P(val); - } else { - retval = i_zend_is_true(val); - FREE_OP1(); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); + if (UNEXPECTED(EG(exception) != NULL)) { + zend_throw_exception_internal(NULL TSRMLS_CC); + if (RETURN_VALUE_USED(opline) && EX_T(opline->result.var).var.ptr) { + zval_ptr_dtor(&EX_T(opline->result.var).var.ptr); + } + HANDLE_EXCEPTION_LEAVE(); + } + + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); } } - Z_LVAL(EX_T(opline->result.var).tmp_var) = retval; - Z_TYPE(EX_T(opline->result.var).tmp_var) = IS_BOOL; - if (retval) { - #if DEBUG_ZEND>=2 - printf("Conditional jmp to %d\n", opline->op2.opline_num); - #endif - ZEND_VM_SET_OPCODE(opline->op2.jmp_addr); - ZEND_VM_CONTINUE(); - } - ZEND_VM_NEXT_OPCODE(); + ZEND_VM_RETURN(); } - ZEND_VM_HANDLER(70, ZEND_FREE, TMP|VAR, ANY) + ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) { USE_OPLINE + zend_bool should_change_scope = 0; + zend_function *fbc = EX(function_state).function; SAVE_OPLINE(); - if (OP1_TYPE == IS_TMP_VAR) { - zendi_zval_dtor(EX_T(opline->op1.var).tmp_var); - } else { - zval_ptr_dtor(&EX_T(opline->op1.var).var.ptr); + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) { + if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) { + zend_error_noreturn(E_ERROR, "Cannot call abstract method %s::%s()", fbc->common.scope->name, fbc->common.function_name); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); /* Never reached */ + } + if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { + zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated", + fbc->common.scope ? fbc->common.scope->name : "", + fbc->common.scope ? "::" : "", + fbc->common.function_name); + } } - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - - ZEND_VM_HANDLER(53, ZEND_INIT_STRING, ANY, ANY) - { - USE_OPLINE - zval *tmp = &EX_T(opline->result.var).tmp_var; - - SAVE_OPLINE(); - tmp->value.str.val = emalloc(1); - tmp->value.str.val[0] = 0; - tmp->value.str.len = 0; - Z_SET_REFCOUNT_P(tmp, 1); - tmp->type = IS_STRING; - Z_UNSET_ISREF_P(tmp); - /*CHECK_EXCEPTION();*/ - ZEND_VM_NEXT_OPCODE(); - } - - ZEND_VM_HANDLER(54, ZEND_ADD_CHAR, TMP|UNUSED, CONST) - { - USE_OPLINE - zval *str = &EX_T(opline->result.var).tmp_var; - - SAVE_OPLINE(); + if (fbc->common.scope && + !(fbc->common.fn_flags & ZEND_ACC_STATIC) && + !EX(object)) { - if (OP1_TYPE == IS_UNUSED) { - /* Initialize for erealloc in add_char_to_string */ - Z_STRVAL_P(str) = NULL; - Z_STRLEN_P(str) = 0; - Z_TYPE_P(str) = IS_STRING; + if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { + /* FIXME: output identifiers properly */ + zend_error(E_STRICT, "Non-static method %s::%s() should not be called statically", fbc->common.scope->name, fbc->common.function_name); + } else { + /* FIXME: output identifiers properly */ + /* An internal function assumes $this is present and won't check that. So PHP would crash by allowing the call. */ + zend_error_noreturn(E_ERROR, "Non-static method %s::%s() cannot be called statically", fbc->common.scope->name, fbc->common.function_name); + } + } - INIT_PZVAL(str); + if (fbc->type == ZEND_USER_FUNCTION || fbc->common.scope) { + should_change_scope = 1; + EX(current_this) = EG(This); + EX(current_scope) = EG(scope); + EX(current_called_scope) = EG(called_scope); + EG(This) = EX(object); + EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX(object)) ? fbc->common.scope : NULL; + EG(called_scope) = EX(called_scope); } - add_char_to_string(str, str, opline->op2.zv); + zend_arg_types_stack_3_pop(&EG(arg_types_stack), &EX(called_scope), &EX(current_object), &EX(fbc)); + EX(function_state).arguments = zend_vm_stack_push_args(opline->extended_value TSRMLS_CC); + LOAD_OPLINE(); - /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ - /*CHECK_EXCEPTION();*/ - ZEND_VM_NEXT_OPCODE(); - } + if (fbc->type == ZEND_INTERNAL_FUNCTION) { + temp_variable *ret = &EX_T(opline->result.var); - ZEND_VM_HANDLER(55, ZEND_ADD_STRING, TMP|UNUSED, CONST) - { - USE_OPLINE - zval *str = &EX_T(opline->result.var).tmp_var; + MAKE_STD_ZVAL(ret->var.ptr); + ZVAL_NULL(ret->var.ptr); + ret->var.ptr_ptr = &ret->var.ptr; + ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; + + if (fbc->common.arg_info) { + zend_uint i=0; + zval **p = (zval**)EX(function_state).arguments; + ulong arg_count = opline->extended_value; - SAVE_OPLINE(); + while (arg_count>0) { + zend_verify_arg_type(fbc, ++i, *(p-arg_count), 0 TSRMLS_CC); + arg_count--; + } + } - if (OP1_TYPE == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - Z_STRVAL_P(str) = NULL; - Z_STRLEN_P(str) = 0; - Z_TYPE_P(str) = IS_STRING; + if (!zend_execute_internal) { + /* saves one function call if zend_execute_internal is not used */ + fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); + } else { + zend_execute_internal(EXECUTE_DATA, RETURN_VALUE_USED(opline) TSRMLS_CC); + } - INIT_PZVAL(str); - } + if (!RETURN_VALUE_USED(opline)) { + zval_ptr_dtor(&ret->var.ptr); + } + } else if (fbc->type == ZEND_USER_FUNCTION) { + EX(original_return_value) = EG(return_value_ptr_ptr); + EG(active_symbol_table) = NULL; + EG(active_op_array) = &fbc->op_array; + EG(return_value_ptr_ptr) = NULL; + if (RETURN_VALUE_USED(opline)) { + temp_variable *ret = &EX_T(opline->result.var); - add_string_to_string(str, str, opline->op2.zv); + ret->var.ptr = NULL; + EG(return_value_ptr_ptr) = &ret->var.ptr; + ret->var.ptr_ptr = &ret->var.ptr; + ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; + } - /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ - /*CHECK_EXCEPTION();*/ - ZEND_VM_NEXT_OPCODE(); - } - if (EXPECTED(zend_execute == execute)) { ++ if (EG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { ++ if (RETURN_VALUE_USED(opline)) { ++ EX_T(opline->result.var).var.ptr = zend_generator_create_zval(EG(active_op_array) TSRMLS_CC); ++ } ++ } else if (EXPECTED(zend_execute == execute)) { + if (EXPECTED(EG(exception) == NULL)) { + ZEND_VM_ENTER(); + } + } else { + zend_execute(EG(active_op_array) TSRMLS_CC); + } - ZEND_VM_HANDLER(56, ZEND_ADD_VAR, TMP|UNUSED, TMP|VAR|CV) - { - USE_OPLINE - zend_free_op free_op2; - zval *str = &EX_T(opline->result.var).tmp_var; - zval *var; - zval var_copy; - int use_copy = 0; + EG(opline_ptr) = &EX(opline); + EG(active_op_array) = EX(op_array); + EG(return_value_ptr_ptr) = EX(original_return_value); + if (EG(active_symbol_table)) { - if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { - zend_hash_destroy(EG(active_symbol_table)); - FREE_HASHTABLE(EG(active_symbol_table)); - } else { - /* clean before putting into the cache, since clean - could call dtors, which could use cached hash */ - zend_hash_clean(EG(active_symbol_table)); - *(++EG(symtable_cache_ptr)) = EG(active_symbol_table); - } ++ zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); + } + EG(active_symbol_table) = EX(symbol_table); + } else { /* ZEND_OVERLOADED_FUNCTION */ + MAKE_STD_ZVAL(EX_T(opline->result.var).var.ptr); + ZVAL_NULL(EX_T(opline->result.var).var.ptr); - SAVE_OPLINE(); - var = GET_OP2_ZVAL_PTR(BP_VAR_R); - /* Not sure what should be done here if it's a static method */ ++ /* Not sure what should be done here if it's a static method */ + if (EXPECTED(EX(object) != NULL)) { + Z_OBJ_HT_P(EX(object))->call_method(fbc->common.function_name, opline->extended_value, EX_T(opline->result.var).var.ptr, &EX_T(opline->result.var).var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); + } else { + zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object"); + } - if (OP1_TYPE == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - Z_STRVAL_P(str) = NULL; - Z_STRLEN_P(str) = 0; - Z_TYPE_P(str) = IS_STRING; + if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { + efree((char*)fbc->common.function_name); + } + efree(fbc); - INIT_PZVAL(str); + if (!RETURN_VALUE_USED(opline)) { + zval_ptr_dtor(&EX_T(opline->result.var).var.ptr); + } else { + Z_UNSET_ISREF_P(EX_T(opline->result.var).var.ptr); + Z_SET_REFCOUNT_P(EX_T(opline->result.var).var.ptr, 1); + EX_T(opline->result.var).var.fcall_returned_reference = 0; + EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; + } } - if (Z_TYPE_P(var) != IS_STRING) { - zend_make_printable_zval(var, &var_copy, &use_copy); + EX(function_state).function = (zend_function *) EX(op_array); + EX(function_state).arguments = NULL; - if (use_copy) { - var = &var_copy; + if (should_change_scope) { + if (EG(This)) { + if (UNEXPECTED(EG(exception) != NULL) && IS_CTOR_CALL(EX(called_scope))) { + if (IS_CTOR_USED(EX(called_scope))) { + Z_DELREF_P(EG(This)); + } + if (Z_REFCOUNT_P(EG(This)) == 1) { + zend_object_store_ctor_failed(EG(This) TSRMLS_CC); + } + } + zval_ptr_dtor(&EG(This)); } + EG(This) = EX(current_this); + EG(scope) = EX(current_scope); + EG(called_scope) = EX(current_called_scope); } - add_string_to_string(str, str, var); - if (use_copy) { - zval_dtor(var); + EX(object) = EX(current_object); + EX(called_scope) = DECODE_CTOR(EX(called_scope)); + + zend_vm_stack_clear_multiple(TSRMLS_C); + + if (UNEXPECTED(EG(exception) != NULL)) { + zend_throw_exception_internal(NULL TSRMLS_CC); + if (RETURN_VALUE_USED(opline) && EX_T(opline->result.var).var.ptr) { + zval_ptr_dtor(&EX_T(opline->result.var).var.ptr); + } + HANDLE_EXCEPTION(); } - /* original comment, possibly problematic: - * FREE_OP is missing intentionally here - we're always working on the same temporary variable - * (Zeev): I don't think it's problematic, we only use variables - * which aren't affected by FREE_OP(Ts, )'s anyway, unless they're - * string offsets or overloaded objects - */ - FREE_OP2(); - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@@ -5346,206 -5350,26 +5376,184 @@@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR ZEND_VM_HANDLER(159, ZEND_LEAVE, ANY, ANY) { - USE_OPLINE - zend_uint i, op_num = opline - EG(active_op_array)->opcodes; - - SAVE_OPLINE(); zend_exception_restore(TSRMLS_C); - if (EX(leaving)) { - zend_uint catch_op_num = 0, finally_op_num = 0; - for (i = 0; i < EX(leaving); i++) { - if (EG(active_op_array)->try_catch_array[i].try_op > op_num) { - break; - } - if (op_num < EG(active_op_array)->try_catch_array[i].finally_op) { - finally_op_num = EG(active_op_array)->try_catch_array[i].finally_op; - } - if (EG(exception)) { - if (op_num < EG(active_op_array)->try_catch_array[i].catch_op) { - catch_op_num = EG(active_op_array)->try_catch_array[i].catch_op; - } - } - } - - if (catch_op_num && finally_op_num) { - if (catch_op_num > finally_op_num) { - ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[finally_op_num]); - ZEND_VM_CONTINUE(); - } else { - EX(leaving) = 0; - ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]); - ZEND_VM_CONTINUE(); - } - } else if (catch_op_num) { - ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]); - ZEND_VM_CONTINUE(); - } else if (finally_op_num) { - ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[finally_op_num]); - ZEND_VM_CONTINUE(); - } else { - ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); - } - } else { + if (!EX(leaving)) { ZEND_VM_NEXT_OPCODE(); + } else { + zend_uint leaving = EX(leaving); + switch (leaving) { + case ZEND_RETURN: + case ZEND_RETURN_BY_REF: + case ZEND_THROW: + leaving = ZEND_LEAVE; + case ZEND_JMP: + case ZEND_BRK: + case ZEND_CONT: + case ZEND_GOTO: + ZEND_VM_DISPATCH_TO_HELPER_EX(zend_finally_handler_leaving, type, leaving); + break; + } } + + ZEND_VM_CONTINUE(); } +ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (OP1_TYPE != IS_UNUSED) { + zend_free_op free_op1; + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) { + zval *value, *copy; + + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + value = GET_OP1_ZVAL_PTR(BP_VAR_R); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!IS_OP1_TMP_FREE()) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W); + + if (OP1_TYPE == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (OP1_TYPE == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + FREE_OP1_IF_VAR(); + } + } else { + zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R); + + /* Consts, temporary variables and references need copying */ + if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!IS_OP1_TMP_FREE()) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + FREE_OP1_IF_VAR(); + } + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (OP2_TYPE != IS_UNUSED) { + zend_free_op free_op2; + zval *key = GET_OP2_ZVAL_PTR(BP_VAR_R); + + /* Consts, temporary variables and references need copying */ + if (OP2_TYPE == IS_CONST || OP2_TYPE == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!IS_OP1_TMP_FREE()) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + + FREE_OP2_IF_VAR(); + } else { + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + +ZEND_VM_HANDLER(161, ZEND_DELEGATE_YIELD, CONST|TMP|VAR|CV, ANY) +{ + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --cc Zend/zend_vm_execute.h index 8d2b7bfd7e,bd19124574..402442f23a --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@@ -457,40 -427,6 +457,13 @@@ ZEND_API void execute_ex(zend_execute_d zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen"); } +ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) +{ + zend_execute_data *execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC); + + execute_ex(execute_data TSRMLS_CC); +} + - static int ZEND_FASTCALL ZEND_JMP_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) - { - USE_OPLINE - - #if DEBUG_ZEND>=2 - printf("Jumping to %d\n", opline->op1.opline_num); - #endif - ZEND_VM_SET_OPCODE(opline->op1.jmp_addr); - ZEND_VM_CONTINUE(); /* CHECK_ME */ - } - - static int ZEND_FASTCALL ZEND_INIT_STRING_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) - { - USE_OPLINE - zval *tmp = &EX_T(opline->result.var).tmp_var; - - SAVE_OPLINE(); - tmp->value.str.val = emalloc(1); - tmp->value.str.val[0] = 0; - tmp->value.str.len = 0; - Z_SET_REFCOUNT_P(tmp, 1); - tmp->type = IS_STRING; - Z_UNSET_ISREF_P(tmp); - /*CHECK_EXCEPTION();*/ - ZEND_VM_NEXT_OPCODE(); - } - static int ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) { zend_bool nested; @@@ -564,7 -489,7 +537,6 @@@ ZEND_VM_INC_OPCODE(); ZEND_VM_LEAVE(); } else { -- EG(opline_ptr) = &EX(opline); EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); @@@ -725,7 -662,7 +697,7 @@@ static int ZEND_FASTCALL zend_do_fcall_ MAKE_STD_ZVAL(EX_T(opline->result.var).var.ptr); ZVAL_NULL(EX_T(opline->result.var).var.ptr); -- /* Not sure what should be done here if it's a static method */ ++ /* Not sure what should be done here if it's a static method */ if (EXPECTED(EX(object) != NULL)) { Z_OBJ_HT_P(EX(object))->call_method(fbc->common.function_name, opline->extended_value, EX_T(opline->result.var).var.ptr, &EX_T(opline->result.var).var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); } else {