changeset 25360:bc5f225bc578

eliminate some global accesses to the breakpoint table * pt-eval.h, pt-eval.cc (tree_evaluator::m_bp_table): Store breakpoint table in evaluator, not interpreter. Change all uses. (tree_evaluator::get_user_code): Move here from interpreter. (tree_evaluator::m_dbstep_flag, tree_evaluator::m_in_loop_command): Now a member variables instead of static. Change all uses. (tree_evaluator::reset_debug_state): Now a member function instead of static. Change all uses. New overload that accepts mode directly. (tree_evaluator::final_index_error): Now a member function instead of static. Change all uses. * bp-table.h, bp-table.cc (bp_table::m_evaluator): Store reference to the evaluator in bp_table instead of a reference to the interpreter. Change all uses. * debug.cc (Fdbstep, Fdbcont, Fdbquit): Define with DEFMETHOD. * octave-cmd.h, octave-cmd.cc (octave_cmd_exec::execute, octave_cmd_eval::execute, octave_cmd_debug::execute): Accept reference to interpreter as argument. (octave_command_queue::execute_command_callback): Pass interpreter to execute functions.
author John W. Eaton <jwe@octave.org>
date Tue, 08 May 2018 11:53:34 -0400
parents b93522e36446
children 82445187633e
files libgui/src/octave-cmd.cc libgui/src/octave-cmd.h libinterp/corefcn/debug.cc libinterp/corefcn/interpreter-private.cc libinterp/corefcn/interpreter.cc libinterp/corefcn/interpreter.h libinterp/octave-value/ov-classdef.cc libinterp/parse-tree/bp-table.cc libinterp/parse-tree/bp-table.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h
diffstat 11 files changed, 236 insertions(+), 213 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/src/octave-cmd.cc	Fri May 04 16:18:30 2018 -0400
+++ b/libgui/src/octave-cmd.cc	Tue May 08 11:53:34 2018 -0400
@@ -28,14 +28,16 @@
 
 #include "octave-cmd.h"
 
-#include "octave-qt-link.h"
+#include "builtin-defun-decls.h"
 #include "cmd-edit.h"
