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);
   }