Skip to content

Commit eb4825b

Browse files
committed
Improved "finally" im[plementation
1 parent a213caf commit eb4825b

File tree

8 files changed

+375
-433
lines changed

8 files changed

+375
-433
lines changed

Zend/zend_compile.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2709,6 +2709,7 @@ static int zend_add_try_element(zend_uint try_op TSRMLS_DC) /* {{{ */
27092709

27102710
CG(active_op_array)->try_catch_array = erealloc(CG(active_op_array)->try_catch_array, sizeof(zend_try_catch_element)*CG(active_op_array)->last_try_catch);
27112711
CG(active_op_array)->try_catch_array[try_catch_offset].try_op = try_op;
2712+
CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = 0;
27122713
CG(active_op_array)->try_catch_array[try_catch_offset].finally_op = 0;
27132714
CG(active_op_array)->try_catch_array[try_catch_offset].finally_end = 0;
27142715
return try_catch_offset;
@@ -2770,9 +2771,25 @@ void zend_do_try(znode *try_token TSRMLS_DC) /* {{{ */
27702771
}
27712772
/* }}} */
27722773

2773-
void zend_do_finally(znode *finally_token TSRMLS_DC) /* {{{ */ {
2774+
void zend_do_finally(znode *finally_token TSRMLS_DC) /* {{{ */
2775+
{
2776+
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
2777+
27742778
finally_token->u.op.opline_num = get_next_op_number(CG(active_op_array));
2775-
} /* }}} */
2779+
/* call the the "finally" block */
2780+
opline->opcode = ZEND_FAST_CALL;
2781+
SET_UNUSED(opline->op1);
2782+
opline->op1.opline_num = finally_token->u.op.opline_num + 1;
2783+
SET_UNUSED(opline->op2);
2784+
/* jump to code after the "finally" block,
2785+
* the actual jump address is going to be set in zend_do_end_finally()
2786+
*/
2787+
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
2788+
opline->opcode = ZEND_JMP;
2789+
SET_UNUSED(opline->op1);
2790+
SET_UNUSED(opline->op2);
2791+
}
2792+
/* }}} */
27762793

27772794
void zend_do_begin_catch(znode *catch_token, znode *class_name, znode *catch_var, znode *first_catch TSRMLS_DC) /* {{{ */
27782795
{
@@ -2837,18 +2854,19 @@ void zend_do_end_finally(znode *try_token, znode* catch_token, znode *finally_to
28372854
zend_error(E_COMPILE_ERROR, "Cannot use try without catch or finally");
28382855
}
28392856
if (finally_token->op_type != IS_UNUSED) {
2840-
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
2841-
CG(active_op_array)->try_catch_array[try_token->u.op.opline_num].finally_op = finally_token->u.op.opline_num;
2857+
zend_op *opline;
2858+
2859+
CG(active_op_array)->try_catch_array[try_token->u.op.opline_num].finally_op = finally_token->u.op.opline_num + 1;
28422860
CG(active_op_array)->try_catch_array[try_token->u.op.opline_num].finally_end = get_next_op_number(CG(active_op_array));
28432861
CG(active_op_array)->has_finally_block = 1;
28442862

2845-
opline->opcode = ZEND_LEAVE;
2863+
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
2864+
opline->opcode = ZEND_FAST_RET;
28462865
SET_UNUSED(opline->op1);
28472866
SET_UNUSED(opline->op2);
2867+
2868+
CG(active_op_array)->opcodes[finally_token->u.op.opline_num].op1.opline_num = get_next_op_number(CG(active_op_array));
28482869
}
2849-
if (catch_token->op_type == IS_UNUSED) {
2850-
CG(active_op_array)->try_catch_array[try_token->u.op.opline_num].catch_op = 0;
2851-
}
28522870
}
28532871
/* }}} */
28542872

Zend/zend_compile.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,7 @@ struct _zend_execute_data {
387387
zend_class_entry *current_called_scope;
388388
zval *current_this;
389389
zval *current_object;
390-
zend_uint leaving;
391-
zend_uint leaving_dest;
390+
struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
392391
};
393392

394393
#define EX(element) execute_data.element
@@ -826,6 +825,9 @@ int zend_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC);
826825
#define ZEND_RETURNS_FUNCTION 1<<0
827826
#define ZEND_RETURNS_NEW 1<<1
828827

828+
#define ZEND_FAST_RET_TO_CATCH 1
829+
#define ZEND_FAST_RET_TO_FINALLY 2
830+
829831
END_EXTERN_C()
830832

831833
#define ZEND_CLONE_FUNC_NAME "__clone"

