view libinterp/corefcn/call-stack.cc @ 31607:aac27ad79be6 stable

maint: Re-indent code after switch to using namespace macros. * build-env.h, build-env.in.cc, Cell.h, __betainc__.cc, __eigs__.cc, __ftp__.cc, __ichol__.cc, __ilu__.cc, __isprimelarge__.cc, __magick_read__.cc, __pchip_deriv__.cc, amd.cc, base-text-renderer.cc, base-text-renderer.h, besselj.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.h, call-stack.cc, call-stack.h, ccolamd.cc, cellfun.cc, chol.cc, colamd.cc, dasrt.cc, data.cc, debug.cc, defaults.cc, defaults.h, det.cc, display.cc, display.h, dlmread.cc, dynamic-ld.cc, dynamic-ld.h, ellipj.cc, environment.cc, environment.h, error.cc, error.h, errwarn.h, event-manager.cc, event-manager.h, event-queue.cc, event-queue.h, fcn-info.cc, fcn-info.h, fft.cc, fft2.cc, file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, ft-text-renderer.h, gcd.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h, graphics-toolkit.cc, graphics-toolkit.h, graphics.cc, gsvd.cc, gtk-manager.cc, gtk-manager.h, help.cc, help.h, hook-fcn.cc, hook-fcn.h, input.cc, input.h, interpreter-private.cc, interpreter-private.h, interpreter.cc, interpreter.h, inv.cc, jsondecode.cc, jsonencode.cc, latex-text-renderer.cc, latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h, lookup.cc, ls-hdf5.cc, ls-mat4.cc, ls-mat5.cc, lsode.cc, lu.cc, mappers.cc, matrix_type.cc, max.cc, mex.cc, mexproto.h, mxarray.h, mxtypes.in.h, oct-errno.in.cc, oct-hdf5-types.cc, oct-hist.cc, oct-hist.h, oct-map.cc, oct-map.h, oct-opengl.h, oct-prcstrm.h, oct-process.cc, oct-process.h, oct-stdstrm.h, oct-stream.cc, oct-stream.h, oct-strstrm.h, octave-default-image.h, ordqz.cc, ordschur.cc, pager.cc, pager.h, pinv.cc, pow2.cc, pr-output.cc, psi.cc, qr.cc, quadcc.cc, rand.cc, regexp.cc, settings.cc, settings.h, sighandlers.cc, sighandlers.h, sparse-xpow.cc, sqrtm.cc, stack-frame.cc, stack-frame.h, stream-euler.cc, strfns.cc, svd.cc, syminfo.cc, syminfo.h, symrcm.cc, symrec.cc, symrec.h, symscope.cc, symscope.h, symtab.cc, symtab.h, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h, text-renderer.cc, text-renderer.h, time.cc, toplev.cc, typecast.cc, url-handle-manager.cc, url-handle-manager.h, urlwrite.cc, utils.cc, utils.h, variables.cc, variables.h, xdiv.cc, __delaunayn__.cc, __init_fltk__.cc, __init_gnuplot__.cc, __ode15__.cc, __voronoi__.cc, audioread.cc, convhulln.cc, gzip.cc, cdef-class.cc, cdef-class.h, cdef-fwd.h, cdef-manager.cc, cdef-manager.h, cdef-method.cc, cdef-method.h, cdef-object.cc, cdef-object.h, cdef-package.cc, cdef-package.h, cdef-property.cc, cdef-property.h, cdef-utils.cc, cdef-utils.h, ov-base-diag.cc, ov-base-int.cc, ov-base-mat.cc, ov-base-mat.h, ov-base-scalar.cc, ov-base.cc, ov-base.h, ov-bool-mat.cc, ov-bool-mat.h, ov-bool-sparse.cc, ov-bool.cc, ov-builtin.h, ov-cell.cc, ov-ch-mat.cc, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h, ov-complex.cc, ov-cx-diag.cc, ov-cx-mat.cc, ov-cx-sparse.cc, ov-dld-fcn.cc, ov-dld-fcn.h, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.h, ov-float.cc, ov-flt-complex.cc, ov-flt-cx-diag.cc, ov-flt-cx-mat.cc, ov-flt-re-diag.cc, ov-flt-re-mat.cc, ov-flt-re-mat.h, ov-intx.h, ov-java.cc, ov-lazy-idx.cc, ov-legacy-range.cc, ov-magic-int.cc, ov-mex-fcn.cc, ov-mex-fcn.h, ov-null-mat.cc, ov-perm.cc, ov-range.cc, ov-re-diag.cc, ov-re-mat.cc, ov-re-mat.h, ov-re-sparse.cc, ov-scalar.cc, ov-str-mat.cc, ov-struct.cc, ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc, ov.h, ovl.h, octave.cc, octave.h, op-b-sbm.cc, op-bm-sbm.cc, op-cs-scm.cc, op-fm-fcm.cc, op-fs-fcm.cc, op-s-scm.cc, op-scm-cs.cc, op-scm-s.cc, op-sm-cs.cc, ops.h, anon-fcn-validator.cc, anon-fcn-validator.h, bp-table.cc, bp-table.h, comment-list.cc, comment-list.h, filepos.h, lex.h, oct-lvalue.cc, oct-lvalue.h, parse.h, profiler.cc, profiler.h, pt-anon-scopes.cc, pt-anon-scopes.h, pt-arg-list.cc, pt-arg-list.h, pt-args-block.cc, pt-args-block.h, pt-array-list.cc, pt-array-list.h, pt-assign.cc, pt-assign.h, pt-binop.cc, pt-binop.h, pt-bp.cc, pt-bp.h, pt-cbinop.cc, pt-cbinop.h, pt-cell.cc, pt-cell.h, pt-check.cc, pt-check.h, pt-classdef.cc, pt-classdef.h, pt-cmd.h, pt-colon.cc, pt-colon.h, pt-const.cc, pt-const.h, pt-decl.cc, pt-decl.h, pt-eval.cc, pt-eval.h, pt-except.cc, pt-except.h, pt-exp.cc, pt-exp.h, pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h, pt-jump.h, pt-loop.cc, pt-loop.h, pt-mat.cc, pt-mat.h, pt-misc.cc, pt-misc.h, pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h, pt-stmt.cc, pt-stmt.h, pt-tm-const.cc, pt-tm-const.h, pt-unop.cc, pt-unop.h, pt-walk.cc, pt-walk.h, pt.cc, pt.h, token.cc, token.h, Range.cc, Range.h, idx-vector.cc, idx-vector.h, range-fwd.h, CollocWt.cc, CollocWt.h, aepbalance.cc, aepbalance.h, chol.cc, chol.h, gepbalance.cc, gepbalance.h, gsvd.cc, gsvd.h, hess.cc, hess.h, lo-mappers.cc, lo-mappers.h, lo-specfun.cc, lo-specfun.h, lu.cc, lu.h, oct-convn.cc, oct-convn.h, oct-fftw.cc, oct-fftw.h, oct-norm.cc, oct-norm.h, oct-rand.cc, oct-rand.h, oct-spparms.cc, oct-spparms.h, qr.cc, qr.h, qrp.cc, qrp.h, randgamma.cc, randgamma.h, randmtzig.cc, randmtzig.h, randpoisson.cc, randpoisson.h, schur.cc, schur.h, sparse-chol.cc, sparse-chol.h, sparse-lu.cc, sparse-lu.h, sparse-qr.cc, sparse-qr.h, svd.cc, svd.h, child-list.cc, child-list.h, dir-ops.cc, dir-ops.h, file-ops.cc, file-ops.h, file-stat.cc, file-stat.h, lo-sysdep.cc, lo-sysdep.h, lo-sysinfo.cc, lo-sysinfo.h, mach-info.cc, mach-info.h, oct-env.cc, oct-env.h, oct-group.cc, oct-group.h, oct-password.cc, oct-password.h, oct-syscalls.cc, oct-syscalls.h, oct-time.cc, oct-time.h, oct-uname.cc, oct-uname.h, action-container.cc, action-container.h, base-list.h, cmd-edit.cc, cmd-edit.h, cmd-hist.cc, cmd-hist.h, f77-fcn.h, file-info.cc, file-info.h, lo-array-errwarn.cc, lo-array-errwarn.h, lo-hash.cc, lo-hash.h, lo-ieee.h, lo-regexp.cc, lo-regexp.h, lo-utils.cc, lo-utils.h, oct-base64.cc, oct-base64.h, oct-glob.cc, oct-glob.h, oct-inttypes.h, oct-mutex.cc, oct-mutex.h, oct-refcount.h, oct-shlib.cc, oct-shlib.h, oct-sparse.cc, oct-sparse.h, oct-string.h, octave-preserve-stream-state.h, pathsearch.cc, pathsearch.h, quit.cc, quit.h, unwind-prot.cc, unwind-prot.h, url-transfer.cc, url-transfer.h: Re-indent code after switch to using namespace macros.
author Rik <rik@octave.org>
date Thu, 01 Dec 2022 18:02:15 -0800
parents e88a07dec498
children 597f3ee61a48
line wrap: on
line source

