view libinterp/corefcn/call-stack.cc @ 31605:e88a07dec498 stable

maint: Use macros to begin/end C++ namespaces. * oct-conf-post-public.in.h: Define two macros (OCTAVE_BEGIN_NAMESPACE, OCTAVE_END_NAMESPACE) that can be used to start/end a namespace. * mk-opts.pl, build-env.h, build-env.in.cc, __betainc__.cc, __contourc__.cc, __dsearchn__.cc, __eigs__.cc, __expint__.cc, __ftp__.cc, __gammainc__.cc, __ichol__.cc, __ilu__.cc, __isprimelarge__.cc, __lin_interpn__.cc, __magick_read__.cc, __pchip_deriv__.cc, __qp__.cc, amd.cc, auto-shlib.cc, auto-shlib.h, balance.cc, base-text-renderer.cc, base-text-renderer.h, besselj.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.cc, c-file-ptr-stream.h, call-stack.cc, call-stack.h, ccolamd.cc, cellfun.cc, chol.cc, colamd.cc, colloc.cc, conv2.cc, daspk.cc, dasrt.cc, dassl.cc, data.cc, data.h, debug.cc, defaults.cc, defaults.h, defun-int.h, defun.cc, det.cc, dirfns.cc, display.cc, display.h, dlmread.cc, dmperm.cc, dot.cc, dynamic-ld.cc, dynamic-ld.h, eig.cc, 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, fftn.cc, file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, ft-text-renderer.h, gcd.cc, getgrent.cc, getpwent.cc, getrusage.cc, givens.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h, graphics-toolkit.cc, graphics-toolkit.h, graphics.cc, graphics.in.h, gsvd.cc, gtk-manager.cc, gtk-manager.h, hash.cc, help.cc, help.h, hess.cc, hex2num.cc, 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, kron.cc, latex-text-renderer.cc, latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h, lookup.cc, ls-ascii-helper.cc, ls-ascii-helper.h, ls-oct-text.cc, ls-utils.cc, ls-utils.h, lsode.cc, lu.cc, mappers.cc, matrix_type.cc, max.cc, mex-private.h, mex.cc, mgorth.cc, nproc.cc, oct-fstrm.cc, oct-fstrm.h, oct-hdf5-types.cc, oct-hdf5-types.h, oct-hist.cc, oct-hist.h, oct-iostrm.cc, oct-iostrm.h, oct-opengl.h, oct-prcstrm.cc, oct-prcstrm.h, oct-procbuf.cc, oct-procbuf.h, oct-process.cc, oct-process.h, oct-stdstrm.h, oct-stream.cc, oct-stream.h, oct-strstrm.cc, oct-strstrm.h, oct-tex-lexer.in.ll, oct-tex-parser.yy, ordqz.cc, ordschur.cc, pager.cc, pager.h, pinv.cc, pow2.cc, pr-flt-fmt.cc, pr-output.cc, procstream.cc, procstream.h, psi.cc, qr.cc, quad.cc, quadcc.cc, qz.cc, rand.cc, rcond.cc, regexp.cc, schur.cc, settings.cc, settings.h, sighandlers.cc, sighandlers.h, sparse-xdiv.cc, sparse-xdiv.h, sparse-xpow.cc, sparse-xpow.h, sparse.cc, spparms.cc, sqrtm.cc, stack-frame.cc, stack-frame.h, stream-euler.cc, strfind.cc, strfns.cc, sub2ind.cc, svd.cc, sylvester.cc, symbfact.cc, syminfo.cc, syminfo.h, symrcm.cc, symrec.cc, symrec.h, symscope.cc, symscope.h, symtab.cc, symtab.h, syscalls.cc, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h, text-renderer.cc, text-renderer.h, time.cc, toplev.cc, tril.cc, tsearch.cc, typecast.cc, url-handle-manager.cc, url-handle-manager.h, urlwrite.cc, utils.cc, utils.h, variables.cc, variables.h, xdiv.cc, xdiv.h, xnorm.cc, xnorm.h, xpow.cc, xpow.h, __delaunayn__.cc, __fltk_uigetfile__.cc, __glpk__.cc, __init_fltk__.cc, __init_gnuplot__.cc, __ode15__.cc, __voronoi__.cc, audiodevinfo.cc, audioread.cc, convhulln.cc, fftw.cc, gzip.cc, mk-build-env-features.sh, mk-builtins.pl, 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.cc, ov-base.h, ov-bool-mat.cc, ov-builtin.h, ov-cell.cc, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h, ov-complex.cc, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.h, ov-java.cc, ov-java.h, ov-mex-fcn.h, ov-null-mat.cc, ov-oncleanup.cc, ov-struct.cc, ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc, ov.h, octave.cc, octave.h, mk-ops.sh, op-b-b.cc, op-b-bm.cc, op-b-sbm.cc, op-bm-b.cc, op-bm-bm.cc, op-bm-sbm.cc, op-cdm-cdm.cc, op-cell.cc, op-chm.cc, op-class.cc, op-cm-cm.cc, op-cm-cs.cc, op-cm-m.cc, op-cm-s.cc, op-cm-scm.cc, op-cm-sm.cc, op-cs-cm.cc, op-cs-cs.cc, op-cs-m.cc, op-cs-s.cc, op-cs-scm.cc, op-cs-sm.cc, op-dm-dm.cc, op-dm-scm.cc, op-dm-sm.cc, op-dm-template.cc, op-dms-template.cc, op-fcdm-fcdm.cc, op-fcm-fcm.cc, op-fcm-fcs.cc, op-fcm-fm.cc, op-fcm-fs.cc, op-fcn.cc, op-fcs-fcm.cc, op-fcs-fcs.cc, op-fcs-fm.cc, op-fcs-fs.cc, op-fdm-fdm.cc, op-fm-fcm.cc, op-fm-fcs.cc, op-fm-fm.cc, op-fm-fs.cc, op-fs-fcm.cc, op-fs-fcs.cc, op-fs-fm.cc, op-fs-fs.cc, op-i16-i16.cc, op-i32-i32.cc, op-i64-i64.cc, op-i8-i8.cc, op-int-concat.cc, op-m-cm.cc, op-m-cs.cc, op-m-m.cc, op-m-s.cc, op-m-scm.cc, op-m-sm.cc, op-mi.cc, op-pm-pm.cc, op-pm-scm.cc, op-pm-sm.cc, op-pm-template.cc, op-range.cc, op-s-cm.cc, op-s-cs.cc, op-s-m.cc, op-s-s.cc, op-s-scm.cc, op-s-sm.cc, op-sbm-b.cc, op-sbm-bm.cc, op-sbm-sbm.cc, op-scm-cm.cc, op-scm-cs.cc, op-scm-m.cc, op-scm-s.cc, op-scm-scm.cc, op-scm-sm.cc, op-sm-cm.cc, op-sm-cs.cc, op-sm-m.cc, op-sm-s.cc, op-sm-scm.cc, op-sm-sm.cc, op-str-m.cc, op-str-s.cc, op-str-str.cc, op-struct.cc, op-ui16-ui16.cc, op-ui32-ui32.cc, op-ui64-ui64.cc, op-ui8-ui8.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, lex.ll, oct-lvalue.cc, oct-lvalue.h, oct-parse.yy, 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-vm-eval.cc, 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 : Use new macros to begin/end C++ namespaces.
author Rik <rik@octave.org>
date Thu, 01 Dec 2022 14:23:45 -0800
parents 32d2b6604a9f
children aac27ad79be6
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)