changeset 7901:3e4c9b69069d

call stack changes
author John W. Eaton <jwe@octave.org>
date Tue, 08 Jul 2008 12:00:32 -0400
parents 5b077861d168
children c51ae36fcbce
files src/ChangeLog src/debug.cc src/ls-mat5.cc src/mex.cc src/ov-fcn-handle.cc src/ov-usr-fcn.cc src/parse.y src/symtab.cc src/symtab.h src/toplev.cc src/toplev.h
diffstat 11 files changed, 380 insertions(+), 160 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/ChangeLog	Tue Jul 08 12:00:32 2008 -0400
@@ -1,3 +1,60 @@
+2008-06-28  John W. Eaton  <jwe@octave.org>
+
+	* debug.cc (push_dummy_call_stack_elt): Delete.
+	(Fdbstack): Don't push dummy stack elt.  Use nskip instead of
+	nframes, and curr_frame instead of idx.  Pass curr_frame to
+	octave_call_stack::backtrace.
+
+	* ls-mat5.cc (read_mat5_binary_element): Explicitly pass 0 for
+	current_context in call to symbol_table::varref.
+	* ov-fcn-handle.cc (octave_fcn_handle::load_ascii): Likewise.
+	Fix test.
+
+	* mex.cc (mexGetVariable, mexPutVariable): Use octave_call_stack
+	methods to manipulate scope and context.
+	* parse.y (source_file, Fassignin, Fevalin): Likewise.
+
+	* ov-usr-fcn.cc (octave_user_function::do_multi_index_op):
+	Call octave_call_stack::push after call to symbol_table::push_scope.
+	Call unwind_protect::add with octave_call_stack::unwind_pop
+	immediately after call to octave_call_stack::push.
+
+	* symtab.cc (symbol_table::xcurrent_caller_scope): Delete definition.
+	* symtab.h (symbol_table::xcurrent_caller_scope): Delete decl.
+	Delete all uses.
+	(xcurrent_context_this_table): Delete.  Delete all uses.
+	(symbol_table::current_caller_scope): Delete.
+
+	* toplev.cc (octave_call_stack::do_num_user_code_frames):
+	New function.
+	(octave_call_stack::do_backtrace): New arg, curr_user_frame.	
+	Rename nframes arg to nskip.  Correctly handle curr_frame not at
+	end of stack.
+	(octave_call_stack::do_goto_frame_relative): Rename n arg to nskip.
+	Correctly handle curr_frame not at end of stack.
+	(octave_call_stack::do_goto_caller_frame): New function.
+	(octave_call_stack::do_goto_base_frame): New function.
+	* toplev.h: Provide decls for new functions.
+	(octave_call_stack::call_stack_elt::prev): New data member.
+	Initialize it in constructor.
+	(octave_call_stack::const_reverse_iterator,
+	octave_call_stack::reverse_iterator): New typedefs.
+	(octave_call_stack::symbol_table::scope_id current_scope,
+	octave_call_stack::symbol_table::context_id current_context,
+	octave_call_stack::num_user_code_frames,
+	octave_call_stack::goto_caller_frame,
+	octave_call_stack::goto_base_frame,
+	octave_call_stack::do_num_user_code_frames,
+	octave_call_stack::do_current_scope,
+	octave_call_stack::do_current_context): New functions.
+
+	(octave_call_stack::push): Default value for context arg is
+	symbol_table::current_context, not 0.
+	(octave_call_stack::do_push): Save previous frame.  Always push
+	new frame on back of stack.  Call symbol_table::set_scope_and_context.
+	(octave_call_stack::do_pop): Restore previous frame.  Always pop
+	frame from back of stack.  Call symbol_table::set_scope_and_context.
+
 2008-06-26  John W. Eaton  <jwe@octave.org>
 
 	* token.h: Omit "class symbol_table::symbol_record" decl;
--- a/src/debug.cc	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/debug.cc	Tue Jul 08 12:00:32 2008 -0400
@@ -695,12 +695,6 @@
   return retval;
 }
 