////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 1995-2022 The Octave Project Developers
//
// See the file COPYRIGHT.md in the top-level directory of this
// distribution or <https://octave.org/copyright/>.
//
// This file is part of Octave.
//
// Octave is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Octave is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Octave; see the file COPYING.  If not, see
// <https://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include "lo-regexp.h"
#include "str-vec.h"

#include "call-stack.h"
#include "defun.h"
#include "interpreter.h"
#include "interpreter-private.h"
#include "oct-map.h"
#include "ov.h"
#include "ov-fcn.h"
#include "ov-fcn-handle.h"
#include "ov-usr-fcn.h"
#include "pager.h"
#include "parse.h"
#include "stack-frame.h"
#include "syminfo.h"
#include "symrec.h"
#include "symscope.h"
#include "variables.h"

OCTAVE_BEGIN_NAMESPACE(octave)

// Use static fields for the best efficiency.
// NOTE: C++0x will allow these two to be merged into one.
static const char *bt_fieldnames[] =
{ "file", "name", "line", "column", nullptr };

static const octave_fields bt_fields (bt_fieldnames);

call_stack::call_stack (tree_evaluator& evaluator)
  : m_evaluator (evaluator), m_cs (), m_curr_frame (0),
    m_max_stack_depth (1024), m_global_values ()
{
  push (symbol_scope ("top scope"));
}

