Skip to content

Commit 3ebf98f

Browse files
author
Andi Gutmans
committed
- This is supposed to be commited to the RETURN_REF_PATCH branch which is
the beginning of work on allowing returning of references from functions.
1 parent b5d60d2 commit 3ebf98f

File tree

6 files changed

+65
-33
lines changed

6 files changed

+65
-33
lines changed

Zend/zend-parser.y

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,9 @@ statement:
179179
| T_BREAK expr ';' { do_brk_cont(ZEND_BRK, &$2 CLS_CC); }
180180
| T_CONTINUE ';' { do_brk_cont(ZEND_CONT, NULL CLS_CC); }
181181
| T_CONTINUE expr ';' { do_brk_cont(ZEND_CONT, &$2 CLS_CC); }
182-
| T_RETURN ';' { do_return(NULL CLS_CC); }
183-
| T_RETURN expr ';' { do_return(&$2 CLS_CC); }
182+
| T_RETURN ';' { do_return(NULL, 0 CLS_CC); }
183+
| T_RETURN expr ';' { do_return(&$2, 0 CLS_CC); }
184+
| T_RETURN '&' w_cvar ';' { do_return(&$3, 1 CLS_CC); }
184185
| T_GLOBAL global_var_list
185186
| T_STATIC static_var_list
186187
| T_ECHO echo_expr_list ';'

Zend/zend_compile.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ void do_end_function_call(znode *function_name, znode *result, znode *argument_l
839839
}
840840
opline->op1 = *function_name;
841841
opline->result.u.var = get_temporary_variable(CG(active_op_array));
842-
opline->result.op_type = IS_TMP_VAR;
842+
opline->result.op_type = IS_VAR;
843843
*result = opline->result;
844844
SET_UNUSED(opline->op2);
845845
opline->op2.u.constant.value.lval = is_method;
@@ -948,7 +948,7 @@ static int generate_free_foreach_copy(znode *foreach_copy CLS_DC)
948948
return 0;
949949
}
950950

