diff options
Diffstat (limited to 'Zend/zend_execute.c')
-rw-r--r-- | Zend/zend_execute.c | 84 |
1 files changed, 67 insertions, 17 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index b1ebfb0b86..78982c85c4 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -378,7 +378,7 @@ static inline void zend_assign_to_variable(znode *result, znode *op1, znode *op2 */ /* break missing intentionally */ case IS_CONST: - if (PZVAL_IS_REF(value)) { + if (PZVAL_IS_REF(value) && value->refcount > 0) { variable_ptr = *variable_ptr_ptr = (zval *) emalloc(sizeof(zval)); *variable_ptr = *value; zval_copy_ctor(variable_ptr); @@ -1218,14 +1218,19 @@ binary_assign_op_addr: { AI_USE_PTR(Ts[opline->result.u.var].var); break; case ZEND_ASSIGN: { - zval *value = get_zval_ptr(&opline->op2, Ts, &EG(free_op2), BP_VAR_R); + zval *value; + SUSPEND_GARBAGE(); + value = get_zval_ptr(&opline->op2, Ts, &EG(free_op2), BP_VAR_R); + RESUME_GARBAGE(); zend_assign_to_variable(&opline->result, &opline->op1, &opline->op2, value, (EG(free_op2)?IS_TMP_VAR:opline->op2.op_type), Ts ELS_CC); /* zend_assign_to_variable() always takes care of op2, never free it! */ } break; case ZEND_ASSIGN_REF: + SUSPEND_GARBAGE(); zend_assign_to_variable_reference(&opline->result, get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_W), get_zval_ptr_ptr(&opline->op2, Ts, BP_VAR_W), Ts ELS_CC); + RESUME_GARBAGE(); break; case ZEND_JMP: #if DEBUG_ZEND>=2 @@ -1467,16 +1472,23 @@ overloaded_function_call_cont: } do_fcall_common: { - zval *original_return_value; + zval **original_return_value; int return_value_not_used = (opline->result.u.EA.type & EXT_TYPE_UNUSED); zend_ptr_stack_push(&EG(argument_stack), (void *) opline->extended_value); - var_uninit(&Ts[opline->result.u.var].tmp_var); + Ts[opline->result.u.var].var.ptr = (zval *)emalloc(sizeof(zval)); + Ts[opline->result.u.var].var.ptr_ptr = &Ts[opline->result.u.var].var.ptr; + var_uninit(Ts[opline->result.u.var].var.ptr); + Ts[opline->result.u.var].var.ptr->is_ref = 0; + Ts[opline->result.u.var].var.ptr->refcount = 1; + if (function_state.function->type==ZEND_INTERNAL_FUNCTION) { - ((zend_internal_function *) function_state.function)->handler(opline->extended_value, &Ts[opline->result.u.var].tmp_var, &EG(regular_list), &EG(persistent_list), object.ptr, !return_value_not_used); + ((zend_internal_function *) function_state.function)->handler(opline->extended_value, Ts[opline->result.u.var].var.ptr, &EG(regular_list), &EG(persistent_list), object.ptr, !return_value_not_used); if (object.ptr) { object.ptr->refcount--; } + Ts[opline->result.u.var].var.ptr->is_ref = 0; + Ts[opline->result.u.var].var.ptr->refcount = 1; } else if (function_state.function->type==ZEND_USER_FUNCTION) { HashTable *calling_symbol_table; @@ -1502,13 +1514,13 @@ do_fcall_common: *this_ptr = object.ptr; object.ptr = NULL; } - original_return_value = EG(return_value); - EG(return_value) = &Ts[opline->result.u.var].tmp_var; + original_return_value = EG(return_value_ptr_ptr); + EG(return_value_ptr_ptr) = Ts[opline->result.u.var].var.ptr_ptr; EG(active_op_array) = (zend_op_array *) function_state.function; zend_execute(EG(active_op_array) ELS_CC); EG(opline_ptr) = &opline; EG(active_op_array) = op_array; - EG(return_value)=original_return_value; + EG(return_value_ptr_ptr)=original_return_value; if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { zend_hash_destroy(function_state.function_symbol_table); efree(function_state.function_symbol_table); @@ -1518,11 +1530,11 @@ do_fcall_common: } EG(active_symbol_table) = calling_symbol_table; } else { /* ZEND_OVERLOADED_FUNCTION */ - call_overloaded_function(opline->extended_value, &Ts[opline->result.u.var].tmp_var, &EG(regular_list), &EG(persistent_list) ELS_CC); + call_overloaded_function(opline->extended_value, Ts[opline->result.u.var].var.ptr, &EG(regular_list), &EG(persistent_list) ELS_CC); efree(fbc); } if (return_value_not_used) { - zendi_zval_dtor(Ts[opline->result.u.var].tmp_var); + zval_ptr_dtor(&Ts[opline->result.u.var].var.ptr); } object.ptr = zend_ptr_stack_pop(&EG(arg_types_stack)); if (opline->opcode == ZEND_DO_FCALL_BY_NAME) { @@ -1534,11 +1546,41 @@ do_fcall_common: } break; case ZEND_RETURN: { - zval *retval = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R); + zval *retval_ptr; + zval **retval_ptr_ptr; - *EG(return_value) = *retval; - if (!EG(free_op1)) { - zendi_zval_copy_ctor(*EG(return_value)); + if ((EG(active_op_array)->return_reference == ZEND_RETURN_REF) && + (opline->op1.op_type != IS_CONST) && + (opline->op1.op_type != IS_TMP_VAR)) { + + retval_ptr_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_W); + + if (!PZVAL_IS_REF(*retval_ptr_ptr)) { + SEPARATE_ZVAL(retval_ptr_ptr); + (*retval_ptr_ptr)->is_ref = 1; + } + (*retval_ptr_ptr)->refcount++; + efree(*EG(return_value_ptr_ptr)); + (*EG(return_value_ptr_ptr)) = (*retval_ptr_ptr); + } else { + retval_ptr = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R); + + if (!EG(free_op1)) { /* Not a temp var */ + if (PZVAL_IS_REF(retval_ptr) && retval_ptr->refcount > 0) { + **EG(return_value_ptr_ptr) = *retval_ptr; + (*EG(return_value_ptr_ptr))->is_ref = 0; + (*EG(return_value_ptr_ptr))->refcount = 1; + zval_copy_ctor(*EG(return_value_ptr_ptr)); + } else { + efree(*EG(return_value_ptr_ptr)); + *EG(return_value_ptr_ptr) = retval_ptr; + retval_ptr->refcount++; + } + } else { + **EG(return_value_ptr_ptr) = *retval_ptr; + (*EG(return_value_ptr_ptr))->refcount = 1; + (*EG(return_value_ptr_ptr))->is_ref = 0; + } } #if SUPPORT_INTERACTIVE op_array->last_executed_op_number = opline-op_array->opcodes; @@ -1566,7 +1608,10 @@ do_fcall_common: goto send_by_ref; } { - zval *varptr = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R); + zval *varptr; + SUSPEND_GARBAGE(); + varptr = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R); + RESUME_GARBAGE(); if (varptr == &EG(uninitialized_zval)) { varptr = (zval *) emalloc(sizeof(zval)); @@ -1589,8 +1634,13 @@ do_fcall_common: break; send_by_ref: case ZEND_SEND_REF: { - zval **varptr_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_W); - zval *varptr = *varptr_ptr; + zval **varptr_ptr; + zval *varptr; + SUSPEND_GARBAGE(); + varptr_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_W); + RESUME_GARBAGE(); + + varptr = *varptr_ptr; if (!PZVAL_IS_REF(varptr)) { /* code to break away this variable */ |