diff options
-rw-r--r-- | sapi/phpdbg/phpdbg.h | 1 | ||||
-rw-r--r-- | sapi/phpdbg/phpdbg_prompt.c | 40 | ||||
-rw-r--r-- | sapi/phpdbg/phpdbg_utils.c | 4 |
3 files changed, 42 insertions, 3 deletions
diff --git a/sapi/phpdbg/phpdbg.h b/sapi/phpdbg/phpdbg.h index 6de6e9e2b8..2cb588d086 100644 --- a/sapi/phpdbg/phpdbg.h +++ b/sapi/phpdbg/phpdbg.h @@ -233,6 +233,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) HashTable registered; /* registered */ HashTable seek; /* seek oplines */ zend_execute_data *seek_ex; /* call frame of oplines to seek to */ + zend_object *handled_exception; /* last handled exception (prevent multiple handling of same exception) */ phpdbg_frame_t frame; /* frame */ uint32_t last_line; /* last executed line */ diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index ac3fb67dbd..11af5bc790 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -24,6 +24,7 @@ #include "zend_compile.h" #include "zend_exceptions.h" #include "zend_vm.h" +#include "zend_generators.h" #include "phpdbg.h" #include "phpdbg_help.h" @@ -647,6 +648,7 @@ PHPDBG_COMMAND(run) /* {{{ */ } else { zend_rebuild_symbol_table(); } + PHPDBG_G(handled_exception) = NULL; /* clean seek state */ PHPDBG_G(flags) &= ~PHPDBG_SEEK_MASK; @@ -1376,7 +1378,12 @@ void phpdbg_clean(zend_bool full) /* {{{ */ } } /* }}} */ +/* code may behave weirdly if EG(exception) is set */ #define DO_INTERACTIVE(allow_async_unsafe) do { \ + if (exception) { \ + ++GC_REFCOUNT(exception); \ + zend_clear_exception(); \ + } \ if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \ const char *file_char = zend_get_executed_filename(); \ zend_string *file = zend_string_init(file_char, strlen(file_char), 0); \ @@ -1385,6 +1392,13 @@ void phpdbg_clean(zend_bool full) /* {{{ */ } \ \ switch (phpdbg_interactive(allow_async_unsafe)) { \ + zval zv; \ + default: \ + if (exception) { \ + Z_OBJ(zv) = exception; \ + zend_throw_exception_internal(&zv); \ + } \ + /* fallthrough */ \ case PHPDBG_LEAVE: \ case PHPDBG_FINISH: \ case PHPDBG_UNTIL: \ @@ -1408,6 +1422,8 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */ PHPDBG_G(in_execution) = 1; while (1) { + zend_object *exception = EG(exception); + if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) { /* resolve nth opline breakpoints */ phpdbg_resolve_op_array_breaks(&execute_data->func->op_array); @@ -1419,6 +1435,28 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */ } #endif + /* check for uncaught exceptions */ + if (exception && PHPDBG_G(handled_exception) != exception) { + zend_execute_data *prev_ex = execute_data; + + do { + prev_ex = zend_generator_check_placeholder_frame(prev_ex); + /* assuming that no internal functions will silently swallow exceptions ... */ + if (!prev_ex->func || !ZEND_USER_CODE(prev_ex->func->common.type)) { + continue; + } + + if (phpdbg_check_caught_ex(prev_ex)) { + goto ex_is_caught; + } + } while ((prev_ex = prev_ex->prev_execute_data)); + + PHPDBG_G(handled_exception) = EG(exception); + phpdbg_error("exception", "name=\"%s\"", "Uncaught exception %s", exception->ce->name->val); + DO_INTERACTIVE(1); + } +ex_is_caught: + /* allow conditional breakpoints and initialization to access the vm uninterrupted */ if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) || @@ -1540,6 +1578,7 @@ next: /* only if *not* interactive and while executing */ void phpdbg_force_interruption(void) /* {{{ */ { + zend_object *exception = EG(exception); zend_execute_data *data = EG(current_execute_data); /* should be always readable if not NULL */ PHPDBG_G(flags) |= PHPDBG_IN_SIGNAL_HANDLER; @@ -1581,4 +1620,3 @@ PHPDBG_COMMAND(eol) /* {{{ */ return SUCCESS; } /* }}} */ - diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index e1f6c59502..fce042a225 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -726,8 +726,8 @@ PHPDBG_API zend_bool phpdbg_check_caught_ex(zend_execute_data *ex) { op_num = op - op_array->opcodes; - for (i = 0; i < op_array->last_try_catch && op_array->try_catch_array[i].try_op > op_num; i++) { - if (op_num < op_array->try_catch_array[i].catch_op || op_num < op_array->try_catch_array[i].finally_op) { + for (i = 0; i < op_array->last_try_catch && op_array->try_catch_array[i].try_op < op_num; i++) { + if (op_num <= op_array->try_catch_array[i].catch_op || op_num <= op_array->try_catch_array[i].finally_op) { return 1; } } |