-#include "builtin-defun-decls.h"
+#include "interpreter-private.h"
+#include "interpreter.h"
+#include "octave-qt-link.h"
 #include "utils.h"
 
 namespace octave
 {
-  void octave_cmd_exec::execute (void)
+  void octave_cmd_exec::execute (interpreter&)
   {
     std::string pending_input = command_editor::get_current_line ();
 
@@ -45,7 +47,7 @@
     command_editor::accept_line ();
   }
 
-  void octave_cmd_eval::execute (void)
+  void octave_cmd_eval::execute (interpreter&)
   {
     QString function_name = m_info.fileName ();
     function_name.chop (m_info.suffix ().length () + 1);
@@ -73,24 +75,24 @@
     command_editor::accept_line ();
   }
 
-  void octave_cmd_debug::execute (void)
+  void octave_cmd_debug::execute (interpreter& interp)
   {
     if (m_cmd == "step")
       {
         F__db_next_breakpoint_quiet__ (ovl (m_suppress_dbg_location));
-        Fdbstep ();
+        Fdbstep (interp);
       }
     else if (m_cmd == "cont")
       {
         F__db_next_breakpoint_quiet__ (ovl (m_suppress_dbg_location));
-        Fdbcont ();
+        Fdbcont (interp);
       }
     else if (m_cmd == "quit")
-      Fdbquit ();
+      Fdbquit (interp);
     else
       {
         F__db_next_breakpoint_quiet__ (ovl (m_suppress_dbg_location));
-        Fdbstep (ovl (m_cmd.toStdString ()));
+        Fdbstep (interp, ovl (m_cmd.toStdString ()));
       }
 
     command_editor::interrupt (true);
@@ -126,7 +128,13 @@
           repost = true;          // not empty, repost at end
         m_queue_mutex.unlock ();
 
-        cmd->execute ();
+        // FIXME: Could we store a reference to the interpreter in the
+        // octave_command_queue object?  If so, where is the proper
+        // place to initialize that?
+
+        interpreter& interp = __get_interpreter__ ("octave_command_queue::execute_command_callback");
+
+        cmd->execute (interp);
 
         delete cmd;
       }
--- a/libgui/src/octave-cmd.h	Fri May 04 16:18:30 2018 -0400
+++ b/libgui/src/octave-cmd.h	Tue May 08 11:53:34 2018 -0400
@@ -34,6 +34,8 @@
 
 namespace octave
 {
+  class interpreter;
+
   class octave_cmd
   {
   public:
@@ -42,7 +44,7 @@
 
     virtual ~octave_cmd (void) = default;
 
-    virtual void execute (void) { }
+    virtual void execute (interpreter&) { }
   };
 
   class octave_cmd_exec : public octave_cmd
@@ -51,7 +53,7 @@
 
     octave_cmd_exec (const QString& cmd) : octave_cmd () { m_cmd = cmd; }
 
-    void execute (void);
+    void execute (interpreter& interp);
 
   protected:
 
@@ -64,7 +66,7 @@
 
     octave_cmd_eval (const QFileInfo& info) : octave_cmd () { m_info = info; }
 
-    void execute (void);
+    void execute (interpreter& interp);
 
   protected:
 
@@ -78,7 +80,7 @@
     octave_cmd_debug (const QString& cmd, bool suppress_location)
       : octave_cmd_exec (cmd), m_suppress_dbg_location (suppress_location) { }
 
-    void execute (void);
+    void execute (interpreter& interp);
 
   protected:
 
--- a/libinterp/corefcn/debug.cc	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/corefcn/debug.cc	Tue May 08 11:53:34 2018 -0400
@@ -176,7 +176,9 @@
   std::string condition = "";
   octave_value retval;
 
-  octave::bp_table& bptab = interp.get_bp_table ();
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
+  octave::bp_table& bptab = tw.get_bp_table ();
 
   if (args.length() >= 1 && ! args(0).isstruct ())
     {
@@ -302,7 +304,9 @@
 
   int nargin = args.length ();
 
-  octave::bp_table& bptab = interp.get_bp_table ();
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
+  octave::bp_table& bptab = tw.get_bp_table ();
 
   bptab.parse_dbfunction_params ("dbclear", args, symbol_name, lines, dummy);
 
@@ -374,7 +378,9 @@
   octave::bp_table::fname_bp_map bp_list;
   std::string symbol_name;
 
-  octave::bp_table& bptab = interp.get_bp_table ();
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
+  octave::bp_table& bptab = tw.get_bp_table ();
 
   if (nargin == 1)
     {
@@ -390,7 +396,7 @@
 /*
       if (Vdebugging)
         {
-          octave_user_code *dbg_fcn = interp.get_user_code ();
+          octave_user_code *dbg_fcn = tw.get_user_code ();
           if (dbg_fcn)
             {
               symbol_name = dbg_fcn->name ();
@@ -538,7 +544,9 @@
 @seealso{dbstack, dblist, dbstatus, dbcont, dbstep, dbup, dbdown}
 @end deftypefn */)
 {
-  octave_user_code *dbg_fcn = interp.get_user_code ();
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
+  octave_user_code *dbg_fcn = tw.get_user_code ();
 
   if (! dbg_fcn)
     {
@@ -635,10 +643,12 @@
 
   string_vector argv = args.make_argv ("dbtype");
 
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
   switch (args.length ())
     {
     case 0:  // dbtype
-      dbg_fcn = interp.get_user_code ();
+      dbg_fcn = tw.get_user_code ();
 
       if (! dbg_fcn)
         error ("dbtype: must be inside a user function to give no arguments to dbtype\n");
@@ -656,7 +666,7 @@
 
         if (ind != std::string::npos)  // (dbtype start:end)
           {
-            dbg_fcn = interp.get_user_code ();
+            dbg_fcn = tw.get_user_code ();
 
             if (dbg_fcn)
               {
@@ -686,7 +696,7 @@
 
             if (line == 0)  // (dbtype func)
               {
-                dbg_fcn = interp.get_user_code (arg);
+                dbg_fcn = tw.get_user_code (arg);
 
                 if (! dbg_fcn)
                   error ("dbtype: function <%s> not found\n", arg.c_str ());
@@ -699,7 +709,7 @@
                 if (line <= 0)
                   error ("dbtype: start and end lines must be >= 1\n");
 
-                dbg_fcn = interp.get_user_code ();
+                dbg_fcn = tw.get_user_code ();
 
                 if (dbg_fcn)
                   do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
@@ -711,7 +721,7 @@
 
     case 2:  // (dbtype func start:end) || (dbtype func start)
       {
-        dbg_fcn = interp.get_user_code (argv[1]);
+        dbg_fcn = tw.get_user_code (argv[1]);
 
         if (! dbg_fcn)
           error ("dbtype: function <%s> not found\n", argv[1].c_str ());
@@ -784,7 +794,9 @@
         error ("dblist: N must be a non-negative integer");
     }
 
-  octave_user_code *dbg_fcn = interp.get_user_code ();
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
+  octave_user_code *dbg_fcn = tw.get_user_code ();
 
   if (! dbg_fcn)
     error ("dblist: must be inside a user function to use dblist\n");
@@ -1050,8 +1062,8 @@
   return ovl ();
 }
 
-DEFUN (dbstep, args, ,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (dbstep, interp, args, ,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {} dbstep
 @deftypefnx {} {} dbstep @var{n}
 @deftypefnx {} {} dbstep in
@@ -1081,6 +1093,8 @@
   if (nargin > 1)
     print_usage ();
 
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
   if (nargin == 1)
     {
       std::string arg = args(0).xstring_value ("dbstep: input argument must be a string");
@@ -1090,14 +1104,14 @@
           Vdebugging = false;
           Vtrack_line_num = true;
 
-          octave::tree_evaluator::dbstep_flag = -1;
+          tw.set_dbstep_flag (-1);
         }
       else if (arg == "out")
         {
           Vdebugging = false;
           Vtrack_line_num = true;
 
-          octave::tree_evaluator::dbstep_flag = -2;
+          tw.set_dbstep_flag (-2);
         }
       else
         {
@@ -1109,7 +1123,7 @@
           Vdebugging = false;
           Vtrack_line_num = true;
 
-          octave::tree_evaluator::dbstep_flag = n;
+          tw.set_dbstep_flag (n);
         }
     }
   else
@@ -1117,7 +1131,7 @@
       Vdebugging = false;
       Vtrack_line_num = true;
 
-      octave::tree_evaluator::dbstep_flag = 1;
+      tw.set_dbstep_flag (1);
     }
 
   return ovl ();
@@ -1125,8 +1139,8 @@
 
 DEFALIAS (dbnext, dbstep);
 
-DEFUN (dbcont, args, ,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (dbcont, interp, args, ,
+           doc: /* -*- texinfo -*-
 @deftypefn {} {} dbcont
 Leave command-line debugging mode and continue code execution normally.
 @seealso{dbstep, dbquit}
@@ -1141,13 +1155,15 @@
   Vdebugging = false;
   Vtrack_line_num = true;
 
-  octave::tree_evaluator::reset_debug_state ();
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
+  tw.reset_debug_state ();
 
   return ovl ();
 }
 
-DEFUN (dbquit, args, ,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (dbquit, interp, args, ,
+           doc: /* -*- texinfo -*-
 @deftypefn {} {} dbquit
 Quit debugging mode immediately without further code execution and return to
 the Octave prompt.
@@ -1164,8 +1180,9 @@
 
   Vdebugging = false;
 
-  octave::tree_evaluator::reset_debug_state ();
-  octave::tree_evaluator::debug_mode = false;
+  octave::tree_evaluator& tw = interp.get_evaluator ();
+
+  tw.reset_debug_state (false);
 
   throw octave::interrupt_exception ();
 
--- a/libinterp/corefcn/interpreter-private.cc	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/corefcn/interpreter-private.cc	Tue May 08 11:53:34 2018 -0400
@@ -114,9 +114,9 @@
 
   bp_table& __get_bp_table__ (const std::string& who)
   {
-    interpreter& interp = __get_interpreter__ (who);
+    tree_evaluator& tw = __get_evaluator__ (who);
 
-    return interp.get_bp_table ();
+    return tw.get_bp_table ();
   }
 
   call_stack& __get_call_stack__ (const std::string& who)
--- a/libinterp/corefcn/interpreter.cc	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/corefcn/interpreter.cc	Tue May 08 11:53:34 2018 -0400
@@ -29,7 +29,6 @@
 
 #include "cmd-edit.h"
 #include "cmd-hist.h"
-#include "file-ops.h"
 #include "file-stat.h"
 #include "fpucw-wrappers.h"
 #include "lo-blas-proto.h"
@@ -332,7 +331,6 @@
       m_type_info (),
       m_symbol_table (),
       m_evaluator (*this),
-      m_bp_table (*this),
       m_stream_list (*this),
       m_child_list (),
       m_url_handle_manager (),
@@ -941,7 +939,7 @@
             parser.reset ();
 
             if (m_symbol_table.at_top_level ())
-              tree_evaluator::reset_debug_state ();
+              m_evaluator.reset_debug_state ();
 
             retval = parser.run ();
 
@@ -1182,46 +1180,6 @@
     return m_evaluator.get_profiler ();
   }
 
-  // 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 *
-  interpreter::get_user_code (const std::string& fname)
-  {
-    octave_user_code *user_code = nullptr;
-
-    if (fname.empty ())
-      {
-        call_stack& cs = get_call_stack ();
-
-        user_code = cs.debug_user_code ();
-      }
-    else
-      {
-        std::string name = fname;
-
-        if (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, '/', 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_value fcn = m_symbol_table.find_function (name);
-
-        if (fcn.is_defined () && fcn.is_user_code ())
-          user_code = fcn.user_code_value ();
-      }
-
-    return user_code;
-  }
-
   void interpreter::mlock (void)
   {
     call_stack& cs = get_call_stack ();
--- a/libinterp/corefcn/interpreter.h	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/corefcn/interpreter.h	Tue May 08 11:53:34 2018 -0400
@@ -31,7 +31,6 @@
 #include "quit.h"
 #include "str-vec.h"
 
-#include "bp-table.h"
 #include "dynamic-ld.h"
 #include "environment.h"
 #include "gtk-manager.h"
@@ -53,8 +52,6 @@
 // TRUE means we've processed all the init code and we are good to go.
 extern OCTINTERP_API bool octave_initialized;
 
-class octave_user_code;
-
 namespace octave
 {
   class profiler;
@@ -181,11 +178,6 @@
     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);
@@ -211,8 +203,6 @@
       return m_gtk_manager;
     }
 
-    octave_user_code * get_user_code (const std::string& fname = "");
-
     void mlock (void);
 
     void munlock (const std::string& nm);
@@ -269,8 +259,6 @@
 
     tree_evaluator m_evaluator;
 
-    bp_table m_bp_table;
-
     stream_list m_stream_list;
 
     child_list m_child_list;
--- a/libinterp/octave-value/ov-classdef.cc	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/octave-value/ov-classdef.cc	Tue May 08 11:53:34 2018 -0400
@@ -1139,7 +1139,7 @@
   octave_function * function_value (bool = false) { return this; }
 
   octave_value_list
-  call (octave::tree_evaluator&, int nargout, const octave_value_list& idx)
+  call (octave::tree_evaluator& tw, int nargout, const octave_value_list& idx)
   {
     octave_value_list retval;
 
@@ -1163,12 +1163,11 @@
           error ("`%s' is not a direct superclass of `%s'",
                  cname.c_str (), ctx.get_name ().c_str ());
 
-        if (! is_constructed_object (mname))
+        if (! is_constructed_object (tw, mname))
           error ("cannot call superclass constructor with variable `%s'",
                  mname.c_str ());
 
-        octave::symbol_scope scope
-          = octave::__require_current_scope__ ("octave_classdef_superclass_ref::call");
+        octave::symbol_scope scope = tw.get_current_scope ();
 
         octave_value sym = scope.varval (mname);
 
@@ -1211,10 +1210,10 @@
   }
 
 private:
-  bool is_constructed_object (const std::string nm)
+  bool is_constructed_object (octave::tree_evaluator& tw,
+                              const std::string& nm)
   {
-    octave::call_stack& cs
-      = octave::__get_call_stack__ ("octave_classdef_superclass_ref::is_constructed_object");
+    octave::call_stack& cs = tw.get_call_stack ();
 
     octave_function *of = cs.current ();
 
@@ -3773,8 +3772,7 @@
 
           if (pos == std::string::npos)
             {
-              octave::symbol_table& symtab
-                = octave::__get_symbol_table__ ("cdef_manager::find_class");
+              octave::symbol_table& symtab = m_interpreter.get_symbol_table ();
 
               ov_cls = symtab.find (name);
             }
@@ -3851,8 +3849,7 @@
     }
   else
     {
-      octave::load_path& lp
-        = octave::__get_load_path__ ("cdef_manager::find_package");
+      octave::load_path& lp = m_interpreter.get_load_path ();
 
       if (load_if_not_found && lp.find_package (name))
         {
--- a/libinterp/parse-tree/bp-table.cc	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/parse-tree/bp-table.cc	Tue May 08 11:53:34 2018 -0400
@@ -341,8 +341,7 @@
               {
                 // It was a line number.  Get function name from debugger.
                 if (Vdebugging)
-                  symbol_name
-                    = m_interpreter.get_user_code ()->profiler_name ();
+                  symbol_name = m_evaluator.get_user_code ()->profiler_name ();
                 else
                   error ("%s: function name must come before line number "
                          "and 'if'", who);
@@ -561,7 +560,7 @@
                                              const bp_table::intmap& line,
                                              const std::string& condition)
   {
-    octave_user_code *main_fcn = m_interpreter.get_user_code (fname);
+    octave_user_code *main_fcn = m_evaluator.get_user_code (fname);
 
     if (! main_fcn)
       error ("add_breakpoint: unable to find function '%s'\n", fname.c_str ());
@@ -659,7 +658,7 @@
       }
     else
       {
-        octave_user_code *dbg_fcn = m_interpreter.get_user_code (fname);
+        octave_user_code *dbg_fcn = m_evaluator.get_user_code (fname);
 
         if (! dbg_fcn)
           error ("remove_breakpoint: unable to find function %s\n",
@@ -701,7 +700,7 @@
   {
     intmap retval;
 
-    octave_user_code *dbg_fcn = m_interpreter.get_user_code (fname);
+    octave_user_code *dbg_fcn = m_evaluator.get_user_code (fname);
 
     if (dbg_fcn)
       {
@@ -771,7 +770,7 @@
         if (fname_list.empty ()
             || find_bkpt_list (fname_list, bp_fname) != "")
           {
-            octave_user_code *dbg_fcn = m_interpreter.get_user_code (bp_fname);
+            octave_user_code *dbg_fcn = m_evaluator.get_user_code (bp_fname);
 
             if (dbg_fcn)
               {
@@ -934,8 +933,8 @@
   octave_user_code *
   get_user_code (const std::string& fname)
   {
-    interpreter& interp = __get_interpreter__ ("get_user_code");
+    tree_evaluator& tw = __get_evaluator__ ("get_user_code");
 
-    return interp.get_user_code (fname);
+    return tw.get_user_code (fname);
   }
 }
--- a/libinterp/parse-tree/bp-table.h	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/parse-tree/bp-table.h	Tue May 08 11:53:34 2018 -0400
@@ -36,7 +36,7 @@
 
 namespace octave
 {
-  class interpreter;
+  class tree_evaluator;
 
   struct bp_type
   {
@@ -51,8 +51,8 @@
   {
   public:
 
-    bp_table (interpreter& interp)
-      : m_interpreter (interp), m_bp_set (), m_errors_that_stop (),
+    bp_table (tree_evaluator& tw)
+      : m_evaluator (tw), m_bp_set (), m_errors_that_stop (),
         m_caught_that_stop (), m_warnings_that_stop ()
     { }
 
@@ -130,7 +130,7 @@
     typedef std::set<std::string>::const_iterator const_bp_set_iterator;
     typedef std::set<std::string>::iterator bp_set_iterator;
 
-    interpreter& m_interpreter;
+    tree_evaluator& m_evaluator;
 
     // Set of function (.m file) names containing at least one breakpoint.
     std::set<std::string> m_bp_set;
--- a/libinterp/parse-tree/pt-eval.cc	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Tue May 08 11:53:34 2018 -0400
@@ -32,6 +32,7 @@
 #include <typeinfo>
 
 #include "cmd-edit.h"
+#include "file-ops.h"
 #include "oct-env.h"
 
 #include "bp-table.h"
@@ -60,8 +61,6 @@
 
 namespace octave
 {
-  int tree_evaluator::dbstep_flag = 0;
-
   size_t tree_evaluator::current_frame = 0;
 
   bool tree_evaluator::debug_mode = false;
@@ -71,8 +70,6 @@
   tree_evaluator::stmt_list_type tree_evaluator::statement_context
     = tree_evaluator::other;
 
-  bool tree_evaluator::in_loop_command = false;
-
   // Normal evaluator.
 
   void
@@ -342,7 +339,7 @@
     if (debug_mode)
       do_breakpoint (cmd.is_breakpoint (true));
 
-    if (in_loop_command)
+    if (m_in_loop_command)
       tree_break_command::breaking = 1;
     else
       error ("break must appear in a loop in the same file as loop command");
@@ -424,7 +421,7 @@
     if (debug_mode)
       do_breakpoint (cmd.is_breakpoint (true));
 
-    if (in_loop_command)
+    if (m_in_loop_command)
       tree_continue_command::continuing = 1;
   }
 
@@ -438,11 +435,17 @@
   void
   tree_evaluator::reset_debug_state (void)
   {
-    bp_table& bptab = __get_bp_table__ ("tree_evaluator::reset_debug_state");
-
-    debug_mode = bptab.have_breakpoints () || Vdebugging;
-
-    dbstep_flag = 0;
+    debug_mode = m_bp_table.have_breakpoints () || Vdebugging;
+
+    m_dbstep_flag = 0;
+  }
+
+  void
+  tree_evaluator::reset_debug_state (bool mode)
+  {
+    debug_mode = mode;
+
+    m_dbstep_flag = 0;
   }
 
   Matrix
@@ -692,6 +695,44 @@
     return symtab.current_scope ();
   }
 
+  // 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 *
+  tree_evaluator::get_user_code (const std::string& fname)
+  {
+    octave_user_code *user_code = nullptr;
+
+    if (fname.empty ())
+      user_code = m_call_stack.debug_user_code ();
+    else
+      {
+        std::string name = fname;
+
+        if (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, '/', 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);
+
+        symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+        octave_value fcn = symtab.find_function (name);
+
+        if (fcn.is_defined () && fcn.is_user_code ())
+          user_code = fcn.user_code_value ();
+      }
+
+    return user_code;
+  }
+
   void
   tree_evaluator::visit_decl_command (tree_decl_command& cmd)
   {
@@ -796,9 +837,9 @@
 
     unwind_protect frame;
 
-    frame.protect_var (in_loop_command);
-
-    in_loop_command = true;
+    frame.protect_var (m_in_loop_command);
+
+    m_in_loop_command = true;
 
     tree_expression *expr = cmd.control_expr ();
 
@@ -931,9 +972,9 @@
 
     unwind_protect frame;
 
-    frame.protect_var (in_loop_command);
-
-    in_loop_command = true;
+    frame.protect_var (m_in_loop_command);
+
+    m_in_loop_command = true;
 
     tree_expression *expr = cmd.control_expr ();
 
@@ -1151,49 +1192,6 @@
       }
   }
 
-  // Final step of processing an indexing error.  Add the name of the
-  // variable being indexed, if any, then issue an error.  (Will this also
-  // be needed by pt-lvalue, which calls subsref?)
-
-  static void
-  final_index_error (index_exception& e,
-                     const tree_expression *expr)
-  {
-    std::string extra_message;
-
-    // FIXME: make this a member function for direct access to symbol
-    // table and scope?
-
-    symbol_scope scope
-      = __require_current_scope__ ("final_index_error");
-
-    symbol_record::context_id context = scope.current_context ();
-
-    if (expr->is_identifier ()
-        && dynamic_cast<const tree_identifier *> (expr)->is_variable (context))
-      {
-        std::string var = expr->name ();
-
-        e.set_var (var);
-
-        symbol_table& symtab = __get_symbol_table__ ("final_index_error");
-
-        octave_value fcn = symtab.find_function (var);
-
-        if (fcn.is_function ())
-          {
-            octave_function *fp = fcn.function_value ();
-
-            if (fp && fp->name () == var)
-              extra_message = " (note: variable '" + var + "' shadows function)";
-          }
-      }
-
-    std::string msg = e.message () + extra_message;
-
-    error_with_id (e.err_id (), msg.c_str ());
-  }
-
   // Unlike Matlab, which does not allow the result of a function call
   // or array indexing expression to be further indexed, Octave attempts
   // to handle arbitrary index expressions.  For example, Octave allows
@@ -2156,7 +2154,7 @@
         reset_debug_state ();
       }
     else if (statement_context == function || statement_context == script
-             || in_loop_command)
+             || m_in_loop_command)
       tree_return_command::returning = 1;
   }
 
@@ -2665,9 +2663,9 @@
 
     unwind_protect frame;
 
-    frame.protect_var (in_loop_command);
-
-    in_loop_command = true;
+    frame.protect_var (m_in_loop_command);
+
+    m_in_loop_command = true;
 
     tree_expression *expr = cmd.condition ();
 
@@ -2715,9 +2713,9 @@
 
     unwind_protect frame;
 
-    frame.protect_var (in_loop_command);
-
-    in_loop_command = true;
+    frame.protect_var (m_in_loop_command);
+
+    m_in_loop_command = true;
 
     tree_expression *expr = cmd.condition ();
     int until_line = cmd.line ();
@@ -2779,14 +2777,14 @@
   }
 
   void
-  tree_evaluator::do_breakpoint (tree_statement& stmt) const
+  tree_evaluator::do_breakpoint (tree_statement& stmt)
   {
     do_breakpoint (stmt.is_breakpoint (true), stmt.is_end_of_fcn_or_script ());
   }
 
   void
   tree_evaluator::do_breakpoint (bool is_breakpoint,
-                                 bool is_end_of_fcn_or_script) const
+                                 bool is_end_of_fcn_or_script)
   {
     bool break_on_this_statement = false;
 
@@ -2802,15 +2800,15 @@
       {
         break_on_this_statement = true;
 
-        dbstep_flag = 0;
+        m_dbstep_flag = 0;
 
         current_frame = m_call_stack.current_frame ();
       }
-    else if (dbstep_flag > 0)
+    else if (m_dbstep_flag > 0)
       {
         if (m_call_stack.current_frame () == current_frame)
           {
-            if (dbstep_flag == 1 || is_end_of_fcn_or_script)
+            if (m_dbstep_flag == 1 || is_end_of_fcn_or_script)
               {
                 // We get here if we are doing a "dbstep" or a "dbstep N" and the
                 // count has reached 1 so that we must stop and return to debug
@@ -2820,17 +2818,17 @@
 
                 break_on_this_statement = true;
 
-                dbstep_flag = 0;
+                m_dbstep_flag = 0;
               }
             else
               {
                 // Executing "dbstep N".  Decrease N by one and continue.
 
-                dbstep_flag--;
+                m_dbstep_flag--;
               }
 
           }
-        else if (dbstep_flag == 1
+        else if (m_dbstep_flag == 1
                  && m_call_stack.current_frame () < current_frame)
           {
             // We stepped out from the end of a function.
@@ -2839,20 +2837,20 @@
 
             break_on_this_statement = true;
 
-            dbstep_flag = 0;
+            m_dbstep_flag = 0;
           }
       }
-    else if (dbstep_flag == -1)
+    else if (m_dbstep_flag == -1)
       {
         // We get here if we are doing a "dbstep in".
 
         break_on_this_statement = true;
 
-        dbstep_flag = 0;
+        m_dbstep_flag = 0;
 
         current_frame = m_call_stack.current_frame ();
       }
-    else if (dbstep_flag == -2)
+    else if (m_dbstep_flag == -2)
       {
         // We get here if we are doing a "dbstep out".  Check for end of
         // function and whether the current frame is the same as the
@@ -2862,7 +2860,7 @@
 
         if (is_end_of_fcn_or_script
             && m_call_stack.current_frame () == current_frame)
-          dbstep_flag = -1;
+          m_dbstep_flag = -1;
       }
 
     if (break_on_this_statement)
@@ -3226,6 +3224,45 @@
           octave_stdout << prefix << elt << std::endl;
       }
   }
+
+  // Final step of processing an indexing error.  Add the name of the
+  // variable being indexed, if any, then issue an error.  (Will this also
+  // be needed by pt-lvalue, which calls subsref?)
+
+  void tree_evaluator::final_index_error (index_exception& e,
+                                          const tree_expression *expr)
+  {
+    std::string extra_message;
+
+    symbol_scope scope = get_current_scope ();
+
+    symbol_record::context_id ctxt = scope.current_context ();
+
+    if (expr->is_identifier ()
+        && dynamic_cast<const tree_identifier *> (expr)->is_variable (ctxt))
+      {
+        std::string var = expr->name ();
+
+        e.set_var (var);
+
+        symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+        octave_value fcn = symtab.find_function (var);
+
+        if (fcn.is_function ())
+          {
+            octave_function *fp = fcn.function_value ();
+
+            if (fp && fp->name () == var)
+              extra_message
+                = " (note: variable '" + var + "' shadows function)";
+          }
+      }
+
+    std::string msg = e.message () + extra_message;
+
+    error_with_id (e.err_id (), msg.c_str ());
+  }
 }
 
 DEFMETHOD (max_recursion_depth, interp, args, nargout,
--- a/libinterp/parse-tree/pt-eval.h	Fri May 04 16:18:30 2018 -0400
+++ b/libinterp/parse-tree/pt-eval.h	Tue May 08 11:53:34 2018 -0400
@@ -30,6 +30,7 @@
 #include <stack>
 #include <string>
 
+#include "bp-table.h"
 #include "call-stack.h"
 #include "ov.h"
 #include "ovl.h"
@@ -37,6 +38,8 @@
 #include "pt-exp.h"
 #include "pt-walk.h"
 
+class octave_user_code;
+
 namespace octave
 {
   class symbol_scope;
@@ -126,11 +129,11 @@
       : m_interpreter (interp), m_result_type (RT_UNDEFINED),
         m_expr_result_value (), m_expr_result_value_list (),
         m_lvalue_list_stack (), m_nargout_stack (),
-        m_call_stack (interp), m_profiler (),
+        m_bp_table (*this), m_call_stack (interp), m_profiler (),
         m_max_recursion_depth (256), m_silent_functions (false),
-        m_string_fill_char (' '), m_PS4 ("+ "), m_echo (ECHO_OFF),
-        m_echo_state (false), m_echo_file_name (), m_echo_file_pos (1),
-        m_echo_files ()
+        m_string_fill_char (' '), m_PS4 ("+ "), m_dbstep_flag (0),
+        m_echo (ECHO_OFF), m_echo_state (false), m_echo_file_name (),
+        m_echo_file_pos (1), m_echo_files (), m_in_loop_command (false)
     { }
 
     // No copying!
@@ -238,13 +241,11 @@
 
     bool statement_printing_enabled (void);
 
-    static void reset_debug_state (void);
+    void reset_debug_state (void);
 
-    // If > 0, stop executing at the (N-1)th stopping point, counting
-    //         from the the current execution point in the current frame.
-    //
-    // If < 0, stop executing at the next possible stopping point.
-    static int dbstep_flag;
+    void reset_debug_state (bool mode);
+
+    void set_dbstep_flag (int step) { m_dbstep_flag = step; }
 
     // The number of the stack frame we are currently debugging.
     static size_t current_frame;
@@ -264,9 +265,6 @@
     // The context for the current evaluation.
     static stmt_list_type statement_context;
 
-    // TRUE means we are evaluating some kind of looping construct.
-    static bool in_loop_command;
-
     Matrix ignored_fcn_outputs (void) const;
 
     bool isargout (int nargout, int iout) const;
@@ -368,12 +366,18 @@
     bool switch_case_label_matches (tree_switch_case *expr,
                                     const octave_value& val);
 
+    interpreter& get_interpreter (void) { return m_interpreter; }
+
+    bp_table& get_bp_table (void) { return m_bp_table; }
+
     call_stack& get_call_stack (void) { return m_call_stack; }
 
     profiler& get_profiler (void) { return m_profiler; }
 
     symbol_scope get_current_scope (void);
 
+    octave_user_code * get_user_code (const std::string& fname = "");
+
     int max_recursion_depth (void) const { return m_max_recursion_depth; }
 
     int max_recursion_depth (int n)
@@ -445,10 +449,10 @@
 
     bool maybe_push_echo_state_cleanup (void);
 
-    void do_breakpoint (tree_statement& stmt) const;
+    void do_breakpoint (tree_statement& stmt);
 
     void do_breakpoint (bool is_breakpoint,
-                        bool is_end_of_fcn_or_script = false) const;
+                        bool is_end_of_fcn_or_script = false);
 
     virtual octave_value
     do_keyboard (const octave_value_list& args = octave_value_list ()) const;
@@ -481,6 +485,8 @@
 
     void echo_code (size_t line);
 
+    void final_index_error (index_exception& e, const tree_expression *expr);
+
     interpreter& m_interpreter;
 
     result_type m_result_type;
@@ -491,6 +497,8 @@
 
     value_stack<int> m_nargout_stack;
 
+    bp_table m_bp_table;
+
     call_stack m_call_stack;
 
     profiler m_profiler;
@@ -509,6 +517,12 @@
     // String printed before echoed commands (enabled by --echo-commands).
     std::string m_PS4;
 
+    // If > 0, stop executing at the (N-1)th stopping point, counting
+    //         from the the current execution point in the current frame.
+    //
+    // If < 0, stop executing at the next possible stopping point.
+    int m_dbstep_flag;
+
     // Echo commands as they are executed?
     //
     //   1  ==>  echo commands read from script files
@@ -527,6 +541,9 @@
     size_t m_echo_file_pos;
 
     std::map<std::string, bool> m_echo_files;
+
+    // TRUE means we are evaluating some kind of looping construct.
+    bool m_in_loop_command;
   };
 }