octave_function *call_stack::current_function (bool skip_first) const
{
  if (m_cs.empty ())
    error ("current_function: call stack is empty");

  octave_function *fcn = nullptr;

  std::size_t idx = m_curr_frame;

  if (idx > 0 && skip_first)
    --idx;

  while (true)
    {
      fcn = m_cs[idx]->function ();

      if (fcn || idx == 0)
        break;

      --idx;
    }

  return fcn;
}

int call_stack::current_line (void) const
{
  int retval = -1;

  if (! m_cs.empty ())
    {
      const std::shared_ptr<stack_frame> elt = m_cs[m_curr_frame];
      retval = elt->line ();
    }

  return retval;
}

int call_stack::current_column (void) const
{
  int retval = -1;

  if (! m_cs.empty ())
    {
      const std::shared_ptr<stack_frame> elt = m_cs[m_curr_frame];
      retval = elt->column ();
    }

  return retval;
}

octave_user_code *call_stack::current_user_code (void) const
{
  // Start at current frame.

  std::size_t xframe = find_current_user_frame ();

  if (xframe > 0)
    {
      const std::shared_ptr<stack_frame> elt = m_cs[xframe];

      octave_function *f = elt->function ();

      if (f && f->is_user_code ())
        return dynamic_cast<octave_user_code *> (f);
    }

  return nullptr;
}

int call_stack::current_user_code_line (void) const
{
  // Start at current frame.

  std::size_t xframe = find_current_user_frame ();

  if (xframe > 0)
    {
      const std::shared_ptr<stack_frame> elt = m_cs[xframe];

      octave_function *f = elt->function ();

      if (f && f->is_user_code ())
        {
          int line = elt->line ();

          if (line > 0)
            return line;
        }
    }

  return -1;
}

int call_stack::current_user_code_column (void) const
{
  // Start at current frame.

  std::size_t xframe = find_current_user_frame ();

  if (xframe > 0)
    {
      const std::shared_ptr<stack_frame> elt = m_cs[xframe];

      octave_function *f = elt->function ();

      if (f && f->is_user_code ())
        {
          int column = elt->column ();

          if (column > 0)
            return column;
        }
    }

  return -1;
}

unwind_protect *call_stack::curr_fcn_unwind_protect_frame (void)
{
  // Start at current frame.

  std::size_t xframe = find_current_user_frame ();

  if (xframe > 0)
    {
      const std::shared_ptr<stack_frame> elt = m_cs[xframe];

      octave_function *f = elt->function ();

      if (f && f->is_user_code ())
        return elt->unwind_protect_frame ();
    }

  return nullptr;
}

octave_user_code *call_stack::debug_user_code (void) const
{
  octave_user_code *retval = nullptr;

  // This should never happen...
  if (m_curr_frame == 0)
    return retval;

  std::size_t i = m_curr_frame;

  while (i != 0)
    {
      const std::shared_ptr<stack_frame> elt = m_cs[i--];

      octave_function *f = elt->function ();

      if (f && f->is_user_code ())
        {
          retval = dynamic_cast<octave_user_code *> (f);
          break;
        }
    }

  return retval;
}

int call_stack::debug_user_code_line (void) const
{
  int retval = -1;

  // This should never happen...
  if (m_curr_frame == 0)
    return retval;

  std::size_t i = m_curr_frame;

  while (i != 0)
    {
      const std::shared_ptr<stack_frame> elt = m_cs[i--];

      octave_function *f = elt->function ();

      if (f && f->is_user_code ())
        {
          if (elt->line ())
            {
              retval = elt->line ();
              break;
            }
        }
    }

  return retval;
}

int call_stack::debug_user_code_column (void) const
{
  int retval = -1;

  // This should never happen...
  if (m_curr_frame == 0)
    return retval;

  // Start looking with the caller of the calling debug function.
  std::size_t i = m_curr_frame;

  while (i != 0)
    {
      const std::shared_ptr<stack_frame> elt = m_cs[i--];

      octave_function *f = elt->function ();

      if (f && f->is_user_code ())
        {
          if (elt->column ())
            {
              retval = elt->column ();
              break;
            }
        }
    }

  return retval;
}

std::string call_stack::get_dispatch_class (void) const
{
  return m_cs[m_curr_frame]->get_dispatch_class ();
}

void call_stack::set_dispatch_class (const std::string& class_name)
{
  m_cs[m_curr_frame]->set_dispatch_class (class_name);
}

bool call_stack::is_class_method_executing (std::string& dispatch_class) const
{
  dispatch_class = "";

  octave_function *f = current_function ();

  bool retval = (f && f->is_class_method ());

  if (retval)
    dispatch_class = f->dispatch_class ();

  return retval;
}