951-
void do_return(znode *expr CLS_DC)
951+
void do_return(znode *expr, int return_reference CLS_DC)
952952
{
953953
zend_op *opline;
954954

@@ -963,6 +963,7 @@ void do_return(znode *expr CLS_DC)
963963
opline = get_next_op(CG(active_op_array) CLS_CC);
964964

965965
opline->opcode = ZEND_RETURN;
966+
opline->extended_value = return_reference;
966967
if (expr) {
967968
opline->op1 = *expr;
968969
} else {

Zend/zend_compile.h

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ int do_begin_function_call(znode *function_name CLS_DC);
267267
void do_begin_dynamic_function_call(znode *function_name CLS_DC);
268268
void do_begin_class_member_function_call(znode *class_name, znode *function_name CLS_DC);
269269
void do_end_function_call(znode *function_name, znode *result, znode *argument_list, int is_method, int is_dynamic_fcall CLS_DC);
270-
void do_return(znode *expr CLS_DC);
270+
void do_return(znode *expr, int return_reference CLS_DC);
271271
ZEND_API int do_bind_function_or_class(zend_op *opline, HashTable *function_table, HashTable *class_table, int compile_time);
272272
void do_early_binding(CLS_D);
273273

@@ -573,21 +573,24 @@ int zendlex(znode *zendlval CLS_DC);
573573
#define PZVAL_IS_REF(z) ((z)->is_ref)
574574

575575
#define PZVAL_LOCK(z) ((z)->refcount++)
576-
#define PZVAL_UNLOCK(z) { ((z)->refcount--); \
577-
if (!(z)->refcount) { \
578-
EG(garbage)[EG(garbage_ptr)++] = (z); \
579-
if (EG(garbage_ptr) == 4) { \
580-
zval_dtor(EG(garbage)[0]); \
581-
efree(EG(garbage)[0]); \
582-
zval_dtor(EG(garbage)[1]); \
583-
efree(EG(garbage)[1]); \
584-
EG(garbage)[0] = EG(garbage)[2]; \
585-
EG(garbage)[1] = EG(garbage)[3]; \
586-
EG(garbage_ptr) -= 2; \
587-
} \
588-
} \
576+
#define PZVAL_UNLOCK(z) { ((z)->refcount--); \
577+
if (!(z)->refcount && !EG(suspend_garbage)) { \
578+
EG(garbage)[EG(garbage_ptr)++] = (z); \
579+
if (EG(garbage_ptr) == 4) { \
580+
zval_dtor(EG(garbage)[0]); \
581+
efree(EG(garbage)[0]); \
582+
zval_dtor(EG(garbage)[1]); \
583+
efree(EG(garbage)[1]); \
584+
EG(garbage)[0] = EG(garbage)[2]; \
585+
EG(garbage)[1] = EG(garbage)[3]; \
586+
EG(garbage_ptr) -= 2; \
587+
} \
588+
} \
589589
}
590590

591+
#define SUSPEND_GARBAGE() (EG(suspend_garbage)=1)
592+
#define RESUME_GARBAGE() (EG(suspend_garbage)=0)
593+
591594
#define SELECTIVE_PZVAL_LOCK(pzv, pzn) if (!((pzn)->u.EA.type & EXT_TYPE_UNUSED)) { PZVAL_LOCK(pzv); }
592595

593596

Zend/zend_execute.c

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,14 +1218,19 @@ binary_assign_op_addr: {
12181218
AI_USE_PTR(Ts[opline->result.u.var].var);
12191219
break;
12201220
case ZEND_ASSIGN: {
1221-
zval *value = get_zval_ptr(&opline->op2, Ts, &EG(free_op2), BP_VAR_R);
1221+
zval *value;
1222+
SUSPEND_GARBAGE();
1223+
value = get_zval_ptr(&opline->op2, Ts, &EG(free_op2), BP_VAR_R);
1224+
RESUME_GARBAGE();
12221225

12231226
zend_assign_to_variable(&opline->result, &opline->op1, &opline->op2, value, (EG(free_op2)?IS_TMP_VAR:opline->op2.op_type), Ts ELS_CC);
12241227
/* zend_assign_to_variable() always takes care of op2, never free it! */
12251228
}
12261229
break;
12271230
case ZEND_ASSIGN_REF:
1231+
SUSPEND_GARBAGE();
12281232
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);
1233+
RESUME_GARBAGE();
12291234
break;
12301235
case ZEND_JMP:
12311236
#if DEBUG_ZEND>=2
@@ -1467,13 +1472,18 @@ binary_assign_op_addr: {
14671472
}
14681473
do_fcall_common:
14691474
{
1470-
zval *original_return_value;
1475+
zval **original_return_value;
14711476
int return_value_not_used = (opline->result.u.EA.type & EXT_TYPE_UNUSED);
14721477

14731478
zend_ptr_stack_push(&EG(argument_stack), (void *) opline->extended_value);
1474-
var_uninit(&Ts[opline->result.u.var].tmp_var);
1479+
Ts[opline->result.u.var].var.ptr = (zval *)emalloc(sizeof(zval));
1480+
Ts[opline->result.u.var].var.ptr_ptr = &Ts[opline->result.u.var].var.ptr;
1481+
var_uninit(Ts[opline->result.u.var].var.ptr);
1482+
Ts[opline->result.u.var].var.ptr->is_ref = 0;
1483+
Ts[opline->result.u.var].var.ptr->refcount = 1;
1484+
14751485
if (function_state.function->type==ZEND_INTERNAL_FUNCTION) {
1476-
((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);
1486+
((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);
14771487
if (object.ptr) {
14781488
object.ptr->refcount--;
14791489
}
@@ -1502,13 +1512,13 @@ binary_assign_op_addr: {
15021512
*this_ptr = object.ptr;
15031513
object.ptr = NULL;
15041514
}
1505-
original_return_value = EG(return_value);
1506-
EG(return_value) = &Ts[opline->result.u.var].tmp_var;
1515+
original_return_value = EG(return_value_ptr_ptr);
1516+
EG(return_value_ptr_ptr) = Ts[opline->result.u.var].var.ptr_ptr;
15071517
EG(active_op_array) = (zend_op_array *) function_state.function;
15081518
zend_execute(EG(active_op_array) ELS_CC);
15091519
EG(opline_ptr) = &opline;
15101520
EG(active_op_array) = op_array;
1511-
EG(return_value)=original_return_value;
1521+
EG(return_value_ptr_ptr)=original_return_value;
15121522
if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) {
15131523
zend_hash_destroy(function_state.function_symbol_table);
15141524
efree(function_state.function_symbol_table);
@@ -1518,11 +1528,11 @@ binary_assign_op_addr: {
15181528
}
15191529
EG(active_symbol_table) = calling_symbol_table;
15201530
} else { /* ZEND_OVERLOADED_FUNCTION */
1521-
call_overloaded_function(opline->extended_value, &Ts[opline->result.u.var].tmp_var, &EG(regular_list), &EG(persistent_list) ELS_CC);
1531+
call_overloaded_function(opline->extended_value, Ts[opline->result.u.var].var.ptr, &EG(regular_list), &EG(persistent_list) ELS_CC);
15221532
efree(fbc);
15231533
}
15241534
if (return_value_not_used) {
1525-
zendi_zval_dtor(Ts[opline->result.u.var].tmp_var);
1535+
zval_ptr_dtor(&Ts[opline->result.u.var].var.ptr);
15261536
}
15271537
object.ptr = zend_ptr_stack_pop(&EG(arg_types_stack));
15281538
if (opline->opcode == ZEND_DO_FCALL_BY_NAME) {
@@ -1536,9 +1546,15 @@ binary_assign_op_addr: {
15361546
case ZEND_RETURN: {
15371547
zval *retval = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R);
15381548

1539-
*EG(return_value) = *retval;
1540-
if (!EG(free_op1)) {
1541-
zendi_zval_copy_ctor(*EG(return_value));
1549+
if (!EG(free_op1)) { /* Not a temp var */
1550+
efree(*EG(return_value_ptr_ptr));
1551+
/* Still need to check for reference */
1552+
*EG(return_value_ptr_ptr) = retval;
1553+
retval->refcount++;
1554+
} else {
1555+
**EG(return_value_ptr_ptr) = *retval;
1556+
(*EG(return_value_ptr_ptr))->refcount = 1;
1557+
(*EG(return_value_ptr_ptr))->is_ref = 0;
15421558
}
15431559
#if SUPPORT_INTERACTIVE
15441560
op_array->last_executed_op_number = opline-op_array->opcodes;
@@ -1566,7 +1582,10 @@ binary_assign_op_addr: {
15661582
goto send_by_ref;
15671583
}
15681584
{
1569-
zval *varptr = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R);
1585+
zval *varptr;
1586+
SUSPEND_GARBAGE();
1587+
varptr = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R);
1588+
RESUME_GARBAGE();
15701589

15711590
if (varptr == &EG(uninitialized_zval)) {
15721591
varptr = (zval *) emalloc(sizeof(zval));
@@ -1589,8 +1608,13 @@ binary_assign_op_addr: {
15891608
break;
15901609
send_by_ref:
15911610
case ZEND_SEND_REF: {
1592-
zval **varptr_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_W);
1593-
zval *varptr = *varptr_ptr;
1611+
zval **varptr_ptr;
1612+
zval *varptr;
1613+
SUSPEND_GARBAGE();
1614+
varptr_ptr = get_zval_ptr_ptr(&opline->op1, Ts, BP_VAR_W);
1615+
RESUME_GARBAGE();
1616+
1617+
varptr = *varptr_ptr;
15941618

15951619
if (!PZVAL_IS_REF(varptr)) {
15961620
/* code to break away this variable */

Zend/zend_execute_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ void init_executor(CLS_D ELS_DC)
115115
zend_llist_apply(&zend_extensions, (void (*)(void *)) zend_extension_activator);
116116
EG(opline_ptr) = NULL;
117117
EG(garbage_ptr) = 0;
118+
EG(suspend_garbage) = 0;
118119

119120
zend_hash_init(&EG(imported_files), 5, NULL, NULL, 0);
120121
}

Zend/zend_globals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ struct _zend_compiler_globals {
110110

111111
struct _zend_executor_globals {
112112
zval *return_value;
113+
zval **return_value_ptr_ptr;
113114

114115
zval uninitialized_zval;
115116
zval *uninitialized_zval_ptr;
@@ -160,6 +161,7 @@ struct _zend_executor_globals {
160161

161162
zval *garbage[4];
162163
int garbage_ptr;
164+
zend_bool suspend_garbage;
163165

164166
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
165167
#if SUPPORT_INTERACTIVE

0 commit comments

Comments
 (0)