-void
-push_dummy_call_stack_elt (void *)
-{
-  octave_call_stack::push (static_cast<octave_function *> (0));
-}
-
 DEFCMD (dbstack, args, nargout,
   "-*- texinfo -*-\n\
 @deftypefn {Loadable Function} {[@var{stack}, @var{idx}]} dbstack (@var{n})\n\
@@ -713,18 +707,9 @@
 
   unwind_protect::begin_frame ("Fdbstack");
 
-  // Debugging functions should not be included in the call stack, so
-  // we pop the dbstack function from the stack and then set up to
-  // restore a stack element when we exit this function (or when it is
-  // cleaned up).
+  octave_idx_type curr_frame = -1;
 
-  octave_call_stack::pop ();
-
-  unwind_protect::add (push_dummy_call_stack_elt, 0);
-
-  size_t total_frames = octave_call_stack::size ();
-
-  size_t nframes = total_frames;
+  size_t nskip = 0;
 
   if (args.length () == 1)
     {
@@ -742,18 +727,14 @@
 	n = args(0).int_value ();
 
       if (n > 0)
-	nframes = n;
+	nskip = n;
       else
 	error ("dbstack: expecting N to be a nonnegative integer");
     }
 
   if (! error_state)
     {
-      size_t curr_frame = octave_call_stack::current_frame ();
-
-      Octave_map stk = octave_call_stack::backtrace (nframes);
-
-      octave_idx_type idx = total_frames - curr_frame;
+      Octave_map stk = octave_call_stack::backtrace (nskip, curr_frame);
 
       if (nargout == 0)
 	{
@@ -773,7 +754,7 @@
 		  octave_value line = lines(i);
 		  octave_value column = columns(i);
 
-		  octave_stdout << (i == idx ? "--> " : "    ")
+		  octave_stdout << (i == curr_frame ? "--> " : "    ")
 				<< name.string_value ()
 				<< " at line " << line.int_value ()
 				<< " column " << column.int_value ()
@@ -783,7 +764,7 @@
 	}
       else
 	{
-	  retval(1) = idx < 0 ? 1 : idx + 1;
+	  retval(1) = curr_frame < 0 ? 1 : curr_frame + 1;
 	  retval(0) = stk;
 	}
     }
--- a/src/ls-mat5.cc	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/ls-mat5.cc	Tue Jul 08 12:00:32 2008 -0400
@@ -874,7 +874,7 @@
 		    std::string key = m2.key(p0);
 		    octave_value val = m2.contents(p0)(0);
 
-		    symbol_table::varref (key, local_scope) = val;
+		    symbol_table::varref (key, local_scope, 0) = val;
                   }
 	      }
 	    
--- a/src/mex.cc	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/mex.cc	Tue Jul 08 12:00:32 2008 -0400
@@ -3268,16 +3268,22 @@
     val = get_global_value (name);
   else
     {
-      symbol_table::scope_id scope = -1;
-
-      if (! strcmp (space, "caller"))
-	scope = symbol_table::current_caller_scope ();
-      else if (! strcmp (space, "base"))
-	scope = symbol_table::top_scope ();
+      bool caller = ! strcmp (space, "caller");
+      bool base = ! strcmp (space, "base");
+
+      if (caller || base)
+	{
+	  if (caller)
+	    octave_call_stack::goto_caller_frame ();
+	  else
+	    octave_call_stack::goto_base_frame ();
+
+	  val = symbol_table::varval (name);
+
+	  octave_call_stack::pop ();
+	}
       else
 	mexErrMsgTxt ("mexGetVariable: symbol table does not exist");
-
-      val = symbol_table::varval (name, scope);
     }
 
   if (val.is_defined ())
@@ -3317,16 +3323,22 @@
     {
       // FIXME -- should this be in variables.cc?
 
-      symbol_table::scope_id scope = -1;
-
-      if (! strcmp (space, "caller"))
-	scope = symbol_table::current_caller_scope ();
-      else if (! strcmp (space, "base"))
-	scope = symbol_table::top_scope ();
+      bool caller = ! strcmp (space, "caller");
+      bool base = ! strcmp (space, "base");
+
+      if (caller || base)
+	{
+	  if (caller)
+	    octave_call_stack::goto_caller_frame ();
+	  else
+	    octave_call_stack::goto_base_frame ();
+
+	  symbol_table::varref (name) = mxArray::as_octave_value (ptr);
+
+	  octave_call_stack::pop ();
+	}
       else
 	mexErrMsgTxt ("mexPutVariable: symbol table does not exist");
-
-      symbol_table::varref (name, scope) = mxArray::as_octave_value (ptr);
     }
 
   return 0;
