Skip to content

Instantly share code, notes, and snippets.

@dstogov
Last active May 23, 2016 11:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dstogov/0a809891c6a3ac3fac4bd0d9711dd330 to your computer and use it in GitHub Desktop.
Save dstogov/0a809891c6a3ac3fac4bd0d9711dd330 to your computer and use it in GitHub Desktop.
diff --git a/Zend/tests/bug65784.phpt b/Zend/tests/bug65784.phpt
index c079b3d..397c52a 100644
--- a/Zend/tests/bug65784.phpt
+++ b/Zend/tests/bug65784.phpt
@@ -57,8 +57,13 @@ $bar = foo3();
string(9) "not catch"
NULL
-Fatal error: Uncaught Error: Class 'NotExists' not found in %sbug65784.php:%d
+Fatal error: Uncaught Exception: not catched in %sbug65784.php:42
Stack trace:
-#0 %s(%d): foo3()
+#0 %sbug65784.php(52): foo3()
#1 {main}
- thrown in %sbug65784.php on line %d
+
+Next Error: Class 'NotExists' not found in %s/bug65784.php:46
+Stack trace:
+#0 %sbug65784.php(52): foo3()
+#1 {main}
+ thrown in %sbug65784.php on line 46
diff --git a/Zend/tests/try/bug72213.phpt b/Zend/tests/try/bug72213.phpt
index aee4c95..6240502 100644
--- a/Zend/tests/try/bug72213.phpt
+++ b/Zend/tests/try/bug72213.phpt
@@ -1,7 +1,5 @@
--TEST--
Bug #72213 (Finally leaks on nested exceptions)
---XFAIL--
-See https://bugs.php.net/bug.php?id=72213
--FILE--
<?php
function test() {
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 0994681..2ace159 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -234,6 +234,7 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
CG(context).backpatch_count = 0;
CG(context).in_finally = 0;
CG(context).fast_call_var = -1;
+ CG(context).try_catch_offset = -1;
CG(context).current_brk_cont = -1;
CG(context).last_brk_cont = 0;
CG(context).brk_cont_array = NULL;
@@ -4061,6 +4062,7 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
opline->op1_type = IS_TMP_VAR;
opline->op1.var = CG(context).fast_call_var;
+ opline->op2.num = CG(context).try_catch_offset;
}
/* Generator return types are handled separately */
@@ -4573,6 +4575,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
uint32_t try_catch_offset;
uint32_t *jmp_opnums = safe_emalloc(sizeof(uint32_t), catches->children, 0);
uint32_t orig_fast_call_var = CG(context).fast_call_var;
+ uint32_t orig_try_catch_offset = CG(context).try_catch_offset;
if (catches->children == 0 && !finally_ast) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use try without catch or finally");
@@ -4606,6 +4609,8 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_stack_push(&CG(loop_var_stack), &fast_call);
}
+ CG(context).try_catch_offset = try_catch_offset;
+
zend_compile_stmt(try_ast);
if (catches->children != 0) {
@@ -4704,12 +4709,15 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
opline = zend_emit_op(NULL, ZEND_FAST_RET, NULL, NULL);
opline->op1_type = IS_TMP_VAR;
opline->op1.var = CG(context).fast_call_var;
+ opline->op2.num = orig_try_catch_offset;
zend_update_jump_target_to_next(opnum_jmp);
CG(context).fast_call_var = orig_fast_call_var;
}
+ CG(context).try_catch_offset = try_catch_offset;
+
efree(jmp_opnums);
}
/* }}} */
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 0de475d..ef341c5 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -191,6 +191,7 @@ typedef struct _zend_oparray_context {
int backpatch_count;
int in_finally;
uint32_t fast_call_var;
+ uint32_t try_catch_offset;
int current_brk_cont;
int last_brk_cont;
zend_brk_cont_element *brk_cont_array;
@@ -958,11 +959,6 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
#define ZEND_RETURNS_FUNCTION 1<<0
#define ZEND_RETURNS_VALUE 1<<1
-#define ZEND_FAST_RET_TO_CATCH 1
-#define ZEND_FAST_RET_TO_FINALLY 2
-
-#define ZEND_FAST_CALL_FROM_FINALLY 1
-
#define ZEND_ARRAY_ELEMENT_REF (1<<0)
#define ZEND_ARRAY_NOT_PACKED (1<<1)
#define ZEND_ARRAY_SIZE_SHIFT 2
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 34e060f..0b5fe75 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -534,57 +534,6 @@ static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num
}
}
-static void zend_resolve_fast_call(zend_op_array *op_array, uint32_t op_num)
-{
- int i;
- uint32_t finally_num = (uint32_t)-1;
-
- 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_num = i;
- }
- }
-
- if (finally_num != (uint32_t)-1) {
- /* Must be ZEND_FAST_CALL */
- ZEND_ASSERT(op_array->opcodes[op_array->try_catch_array[finally_num].finally_op - 2].opcode == ZEND_FAST_CALL);
- op_array->opcodes[op_num].extended_value = ZEND_FAST_CALL_FROM_FINALLY;
- }
-}
-
-static void zend_resolve_finally_ret(zend_op_array *op_array, uint32_t op_num)
-{
- int i;
- uint32_t finally_num = (uint32_t)-1;
- uint32_t catch_num = (uint32_t)-1;
-
- for (i = 0; i < op_array->last_try_catch; i++) {
- if (op_array->try_catch_array[i].try_op > op_num) {
- break;
- }
- if (op_num < op_array->try_catch_array[i].finally_op) {
- finally_num = i;
- }
- if (op_num < op_array->try_catch_array[i].catch_op) {
- catch_num = i;
- }
- }
-
- if (finally_num != (uint32_t)-1 &&
- (catch_num == (uint32_t)-1 ||
- op_array->try_catch_array[catch_num].catch_op >=
- op_array->try_catch_array[finally_num].finally_op)) {
- /* in case of unhandled exception return to upward finally block */
- op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_FINALLY;
- op_array->opcodes[op_num].op2.num = finally_num;
- } else if (catch_num != (uint32_t)-1) {
- /* in case of unhandled exception return to upward catch block */
- op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_CATCH;
- op_array->opcodes[op_num].op2.num = catch_num;
- }
-}
-
static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const zend_op *opline) {
int nest_levels = opline->op2.num;
int array_offset = opline->op1.num;
@@ -633,12 +582,8 @@ ZEND_API int pass_two(zend_op_array *op_array)
switch (opline->opcode) {
case ZEND_FAST_CALL:
opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op;
- zend_resolve_fast_call(op_array, opline - op_array->opcodes);
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
break;
- case ZEND_FAST_RET:
- zend_resolve_finally_ret(op_array, opline - op_array->opcodes);
- break;
case ZEND_BRK:
case ZEND_CONT:
{
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 93ff58e..11340db 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -7129,7 +7129,6 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
/* further blocks will not be relevant... */
break;
}
- in_finally = 0;
if (op_num < EX(func)->op_array.try_catch_array[i].catch_op) {
catch_op_num = EX(func)->op_array.try_catch_array[i].catch_op;
}
@@ -7150,9 +7149,6 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
cleanup_live_vars(execute_data, op_num, finally_op_num);
- if (in_finally && Z_OBJ_P(fast_call)) {
- zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
- }
Z_OBJ_P(fast_call) = EG(exception);
EG(exception) = NULL;
fast_call->u2.lineno = (uint32_t)-1;
@@ -7162,12 +7158,25 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
cleanup_live_vars(execute_data, op_num, catch_op_num);
if (in_finally) {
/* we are going out of current finally scope */
- zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
+ zval *fast_call;
+ uint32_t try_catch_offset;
+ zend_object *obj = EG(exception);
- if (Z_OBJ_P(fast_call)) {
- zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
- Z_OBJ_P(fast_call) = NULL;
- }
+ do {
+ if (catch_op_num && catch_op_num < finally_op_end) {
+ break;
+ }
+ fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
+ if (Z_OBJ_P(fast_call)) {
+ zend_exception_set_previous(obj, Z_OBJ_P(fast_call));
+ obj = Z_OBJ_P(fast_call);
+ }
+ try_catch_offset = EX(func)->op_array.opcodes[finally_op_end].op2.num;
+ if (try_catch_offset == (uint32_t)-1) {
+ break;
+ }
+ finally_op_end = EX(func)->op_array.try_catch_array[try_catch_offset].finally_end;
+ } while (finally_op_end);
}
if (catch_op_num) {
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
@@ -7528,39 +7537,48 @@ ZEND_VM_HANDLER(142, ZEND_YIELD_FROM, CONST|TMP|VAR|CV, ANY)
ZEND_VM_RETURN();
}
-ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, ANY)
+ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, TRY_CATCH)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
+ uint32_t try_catch_offset;
+ SAVE_OPLINE();
/* check for delayed exception */
if (Z_OBJ_P(fast_call) != NULL) {
- SAVE_OPLINE();
/* discard the previously thrown exception */
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
+ try_catch_offset = EX(func)->op_array.opcodes[EX(func)->op_array.try_catch_array[opline->op2.num].finally_end].op2.num;
+ while (try_catch_offset != (uint32_t)-1) {
+ zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + try_catch_offset;
+ zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
+
+ if (Z_OBJ_P(fast_call) != NULL) {
+ /* discard the previously thrown exception */
+ OBJ_RELEASE(Z_OBJ_P(fast_call));
+ Z_OBJ_P(fast_call) = NULL;
+ }
+ try_catch_offset = EX(func)->op_array.opcodes[try_catch->finally_end].op2.num;
+ }
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(162, ZEND_FAST_CALL, JMP_ADDR, ANY, FAST_CALL)
+ZEND_VM_HANDLER(162, ZEND_FAST_CALL, JMP_ADDR, ANY)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->result.var);
- if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY && UNEXPECTED(Z_OBJ_P(fast_call) != NULL)) {
- fast_call->u2.lineno = (uint32_t)-1;
- } else {
- Z_OBJ_P(fast_call) = NULL;
- /* set return address */
- fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
- }
+ Z_OBJ_P(fast_call) = NULL;
+ /* set return address */
+ fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op1));
ZEND_VM_CONTINUE();
}
-ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH, FAST_RET)
+ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
@@ -7571,36 +7589,43 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH, FAST_RET)
ZEND_VM_CONTINUE();
} else {
/* special case for unhandled exceptions */
- USE_OPLINE
-
- if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
- uint32_t finally_op = EX(func)->op_array.try_catch_array[opline->op2.num].finally_op;
- uint32_t finally_end = EX(func)->op_array.try_catch_array[opline->op2.num].finally_end;
- zval *next_fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_end].op1.var);
-
- Z_OBJ_P(next_fast_call) = Z_OBJ_P(fast_call);
- next_fast_call->u2.lineno = (uint32_t)-1;
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, finally_op);
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[finally_op]);
- ZEND_VM_CONTINUE();
- } else {
- EG(exception) = Z_OBJ_P(fast_call);
- Z_OBJ_P(fast_call) = NULL;
- if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
- uint32_t catch_op = EX(func)->op_array.try_catch_array[opline->op2.num].catch_op;
-
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, catch_op);
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op]);
+ if (opline->op2.num != (uint32_t)-1) {
+ /* check upper finally block */
+ zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + opline->op2.num;
+
+ if (try_catch->catch_op && opline < &EX(func)->op_array.opcodes[try_catch->catch_op]) {
+ EG(exception) = Z_OBJ_P(fast_call);
+ cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, try_catch->catch_op);
+ ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
ZEND_VM_CONTINUE();
} else {
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, 0);
- if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
- zend_generator *generator = zend_get_running_generator(execute_data);
- zend_generator_close(generator, 1);
- ZEND_VM_RETURN();
+ uint32_t target;
+ zval *next_fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
+
+ if (try_catch->finally_op && opline < &EX(func)->op_array.opcodes[try_catch->finally_op]) {
+ target = try_catch->finally_op;
} else {
- ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
+ target = try_catch->finally_end;
+ if (Z_OBJ_P(next_fast_call)) {
+ zend_exception_set_previous(Z_OBJ_P(fast_call), Z_OBJ_P(next_fast_call));
+ }
}
+ Z_OBJ_P(next_fast_call) = Z_OBJ_P(fast_call);
+ next_fast_call->u2.lineno = (uint32_t)-1;
+ cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, target);
+ ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[target]);
+ ZEND_VM_CONTINUE();
+ }
+ } else {
+ /* leave function with exception */
+ EG(exception) = Z_OBJ_P(fast_call);
+ cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, 0);
+ if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
+ zend_generator *generator = zend_get_running_generator(execute_data);
+ zend_generator_close(generator, 1);
+ ZEND_VM_RETURN();
+ } else {
+ ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
}
}
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 1b673b8..672ad36 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1714,7 +1714,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
/* further blocks will not be relevant... */
break;
}
- in_finally = 0;
if (op_num < EX(func)->op_array.try_catch_array[i].catch_op) {
catch_op_num = EX(func)->op_array.try_catch_array[i].catch_op;
}
@@ -1735,9 +1734,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
cleanup_live_vars(execute_data, op_num, finally_op_num);
- if (in_finally && Z_OBJ_P(fast_call)) {
- zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
- }
Z_OBJ_P(fast_call) = EG(exception);
EG(exception) = NULL;
fast_call->u2.lineno = (uint32_t)-1;
@@ -1747,12 +1743,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
cleanup_live_vars(execute_data, op_num, catch_op_num);
if (in_finally) {
/* we are going out of current finally scope */
- zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
+ zval *fast_call;
+ uint32_t try_catch_offset;
+ zend_object *obj = EG(exception);
- if (Z_OBJ_P(fast_call)) {
- zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
- Z_OBJ_P(fast_call) = NULL;
- }
+ do {
+ if (catch_op_num && catch_op_num < finally_op_end) {
+ break;
+ }
+ fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
+ if (Z_OBJ_P(fast_call)) {
+ zend_exception_set_previous(obj, Z_OBJ_P(fast_call));
+ obj = Z_OBJ_P(fast_call);
+ }
+ try_catch_offset = EX(func)->op_array.opcodes[finally_op_end].op2.num;
+ if (try_catch_offset == (uint32_t)-1) {
+ break;
+ }
+ finally_op_end = EX(func)->op_array.try_catch_array[try_catch_offset].finally_end;
+ } while (finally_op_end);
}
if (catch_op_num) {
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
@@ -1811,14 +1820,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DISCARD_EXCEPTION_SPEC_HANDLER
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
+ uint32_t try_catch_offset;
+ SAVE_OPLINE();
/* check for delayed exception */
if (Z_OBJ_P(fast_call) != NULL) {
- SAVE_OPLINE();
/* discard the previously thrown exception */
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
+ try_catch_offset = EX(func)->op_array.opcodes[EX(func)->op_array.try_catch_array[opline->op2.num].finally_end].op2.num;
+ while (try_catch_offset != (uint32_t)-1) {
+ zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + try_catch_offset;
+ zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
+
+ if (Z_OBJ_P(fast_call) != NULL) {
+ /* discard the previously thrown exception */
+ OBJ_RELEASE(Z_OBJ_P(fast_call));
+ Z_OBJ_P(fast_call) = NULL;
+ }
+ try_catch_offset = EX(func)->op_array.opcodes[try_catch->finally_end].op2.num;
+ }
ZEND_VM_NEXT_OPCODE();
}
@@ -1828,13 +1850,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OP
USE_OPLINE
zval *fast_call = EX_VAR(opline->result.var);
- if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY && UNEXPECTED(Z_OBJ_P(fast_call) != NULL)) {
- fast_call->u2.lineno = (uint32_t)-1;
- } else {
- Z_OBJ_P(fast_call) = NULL;
- /* set return address */
- fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
- }
+ Z_OBJ_P(fast_call) = NULL;
+ /* set return address */
+ fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op1));
ZEND_VM_CONTINUE();
}
@@ -1850,36 +1868,43 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPC
ZEND_VM_CONTINUE();
} else {
/* special case for unhandled exceptions */
- USE_OPLINE
-
- if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
- uint32_t finally_op = EX(func)->op_array.try_catch_array[opline->op2.num].finally_op;
- uint32_t finally_end = EX(func)->op_array.try_catch_array[opline->op2.num].finally_end;
- zval *next_fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_end].op1.var);
-
- Z_OBJ_P(next_fast_call) = Z_OBJ_P(fast_call);
- next_fast_call->u2.lineno = (uint32_t)-1;
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, finally_op);
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[finally_op]);
- ZEND_VM_CONTINUE();
- } else {
- EG(exception) = Z_OBJ_P(fast_call);
- Z_OBJ_P(fast_call) = NULL;
- if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
- uint32_t catch_op = EX(func)->op_array.try_catch_array[opline->op2.num].catch_op;
-
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, catch_op);
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op]);
+ if (opline->op2.num != (uint32_t)-1) {
+ /* check upper finally block */
+ zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + opline->op2.num;
+
+ if (try_catch->catch_op && opline < &EX(func)->op_array.opcodes[try_catch->catch_op]) {
+ EG(exception) = Z_OBJ_P(fast_call);
+ cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, try_catch->catch_op);
+ ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
ZEND_VM_CONTINUE();
} else {
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, 0);
- if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
- zend_generator *generator = zend_get_running_generator(execute_data);
- zend_generator_close(generator, 1);
- ZEND_VM_RETURN();
+ uint32_t target;
+ zval *next_fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
+
+ if (try_catch->finally_op && opline < &EX(func)->op_array.opcodes[try_catch->finally_op]) {
+ target = try_catch->finally_op;
} else {
- ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ target = try_catch->finally_end;
+ if (Z_OBJ_P(next_fast_call)) {
+ zend_exception_set_previous(Z_OBJ_P(fast_call), Z_OBJ_P(next_fast_call));
+ }
}
+ Z_OBJ_P(next_fast_call) = Z_OBJ_P(fast_call);
+ next_fast_call->u2.lineno = (uint32_t)-1;
+ cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, target);
+ ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[target]);
+ ZEND_VM_CONTINUE();
+ }
+ } else {
+ /* leave function with exception */
+ EG(exception) = Z_OBJ_P(fast_call);
+ cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, 0);
+ if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
+ zend_generator *generator = zend_get_running_generator(execute_data);
+ zend_generator_close(generator, 1);
+ ZEND_VM_RETURN();
+ } else {
+ ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
}
}
diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php
index 88fe287..03d9817 100644
--- a/Zend/zend_vm_gen.php
+++ b/Zend/zend_vm_gen.php
@@ -83,8 +83,8 @@ $vm_op_flags = array(
"ZEND_VM_EXT_CONST_FETCH" => 0x06000000,
"ZEND_VM_EXT_TYPE" => 0x07000000,
"ZEND_VM_EXT_EVAL" => 0x08000000,
- "ZEND_VM_EXT_FAST_CALL" => 0x09000000,
- "ZEND_VM_EXT_FAST_RET" => 0x0a000000,
+ // unused 0x09000000,
+ // unused 0x0a000000,
"ZEND_VM_EXT_SRC" => 0x0b000000,
"ZEND_VM_EXT_SEND" => 0x0c000000,
"ZEND_VM_NO_CONST_CONST" => 0x40000000,
@@ -124,8 +124,6 @@ $vm_ext_decode = array(
"ARRAY_INIT" => ZEND_VM_EXT_ARRAY_INIT,
"TYPE" => ZEND_VM_EXT_TYPE,
"EVAL" => ZEND_VM_EXT_EVAL,
- "FAST_CALL" => ZEND_VM_EXT_FAST_CALL,
- "FAST_RET" => ZEND_VM_EXT_FAST_RET,
"ISSET" => ZEND_VM_EXT_ISSET,
"ARG_NUM" => ZEND_VM_EXT_ARG_NUM,
"REF" => ZEND_VM_EXT_REF,
diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c
index 180b745..a1713ef 100644
--- a/Zend/zend_vm_opcodes.c
+++ b/Zend/zend_vm_opcodes.c
@@ -368,11 +368,11 @@ static uint32_t zend_vm_opcodes_flags[184] = {
0x00000101,
0x05000000,
0x00000000,
- 0x00000000,
+ 0x00003000,
0x0b000303,
0x00000003,
- 0x09000020,
- 0x0a003000,
+ 0x00000020,
+ 0x00003000,
0x00000010,
0x00000000,
0x00000707,
diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h
index 00aa20e..4f5a0d6 100644
--- a/Zend/zend_vm_opcodes.h
+++ b/Zend/zend_vm_opcodes.h
@@ -54,8 +54,6 @@
#define ZEND_VM_EXT_CONST_FETCH 0x06000000
#define ZEND_VM_EXT_TYPE 0x07000000
#define ZEND_VM_EXT_EVAL 0x08000000
-#define ZEND_VM_EXT_FAST_CALL 0x09000000
-#define ZEND_VM_EXT_FAST_RET 0x0a000000
#define ZEND_VM_EXT_SRC 0x0b000000
#define ZEND_VM_EXT_SEND 0x0c000000
#define ZEND_VM_NO_CONST_CONST 0x40000000
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c
index 6ad5110..674f27f 100644
--- a/ext/opcache/Optimizer/block_pass.c
+++ b/ext/opcache/Optimizer/block_pass.c
@@ -904,9 +904,8 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
zend_op *opline = new_opcodes;
zend_op *end = opline + len;
while (opline < end) {
- if ((opline->opcode == ZEND_FAST_CALL ||
- opline->opcode == ZEND_FAST_RET) &&
- opline->extended_value &&
+ if (opline->opcode == ZEND_FAST_RET &&
+ opline->op2.num != (uint32_t)-1 &&
opline->op2.num < (uint32_t)j) {
opline->op2.num = map[opline->op2.num];
}
diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c
index 70abe4d..edf7c3d 100644
--- a/ext/opcache/Optimizer/zend_dump.c
+++ b/ext/opcache/Optimizer/zend_dump.c
@@ -92,7 +92,7 @@ static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t fla
if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
fprintf(stderr, " %u", op.num);
} else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
- if (opline->opcode != ZEND_FAST_RET || opline->extended_value) {
+ if (op.num != (uint32_t)-1) {
fprintf(stderr, " try-catch(%u)", op.num);
}
} else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
@@ -498,16 +498,6 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
fprintf(stderr, " (\?\?\?)");
break;
}
- } else if (ZEND_VM_EXT_FAST_CALL == (flags & ZEND_VM_EXT_MASK)) {
- if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY) {
- fprintf(stderr, " (from-finally)");
- }
- } else if (ZEND_VM_EXT_FAST_RET == (flags & ZEND_VM_EXT_MASK)) {
- if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
- fprintf(stderr, " (to-catch)");
- } else if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
- fprintf(stderr, " (to-finally)");
- }
} else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) {
if (opline->extended_value == ZEND_RETURNS_VALUE) {
fprintf(stderr, " (value)");
diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c
index 8bd7c4e..44119c9 100644
--- a/sapi/phpdbg/phpdbg_opcode.c
+++ b/sapi/phpdbg/phpdbg_opcode.c
@@ -74,7 +74,7 @@ char *phpdbg_decode_input_op(
} else if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
spprintf(&result, 0, "%" PRIu32, op.num);
} else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
- if (opline->opcode != ZEND_FAST_RET || opline->extended_value) {
+ if (op.num != (uint32_t)-1) {
spprintf(&result, 0, "try-catch(%" PRIu32 ")", op.num);
}
} else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
@@ -99,21 +99,6 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *opline) /*{{{ */
uint32_t flags = zend_get_opcode_flags(opline->opcode);
char *result, *decode[4] = {NULL, NULL, NULL, NULL};
- /* EX */
- switch (opline->opcode) {
- case ZEND_FAST_CALL:
- if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY) {
- decode[0] = estrdup("FAST_CALL<FROM_FINALLY>");
- }
- break;
- case ZEND_FAST_RET:
- if (opline->extended_value != 0) {
- spprintf(&decode[0], 0, "FAST_RET<%s>",
- opline->extended_value == ZEND_FAST_RET_TO_CATCH ? "TO_CATCH" : "TO_FINALLY");
- }
- break;
- }
-
/* OP1 */
decode[1] = phpdbg_decode_input_op(
ops, opline, opline->op1, opline->op1_type, ZEND_VM_OP1_FLAGS(flags));
diff --git a/sapi/phpdbg/tests/exceptions_003.phpt b/sapi/phpdbg/tests/exceptions_003.phpt
index 37e7289..2a982bc 100644
--- a/sapi/phpdbg/tests/exceptions_003.phpt
+++ b/sapi/phpdbg/tests/exceptions_003.phpt
@@ -25,7 +25,7 @@ prompt> [L7 %s ECHO "ok "
00008: }
00009: } catch (Error $e) {
prompt> ok
-[L7 %s FAST_RET<TO_CATCH> ~%d try-catch(0) %s]
+[L7 %s FAST_RET ~%d try-catch(0) %s]
[L9 %s CATCH "Error" $e 1 %s]
>00005: x();
00006: } finally {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment