Skip to content

Commit

Permalink
Improve fix for #66608
Browse files Browse the repository at this point in the history
  • Loading branch information
laruence committed Jul 19, 2014
1 parent 9ce1a36 commit 8ff00e6
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 35 deletions.
46 changes: 40 additions & 6 deletions Zend/tests/bug66608.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,56 @@ Bug #66608 (Incorrect behavior with nested "finally" blocks)
function bar() {
try {
echo "1\n";
try {
} finally {
try {
} finally {
}
echo "2\n";
}
} finally {
try {
throw new Exception ("");
} catch (Exception $ab) {
echo "2\n";
echo "3\n";
} finally {
try {
} finally {
echo "3\n";
echo "4\n";
try {
} finally {
}
echo "4\n";
echo "5\n";
}
}
echo "5\n";
echo "6\n";
try {
} finally {
echo "6\n";
while (1) {
try {
echo "7\n";
break;
} finally {
echo "8\n";
}
echo "bad";
}
echo "9\n";
while (1) {
try {
throw new Exception("");
} catch(Exception $e) {
echo "10\n";
break;
} finally {
echo "11\n";
}
echo "bak\n";
}
}
echo "12\n";
}
echo "7\n";
echo "13\n";
}
bar();
--EXPECT--
Expand All @@ -37,3 +65,9 @@ bar();
5
6
7
8
9
10
11
12
13
4 changes: 2 additions & 2 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -843,8 +843,8 @@ int zend_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC);
#define ZEND_FAST_RET_TO_CATCH 1
#define ZEND_FAST_RET_TO_FINALLY 2

#define ZEND_FAST_CALL_FOR_CATCH 1
#define ZEND_FAST_CALL_FOR_FINALLY 2
#define ZEND_FAST_CALL_FROM_CATCH 1
#define ZEND_FAST_CALL_FROM_FINALLY 2

END_EXTERN_C()

Expand Down
81 changes: 58 additions & 23 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,49 @@ static void zend_check_finally_breakout(zend_op_array *op_array, zend_uint op_nu
}
}

static void zend_adjust_fast_call(zend_op_array *op_array, zend_uint fast_call, zend_uint start, zend_uint end TSRMLS_DC)
{
int i;
zend_uint op_num = 0;

for (i = 0; i < op_array->last_try_catch; i++) {
if (op_array->try_catch_array[i].finally_op > start
&& op_array->try_catch_array[i].finally_end < end) {
op_num = op_array->try_catch_array[i].finally_op;
start = op_array->try_catch_array[i].finally_end;
}
}

if (op_num) {
/* Must be ZEND_FAST_CALL */
ZEND_ASSERT(op_array->opcodes[op_num - 2].opcode == ZEND_FAST_CALL);
op_array->opcodes[op_num - 2].extended_value = ZEND_FAST_CALL_FROM_FINALLY;
op_array->opcodes[op_num - 2].op2.opline_num = fast_call;
}
}

static void zend_resolve_fast_call(zend_op_array *op_array, zend_uint fast_call, zend_uint op_num TSRMLS_DC)
{
int i;
zend_uint finally_op_num = 0;

for (i = 0; i < op_array->last_try_catch; i++) {
if (op_num >= op_array->try_catch_array[i].finally_op
&& op_num < op_array->try_catch_array[i].finally_end) {
finally_op_num = op_array->try_catch_array[i].finally_op;
}
}

if (finally_op_num) {
/* Must be ZEND_FAST_CALL */
ZEND_ASSERT(op_array->opcodes[finally_op_num - 2].opcode == ZEND_FAST_CALL);
if (op_array->opcodes[fast_call].extended_value == 0) {
op_array->opcodes[fast_call].extended_value = ZEND_FAST_CALL_FROM_FINALLY;
op_array->opcodes[fast_call].op2.opline_num = finally_op_num - 2;
}
}
}