--- a/src/ov-fcn-handle.cc	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/ov-fcn-handle.cc	Tue Jul 08 12:00:32 2008 -0400
@@ -342,7 +342,7 @@
 		      break;
 		    }
 
-		  symbol_table::varref (name, local_scope) = t2;
+		  symbol_table::varref (name, local_scope, 0) = t2;
 		}
 	    }
 	}
@@ -1122,8 +1122,8 @@
 %! if (!isempty(findstr(octave_config_info ("DEFS"),"HAVE_HDF5")))
 %!   modes(end+1) = "-hdf5";
 %! endif
-%! for i = modes
-%!   mode = modes{1};
+%! for i = 1:numel (modes)
+%!   mode = modes{i};
 %!   nm = tmpnam();
 %!   unwind_protect
 %!     save (mode, nm, "f2", "g2", "hm2", "hdld2", "hbi2");
--- a/src/ov-usr-fcn.cc	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/ov-usr-fcn.cc	Tue Jul 08 12:00:32 2008 -0400
@@ -389,11 +389,12 @@
   // Save old and set current symbol table context, for
   // eval_undefined_error().
 
-  octave_call_stack::push (this, local_scope, call_depth);
-
   symbol_table::push_scope (local_scope);
   unwind_protect::add (symbol_table::pop_scope);
 
+  octave_call_stack::push (this, local_scope, call_depth);
+  unwind_protect::add (octave_call_stack::unwind_pop, 0);
+
   if (call_depth > 0)
     {
       symbol_table::push_context ();
@@ -406,8 +407,6 @@
       unwind_protect::add (symbol_table::clear_variables);
     }
 
-  unwind_protect::add (octave_call_stack::unwind_pop, 0);
-
   if (! (is_nested_function () || is_inline_function ()))
     {
       unwind_protect_ptr (curr_parent_function);
--- a/src/parse.y	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/parse.y	Tue Jul 08 12:00:32 2008 -0400
@@ -3407,14 +3407,14 @@
   if (! context.empty ())
     {
       if (context == "caller")
-	symbol_table::push_scope (symbol_table::current_caller_scope ());
+	octave_call_stack::goto_caller_frame ();
       else if (context == "base")
-	symbol_table::push_scope (symbol_table::top_scope ());
+	octave_call_stack::goto_base_frame ();
       else
 	error ("source: context must be \"caller\" or \"base\"");
 
       if (! error_state)
-	unwind_protect::add (symbol_table::pop_scope);
+	unwind_protect::add (octave_call_stack::unwind_pop);
     }      
 
   if (! error_state)
@@ -3965,22 +3965,16 @@
 	  symbol_table::scope_id scope = -1;
 
 	  if (context == "caller")
-	    {
-	      if (symbol_table::current_scope () == symbol_table::current_caller_scope ())
-		{
-		  error ("assignin: assignment in caller not implemented yet for direct recursion");
-		  return retval;
-		}
-	      else
-		scope = symbol_table::current_caller_scope ();
-	    }
+	    octave_call_stack::goto_caller_frame ();
 	  else if (context == "base")
-	    scope = symbol_table::top_scope ();
+	    octave_call_stack::goto_base_frame ();
 	  else
 	    error ("assignin: context must be \"caller\" or \"base\"");
 
 	  if (! error_state)
 	    {
+	      unwind_protect::add (octave_call_stack::unwind_pop);
+
 	      std::string nm = args(1).string_value ();
 
 	      if (! error_state)
@@ -4024,23 +4018,15 @@
 	  unwind_protect::begin_frame ("Fevalin");
 
 	  if (context == "caller")
-	    {
-	      if (symbol_table::current_scope () == symbol_table::current_caller_scope ())
-		{
-		  error ("evalin: evaluation in caller not implemented yet for direct recursion");
-		  return retval;
-		}
-	      else
-		symbol_table::push_scope (symbol_table::current_caller_scope ());
-	    }
+	    octave_call_stack::goto_caller_frame ();
 	  else if (context == "base")
-	    symbol_table::push_scope (symbol_table::top_scope ());
+	    octave_call_stack::goto_base_frame ();
 	  else
 	    error ("evalin: context must be \"caller\" or \"base\"");
 
 	  if (! error_state)
 	    {
-	      unwind_protect::add (symbol_table::pop_scope);
+	      unwind_protect::add (octave_call_stack::unwind_pop);
 
 	      if (nargin > 2)
 	        {
--- a/src/symtab.cc	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/symtab.cc	Tue Jul 08 12:00:32 2008 -0400
@@ -57,7 +57,6 @@
 const symbol_table::scope_id symbol_table::xtop_scope = 1;
 
 symbol_table::scope_id symbol_table::xcurrent_scope = 1;
-symbol_table::scope_id symbol_table::xcurrent_caller_scope = -1;
 
 symbol_table::scope_id symbol_table::xparent_scope = -1;
 
--- a/src/symtab.h	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/symtab.h	Tue Jul 08 12:00:32 2008 -0400
@@ -859,7 +859,6 @@
   static scope_id top_scope (void) { return xtop_scope; }
 
   static scope_id current_scope (void) { return xcurrent_scope; }
-  static scope_id current_caller_scope (void) { return xcurrent_caller_scope; }
 
   static context_id current_context (void) { return xcurrent_context; }
 
@@ -887,7 +886,7 @@
 	  instance = p->second;
 
 	xcurrent_scope = scope;
-	xcurrent_context = instance->xcurrent_context_this_table;
+	xcurrent_context = 0;
       }
   }
 
@@ -921,8 +920,6 @@
     if (scope_stack.empty ())
       scope_stack.push_front (xtop_scope);
 
-    xcurrent_caller_scope = xcurrent_scope;
-
     set_scope (scope);
 
     scope_stack.push_front (scope);
@@ -933,8 +930,6 @@
     scope_stack.pop_front ();
 
     set_scope (scope_stack[0]);
-
-    xcurrent_caller_scope = scope_stack.size () > 1 ? scope_stack[1] : -1;
   }
 
   static void pop_scope (void *) { pop_scope (); }
@@ -946,8 +941,6 @@
     scope_stack.push_front (xtop_scope);
 
     set_scope (xtop_scope);
-
-    xcurrent_caller_scope = -1;
   }
 
   static void set_parent_scope (scope_id scope)