bool call_stack::is_class_constructor_executing (std::string& dispatch_class) const
{
  dispatch_class = "";

  octave_function *f = current_function ();

  bool retval = (f && f->is_class_constructor ());

  if (retval)
    dispatch_class = f->dispatch_class ();

  return retval;
}

bool call_stack::all_scripts (void) const
{
  bool retval = true;

  auto p = m_cs.cend ();

  while (p != m_cs.cbegin ())
    {
      const std::shared_ptr<stack_frame> elt = *(--p);

      octave_function *f = elt->function ();

      if (f && ! f->is_user_script ())
        {
          retval = false;
          break;
        }
    }

  return retval;
}

void call_stack::get_new_frame_index_and_links
(std::size_t& new_frame_idx, std::shared_ptr<stack_frame>& parent_link,
 std::shared_ptr<stack_frame>& static_link) const
{
  // FIXME: is there a better way?

  std::size_t prev_frame_idx = m_curr_frame;

  new_frame_idx = m_cs.size ();

  // m_max_stack_depth should never be less than zero.
  if (new_frame_idx > static_cast<std::size_t> (m_max_stack_depth))
    error ("max_stack_depth exceeded");

  // There can't be any links to previous frames if this is the first
  // frame on the stack.

  if (new_frame_idx == 0)
    return;

  parent_link = m_cs[prev_frame_idx];

  octave_function *t_fcn = parent_link->function ();

  static_link = (t_fcn
                 ? (t_fcn->is_user_code ()
                    ? parent_link : parent_link->static_link ())
                 : parent_link);
}

void call_stack::push (const symbol_scope& scope)
{
  std::size_t new_frame_idx;
  std::shared_ptr<stack_frame> parent_link;
  std::shared_ptr<stack_frame> static_link;

  get_new_frame_index_and_links (new_frame_idx, parent_link, static_link);

  std::shared_ptr<stack_frame>
  new_frame (stack_frame::create (m_evaluator, scope, new_frame_idx,
                                  parent_link, static_link));

  m_cs.push_back (new_frame);

  m_curr_frame = new_frame_idx;
}

void call_stack::push (octave_user_function *fcn,
                       const std::shared_ptr<stack_frame>& closure_frames)
{
  std::size_t new_frame_idx;
  std::shared_ptr<stack_frame> parent_link;
  std::shared_ptr<stack_frame> static_link;

  get_new_frame_index_and_links (new_frame_idx, parent_link, static_link);

  std::shared_ptr<stack_frame>
  new_frame (stack_frame::create (m_evaluator, fcn, new_frame_idx,
                                  parent_link, static_link,
                                  closure_frames));

  m_cs.push_back (new_frame);

  m_curr_frame = new_frame_idx;
}

void call_stack::push (octave_user_function *fcn,
                       const stack_frame::local_vars_map& local_vars,
                       const std::shared_ptr<stack_frame>& closure_frames)
{
  std::size_t new_frame_idx;
  std::shared_ptr<stack_frame> parent_link;
  std::shared_ptr<stack_frame> static_link;

  get_new_frame_index_and_links (new_frame_idx, parent_link, static_link);

  std::shared_ptr<stack_frame>
  new_frame (stack_frame::create (m_evaluator, fcn, new_frame_idx,
                                  parent_link, static_link, local_vars,
                                  closure_frames));

  m_cs.push_back (new_frame);

  m_curr_frame = new_frame_idx;
}

void call_stack::push (octave_user_script *script)
{
  std::size_t new_frame_idx;
  std::shared_ptr<stack_frame> parent_link;
  std::shared_ptr<stack_frame> static_link;

  get_new_frame_index_and_links (new_frame_idx, parent_link, static_link);

  std::shared_ptr<stack_frame>
  new_frame (stack_frame::create (m_evaluator, script, new_frame_idx,
                                  parent_link, static_link));

  m_cs.push_back (new_frame);

  m_curr_frame = new_frame_idx;
}

void call_stack::push (octave_function *fcn)
{
  std::size_t new_frame_idx;
  std::shared_ptr<stack_frame> parent_link;
  std::shared_ptr<stack_frame> static_link;

  get_new_frame_index_and_links (new_frame_idx, parent_link, static_link);

  std::shared_ptr<stack_frame>
  new_frame (stack_frame::create (m_evaluator, fcn, new_frame_idx,
                                  parent_link, static_link));

  m_cs.push_back (new_frame);

  m_curr_frame = new_frame_idx;
}

bool call_stack::goto_frame (std::size_t n, bool verbose)
{
  bool retval = false;

  if (n < m_cs.size ())
    {
      retval = true;

      m_curr_frame = n;

      if (verbose)
        {
          const std::shared_ptr<stack_frame> elt = m_cs[n];

          elt->display_stopped_in_message (octave_stdout);
        }
    }

  return retval;
}

std::size_t call_stack::find_current_user_frame (void) const
{
  std::size_t user_frame = m_curr_frame;

  std::shared_ptr<stack_frame> frm = m_cs[user_frame];

  if (! (frm->is_user_fcn_frame () || frm->is_user_script_frame ()
         || frm->is_scope_frame ()))
    {
      frm = frm->static_link ();

      user_frame = frm->index ();
    }

  return user_frame;
}