static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num, zend_uint dst_num TSRMLS_DC)
{
zend_uint start_op;
Expand Down Expand Up @@ -536,11 +579,23 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num,
opline->opcode = ZEND_FAST_CALL;
SET_UNUSED(opline->op1);
SET_UNUSED(opline->op2);
opline->op1.opline_num = op_array->try_catch_array[i].finally_op;
zend_adjust_fast_call(op_array, start_op,
op_array->try_catch_array[i].finally_op,
op_array->try_catch_array[i].finally_end TSRMLS_CC);
if (op_array->try_catch_array[i].catch_op) {
opline->extended_value = ZEND_FAST_CALL_FOR_CATCH;
opline->extended_value = ZEND_FAST_CALL_FROM_CATCH;
opline->op2.opline_num = op_array->try_catch_array[i].catch_op;
opline->op1.opline_num = get_next_op_number(op_array);
/* generate a FAST_CALL to hole CALL_FROM_FINALLY */
opline = get_next_op(op_array TSRMLS_CC);
opline->opcode = ZEND_FAST_CALL;
SET_UNUSED(opline->op1);
SET_UNUSED(opline->op2);
zend_resolve_fast_call(op_array, start_op + 1, op_array->try_catch_array[i].finally_op - 2 TSRMLS_CC);
} else {
zend_resolve_fast_call(op_array, start_op, op_array->try_catch_array[i].finally_op - 2 TSRMLS_CC);
}
opline->op1.opline_num = op_array->try_catch_array[i].finally_op;

/* generate a sequence of FAST_CALL to upward finally block */
while (i > 0) {
Expand Down Expand Up @@ -603,26 +658,6 @@ static void zend_resolve_finally_ret(zend_op_array *op_array, zend_uint op_num T
}
}

static void zend_resolve_fast_call(zend_op_array *op_array, zend_uint op_num TSRMLS_DC)
{
int i;
zend_uint finally_op_num = 0;

for (i = 0; i < op_array->last_try_catch; i++) {
if (op_array->try_catch_array[i].finally_op > op_num) {
break;
}
if (op_num < op_array->try_catch_array[i].finally_end) {
finally_op_num = op_array->try_catch_array[i].finally_op;
}
}

if (finally_op_num) {
op_array->opcodes[op_num].extended_value = ZEND_FAST_CALL_FOR_FINALLY;
op_array->opcodes[op_num].op2.opline_num = finally_op_num - 2; /* it must be ZEND_FAST_CALL */
}
}

static void zend_resolve_finally_calls(zend_op_array *op_array TSRMLS_DC)
{
zend_uint i, j;
Expand Down Expand Up @@ -666,7 +701,7 @@ static void zend_resolve_finally_calls(zend_op_array *op_array TSRMLS_DC)
zend_resolve_finally_call(op_array, i, opline->op1.opline_num TSRMLS_CC);
break;
case ZEND_FAST_CALL:
zend_resolve_fast_call(op_array, i TSRMLS_CC);
zend_resolve_fast_call(op_array, i, i TSRMLS_CC);
break;
case ZEND_FAST_RET:
zend_resolve_finally_ret(op_array, i TSRMLS_CC);
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -5396,7 +5396,7 @@ ZEND_VM_HANDLER(162, ZEND_FAST_CALL, ANY, ANY)
{
USE_OPLINE

if ((opline->extended_value & ZEND_FAST_CALL_FOR_CATCH) &&
if ((opline->extended_value & ZEND_FAST_CALL_FROM_CATCH) &&
UNEXPECTED(EG(prev_exception) != NULL)) {
/* in case of unhandled exception jump to catch block instead of finally */
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
Expand All @@ -5411,7 +5411,7 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, ANY)
{
if (EX(fast_ret)) {
ZEND_VM_SET_OPCODE(EX(fast_ret) + 1);
if ((EX(fast_ret)->extended_value & ZEND_FAST_CALL_FOR_FINALLY)) {
if ((EX(fast_ret)->extended_value & ZEND_FAST_CALL_FROM_FINALLY)) {
EX(fast_ret) = &EX(op_array)->opcodes[EX(fast_ret)->op2.opline_num];
}
ZEND_VM_CONTINUE();
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,7 @@ static int ZEND_FASTCALL ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE

if ((opline->extended_value & ZEND_FAST_CALL_FOR_CATCH) &&
if ((opline->extended_value & ZEND_FAST_CALL_FROM_CATCH) &&
UNEXPECTED(EG(prev_exception) != NULL)) {
/* in case of unhandled exception jump to catch block instead of finally */
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
Expand All @@ -1152,7 +1152,7 @@ static int ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
if (EX(fast_ret)) {
ZEND_VM_SET_OPCODE(EX(fast_ret) + 1);
if ((EX(fast_ret)->extended_value & ZEND_FAST_CALL_FOR_FINALLY)) {
if ((EX(fast_ret)->extended_value & ZEND_FAST_CALL_FROM_FINALLY)) {
EX(fast_ret) = &EX(op_array)->opcodes[EX(fast_ret)->op2.opline_num];
}
ZEND_VM_CONTINUE();
Expand Down

0 comments on commit 8ff00e6

Please sign in to comment.