@@ -1825,19 +1818,16 @@
   static const scope_id xtop_scope;
 
   static scope_id xcurrent_scope;
-  static scope_id xcurrent_caller_scope;
 
   // We use parent_scope to handle parsing subfunctions.
   static scope_id xparent_scope;
 
-  // Used to handle recursive calls.
-  context_id xcurrent_context_this_table;
   static context_id xcurrent_context;
 
   static std::deque<scope_id> scope_stack;
 
   symbol_table (void)
-    : table_name (), table (), xcurrent_context_this_table () { }
+    : table_name (), table () { }
 
   ~symbol_table (void) { }
 
@@ -2025,16 +2015,12 @@
 
   void do_push_context (void)
   {
-    xcurrent_context = ++xcurrent_context_this_table;
-
     for (table_iterator p = table.begin (); p != table.end (); p++)
       p->second.push_context ();
   }
 
   void do_pop_context (void)
   {
-    xcurrent_context = --xcurrent_context_this_table;
-
     for (table_iterator p = table.begin (); p != table.end (); )
       {
 	if (p->second.pop_context () == 0)
--- a/src/toplev.cc	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/toplev.cc	Tue Jul 08 12:00:32 2008 -0400
@@ -167,6 +167,43 @@
   return retval;
 }
 
+size_t
+octave_call_stack::do_num_user_code_frames (octave_idx_type& curr_user_frame) const
+{
+  size_t retval = 0;
+
+  curr_user_frame = 0;
+
+  // Look for the caller of dbstack.
+  size_t frame = cs[curr_frame].prev;
+
+  bool found = false;
+
+  size_t k = cs.size ();
+
+  for (const_reverse_iterator p = cs.rbegin (); p != cs.rend (); p++)
+    {
+      octave_function *f = (*p).fcn;
+
+      if (--k == frame)
+	found = true;
+
+      if (f && f->is_user_code ())
+	{
+	  if (! found)
+	    curr_user_frame++;
+
+	  retval++;
+	}
+    }
+
+  // We counted how many user frames were not the one, in reverse.
+  // Now set curr_user_frame to be the index in the other direction.
+  curr_user_frame = retval - curr_user_frame - 1;
+
+  return retval;
+}
+
 octave_user_script *
 octave_call_stack::do_caller_user_script (difference_type q) const
 {
@@ -237,72 +274,76 @@
 }
 
 Octave_map
-octave_call_stack::do_backtrace (int nframes) const
+octave_call_stack::do_backtrace (size_t nskip,
+				 octave_idx_type& curr_user_frame) const
 {
   Octave_map retval;
 
-  if (nframes > 0 && nframes <= cs.size ())
-    {
-      Cell keys (6, 1);
+  size_t user_code_frames = do_num_user_code_frames (curr_user_frame);
+
+  size_t nframes = nskip <= user_code_frames ? user_code_frames - nskip : 0;
 
-      keys(0) = "file";
-      keys(1) = "name";
-      keys(2) = "line";
-      keys(3) = "column";
-      keys(4) = "scope";
-      keys(5) = "context";
+  // Our list is reversed.
+  curr_user_frame = nframes - curr_user_frame - 1;
+
+  Cell keys (6, 1);
 
-      Cell file (nframes, 1);
-      Cell name (nframes, 1);
-      Cell line (nframes, 1);
-      Cell column (nframes, 1);
-      Cell scope (nframes, 1);
-      Cell context (nframes, 1);
-
-      octave_idx_type k = nframes - 1;
+  keys(0) = "file";
+  keys(1) = "name";
+  keys(2) = "line";
+  keys(3) = "column";
+  keys(4) = "scope";
+  keys(5) = "context";
 
-      const_iterator p = cs.begin ();
+  Cell file (nframes, 1);
+  Cell name (nframes, 1);
+  Cell line (nframes, 1);
+  Cell column (nframes, 1);
+  Cell scope (nframes, 1);
+  Cell context (nframes, 1);
 
-      while (k >= 0)
+  if (nframes > 0)
+    {
+      int k = 0;
+
+      for (const_reverse_iterator p = cs.rbegin (); p != cs.rend (); p++)
 	{
-	  const call_stack_elt& elt = *p++;
-
-	  scope(k) = elt.scope;
-	  context(k) = elt.context;
+	  const call_stack_elt& elt = *p;
 
 	  octave_function *f = elt.fcn;
 
-	  if (f)
+	  if (f && f->is_user_code ())
 	    {
-	      file(k) = f->fcn_file_name ();
-	      std::string parent_fcn_name = f->parent_fcn_name ();
-	      if (parent_fcn_name == std::string ())
-		name(k) = f->name ();
-	      else
-		name(k) = f->parent_fcn_name () + Vfilemarker + f->name ();
-
-	      tree_statement *stmt = elt.stmt;
-
-	      if (stmt)
-		{
-		  line(k) = stmt->line ();
-		  column(k) = stmt->column ();
-		}
+	      if (nskip > 0)
+		nskip--;
 	      else
 		{
-		  line(k) = -1;
-		  column(k) = -1;
+		  scope(k) = elt.scope;
+		  context(k) = elt.context;
+
+		  file(k) = f->fcn_file_name ();
+		  std::string parent_fcn_name = f->parent_fcn_name ();
+		  if (parent_fcn_name == std::string ())
+		    name(k) = f->name ();
+		  else
+		    name(k) = f->parent_fcn_name () + Vfilemarker + f->name ();
+
+		  tree_statement *stmt = elt.stmt;
+
+		  if (stmt)
+		    {
+		      line(k) = stmt->line ();
+		      column(k) = stmt->column ();
+		    }
+		  else
+		    {
+		      line(k) = -1;
+		      column(k) = -1;
+		    }
+
+		  k++;
 		}
 	    }
-	  else
-	    {
-	      file(k) = "<unknown>";
-	      name(k) = "<unknown>";
-	      line(k) = -1;
-	      column(k) = -1;
-	    }
-
-	  k--;
 	}
 
       retval.assign ("file", file);
@@ -356,26 +397,126 @@
 }
 
 bool
-octave_call_stack::do_goto_frame_relative (int n, bool verbose)
+octave_call_stack::do_goto_frame_relative (int nskip, bool verbose)
 {
   bool retval = false;
 
-  size_t sz = cs.size ();
-
-  if (n == 0)
+  if (nskip == 0)
     retval = true;
   else
     {
-      size_t frame = static_cast<size_t> (n) + curr_frame;
+      int incr = nskip < 0 ? -1 : 1;
+
+      // Start looking with the caller of dbup/dbdown.
+      size_t frame = cs[curr_frame].prev;
+
+      while (true)
+	{
+	  if ((incr < 0 && frame == 0) || (incr > 0 && frame == cs.size () - 1))
+	    break;
+
+	  frame += incr;
+
+	  const call_stack_elt& elt = cs[frame];
+
+	  octave_function *f = elt.fcn;
+
+	  if (f && f->is_user_code ())
+	    {
+	      if (nskip > 0)
+		nskip--;
+	      else if (nskip < 0)
+		nskip++;
+
+	      if (nskip == 0)
+		{
+		  curr_frame = frame;
+		  cs[cs.size () - 1].prev = curr_frame;
 
-      if ((n > 0 && frame < sz) || (n < 0 && frame >= 0))
-	retval = goto_frame (frame, verbose);
+		  if (verbose)
+		    {
+		      tree_statement *s = elt.stmt;
+		      int l = -1;
+		      int c = -1;
+		      if (s)
+			{
+			  l = s->line ();
+			  c = s->column ();
+			}
+
+		      std::ostringstream buf;
+		      buf << f->name () << ": " << " line " << l
+			  << ", column " << c << std::endl;
+
+		      octave_stdout << buf.str ();
+		    }
+
+		  retval = true;
+		  break;
+		}
+	    }
+	}
+
+      // There is no need to set scope and context here.  That will
+      // happen when the dbup/dbdown frame is popped and we jump to
+      // the new "prev" frame set above.
     }
 
   return retval;
 }
 
 void
+octave_call_stack::do_goto_caller_frame (void)
+{
+  size_t frame = curr_frame;
+
+  bool skipped = false;
+
+  while (frame != 0)
+    {
+      frame = cs[frame].prev;
+
+      const call_stack_elt& elt = cs[frame];
+
+      octave_function *f = elt.fcn;
+
+      if (f && f->is_user_code ())
+	{
+	  if (! skipped)
+	    // We found the current user code frame, so skip it.
+	    skipped = true;
+	  else
+	    {
+	      // We found the caller user code frame.
+	      call_stack_elt tmp (elt);
+	      tmp.prev = curr_frame;
+
+	      curr_frame = cs.size ();
+
+	      cs.push_back (tmp);
+
+	      symbol_table::set_scope_and_context (tmp.scope, tmp.context);
+
+	      break;
+	    }
+	}
+    }
+}
+
+void
+octave_call_stack::do_goto_base_frame (void)
+{
+  call_stack_elt tmp (cs[0]);
+  tmp.prev = curr_frame;
+
+  curr_frame = cs.size ();
+
+  cs.push_back (tmp);
+
+  symbol_table::set_scope_and_context (tmp.scope, tmp.context);
+}
+
+void
 recover_from_exception (void)
 {
   can_interrupt = true;
--- a/src/toplev.h	Sat Jun 28 05:15:02 2008 -0400
+++ b/src/toplev.h	Tue Jul 08 12:00:32 2008 -0400
@@ -77,13 +77,18 @@
   struct call_stack_elt
   {
     call_stack_elt (octave_function *f, symbol_table::scope_id s,
-		    symbol_table::context_id c)
-      : fcn (f), stmt (0), scope (s), context (c) { }
+		    symbol_table::context_id c, size_t p = 0)
+      : fcn (f), stmt (0), scope (s), context (c), prev (p) { }
+
+    call_stack_elt (const call_stack_elt& elt)
+      : fcn (elt.fcn), stmt (elt.stmt), scope (elt.scope),
+	context (elt.context), prev (elt.prev) { }
 
     octave_function *fcn;
     tree_statement *stmt;
     symbol_table::scope_id scope;
     symbol_table::context_id context;
+    size_t prev;
   };
 
 protected:
@@ -94,6 +99,10 @@
 
   typedef std::deque<call_stack_elt>::iterator iterator;
   typedef std::deque<call_stack_elt>::const_iterator const_iterator;
+
+  typedef std::deque<call_stack_elt>::reverse_iterator reverse_iterator;
+  typedef std::deque<call_stack_elt>::const_reverse_iterator const_reverse_iterator;
+
   typedef std::deque<call_stack_elt>::difference_type difference_type;
 
   static bool instance_ok (void)
@@ -159,6 +168,22 @@
     return instance_ok () ? instance->do_size () : 0;
   }
 
+  static size_t num_user_code_frames (octave_idx_type& curr_user_frame)
+  {
+    return instance_ok ()
+      ? instance->do_num_user_code_frames (curr_user_frame) : 0;
+  }
+
+  static symbol_table::scope_id current_scope (void)
+  {
+    return instance_ok () ? instance->do_current_scope () : 0;
+  }
+
+  static symbol_table::context_id current_context (void)
+  {
+    return instance_ok () ? instance->do_current_context () : 0;
+  }
+
   // Function at location N on the call stack (N == 0 is current), may
   // be built-in.
   static octave_function *element (size_t n)
@@ -187,7 +212,7 @@
   static void
   push (octave_function *f,
 	symbol_table::scope_id scope = symbol_table::current_scope (),
-	symbol_table::context_id context = 0)
+	symbol_table::context_id context = symbol_table::current_context ())
   {
     if (instance_ok ())
       instance->do_push (f, scope, context);
@@ -220,9 +245,22 @@
       ? instance->do_goto_frame_relative (n, verbose) : false;
   }
 
-  static Octave_map backtrace (int n = 0)
+  static void goto_caller_frame (void)
+  {
+    if (instance_ok ())
+      instance->do_goto_caller_frame ();
+  }
+
+  static void goto_base_frame (void)
   {
-    return instance_ok () ? instance->do_backtrace (n) : Octave_map ();
+    if (instance_ok ())
+      instance->do_goto_base_frame ();
+  }
+
+  static Octave_map backtrace (size_t nskip, octave_idx_type& curr_user_frame)
+  {
+    return instance_ok ()
+      ? instance->do_backtrace (nskip, curr_user_frame) : Octave_map ();
   }
 
   static void pop (void)
@@ -262,6 +300,20 @@
 
   size_t do_size (void) { return cs.size (); }
 
+  size_t do_num_user_code_frames (octave_idx_type& curr_user_frame) const;
+
+  symbol_table::scope_id do_current_scope (void) const
+  {
+    return curr_frame > 0 && curr_frame < cs.size ()
+      ? cs[curr_frame].scope : 0;
+  }
+
+  symbol_table::context_id do_current_context (void) const
+  {
+    return curr_frame > 0 && curr_frame < cs.size ()
+      ? cs[curr_frame].context : 0;
+  }
+
   octave_function *do_element (size_t n)
   {
     octave_function *retval = 0;
@@ -284,9 +336,10 @@
   void do_push (octave_function *f, symbol_table::scope_id scope,
 		symbol_table::context_id context)
   {
-    curr_frame++;
-
-    cs.push_back (call_stack_elt (f, scope, context));
+    size_t prev_frame = curr_frame;
+    curr_frame = cs.size ();
+    cs.push_back (call_stack_elt (f, scope, context, prev_frame));
+    symbol_table::set_scope_and_context (scope, context);
   }
 
   octave_function *do_top (void) const
@@ -320,24 +373,30 @@
     if (! cs.empty ())
       {
 	call_stack_elt& elt = cs.back ();
-
 	elt.stmt = s;
       }
   }
 
-  Octave_map do_backtrace (int n) const;
+  Octave_map do_backtrace (size_t nskip,
+			   octave_idx_type& curr_user_frame) const;
 
   bool do_goto_frame (size_t n, bool verbose);
 
   bool do_goto_frame_relative (int n, bool verbose);
 
+  void do_goto_caller_frame (void);
+
+  void do_goto_base_frame (void);
+
   void do_pop (void)
   {
-    if (! cs.empty ())
+    if (cs.size () > 1)
       {
-	curr_frame--;
-
+	const call_stack_elt& elt = cs.back ();
+	curr_frame = elt.prev;
 	cs.pop_back ();
+	const call_stack_elt& new_elt = cs[curr_frame];
+	symbol_table::set_scope_and_context (new_elt.scope, new_elt.context);
       }
   }