std::shared_ptr<stack_frame> call_stack::current_user_frame (void) const
{
  std::size_t frame = find_current_user_frame ();

  return m_cs[frame];
}

// Go to the Nth frame (up if N is negative or down if positive) in
// the call stack that corresponds to a script, function, or scope
// beginning with the frame indexed by START.

std::size_t call_stack::dbupdown (std::size_t start, int n, bool verbose)
{
  if (start >= m_cs.size ())
    error ("invalid stack frame");

  // Can't go up from here.

  if (start == 0 && n < 0)
    {
      if (verbose)
        m_cs[start]->display_stopped_in_message (octave_stdout);

      return start;
    }

  std::shared_ptr<stack_frame> frm = m_cs[start];

  if (! (frm && (frm->is_user_fcn_frame ()
                 || frm->is_user_script_frame ()
                 || frm->is_scope_frame ())))
    error ("call_stack::dbupdown: invalid initial frame in call stack!");

  // Use index into the call stack to begin the search.  At this point
  // we iterate up or down using indexing instead of static links
  // because ... FIXME: it's a bit complicated, but deserves
  // explanation.  May be easiest with some pictures of the call stack
  // for an example or two.

  std::size_t xframe = frm->index ();

  if (n == 0)
    {
      if (verbose)
        frm->display_stopped_in_message (octave_stdout);

      return xframe;
    }

  int incr = 0;

  if (n < 0)
    {
      incr = -1;
      n = -n;
    }
  else if (n > 0)
    incr = 1;

  std::size_t last_good_frame = 0;

  while (true)
    {
      frm = m_cs[xframe];

      if (frm->is_user_fcn_frame () || frm->is_user_script_frame ()
          || frm->is_scope_frame ())
        {
          last_good_frame = xframe;

          if (n == 0)
            break;

          n--;
        }

      xframe += incr;

      if (xframe == 0)
        {
          last_good_frame = 0;
          break;
        }

      if (xframe == m_cs.size ())
        break;
    }

  if (verbose)
    m_cs[last_good_frame]->display_stopped_in_message (octave_stdout);

  return last_good_frame;
}

// Like dbupdown above but find the starting frame automatically from
// the current frame.  If the current frame is already a user
// function, script, or scope frame, use that.  Otherwise, follow
// the static link for the current frame.  If that is not a user
// function, script or scope frame then there is an error in the
// implementation.

std::size_t call_stack::dbupdown (int n, bool verbose)
{
  std::size_t start = find_current_user_frame ();

  return dbupdown (start, n, verbose);
}

// May be used to temporarily change the value ov m_curr_frame inside
// a function like evalin.  If used in a function like dbup, the new
// value of m_curr_frame would be wiped out when dbup returns and the
// stack frame for dbup is popped.

void call_stack::goto_caller_frame (void)
{
  std::size_t start = find_current_user_frame ();

  std::shared_ptr<stack_frame> caller_frame = m_cs[start]->static_link ();

  // Allow evalin ('caller', ...) to work when called from the
  // top-level prompt.

  m_curr_frame = caller_frame ? caller_frame->index () : 0;
}

void call_stack::goto_base_frame (void)
{
  if (m_curr_frame > 0)
    m_curr_frame = 0;
}

std::list<std::shared_ptr<stack_frame>>
                                     call_stack::backtrace_frames (octave_idx_type& curr_user_frame) const
{
  std::list<std::shared_ptr<stack_frame>> frames;

  // curr_frame is the index to the current frame in the overall call
  // stack, which includes any compiled function frames and scope
  // frames.  The curr_user_frame value we set is the index into the
  // subset of frames returned in the octave_map object.

  std::size_t curr_frame = find_current_user_frame ();

  // Don't include top-level stack frame in the list.

  for (std::size_t n = m_cs.size () - 1; n > 0; n--)
    {
      std::shared_ptr<stack_frame> frm = m_cs[n];

      if (frm->is_user_script_frame () || frm->is_user_fcn_frame ()
          || frm->is_scope_frame ())
        {
          if (frm->index () == curr_frame)
            curr_user_frame = frames.size ();

          frames.push_back (frm);
        }

      if (n == 0)
        break;
    }

  return frames;
}

std::list<std::shared_ptr<stack_frame>>
                                     call_stack::backtrace_frames (void) const
{
  octave_idx_type curr_user_frame = -1;

  return backtrace_frames (curr_user_frame);
}

std::list<frame_info>
call_stack::backtrace_info (octave_idx_type& curr_user_frame,
                            bool print_subfn) const
{
  std::list<std::shared_ptr<stack_frame>> frames
                                       = backtrace_frames (curr_user_frame);

  std::list<frame_info> retval;

  for (const auto& frm : frames)
    {
      if (frm->is_user_script_frame () || frm->is_user_fcn_frame ()
          || frm->is_scope_frame ())
        {
          retval.push_back (frame_info (frm->fcn_file_name (),
                                        frm->fcn_name (print_subfn),
                                        frm->line (), frm->column ()));
        }
    }

  return retval;
}

