changeset 21703:4bf980861fd6

Allow breakpoints in nested functions, and in mixed subfunctions (bug #47917). * debug.cc (bp_table::do_add_breakpoint): For each line to be added, find the function within whose scope the line lies before walking the tree. * debug.cc: Add semicolons to existing BIST tests.
author Lachlan Andrew <lachlanbis@gmail.com>
date Sat, 14 May 2016 10:17:11 +1000
parents 19e8eddd4773
children ac59b72712fd
files libinterp/corefcn/debug.cc
diffstat 1 files changed, 67 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/debug.cc	Sat May 14 09:19:43 2016 -0700
+++ b/libinterp/corefcn/debug.cc	Sat May 14 10:17:11 2016 +1000
@@ -28,6 +28,7 @@
 #include <fstream>
 #include <iomanip>
 #include <iostream>
+#include <limits>
 #include <set>
 #include <string>
 
@@ -437,9 +438,9 @@
 %! dbstop help at 100;
 %! dbstop in ls 100;
 %! dbstop help 200 if a==5;
-%! dbstop if error Octave:undefined-function
+%! dbstop if error Octave:undefined-function;
 %! s = dbstatus;
-%! dbclear all
+%! dbclear all;
 %! assert ({s.bkpt(:).name}, {"help", "help", "help>do_contents", "ls", "ls"});
 %! assert ([s.bkpt(:).line], [48, 100, 200, 58, 100]);
 %! assert (s.errs, {"Octave:undefined-function"});
@@ -652,42 +653,85 @@
                              const bp_table::intmap& line,
                              const std::string& condition)
 {
-  octave_user_code *dbg_fcn = get_user_code (fname);
+  octave_user_code *main_fcn = get_user_code (fname);
 
-  if (! dbg_fcn)
+  if (! main_fcn)
     error ("add_breakpoint: unable to find function '%s'\n", fname.c_str ());
 
-  condition_valid (condition); // Throw error if condition not valid.
+  condition_valid (condition);  // Throw error if condition not valid.
 
   intmap retval;
 
-  if (! do_add_breakpoint_1 (dbg_fcn, fname, line, condition, retval))
+  const std::list<std::string> subfcn_names   = main_fcn->subfunction_names ();
+  std::map<std::string, octave_value> subfcns = main_fcn->subfunctions ();
+
+  octave_idx_type len = line.size ();
+
+  for (int i = 0; i < len; i++)
     {
-      // Search subfunctions in the order they appear in the file.
+      const_intmap_iterator m = line.find (i);
+
+      if (m != line.end ())
+        {
+          int lineno = m->second;
 
-      const std::list<std::string> subfcn_names
-        = dbg_fcn->subfunction_names ();
+          octave_user_code *dbg_fcn = 0;
+          octave_user_code *next_fcn = 0;  // 1st function starting after line
+
+          // Find innermost nested (or parent) function containing line.
+          int earliest_end = std::numeric_limits<int>::max ();
 
-      std::map<std::string, octave_value> subfcns
-        = dbg_fcn->subfunctions ();
+          for (std::list<std::string>::const_iterator p
+                                                      = subfcn_names.begin ();
+               p != subfcn_names.end (); p++)
+            {
+              std::map<std::string, octave_value>::const_iterator
+                q = subfcns.find (*p);
+
+              if (q != subfcns.end () && q->second.is_user_function ())
+                {
+                  octave_user_function *dbg_subfcn
+                                        = q->second.user_function_value ();
 
-      for (std::list<std::string>::const_iterator p = subfcn_names.begin ();
-           p != subfcn_names.end (); p++)
-        {
-          std::map<std::string, octave_value>::const_iterator
-            q = subfcns.find (*p);
+                  if (dbg_subfcn->ending_line () < earliest_end
+                      && dbg_subfcn->ending_line () >= lineno
+                      && dbg_subfcn->beginning_line () <= lineno)
+                    {
+                      earliest_end = dbg_subfcn->ending_line ();
+                      dbg_fcn = dbg_subfcn;
+                    }
+
+                  // First fcn starting after line.
+                  // This is used if line is not inside any function.
+                  if (dbg_subfcn->beginning_line () >= lineno && ! next_fcn)
+                    next_fcn = dbg_subfcn;
+                }
+            }
 
-          if (q != subfcns.end ())
+          // The breakpoint is either in the subfunction found above,
+          // or in the main function, which we check now.
+          if (main_fcn->is_user_function ())
             {
-              octave_user_code *dbg_subfcn = q->second.user_code_value ();
+              int e = dynamic_cast<octave_user_function*> (main_fcn)
+                      ->ending_line ();
+              if (e >= lineno && e < earliest_end)
+                dbg_fcn = main_fcn;
+            }
 
-              if (do_add_breakpoint_1 (dbg_subfcn, fname, line, condition,
-                                       retval))
-              break;
-            }
+          if (! dbg_fcn)
+            dbg_fcn = next_fcn;
+
+          // 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));
         }
     }
 
+
   tree_evaluator::debug_mode = bp_table::have_breakpoints () || Vdebugging;
 
   return retval;
@@ -1496,7 +1540,7 @@
 %! dbstop quantile>__quantile__;
 %! dbstop ls;
 %! s = dbstatus;
-%! dbclear all
+%! dbclear all;
 %! assert (s(1).name, "@audioplayer/set>setproperty");
 %! assert (s(2).name, "@ftp/dir");
 %! assert (s(3).name, "ls");