Zend/zend_generators.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio
6060
* the resume. */
6161
if (finally_op_num) {
6262
execute_data->opline = &op_array->opcodes[finally_op_num];
63-
execute_data->leaving = ZEND_RETURN;
6463
generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
6564
zend_generator_resume(generator TSRMLS_CC);
6665
return;

Zend/zend_opcode.c

Lines changed: 154 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -499,31 +499,178 @@ static void zend_extension_op_array_handler(zend_extension *extension, zend_op_a
499499
}
500500
}
501501

502-
static void zend_check_finally_breakout(zend_op_array *op_array, zend_op *opline, zend_uint dst_num TSRMLS_DC) {
503-
zend_uint i, op_num = opline - op_array->opcodes;
504-
for (i=0; i < op_array->last_try_catch; i++) {
502+
static void zend_check_finally_breakout(zend_op_array *op_array, zend_uint op_num, zend_uint dst_num TSRMLS_DC)
503+
{
504+
zend_uint i;
505+
506+
for (i = 0; i < op_array->last_try_catch; i++) {
505507
if (op_array->try_catch_array[i].try_op > op_num) {
506508
break;
507509
}
508510
if ((op_num >= op_array->try_catch_array[i].finally_op
509-
&& op_num < op_array->try_catch_array[i].finally_end)
510-
&& (dst_num >= op_array->try_catch_array[i].finally_end
511+
&& op_num <= op_array->try_catch_array[i].finally_end)
512+
&& (dst_num > op_array->try_catch_array[i].finally_end
511513
|| dst_num < op_array->try_catch_array[i].finally_op)) {
512514
CG(in_compilation) = 1;
513515
CG(active_op_array) = op_array;
514-
CG(zend_lineno) = opline->lineno;
516+
CG(zend_lineno) = op_array->opcodes[op_num].lineno;
515517
zend_error(E_COMPILE_ERROR, "jump out of a finally block is disallowed");
516518
}
517519
}
518520
}
519521

522+
static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num, zend_uint dst_num TSRMLS_DC)
523+
{
524+
zend_uint start_op;
525+
zend_op *opline;
526+
zend_uint i = op_array->last_try_catch;
527+
528+
if (dst_num != (zend_uint)-1) {
529+
zend_check_finally_breakout(op_array, op_num, dst_num TSRMLS_CC);
530+
}
531+
532+
/* the backward order is mater */
533+
while (i > 0) {
534+
i--;
535+
if (op_array->try_catch_array[i].finally_op &&
536+
op_num >= op_array->try_catch_array[i].try_op &&
537+
op_num < op_array->try_catch_array[i].finally_op - 1 &&
538+
(dst_num < op_array->try_catch_array[i].try_op ||
539+
dst_num > op_array->try_catch_array[i].finally_end)) {
540+
/* we have a jump out of try block that needs executing finally */
541+
542+
/* generate a FAST_CALL to finaly block */
543+
start_op = get_next_op_number(op_array);
544+
opline = get_next_op(op_array TSRMLS_CC);
545+
opline->opcode = ZEND_FAST_CALL;
546+
SET_UNUSED(opline->op1);
547+
SET_UNUSED(opline->op2);
548+
opline->op1.opline_num = op_array->try_catch_array[i].finally_op;
549+
if (op_array->try_catch_array[i].catch_op) {
550+
opline->extended_value = 1;
551+
opline->op2.opline_num = op_array->try_catch_array[i].catch_op;
552+
}
553+
554+
/* generate a sequence of FAST_CALL to upward finaly block */
555+
while (i > 0) {
556+
i--;
557+
if (op_array->try_catch_array[i].finally_op &&
558+
op_num >= op_array->try_catch_array[i].try_op &&
559+
op_num < op_array->try_catch_array[i].finally_op - 1 &&
560+
(dst_num < op_array->try_catch_array[i].try_op ||
561+
dst_num > op_array->try_catch_array[i].finally_end)) {
562+
563+
opline = get_next_op(op_array TSRMLS_CC);
564+
opline->opcode = ZEND_FAST_CALL;
565+
SET_UNUSED(opline->op1);
566+
SET_UNUSED(opline->op2);
567+
opline->op1.opline_num = op_array->try_catch_array[i].finally_op;
568+
}
569+
}
570+
571+
/* Finish the sequence with original opcode */
572+
opline = get_next_op(op_array TSRMLS_CC);
573+
*opline = op_array->opcodes[op_num];
574+
575+
/* Replace original opcode with jump to this sequence */
576+
opline = op_array->opcodes + op_num;
577+
opline->opcode = ZEND_JMP;
578+
SET_UNUSED(opline->op1);
579+
SET_UNUSED(opline->op2);
580+
opline->op1.opline_num = start_op;
581+
582+
break;
583+
}
584+
}
585+
}
586+
587+
static void zend_resolve_finally_ret(zend_op_array *op_array, zend_uint op_num TSRMLS_DC)
588+
{
589+
int i;
590+
zend_uint catch_op_num = 0, finally_op_num = 0;
591+
592+
for (i = 0; i < op_array->last_try_catch; i++) {
593+
if (op_array->try_catch_array[i].try_op > op_num) {
594+
break;
595+
}
596+
if (op_num < op_array->try_catch_array[i].finally_op) {
597+
finally_op_num = op_array->try_catch_array[i].finally_op;
598+
}
599+
if (op_num < op_array->try_catch_array[i].catch_op) {
600+
catch_op_num = op_array->try_catch_array[i].catch_op;
601+
}
602+
}
603+
604+
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
605+
/* in case of unhandled exception return to upward finally block */
606+
op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_FINALLY;
607+
op_array->opcodes[op_num].op2.opline_num = finally_op_num;
608+
} else if (catch_op_num) {
609+
/* in case of unhandled exception return to upward catch block */
610+
op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_CATCH;
611+
op_array->opcodes[op_num].op2.opline_num = catch_op_num;
612+
}
613+
}
614+
615+
static void zend_resolve_finally_calls(zend_op_array *op_array TSRMLS_DC)
616+
{
617+
zend_uint i;
618+
zend_op *opline;
619+
620+
for (i = 0; i < op_array->last; i++) {
621+
opline = op_array->opcodes + i;
622+
switch (opline->opcode) {
623+
case ZEND_RETURN:
624+
case ZEND_RETURN_BY_REF:
625+
zend_resolve_finally_call(op_array, i, (zend_uint)-1 TSRMLS_CC);
626+
break;
627+
case ZEND_BRK:
628+
case ZEND_CONT:
629+
{
630+
int nest_levels, array_offset;
631+
zend_brk_cont_element *jmp_to;
632+
633+
nest_levels = Z_LVAL(op_array->literals[opline->op2.constant].constant);
634+
array_offset = opline->op1.opline_num;
635+
do {
636+
jmp_to = &op_array->brk_cont_array[array_offset];
637+
if (nest_levels > 1) {
638+
array_offset = jmp_to->parent;
639+
}
640+
} while (--nest_levels > 0);
641+
zend_resolve_finally_call(op_array, i, opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont TSRMLS_CC);
642+
break;
643+
}
644+
case ZEND_GOTO:
645+
if (Z_TYPE(op_array->literals[opline->op2.constant].constant) != IS_LONG) {
646+
zend_uint num = opline->op2.constant;
647+
opline->op2.zv = &op_array->literals[opline->op2.constant].constant;
648+
zend_resolve_goto_label(op_array, opline, 1 TSRMLS_CC);
649+
opline->op2.constant = num;
650+
}
651+
/* break omitted intentionally */
652+
case ZEND_JMP:
653+
zend_resolve_finally_call(op_array, i, opline->op1.opline_num TSRMLS_CC);
654+
break;
655+
case ZEND_FAST_RET:
656+
zend_resolve_finally_ret(op_array, i TSRMLS_CC);
657+
break;
658+
default:
659+
break;
660+
}
661+
}
662+
}
663+
520664
ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC)
521665
{
522666
zend_op *opline, *end;
523667

524668
if (op_array->type!=ZEND_USER_FUNCTION && op_array->type!=ZEND_EVAL_CODE) {
525669
return 0;
526670
}
671+
if (op_array->has_finally_block) {
672+
zend_resolve_finally_calls(op_array TSRMLS_CC);
673+
}
527674
if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) {
528675
zend_update_extended_info(op_array TSRMLS_CC);
529676
}
@@ -560,30 +707,9 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC)
560707
}
561708
/* break omitted intentionally */
562709
case ZEND_JMP:
563-
if (op_array->last_try_catch) {
564-
zend_check_finally_breakout(op_array, opline, opline->op1.opline_num TSRMLS_CC);
565-
}
710+
case ZEND_FAST_CALL:
566711
opline->op1.jmp_addr = &op_array->opcodes[opline->op1.opline_num];
567712
break;
568-
case ZEND_BRK:
569-
case ZEND_CONT:
570-
if (op_array->last_try_catch) {
571-
int nest_levels, array_offset;
572-
zend_brk_cont_element *jmp_to;
573-
574-
nest_levels = Z_LVAL_P(opline->op2.zv);
575-
array_offset = opline->op1.opline_num;
576-
do {
577-
jmp_to = &op_array->brk_cont_array[array_offset];
578-
if (nest_levels > 1) {
579-
array_offset = jmp_to->parent;
580-
}
581-
} while (--nest_levels > 0);
582-
if (op_array->last_try_catch) {
583-
zend_check_finally_breakout(op_array, opline, jmp_to->brk TSRMLS_CC);
584-
}
585-
}
586-
break;
587713
case ZEND_JMPZ:
588714
case ZEND_JMPNZ:
589715
case ZEND_JMPZ_EX:

0 commit comments

Comments
 (0)