std::list<frame_info> call_stack::backtrace_info (void) const
{
  octave_idx_type curr_user_frame = -1;

  return backtrace_info (curr_user_frame, true);
}

octave_map call_stack::backtrace (octave_idx_type& curr_user_frame,
                                  bool print_subfn) const
{
  std::list<std::shared_ptr<stack_frame>> frames
                                       = backtrace_frames (curr_user_frame);

  std::size_t nframes = frames.size ();

  octave_map retval (dim_vector (nframes, 1), bt_fields);

  Cell& file = retval.contents (0);
  Cell& name = retval.contents (1);
  Cell& line = retval.contents (2);
  Cell& column = retval.contents (3);

  octave_idx_type k = 0;

  for (const auto& frm : frames)
    {
      if (frm->is_user_script_frame () || frm->is_user_fcn_frame ()
          || frm->is_scope_frame ())
        {
          file(k) = frm->fcn_file_name ();
          name(k) = frm->fcn_name (print_subfn);
          line(k) = frm->line ();
          column(k) = frm->column ();

          k++;
        }
    }

  return retval;
}

octave_map call_stack::backtrace (void) const
{
  octave_idx_type curr_user_frame = -1;

  return backtrace (curr_user_frame, true);
}

octave_map call_stack::empty_backtrace (void) const
{
  return octave_map (dim_vector (0, 1), bt_fields);
}

void call_stack::pop (void)
{
  // Never pop top scope.
  // FIXME: is it possible for this case to happen?

  if (m_cs.size () > 1)
    {
      std::shared_ptr<stack_frame> elt = m_cs.back ();

      std::shared_ptr<stack_frame> caller = elt->parent_link ();

      m_curr_frame = caller->index ();

      if (elt->is_closure_context ())
        elt->break_closure_cycles (elt);

      m_cs.pop_back ();
    }
}

void call_stack::clear (void)
{
  while (! m_cs.empty ())
    pop ();
}

symbol_info_list call_stack::all_variables (void)
{
  return m_cs[m_curr_frame]->all_variables ();
}

std::list<std::string> call_stack::global_variable_names (void) const
{
  std::list<std::string> retval;

  for (const auto& nm_ov : m_global_values)
    {
      if (nm_ov.second.is_defined ())
        retval.push_back (nm_ov.first);
    }

  retval.sort ();

  return retval;
}

std::list<std::string> call_stack::top_level_variable_names (void) const
{
  return m_cs[0]->variable_names ();
}

std::list<std::string> call_stack::variable_names (void) const
{
  return m_cs[m_curr_frame]->variable_names ();
}

void call_stack::clear_global_variable (const std::string& name)
{
  auto p = m_global_values.find (name);

  if (p != m_global_values.end ())
    p->second = octave_value ();
}

void call_stack::clear_global_variable_pattern (const std::string& pattern)
{
  glob_match pat (pattern);

  for (auto& nm_ov : m_global_values)
    {
      if (pat.match (nm_ov.first))
        nm_ov.second = octave_value ();
    }
}

void call_stack::clear_global_variable_regexp (const std::string& pattern)
{
  regexp pat (pattern);

  for (auto& nm_ov : m_global_values)
    {
      if (pat.is_match (nm_ov.first))
        nm_ov.second = octave_value ();
    }
}

void call_stack::clear_global_variables (void)
{
  for (auto& nm_ov : m_global_values)
    nm_ov.second = octave_value ();
}

symbol_info_list
call_stack::glob_symbol_info (const std::string& pattern) const
{
  return m_cs[m_curr_frame]->glob_symbol_info (pattern);
}

symbol_info_list
call_stack::regexp_symbol_info (const std::string& pattern) const
{
  return m_cs[m_curr_frame]->regexp_symbol_info (pattern);
}

symbol_info_list call_stack::get_symbol_info (void)
{
  return m_cs[m_curr_frame]->get_symbol_info ();
}

symbol_info_list call_stack::top_scope_symbol_info (void) const
{
  return m_cs[0]->get_symbol_info ();
}

octave_value call_stack::max_stack_depth (const octave_value_list& args,
    int nargout)
{
  return set_internal_variable (m_max_stack_depth, args, nargout,
                                "max_stack_depth", 0);
}

void call_stack::make_persistent (const symbol_record& sym)
{
  m_cs[m_curr_frame]->make_persistent (sym);
}

void call_stack::make_global (const symbol_record& sym)
{
  m_cs[m_curr_frame]->make_global (sym);
}

octave_value call_stack::global_varval (const std::string& name) const
{
  auto p = m_global_values.find (name);

  return p == m_global_values.end () ? octave_value () : p->second;
}

octave_value& call_stack::global_varref (const std::string& name)
{
  return m_global_values[name];
}

octave_value call_stack::get_top_level_value (const std::string& name) const
{
  return m_cs[0]->varval (name);
}

void call_stack::set_top_level_value (const std::string& name,
                                      const octave_value& value)
{
  m_cs[0]->assign (name, value);
}

