Mercurial > octave
changeset 24738:3695c2cd69b8
don't use singleton pattern for bp_table
* bp-table.h, bp-table.cc (class bp_table): Don't use singleton
pattern. Move inside octave namespace. Change all uses.
* interpreter-private.h, interpreter-private.cc (__get_bp_table__):
New function.
* interpreter.h, interpreter.cc (interpreter::m_bp_table): New data
member.
(interpreter::interpreter): Initialize it.
(interpreter::get_bp_table): New function.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Mon, 12 Feb 2018 00:58:31 -0500 |
parents | 5be92b26ef8f |
children | 5c266e59ebb9 |
files | libgui/src/m-editor/file-editor-tab.cc libinterp/corefcn/debug.cc libinterp/corefcn/error.cc libinterp/corefcn/interpreter-private.cc libinterp/corefcn/interpreter-private.h libinterp/corefcn/interpreter.cc libinterp/corefcn/interpreter.h libinterp/corefcn/symtab.cc libinterp/parse-tree/bp-table.cc libinterp/parse-tree/bp-table.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-jit.cc |
diffstat | 12 files changed, 1014 insertions(+), 1026 deletions(-) [+] |
line wrap: on
line diff
--- a/libgui/src/m-editor/file-editor-tab.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libgui/src/m-editor/file-editor-tab.cc Mon Feb 12 00:58:31 2018 -0500 @@ -397,7 +397,10 @@ frame.protect_var (buffer_error_messages); buffer_error_messages++; - bp_table::condition_valid (new_condition.toStdString ()); + octave::bp_table& bptab + = octave::__get_bp_table__ ("handle_context_menu_break_condition"); + + bptab.condition_valid (new_condition.toStdString ()); valid = true; } catch (const index_exception& e) { } @@ -991,7 +994,12 @@ line_info[0] = info.line; if (octave_qt_link::file_in_path (info.file, info.dir)) - bp_table::add_breakpoint (info.function_name, line_info, info.condition); + { + octave::bp_table& bptab + = octave::__get_bp_table__ ("octave_qt_link::file_in_path"); + + bptab.add_breakpoint (info.function_name, line_info, info.condition); + } } void file_editor_tab::remove_breakpoint_callback (const bp_info& info) @@ -1000,13 +1008,23 @@ line_info[0] = info.line; if (octave_qt_link::file_in_path (info.file, info.dir)) - bp_table::remove_breakpoint (info.function_name, line_info); + { + octave::bp_table& bptab + = octave::__get_bp_table__ ("remove_breakpoint_callback"); + + bptab.remove_breakpoint (info.function_name, line_info); + } } void file_editor_tab::remove_all_breakpoints_callback (const bp_info& info) { if (octave_qt_link::file_in_path (info.file, info.dir)) - bp_table::remove_all_breakpoints_in_file (info.function_name, true); + { + octave::bp_table& bptab + = octave::__get_bp_table__ ("remove_all_breakpoints_callback"); + + bptab.remove_all_breakpoints_in_file (info.function_name, true); + } } file_editor_tab::bp_info::bp_info (const QString& fname, int l,
--- a/libinterp/corefcn/debug.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/corefcn/debug.cc Mon Feb 12 00:58:31 2018 -0500 @@ -58,7 +58,7 @@ #include "variables.h" static octave_value -intmap_to_ov (const bp_table::intmap& line) +intmap_to_ov (const octave::bp_table::intmap& line) { int idx = 0; @@ -66,7 +66,7 @@ for (size_t i = 0; i < line.size (); i++) { - bp_table::const_intmap_iterator p = line.find (i); + octave::bp_table::const_intmap_iterator p = line.find (i); if (p != line.end ()) { @@ -170,24 +170,26 @@ @seealso{dbclear, dbstatus, dbstep, debug_on_error, debug_on_warning, debug_on_interrupt} @end deftypefn */) { - bp_table::intmap retmap; + octave::bp_table::intmap retmap; std::string symbol_name = ""; // stays empty for "dbstop if error" etc - bp_table::intmap lines; + octave::bp_table::intmap lines; std::string condition = ""; octave_value retval; + octave::bp_table& bptab = octave::__get_bp_table__ ("Fdbstop"); + if (args.length() >= 1 && ! args(0).isstruct ()) { // explicit function / line / condition - bp_table::parse_dbfunction_params ("dbstop", args, symbol_name, - lines, condition); + bptab.parse_dbfunction_params ("dbstop", args, symbol_name, + lines, condition); if (lines.size () == 0) lines[0] = 1; if (symbol_name != "") { - retmap = bp_table::add_breakpoint (symbol_name, lines, condition); + retmap = bptab.add_breakpoint (symbol_name, lines, condition); retval = intmap_to_ov (retmap); } } @@ -201,7 +203,7 @@ if (mv.isfield ("bkpt") || mv.isfield ("errs") || mv.isfield ("warn") || mv.isfield ("intr")) { - bp_table::dbstop_process_map_args (mv); + bptab.dbstop_process_map_args (mv); // Replace mv by "bkpt", to use the processing below. octave_value bkpt = mv.getfield ("bkpt"); @@ -238,9 +240,10 @@ for (octave_idx_type i = 0; i < line.numel (); i++) { lines [0] = line(i).double_value (); - bp_table::add_breakpoint (name(i).string_value (), lines, - use_cond ? cond(i).string_value () - : unconditional ); + bptab.add_breakpoint (name(i).string_value (), lines, + (use_cond + ? cond(i).string_value () + : unconditional)); } retval = octave_value (line.numel ()); } @@ -294,23 +297,24 @@ @end deftypefn */) { std::string symbol_name = ""; // stays empty for "dbclear if error" etc - bp_table::intmap lines; + octave::bp_table::intmap lines; std::string dummy; // "if" condition -- only used for dbstop int nargin = args.length (); - bp_table::parse_dbfunction_params ("dbclear", args, symbol_name, - lines, dummy); + octave::bp_table& bptab = octave::__get_bp_table__ ("Fdbclear"); + + bptab.parse_dbfunction_params ("dbclear", args, symbol_name, lines, dummy); if (nargin == 1 && symbol_name == "all") { - bp_table::remove_all_breakpoints (); - bp_table::dbclear_all_signals (); + bptab.remove_all_breakpoints (); + bptab.dbclear_all_signals (); } else { if (symbol_name != "") - bp_table::remove_breakpoint (symbol_name, lines); + bptab.remove_breakpoint (symbol_name, lines); } return ovl (); @@ -367,9 +371,11 @@ error ("dbstatus: only zero or one arguments accepted\n"); octave_value_list fcn_list; - bp_table::fname_bp_map bp_list; + octave::bp_table::fname_bp_map bp_list; std::string symbol_name; + octave::bp_table& bptab = octave::__get_bp_table__ ("Fdbstatus"); + if (nargin == 1) { if (! args(0).is_string ()) @@ -377,7 +383,7 @@ symbol_name = args(0).string_value (); fcn_list(0) = symbol_name; - bp_list = bp_table::get_breakpoint_list (fcn_list); + bp_list = bptab.get_breakpoint_list (fcn_list); } else { @@ -393,7 +399,7 @@ } */ - bp_list = bp_table::get_breakpoint_list (fcn_list); + bp_list = bptab.get_breakpoint_list (fcn_list); } if (nargout == 0) @@ -402,7 +408,7 @@ for (auto& fnm_bp_p: bp_list) { - std::list<bp_type> m = fnm_bp_p.second; + std::list<octave::bp_type> m = fnm_bp_p.second; // print unconditional breakpoints, if any, on a single line @@ -441,7 +447,7 @@ } } - bp_table::stop_on_err_warn_status (true); + bptab.stop_on_err_warn_status (true); return ovl (); } @@ -489,7 +495,7 @@ retmap.assign ("line", line); retmap.assign ("cond", cond); - octave_map ew = bp_table::stop_on_err_warn_status (false); + octave_map ew = bptab.stop_on_err_warn_status (false); if (ew.isempty ()) { retval = octave_value (retmap); @@ -532,7 +538,7 @@ @seealso{dbstack, dblist, dbstatus, dbcont, dbstep, dbup, dbdown} @end deftypefn */) { - octave_user_code *dbg_fcn = get_user_code (); + octave_user_code *dbg_fcn = octave::get_user_code (); if (! dbg_fcn) { @@ -632,7 +638,7 @@ switch (args.length ()) { case 0: // dbtype - dbg_fcn = get_user_code (); + dbg_fcn = octave::get_user_code (); if (! dbg_fcn) error ("dbtype: must be inside a user function to give no arguments to dbtype\n"); @@ -650,7 +656,7 @@ if (ind != std::string::npos) // (dbtype start:end) { - dbg_fcn = get_user_code (); + dbg_fcn = octave::get_user_code (); if (dbg_fcn) { @@ -680,7 +686,7 @@ if (line == 0) // (dbtype func) { - dbg_fcn = get_user_code (arg); + dbg_fcn = octave::get_user_code (arg); if (! dbg_fcn) error ("dbtype: function <%s> not found\n", arg.c_str ()); @@ -693,7 +699,7 @@ if (line <= 0) error ("dbtype: start and end lines must be >= 1\n"); - dbg_fcn = get_user_code (); + dbg_fcn = octave::get_user_code (); if (dbg_fcn) do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (), @@ -705,7 +711,7 @@ case 2: // (dbtype func start:end) || (dbtype func start) { - dbg_fcn = get_user_code (argv[1]); + dbg_fcn = octave::get_user_code (argv[1]); if (! dbg_fcn) error ("dbtype: function <%s> not found\n", argv[1].c_str ()); @@ -778,7 +784,7 @@ error ("dblist: N must be a non-negative integer"); } - octave_user_code *dbg_fcn = get_user_code (); + octave_user_code *dbg_fcn = octave::get_user_code (); if (! dbg_fcn) error ("dblist: must be inside a user function to use dblist\n");
--- a/libinterp/corefcn/error.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/corefcn/error.cc Mon Feb 12 00:58:31 2018 -0500 @@ -356,11 +356,12 @@ bool show_stack_trace = false) { octave::call_stack& cs = octave::__get_call_stack__ ("maybe_enter_debugger"); + octave::bp_table& bptab = octave::__get_bp_table__ ("maybe_enter_debugger"); if ((octave::application::interactive () || octave::application::forced_interactive ()) - && ((Vdebug_on_error && bp_table::debug_on_err (last_error_id ())) - || (Vdebug_on_caught && bp_table::debug_on_caught (last_error_id ()))) + && ((Vdebug_on_error && bptab.debug_on_err (last_error_id ())) + || (Vdebug_on_caught && bptab.debug_on_caught (last_error_id ()))) && cs.caller_user_code ()) { octave::unwind_protect frame; @@ -770,9 +771,12 @@ && ! discard_warning_messages) pr_where (std::cerr, "warning"); + octave::bp_table& bptab + = octave::__get_bp_table__ ("warning_1"); + if ((octave::application::interactive () || octave::application::forced_interactive ()) - && Vdebug_on_warning && in_user_code && bp_table::debug_on_warn (id)) + && Vdebug_on_warning && in_user_code && bptab.debug_on_warn (id)) { octave::unwind_protect frame; frame.protect_var (Vdebug_on_warning);
--- a/libinterp/corefcn/interpreter-private.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/corefcn/interpreter-private.cc Mon Feb 12 00:58:31 2018 -0500 @@ -26,6 +26,7 @@ #include <string> +#include "bp-table.h" #include "call-stack.h" #include "child-list.h" #include "error.h" @@ -111,6 +112,13 @@ return interp.get_evaluator (); } + bp_table& __get_bp_table__ (const std::string& who) + { + interpreter& interp = __get_interpreter__ (who); + + return interp.get_bp_table (); + } + call_stack& __get_call_stack__ (const std::string& who) { interpreter& interp = __get_interpreter__ (who);
--- a/libinterp/corefcn/interpreter-private.h Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/corefcn/interpreter-private.h Mon Feb 12 00:58:31 2018 -0500 @@ -33,6 +33,7 @@ namespace octave { + class bp_table; class call_stack; class child_list; class dynamic_loader; @@ -61,6 +62,8 @@ extern tree_evaluator& __get_evaluator__ (const std::string& who); + extern bp_table& __get_bp_table__ (const std::string& who); + extern call_stack& __get_call_stack__ (const std::string& who); extern child_list& __get_child_list__ (const std::string& who);
--- a/libinterp/corefcn/interpreter.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/corefcn/interpreter.cc Mon Feb 12 00:58:31 2018 -0500 @@ -353,6 +353,7 @@ m_type_info (), m_symbol_table (), m_evaluator (*this), + m_bp_table (), m_stream_list (*this), m_child_list (), m_url_handle_manager (),
--- a/libinterp/corefcn/interpreter.h Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/corefcn/interpreter.h Mon Feb 12 00:58:31 2018 -0500 @@ -31,6 +31,7 @@ #include "quit.h" #include "str-vec.h" +#include "bp-table.h" #include "dynamic-ld.h" #include "environment.h" #include "gtk-manager.h" @@ -178,6 +179,11 @@ symbol_scope get_current_scope (void); symbol_scope require_current_scope (const std::string& who); + bp_table& get_bp_table (void) + { + return m_bp_table; + } + call_stack& get_call_stack (void); profiler& get_profiler (void); @@ -259,6 +265,8 @@ tree_evaluator m_evaluator; + bp_table m_bp_table; + stream_list m_stream_list; child_list m_child_list;
--- a/libinterp/corefcn/symtab.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/corefcn/symtab.cc Mon Feb 12 00:58:31 2018 -0500 @@ -282,8 +282,13 @@ // If the function has been replaced then clear any // breakpoints associated with it if (clear_breakpoints) - bp_table::remove_all_breakpoints_in_file (canonical_nm, - true); + { + octave::bp_table& bptab + = octave::__get_bp_table__ ("out_of_date_check"); + + bptab.remove_all_breakpoints_in_file (canonical_nm, + true); + } } } }
--- a/libinterp/parse-tree/bp-table.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/parse-tree/bp-table.cc Mon Feb 12 00:58:31 2018 -0500 @@ -36,7 +36,6 @@ #include <iostream> #include "file-ops.h" -#include "singleton-cleanup.h" #include "bp-table.h" #include "defun-int.h" @@ -56,940 +55,919 @@ #include "sighandlers.h" #include "symtab.h" -// Initialize the singleton object -bp_table *bp_table::instance = nullptr; - -std::set<std::string> bp_table::errors_that_stop; -std::set<std::string> bp_table::caught_that_stop; -std::set<std::string> bp_table::warnings_that_stop; - -// Return a pointer to the user-defined function FNAME. If FNAME is empty, -// search backward for the first user-defined function in the -// current call stack. - -octave_user_code * -get_user_code (const std::string& fname) -{ - octave_user_code *dbg_fcn = nullptr; - - if (fname.empty ()) - { - octave::call_stack& cs = octave::__get_call_stack__ ("get_user_code"); - - dbg_fcn = cs.debug_user_code (); - } - else - { - std::string name = fname; - - if (octave::sys::file_ops::dir_sep_char () != '/' && name[0] == '@') - { - auto beg = name.begin () + 2; // never have @/method - auto end = name.end () - 1; // never have trailing '/' - std::replace (beg, end, '/', octave::sys::file_ops::dir_sep_char ()); - } - - size_t name_len = name.length (); - - if (name_len > 2 && name.substr (name_len-2) == ".m") - name = name.substr (0, name_len-2); - - octave::symbol_table& symtab = - octave::__get_symbol_table__ ("get_user_code"); - - octave_value fcn = symtab.find_function (name); - - if (fcn.is_defined () && fcn.is_user_code ()) - dbg_fcn = fcn.user_code_value (); - } - - return dbg_fcn; -} - -// Return true if there is a valid breakpoint table, false otherwise. -// If no table exists, one is created; false is only returned if this fails. -bool -bp_table::instance_ok (void) -{ - if (! instance) - { - instance = new bp_table (); - - if (instance) - singleton_cleanup_list::add (cleanup_instance); - } - - if (! instance) - error ("unable to create breakpoint table!"); - - return true; -} - -// Clear all reasons to stop, other than breakpoints -void -bp_table::dbclear_all_signals (void) -{ - Vdebug_on_error = false; - bp_table::errors_that_stop.clear (); - - Vdebug_on_caught = false; - bp_table::caught_that_stop.clear (); - - Vdebug_on_warning = false; - bp_table::warnings_that_stop.clear (); - - octave::Vdebug_on_interrupt = false; -} - -// Process the "warn", "errs", "caught" and "intr" fields for a call of -// "dbstop (p)". -void -bp_table::dbstop_process_map_args (const octave_map& mv) +namespace octave { - // process errs - // why so many levels of indirection needed? - bool fail = false; - Cell U = mv.contents ("errs"); - if (U.numel () != 1) - fail = (U.numel () > 1); - else - { - Array<octave_value> W = U.index (static_cast<octave_idx_type> (0)); - if (W.isempty () || W(0).isempty ()) - Vdebug_on_error = 1; // like "dbstop if error" with no identifier - else if (! W(0).iscell ()) - fail = true; - else - { - Cell V = W(0).cell_value (); - for (int i = 0; i < V.numel (); i++) - { - errors_that_stop.insert (V(i).string_value ()); - Vdebug_on_error = 1; - } - } - } - if (fail) - error ("dbstop: invalid 'errs' field"); + // Clear all reasons to stop, other than breakpoints. + + void bp_table::dbclear_all_signals (void) + { + Vdebug_on_error = false; + bp_table::m_errors_that_stop.clear (); + + Vdebug_on_caught = false; + bp_table::m_caught_that_stop.clear (); + + Vdebug_on_warning = false; + bp_table::m_warnings_that_stop.clear (); + + octave::Vdebug_on_interrupt = false; + } + + // Process the "warn", "errs", "caught" and "intr" fields for a call of + // "dbstop (p)". - // process caught - // why so many levels of indirection needed? - fail = false; - U = mv.contents ("caught"); - if (U.numel () != 1) - fail = (U.numel () > 1); - else - { - Array<octave_value> W = U.index (static_cast<octave_idx_type> (0)); - if (W.isempty () || W(0).isempty ()) - Vdebug_on_caught = 1; // like "dbstop if caught error" with no ID - else if (! W(0).iscell ()) - fail = true; - else - { - Cell V = W(0).cell_value (); - for (int i = 0; i < V.numel (); i++) - { - caught_that_stop.insert (V(i).string_value ()); - Vdebug_on_caught = 1; - } - } - } - if (fail) - error ("dbstop: invalid 'caught' field"); + void bp_table::dbstop_process_map_args (const octave_map& mv) + { + // process errs + // why so many levels of indirection needed? + bool fail = false; + Cell U = mv.contents ("errs"); + if (U.numel () != 1) + fail = (U.numel () > 1); + else + { + Array<octave_value> W = U.index (static_cast<octave_idx_type> (0)); + if (W.isempty () || W(0).isempty ()) + Vdebug_on_error = 1; // like "dbstop if error" with no identifier + else if (! W(0).iscell ()) + fail = true; + else + { + Cell V = W(0).cell_value (); + for (int i = 0; i < V.numel (); i++) + { + m_errors_that_stop.insert (V(i).string_value ()); + Vdebug_on_error = 1; + } + } + } + if (fail) + error ("dbstop: invalid 'errs' field"); + + // process caught + // why so many levels of indirection needed? + fail = false; + U = mv.contents ("caught"); + if (U.numel () != 1) + fail = (U.numel () > 1); + else + { + Array<octave_value> W = U.index (static_cast<octave_idx_type> (0)); + if (W.isempty () || W(0).isempty ()) + Vdebug_on_caught = 1; // like "dbstop if caught error" with no ID + else if (! W(0).iscell ()) + fail = true; + else + { + Cell V = W(0).cell_value (); + for (int i = 0; i < V.numel (); i++) + { + m_caught_that_stop.insert (V(i).string_value ()); + Vdebug_on_caught = 1; + } + } + } + if (fail) + error ("dbstop: invalid 'caught' field"); - // process warn - // why so many levels of indirection needed? - fail = false; - U = mv.contents ("warn"); - if (U.numel () != 1) - fail = (U.numel () > 1); - else - { - Array<octave_value> W = U.index (static_cast<octave_idx_type> (0)); - if (W.isempty () || W(0).isempty ()) - Vdebug_on_warning = 1; // like "dbstop if warning" with no identifier - else if (! W(0).iscell ()) - fail = true; - else - { - Cell V = W(0).cell_value (); - for (int i = 0; i < V.numel (); i++) - { - warnings_that_stop.insert (V(i).string_value ()); - Vdebug_on_warning = 1; - } - } - } - if (fail) - error ("dbstop: invalid 'warn' field"); + // process warn + // why so many levels of indirection needed? + fail = false; + U = mv.contents ("warn"); + if (U.numel () != 1) + fail = (U.numel () > 1); + else + { + Array<octave_value> W = U.index (static_cast<octave_idx_type> (0)); + if (W.isempty () || W(0).isempty ()) + Vdebug_on_warning = 1; // like "dbstop if warning" with no identifier + else if (! W(0).iscell ()) + fail = true; + else + { + Cell V = W(0).cell_value (); + for (int i = 0; i < V.numel (); i++) + { + m_warnings_that_stop.insert (V(i).string_value ()); + Vdebug_on_warning = 1; + } + } + } + if (fail) + error ("dbstop: invalid 'warn' field"); - // process interrupt - if (mv.isfield ("intr")) - octave::Vdebug_on_interrupt = 1; -} + // process interrupt + if (mv.isfield ("intr")) + octave::Vdebug_on_interrupt = 1; + } + + // Insert a breakpoint in function fcn at line within file fname, + // to stop only when condition is true. + // Record in m_bp_set that fname contains a breakpoint. + + bool bp_table::add_breakpoint_1 (octave_user_code *fcn, + const std::string& fname, + const bp_table::intmap& line, + const std::string& condition, + bp_table::intmap& retval) + { + bool found = false; + + octave::tree_statement_list *cmds = fcn->body (); + + std::string file = fcn->fcn_file_name (); + + if (cmds) + { + retval = cmds->add_breakpoint (file, line, condition); -// Insert a breakpoint in function fcn at line within file fname, -// to stop only when condition is true. -// Record in bp_set that fname contains a breakpoint. -bool -bp_table::do_add_breakpoint_1 (octave_user_code *fcn, - const std::string& fname, - const bp_table::intmap& line, - const std::string& condition, - bp_table::intmap& retval) -{ - bool found = false; + for (auto& idx_line_p : retval) + { + if (idx_line_p.second != 0) + { + // Normalize to store only the file name. + // Otherwise, there can be an entry for both + // file>subfunction and file, which causes a crash on + // dbclear all + const char *s = strchr (fname.c_str (), '>'); + if (s) + m_bp_set.insert (fname.substr (0, s - fname.c_str ())); + else + m_bp_set.insert (fname); + found = true; + break; + } + } + } - octave::tree_statement_list *cmds = fcn->body (); - - std::string file = fcn->fcn_file_name (); + return found; + } - if (cmds) - { - retval = cmds->add_breakpoint (file, line, condition); + // Cursory check that cond is a valid condition to use for a breakpoint. + // Currently allows conditions with side-effects, like 'y+=10' and 'y++'; + // it is odd that the former is not flagged by "is_assignment_expression". + // Throws an exception if not valid. - for (auto& idx_line_p : retval) - { - if (idx_line_p.second != 0) - { - // Normalize to store only the file name. - // Otherwise, there can be an entry for both file>subfunction and - // file, which causes a crash on dbclear all - const char *s = strchr (fname.c_str (), '>'); - if (s) - bp_set.insert (fname.substr (0, s - fname.c_str ())); - else - bp_set.insert (fname); - found = true; - break; - } - } - } + bool bp_table::condition_valid (const std::string& cond) + { + if (cond.length () > 0) + { + octave::parser parser (cond + " ;"); // ; to reject partial expr like "y==" + parser.reset (); + int parse_status = parser.run (); + if (parse_status) + error ("dbstop: Cannot parse condition '%s'", cond.c_str ()); + else + { + octave::tree_statement *stmt = nullptr; + if (! parser.m_stmt_list) + error ("dbstop: " + "condition is not empty, but has nothing to evaluate"); + else + { + if (parser.m_stmt_list->length () == 1 + && (stmt = parser.m_stmt_list->front ()) + && stmt->is_expression ()) + { + octave::tree_expression *expr = stmt->expression (); + if (expr->is_assignment_expression ()) + error ("dbstop: condition cannot be an assignment. " + "Did you mean '=='?"); + } + else + error ("dbstop: condition must be an expression"); + } + } + } + return true; + } - return found; -} - -// Cursory check that cond is a valid condition to use for a breakpoint. -// Currently allows conditions with side-effects, like 'y+=10' and 'y++'; -// it is odd that the former is not flagged by "is_assignment_expression". -// Throws an exception if not valid. -bool -bp_table::condition_valid (const std::string& cond) -{ - if (cond.length () > 0) + enum dbstop_args { - octave::parser parser (cond + " ;"); // ; to reject partial expr like "y==" - parser.reset (); - int parse_status = parser.run (); - if (parse_status) - error ("dbstop: Cannot parse condition '%s'", cond.c_str ()); - else - { - octave::tree_statement *stmt = nullptr; - if (! parser.m_stmt_list) - error ("dbstop: " - "condition is not empty, but has nothing to evaluate"); - else - { - if (parser.m_stmt_list->length () == 1 - && (stmt = parser.m_stmt_list->front ()) - && stmt->is_expression ()) - { - octave::tree_expression *expr = stmt->expression (); - if (expr->is_assignment_expression ()) - error ("dbstop: condition cannot be an assignment. " - "Did you mean '=='?"); - } - else - error ("dbstop: condition must be an expression"); - } - } - } - return true; -} + dbstop_in, + dbstop_at, + dbstop_if, + dbstop_none + }; + + // Parse parameters (args) of dbstop and dbclear commands. + // For dbstop, who=="dbstop"; for dbclear, who=="dbclear". + // The syntax is: dbstop [[in] symbol] [[at] line [line [...]]] [if condition] + // where the form of condition depends on whether or not a file or line has + // been seen. + // Also execute "if [error|warning|interrupt|naninf]" clauses. + + void bp_table::parse_dbfunction_params (const char *who, + const octave_value_list& args, + std::string& symbol_name, + bp_table::intmap& lines, + std::string& cond) + { + int nargin = args.length (); + int list_idx = 0; + symbol_name = ""; + lines = bp_table::intmap (); + + if (nargin == 0 || ! args(0).is_string ()) + print_usage (who); -enum -dbstop_args {dbstop_in, dbstop_at, dbstop_if, dbstop_none}; - -// Parse parameters (args) of dbstop and dbclear commands. -// For dbstop, who=="dbstop"; for dbclear, who=="dbclear". -// The syntax is: dbstop [[in] symbol] [[at] line [line [...]]] [if condition] -// where the form of condition depends on whether or not a file or line has -// been seen. -// Also execute "if [error|warning|interrupt|naninf]" clauses. -void -bp_table::parse_dbfunction_params (const char *who, - const octave_value_list& args, - std::string& symbol_name, - bp_table::intmap& lines, - std::string& cond) -{ - int nargin = args.length (); - int list_idx = 0; - symbol_name = ""; - lines = bp_table::intmap (); - - if (nargin == 0 || ! args(0).is_string ()) - print_usage (who); + // elements already processed + bool seen_in = false, seen_at = false, seen_if = false; + int pos = 0; + dbstop_args tok = dbstop_none; + while (pos < nargin) + { + // allow "in" and "at" to be implicit + if (args(pos).is_string ()) + { + std::string arg = args(pos).string_value (); + if (arg == "in") + { + tok = dbstop_in; + pos++; + } + else if (arg == "at") + { + tok = dbstop_at; + pos++; + } + else if (arg == "if") + { + tok = dbstop_if; + pos++; + } + else if (atoi (args(pos).string_value ().c_str ()) > 0) + tok = dbstop_at; + else + tok = dbstop_in; + } + else + tok = dbstop_at; - // elements already processed - bool seen_in = false, seen_at = false, seen_if = false; - int pos = 0; - dbstop_args tok = dbstop_none; - while (pos < nargin) - { - // allow "in" and "at" to be implicit - if (args(pos).is_string ()) - { - std::string arg = args(pos).string_value (); - if (arg == "in") - { - tok = dbstop_in; - pos++; - } - else if (arg == "at") - { - tok = dbstop_at; - pos++; - } - else if (arg == "if") - { - tok = dbstop_if; - pos++; - } - else if (atoi (args(pos).string_value ().c_str ()) > 0) - tok = dbstop_at; - else - tok = dbstop_in; - } - else - tok = dbstop_at; + if (pos >= nargin) + error ("%s: '%s' missing argument", who, + (tok == dbstop_in + ? "in" : (tok == dbstop_at ? "at" : "if"))); + + // process the actual arguments + switch (tok) + { + case dbstop_in: + symbol_name = args(pos).string_value (); + if (seen_in) + error ("%s: Too many function names specified -- %s", + who, symbol_name.c_str ()); + else if (seen_at || seen_if) + error ("%s: function name must come before line number and 'if'", + who); + seen_in = true; + pos++; + break; + + case dbstop_at: + if (seen_at) + error ("%s: Only one 'at' clause is allowed -- %s", + who, args(pos).string_value ().c_str ()); + else if (seen_if) + error ("%s: line number must come before 'if' clause\n"); + seen_at = true; - if (pos >= nargin) - error ("%s: '%s' missing argument", who, - (tok == dbstop_in - ? "in" : (tok == dbstop_at ? "at" : "if"))); + if (! seen_in) + { + // It was a line number. Get function name from debugger. + if (Vdebugging) + symbol_name = get_user_code ()->profiler_name (); + else + error ("%s: function name must come before line number " + "and 'if'", who); + seen_in = true; + } + else if (seen_if) + error ("%s: line number must come before 'if' clause\n"); - // process the actual arguments - switch (tok) - { - case dbstop_in: - symbol_name = args(pos).string_value (); - if (seen_in) - error ("%s: Too many function names specified -- %s", - who, symbol_name.c_str ()); - else if (seen_at || seen_if) - error ("%s: function name must come before line number and 'if'", - who); - seen_in = true; - pos++; - break; + // Read a list of line numbers (or arrays thereof) + for ( ; pos < nargin; pos++) + { + if (args(pos).is_string ()) + { + int line = atoi (args(pos).string_value ().c_str ()); - case dbstop_at: - if (seen_at) - error ("%s: Only one 'at' clause is allowed -- %s", - who, args(pos).string_value ().c_str ()); - else if (seen_if) - error ("%s: line number must come before 'if' clause\n"); - seen_at = true; + if (line > 0) + lines[list_idx++] = line; + else + break; // may be "if" + } + else if (args(pos).isnumeric ()) + { + const NDArray arg = args(pos).array_value (); + + for (octave_idx_type j = 0; j < arg.numel (); j++) + lines[list_idx++] = static_cast<int> (arg.elem (j)); + } + else + error ("%s: Invalid argument type %s", + args(pos).type_name ().c_str ()); + } + break; - if (! seen_in) - { - // It was a line number. Get function name from debugger. - if (Vdebugging) - symbol_name = get_user_code ()->profiler_name (); - else - error ("%s: function name must come before line number " - "and 'if'", who); - seen_in = true; - } - else if (seen_if) - error ("%s: line number must come before 'if' clause\n"); + case dbstop_if: + if (seen_in) // conditional breakpoint + { + cond = ""; // remaining arguments form condition + for (; pos < nargin; pos++) + { + if (args(pos).is_string ()) + cond += ' ' + args(pos).string_value (); + else + error ("%s: arguments to 'if' must all be strings", who); + } - // Read a list of line numbers (or arrays thereof) - for ( ; pos < nargin; pos++) - { - if (args(pos).is_string ()) - { - int line = atoi (args(pos).string_value ().c_str ()); + cond = cond.substr (1); // omit initial space + } + else // stop on event (error, warning, interrupt, NaN/inf) + { + std::string condition = args(pos).string_value (); + int on_off = ! strcmp(who, "dbstop"); - if (line > 0) - lines[list_idx++] = line; - else - break; // may be "if" - } - else if (args(pos).isnumeric ()) - { - const NDArray arg = args(pos).array_value (); + // list of error/warning IDs to update + std::set<std::string> *id_list = nullptr; + bool *stop_flag = nullptr; // Vdebug_on_... flag - for (octave_idx_type j = 0; j < arg.numel (); j++) - lines[list_idx++] = static_cast<int> (arg.elem (j)); - } - else - error ("%s: Invalid argument type %s", - args(pos).type_name ().c_str ()); - } - break; - - case dbstop_if: - if (seen_in) // conditional breakpoint - { - cond = ""; // remaining arguments form condition - for (; pos < nargin; pos++) - { - if (args(pos).is_string ()) - cond += ' ' + args(pos).string_value (); - else - error ("%s: arguments to 'if' must all be strings", who); - } - - cond = cond.substr (1); // omit initial space - } - else // stop on event (error, warning, interrupt, NaN/inf) - { - std::string condition = args(pos).string_value (); - int on_off = ! strcmp(who, "dbstop"); - - // list of error/warning IDs to update - std::set<std::string> *id_list = nullptr; - bool *stop_flag = nullptr; // Vdebug_on_... flag + if (condition == "error") + { + id_list = &m_errors_that_stop; + stop_flag = &Vdebug_on_error; + } + else if (condition == "warning") + { + id_list = &m_warnings_that_stop; + stop_flag = &Vdebug_on_warning; + } + else if (condition == "caught" && nargin > pos+1 + && args(pos+1).string_value () == "error") + { + id_list = &m_caught_that_stop; + stop_flag = &Vdebug_on_caught; + pos++; + } + else if (condition == "interrupt") + { + octave::Vdebug_on_interrupt = on_off; + } + else if (condition == "naninf") + { +#if defined (DBSTOP_NANINF) + Vdebug_on_naninf = on_off; + enable_fpe (on_off); +#else + warning ("%s: condition '%s' not yet supported", + who, condition.c_str ()); +#endif + } + else + error ("%s: invalid condition %s", + who, condition.c_str ()); - if (condition == "error") - { - id_list = &bp_table::errors_that_stop; - stop_flag = &Vdebug_on_error; - } - else if (condition == "warning") - { - id_list = &bp_table::warnings_that_stop; - stop_flag = &Vdebug_on_warning; - } - else if (condition == "caught" && nargin > pos+1 - && args(pos+1).string_value () == "error") - { - id_list = &bp_table::caught_that_stop; - stop_flag = &Vdebug_on_caught; - pos++; - } - else if (condition == "interrupt") - { - octave::Vdebug_on_interrupt = on_off; - } - else if (condition == "naninf") - { -#if defined (DBSTOP_NANINF) - Vdebug_on_naninf = on_off; - enable_fpe (on_off); -#else - warning ("%s: condition '%s' not yet supported", - who, condition.c_str ()); -#endif - } - else - error ("%s: invalid condition %s", - who, condition.c_str ()); + // process ID list for "dbstop if error <error_ID>" etc + if (id_list) + { + pos++; + if (pos < nargin) // only affect a single error ID + { + if (! args(pos).is_string () || nargin > pos+1) + error ("%s: ID must be a single string", who); + else if (on_off == 1) + { + id_list->insert (args(pos).string_value ()); + *stop_flag = true; + } + else + { + id_list->erase (args(pos).string_value ()); + if (id_list->empty ()) + *stop_flag = false; + } + } + else // unqualified. Turn all on or off + { + id_list->clear (); + *stop_flag = on_off; + if (stop_flag == &Vdebug_on_error) + { + // Matlab stops on both. + octave::Vdebug_on_interrupt = on_off; + } + } + } + + pos = nargin; + } + break; - // process ID list for "dbstop if error <error_ID>" etc - if (id_list != nullptr) - { - pos++; - if (pos < nargin) // only affect a single error ID - { - if (! args(pos).is_string () || nargin > pos+1) - error ("%s: ID must be a single string", who); - else if (on_off == 1) - { - id_list->insert (args(pos).string_value ()); - *stop_flag = true; - } - else - { - id_list->erase (args(pos).string_value ()); - if (id_list->empty ()) - *stop_flag = false; - } - } - else // unqualified. Turn all on or off - { - id_list->clear (); - *stop_flag = on_off; - if (stop_flag == &Vdebug_on_error) - { - // Matlab stops on both. - octave::Vdebug_on_interrupt = on_off; - } - } - } + default: // dbstop_none should never occur + break; + } + } + } + + /* + %!test + %! dbclear all; # Clear out breakpoints before test + %! dbstop help; + %! dbstop in ls; + %! dbstop help at 100; + %! dbstop in ls 100; + %! dbstop help 201 if a==5; + %! dbstop if error Octave:undefined-function; + %! s = dbstatus; + %! dbclear all; + %! assert ({s.bkpt(:).name}, {"help", "help", "help>do_contents", "ls", "ls"}); + %! assert ([s.bkpt(:).line], [48, 100, 201, 58, 100]); + %! assert (s.errs, {"Octave:undefined-function"}); + */ + + // Return the sub/nested/main function of MAIN_FCN that contains + // line number LINENO of the source file. + // If END_LINE != 0, *END_LINE is set to last line of the returned function. + + static octave_user_code * find_fcn_by_line (octave_user_code *main_fcn, + int lineno, int *end_line = nullptr) + { + octave_user_code *retval = nullptr; + octave_user_code *next_fcn = nullptr; // 1st function starting after lineno + + // Find innermost nested (or parent) function containing lineno. + int earliest_end = std::numeric_limits<int>::max (); + + std::map<std::string, octave_value> subfcns = main_fcn->subfunctions (); + for (const auto& str_val_p : subfcns) + { + if (str_val_p.second.is_user_function ()) + { + auto *dbg_subfcn = str_val_p.second.user_function_value (); - pos = nargin; - } - break; + // Check if lineno is within dbg_subfcn. + // FIXME: we could break when beginning_line() > lineno, + // but that makes the code "fragile" + // if the order of walking subfcns changes, + // for a minor speed improvement in non-critical code. + if (dbg_subfcn->ending_line () < earliest_end + && dbg_subfcn->ending_line () >= lineno + && dbg_subfcn->beginning_line () <= lineno) + { + earliest_end = dbg_subfcn->ending_line (); + retval = find_fcn_by_line (dbg_subfcn, lineno, &earliest_end); + } - default: // dbstop_none should never occur - break; - } - } -} + // Find the first fcn starting after lineno. + // This is used if line is not inside any function. + if (dbg_subfcn->beginning_line () >= lineno && ! next_fcn) + next_fcn = dbg_subfcn; + } + } + + // The breakpoint is either in the subfunction found above, + // or in the main function, which we check now. + if (main_fcn->is_user_function ()) + { + int e = dynamic_cast<octave_user_function *> (main_fcn)->ending_line (); + if (e >= lineno && e < earliest_end) + retval = main_fcn; -/* -%!test -%! dbclear all; # Clear out breakpoints before test -%! dbstop help; -%! dbstop in ls; -%! dbstop help at 100; -%! dbstop in ls 100; -%! dbstop help 201 if a==5; -%! dbstop if error Octave:undefined-function; -%! s = dbstatus; -%! dbclear all; -%! assert ({s.bkpt(:).name}, {"help", "help", "help>do_contents", "ls", "ls"}); -%! assert ([s.bkpt(:).line], [48, 100, 201, 58, 100]); -%! assert (s.errs, {"Octave:undefined-function"}); -*/ + if (! retval) + retval = next_fcn; + } + else // main_fcn is a script. + { + if (! retval) + retval = main_fcn; + } + + if (end_line && earliest_end < *end_line) + *end_line = earliest_end; + + return retval; + } -// Return the sub/nested/main function of MAIN_FCN that contains -// line number LINENO of the source file. -// If END_LINE != 0, *END_LINE is set to last line of the returned function. -static octave_user_code* -find_fcn_by_line (octave_user_code *main_fcn, int lineno, int *end_line = nullptr) -{ - octave_user_code *retval = nullptr; - octave_user_code *next_fcn = nullptr; // 1st function starting after lineno + // Given file name fname, find the subfunction at line and create + // a breakpoint there. Put the system into debug_mode. + bp_table::intmap bp_table::add_breakpoint (const std::string& fname, + const bp_table::intmap& line, + const std::string& condition) + { + octave_user_code *main_fcn = get_user_code (fname); - // Find innermost nested (or parent) function containing lineno. - int earliest_end = std::numeric_limits<int>::max (); + if (! main_fcn) + error ("add_breakpoint: unable to find function '%s'\n", fname.c_str ()); - std::map<std::string, octave_value> subfcns = main_fcn->subfunctions (); - for (const auto& str_val_p : subfcns) - { - if (str_val_p.second.is_user_function ()) - { - auto *dbg_subfcn = str_val_p.second.user_function_value (); + condition_valid (condition); // Throw error if condition not valid. + + intmap retval; - // Check if lineno is within dbg_subfcn. - // FIXME: we could break when beginning_line() > lineno, - // but that makes the code "fragile" - // if the order of walking subfcns changes, - // for a minor speed improvement in non-critical code. - if (dbg_subfcn->ending_line () < earliest_end - && dbg_subfcn->ending_line () >= lineno - && dbg_subfcn->beginning_line () <= lineno) - { - earliest_end = dbg_subfcn->ending_line (); - retval = find_fcn_by_line (dbg_subfcn, lineno, &earliest_end); - } + octave_idx_type len = line.size (); + + for (int i = 0; i < len; i++) + { + const_intmap_iterator m = line.find (i); + + if (m != line.end ()) + { + int lineno = m->second; + + octave_user_code *dbg_fcn = find_fcn_by_line (main_fcn, lineno); - // Find the first fcn starting after lineno. - // This is used if line is not inside any function. - if (dbg_subfcn->beginning_line () >= lineno && ! next_fcn) - next_fcn = dbg_subfcn; - } - } + // We've found the right (sub)function. Now insert the breakpoint. + // We insert all breakpoints. + // If multiple are in the same function, we insert multiple times. + intmap ret_one; + if (dbg_fcn + && add_breakpoint_1 (dbg_fcn, fname, line, condition, ret_one)) + retval.insert (std::pair<int,int> (i, ret_one.find (i)->second)); + } + } - // The breakpoint is either in the subfunction found above, - // or in the main function, which we check now. - if (main_fcn->is_user_function ()) - { - int e = dynamic_cast<octave_user_function *> (main_fcn)->ending_line (); - if (e >= lineno && e < earliest_end) - retval = main_fcn; + octave::tree_evaluator::debug_mode = bp_table::have_breakpoints () + || Vdebugging; + + return retval; + } - if (! retval) - retval = next_fcn; - } - else // main_fcn is a script. - { - if (! retval) - retval = main_fcn; - } + int bp_table::remove_breakpoint_1 (octave_user_code *fcn, + const std::string& fname, + const bp_table::intmap& line) + { + int retval = 0; - if (end_line != nullptr && earliest_end < *end_line) - *end_line = earliest_end; + std::string file = fcn->fcn_file_name (); - return retval; -} + octave::tree_statement_list *cmds = fcn->body (); + + // FIXME: move the operation on cmds to the tree_statement_list class? -// Given file name fname, find the subfunction at line and create -// a breakpoint there. Put the system into debug_mode. -bp_table::intmap -bp_table::do_add_breakpoint (const std::string& fname, - const bp_table::intmap& line, - const std::string& condition) -{ - octave_user_code *main_fcn = get_user_code (fname); + if (cmds) + { + octave_value_list results = cmds->list_breakpoints (); - if (! main_fcn) - error ("add_breakpoint: unable to find function '%s'\n", fname.c_str ()); + if (results.length () > 0) + { + octave_idx_type len = line.size (); - condition_valid (condition); // Throw error if condition not valid. - - intmap retval; - - octave_idx_type len = line.size (); + for (int i = 0; i < len; i++) + { + const_intmap_iterator p = line.find (i); - for (int i = 0; i < len; i++) - { - const_intmap_iterator m = line.find (i); + if (p != line.end ()) + { + int lineno = p->second; - if (m != line.end ()) - { - int lineno = m->second; + cmds->delete_breakpoint (lineno); - octave_user_code *dbg_fcn = find_fcn_by_line (main_fcn, lineno); + if (! file.empty ()) + octave_link::update_breakpoint (false, file, lineno); + } + } - // We've found the right (sub)function. Now insert the breakpoint. - // We insert all breakpoints. - // If multiple are in the same function, we insert multiple times. - intmap ret_one; - if (dbg_fcn - && do_add_breakpoint_1 (dbg_fcn, fname, line, condition, ret_one)) - retval.insert (std::pair<int,int> (i, ret_one.find (i)->second)); - } - } + results = cmds->list_breakpoints (); + + bp_set_iterator it = m_bp_set.find (fname); + if (results.empty () && it != m_bp_set.end ()) + m_bp_set.erase (it); + } - octave::tree_evaluator::debug_mode = bp_table::have_breakpoints () - || Vdebugging; + retval = results.length (); + } - return retval; -} + return retval; + } -int -bp_table::do_remove_breakpoint_1 (octave_user_code *fcn, - const std::string& fname, - const bp_table::intmap& line) -{ - int retval = 0; + int bp_table::remove_breakpoint (const std::string& fname, + const bp_table::intmap& line) + { + int retval = 0; - std::string file = fcn->fcn_file_name (); + octave_idx_type len = line.size (); - octave::tree_statement_list *cmds = fcn->body (); - - // FIXME: move the operation on cmds to the tree_statement_list class? + if (len == 0) + { + intmap results = remove_all_breakpoints_in_file (fname); + retval = results.size (); + } + else + { + octave_user_code *dbg_fcn = get_user_code (fname); - if (cmds) - { - octave_value_list results = cmds->list_breakpoints (); + if (! dbg_fcn) + error ("remove_breakpoint: unable to find function %s\n", + fname.c_str ()); - if (results.length () > 0) - { - octave_idx_type len = line.size (); + retval = remove_breakpoint_1 (dbg_fcn, fname, line); - for (int i = 0; i < len; i++) - { - const_intmap_iterator p = line.find (i); + // Search subfunctions in the order they appear in the file. - if (p != line.end ()) - { - int lineno = p->second; + const std::list<std::string> subfcn_names + = dbg_fcn->subfunction_names (); + + std::map<std::string, octave_value> subfcns + = dbg_fcn->subfunctions (); - cmds->delete_breakpoint (lineno); + for (const auto& subf_nm : subfcn_names) + { + const auto q = subfcns.find (subf_nm); - if (! file.empty ()) - octave_link::update_breakpoint (false, file, lineno); - } - } - - results = cmds->list_breakpoints (); + if (q != subfcns.end ()) + { + octave_user_code *dbg_subfcn = q->second.user_code_value (); - bp_set_iterator it = bp_set.find (fname); - if (results.empty () && it != bp_set.end ()) - bp_set.erase (it); - } + retval += remove_breakpoint_1 (dbg_subfcn, fname, line); + } + } + } - retval = results.length (); - } + octave::tree_evaluator::debug_mode = bp_table::have_breakpoints () + || Vdebugging; - return retval; -} + return retval; + } + + // Remove all breakpoints from a file, including those in subfunctions. -int -bp_table::do_remove_breakpoint (const std::string& fname, - const bp_table::intmap& line) -{ - int retval = 0; - - octave_idx_type len = line.size (); + bp_table::intmap + bp_table::remove_all_breakpoints_in_file (const std::string& fname, + bool silent) + { + intmap retval; - if (len == 0) - { - intmap results = remove_all_breakpoints_in_file (fname); - retval = results.size (); - } - else - { - octave_user_code *dbg_fcn = get_user_code (fname); + octave_user_code *dbg_fcn = get_user_code (fname); + + if (dbg_fcn) + { + std::string file = dbg_fcn->fcn_file_name (); + + octave::tree_statement_list *cmds = dbg_fcn->body (); - if (! dbg_fcn) - error ("remove_breakpoint: unable to find function %s\n", - fname.c_str ()); - - retval = do_remove_breakpoint_1 (dbg_fcn, fname, line); + if (cmds) + { + retval = cmds->remove_all_breakpoints (file); - // Search subfunctions in the order they appear in the file. + bp_set_iterator it = m_bp_set.find (fname); + if (it != m_bp_set.end ()) + m_bp_set.erase (it); + } + } + else if (! silent) + error ("remove_all_breakpoint_in_file: " + "unable to find function %s\n", fname.c_str ()); - const std::list<std::string> subfcn_names - = dbg_fcn->subfunction_names (); - - std::map<std::string, octave_value> subfcns - = dbg_fcn->subfunctions (); + octave::tree_evaluator::debug_mode = bp_table::have_breakpoints () + || Vdebugging; - for (const auto& subf_nm : subfcn_names) - { - const auto q = subfcns.find (subf_nm); - - if (q != subfcns.end ()) - { - octave_user_code *dbg_subfcn = q->second.user_code_value (); + return retval; + } - retval += do_remove_breakpoint_1 (dbg_subfcn, fname, line); - } - } - } + void bp_table::remove_all_breakpoints (void) + { + // Odd loop structure required because delete will invalidate m_bp_set iterators + for (const_bp_set_iterator it = m_bp_set.begin (), it_next = it; + it != m_bp_set.end (); + it = it_next) + { + ++it_next; + remove_all_breakpoints_in_file (*it); + } - octave::tree_evaluator::debug_mode = bp_table::have_breakpoints () - || Vdebugging; - - return retval; -} + octave::tree_evaluator::debug_mode = bp_table::have_breakpoints () + || Vdebugging; + } -// Remove all breakpoints from a file, including those in subfunctions -bp_table::intmap -bp_table::do_remove_all_breakpoints_in_file (const std::string& fname, - bool silent) -{ - intmap retval; - - octave_user_code *dbg_fcn = get_user_code (fname); + std::string find_bkpt_list (octave_value_list slist, std::string match) + { + std::string retval; - if (dbg_fcn) - { - std::string file = dbg_fcn->fcn_file_name (); + for (int i = 0; i < slist.length (); i++) + { + if (slist(i).string_value () == match) + { + retval = slist(i).string_value (); + break; + } + } - octave::tree_statement_list *cmds = dbg_fcn->body (); + return retval; + } - if (cmds) - { - retval = cmds->remove_all_breakpoints (file); + bp_table::fname_bp_map + bp_table::get_breakpoint_list (const octave_value_list& fname_list) + { + fname_bp_map retval; + + // make copy since changes may invalidate iters of m_bp_set. + std::set<std::string> tmp_bp_set = m_bp_set; - bp_set_iterator it = bp_set.find (fname); - if (it != bp_set.end ()) - bp_set.erase (it); - } - } - else if (! silent) - error ("remove_all_breakpoint_in_file: " - "unable to find function %s\n", fname.c_str ()); + for (auto& bp_fname : tmp_bp_set) + { + if (fname_list.empty () + || find_bkpt_list (fname_list, bp_fname) != "") + { + octave_user_code *dbg_fcn = get_user_code (bp_fname); - octave::tree_evaluator::debug_mode = bp_table::have_breakpoints () - || Vdebugging; - - return retval; -} + if (dbg_fcn) + { + octave::tree_statement_list *cmds = dbg_fcn->body (); -void -bp_table::do_remove_all_breakpoints (void) -{ - // Odd loop structure required because delete will invalidate bp_set iterators - for (const_bp_set_iterator it=bp_set.begin (), it_next=it; - it != bp_set.end (); - it=it_next) - { - ++it_next; - remove_all_breakpoints_in_file (*it); - } + // FIXME: move the operation on cmds to the + // tree_statement_list class? + if (cmds) + { + std::list<bp_type> bkpts = cmds->breakpoints_and_conds (); - octave::tree_evaluator::debug_mode = bp_table::have_breakpoints () - || Vdebugging; -} + if (! bkpts.empty ()) + retval[bp_fname] = bkpts; + } -std::string -do_find_bkpt_list (octave_value_list slist, std::string match) -{ - std::string retval; + // look for breakpoints in subfunctions + const std::list<std::string> subf_nm + = dbg_fcn->subfunction_names (); + + std::map<std::string, octave_value> subfcns + = dbg_fcn->subfunctions (); - for (int i = 0; i < slist.length (); i++) - { - if (slist(i).string_value () == match) - { - retval = slist(i).string_value (); - break; - } - } + for (const auto& subfcn_nm : subf_nm) + { + const auto q = subfcns.find (subfcn_nm); - return retval; -} + if (q != subfcns.end ()) + { + octave_user_code *dbg_subfcn + = q->second.user_code_value (); -bp_table::fname_bp_map -bp_table::do_get_breakpoint_list (const octave_value_list& fname_list) -{ - fname_bp_map retval; - - // make copy since changes may invalidate iters of bp_set. - std::set<std::string> tmp_bp_set = bp_set; + cmds = dbg_subfcn->body (); + if (cmds) + { + std::list<bp_type> bkpts + = cmds->breakpoints_and_conds (); - for (auto& bp_fname : tmp_bp_set) - { - if (fname_list.empty () - || do_find_bkpt_list (fname_list, bp_fname) != "") - { - octave_user_code *dbg_fcn = get_user_code (bp_fname); + if (! bkpts.empty ()) + { + std::string key + = bp_fname + '>' + dbg_subfcn->name (); - if (dbg_fcn) - { - octave::tree_statement_list *cmds = dbg_fcn->body (); + retval[key] = bkpts; + } + } + } + } + } + } + } - // FIXME: move the operation on cmds to the - // tree_statement_list class? - if (cmds) - { - std::list<bp_type> bkpts = cmds->breakpoints_and_conds (); + return retval; + } - if (! bkpts.empty ()) - retval[bp_fname] = bkpts; - } + // Report the status of "dbstop if error ..." and "dbstop if warning ..." + // If to_screen is true, the output goes to octave_stdout; otherwise it is + // returned. + // If dbstop if error is true but no explicit IDs are specified, the return + // value will have an empty field called "errs". If IDs are specified, the + // "errs" field will have a row per ID. If dbstop if error is false, there + // is no "errs" field. The "warn" field is set similarly by dbstop if warning - // look for breakpoints in subfunctions - const std::list<std::string> subf_nm - = dbg_fcn->subfunction_names (); + octave_map bp_table::stop_on_err_warn_status (bool to_screen) + { + octave_map retval; - std::map<std::string, octave_value> subfcns - = dbg_fcn->subfunctions (); - - for (const auto& subfcn_nm : subf_nm) - { - const auto q = subfcns.find (subfcn_nm); - - if (q != subfcns.end ()) - { - octave_user_code *dbg_subfcn - = q->second.user_code_value (); - - cmds = dbg_subfcn->body (); - if (cmds) - { - std::list<bp_type> bkpts - = cmds->breakpoints_and_conds (); + // print dbstop if error information + if (Vdebug_on_error) + { + if (m_errors_that_stop.empty ()) + { + if (to_screen) + octave_stdout << "stop if error\n"; + else + retval.assign ("errs", octave_value("")); + } + else + { + Cell errs (dim_vector (bp_table::m_errors_that_stop.size (), 1)); + int i = 0; - if (! bkpts.empty ()) - { - std::string key - = bp_fname + '>' + dbg_subfcn->name (); + for (const auto& e : m_errors_that_stop) + { + if (to_screen) + octave_stdout << "stop if error " << e << "\n"; + else + errs(i++) = e; + } + if (! to_screen) + retval.assign ("errs", octave_value (errs)); + } + } - retval[key] = bkpts; - } - } - } - } - } - } - } - - return retval; -} + // print dbstop if caught error information + if (Vdebug_on_caught) + { + if (m_caught_that_stop.empty ()) + { + if (to_screen) + octave_stdout << "stop if caught error\n"; + else + retval.assign ("caught", octave_value("")); + } + else + { + Cell errs (dim_vector (m_caught_that_stop.size (), 1)); + int i = 0; -// Report the status of "dbstop if error ..." and "dbstop if warning ..." -// If to_screen is true, the output goes to octave_stdout; otherwise it is -// returned. -// If dbstop if error is true but no explicit IDs are specified, the return -// value will have an empty field called "errs". If IDs are specified, the -// "errs" field will have a row per ID. If dbstop if error is false, there -// is no "errs" field. The "warn" field is set similarly by dbstop if warning -octave_map -bp_table::stop_on_err_warn_status (bool to_screen) -{ - octave_map retval; + for (const auto& e : m_caught_that_stop) + { + if (to_screen) + octave_stdout << "stop if caught error " << e << "\n"; + else + errs(i++) = e; + } + if (! to_screen) + retval.assign ("caught", octave_value (errs)); + } + } - // print dbstop if error information - if (Vdebug_on_error) - { - if (errors_that_stop.empty ()) - { - if (to_screen) - octave_stdout << "stop if error\n"; - else - retval.assign ("errs", octave_value("")); - } - else - { - Cell errs (dim_vector (bp_table::errors_that_stop.size (), 1)); - int i = 0; - - for (const auto& e : errors_that_stop) - { - if (to_screen) - octave_stdout << "stop if error " << e << "\n"; - else - errs(i++) = e; - } - if (! to_screen) - retval.assign ("errs", octave_value (errs)); - } - } + // print dbstop if warning information + if (Vdebug_on_warning) + { + if (m_warnings_that_stop.empty ()) + { + if (to_screen) + octave_stdout << "stop if warning\n"; + else + retval.assign ("warn", octave_value("")); + } + else + { + Cell warn (dim_vector (m_warnings_that_stop.size (), 1)); + int i = 0; - // print dbstop if caught error information - if (Vdebug_on_caught) - { - if (caught_that_stop.empty ()) - { - if (to_screen) - octave_stdout << "stop if caught error\n"; - else - retval.assign ("caught", octave_value("")); - } - else - { - Cell errs (dim_vector (caught_that_stop.size (), 1)); - int i = 0; + for (const auto& w : m_warnings_that_stop) + { + if (to_screen) + octave_stdout << "stop if warning " << w << "\n"; + else + warn(i++) = w; + } + if (! to_screen) + retval.assign ("warn", octave_value (warn)); + } + } - for (const auto& e : caught_that_stop) - { - if (to_screen) - octave_stdout << "stop if caught error " << e << "\n"; - else - errs(i++) = e; - } - if (! to_screen) - retval.assign ("caught", octave_value (errs)); - } - } + // print dbstop if interrupt information + if (octave::Vdebug_on_interrupt) + { + if (to_screen) + octave_stdout << "stop if interrupt\n"; + else + retval.assign ("intr", octave_value ()); + } + + return retval; + } + + // Return a pointer to the user-defined function FNAME. If FNAME is empty, + // search backward for the first user-defined function in the + // current call stack. + + octave_user_code * + get_user_code (const std::string& fname) + { + octave_user_code *dbg_fcn = nullptr; - // print dbstop if warning information - if (Vdebug_on_warning) - { - if (warnings_that_stop.empty ()) - { - if (to_screen) - octave_stdout << "stop if warning\n"; - else - retval.assign ("warn", octave_value("")); - } - else - { - Cell warn (dim_vector (warnings_that_stop.size (), 1)); - int i = 0; + if (fname.empty ()) + { + octave::call_stack& cs = octave::__get_call_stack__ ("get_user_code"); + + dbg_fcn = cs.debug_user_code (); + } + else + { + std::string name = fname; + + if (octave::sys::file_ops::dir_sep_char () != '/' && name[0] == '@') + { + auto beg = name.begin () + 2; // never have @/method + auto end = name.end () - 1; // never have trailing '/' + std::replace (beg, end, '/', octave::sys::file_ops::dir_sep_char ()); + } - for (const auto& w : warnings_that_stop) - { - if (to_screen) - octave_stdout << "stop if warning " << w << "\n"; - else - warn(i++) = w; - } - if (! to_screen) - retval.assign ("warn", octave_value (warn)); - } - } + size_t name_len = name.length (); + + if (name_len > 2 && name.substr (name_len-2) == ".m") + name = name.substr (0, name_len-2); + + octave::symbol_table& symtab = + octave::__get_symbol_table__ ("get_user_code"); - // print dbstop if interrupt information - if (octave::Vdebug_on_interrupt) - { - if (to_screen) - octave_stdout << "stop if interrupt\n"; - else - retval.assign ("intr", octave_value ()); - } + octave_value fcn = symtab.find_function (name); - return retval; + if (fcn.is_defined () && fcn.is_user_code ()) + dbg_fcn = fcn.user_code_value (); + } + + return dbg_fcn; + } } -
--- a/libinterp/parse-tree/bp-table.h Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/parse-tree/bp-table.h Mon Feb 12 00:58:31 2018 -0500 @@ -34,170 +34,122 @@ class octave_user_code; class octave_value_list; -struct -bp_type +namespace octave { - int line; - std::string cond; - - bp_type (int l, const std::string& c) : line (l), cond (c) { } -}; + struct bp_type + { + int line; + std::string cond; -// Interface to breakpoints. -class -OCTINTERP_API -bp_table -{ -private: - - bp_table (void) : bp_set () { } + bp_type (int l, const std::string& c) : line (l), cond (c) { } + }; - ~bp_table (void) = default; - -public: - - // mapping from (FIXME: arbitrary index??) to line number of breakpoint - typedef std::map<int, int> intmap; - - typedef intmap::const_iterator const_intmap_iterator; - typedef intmap::iterator intmap_iterator; + // Interface to breakpoints. + class OCTINTERP_API bp_table + { + public: - typedef std::map <std::string, intmap> fname_line_map; - - typedef fname_line_map::const_iterator const_fname_line_map_iterator; - typedef fname_line_map::iterator fname_line_map_iterator; + bp_table (void) + : m_bp_set (), m_errors_that_stop (), m_caught_that_stop (), + m_warnings_that_stop () + { } - typedef std::map <std::string, std::list<bp_type>> fname_bp_map; - typedef fname_bp_map::const_iterator const_fname_bp_map_iterator; - typedef fname_bp_map::iterator fname_bp_map_iterator; + ~bp_table (void) = default; - static bool instance_ok (void); + // mapping from (FIXME: arbitrary index??) to line number of breakpoint + typedef std::map<int, int> intmap; + + typedef intmap::const_iterator const_intmap_iterator; + typedef intmap::iterator intmap_iterator; - // Add a breakpoint at the nearest executable line. - static intmap add_breakpoint (const std::string& fname = "", - const intmap& lines = intmap (), - const std::string& condition = "") - { - return instance_ok () - ? instance->do_add_breakpoint (fname, lines, condition) : intmap (); - } + typedef std::map <std::string, intmap> fname_line_map; + + typedef fname_line_map::const_iterator const_fname_line_map_iterator; + typedef fname_line_map::iterator fname_line_map_iterator; - // Remove a breakpoint from a line in file. - static int remove_breakpoint (const std::string& fname = "", - const intmap& lines = intmap ()) - { - return instance_ok () - ? instance->do_remove_breakpoint (fname, lines) : 0; - } + typedef std::map <std::string, std::list<bp_type>> fname_bp_map; + typedef fname_bp_map::const_iterator const_fname_bp_map_iterator; + typedef fname_bp_map::iterator fname_bp_map_iterator; + + // Add a breakpoint at the nearest executable line. + intmap add_breakpoint (const std::string& fname = "", + const intmap& lines = intmap (), + const std::string& condition = ""); - // Remove all the breakpoints in a specified file. - static intmap remove_all_breakpoints_in_file (const std::string& fname, - bool silent = false) - { - return instance_ok () - ? instance->do_remove_all_breakpoints_in_file (fname, silent) - : intmap (); - } + // Remove a breakpoint from a line in file. + int remove_breakpoint (const std::string& fname = "", + const intmap& lines = intmap ()); + + // Remove all the breakpoints in a specified file. + intmap remove_all_breakpoints_in_file (const std::string& fname, + bool silent = false); - // Remove all the breakpoints registered with octave. - static void remove_all_breakpoints (void) - { - if (instance_ok ()) - instance->do_remove_all_breakpoints (); - } + // Remove all the breakpoints registered with octave. + void remove_all_breakpoints (void); - // Return all breakpoints. Each element of the map is a vector - // containing the breakpoints corresponding to a given function name. - static fname_bp_map - get_breakpoint_list (const octave_value_list& fname_list) - { - return instance_ok () - ? instance->do_get_breakpoint_list (fname_list) : fname_bp_map (); - } + // Return all breakpoints. Each element of the map is a vector + // containing the breakpoints corresponding to a given function name. + fname_bp_map get_breakpoint_list (const octave_value_list& fname_list); + + bool have_breakpoints (void) { return (! m_bp_set.empty ()); } - static bool - have_breakpoints (void) - { - return instance_ok () ? instance->do_have_breakpoints () : 0; - } + // Should we enter debugging for this particular error identifier? + bool debug_on_err (const std::string& id) + { + return (m_errors_that_stop.empty () || m_errors_that_stop.count (id)); + } - // Should we enter debugging for this particular error identifier? - static bool - debug_on_err (const std::string& ID) - { - return (errors_that_stop.empty () || errors_that_stop.count (ID)); - } - - // Should we enter debugging for this particular identifier in a try/catch? - static bool - debug_on_caught (const std::string& ID) - { - return (caught_that_stop.empty () || caught_that_stop.count (ID)); - } + // Should we enter debugging for this particular identifier in a try/catch? + bool debug_on_caught (const std::string& id) + { + return (m_caught_that_stop.empty () || m_caught_that_stop.count (id)); + } - // Should we enter debugging for this particular warning identifier? - static bool - debug_on_warn (const std::string& ID) - { - return (warnings_that_stop.empty () || warnings_that_stop.count (ID)); - } - - static octave_map stop_on_err_warn_status (bool toScreen); + // Should we enter debugging for this particular warning identifier? + bool debug_on_warn (const std::string& id) + { + return (m_warnings_that_stop.empty () || m_warnings_that_stop.count (id)); + } - static void dbstop_process_map_args (const octave_map& mv); + octave_map stop_on_err_warn_status (bool to_screen); - static void dbclear_all_signals (void); - - static bool condition_valid (const std::string& cond); + void dbstop_process_map_args (const octave_map& mv); - static void parse_dbfunction_params (const char *, const octave_value_list&, - std::string&, bp_table::intmap&, - std::string&); + void dbclear_all_signals (void); -private: + bool condition_valid (const std::string& cond); - typedef std::set<std::string>::const_iterator const_bp_set_iterator; - typedef std::set<std::string>::iterator bp_set_iterator; + void parse_dbfunction_params (const char *, const octave_value_list&, + std::string&, bp_table::intmap&, + std::string&); - // Set of function (.m file) names containing at least one breakpoint. - std::set<std::string> bp_set; + private: - // Set of error and warning message IDs that cause us to stop - // *if* Vdebug_on_error / Vdebug_on_caught / Vdebug_on_warning is set. - // Empty means stop on any error / caught error / warning. - static std::set<std::string> errors_that_stop; - static std::set<std::string> caught_that_stop; - static std::set<std::string> warnings_that_stop; + typedef std::set<std::string>::const_iterator const_bp_set_iterator; + typedef std::set<std::string>::iterator bp_set_iterator; - static bp_table *instance; - - static void cleanup_instance (void) { delete instance; instance = nullptr; } - - bool do_add_breakpoint_1 (octave_user_code *fcn, const std::string& fname, - const intmap& line, const std::string& condition, - intmap& retval); + // Set of function (.m file) names containing at least one breakpoint. + std::set<std::string> m_bp_set; - intmap do_add_breakpoint (const std::string& fname, const intmap& lines, - const std::string& condition); - - int do_remove_breakpoint_1 (octave_user_code *fcn, const std::string&, - const intmap& lines); - - int do_remove_breakpoint (const std::string&, const intmap& lines); - - intmap do_remove_all_breakpoints_in_file_1 (octave_user_code *fcn, - const std::string& fname); + // Set of error and warning message IDs that cause us to stop + // *if* Vdebug_on_error / Vdebug_on_caught / Vdebug_on_warning is set. + // Empty means stop on any error / caught error / warning. + std::set<std::string> m_errors_that_stop; + std::set<std::string> m_caught_that_stop; + std::set<std::string> m_warnings_that_stop; - intmap do_remove_all_breakpoints_in_file (const std::string& fname, - bool silent); + bool add_breakpoint_1 (octave_user_code *fcn, const std::string& fname, + const intmap& line, const std::string& condition, + intmap& retval); - void do_remove_all_breakpoints (void); + int remove_breakpoint_1 (octave_user_code *fcn, const std::string&, + const intmap& lines); - fname_bp_map do_get_breakpoint_list (const octave_value_list& fname_list); + intmap remove_all_breakpoints_in_file_1 (octave_user_code *fcn, + const std::string& fname); + }; - bool do_have_breakpoints (void) { return (! bp_set.empty ()); } -}; - -extern octave_user_code * get_user_code (const std::string& fname = ""); + extern octave_user_code * get_user_code (const std::string& fname = ""); +} #endif
--- a/libinterp/parse-tree/pt-eval.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/parse-tree/pt-eval.cc Mon Feb 12 00:58:31 2018 -0500 @@ -438,7 +438,9 @@ void tree_evaluator::reset_debug_state (void) { - debug_mode = bp_table::have_breakpoints () || Vdebugging; + bp_table& bptab = __get_bp_table__ ("tree_evaluator::reset_debug_state"); + + debug_mode = bptab.have_breakpoints () || Vdebugging; dbstep_flag = 0; }
--- a/libinterp/parse-tree/pt-jit.cc Sun Feb 11 10:26:22 2018 -0800 +++ b/libinterp/parse-tree/pt-jit.cc Mon Feb 12 00:58:31 2018 -0500 @@ -2291,10 +2291,13 @@ bool tree_jit::enabled (void) { + octave::bp_table& bptab + = octave::__get_bp_table__ ("tree_jit::enabled"); + // Ideally, we should only disable JIT if there is a breakpoint in the code // we are about to run. However, we can't figure this out in O(1) time, so // we conservatively check for the existence of any breakpoints. - return (Vjit_enable && ! bp_table::have_breakpoints () + return (Vjit_enable && ! bptab.have_breakpoints () && ! Vdebug_on_interrupt && ! Vdebug_on_error); }