diff options
author | Dmitry Stogov <dmitry@zend.com> | 2015-04-22 02:30:10 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2015-04-22 02:30:10 +0300 |
commit | c61bf4bc1120a92c7525b1bc1cd0ae3e1837b3c7 (patch) | |
tree | f0c4b140ef814137762785aeb46b4b5e01952c2b | |
parent | 770cb1da71656fa0dce7dfab26e5e88cb557b639 (diff) | |
parent | 0ec3a6ff33e815f8d50d437fd00ba0f8b09bdf85 (diff) | |
download | php-git-c61bf4bc1120a92c7525b1bc1cd0ae3e1837b3c7.tar.gz |
Merge branch 'master' of git.php.net:php-src
* 'master' of git.php.net:php-src:
FAST_RET opcode also returns
Also consider specific catches when checking for uncaught
Break on uncaught exceptions in current frame in phpdbg
-rw-r--r-- | sapi/phpdbg/phpdbg.h | 1 | ||||
-rw-r--r-- | sapi/phpdbg/phpdbg_prompt.c | 46 | ||||
-rw-r--r-- | sapi/phpdbg/phpdbg_utils.c | 38 | ||||
-rw-r--r-- | sapi/phpdbg/phpdbg_utils.h | 2 |
4 files changed, 75 insertions, 12 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..621fc1e318 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" @@ -485,6 +486,7 @@ int phpdbg_skip_line_helper() /* {{{ */ { while (++opline < op_array->opcodes + op_array->last) { if (opline->lineno != EG(current_execute_data)->opline->lineno || opline->opcode == ZEND_RETURN + || opline->opcode == ZEND_FAST_RET || opline->opcode == ZEND_GENERATOR_RETURN || opline->opcode == ZEND_EXIT || opline->opcode == ZEND_YIELD @@ -528,11 +530,12 @@ static void phpdbg_seek_to_end(void) /* {{{ */ { while (++opline < op_array->opcodes + op_array->last) { switch (opline->opcode) { case ZEND_RETURN: + case ZEND_FAST_RET: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: case ZEND_YIELD: zend_hash_index_update_ptr(&PHPDBG_G(seek), (zend_ulong) opline, (void *) opline); - return; + return; } } } @@ -647,6 +650,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 +1380,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 +1394,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 +1424,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 +1437,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, exception)) { + goto ex_is_caught; + } + } while ((prev_ex = prev_ex->prev_execute_data)); + + PHPDBG_G(handled_exception) = 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) || @@ -1436,7 +1476,7 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */ goto next; } -#define INDEX_EXISTS_CHECK (zend_hash_index_exists(&PHPDBG_G(seek), address) || (EG(exception) && phpdbg_check_caught_ex(execute_data) == 0)) +#define INDEX_EXISTS_CHECK (zend_hash_index_exists(&PHPDBG_G(seek), address) || (exception && phpdbg_check_caught_ex(execute_data, exception) == 0)) /* run to next line */ if (PHPDBG_G(flags) & PHPDBG_IN_UNTIL) { @@ -1540,6 +1580,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 +1622,3 @@ PHPDBG_COMMAND(eol) /* {{{ */ return SUCCESS; } /* }}} */ - diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index e1f6c59502..9d63fcf727 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -713,24 +713,46 @@ head_done: } phpdbg_end_try_access(); } -PHPDBG_API zend_bool phpdbg_check_caught_ex(zend_execute_data *ex) { +PHPDBG_API zend_bool phpdbg_check_caught_ex(zend_execute_data *execute_data, zend_object *exception) { const zend_op *op; + zend_op *cur; uint32_t op_num, i; - zend_op_array *op_array = &ex->func->op_array; + zend_op_array *op_array = &execute_data->func->op_array; - if (ex->opline >= EG(exception_op) && ex->opline < EG(exception_op) + 3) { + if (execute_data->opline >= EG(exception_op) && execute_data->opline < EG(exception_op) + 3) { op = EG(opline_before_exception); } else { - op = ex->opline; + op = execute_data->opline; } 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) { - return 1; + for (i = 0; i < op_array->last_try_catch && op_array->try_catch_array[i].try_op < op_num; i++) { + uint32_t catch = op_array->try_catch_array[i].catch_op, finally = op_array->try_catch_array[i].finally_op; + if (op_num <= catch || op_num <= finally) { + if (finally && finally < catch) { + return 0; + } + + do { + zend_class_entry *ce; + cur = &op_array->opcodes[catch]; + + if (!(ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(cur->op1))))) { + ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(cur->op1)), EX_CONSTANT(cur->op1) + 1, ZEND_FETCH_CLASS_NO_AUTOLOAD); + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(cur->op1)), ce); + } + + if (ce == exception->ce || (ce && instanceof_function(exception->ce, ce))) { + return 1; + } + + catch = cur->extended_value; + } while (!cur->result.num); + + return 0; } } - return 0; + return op->opcode == ZEND_CATCH; } diff --git a/sapi/phpdbg/phpdbg_utils.h b/sapi/phpdbg/phpdbg_utils.h index 5ed2353987..dcf713e05b 100644 --- a/sapi/phpdbg/phpdbg_utils.h +++ b/sapi/phpdbg/phpdbg_utils.h @@ -94,7 +94,7 @@ int phpdbg_is_auto_global(char *name, int len); PHPDBG_API void phpdbg_xml_var_dump(zval *zv); -PHPDBG_API zend_bool phpdbg_check_caught_ex(zend_execute_data *ex); +PHPDBG_API zend_bool phpdbg_check_caught_ex(zend_execute_data *ex, zend_object *exception); #ifdef ZTS #define PHPDBG_OUTPUT_BACKUP_DEFINES() \ |