octave_value call_stack::do_who (int argc, const string_vector& argv,
                                 bool return_list, bool verbose)
{
  octave_value retval;

  std::string my_name = argv[0];

  std::string file_name;

  bool from_file = false;
  bool global_only = false;
  bool have_regexp = false;

  int i = 1;
  while (i < argc)
    {
      if (argv[i] == "-file")
        {
          if (from_file)
            error ("%s: -file option may only be specified once",
                   my_name.c_str ());

          from_file = true;

          if (i == argc - 1)
            error ("%s: -file argument must be followed by a filename",
                   my_name.c_str ());

          file_name = argv[++i];
        }
      else if (argv[i] == "-regexp")
        {
          have_regexp = true;
        }
      else if (argv[i] == "global")
        global_only = true;
      else if (argv[i][0] == '-')
        warning ("%s: unrecognized option '%s'", my_name.c_str (),
                 argv[i].c_str ());
      else
        break;

      i++;
    }

  int npatterns = argc - i;
  string_vector patterns;
  if (npatterns > 0)
    {
      patterns.resize (npatterns);
      for (int j = 0; j < npatterns; j++)
        patterns[j] = argv[i+j];
    }
  else
    {
      patterns.resize (1);
      patterns[0] = "*";
    }

  if (from_file)
    {
      // FIXME: This is an inefficient manner to implement this as the
      // variables are loaded in to a temporary context and then treated.
      // It would be better to refactor symbol_info_list to not store the
      // symbol records and then use it in load-save.cc (do_load) to
      // implement this option there so that the variables are never
      // stored at all.

      // Set up temporary scope.

      symbol_scope tmp_scope ("$dummy_scope$");

      push (tmp_scope);

      unwind_action restore_scope ([=] (void) { pop (); });

      feval ("load", octave_value (file_name), 0);

      std::string newmsg = "Variables in the file " + file_name + ":\n\n";

      if (global_only)
        return do_global_who_two (patterns, have_regexp, return_list,
                                  verbose, newmsg);
      else
        return do_who_two (patterns, have_regexp, return_list, verbose,
                           newmsg);
    }
  else
    {
      if (global_only)
        return do_global_who_two (patterns, have_regexp, return_list,
                                  verbose);
      else
        return do_who_two (patterns, have_regexp, return_list, verbose);
    }
}

octave_value call_stack::do_who_two (const string_vector& patterns,
                                     bool have_regexp, bool return_list,
                                     bool verbose, const std::string& msg)
{
  return m_cs[m_curr_frame]->who (patterns, have_regexp, return_list,
                                  verbose, m_evaluator.whos_line_format (),
                                  msg);
}

octave_value call_stack::do_global_who_two (const string_vector& patterns,
    bool have_regexp,
    bool return_list, bool verbose,
    const std::string& msg)
{
  symbol_info_list symbol_stats;
  std::list<std::string> symbol_names;

  octave_idx_type npatterns = patterns.numel ();

  for (octave_idx_type j = 0; j < npatterns; j++)
    {
      std::string pattern = patterns[j];

      std::list<std::string> tmp;

      if (have_regexp)
        {
          regexp pat (pattern);

          for (auto& nm_ov : m_global_values)
            {
              if (pat.is_match (nm_ov.first))
                tmp.push_back (nm_ov.first);
            }
        }
      else
        {
          glob_match pat (pattern);

          for (auto& nm_ov : m_global_values)
            {
              if (pat.match (nm_ov.first))
                tmp.push_back (nm_ov.first);
            }
        }

      for (const auto& nm : tmp)
        {
          octave_value value = m_global_values[nm];

          if (value.is_defined ())
            {
              if (verbose)
                {
                  bool is_formal = false;
                  bool is_global = true;
                  bool is_persistent = false;

                  symbol_info syminf (nm, value, is_formal, is_global,
                                      is_persistent);

                  symbol_stats.append (syminf);
                }
              else
                symbol_names.push_back (nm);
            }
        }
    }

  if (return_list)
    {
      if (verbose)
        {
          std::string caller_fcn_name;
          octave_function *caller_fcn = caller_function ();
          if (caller_fcn)
            caller_fcn_name = caller_fcn->name ();

          return symbol_stats.map_value (caller_fcn_name, 1);
        }
      else
        return Cell (string_vector (symbol_names));
    }
  else if (! (symbol_stats.empty () && symbol_names.empty ()))
    {
      if (msg.empty ())
        octave_stdout << "Global variables:\n\n";
      else
        octave_stdout << msg;

      if (verbose)
        symbol_stats.display (octave_stdout,
                              m_evaluator.whos_line_format ());
      else
        {
          string_vector names (symbol_names);

          names.list_in_columns (octave_stdout);
        }

      octave_stdout << "\n";
    }

  return octave_value ();
}

void call_stack::display (void) const
{
  std::ostream& os = octave_stdout;

  std::size_t nframes = size ();

  for (std::size_t i = 0; i < nframes; i++)
    {
      m_cs[i]->display (false);
      if (i < nframes - 1)
        os << std::endl;
    }
}

