| T_CONTINUE ';' { do_brk_cont(ZEND_CONT, NULL CLS_CC); }
| T_CONTINUE expr ';' { do_brk_cont(ZEND_CONT, &$2 CLS_CC); }
| T_RETURN ';' { do_return(NULL CLS_CC); }
- | T_RETURN expr ';' { do_return(&$2 CLS_CC); }
+ | T_RETURN expr_without_variable ';' { do_return(&$2 CLS_CC); }
+ | T_RETURN cvar ';' { do_return(&$2 CLS_CC); }
| T_GLOBAL global_var_list
| T_STATIC static_var_list
| T_ECHO echo_expr_list ';'
declaration_statement:
- T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } T_STRING { do_begin_function_declaration(&$1, &$3, 0 CLS_CC); }
+ T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { do_begin_function_declaration(&$1, &$4, 0, $3.op_type CLS_CC); }
'(' parameter_list ')' '{' inner_statement_list '}' { do_end_function_declaration(&$1 CLS_CC); }
- | T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } T_STRING { do_begin_function_declaration(&$1, &$3, 0 CLS_CC); }
+ | T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { do_begin_function_declaration(&$1, &$4, 0, $3.op_type CLS_CC); }
parameter_list '(' inner_statement_list ')' ';' { do_end_function_declaration(&$1 CLS_CC); }
| T_CLASS T_STRING { do_begin_class_declaration(&$2, NULL CLS_CC); } '{' class_statement_list '}' { do_end_class_declaration(CLS_C); }
| T_CLASS T_STRING T_EXTENDS T_STRING { do_begin_class_declaration(&$2, &$4 CLS_CC); } '{' class_statement_list '}' { do_end_class_declaration(CLS_C); }
class_statement:
T_VAR class_variable_decleration ';'
- | T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } T_STRING { do_begin_function_declaration(&$1, &$3, 1 CLS_CC); } '('
+ | T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { do_begin_function_declaration(&$1, &$4, 1, $3.op_type CLS_CC); } '('
parameter_list ')' '{' inner_statement_list '}' { do_end_function_declaration(&$1 CLS_CC); }
- | T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } T_STRING { do_begin_function_declaration(&$1, &$3, 1 CLS_CC); }
+ | T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { do_begin_function_declaration(&$1, &$4, 1, $3.op_type CLS_CC); }
parameter_list '(' inner_statement_list ')' ';' { do_end_function_declaration(&$1 CLS_CC); }
;
+is_reference:
+ /* empty */ { $$.op_type = ZEND_RETURN_VAL; }
+ | '&' { $$.op_type = ZEND_RETURN_REF; }
class_variable_decleration:
class_variable_decleration ',' T_VARIABLE { do_declare_property(&$3, NULL CLS_CC); }
T_LIST '(' { do_list_init(CLS_C); } assignment_list ')' '=' expr { do_list_end(&$$, &$7 CLS_CC); }
| cvar '=' expr { do_end_variable_parse(BP_VAR_W, 0 CLS_CC); do_assign(&$$, &$1, &$3 CLS_CC); }
| cvar '=' '&' w_cvar { do_end_variable_parse(BP_VAR_W, 0 CLS_CC); do_assign_ref(&$$, &$1, &$4 CLS_CC); }
+ | cvar '=' '&' function_call { do_end_variable_parse(BP_VAR_W, 0 CLS_CC); do_assign_ref(&$$, &$1, &$4 CLS_CC); }
| T_NEW class_name { do_extended_fcall_begin(CLS_C); do_begin_new_object(&$1, &$2 CLS_CC); } ctor_arguments { do_end_new_object(&$$, &$2, &$1, &$4 CLS_CC); do_extended_fcall_end(CLS_C);}
| cvar T_PLUS_EQUAL expr { do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); do_binary_assign_op(ZEND_ASSIGN_ADD, &$$, &$1, &$3 CLS_CC); }
| cvar T_MINUS_EQUAL expr { do_end_variable_parse(BP_VAR_RW, 0 CLS_CC); do_binary_assign_op(ZEND_ASSIGN_SUB, &$$, &$1, &$3 CLS_CC); }
| expr '?' { do_begin_qm_op(&$1, &$2 CLS_CC); }
expr ':' { do_qm_true(&$4, &$2, &$5 CLS_CC); }
expr { do_qm_false(&$$, &$7, &$2, &$5 CLS_CC); }
- | T_STRING '(' { do_extended_fcall_begin(CLS_C); $2.u.opline_num = do_begin_function_call(&$1 CLS_CC); }
- function_call_parameter_list
- ')' { do_end_function_call(&$1, &$$, &$4, 0, $2.u.opline_num CLS_CC); do_extended_fcall_end(CLS_C); }
- | r_cvar '(' { do_extended_fcall_begin(CLS_C); do_begin_dynamic_function_call(&$1 CLS_CC); }
- function_call_parameter_list
- ')' { do_end_function_call(&$1, &$$, &$4, 0, 1 CLS_CC); do_extended_fcall_end(CLS_C);}
- | T_STRING T_PAAMAYIM_NEKUDOTAYIM T_STRING '(' { do_extended_fcall_begin(CLS_C); do_begin_class_member_function_call(&$1, &$3 CLS_CC); }
- function_call_parameter_list
- ')' { do_end_function_call(&$3, &$$, &$6, 1, 1 CLS_CC); do_extended_fcall_end(CLS_C);}
+ | function_call { $$ = $1; }
| internal_functions_in_yacc { $$ = $1; }
| T_INT_CAST expr { do_cast(&$$, &$2, IS_LONG CLS_CC); }
| T_DOUBLE_CAST expr { do_cast(&$$, &$2, IS_DOUBLE CLS_CC); }
;
+function_call:
+ T_STRING '(' { do_extended_fcall_begin(CLS_C); $2.u.opline_num = do_begin_function_call(&$1 CLS_CC); }
+ function_call_parameter_list
+ ')' { do_end_function_call(&$1, &$$, &$4, 0, $2.u.opline_num CLS_CC); do_extended_fcall_end(CLS_C); }
+ | r_cvar '(' { do_extended_fcall_begin(CLS_C); do_begin_dynamic_function_call(&$1 CLS_CC); }
+ function_call_parameter_list
+ ')' { do_end_function_call(&$1, &$$, &$4, 0, 1 CLS_CC); do_extended_fcall_end(CLS_C);}
+ | T_STRING T_PAAMAYIM_NEKUDOTAYIM T_STRING '(' { do_extended_fcall_begin(CLS_C); do_begin_class_member_function_call(&$1, &$3 CLS_CC); }
+ function_call_parameter_list
+ ')' { do_end_function_call(&$3, &$$, &$6, 1, 1 CLS_CC); do_extended_fcall_end(CLS_C);}
+;
+
+
exit_expr:
/* empty */ { $$.op_type = IS_UNUSED; }
| '(' ')' { $$.op_type = IS_UNUSED; }
typedef struct _zend_utility_values {
- unsigned char short_tags;
- unsigned char asp_tags;
+ zend_bool short_tags;
+ zend_bool asp_tags;
+ zend_bool allow_call_time_pass_reference;
char *import_use_extension;
uint import_use_extension_length;
} zend_utility_values;
zend_llist_init(&CG(filenames_list), sizeof(char *), free_filename, 0);
CG(short_tags) = ZEND_UV(short_tags);
CG(asp_tags) = ZEND_UV(asp_tags);
+ CG(allow_call_time_pass_reference) = ZEND_UV(allow_call_time_pass_reference);
CG(handle_op_arrays) = 1;
zend_hash_apply(&module_registry, (int (*)(void *)) module_registry_request_startup);
init_resource_list(ELS_C);
}
-void do_begin_function_declaration(znode *function_token, znode *function_name, int is_method CLS_DC)
+void do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference CLS_DC)
{
zend_op_array op_array;
char *name = function_name->u.constant.value.str.val;
zend_str_tolower(name, name_len);
init_op_array(&op_array, INITIAL_OP_ARRAY_SIZE);
+
op_array.function_name = name;
op_array.arg_types = NULL;
+ op_array.return_reference = return_reference;
if (is_method) {
zend_hash_update(&CG(active_class_entry)->function_table, name, name_len+1, &op_array, sizeof(zend_op_array), (void **) &CG(active_op_array));
}
opline->op1 = *function_name;
opline->result.u.var = get_temporary_variable(CG(active_op_array));
- opline->result.op_type = IS_TMP_VAR;
+ opline->result.op_type = IS_VAR;
*result = opline->result;
SET_UNUSED(opline->op2);
opline->op2.u.constant.value.lval = is_method;
int original_op=op;
zend_function **function_ptr_ptr, *function_ptr;
+
zend_stack_top(&CG(function_call_stack), (void **) &function_ptr_ptr);
function_ptr = *function_ptr_ptr;
+
+ if (original_op==ZEND_SEND_REF
+ && !CG(allow_call_time_pass_reference)) {
+ zend_error(E_COMPILE_WARNING,
+ "Call-time pass-by-reference has been deprecated - argument passed by value; "
+ "If you would like to pass it by reference, modify the declaration of %s(). "
+ "If you would like to enable call-time pass-by-reference, you can set"
+ "allow_call_time_pass_reference to true in your INI file. "
+ "However, future versions may not support this any longer.",
+ (function_ptr?function_ptr->common.function_name:"[runtime function name]"),
+ offset+1);
+ }
+
if (function_ptr) {
arg_types = function_ptr->common.arg_types;
} else {
{
zend_op *opline;
+ if (expr->op_type==IS_VAR) {
+ if (CG(active_op_array)->return_reference) {
+ do_end_variable_parse(BP_VAR_W, 0 CLS_CC);
+ } else {
+ do_end_variable_parse(BP_VAR_R, 0 CLS_CC);
+ }
+ }
#ifdef ZTS
zend_stack_apply_with_argument(&CG(switch_cond_stack), (int (*)(void *element, void *)) generate_free_switch_expr, ZEND_STACK_APPLY_TOPDOWN CLS_CC);
zend_stack_apply_with_argument(&CG(foreach_copy_stack), (int (*)(void *element, void *)) generate_free_foreach_copy, ZEND_STACK_APPLY_TOPDOWN CLS_CC);
opline = get_next_op(CG(active_op_array) CLS_CC);
opline->opcode = ZEND_RETURN;
+
if (expr) {
opline->op1 = *expr;
} else {
int last_executed_op_number;
int backpatch_count;
#endif
+ zend_bool return_reference;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
void do_add_string(znode *result, znode *op1, znode *op2 CLS_DC);
void do_add_variable(znode *result, znode *op1, znode *op2 CLS_DC);
-void do_begin_function_declaration(znode *function_token, znode *function_name, int is_method CLS_DC);
+void do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference CLS_DC);
void do_end_function_declaration(znode *function_token CLS_DC);
void do_receive_arg(int op, znode *var, znode *offset, znode *initialization, unsigned char pass_type CLS_DC);
int do_begin_function_call(znode *function_name CLS_DC);
#define PZVAL_IS_REF(z) ((z)->is_ref)
#define PZVAL_LOCK(z) ((z)->refcount++)
-#define PZVAL_UNLOCK(z) { ((z)->refcount--); \
- if (!(z)->refcount) { \
- EG(garbage)[EG(garbage_ptr)++] = (z); \
- if (EG(garbage_ptr) == 4) { \
- zval_dtor(EG(garbage)[0]); \
- efree(EG(garbage)[0]); \
- zval_dtor(EG(garbage)[1]); \
- efree(EG(garbage)[1]); \
- EG(garbage)[0] = EG(garbage)[2]; \
- EG(garbage)[1] = EG(garbage)[3]; \
- EG(garbage_ptr) -= 2; \
- } \
- } \
+#define PZVAL_UNLOCK(z) { ((z)->refcount--); \
+ if (!(z)->refcount && !EG(suspend_garbage)) { \
+ EG(garbage)[EG(garbage_ptr)++] = (z); \
+ if (EG(garbage_ptr) == 4) { \
+ zval_dtor(EG(garbage)[0]); \
+ efree(EG(garbage)[0]); \
+ zval_dtor(EG(garbage)[1]); \
+ efree(EG(garbage)[1]); \
+ EG(garbage)[0] = EG(garbage)[2]; \
+ EG(garbage)[1] = EG(garbage)[3]; \
+ EG(garbage_ptr) -= 2; \
+ } \
+ } \
}
+#define SUSPEND_GARBAGE() (EG(suspend_garbage)=1)
+#define RESUME_GARBAGE() (EG(suspend_garbage)=0)
+
#define SELECTIVE_PZVAL_LOCK(pzv, pzn) if (!((pzn)->u.EA.type & EXT_TYPE_UNUSED)) { PZVAL_LOCK(pzv); }
) \
)
+#define ZEND_RETURN_VAL 0
+#define ZEND_RETURN_REF 1
+
#endif /* _COMPILE_H */
*/
/* 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);
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
}
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;
*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);
}
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) {
}
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;
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));
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 */
zend_llist_apply(&zend_extensions, (void (*)(void *)) zend_extension_activator);
EG(opline_ptr) = NULL;
EG(garbage_ptr) = 0;
+ EG(suspend_garbage) = 0;
zend_hash_init(&EG(imported_files), 5, NULL, NULL, 0);
}
zend_bool short_tags;
zend_bool asp_tags;
+ zend_bool allow_call_time_pass_reference;
/* For extensions support */
zend_bool extended_info; /* generate extension information for debugger/profiler */
struct _zend_executor_globals {
zval *return_value;
+ zval **return_value_ptr_ptr;
zval uninitialized_zval;
zval *uninitialized_zval_ptr;
zval *garbage[4];
int garbage_ptr;
+ zend_bool suspend_garbage;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
#if SUPPORT_INTERACTIVE
op_array->uses_globals = 0;
+ op_array->return_reference = 0;
+
zend_llist_apply_with_argument(&zend_extensions, (void (*)(void *, void *)) zend_extension_op_array_ctor_handler, op_array);
}