void call_stack::set_auto_fcn_var (stack_frame::auto_var_type avt,
                                   const octave_value& val)
{
  m_cs[m_curr_frame]->set_auto_fcn_var (avt, val);
}

octave_value call_stack::get_auto_fcn_var (stack_frame::auto_var_type avt) const
{
  return m_cs[m_curr_frame]->get_auto_fcn_var (avt);
}

DEFMETHOD (max_stack_depth, interp, args, nargout,
           doc: /* -*- texinfo -*-
@deftypefn  {} {@var{val} =} max_stack_depth ()
@deftypefnx {} {@var{old_val} =} max_stack_depth (@var{new_val})
@deftypefnx {} {@var{old_val} =} max_stack_depth (@var{new_val}, "local")
Query or set the internal limit on the number of times a function may
be called recursively.

If the limit is exceeded, an error message is printed and control returns to
the top level.

When called from inside a function with the @qcode{"local"} option, the
variable is changed locally for the function and any subroutines it calls.
The original variable value is restored when exiting the function.

@seealso{max_recursion_depth}
@end deftypefn */)
{
  tree_evaluator& tw = interp.get_evaluator ();

  return tw.max_stack_depth (args, nargout);
}

/*
%!test
%! orig_val = max_stack_depth ();
%! old_val = max_stack_depth (2*orig_val);
%! assert (orig_val, old_val);
%! assert (max_stack_depth (), 2*orig_val);
%! max_stack_depth (orig_val);
%! assert (max_stack_depth (), orig_val);

%!error max_stack_depth (1, 2)
*/

DEFMETHOD (who, interp, args, nargout,
           doc: /* -*- texinfo -*-
@deftypefn  {} {} who
@deftypefnx {} {} who pattern @dots{}
@deftypefnx {} {} who option pattern @dots{}
@deftypefnx {} {C =} who (@dots{})
List currently defined variables matching the given patterns.

Valid pattern syntax is the same as described for the @code{clear} command.
If no patterns are supplied, all variables are listed.

By default, only variables visible in the local scope are displayed.

The following are valid options, but may not be combined.

@table @code
@item global
List variables in the global scope rather than the current scope.

@item -regexp
The patterns are considered to be regular expressions when matching the
variables to display.  The same pattern syntax accepted by the @code{regexp}
function is used.

@item -file
The next argument is treated as a filename.  All variables found within the
specified file are listed.  No patterns are accepted when reading variables
from a file.
@end table

If called as a function, return a cell array of defined variable names
matching the given patterns.
@seealso{whos, isglobal, isvarname, exist, regexp}
@end deftypefn */)
{
  int argc = args.length () + 1;

  string_vector argv = args.make_argv ("who");

  tree_evaluator& tw = interp.get_evaluator ();

  return tw.do_who (argc, argv, nargout == 1);
}

/*
%!test
%! avar = magic (4);
%! ftmp = [tempname() ".mat"];
%! save_default_options ("-binary", "local");
%! unwind_protect
%!   save (ftmp, "avar");
%!   vars = whos ("-file", ftmp);
%!   assert (numel (vars), 1);
%!   assert (isstruct (vars));
%!   assert (vars.name, "avar");
%!   assert (vars.size, [4, 4]);
%!   assert (vars.class, "double");
%!   assert (vars.bytes, 128);
%! unwind_protect_cleanup
%!   unlink (ftmp);
%! end_unwind_protect
*/

DEFMETHOD (whos, interp, args, nargout,
           doc: /* -*- texinfo -*-
@deftypefn  {} {} whos
@deftypefnx {} {} whos pattern @dots{}
@deftypefnx {} {} whos option pattern @dots{}
@deftypefnx {} {S =} whos ("pattern", @dots{})
Provide detailed information on currently defined variables matching the
given patterns.

Options and pattern syntax are the same as for the @code{who} command.

Extended information about each variable is summarized in a table with the
following default entries.

@table @asis
@item Attr
Attributes of the listed variable.  Possible attributes are:

@table @asis
@item blank
Variable in local scope

@item @code{c}
Variable of complex type.

@item @code{f}
Formal parameter (function argument).

@item @code{g}
Variable with global scope.

@item @code{p}
Persistent variable.
@end table

@item Name
The name of the variable.

@item Size
The logical size of the variable.  A scalar is 1x1, a vector is
@nospell{1xN} or @nospell{Nx1}, a 2-D matrix is @nospell{MxN}.

@item Bytes
The amount of memory currently used to store the variable.

@item Class
The class of the variable.  Examples include double, single, char, uint16,
cell, and struct.
@end table

The table can be customized to display more or less information through
the function @code{whos_line_format}.

If @code{whos} is called as a function, return a struct array of defined
variable names matching the given patterns.  Fields in the structure
describing each variable are: name, size, bytes, class, global, sparse,
complex, nesting, persistent.
@seealso{who, whos_line_format}
@end deftypefn */)
{
  int argc = args.length () + 1;

  string_vector argv = args.make_argv ("whos");

  tree_evaluator& tw = interp.get_evaluator ();

  return tw.do_who (argc, argv, nargout == 1, true);
}

OCTAVE_END_NAMESPACE(octave)