changeset 28444:b743a63e2dab

maint: merge stable to default.
author John W. Eaton <jwe@octave.org>
date Thu, 11 Jun 2020 01:22:45 -0400
parents f5c9bb5955e7 (current diff) bdd52f5e4170 (diff)
children 7d312fccc8c9
files doc/interpreter/octave.texi doc/interpreter/plot.txi libinterp/corefcn/help.cc libinterp/corefcn/interpreter.cc libinterp/corefcn/interpreter.h libinterp/corefcn/ls-mat5.cc libinterp/corefcn/module.mk libinterp/dldfcn/__init_fltk__.cc libinterp/octave-value/cdef-class.cc libinterp/octave-value/ov-base.cc libinterp/octave-value/ov-base.h libinterp/octave-value/ov-class.cc libinterp/octave-value/ov-class.h libinterp/octave-value/ov-classdef.cc libinterp/octave-value/ov-classdef.h libinterp/octave-value/ov-fcn.h libinterp/octave-value/ov-mex-fcn.cc libinterp/octave-value/ov-mex-fcn.h libinterp/octave-value/ov.cc libinterp/octave-value/ov.h libinterp/parse-tree/oct-parse.yy
diffstat 86 files changed, 6147 insertions(+), 4338 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/external.txi	Tue Jun 09 14:11:13 2020 -0700
+++ b/doc/interpreter/external.txi	Thu Jun 11 01:22:45 2020 -0400
@@ -917,8 +917,6 @@
 
 @item Anonymous Function Handle
 
-@item Inline Function
-
 @item String
 @end enumerate
 
@@ -938,8 +936,6 @@
 @result{} 0.84147
 funcdemo (@@(x) sin (x), 1)
 @result{} 0.84147
-funcdemo (inline ("sin (x)"), 1)
-@result{} 0.84147
 funcdemo ("sin", 1)
 @result{} 0.84147
 funcdemo (@@atan2, 1, 1)
@@ -1698,8 +1694,7 @@
 @end group
 @end example
 
-Note that it is not possible to use function handles or inline functions
-within a mex-file.
+Note that it is not possible to use function handles within a mex-file.
 
 @c @node Application Programming Interface for Mex-Files
 @c @subsection Application Programming Interface for Mex-Files
--- a/doc/interpreter/func.txi	Tue Jun 09 14:11:13 2020 -0700
+++ b/doc/interpreter/func.txi	Thu Jun 11 01:22:45 2020 -0400
@@ -40,7 +40,7 @@
 * Validating Arguments::
 * Function Files::
 * Script Files::
-* Function Handles Anonymous Functions Inline Functions::
+* Function Handles and Anonymous Functions::
 * Commands::
 * Organization of Functions::
 @end menu
@@ -1733,11 +1733,10 @@
 @end group
 @end example
 
-@node Function Handles Anonymous Functions Inline Functions
-@section Function Handles, Anonymous Functions, Inline Functions
+@node Function Handles and Anonymous Functions
+@section Function Handles and Anonymous Functions
 @cindex handle, function handles
 @cindex anonymous functions
-@cindex inline, inline functions
 
 It can be very convenient store a function in a variable so that it
 can be passed to a different function.  For example, a function that
@@ -1747,7 +1746,6 @@
 @menu
 * Function Handles::
 * Anonymous Functions::
-* Inline Functions::
 @end menu
 
 @node Function Handles
@@ -1887,34 +1885,6 @@
 @xref{Operator Overloading}, for a list of operators which also have a
 functional form.
 
-@node Inline Functions
-@subsection Inline Functions
-
-An inline function is created from a string containing the function
-body using the @code{inline} function.  The following code defines the
-function @math{f(x) = x^2 + 2}.
-
-@example
-f = inline ("x^2 + 2");
-@end example
-
-@noindent
-After this it is possible to evaluate @math{f} at any @math{x} by
-writing @code{f(x)}.
-
-@strong{Caution}: @sc{matlab} has begun the process of deprecating inline
-functions.  At some point in the future support will be dropped and eventually
-Octave will follow @sc{matlab} and also remove inline functions.  Use anonymous
-functions in all new code.
-
-@DOCSTRING(inline)
-
-@DOCSTRING(argnames)
-
-@DOCSTRING(formula)
-
-@DOCSTRING(symvar)
-
 @node Commands
 @section Commands
 
--- a/doc/interpreter/octave.texi	Tue Jun 09 14:11:13 2020 -0700
+++ b/doc/interpreter/octave.texi	Thu Jun 11 01:22:45 2020 -0400
@@ -411,7 +411,7 @@
 * Default Arguments::
 * Function Files::
 * Script Files::
-* Function Handles Anonymous Functions Inline Functions::
+* Function Handles and Anonymous Functions::
 * Commands::
 * Organization of Functions::
 
@@ -446,11 +446,10 @@
 * HTML Markup::
 * LaTeX Markup::
 
-Function Handles Anonymous Functions Inline Functions
+Function Handles and Anonymous Functions
 
 * Function Handles::
 * Anonymous Functions::
-* Inline Functions::
 
 Errors and Warnings
 
--- a/doc/interpreter/plot.txi	Tue Jun 09 14:11:13 2020 -0700
+++ b/doc/interpreter/plot.txi	Thu Jun 11 01:22:45 2020 -0400
@@ -332,12 +332,11 @@
 @subsubsection Two-dimensional Function Plotting
 @cindex plotting, two-dimensional functions
 
-Octave can plot a function from a function handle, inline function, or
-string defining the function without the user needing to explicitly
-create the data to be plotted.  The function @code{fplot} also generates
-two-dimensional plots with linear axes using a function name and limits
-for the range of the x-coordinate instead of the x and y data.  For
-example,
+Octave can plot a function from a function handle or string defining the
+function without the user needing to explicitly create the data to be
+plotted.  The function @code{fplot} also generates two-dimensional plots
+with linear axes using a function name and limits for the range of the
+x-coordinate instead of the x and y data.  For example,
 
 @example
 @group
@@ -1891,7 +1890,7 @@
 as an anonymous function, or as a string representing an Octave command.  The
 latter syntax is not recommended since syntax errors will only occur when the
 string is evaluated.
-@xref{Function Handles Anonymous Functions Inline Functions, , Function Handles section}.
+@xref{Function Handles and Anonymous Functions, , Function Handles section}.
 
 This can then be associated with an object either at the object's creation, or
 later with the @code{set} function.  For example,
--- a/doc/interpreter/quad.txi	Tue Jun 09 14:11:13 2020 -0700
+++ b/doc/interpreter/quad.txi	Thu Jun 11 01:22:45 2020 -0400
@@ -140,18 +140,12 @@
 is reasonably accurate (to see why, examine what happens to the result
 if you move the lower bound to 0.1, then 0.01, then 0.001, etc.).
 
-The function @qcode{"f"} can be the string name of a function, a function
-handle, or an inline function.  These options make it quite easy to do
-integration without having to fully define a function in an m-file.  For
-example:
+The function @qcode{"f"} can be the string name of a function or a
+function handle.  These options make it quite easy to do integration
+without having to fully define a function in an m-file.  For example:
 
 @example
 @group
-# Verify integral (x^3) = x^4/4
-f = inline ("x.^3");
-quadgk (f, 0, 1)
-     @result{} 0.25000
-
 # Verify gamma function = (n-1)! for n = 4
 f = @@(x) x.^3 .* exp (-x);
 quadcc (f, 0, Inf)
--- a/doc/interpreter/vectorize.txi	Tue Jun 09 14:11:13 2020 -0700
+++ b/doc/interpreter/vectorize.txi	Thu Jun 11 01:22:45 2020 -0400
@@ -130,11 +130,7 @@
 which exploits the fact that @code{a > 5} produces a boolean index.
 
 Use elementwise vector operators whenever possible to avoid looping
-(operators like @code{.*} and @code{.^}).  @xref{Arithmetic Ops}.  For
-simple inline functions, the @code{vectorize} function can do this
-automatically.
-
-@DOCSTRING(vectorize)
+(operators like @code{.*} and @code{.^}).  @xref{Arithmetic Ops}.
 
 Also exploit broadcasting in these elementwise operators both to avoid
 looping and unnecessary intermediate memory allocations.
--- a/libinterp/corefcn/Cell.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/Cell.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -63,6 +63,23 @@
     }
 }
 
+Cell::Cell (const std::list<std::string>& sl)
+  : Array<octave_value> ()
+{
+  octave_idx_type n = sl.size ();
+
+  if (n > 0)
+    {
+      resize (dim_vector (n, 1));
+
+      octave_value *dst = fortran_vec ();
+      auto p = sl.begin ();
+
+      for (octave_idx_type i = 0; i < n; i++)
+        dst[i] = *p++;
+    }
+}
+
 Cell::Cell (const Array<std::string>& sa)
   : Array<octave_value> (sa.dims ())
 {
--- a/libinterp/corefcn/Cell.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/Cell.h	Thu Jun 11 01:22:45 2020 -0400
@@ -28,6 +28,7 @@
 
 #include "octave-config.h"
 
+#include <list>
 #include <string>
 
 #include "Array.h"
@@ -90,6 +91,8 @@
       }
   }
 
+  Cell (const std::list<std::string>& sl);
+
   Cell (const Array<std::string>& sa);
 
   Cell (const dim_vector& dv, const string_vector& sv, bool trim = false);
--- a/libinterp/corefcn/call-stack.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/call-stack.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -44,9 +44,7 @@
 #include "pager.h"
 #include "parse.h"
 #include "stack-frame.h"
-#include "stack-frame-walker.h"
 #include "syminfo.h"
-#include "syminfo-accumulator.h"
 #include "symrec.h"
 #include "symscope.h"
 #include "variables.h"
@@ -96,7 +94,7 @@
 
     if (! m_cs.empty ())
       {
-        const stack_frame *elt = m_cs[m_curr_frame];
+        const std::shared_ptr<stack_frame> elt = m_cs[m_curr_frame];
         retval = elt->line ();
       }
 
@@ -109,7 +107,7 @@
 
     if (! m_cs.empty ())
       {
-        const stack_frame *elt = m_cs[m_curr_frame];
+        const std::shared_ptr<stack_frame> elt = m_cs[m_curr_frame];
         retval = elt->column ();
       }
 
@@ -124,7 +122,7 @@
 
     if (xframe > 0)
       {
-        const stack_frame *elt = m_cs[xframe];
+        const std::shared_ptr<stack_frame> elt = m_cs[xframe];
 
         octave_function *f = elt->function ();
 
@@ -143,7 +141,7 @@
 
     if (xframe > 0)
       {
-        const stack_frame *elt = m_cs[xframe];
+        const std::shared_ptr<stack_frame> elt = m_cs[xframe];
 
         octave_function *f = elt->function ();
 
@@ -167,7 +165,7 @@
 
     if (xframe > 0)
       {
-        const stack_frame *elt = m_cs[xframe];
+        const std::shared_ptr<stack_frame> elt = m_cs[xframe];
 
         octave_function *f = elt->function ();
 
@@ -183,7 +181,7 @@
     return -1;
   }
 
-  unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void) const
+  unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void)
   {
     // Start at current frame.
 
@@ -191,7 +189,7 @@
 
     if (xframe > 0)
       {
-        const stack_frame *elt = m_cs[xframe];
+        const std::shared_ptr<stack_frame> elt = m_cs[xframe];
 
         octave_function *f = elt->function ();
 
@@ -214,7 +212,7 @@
 
     while (i != 0)
       {
-        const stack_frame *elt = m_cs[i--];
+        const std::shared_ptr<stack_frame> elt = m_cs[i--];
 
         octave_function *f = elt->function ();
 
@@ -240,7 +238,7 @@
 
     while (i != 0)
       {
-        const stack_frame *elt = m_cs[i--];
+        const std::shared_ptr<stack_frame> elt = m_cs[i--];
 
         octave_function *f = elt->function ();
 
@@ -270,7 +268,7 @@
 
     while (i != 0)
       {
-        const stack_frame *elt = m_cs[i--];
+        const std::shared_ptr<stack_frame> elt = m_cs[i--];
 
         octave_function *f = elt->function ();
 
@@ -333,7 +331,7 @@
 
     while (p != m_cs.cbegin ())
       {
-        const stack_frame *elt = *(--p);
+        const std::shared_ptr<stack_frame> elt = *(--p);
 
         octave_function *f = elt->function ();
 
@@ -347,11 +345,12 @@
     return retval;
   }
 
-  stack_frame * call_stack::get_static_link (size_t prev_frame) const
+  std::shared_ptr<stack_frame>
+  call_stack::get_static_link (size_t prev_frame) const
   {
     // FIXME: is there a better way?
 
-    stack_frame *slink = nullptr;
+    std::shared_ptr<stack_frame> slink;
 
     if (m_curr_frame > 0)
       {
@@ -375,16 +374,16 @@
     if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    stack_frame *slink = get_static_link (prev_frame);
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
-    stack_frame *new_frame
-      = new scope_stack_frame (m_evaluator, scope, m_curr_frame, slink);
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, scope, m_curr_frame, slink));
 
     m_cs.push_back (new_frame);
   }
 
-  void call_stack::push (octave_user_function *fcn, unwind_protect *up_frame,
-                         stack_frame *closure_frames)
+  void call_stack::push (octave_user_function *fcn,
+                         const std::shared_ptr<stack_frame>& closure_frames)
   {
     size_t prev_frame = m_curr_frame;
     m_curr_frame = m_cs.size ();
@@ -393,16 +392,17 @@
     if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    stack_frame *slink = get_static_link (prev_frame);
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
-    stack_frame *new_frame
-      = new user_fcn_stack_frame (m_evaluator, fcn, up_frame, m_curr_frame,
-                                  slink, closure_frames);
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, fcn, m_curr_frame, slink,
+                                      closure_frames));
 
     m_cs.push_back (new_frame);
   }
 
-  void call_stack::push (octave_user_script *script, unwind_protect *up_frame)
+  void call_stack::push (octave_user_function *fcn,
+                         const stack_frame::local_vars_map& local_vars)
   {
     size_t prev_frame = m_curr_frame;
     m_curr_frame = m_cs.size ();
@@ -411,11 +411,29 @@
     if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    stack_frame *slink = get_static_link (prev_frame);
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
+
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, fcn, m_curr_frame, slink,
+                                      local_vars));
+
+    m_cs.push_back (new_frame);
+  }
 
-    stack_frame *new_frame
-      = new script_stack_frame (m_evaluator, script, up_frame, m_curr_frame,
-                                slink);
+  void call_stack::push (octave_user_script *script)
+  {
+    size_t prev_frame = m_curr_frame;
+    m_curr_frame = m_cs.size ();
+
+    // m_max_stack_depth should never be less than zero.
+    if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
+      error ("max_stack_depth exceeded");
+
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
+
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, script, m_curr_frame,
+                                      slink));
 
     m_cs.push_back (new_frame);
   }
@@ -429,10 +447,10 @@
     if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    stack_frame *slink = get_static_link (prev_frame);
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
-    stack_frame *new_frame
-      = new compiled_fcn_stack_frame (m_evaluator, fcn, m_curr_frame, slink);
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, fcn, m_curr_frame, slink));
 
     m_cs.push_back (new_frame);
   }
@@ -449,7 +467,7 @@
 
         if (verbose)
           {
-            const stack_frame *elt = m_cs[n];
+            const std::shared_ptr<stack_frame> elt = m_cs[n];
 
             elt->display_stopped_in_message (octave_stdout);
           }
@@ -462,7 +480,7 @@
   {
     size_t user_frame = m_curr_frame;
 
-    stack_frame *frm = m_cs[user_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 ()))
@@ -475,7 +493,7 @@
     return user_frame;
   }
 
-  stack_frame *call_stack::current_user_frame (void) const
+  std::shared_ptr<stack_frame> call_stack::current_user_frame (void) const
   {
     size_t frame = find_current_user_frame ();
 
@@ -501,7 +519,7 @@
         return start;
       }
 
-    stack_frame *frm = m_cs[start];
+    std::shared_ptr<stack_frame> frm = m_cs[start];
 
     if (! (frm && (frm->is_user_fcn_frame ()
                    || frm->is_user_script_frame ()
@@ -605,10 +623,10 @@
       m_curr_frame = 0;
   }
 
-  std::list<stack_frame *>
+  std::list<std::shared_ptr<stack_frame>>
   call_stack::backtrace_frames (octave_idx_type& curr_user_frame) const
   {
-    std::list<stack_frame *> frames;
+    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
@@ -621,7 +639,7 @@
 
     for (size_t n = m_cs.size () - 1; n > 0; n--)
       {
-        stack_frame *frm = m_cs[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 ())
@@ -639,7 +657,7 @@
     return frames;
   }
 
-  std::list<stack_frame *>
+  std::list<std::shared_ptr<stack_frame>>
   call_stack::backtrace_frames (void) const
   {
     octave_idx_type curr_user_frame = -1;
@@ -651,11 +669,12 @@
   call_stack::backtrace_info (octave_idx_type& curr_user_frame,
                               bool print_subfn) const
   {
-    std::list<stack_frame *> frames = backtrace_frames (curr_user_frame);
+    std::list<std::shared_ptr<stack_frame>> frames
+      = backtrace_frames (curr_user_frame);
 
     std::list<frame_info> retval;
 
-    for (const auto *frm : frames)
+    for (const auto& frm : frames)
       {
         if (frm->is_user_script_frame () || frm->is_user_fcn_frame ()
             || frm->is_scope_frame ())
@@ -679,7 +698,8 @@
   octave_map call_stack::backtrace (octave_idx_type& curr_user_frame,
                                     bool print_subfn) const
   {
-    std::list<stack_frame *> frames = backtrace_frames (curr_user_frame);
+    std::list<std::shared_ptr<stack_frame>> frames
+      = backtrace_frames (curr_user_frame);
 
     size_t nframes = frames.size ();
 
@@ -692,7 +712,7 @@
 
     octave_idx_type k = 0;
 
-    for (const auto *frm : frames)
+    for (const auto& frm : frames)
       {
         if (frm->is_user_script_frame () || frm->is_user_fcn_frame ()
             || frm->is_scope_frame ())
@@ -728,15 +748,13 @@
 
     if (m_cs.size () > 1)
       {
-        stack_frame *elt = m_cs.back ();
+        std::shared_ptr<stack_frame> elt = m_cs.back ();
 
-        stack_frame *caller = elt->static_link ();
+        std::shared_ptr<stack_frame> caller = elt->static_link ();
 
         m_curr_frame = caller->index ();
 
         m_cs.pop_back ();
-
-        delete elt;
       }
   }
 
@@ -976,39 +994,9 @@
                                        bool have_regexp, bool return_list,
                                        bool verbose, const std::string& msg)
   {
-    symbol_info_accumulator sym_inf_accum (patterns, have_regexp);
-
-    m_cs[m_curr_frame]->accept (sym_inf_accum);
-
-    if (return_list)
-      {
-        if (verbose)
-          return sym_inf_accum.map_value ();
-        else
-          return Cell (string_vector (sym_inf_accum.names ()));
-      }
-    else if (! sym_inf_accum.is_empty ())
-      {
-
-        if (msg.empty ())
-          octave_stdout << "Variables visible from the current scope:\n";
-        else
-          octave_stdout << msg;
-
-        if (verbose)
-          sym_inf_accum.display (octave_stdout,
-                                 m_evaluator.whos_line_format ());
-        else
-          {
-            octave_stdout << "\n";
-            string_vector names (sym_inf_accum.names ());
-            names.list_in_columns (octave_stdout);
-          }
-
-        octave_stdout << "\n";
-      }
-
-    return octave_value ();
+    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,
--- a/libinterp/corefcn/call-stack.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/call-stack.h	Thu Jun 11 01:22:45 2020 -0400
@@ -29,6 +29,7 @@
 #include "octave-config.h"
 
 #include <deque>
+#include <memory>
 #include <string>
 
 class octave_function;
@@ -55,7 +56,7 @@
   {
   public:
 
-    typedef std::deque<stack_frame *> stack_frames;
+    typedef std::deque<std::shared_ptr<stack_frame>> stack_frames;
 
     typedef stack_frames::iterator iterator;
     typedef stack_frames::const_iterator const_iterator;
@@ -87,14 +88,9 @@
 
     size_t size (void) const { return m_cs.size (); }
 
-    const stack_frame& get_current_stack_frame (void) const
+    std::shared_ptr<stack_frame> get_current_stack_frame (void) const
     {
-      return *(m_cs[m_curr_frame]);
-    }
-
-    stack_frame& get_current_stack_frame (void)
-    {
-      return *(m_cs[m_curr_frame]);
+      return m_cs[m_curr_frame];
     }
 
     symbol_scope top_scope (void) const
@@ -121,10 +117,7 @@
       octave_function *retval = nullptr;
 
       if (m_cs.size () > n)
-        {
-          stack_frame *elt = m_cs[n];
-          retval = elt->function ();
-        }
+        retval = m_cs[n]->function ();
 
       return retval;
     }
@@ -132,7 +125,7 @@
     // User code caller.
     octave_user_code * current_user_code (void) const;
 
-    unwind_protect * curr_fcn_unwind_protect_frame (void) const;
+    unwind_protect * curr_fcn_unwind_protect_frame (void);
 
     // Line in user code caller.
     int current_user_code_line (void) const;
@@ -160,14 +153,17 @@
     // Return TRUE if all elements on the call stack are scripts.
     bool all_scripts (void) const;
 
-    stack_frame * get_static_link (size_t prev_frame) const;
+    std::shared_ptr<stack_frame> get_static_link (size_t prev_frame) const;
 
     void push (const symbol_scope& scope);
 
-    void push (octave_user_function *fcn, unwind_protect *up_frame,
-               stack_frame *closure_frames = nullptr);
+    void push (octave_user_function *fcn,
+               const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
-    void push (octave_user_script *script, unwind_protect *up_frame);
+    void push (octave_user_function *fcn,
+               const stack_frame::local_vars_map& local_vars);
+
+    void push (octave_user_script *script);
 
     void push (octave_function *fcn);
 
@@ -175,7 +171,7 @@
     {
       if (! m_cs.empty ())
         {
-          stack_frame *elt = m_cs.back ();
+          std::shared_ptr<stack_frame> elt = m_cs.back ();
 
           elt->line (l);
           elt->column (c);
@@ -186,7 +182,7 @@
     {
       if (! m_cs.empty ())
         {
-          stack_frame *elt = m_cs.back ();
+          std::shared_ptr<stack_frame> elt = m_cs.back ();
 
           elt->line (l);
         }
@@ -196,7 +192,7 @@
     {
       if (! m_cs.empty ())
         {
-          stack_frame *elt = m_cs.back ();
+          std::shared_ptr<stack_frame> elt = m_cs.back ();
 
           elt->column (c);
         }
@@ -210,7 +206,8 @@
     }
 
     size_t find_current_user_frame (void) const;
-    stack_frame *current_user_frame (void) const;
+
+    std::shared_ptr<stack_frame> current_user_frame (void) const;
 
     size_t dbupdown (size_t start, int n, bool verbose);
     size_t dbupdown (int n = -1, bool verbose = false);
@@ -219,12 +216,12 @@
 
     void goto_base_frame (void);
 
-    std::list<stack_frame *>
+    std::list<std::shared_ptr<stack_frame>>
     backtrace_frames (octave_idx_type& curr_user_frame) const;
 
     // List of raw stack frames.
 
-    std::list<stack_frame *> backtrace_frames (void) const;
+    std::list<std::shared_ptr<stack_frame>> backtrace_frames (void) const;
 
     // List of stack_info objects that can be used in liboctave and
     // stored in the execution_exception object.
--- a/libinterp/corefcn/cellfun.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/cellfun.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -452,7 +452,7 @@
   // more specific function class, so this can result in fewer polymorphic
   // function calls as the function gets called for each value of the array.
   {
-    if (func.is_function_handle ())
+    if (func.is_function_handle () || func.is_inline_function ())
       {
         // We can't check for overloads now.  Is there something else we
         // should be doing instead?
@@ -1156,7 +1156,7 @@
 
       if (! symbol_table_lookup)
         {
-          if (func.is_function_handle ())
+          if (func.is_function_handle () || func.class_name () == "inline")
             {
               // We can't check for overloads now.  Is there something
               // else we should be doing instead?
--- a/libinterp/corefcn/fcn-info.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/fcn-info.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -625,11 +625,8 @@
   }
 
   octave_value
-  fcn_info::fcn_info_rep::xfind (const symbol_scope& search_scope,
-                                 const octave_value_list& args)
+  fcn_info::fcn_info_rep::find_scoped_function (const symbol_scope& search_scope)
   {
-    // Subfunction, local function, or private function.
-
     if (search_scope)
       {
         // Subfunction.
@@ -664,50 +661,80 @@
 
         // Private function.
 
-        std::string dir_name = search_scope.dir_name ();
+        return find_private_function (search_scope.dir_name ());
+      }
+
+    return octave_value ();
+  }
 
-        if (! dir_name.empty ())
+  octave_value
+  fcn_info::fcn_info_rep::find_private_function (const std::string& dir_name)
+  {
+    if (! dir_name.empty ())
+      {
+        auto q = private_functions.find (dir_name);
+
+        if (q == private_functions.end ())
           {
-            auto q = private_functions.find (dir_name);
+            octave_value val = load_private_function (dir_name);
 
-            if (q == private_functions.end ())
+            if (val.is_defined ())
+              return val;
+          }
+        else
+          {
+            octave_value& fval = q->second;
+
+            if (fval.is_defined ())
+              out_of_date_check (fval, "", false);
+
+            if (fval.is_defined ())
+              return fval;
+            else
               {
                 octave_value val = load_private_function (dir_name);
 
                 if (val.is_defined ())
                   return val;
               }
-            else
-              {
-                octave_value& fval = q->second;
-
-                if (fval.is_defined ())
-                  out_of_date_check (fval, "", false);
-
-                if (fval.is_defined ())
-                  return fval;
-                else
-                  {
-                    octave_value val = load_private_function (dir_name);
-
-                    if (val.is_defined ())
-                      return val;
-                  }
-              }
           }
       }
 
-    // Class methods.
+    return octave_value ();
+  }
 
+  octave_value
+  fcn_info::fcn_info_rep::find_method (const octave_value_list& args)
+  {
     if (! args.empty ())
       {
         std::string dispatch_type = get_dispatch_type (args);
 
-        octave_value fcn = find_method (dispatch_type);
+        return find_method (dispatch_type);
+      }
+
+    return octave_value ();
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::xfind (const symbol_scope& search_scope,
+                                 const octave_value_list& args)
+  {
+    // Subfunction, local function, or private function.
 
-        if (fcn.is_defined ())
-          return fcn;
-      }
+    octave_value fcn;
+
+    fcn = find_scoped_function (search_scope);
+
+    if (fcn.is_defined ())
+      return fcn;
+
+    // Class methods.
+
+    fcn = find_method (args);
+
+    if (fcn.is_defined ())
+      return fcn;
 
     // Class constructors.  The class name and function name are the same.
 
@@ -745,7 +772,7 @@
 
     // Autoload?
 
-    octave_value fcn = find_autoload ();
+    fcn = find_autoload ();
 
     if (fcn.is_defined ())
       return fcn;
--- a/libinterp/corefcn/fcn-info.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/fcn-info.h	Thu Jun 11 01:22:45 2020 -0400
@@ -89,8 +89,14 @@
 
       octave_value builtin_find (const symbol_scope& search_scope);
 
+      octave_value find_scoped_function (const symbol_scope& search_scope);
+
+      octave_value find_private_function (const std::string& dir_name);
+
       octave_value find_method (const std::string& dispatch_type);
 
+      octave_value find_method (const octave_value_list& args);
+
       octave_value find_autoload (void);
 
       octave_value find_package (void);
@@ -255,6 +261,16 @@
       return m_rep->builtin_find (search_scope);
     }
 
+    octave_value find_scoped_function (const symbol_scope& search_scope) const
+    {
+      return m_rep->find_scoped_function (search_scope);
+    }
+
+    octave_value find_private_function (const std::string& dir_name) const
+    {
+      return m_rep->find_private_function (dir_name);
+    }
+
     octave_value find_method (const std::string& dispatch_type) const
     {
       return m_rep->find_method (dispatch_type);
--- a/libinterp/corefcn/help.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/help.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -828,30 +828,14 @@
 
   // Find the main function we are in.
   octave::tree_evaluator& tw = interp.get_evaluator ();
-  octave_user_code *parent_fcn = tw.debug_user_code ();
+  octave_user_code *caller = tw.debug_user_code ();
 
-  if (! parent_fcn)
+  if (! caller)
     return ovl (retval);
 
-  // Find the subfunctions of this function.
-  // 1) subfunction_names contains only valid subfunctions
-  // 2) subfunctions contains both nested functions and subfunctions
-  const std::list<std::string> names = parent_fcn->subfunction_names ();
-  const std::map<std::string, octave_value> h = parent_fcn->subfunctions ();
-
-  size_t sz = names.size ();
-  retval.resize (dim_vector (sz, 1));
+  octave::symbol_scope scope = caller->scope ();
 
-  // loop over them.
-  size_t i = 0;
-  for (const auto& nm : names)
-    {
-      std::map<std::string, octave_value>::const_iterator nm_fcn = h.find (nm);
-      if (nm_fcn != h.end ())
-        retval(i++) = octave_value (new octave_fcn_handle (nm_fcn->second, nm));
-    }
-
-  return ovl (retval);
+  return ovl (Cell (scope.localfunctions ()));
 }
 
 /*
--- a/libinterp/corefcn/interpreter-private.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/interpreter-private.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -44,7 +44,7 @@
 #include "load-path.h"
 #include "load-save.h"
 #include "ov.h"
-#include "ov-fcn-inline.h"
+#include "ovl.h"
 #include "pager.h"
 #include "symtab.h"
 
@@ -208,6 +208,9 @@
     return get_function_handle (interp, arg, parameter_names);
   }
 
+  // May return a function handle object, inline function object, or
+  // function object.
+
   octave_value
   get_function_handle (interpreter& interp, const octave_value& arg,
                        const std::list<std::string>& parameter_names)
@@ -228,12 +231,19 @@
         if (fcn.is_defined ())
           return fcn;
 
-        fcn = octave_value (new octave_fcn_inline (fstr, parameter_names));
-
         // Possibly warn here that passing the function body in a
         // character string is discouraged.
 
-        return fcn;
+        octave_value_list args (parameter_names.size () + 1);
+        octave_idx_type i = 0;
+        args(i++) = fstr;
+        for (const auto& pname : parameter_names)
+          args(i++) = pname;
+
+        octave_value_list tmp = interp.feval ("inline", args, 1);
+
+        if (tmp.length () > 0)
+          return tmp(0);
       }
 
     return octave_value ();
--- a/libinterp/corefcn/interpreter.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/interpreter.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -1535,7 +1535,7 @@
       {
         return feval (val.function_value (), args, nargout);
       }
-    else if (val.is_function_handle ())
+    else if (val.is_function_handle () || val.is_inline_function ())
       {
         // This covers function handles, inline functions, and anonymous
         //  functions.
@@ -1586,6 +1586,11 @@
     return feval (f_arg, tmp_args, nargout);
   }
 
+  octave_value interpreter::make_function_handle (const std::string& name)
+  {
+    return m_evaluator.make_fcn_handle (name);
+  }
+
   void interpreter::install_variable (const std::string& name,
                                       const octave_value& value, bool global)
   {
--- a/libinterp/corefcn/interpreter.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/interpreter.h	Thu Jun 11 01:22:45 2020 -0400
@@ -371,6 +371,8 @@
 
     octave_value_list feval (const octave_value_list& args, int nargout = 0);
 
+    octave_value make_function_handle (const std::string& name);
+
     void install_variable (const std::string& name, const octave_value& value,
                            bool global);
 
--- a/libinterp/corefcn/load-path.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/load-path.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -185,46 +185,6 @@
     return retval;
   }
 
-  static std::string find_private_fcn_file (const std::string& dir,
-                                            const std::string& fcn,
-                                            int type)
-  {
-    std::string nm
-      = sys::file_ops::concat (sys::file_ops::concat (dir, "private"), fcn);
-
-    if (type & load_path::OCT_FILE)
-      {
-        std::string fnm = nm + ".oct";
-
-        sys::file_stat fs (fnm);
-
-        if (fs.exists () && fs.is_reg ())
-          return fnm;
-      }
-
-    if (type & load_path::MEX_FILE)
-      {
-        std::string fnm = nm + ".mex";
-
-        sys::file_stat fs (fnm);
-
-        if (fs.exists () && fs.is_reg ())
-          return fnm;
-      }
-
-    if (type & load_path::M_FILE)
-      {
-        std::string fnm = nm + ".m";
-
-        sys::file_stat fs (fnm);
-
-        if (fs.exists () && fs.is_reg ())
-          return fnm;
-      }
-
-    return "";
-  }
-
   // True if a path is contained in a path list separated by path_sep_char
 
   static bool
@@ -1696,9 +1656,8 @@
     std::string retval;
 
     //  update ();
-    std::string canon_dir = sys::canonicalize_file_name (dir);
-
-    const_private_fcn_map_iterator q = private_fcn_map.find (canon_dir);
+
+    const_private_fcn_map_iterator q = private_fcn_map.find (dir);
 
     if (q != private_fcn_map.end ())
       {
@@ -1709,15 +1668,13 @@
         if (p != fcn_file_map.end ())
           {
             std::string fname
-              = sys::file_ops::concat (sys::file_ops::concat (canon_dir, "private"), fcn);
+              = sys::file_ops::concat (sys::file_ops::concat (dir, "private"), fcn);
 
             if (check_file_type (fname, type, p->second, fcn,
                                  "load_path::find_private_fcn"))
               retval = fname;
           }
       }
-    else
-      retval = find_private_fcn_file (canon_dir, fcn, type);
 
     return retval;
   }
--- a/libinterp/corefcn/ls-hdf5.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/ls-hdf5.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -54,6 +54,7 @@
 #include "defun.h"
 #include "error.h"
 #include "errwarn.h"
+#include "interpreter.h"
 #include "interpreter-private.h"
 #include "load-save.h"
 #include "oct-hdf5.h"
@@ -433,6 +434,212 @@
   return type_id;
 }
 
+static herr_t
+load_inline_fcn (hid_t loc_id, const char *name, octave_value& retval)
+{
+#if defined (HAVE_HDF5)
+
+  hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id;
+  hsize_t rank;
+  int slen;
+
+#if defined (HAVE_HDF5_18)
+  group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
+#else
+  group_hid = H5Gopen (loc_id, name);
+#endif
+  if (group_hid < 0) return -1;
+
+#if defined (HAVE_HDF5_18)
+  data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT);
+#else
+  data_hid = H5Dopen (group_hid, "args");
+#endif
+  space_hid = H5Dget_space (data_hid);
+  rank = H5Sget_simple_extent_ndims (space_hid);
+
+  if (rank != 2)
+    {
+      H5Dclose (data_hid);
+      H5Sclose (space_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
+  OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
+
+  H5Sget_simple_extent_dims (space_hid, hdims, maxdims);
+
+  octave_value_list args (hdims[1]+1);
+
+  OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]);
+
+  if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL,
+               octave_H5P_DEFAULT, s1) < 0)
+    {
+      H5Dclose (data_hid);
+      H5Sclose (space_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  H5Dclose (data_hid);
+  H5Sclose (space_hid);
+
+  for (size_t i = 0; i < hdims[1]; i++)
+    args(i+1) = std::string (s1 + i*hdims[0]);
+
+#if defined (HAVE_HDF5_18)
+  data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
+#else
+  data_hid = H5Dopen (group_hid, "nm");
+#endif
+
+  if (data_hid < 0)
+    {
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  type_hid = H5Dget_type (data_hid);
+  type_class_hid = H5Tget_class (type_hid);
+
+  if (type_class_hid != H5T_STRING)
+    {
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  space_hid = H5Dget_space (data_hid);
+  rank = H5Sget_simple_extent_ndims (space_hid);
+
+  if (rank != 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  slen = H5Tget_size (type_hid);
+  if (slen < 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
+
+  // create datatype for (null-terminated) string to read into:
+  st_id = H5Tcopy (H5T_C_S1);
+  H5Tset_size (st_id, slen);
+
+  if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
+               octave_H5P_DEFAULT, nm_tmp) < 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+  H5Tclose (st_id);
+  H5Dclose (data_hid);
+  // NAME is obsolete and unused.
+  // std::string name (nm_tmp);
+
+#if defined (HAVE_HDF5_18)
+  data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT);
+#else
+  data_hid = H5Dopen (group_hid, "iftext");
+#endif
+
+  if (data_hid < 0)
+    {
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  type_hid = H5Dget_type (data_hid);
+  type_class_hid = H5Tget_class (type_hid);
+
+  if (type_class_hid != H5T_STRING)
+    {
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  space_hid = H5Dget_space (data_hid);
+  rank = H5Sget_simple_extent_ndims (space_hid);
+
+  if (rank != 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  slen = H5Tget_size (type_hid);
+  if (slen < 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen);
+
+  // create datatype for (null-terminated) string to read into:
+  st_id = H5Tcopy (H5T_C_S1);
+  H5Tset_size (st_id, slen);
+
+  if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
+               octave_H5P_DEFAULT, iftext_tmp) < 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+  H5Tclose (st_id);
+  H5Dclose (data_hid);
+
+  args(0) = std::string (iftext_tmp);
+
+  octave::interpreter& interp
+    = octave::__get_interpreter__ ("load_inline_fcn");
+
+  octave_value_list tmp = interp.feval ("inline", args, 1);
+
+  if (tmp.length () > 0)
+    {
+      retval = tmp(0);
+      return 1;
+    }
+
+#else
+  octave_unused_parameter (loc_id);
+  octave_unused_parameter (name);
+  octave_unused_parameter (retval);
+
+  warn_load ("hdf5");
+#endif
+
+  return -1;
+}
+
 // This function is designed to be passed to H5Giterate, which calls it
 // on each data item in an HDF5 file.  For the item whose name is NAME in
 // the group GROUP_ID, this function sets dv->tc to an Octave representation
@@ -534,9 +741,16 @@
           H5Tclose (st_id);
           H5Dclose (data_id);
 
-          d->tc = type_info.lookup_type (typ);
+          if (std::string (typ) == "inline function")
+            {
+              retval = load_inline_fcn (subgroup_id, name, d->tc);
+            }
+          else
+            {
+              d->tc = type_info.lookup_type (typ);
 
-          retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1);
+              retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1);
+            }
 
           // check for OCTAVE_GLOBAL attribute:
           d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL");
@@ -545,6 +759,10 @@
         }
       else
         {
+          // It seems that this block only applies to an old list type
+          // and that we shouldn't need to handle the old inline
+          // function type here.
+
           // an HDF5 group is treated as an octave structure by
           // default (since that preserves name information), and an
           // octave list otherwise.
@@ -565,6 +783,10 @@
     }
   else if (info.type == H5G_DATASET && ident_valid)
     {
+      // It seems that this block only applies to an old version of
+      // Octave HDF5 files and that it is probably not important to
+      // handle the old inline function type here.
+
       // For backwards compatibility.
 #if defined (HAVE_HDF5_18)
       data_id = H5Dopen (group_id, name, octave_H5P_DEFAULT);
--- a/libinterp/corefcn/ls-mat5.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/ls-mat5.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -66,12 +66,12 @@
 #include "oct-map.h"
 #include "ov-cell.h"
 #include "ov-class.h"
-#include "ov-fcn-inline.h"
 #include "ov.h"
 #include "ovl.h"
 #include "pager.h"
 #include "parse.h"
 #include "pt-eval.h"
+#include "stack-frame.h"
 #include "sysdep.h"
 #include "unwind-prot.h"
 #include "utils.h"
@@ -862,8 +862,13 @@
         if (ftype == "simple" || ftype == "scopedfunction")
           {
             if (fpath.empty ())
-              // We have a builtin function
-              tc = make_fcn_handle (interp, fname);
+              {
+                octave::tree_evaluator& tw = interp.get_evaluator ();
+
+                // We have a builtin function
+                // XXX FCN_HANDLE: SIMPLE/SCOPED
+                tc = tw.make_fcn_handle (fname);
+              }
             else
               {
                 std::string mroot = m0.contents ("matlabroot").string_value ();
@@ -897,6 +902,7 @@
                                                         "", "", fname);
 
                         if (ov_fcn.is_defined ())
+                          // XXX FCN_HANDLE: SIMPLE/SCOPED
                           tc = octave_value (new octave_fcn_handle (ov_fcn, fname));
                       }
                     else
@@ -923,6 +929,7 @@
                                                         "", "", fname);
 
                         if (ov_fcn.is_defined ())
+                          // XXX FCN_HANDLE: SIMPLE/SCOPED
                           tc = octave_value (new octave_fcn_handle (ov_fcn, fname));
                         else
                           {
@@ -945,6 +952,7 @@
                                                     "", "", fname);
 
                     if (ov_fcn.is_defined ())
+                      // XXX FCN_HANDLE: SIMPLE/SCOPED
                       tc = octave_value (new octave_fcn_handle (ov_fcn, fname));
                     else
                       {
@@ -974,14 +982,7 @@
             tc2 = m2.contents ("MCOS").cell_value ()(1 + off).cell_value ()(1);
             m2 = tc2.scalar_map_value ();
 
-            octave::unwind_protect_safe frame;
-
-            // Set up temporary scope to use for evaluating the text
-            // that defines the anonymous function.
-
-            octave::tree_evaluator& tw = interp.get_evaluator ();
-            tw.push_dummy_scope ("read_mat5_binary_element");
-            frame.add_method (tw, &octave::tree_evaluator::pop_scope);
+            octave::stack_frame::local_vars_map local_vars;
 
             if (m2.nfields () > 0)
               {
@@ -992,10 +993,27 @@
                     std::string key = m2.key (p0);
                     octave_value val = m2.contents (p0);
 
-                    interp.assign (key, val);
+                    local_vars[key] = val;
                   }
               }
 
+            // Set up temporary scope to use for evaluating the text
+            // that defines the anonymous function so that we don't
+            // pick up values of random variables that might be in the
+            // current scope.
+
+            octave::tree_evaluator& tw = interp.get_evaluator ();
+            tw.push_dummy_scope ("read_mat5_binary_element");
+
+            octave::unwind_action act ([&tw] () { tw.pop_scope (); });
+
+            // FIXME: If evaluation of the string gives us an anonymous
+            // function handle object, then why extract the function and
+            // create a new anonymous function object?  Why not just
+            // attach the workspace values to the object returned by
+            // eval_string?  This code is also is duplicated in
+            // anon_fcn_handle::parse_anon_fcn_handle.
+
             int parse_status;
             octave_value anon_fcn_handle
               = interp.eval_string (fname.substr (4), true, parse_status);
@@ -1008,7 +1026,8 @@
             if (! fh)
               error ("load: failed to load anonymous function handle");
 
-            tc = new octave_fcn_handle (fh->fcn_val (), "@<anonymous>");
+            // XXX FCN_HANDLE: ANONYMOUS
+            tc = octave_value (new octave_fcn_handle (fh->fcn_val (), local_vars));
           }
         else
           error ("load: invalid function handle type");
@@ -1175,60 +1194,50 @@
 
         if (isclass)
           {
-            if (classname == "inline")
+            octave::cdef_manager& cdm = interp.get_cdef_manager ();
+
+            if (cdm.find_class (classname, false, true).ok ())
               {
-                // inline is not an object in Octave but rather an
-                // overload of a function handle.  Special case.
-                tc = new octave_fcn_inline (m.contents ("expr")(0).string_value (),
-                                            m.contents ("args")(0).string_value ());
+                tc = m;
+                warning_with_id ("Octave:load:classdef-to-struct",
+                                 "load: classdef element has been converted to a struct");
               }
             else
               {
-                octave::cdef_manager& cdm = interp.get_cdef_manager ();
-
-                if (cdm.find_class (classname, false, true).ok ())
+                octave_class *cls
+                  = new octave_class (m, classname,
+                                      std::list<std::string> ());
+
+                if (cls->reconstruct_exemplar ())
                   {
-                    tc = m;
-                    warning_with_id ("Octave:load:classdef-to-struct",
-                                     "load: classdef element has been converted to a struct");
+
+                    if (! cls->reconstruct_parents ())
+                      warning_with_id ("Octave:load:classdef-object-inheritance",
+                                       "load: unable to reconstruct object inheritance");
+
+                    tc = cls;
+
+                    octave::load_path& lp = interp.get_load_path ();
+
+                    if (lp.find_method (classname, "loadobj") != "")
+                      {
+                        try
+                          {
+                            octave_value_list tmp = octave::feval ("loadobj", tc, 1);
+
+                            tc = tmp(0);
+                          }
+                        catch (const octave::execution_exception&)
+                          {
+                            goto data_read_error;
+                          }
+                      }
                   }
                 else
                   {
-                    octave_class *cls
-                      = new octave_class (m, classname,
-                                          std::list<std::string> ());
-
-                    if (cls->reconstruct_exemplar ())
-                      {
-
-                        if (! cls->reconstruct_parents ())
-                          warning_with_id ("Octave:load:classdef-object-inheritance",
-                                           "load: unable to reconstruct object inheritance");
-
-                        tc = cls;
-
-                        octave::load_path& lp = interp.get_load_path ();
-
-                        if (lp.find_method (classname, "loadobj") != "")
-                          {
-                            try
-                              {
-                                octave_value_list tmp = octave::feval ("loadobj", tc, 1);
-
-                                tc = tmp(0);
-                              }
-                            catch (const octave::execution_exception&)
-                              {
-                                goto data_read_error;
-                              }
-                          }
-                      }
-                    else
-                      {
-                        tc = m;
-                        warning_with_id ("Octave:load:classdef-to-struct",
-                                         "load: element has been converted to a structure");
-                      }
+                    tc = m;
+                    warning_with_id ("Octave:load:classdef-to-struct",
+                                     "load: element has been converted to a structure");
                   }
               }
           }
--- a/libinterp/corefcn/ls-oct-binary.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/ls-oct-binary.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -44,6 +44,7 @@
 #include "defun.h"
 #include "error.h"
 #include "errwarn.h"
+#include "interpreter.h"
 #include "interpreter-private.h"
 #include "load-save.h"
 #include "ls-oct-binary.h"
@@ -56,6 +57,78 @@
 #include "variables.h"
 #include "version.h"
 
+static bool
+load_inline_fcn (std::istream& is, bool swap, octave::mach_info::float_format,
+                 octave_value& retval)
+{
+  int32_t nargs;
+  if (! is.read (reinterpret_cast<char *> (&nargs), 4))
+    return false;
+  if (swap)
+    swap_bytes<4> (&nargs);
+
+  if (nargs < 1)
+    return false;
+  else
+    {
+      int32_t tmp;
+      octave_value_list args (nargs+1);
+      for (int i = 0; i < nargs; i++)
+        {
+          if (! is.read (reinterpret_cast<char *> (&tmp), 4))
+            return false;
+          if (swap)
+            swap_bytes<4> (&tmp);
+
+          OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1);
+          is.read (ctmp, tmp);
+          args(i+1) = std::string (ctmp);
+
+          if (! is)
+            return false;
+        }
+
+      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
+        return false;
+      if (swap)
+        swap_bytes<4> (&tmp);
+
+      OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1);
+      is.read (ctmp1, tmp);
+      // NAME is obsolete and unused.
+      // std::string name (ctmp1);
+
+      if (! is)
+        return false;
+
+      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
+        return false;
+      if (swap)
+        swap_bytes<4> (&tmp);
+
+      OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
+      is.read (ctmp2, tmp);
+
+      if (is)
+        {
+          args(0) = std::string (ctmp2);
+
+          octave::interpreter& interp
+            = octave::__get_interpreter__ ("load_inline_fcn");
+
+          octave_value_list tmp = interp.feval ("inline", args, 1);
+
+          if (tmp.length () > 0)
+            {
+              retval = tmp(0);
+              return true;
+            }
+        }
+    }
+
+  return false;
+}
+
 // Extract one value (scalar, matrix, string, etc.) from stream IS and
 // place it in TC, returning the name of the variable.  If the value
 // is tagged as global in the file, return TRUE in GLOBAL.  If SWAP
@@ -238,6 +311,15 @@
           error ("load: trouble reading binary file '%s'", filename.c_str ());
         s[len] = '\0';
         std::string typ = s;
+
+        if (typ == "inline function")
+          {
+            // Special case for loading old octave_inline_fcn objects.
+            if (! load_inline_fcn (is, swap, fmt, tc))
+              error ("load: trouble reading binary file '%s'", filename.c_str ());
+            return retval;
+          }
+
         tc = type_info.lookup_type (typ);
       }
       break;
--- a/libinterp/corefcn/ls-oct-text.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/ls-oct-text.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -52,6 +52,7 @@
 #include "defun.h"
 #include "error.h"
 #include "errwarn.h"
+#include "interpreter.h"
 #include "interpreter-private.h"
 #include "load-save.h"
 #include "ls-ascii-helper.h"
@@ -235,6 +236,53 @@
 
 #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == (t))
 
+static octave_value
+load_inline_fcn (std::istream& is, const std::string& filename)
+{
+  int nargs;
+  if (extract_keyword (is, "nargs", nargs, true))
+    {
+      std::string name;
+      octave_value_list args (nargs+1);
+      for (int i = 0; i < nargs; i++)
+        {
+          std::string tmp;
+          is >> tmp;
+          args(i+1) = tmp;
+        }
+      is >> name;
+      if (name == "0")
+        name = "";
+
+      skip_preceeding_newline (is);
+
+      std::string buf;
+
+      if (is)
+        {
+
+          // Get a line of text whitespace characters included,
+          // leaving newline in the stream.
+          buf = read_until_newline (is, true);
+        }
+
+      if (is)
+        {
+          args(0) = std::string (buf);
+
+          octave::interpreter& interp
+            = octave::__get_interpreter__ ("load_inline_fcn");
+
+          octave_value_list tmp = interp.feval ("inline", args, 1);
+
+          if (tmp.length () > 0)
+            return tmp(0);
+        }
+    }
+
+  error ("load: trouble reading ascii file '%s'", filename.c_str ());
+}
+
 std::string
 read_text_data (std::istream& is, const std::string& filename, bool& global,
                 octave_value& tc, octave_idx_type count,
@@ -280,6 +328,12 @@
   // Special case for backward compatibility.  A small bit of cruft
   if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array"))
     tc = charMatrix ();
+  else if (SUBSTRING_COMPARE_EQ (typ, 0, 15, "inline function"))
+    {
+      // Special case for loading old octave_inline_fcn objects.
+      tc = load_inline_fcn (is, filename);
+      return name;
+    }
   else
     {
       octave::type_info& type_info
--- a/libinterp/corefcn/module.mk	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/module.mk	Thu Jun 11 01:22:45 2020 -0400
@@ -83,9 +83,7 @@
   %reldir%/sparse-xdiv.h \
   %reldir%/sparse-xpow.h \
   %reldir%/stack-frame.h \
-  %reldir%/stack-frame-walker.h \
   %reldir%/syminfo.h \
-  %reldir%/syminfo-accumulator.h \
   %reldir%/symrec.h \
   %reldir%/symscope.h \
   %reldir%/symtab.h \
--- a/libinterp/corefcn/stack-frame-walker.h	Tue Jun 09 14:11:13 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2019-2020 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 (octave_stack_frame_walker_h)
-#define octave_stack_frame_walker_h 1
-
-#include "octave-config.h"
-
-namespace octave
-{
-  class compiled_fcn_stack_frame;
-  class script_stack_frame;
-  class user_fcn_stack_frame;
-  class scope_stack_frame;
-
-  class stack_frame_walker
-  {
-  protected:
-
-    stack_frame_walker (void) { }
-
-    virtual ~stack_frame_walker (void) = default;
-
-  public:
-
-    // No copying!
-
-    stack_frame_walker (const stack_frame_walker&) = delete;
-
-    stack_frame_walker& operator = (const stack_frame_walker&) = delete;
-
-    virtual void
-    visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0;
-
-    virtual void
-    visit_script_stack_frame (script_stack_frame&) = 0;
-
-    virtual void
-    visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0;
-
-    virtual void
-    visit_scope_stack_frame (scope_stack_frame&) = 0;
-  };
-}
-
-#endif
--- a/libinterp/corefcn/stack-frame.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/stack-frame.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -42,15 +42,506 @@
 #include "parse.h"
 #include "pt-eval.h"
 #include "stack-frame.h"
-#include "stack-frame-walker.h"
 #include "syminfo.h"
-#include "syminfo-accumulator.h"
 #include "symrec.h"
 #include "symscope.h"
 #include "variables.h"
 
 namespace octave
 {
+  class compiled_fcn_stack_frame : public stack_frame
+  {
+  public:
+
+    compiled_fcn_stack_frame (void) = delete;
+
+    compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn,
+                              size_t index,
+                              const std::shared_ptr<stack_frame>& static_link)
+      : stack_frame (tw, index, static_link, static_link->access_link ()),
+        m_fcn (fcn)
+    { }
+
+    compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default;
+
+    compiled_fcn_stack_frame&
+    operator = (const compiled_fcn_stack_frame& elt) = delete;
+
+    ~compiled_fcn_stack_frame (void) = default;
+
+    bool is_compiled_fcn_frame (void) const { return true; }
+
+    symbol_scope get_scope (void) const
+    {
+      return m_static_link->get_scope ();
+    }
+
+    octave_function * function (void) const { return m_fcn; }
+
+    symbol_record lookup_symbol (const std::string& name) const
+    {
+      return m_static_link->lookup_symbol (name);
+    }
+
+    symbol_record insert_symbol (const std::string& name)
+    {
+      return m_static_link->insert_symbol (name);
+    }
+
+    stack_frame::scope_flags scope_flag (const symbol_record& sym) const
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->scope_flag (sym);
+    }
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_static_link->set_auto_fcn_var (avt, val);
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_static_link->get_auto_fcn_var (avt);
+    }
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using stack_frame::varval;
+    using stack_frame::varref;
+
+    octave_value varval (const symbol_record& sym) const
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->varval (sym);
+    }
+
+    octave_value& varref (const symbol_record& sym)
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->varref (sym);
+    }
+
+    void mark_scope (const symbol_record& sym, scope_flags flag)
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      m_static_link->mark_scope (sym, flag);
+    }
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // Compiled function object associated with this stack frame.
+    // Should always be a built-in, .oct or .mex file function and
+    // should always be valid.
+    octave_function *m_fcn;
+  };
+
+  // Scripts have a symbol_scope object to store the set of variables
+  // in the script, but values for those variables are stored in the
+  // stack frame corresponding to the nearest calling function or in
+  // the top-level scope (the evaluation stack frame).
+  //
+  // Accessing values in a scope requires a mapping from the index of
+  // the variable for the script scope to the list of values in the
+  // evaluation frame(s).  The frame offset tells us how many access
+  // links we must follow to find the stack frame that holds the
+  // value.  The value offset is the index into the vector of values
+  // in that stack frame that we should use to find the value.
+  //
+  // Frame and value offsets are set in this stack frame when it is
+  // created using information from the script and enclosing scopes.
+  //
+  // If a script is invoked in a nested function context, the frame
+  // offsets for individual values may be different.  Some may be
+  // accessed from the invoking function and some may come from a
+  // parent function.
+
+  class script_stack_frame : public stack_frame
+  {
+  public:
+
+    script_stack_frame (void) = delete;
+
+    script_stack_frame (tree_evaluator& tw, octave_user_script *script,
+                        size_t index,
+                        const std::shared_ptr<stack_frame>& static_link);
+
+    script_stack_frame (const script_stack_frame& elt) = default;
+
+    script_stack_frame& operator = (const script_stack_frame& elt) = delete;
+
+    ~script_stack_frame (void)
+    {
+      delete m_unwind_protect_frame;
+    }
+
+    bool is_user_script_frame (void) const { return true; }
+
+    static std::shared_ptr<stack_frame>
+    get_access_link (const std::shared_ptr<stack_frame>& static_link);
+
+    static size_t get_num_symbols (octave_user_script *script);
+
+    void set_script_offsets (void);
+
+    void set_script_offsets_internal (const std::map<std::string,
+                                                     symbol_record>& symbols);
+
+    void resize_and_update_script_offsets (const symbol_record& sym);
+
+    symbol_scope get_scope (void) const { return m_script->scope (); }
+
+    octave_function * function (void) const { return m_script; }
+
+    unwind_protect * unwind_protect_frame (void);
+
+    symbol_record lookup_symbol (const std::string& name) const;
+
+    symbol_record insert_symbol (const std::string&);
+
+    size_t size (void) const { return m_lexical_frame_offsets.size (); }
+
+    void resize (size_t size)
+    {
+      m_lexical_frame_offsets.resize (size, 0);
+      m_value_offsets.resize (size, 0);
+    }
+
+    void get_val_offsets_with_insert (const symbol_record& sym,
+                                      size_t& frame_offset,
+                                      size_t& data_offset);
+
+    bool get_val_offsets_internal (const symbol_record& sym,
+                                   size_t& frame_offset,
+                                   size_t& data_offset) const;
+
+    bool get_val_offsets (const symbol_record& sym, size_t& frame_offset,
+                          size_t& data_offset) const;
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_access_link->set_auto_fcn_var (avt, val);
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_access_link->get_auto_fcn_var (avt);
+    }
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using stack_frame::varval;
+    using stack_frame::varref;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // Script object associated with this stack frame.  Should always
+    // be valid.
+    octave_user_script *m_script;
+
+    // The nearest unwind protect frame that was active when this
+    // stack frame was created.  Should always be valid.
+    unwind_protect *m_unwind_protect_frame;
+
+    // Mapping between the symbols in the symbol_scope object of the
+    // script to the stack frame in which the script is executed.  The
+    // frame offsets may be greater than one if the script is executed
+    // in a nested function context.
+
+    std::vector<size_t> m_lexical_frame_offsets;
+    std::vector<size_t> m_value_offsets;
+  };
+
+  // Base class for values and offsets shared by user_fcn and scope
+  // frames.
+
+  class base_value_stack_frame : public stack_frame
+  {
+  public:
+
+    base_value_stack_frame (void) = delete;
+
+    base_value_stack_frame (tree_evaluator& tw, size_t num_symbols,
+                            size_t index,
+                            const std::shared_ptr<stack_frame>& static_link,
+                            const std::shared_ptr<stack_frame>& access_link)
+      : stack_frame (tw, index, static_link, access_link),
+        m_values (num_symbols, octave_value ()),
+        m_flags (num_symbols, LOCAL),
+        m_auto_vars (NUM_AUTO_VARS, octave_value ())
+    { }
+
+    base_value_stack_frame (const base_value_stack_frame& elt) = default;
+
+    base_value_stack_frame&
+    operator = (const base_value_stack_frame& elt) = delete;
+
+    ~base_value_stack_frame (void) = default;
+
+    size_t size (void) const
+    {
+      return m_values.size ();
+    }
+
+    void resize (size_t size)
+    {
+      m_values.resize (size, octave_value ());
+      m_flags.resize (size, LOCAL);
+    }
+
+    stack_frame::scope_flags get_scope_flag (size_t data_offset) const
+    {
+      return m_flags.at (data_offset);
+    }
+
+    void set_scope_flag (size_t data_offset, scope_flags flag)
+    {
+      m_flags.at (data_offset) = flag;
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_auto_vars.at (avt);
+    }
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_auto_vars.at (avt) = val;
+    }
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using stack_frame::varval;
+    using stack_frame::varref;
+
+    octave_value varval (size_t data_offset) const
+    {
+      return m_values.at (data_offset);
+    }
+
+    octave_value& varref (size_t data_offset)
+    {
+      return m_values.at (data_offset);
+    }
+
+    void display (bool follow = true) const;
+
+  protected:
+
+    // Variable values.  This array is indexed by the data_offset
+    // value stored in the symbol_record objects of the scope
+    // associated with this stack frame.
+    std::vector<octave_value> m_values;
+
+    // The type of each variable (local, global, persistent) of each
+    // value.  This array is indexed by the data_offset value stored
+    // in the symbol_record objects of the scope associated with this
+    // stack frame.  Local values are found in the M_VALUES array.
+    // Global values are stored in the tree_evaluator object that contains
+    // the stack frame.  Persistent values are stored in the function
+    // scope corresponding to the stack frame.
+    std::vector<scope_flags> m_flags;
+
+    // A fixed list of Automatic variables created for this function.
+    // The elements of this vector correspond to the auto_var_type
+    // enum.
+    std::vector<octave_value> m_auto_vars;
+  };
+
+  // User-defined functions have a symbol_scope object to store the set
+  // of variables in the function and values are stored in the stack
+  // frame corresponding to the invocation of the function or one of
+  // its parents.  The frame offset tells us how many access links we
+  // must follow to find the stack frame that holds the value.  The
+  // value offset is the index into the vector of values in that stack
+  // frame that we should use to find the value.
+  //
+  // Frame and value offsets are determined when the corresponding
+  // function is parsed.
+
+  class user_fcn_stack_frame : public base_value_stack_frame
+  {
+  public:
+
+    user_fcn_stack_frame (void) = delete;
+
+    user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
+                          size_t index,
+                          const std::shared_ptr<stack_frame>& static_link,
+                          const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ())
+      : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link,
+                                (access_link
+                                 ? access_link
+                                 : get_access_link (fcn, static_link))),
+        m_fcn (fcn), m_unwind_protect_frame (nullptr)
+    { }
+
+    user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
+                          size_t index,
+                          const std::shared_ptr<stack_frame>& static_link,
+                          const local_vars_map& local_vars)
+      : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link,
+                                get_access_link (fcn, static_link)),
+        m_fcn (fcn), m_unwind_protect_frame (nullptr)
+    {
+      // Initialize local variable values.
+
+      for (const auto& nm_ov : local_vars)
+        assign (nm_ov.first, nm_ov.second);
+    }
+
+    user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default;
+
+    user_fcn_stack_frame&
+    operator = (const user_fcn_stack_frame& elt) = delete;
+
+    ~user_fcn_stack_frame (void)
+    {
+      delete m_unwind_protect_frame;
+    }
+
+    bool is_user_fcn_frame (void) const { return true; }
+
+    static std::shared_ptr<stack_frame>
+    get_access_link (octave_user_function *fcn,
+                     const std::shared_ptr<stack_frame>& static_link);
+
+    static size_t get_num_symbols (octave_user_function *fcn)
+    {
+      symbol_scope fcn_scope = fcn->scope ();
+
+      return fcn_scope.num_symbols ();
+    }
+
+    void clear_values (void);
+
+    symbol_scope get_scope (void) const { return m_fcn->scope (); }
+
+    octave_function * function (void) const { return m_fcn; }
+
+    unwind_protect * unwind_protect_frame (void);
+
+    symbol_record lookup_symbol (const std::string& name) const;
+
+    symbol_record insert_symbol (const std::string&);
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using base_value_stack_frame::varval;
+    using base_value_stack_frame::varref;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // User-defined object associated with this stack frame.  Should
+    // always be valid.
+    octave_user_function *m_fcn;
+
+    // The nearest unwind protect frame that was active when this
+    // stack frame was created.  Should always be valid.
+    unwind_protect *m_unwind_protect_frame;
+  };
+
+  // Pure scope stack frames (primarily the top-level workspace) have
+  // a set of variables and values are stored in the stack frame.  All
+  // variable accesses are direct as there are no parent stack frames.
+  //
+  // Value offsets are determined when the corresponding variable is
+  // entered into the symbol_scope object corresponding to the frame.
+
+  class scope_stack_frame : public base_value_stack_frame
+  {
+  public:
+
+    scope_stack_frame (void) = delete;
+
+    scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope,
+                       size_t index,
+                       const std::shared_ptr<stack_frame>& static_link)
+      : base_value_stack_frame (tw, scope.num_symbols (), index,
+                                static_link, nullptr),
+        m_scope (scope)
+    { }
+
+    scope_stack_frame (const scope_stack_frame& elt) = default;
+
+    scope_stack_frame& operator = (const scope_stack_frame& elt) = delete;
+
+    ~scope_stack_frame (void) = default;
+
+    bool is_scope_frame (void) const { return true; }
+
+    symbol_scope get_scope (void) const { return m_scope; }
+
+    symbol_record lookup_symbol (const std::string& name) const
+    {
+      return m_scope.lookup_symbol (name);
+    }
+
+    symbol_record insert_symbol (const std::string&);
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using base_value_stack_frame::varval;
+    using base_value_stack_frame::varref;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // The scope object associated with this stack frame.
+    symbol_scope m_scope;
+  };
+
   // FIXME: There should probably be a display method for the script,
   // fcn, and scope objects and the script and function objects should
   // be responsible for displaying the scopes they contain.
@@ -78,6 +569,35 @@
       }
   }
 
+  class stack_frame_walker
+  {
+  protected:
+
+    stack_frame_walker (void) { }
+
+    virtual ~stack_frame_walker (void) = default;
+
+  public:
+
+    // No copying!
+
+    stack_frame_walker (const stack_frame_walker&) = delete;
+
+    stack_frame_walker& operator = (const stack_frame_walker&) = delete;
+
+    virtual void
+    visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0;
+
+    virtual void
+    visit_script_stack_frame (script_stack_frame&) = 0;
+
+    virtual void
+    visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0;
+
+    virtual void
+    visit_scope_stack_frame (scope_stack_frame&) = 0;
+  };
+
   class symbol_cleaner : public stack_frame_walker
   {
   public:
@@ -112,7 +632,7 @@
       // link for a compiled_fcn_stack_frame be the same as the static
       // link?
 
-      stack_frame *slink = frame.static_link ();
+      std::shared_ptr<stack_frame> slink = frame.static_link ();
 
       if (slink)
         slink->accept (*this);
@@ -120,7 +640,7 @@
 
     void visit_script_stack_frame (script_stack_frame& frame)
     {
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -130,7 +650,7 @@
     {
       clean_frame (frame);
 
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -140,7 +660,7 @@
     {
       clean_frame (frame);
 
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -237,6 +757,309 @@
     std::set<std::string> m_cleared_names;
   };
 
+  class symbol_info_accumulator : public stack_frame_walker
+  {
+  public:
+
+    symbol_info_accumulator (const std::string& pattern,
+                             bool have_regexp = false)
+      : stack_frame_walker (), m_patterns (pattern), m_match_all (false),
+        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
+        m_found_names ()
+    { }
+
+    symbol_info_accumulator (const string_vector& patterns,
+                             bool have_regexp = false)
+      : stack_frame_walker (), m_patterns (patterns), m_match_all (false),
+        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
+        m_found_names ()
+    { }
+
+    symbol_info_accumulator (bool match_all = true, bool first_only = true)
+      : stack_frame_walker (), m_patterns (), m_match_all (match_all),
+        m_first_only (first_only), m_have_regexp (false),
+        m_sym_inf_list (), m_found_names ()
+    { }
+
+    symbol_info_accumulator (const symbol_info_accumulator&) = delete;
+
+    symbol_info_accumulator& operator = (const symbol_info_accumulator&) = delete;
+
+    ~symbol_info_accumulator (void) = default;
+
+    bool is_empty  (void) const
+    {
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          const symbol_info_list& lst = nm_sil.second;
+
+          if (! lst.empty ())
+            return false;
+        }
+
+      return true;
+    }
+
+    std::list<std::string> names (void) const
+    {
+      std::list<std::string> retval;
+
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          const symbol_info_list& lst = nm_sil.second;
+
+          std::list<std::string> nm_list = lst.names ();
+
+          for (const auto& nm : nm_list)
+            retval.push_back (nm);
+        }
+
+      return retval;
+    }
+
+    symbol_info_list symbol_info (void) const
+    {
+      symbol_info_list retval;
+
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          const symbol_info_list& lst = nm_sil.second;
+
+          for (const auto& syminf : lst)
+            retval.push_back (syminf);
+        }
+
+      return retval;
+    }
+
+    octave_map map_value (void) const
+    {
+      octave_map retval;
+
+      // FIXME: is there a better way to concatenate structures?
+
+      size_t n_frames = m_sym_inf_list.size ();
+
+      OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames);
+
+      size_t j = 0;
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          std::string scope_name = nm_sil.first;
+          const symbol_info_list& lst = nm_sil.second;
+
+          map_list[j] = lst.map_value (scope_name, n_frames-j);
+
+          j++;
+        }
+
+      return octave_map::cat (-1, n_frames, map_list);
+    }
+
+    void display (std::ostream& os, const std::string& format) const
+    {
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          os << "\nvariables in scope: " << nm_sil.first << "\n\n";
+
+          const symbol_info_list& lst = nm_sil.second;
+
+          lst.display (os, format);
+        }
+    }
+
+    void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
+    {
+      // This one follows static link always.  Hmm, should the access
+      // link for a compiled_fcn_stack_frame be the same as the static
+      // link?
+
+      std::shared_ptr<stack_frame> slink = frame.static_link ();
+
+      if (slink)
+        slink->accept (*this);
+    }
+
+    void visit_script_stack_frame (script_stack_frame& frame)
+    {
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+    void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
+    {
+      append_list (frame);
+
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+    void visit_scope_stack_frame (scope_stack_frame& frame)
+    {
+      append_list (frame);
+
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+  private:
+
+    typedef std::pair<std::string, symbol_info_list> syminf_list_elt;
+
+    // FIXME: the following is too complex and duplicates too much
+    // code.  Maybe it should be split up so we have separate classes
+    // that do each job that is needed?
+
+    std::list<symbol_record>
+    filter (stack_frame& frame, const std::list<symbol_record>& symbols)
+    {
+      std::list<symbol_record> new_symbols;
+
+      if (m_match_all)
+        {
+          for (const auto& sym : symbols)
+            {
+              if (frame.is_defined (sym))
+                {
+                  std::string name = sym.name ();
+
+                  if (m_first_only
+                      && m_found_names.find (name) != m_found_names.end ())
+                    continue;
+
+                  m_found_names.insert (name);
+
+                  new_symbols.push_back (sym);
+                }
+            }
+        }
+      else if (m_have_regexp)
+        {
+          octave_idx_type npatterns = m_patterns.numel ();
+
+          for (octave_idx_type j = 0; j < npatterns; j++)
+            {
+              std::string pattern = m_patterns[j];
+
+              regexp pat (pattern);
+
+              for (const auto& sym : symbols)
+                {
+                  std::string name = sym.name ();
+
+                  if (pat.is_match (name) && frame.is_defined (sym))
+                    {
+                      if (m_first_only
+                          && m_found_names.find (name) != m_found_names.end ())
+                        continue;
+
+                      m_found_names.insert (name);
+
+                      new_symbols.push_back (sym);
+                    }
+                }
+            }
+        }
+      else
+        {
+          octave_idx_type npatterns = m_patterns.numel ();
+
+          for (octave_idx_type j = 0; j < npatterns; j++)
+            {
+              std::string pattern = m_patterns[j];
+
+              glob_match pat (pattern);
+
+              for (const auto& sym : symbols)
+                {
+                  std::string name = sym.name ();
+
+                  if (pat.match (name) && frame.is_defined (sym))
+                    {
+                      if (m_first_only
+                          && m_found_names.find (name) == m_found_names.end ())
+                        continue;
+
+                      m_found_names.insert (name);
+
+                      new_symbols.push_back (sym);
+                    }
+                }
+            }
+        }
+
+      return new_symbols;
+    }
+
+    void append_list (stack_frame& frame)
+    {
+      symbol_scope scope = frame.get_scope ();
+
+      std::list<symbol_record> symbols = scope.symbol_list ();
+
+      if (m_match_all || ! m_patterns.empty ())
+        symbols = filter (frame, symbols);
+
+      symbol_info_list syminf_list = frame.make_symbol_info_list (symbols);
+
+      m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list));
+    }
+
+    string_vector m_patterns;
+
+    bool m_match_all;
+    bool m_first_only;
+    bool m_have_regexp;
+
+    std::list<std::pair<std::string, symbol_info_list>> m_sym_inf_list;
+
+    std::set<std::string> m_found_names;
+  };
+
+  stack_frame * stack_frame::create (tree_evaluator& tw, octave_function *fcn,
+                                     size_t index,
+                                     const std::shared_ptr<stack_frame>& static_link)
+  {
+    return new compiled_fcn_stack_frame (tw, fcn, index, static_link);
+  }
+
+  stack_frame * stack_frame::create (tree_evaluator& tw,
+                                     octave_user_script *script,
+                                     size_t index,
+                                     const std::shared_ptr<stack_frame>& static_link)
+  {
+    return new script_stack_frame (tw, script, index, static_link);
+  }
+
+  stack_frame * stack_frame::create (tree_evaluator& tw,
+                                     octave_user_function *fcn, size_t index,
+                                     const std::shared_ptr<stack_frame>& static_link,
+                                     const std::shared_ptr<stack_frame>& access_link)
+  {
+    return new user_fcn_stack_frame (tw, fcn, index, static_link, access_link);
+  }
+
+  stack_frame * stack_frame::create (tree_evaluator& tw,
+                                     octave_user_function *fcn, size_t index,
+                                     const std::shared_ptr<stack_frame>& static_link,
+                                     const local_vars_map& local_vars)
+  {
+    return new user_fcn_stack_frame (tw, fcn, index, static_link, local_vars);
+  }
+
+  stack_frame * stack_frame::create (tree_evaluator& tw,
+                                     const symbol_scope& scope, size_t index,
+                                     const std::shared_ptr<stack_frame>& static_link)
+  {
+    return new scope_stack_frame (tw, scope, index, static_link);
+  }
+
   // This function is only implemented for user_fcn stack frames and
   // only called for those objects using unwind_protect and the
   // call_stack::clear_current_frame_values function.  Anything else
@@ -268,6 +1091,45 @@
     return symbol_stats;
   }
 
+  octave_value stack_frame::who (const string_vector& patterns,
+                                 bool have_regexp, bool return_list,
+                                 bool verbose, const std::string& whos_line_fmt,
+                                 const std::string& msg)
+  {
+    symbol_info_accumulator sym_inf_accum (patterns, have_regexp);
+
+    accept (sym_inf_accum);
+
+    if (return_list)
+      {
+        if (verbose)
+          return sym_inf_accum.map_value ();
+        else
+          return Cell (string_vector (sym_inf_accum.names ()));
+      }
+    else if (! sym_inf_accum.is_empty ())
+      {
+
+        if (msg.empty ())
+          octave_stdout << "Variables visible from the current scope:\n";
+        else
+          octave_stdout << msg;
+
+        if (verbose)
+          sym_inf_accum.display (octave_stdout, whos_line_fmt);
+        else
+          {
+            octave_stdout << "\n";
+            string_vector names (sym_inf_accum.names ());
+            names.list_in_columns (octave_stdout);
+          }
+
+        octave_stdout << "\n";
+      }
+
+    return octave_value ();
+  }
+
   // Return first occurrence of variables in current stack frame and any
   // parent frames reachable through access links.
 
@@ -280,6 +1142,41 @@
     return sia.symbol_info ();
   }
 
+  octave_value stack_frame::workspace (void)
+  {
+    std::list<octave_scalar_map> ws_list;
+
+    stack_frame *frame = this;
+
+    while (frame)
+      {
+        octave::symbol_info_list symbols = frame->all_variables ();
+
+        octave_scalar_map ws;
+
+        for (const auto& sym_name : symbols.names ())
+          {
+            octave_value val = symbols.varval (sym_name);
+
+            if (val.is_defined ())
+              ws.assign (sym_name, val);
+          }
+
+        ws_list.push_back (ws);
+
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
+
+    Cell ws_frames (ws_list.size (), 1);
+
+    octave_idx_type i = 0;
+    for (const auto& elt : ws_list)
+      ws_frames(i++) = elt;
+
+    return ws_frames;
+  }
+
   // FIXME: Should this function also find any variables in parent
   // scopes accessible through access_links?
 
@@ -493,14 +1390,14 @@
 
     os << "static link: ";
     if (m_static_link)
-      os << m_static_link;
+      os << m_static_link.get ();
     else
       os << "NULL";
     os << std::endl;
 
     os << "access link: ";
     if (m_access_link)
-      os << m_access_link;
+      os << m_access_link.get ();
     else
       os << "NULL";
     os << std::endl;
@@ -515,7 +1412,7 @@
       return;
 
     os << "FOLLOWING ACCESS LINKS:" << std::endl;
-    const stack_frame *frm = access_link ();
+    std::shared_ptr<stack_frame> frm = access_link ();
     while (frm)
       {
         frm->display (false);
@@ -525,12 +1422,6 @@
       }
   }
 
-  compiled_fcn_stack_frame *
-  compiled_fcn_stack_frame::dup (void) const
-  {
-    return new compiled_fcn_stack_frame (*this);
-  }
-
   void compiled_fcn_stack_frame::display (bool follow) const
   {
     std::ostream& os = octave_stdout;
@@ -549,23 +1440,16 @@
 
   script_stack_frame::script_stack_frame (tree_evaluator& tw,
                                           octave_user_script *script,
-                                          unwind_protect *up_frame,
                                           size_t index,
-                                          stack_frame *static_link)
+                                          const std::shared_ptr<stack_frame>& static_link)
     : stack_frame (tw, index, static_link, get_access_link (static_link)),
-      m_script (script), m_unwind_protect_frame (up_frame),
+      m_script (script), m_unwind_protect_frame (nullptr),
       m_lexical_frame_offsets (get_num_symbols (script), 1),
       m_value_offsets (get_num_symbols (script), 0)
   {
     set_script_offsets ();
   }
 
-  script_stack_frame *
-  script_stack_frame::dup (void) const
-  {
-    return new script_stack_frame (*this);
-  }
-
   size_t script_stack_frame::get_num_symbols (octave_user_script *script)
   {
     symbol_scope script_scope = script->scope ();
@@ -702,15 +1586,13 @@
   // If this is a nested scope, set access_link to nearest parent
   // stack frame that corresponds to the lexical parent of this scope.
 
-  stack_frame *
-  script_stack_frame::get_access_link (stack_frame *static_link)
+  std::shared_ptr<stack_frame>
+  script_stack_frame::get_access_link (const std::shared_ptr<stack_frame>& static_link)
   {
-    stack_frame *alink = nullptr;
-
     // If this script is called from another script, set access
     // link to ultimate parent stack frame.
 
-    alink = static_link;
+    std::shared_ptr<stack_frame> alink = static_link;
 
     while (alink->is_user_script_frame ())
       {
@@ -723,6 +1605,14 @@
     return alink;
   }
 
+  unwind_protect * script_stack_frame::unwind_protect_frame (void)
+  {
+    if (! m_unwind_protect_frame)
+      m_unwind_protect_frame = new unwind_protect ();
+
+    return m_unwind_protect_frame;
+  }
+
   symbol_record script_stack_frame::lookup_symbol (const std::string& name) const
   {
     symbol_scope scope = get_scope ();
@@ -977,7 +1867,10 @@
     const stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (! frame)
       error ("internal error: invalid access link in function call stack");
@@ -1004,7 +1897,10 @@
     const stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (! frame)
       error ("internal error: invalid access link in function call stack");
@@ -1043,7 +1939,10 @@
     stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (data_offset >= frame->size ())
       frame->resize (data_offset+1);
@@ -1080,7 +1979,7 @@
     if (frame_offset > 1)
       error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used");
 
-    stack_frame *frame = access_link ();
+    std::shared_ptr<stack_frame> frame = access_link ();
 
     if (data_offset >= frame->size ())
       frame->resize (data_offset+1);
@@ -1139,20 +2038,14 @@
       }
   }
 
-  user_fcn_stack_frame *
-  user_fcn_stack_frame::dup (void) const
-  {
-    return new user_fcn_stack_frame (*this);
-  }
-
   // If this is a nested scope, set access_link to nearest parent
   // stack frame that corresponds to the lexical parent of this scope.
 
-  stack_frame *
+  std::shared_ptr<stack_frame>
   user_fcn_stack_frame::get_access_link (octave_user_function *fcn,
-                                         stack_frame *static_link)
+                                         const std::shared_ptr<stack_frame>& static_link)
   {
-    stack_frame *alink = nullptr;
+    std::shared_ptr<stack_frame> alink;
 
     symbol_scope fcn_scope = fcn->scope ();
 
@@ -1230,6 +2123,14 @@
       }
   }
 
+  unwind_protect * user_fcn_stack_frame::unwind_protect_frame (void)
+  {
+    if (! m_unwind_protect_frame)
+      m_unwind_protect_frame = new unwind_protect ();
+
+    return m_unwind_protect_frame;
+  }
+
   symbol_record user_fcn_stack_frame::lookup_symbol (const std::string& name) const
   {
     const stack_frame *frame = this;
@@ -1243,7 +2144,8 @@
         if (sym)
           return sym;
 
-        frame = frame->access_link ();
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
       }
 
     return symbol_record ();
@@ -1299,7 +2201,10 @@
     const stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (! frame)
       error ("internal error: invalid access link in function call stack");
@@ -1321,7 +2226,10 @@
     const stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (! frame)
       error ("internal error: invalid access link in function call stack");
@@ -1359,7 +2267,10 @@
     stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (data_offset >= frame->size ())
       frame->resize (data_offset+1);
@@ -1416,12 +2327,6 @@
     sfw.visit_user_fcn_stack_frame (*this);
   }
 
-  scope_stack_frame *
-  scope_stack_frame::dup (void) const
-  {
-    return new scope_stack_frame (*this);
-  }
-
   symbol_record scope_stack_frame::insert_symbol (const std::string& name)
   {
     // There is no access link for scope frames, so there is no other
--- a/libinterp/corefcn/stack-frame.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/stack-frame.h	Thu Jun 11 01:22:45 2020 -0400
@@ -32,6 +32,7 @@
 #include <iosfwd>
 #include <list>
 #include <map>
+#include <memory>
 #include <string>
 
 class octave_value;
@@ -39,8 +40,6 @@
 
 #include "error.h"
 #include "ov-fcn.h"
-#include "ov-fcn.h"
-#include "ov-fcn-handle.h"
 #include "ov-usr-fcn.h"
 #include "syminfo.h"
 #include "symscope.h"
@@ -104,17 +103,14 @@
   class symbol_info_list;
   class unwind_protect;
 
-  class compiled_fcn_stack_frame;
-  class script_stack_frame;
-  class user_fcn_stack_frame;
-  class scope_stack_frame;
-
   class stack_frame_walker;
 
   class stack_frame
   {
   public:
 
+    typedef std::map<std::string, octave_value> local_vars_map;
+
     // Markers indicating the type of a variable.  Values for local
     // variables are stored in the stack frame.  Values for
     // global variables are stored in the tree_evaluator object that
@@ -144,20 +140,46 @@
     stack_frame (void) = delete;
 
     stack_frame (tree_evaluator& tw, size_t index,
-                 stack_frame *static_link, stack_frame *access_link)
+                 const std::shared_ptr<stack_frame>& static_link,
+                 const std::shared_ptr<stack_frame>& access_link)
       : m_evaluator (tw), m_line (-1), m_column (-1), m_index (index),
         m_static_link (static_link), m_access_link (access_link),
         m_dispatch_class ()
     { }
 
+    // Compiled function.
+    static stack_frame *
+    create (tree_evaluator& tw, octave_function *fcn, size_t index,
+            const std::shared_ptr<stack_frame>& static_link);
+
+    // Script.
+    static stack_frame *
+    create (tree_evaluator& tw, octave_user_script *script, size_t index,
+            const std::shared_ptr<stack_frame>& static_link);
+
+    // User-defined function.
+    static stack_frame *
+    create (tree_evaluator& tw, octave_user_function *fcn, size_t index,
+            const std::shared_ptr<stack_frame>& static_link,
+            const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ());
+
+    // Anonymous user-defined function with init vars.
+    static stack_frame *
+    create (tree_evaluator& tw, octave_user_function *fcn, size_t index,
+            const std::shared_ptr<stack_frame>& static_link,
+            const local_vars_map& local_vars);
+
+    // Scope.
+    static stack_frame *
+    create (tree_evaluator& tw, const symbol_scope& scope, size_t index,
+            const std::shared_ptr<stack_frame>& static_link);
+
     stack_frame (const stack_frame& elt) = default;
 
     stack_frame& operator = (const stack_frame& elt) = delete;
 
     virtual ~stack_frame (void) = default;
 
-    virtual stack_frame * dup (void) const = 0;
-
     // FIXME: It would be nice to eliminate these but there are a few
     // places where we still need to know the specific type of the
     // stack frame that we are handling.
@@ -198,7 +220,7 @@
             retval = parent_fcn_name + '>';
 
           if (fcn->is_anonymous_function ())
-            retval += octave_fcn_handle::anonymous;
+            retval += "@<anonymous>";
           else
             retval += fcn->name ();
         }
@@ -212,14 +234,20 @@
 
     virtual octave_function * function (void) const { return nullptr; }
 
-    virtual unwind_protect *
-    unwind_protect_frame (void) const { return nullptr; }
+    virtual unwind_protect * unwind_protect_frame (void) { return nullptr; }
 
     symbol_info_list
     make_symbol_info_list (const std::list<symbol_record>& symrec_list) const;
 
+    octave_value who (const string_vector& patterns, bool have_regexp,
+                      bool return_list, bool verbose,
+                      const std::string& whos_line_fmt,
+                      const std::string& msg);
+
     symbol_info_list all_variables (void);
 
+    octave_value workspace (void);
+
     std::list<std::string> variable_names (void) const;
 
     // Look for named symbol visible from current scope.  Don't
@@ -271,15 +299,11 @@
       mark_global (sym);
     }
 
-    stack_frame * static_link (void) const {return m_static_link; }
-
-    stack_frame * access_link (void) const {return m_access_link; }
+    std::shared_ptr<stack_frame>
+    static_link (void) const {return m_static_link; }
 
-    void set_closure_links (stack_frame *dup_frame)
-    {
-      m_static_link = dup_frame;
-      m_access_link = dup_frame;
-    }
+    std::shared_ptr<stack_frame>
+    access_link (void) const {return m_access_link; }
 
     virtual size_t size (void) const;
 
@@ -534,495 +558,17 @@
 
     // Pointer to the nearest parent frame that contains variable
     // information (script, function, or scope).
-    stack_frame *m_static_link;
+    std::shared_ptr<stack_frame> m_static_link;
 
     // Pointer to the nearest lexical parent frame.  Used to access
     // non-local variables for nested and anonymous functions or as a
     // link to the parent frame in which a script is executed.
-    stack_frame *m_access_link;
+    std::shared_ptr<stack_frame> m_access_link;
 
     // Allow function handles to temporarily store their dispatch class
     // in the call stack.
     std::string m_dispatch_class;
   };
-
-  class compiled_fcn_stack_frame : public stack_frame
-  {
-  public:
-
-    compiled_fcn_stack_frame (void) = delete;
-
-    compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn,
-                              size_t index, stack_frame *static_link)
-      : stack_frame (tw, index, static_link, static_link->access_link ()),
-        m_fcn (fcn)
-    { }
-
-    compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default;
-
-    compiled_fcn_stack_frame&
-    operator = (const compiled_fcn_stack_frame& elt) = delete;
-
-    ~compiled_fcn_stack_frame (void) = default;
-
-    compiled_fcn_stack_frame * dup (void) const;
-
-    bool is_compiled_fcn_frame (void) const { return true; }
-
-    symbol_scope get_scope (void) const
-    {
-      return m_static_link->get_scope ();
-    }
-
-    octave_function * function (void) const { return m_fcn; }
-
-    symbol_record lookup_symbol (const std::string& name) const
-    {
-      return m_static_link->lookup_symbol (name);
-    }
-
-    symbol_record insert_symbol (const std::string& name)
-    {
-      return m_static_link->insert_symbol (name);
-    }
-
-    stack_frame::scope_flags scope_flag (const symbol_record& sym) const
-    {
-      // Look in closest stack frame that contains values (either the
-      // top scope, or a user-defined function or script).
-
-      return m_static_link->scope_flag (sym);
-    }
-
-    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
-    {
-      m_static_link->set_auto_fcn_var (avt, val);
-    }
-
-    octave_value get_auto_fcn_var (auto_var_type avt) const
-    {
-      return m_static_link->get_auto_fcn_var (avt);
-    }
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using stack_frame::varval;
-    using stack_frame::varref;
-
-    octave_value varval (const symbol_record& sym) const
-    {
-      // Look in closest stack frame that contains values (either the
-      // top scope, or a user-defined function or script).
-
-      return m_static_link->varval (sym);
-    }
-
-    octave_value& varref (const symbol_record& sym)
-    {
-      // Look in closest stack frame that contains values (either the
-      // top scope, or a user-defined function or script).
-
-      return m_static_link->varref (sym);
-    }
-
-    void mark_scope (const symbol_record& sym, scope_flags flag)
-    {
-      // Look in closest stack frame that contains values (either the
-      // top scope, or a user-defined function or script).
-
-      m_static_link->mark_scope (sym, flag);
-    }
-
-    void display (bool follow = true) const;
-
-    void accept (stack_frame_walker& sfw);
-
-  private:
-
-    // Compiled function object associated with this stack frame.
-    // Should always be a built-in, .oct or .mex file function and
-    // should always be valid.
-    octave_function *m_fcn;
-  };
-
-  // Scripts have a symbol_scope object to store the set of variables
-  // in the script, but values for those variables are stored in the
-  // stack frame corresponding to the nearest calling function or in
-  // the top-level scope (the evaluation stack frame).
-  //
-  // Accessing values in a scope requires a mapping from the index of
-  // the variable for the script scope to the list of values in the
-  // evaluation frame(s).  The frame offset tells us how many access
-  // links we must follow to find the stack frame that holds the
-  // value.  The value offset is the index into the vector of values
-  // in that stack frame that we should use to find the value.
-  //
-  // Frame and value offsets are set in this stack frame when it is
-  // created using information from the script and enclosing scopes.
-  //
-  // If a script is invoked in a nested function context, the frame
-  // offsets for individual values may be different.  Some may be
-  // accessed from the invoking function and some may come from a
-  // parent function.
-
-  class script_stack_frame : public stack_frame
-  {
-  public:
-
-    script_stack_frame (void) = delete;
-
-    script_stack_frame (tree_evaluator& tw, octave_user_script *script,
-                        unwind_protect *up_frame, size_t index,
-                        stack_frame *static_link);
-
-    script_stack_frame (const script_stack_frame& elt) = default;
-
-    script_stack_frame& operator = (const script_stack_frame& elt) = delete;
-
-    ~script_stack_frame (void) = default;
-
-    script_stack_frame * dup (void) const;
-
-    bool is_user_script_frame (void) const { return true; }
-
-    static stack_frame * get_access_link (stack_frame *static_link);
-
-    static size_t get_num_symbols (octave_user_script *script);
-
-    void set_script_offsets (void);
-
-    void set_script_offsets_internal (const std::map<std::string,
-                                                     symbol_record>& symbols);
-
-    void resize_and_update_script_offsets (const symbol_record& sym);
-
-    symbol_scope get_scope (void) const { return m_script->scope (); }
-
-    octave_function * function (void) const { return m_script; }
-
-    unwind_protect *
-    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
-
-    symbol_record lookup_symbol (const std::string& name) const;
-
-    symbol_record insert_symbol (const std::string&);
-
-    size_t size (void) const { return m_lexical_frame_offsets.size (); }
-
-    void resize (size_t size)
-    {
-      m_lexical_frame_offsets.resize (size, 0);
-      m_value_offsets.resize (size, 0);
-    }
-
-    void get_val_offsets_with_insert (const symbol_record& sym,
-                                      size_t& frame_offset,
-                                      size_t& data_offset);
-
-    bool get_val_offsets_internal (const symbol_record& sym,
-                                   size_t& frame_offset,
-                                   size_t& data_offset) const;
-
-    bool get_val_offsets (const symbol_record& sym, size_t& frame_offset,
-                          size_t& data_offset) const;
-
-    scope_flags scope_flag (const symbol_record& sym) const;
-
-    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
-    {
-      m_access_link->set_auto_fcn_var (avt, val);
-    }
-
-    octave_value get_auto_fcn_var (auto_var_type avt) const
-    {
-      return m_access_link->get_auto_fcn_var (avt);
-    }
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using stack_frame::varval;
-    using stack_frame::varref;
-
-    octave_value varval (const symbol_record& sym) const;
-
-    octave_value& varref (const symbol_record& sym);
-
-    void mark_scope (const symbol_record& sym, scope_flags flag);
-
-    void display (bool follow = true) const;
-
-    void accept (stack_frame_walker& sfw);
-
-  private:
-
-    // Script object associated with this stack frame.  Should always
-    // be valid.
-    octave_user_script *m_script;
-
-    // The nearest unwind protect frame that was active when this
-    // stack frame was created.  Should always be valid.
-    unwind_protect *m_unwind_protect_frame;
-
-    // Mapping between the symbols in the symbol_scope object of the
-    // script to the stack frame in which the script is executed.  The
-    // frame offsets may be greater than one if the script is executed
-    // in a nested function context.
-
-    std::vector<size_t> m_lexical_frame_offsets;
-    std::vector<size_t> m_value_offsets;
-  };
-
-  // Base class for values and offsets shared by user_fcn and scope
-  // frames.
-
-  class base_value_stack_frame : public stack_frame
-  {
-  public:
-
-    base_value_stack_frame (void) = delete;
-
-    base_value_stack_frame (tree_evaluator& tw, size_t num_symbols,
-                            size_t index, stack_frame *static_link,
-                            stack_frame *access_link)
-      : stack_frame (tw, index, static_link, access_link),
-        m_values (num_symbols, octave_value ()),
-        m_flags (num_symbols, LOCAL),
-        m_auto_vars (NUM_AUTO_VARS, octave_value ())
-    { }
-
-    base_value_stack_frame (const base_value_stack_frame& elt) = default;
-
-    base_value_stack_frame&
-    operator = (const base_value_stack_frame& elt) = delete;
-
-    ~base_value_stack_frame (void) = default;
-
-    size_t size (void) const
-    {
-      return m_values.size ();
-    }
-
-    void resize (size_t size)
-    {
-      m_values.resize (size, octave_value ());
-      m_flags.resize (size, LOCAL);
-    }
-
-    stack_frame::scope_flags get_scope_flag (size_t data_offset) const
-    {
-      return m_flags.at (data_offset);
-    }
-
-    void set_scope_flag (size_t data_offset, scope_flags flag)
-    {
-      m_flags.at (data_offset) = flag;
-    }
-
-    octave_value get_auto_fcn_var (auto_var_type avt) const
-    {
-      return m_auto_vars.at (avt);
-    }
-
-    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
-    {
-      m_auto_vars.at (avt) = val;
-    }
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using stack_frame::varval;
-    using stack_frame::varref;
-
-    octave_value varval (size_t data_offset) const
-    {
-      return m_values.at (data_offset);
-    }
-
-    octave_value& varref (size_t data_offset)
-    {
-      return m_values.at (data_offset);
-    }
-
-    void display (bool follow = true) const;
-
-  protected:
-
-    // Variable values.  This array is indexed by the data_offset
-    // value stored in the symbol_record objects of the scope
-    // associated with this stack frame.
-    std::vector<octave_value> m_values;
-
-    // The type of each variable (local, global, persistent) of each
-    // value.  This array is indexed by the data_offset value stored
-    // in the symbol_record objects of the scope associated with this
-    // stack frame.  Local values are found in the M_VALUES array.
-    // Global values are stored in the tree_evaluator object that contains
-    // the stack frame.  Persistent values are stored in the function
-    // scope corresponding to the stack frame.
-    std::vector<scope_flags> m_flags;
-
-    // A fixed list of Automatic variables created for this function.
-    // The elements of this vector correspond to the auto_var_type
-    // enum.
-    std::vector<octave_value> m_auto_vars;
-  };
-
-  // User-defined functions have a symbol_scope object to store the set
-  // of variables in the function and values are stored in the stack
-  // frame corresponding to the invocation of the function or one of
-  // its parents.  The frame offset tells us how many access links we
-  // must follow to find the stack frame that holds the value.  The
-  // value offset is the index into the vector of values in that stack
-  // frame that we should use to find the value.
-  //
-  // Frame and value offsets are determined when the corresponding
-  // function is parsed.
-
-  class user_fcn_stack_frame : public base_value_stack_frame
-  {
-  public:
-
-    user_fcn_stack_frame (void) = delete;
-
-    user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
-                          unwind_protect *up_frame, size_t index,
-                          stack_frame *static_link,
-                          stack_frame *access_link = nullptr)
-      : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link,
-                                (access_link
-                                 ? access_link
-                                 : get_access_link (fcn, static_link))),
-        m_fcn (fcn), m_unwind_protect_frame (up_frame)
-    { }
-
-    user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default;
-
-    user_fcn_stack_frame&
-    operator = (const user_fcn_stack_frame& elt) = delete;
-
-    ~user_fcn_stack_frame (void) = default;
-
-    user_fcn_stack_frame * dup (void) const;
-
-    bool is_user_fcn_frame (void) const { return true; }
-
-    static stack_frame *
-    get_access_link (octave_user_function *fcn, stack_frame *static_link);
-
-    static size_t get_num_symbols (octave_user_function *fcn)
-    {
-      symbol_scope fcn_scope = fcn->scope ();
-
-      return fcn_scope.num_symbols ();
-    }
-
-    void clear_values (void);
-
-    symbol_scope get_scope (void) const { return m_fcn->scope (); }
-
-    octave_function * function (void) const { return m_fcn; }
-
-    unwind_protect *
-    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
-
-    symbol_record lookup_symbol (const std::string& name) const;
-
-    symbol_record insert_symbol (const std::string&);
-
-    scope_flags scope_flag (const symbol_record& sym) const;
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using base_value_stack_frame::varval;
-    using base_value_stack_frame::varref;
-
-    octave_value varval (const symbol_record& sym) const;
-
-    octave_value& varref (const symbol_record& sym);
-
-    void mark_scope (const symbol_record& sym, scope_flags flag);
-
-    void display (bool follow = true) const;
-
-    void accept (stack_frame_walker& sfw);
-
-  private:
-
-    // User-defined object associated with this stack frame.  Should
-    // always be valid.
-    octave_user_function *m_fcn;
-
-    // The nearest unwind protect frame that was active when this
-    // stack frame was created.  Should always be valid.
-    unwind_protect *m_unwind_protect_frame;
-  };
-
-  // Pure scope stack frames (primarily the top-level workspace) have
-  // a set of variables and values are stored in the stack frame.  All
-  // variable accesses are direct as there are no parent stack frames.
-  //
-  // Value offsets are determined when the corresponding variable is
-  // entered into the symbol_scope object corresponding to the frame.
-
-  class scope_stack_frame : public base_value_stack_frame
-  {
-  public:
-
-    scope_stack_frame (void) = delete;
-
-    scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope,
-                       size_t index, stack_frame *static_link)
-      : base_value_stack_frame (tw, scope.num_symbols (), index,
-                                static_link, nullptr),
-        m_scope (scope)
-    { }
-
-    scope_stack_frame (const scope_stack_frame& elt) = default;
-
-    scope_stack_frame& operator = (const scope_stack_frame& elt) = delete;
-
-    ~scope_stack_frame (void) = default;
-
-    scope_stack_frame * dup (void) const;
-
-    bool is_scope_frame (void) const { return true; }
-
-    symbol_scope get_scope (void) const { return m_scope; }
-
-    symbol_record lookup_symbol (const std::string& name) const
-    {
-      return m_scope.lookup_symbol (name);
-    }
-
-    symbol_record insert_symbol (const std::string&);
-
-    scope_flags scope_flag (const symbol_record& sym) const;
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using base_value_stack_frame::varval;
-    using base_value_stack_frame::varref;
-
-    octave_value varval (const symbol_record& sym) const;
-
-    octave_value& varref (const symbol_record& sym);
-
-    void mark_scope (const symbol_record& sym, scope_flags flag);
-
-    void display (bool follow = true) const;
-
-    void accept (stack_frame_walker& sfw);
-
-  private:
-
-    // The scope object associated with this stack frame.
-    symbol_scope m_scope;
-  };
 }
 
 #endif
--- a/libinterp/corefcn/syminfo-accumulator.h	Tue Jun 09 14:11:13 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,307 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2019-2020 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 (octave_syminfo_accumulator_h)
-#define octave_syminfo_accumulator_h 1
-
-#include "octave-config.h"
-
-#include <string>
-
-#include "oct-map.h"
-#include "ov.h"
-#include "stack-frame.h"
-#include "syminfo.h"
-#include "symrec.h"
-
-namespace octave
-{
-  class symbol_info_accumulator : public stack_frame_walker
-  {
-  public:
-
-    symbol_info_accumulator (const std::string& pattern,
-                             bool have_regexp = false)
-      : stack_frame_walker (), m_patterns (pattern), m_match_all (false),
-        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
-        m_found_names ()
-    { }
-
-    symbol_info_accumulator (const string_vector& patterns,
-                             bool have_regexp = false)
-      : stack_frame_walker (), m_patterns (patterns), m_match_all (false),
-        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
-        m_found_names ()
-    { }
-
-    symbol_info_accumulator (bool match_all = true, bool first_only = true)
-      : stack_frame_walker (), m_patterns (), m_match_all (match_all),
-        m_first_only (first_only), m_have_regexp (false),
-        m_sym_inf_list (), m_found_names ()
-    { }
-
-    symbol_info_accumulator (const symbol_info_accumulator&) = delete;
-
-    symbol_info_accumulator& operator = (const symbol_info_accumulator&) = delete;
-
-    ~symbol_info_accumulator (void) = default;
-
-    bool is_empty  (void) const
-    {
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          const symbol_info_list& lst = nm_sil.second;
-
-          if (! lst.empty ())
-            return false;
-        }
-
-      return true;
-    }
-
-    std::list<std::string> names (void) const
-    {
-      std::list<std::string> retval;
-
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          const symbol_info_list& lst = nm_sil.second;
-
-          std::list<std::string> nm_list = lst.names ();
-
-          for (const auto& nm : nm_list)
-            retval.push_back (nm);
-        }
-
-      return retval;
-    }
-
-    symbol_info_list symbol_info (void) const
-    {
-      symbol_info_list retval;
-
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          const symbol_info_list& lst = nm_sil.second;
-
-          for (const auto& syminf : lst)
-            retval.push_back (syminf);
-        }
-
-      return retval;
-    }
-
-    octave_map map_value (void) const
-    {
-      octave_map retval;
-
-      // FIXME: is there a better way to concatenate structures?
-
-      size_t n_frames = m_sym_inf_list.size ();
-
-      OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames);
-
-      size_t j = 0;
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          std::string scope_name = nm_sil.first;
-          const symbol_info_list& lst = nm_sil.second;
-
-          map_list[j] = lst.map_value (scope_name, n_frames-j);
-
-          j++;
-        }
-
-      return octave_map::cat (-1, n_frames, map_list);
-    }
-
-    void display (std::ostream& os, const std::string& format) const
-    {
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          os << "\nvariables in scope: " << nm_sil.first << "\n\n";
-
-          const symbol_info_list& lst = nm_sil.second;
-
-          lst.display (os, format);
-        }
-    }
-
-    void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
-    {
-      // This one follows static link always.  Hmm, should the access
-      // link for a compiled_fcn_stack_frame be the same as the static
-      // link?
-
-      stack_frame *slink = frame.static_link ();
-
-      if (slink)
-        slink->accept (*this);
-    }
-
-    void visit_script_stack_frame (script_stack_frame& frame)
-    {
-      stack_frame *alink = frame.access_link ();
-
-      if (alink)
-        alink->accept (*this);
-    }
-
-    void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
-    {
-      append_list (frame);
-
-      stack_frame *alink = frame.access_link ();
-
-      if (alink)
-        alink->accept (*this);
-    }
-
-    void visit_scope_stack_frame (scope_stack_frame& frame)
-    {
-      append_list (frame);
-
-      stack_frame *alink = frame.access_link ();
-
-      if (alink)
-        alink->accept (*this);
-    }
-
-  private:
-
-    typedef std::pair<std::string, symbol_info_list> syminf_list_elt;
-
-    // FIXME: the following is too complex and duplicates too much
-    // code.  Maybe it should be split up so we have separate classes
-    // that do each job that is needed?
-
-    std::list<symbol_record>
-    filter (stack_frame& frame, const std::list<symbol_record>& symbols)
-    {
-      std::list<symbol_record> new_symbols;
-
-      if (m_match_all)
-        {
-          for (const auto& sym : symbols)
-            {
-              if (frame.is_defined (sym))
-                {
-                  std::string name = sym.name ();
-
-                  if (m_first_only
-                      && m_found_names.find (name) != m_found_names.end ())
-                    continue;
-
-                  m_found_names.insert (name);
-
-                  new_symbols.push_back (sym);
-                }
-            }
-        }
-      else if (m_have_regexp)
-        {
-          octave_idx_type npatterns = m_patterns.numel ();
-
-          for (octave_idx_type j = 0; j < npatterns; j++)
-            {
-              std::string pattern = m_patterns[j];
-
-              regexp pat (pattern);
-
-              for (const auto& sym : symbols)
-                {
-                  std::string name = sym.name ();
-
-                  if (pat.is_match (name) && frame.is_defined (sym))
-                    {
-                      if (m_first_only
-                          && m_found_names.find (name) != m_found_names.end ())
-                        continue;
-
-                      m_found_names.insert (name);
-
-                      new_symbols.push_back (sym);
-                    }
-                }
-            }
-        }
-      else
-        {
-          octave_idx_type npatterns = m_patterns.numel ();
-
-          for (octave_idx_type j = 0; j < npatterns; j++)
-            {
-              std::string pattern = m_patterns[j];
-
-              glob_match pat (pattern);
-
-              for (const auto& sym : symbols)
-                {
-                  std::string name = sym.name ();
-
-                  if (pat.match (name) && frame.is_defined (sym))
-                    {
-                      if (m_first_only
-                          && m_found_names.find (name) == m_found_names.end ())
-                        continue;
-
-                      m_found_names.insert (name);
-
-                      new_symbols.push_back (sym);
-                    }
-                }
-            }
-        }
-
-      return new_symbols;
-    }
-
-    void append_list (stack_frame& frame)
-    {
-      symbol_scope scope = frame.get_scope ();
-
-      std::list<symbol_record> symbols = scope.symbol_list ();
-
-      if (m_match_all || ! m_patterns.empty ())
-        symbols = filter (frame, symbols);
-
-      symbol_info_list syminf_list = frame.make_symbol_info_list (symbols);
-
-      m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list));
-    }
-
-    string_vector m_patterns;
-
-    bool m_match_all;
-    bool m_first_only;
-    bool m_have_regexp;
-
-    std::list<std::pair<std::string, symbol_info_list>> m_sym_inf_list;
-
-    std::set<std::string> m_found_names;
-  };
-}
-
-#endif
--- a/libinterp/corefcn/symscope.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/symscope.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -92,6 +92,43 @@
       return p->second;
   }
 
+  std::list<octave_value> symbol_scope_rep::localfunctions (void) const
+  {
+    std::list<octave_value> retval;
+
+    // Find the subfunctions of this function (which should be the
+    // primary parent function for this scope).
+
+    // 1) m_subfunction_names contains only valid subfunctions
+    // 2) m_subfunctions contains both nested functions and subfunctions
+
+    // loop over them.
+
+    for (const auto& nm : m_subfunction_names)
+      {
+        auto nm_fcn_iter = m_subfunctions.find (nm);
+
+        if (nm_fcn_iter != m_subfunctions.end ())
+          {
+            octave_value ov_fcn = nm_fcn_iter->second;
+            octave_user_code *fcn = ov_fcn.user_code_value ();
+
+            if (! fcn)
+              continue;
+
+            octave::symbol_scope scope = fcn->scope ();
+
+            std::list<std::string> plst = scope.parent_fcn_names ();
+
+            octave_fcn_handle *fh = new octave_fcn_handle (ov_fcn, nm, plst);
+
+            retval.push_back (octave_value (fh));
+          }
+      }
+
+    return retval;
+  }
+
   octave_value
   symbol_scope_rep::dump (void) const
   {
@@ -243,8 +280,14 @@
         m_is_static = true;
       }
 
+    std::list<std::string> plst = parent_fcn_names ();
+    plst.push_front (m_fcn_name);
+
     for (auto& scope_obj : m_children)
-      scope_obj.update_nest ();
+      {
+        scope_obj.cache_parent_fcn_names (plst);
+        scope_obj.update_nest ();
+      }
   }
 
   bool symbol_scope_rep::look_nonlocal (const std::string& name,
@@ -280,4 +323,21 @@
 
     return false;
   }
+
+  std::list<octave_value> symbol_scope::localfunctions (void) const
+  {
+    if (! m_rep)
+      return std::list<octave_value> ();
+
+    if (is_primary_fcn_scope ())
+      return m_rep->localfunctions ();
+
+    std::shared_ptr<symbol_scope_rep> ppsr
+      = m_rep->primary_parent_scope_rep ();
+
+    if (! ppsr)
+      return std::list<octave_value> ();
+
+    return ppsr->localfunctions ();
+  }
 }
--- a/libinterp/corefcn/symscope.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/symscope.h	Thu Jun 11 01:22:45 2020 -0400
@@ -66,10 +66,11 @@
     subfunctions_iterator;
 
     symbol_scope_rep (const std::string& name = "")
-      : m_name (name), m_symbols (), m_subfunctions (),
-        m_persistent_values (), m_code (nullptr), m_fcn_file_name (),
-        m_dir_name (), m_parent (), m_primary_parent (), m_children (),
-        m_nesting_depth (0), m_is_static (false)
+      : m_name (name), m_symbols (), m_subfunctions (), m_persistent_values (),
+        m_code (nullptr), m_fcn_name (), m_parent_fcn_names (),
+        m_fcn_file_name (), m_dir_name (), m_parent (), m_primary_parent (),
+        m_children (), m_nesting_depth (0), m_is_static (false),
+        m_is_primary_fcn_scope (false)
     {
       // All scopes have ans as the first symbol, initially undefined.
 
@@ -126,6 +127,8 @@
       new_sid->m_persistent_values = m_persistent_values;
       new_sid->m_subfunction_names = m_subfunction_names;
       new_sid->m_code = m_code;
+      new_sid->m_fcn_name = m_fcn_name;
+      new_sid->m_parent_fcn_names = m_parent_fcn_names;
       new_sid->m_fcn_file_name = m_fcn_file_name;
       new_sid->m_dir_name = m_dir_name;
       new_sid->m_parent = m_parent;
@@ -133,6 +136,7 @@
       new_sid->m_children = m_children;
       new_sid->m_nesting_depth = m_nesting_depth;
       new_sid->m_is_static = m_is_static;
+      new_sid->m_is_primary_fcn_scope = m_is_primary_fcn_scope;
 
       return new_sid;
     }
@@ -213,6 +217,7 @@
         nm_sf.second.unlock ();
     }
 
+    // Pairs of name, function objects.
     std::map<std::string, octave_value> subfunctions (void) const
     {
       return m_subfunctions;
@@ -240,12 +245,28 @@
       return m_subfunction_names;
     }
 
+    std::list<octave_value> localfunctions (void) const;
+
     octave_value dump (void) const;
 
     std::string name (void) const { return m_name; }
 
     void cache_name (const std::string& name) { m_name = name; }
 
+    std::string fcn_name (void) const { return m_fcn_name; }
+
+    void cache_fcn_name (const std::string& name) { m_fcn_name = name; }
+
+    std::list<std::string> parent_fcn_names (void) const
+    {
+      return m_parent_fcn_names;
+    }
+
+    void cache_parent_fcn_names (const std::list<std::string>& names)
+    {
+      m_parent_fcn_names = names;
+    }
+
     octave_user_code *user_code (void) const { return m_code; }
 
     void set_user_code (octave_user_code *code) { m_code = code; }
@@ -259,11 +280,15 @@
       m_fcn_file_name = name;
     }
 
+    std::string fcn_file_name (void) const { return m_fcn_file_name; }
+
     void cache_dir_name (const std::string& name);
 
-    std::string fcn_file_name (void) const { return m_fcn_file_name; }
+    std::string dir_name (void) const { return m_dir_name; }
 
-    std::string dir_name (void) const { return m_dir_name; }
+    void mark_primary_fcn_scope (void) { m_is_primary_fcn_scope = true; }
+
+    bool is_primary_fcn_scope (void) const { return m_is_primary_fcn_scope; }
 
     bool is_relative (const std::shared_ptr<symbol_scope_rep>& scope) const;
 
@@ -313,6 +338,14 @@
 
     octave_user_code *m_code;
 
+    //! Simple name of the function corresponding to this scope.
+
+    std::string m_fcn_name;
+
+    //! List Simple names of the parent functions corresponding to this scope.
+
+    std::list<std::string> m_parent_fcn_names;
+
     //! The file name associated with m_code.
 
     std::string m_fcn_file_name;
@@ -341,6 +374,9 @@
     //! If true then no variables can be added.
 
     bool m_is_static;
+
+    //! If true, this is the scope of a primary function.
+    bool m_is_primary_fcn_scope;
   };
 
   class symbol_scope
@@ -532,6 +568,9 @@
       return m_rep ? m_rep->subfunction_names () : std::list<std::string> ();
     }
 
+    // List of function handle objects.
+    std::list<octave_value> localfunctions (void) const;
+
     octave_value dump (void) const
     {
       return m_rep ? m_rep->dump () : octave_value ();
@@ -548,6 +587,28 @@
         m_rep->cache_name (name);
     }
 
+    std::string fcn_name (void) const
+    {
+      return m_rep ? m_rep->fcn_name () : "";
+    }
+
+    void cache_fcn_name (const std::string& name)
+    {
+      if (m_rep)
+        m_rep->cache_fcn_name (name);
+    }
+
+    std::list<std::string> parent_fcn_names (void) const
+    {
+      return m_rep ? m_rep->parent_fcn_names () : std::list<std::string> ();
+    }
+
+    void cache_parent_fcn_names (const std::list<std::string>& names)
+    {
+      if (m_rep)
+        m_rep->cache_parent_fcn_names (names);
+    }
+
     octave_user_code * user_code (void) const
     {
       return m_rep ? m_rep->user_code () : nullptr;
@@ -593,6 +654,17 @@
       return m_rep ? m_rep->dir_name () : "";
     }
 
+    void mark_primary_fcn_scope (void)
+    {
+      if (m_rep)
+        m_rep->mark_primary_fcn_scope ();
+    }
+
+    bool is_primary_fcn_scope (void) const
+    {
+      return m_rep ? m_rep->is_primary_fcn_scope () : false;
+    }
+
     bool is_relative (const symbol_scope& scope) const
     {
       return m_rep ? m_rep->is_relative (scope.get_rep ()) : false;
--- a/libinterp/corefcn/symtab.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/symtab.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -71,6 +71,54 @@
     return val.is_defined ();
   }
 
+  octave_value
+  symbol_table::find_scoped_function (const std::string& name,
+                                      const symbol_scope& search_scope)
+  {
+    if (name.empty ())
+      return octave_value ();
+
+    fcn_table_const_iterator p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      return p->second.find_scoped_function (search_scope);
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find_scoped_function (search_scope);
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
+  }
+
+  octave_value
+  symbol_table::find_private_function (const std::string& dir_name,
+                                       const std::string& name)
+  {
+    if (name.empty ())
+      return octave_value ();
+
+    fcn_table_const_iterator p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      return p->second.find_private_function (dir_name);
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find_private_function (dir_name);
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
+  }
+
   // FIXME: this function only finds legacy class methods, not
   // classdef methods.
 
@@ -101,8 +149,19 @@
   {
     fcn_table_const_iterator p = m_fcn_table.find (name);
 
-    return (p != m_fcn_table.end ()
-            ? p->second.find_built_in_function () : octave_value ());
+    if (p != m_fcn_table.end ())
+      return p->second.find_built_in_function ();
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find_built_in_function ();
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
   }
 
   octave_value symbol_table::find_autoload (const std::string& name)
@@ -112,8 +171,19 @@
 
     auto p = m_fcn_table.find (name);
 
-    return (p != m_fcn_table.end ()
-            ? p->second.find_autoload () : octave_value ());
+    if (p != m_fcn_table.end ())
+      return p->second.find_autoload ();
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find_autoload ();
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
   }
 
   octave_value
@@ -222,8 +292,19 @@
 
     auto p = m_fcn_table.find (name);
 
-    return (p != m_fcn_table.end ()
-            ? p->second.find_user_function () : octave_value ());
+    if (p != m_fcn_table.end ())
+      return p->second.find_user_function ();
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find_user_function ();
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
   }
 
   octave_value symbol_table::find_cmdline_function (const std::string& name)
@@ -233,8 +314,19 @@
 
     auto p = m_fcn_table.find (name);
 
-    return (p != m_fcn_table.end ()
-            ? p->second.find_cmdline_function () : octave_value ());
+    if (p != m_fcn_table.end ())
+      return p->second.find_cmdline_function ();
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find_cmdline_function ();
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
   }
 
   void symbol_table::install_cmdline_function (const std::string& name,
--- a/libinterp/corefcn/symtab.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/corefcn/symtab.h	Thu Jun 11 01:22:45 2020 -0400
@@ -72,6 +72,12 @@
 
     bool is_built_in_function_name (const std::string& name);
 
+    octave_value find_scoped_function (const std::string& name,
+                                       const symbol_scope& search_scope);
+
+    octave_value find_private_function (const std::string& dir_name,
+                                        const std::string& name);
+
     // FIXME: this function only finds legacy class methods, not
     // classdef methods.
     octave_value find_method (const std::string& name,
--- a/libinterp/dldfcn/__init_fltk__.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/dldfcn/__init_fltk__.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -2531,7 +2531,8 @@
       toolkit_loaded = true;
 
       octave_value fcn (new octave_builtin (F__fltk_check__));
-      octave_value fcn_handle (new octave_fcn_handle (fcn, "@__fltk_check__"));
+      octave_value fcn_handle (new octave_fcn_handle (fcn));
+
       octave_value_list id = Fadd_input_event_hook (interp, fcn_handle, 1);
 
       fltk->set_input_event_hook_id (id);
--- a/libinterp/octave-value/cdef-class.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/cdef-class.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -63,18 +63,18 @@
 namespace octave
 {
   static octave_value
-  make_fcn_handle (interpreter& interp, const octave_value& fcn,
-                   const std::string& nm)
+  make_fcn_handle (const octave_value& fcn, const std::string& meth_name,
+                   const std::string& class_name)
   {
     octave_value retval;
 
     if (fcn.is_defined ())
       {
-        tree_evaluator& tw = interp.get_evaluator ();
+        // FCN_HANDLE: METHOD
+        octave_fcn_handle *fh
+          = new octave_fcn_handle (fcn, class_name, meth_name);
 
-        symbol_scope curr_scope = tw.get_current_scope ();
-
-        retval = octave_value (new octave_fcn_handle (curr_scope, fcn, nm));
+        retval = octave_value (fh);
       }
 
     return retval;
@@ -981,12 +981,10 @@
 
                     if (mprefix == "get.")
                       get_methods[mname.substr (4)]
-                        = make_fcn_handle (interp, mtd,
-                                           full_class_name + '>' + mname);
+                        = make_fcn_handle (mtd, mname, full_class_name);
                     else if (mprefix == "set.")
                       set_methods[mname.substr (4)]
-                        = make_fcn_handle (interp, mtd,
-                                           full_class_name + '>' + mname);
+                        = make_fcn_handle (mtd, mname, full_class_name);
                     else
                       {
                         cdef_method meth = cdm.make_method (retval, mname, mtd);
--- a/libinterp/octave-value/cdef-manager.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/cdef-manager.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -39,9 +39,7 @@
   {
     octave_value fcn (new octave_builtin (ff, nm));
 
-    octave_value fcn_handle (new octave_fcn_handle (fcn, nm));
-
-    return fcn_handle;
+    return octave_value (new octave_fcn_handle (fcn));
   }
 
   static octave_value_list
--- a/libinterp/octave-value/module.mk	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/module.mk	Thu Jun 11 01:22:45 2020 -0400
@@ -43,7 +43,6 @@
   %reldir%/ov-cx-mat.h \
   %reldir%/ov-dld-fcn.h \
   %reldir%/ov-fcn-handle.h \
-  %reldir%/ov-fcn-inline.h \
   %reldir%/ov-fcn.h \
   %reldir%/ov-float.h \
   %reldir%/ov-flt-complex.h \
@@ -108,7 +107,6 @@
   %reldir%/ov-cx-mat.cc \
   %reldir%/ov-dld-fcn.cc \
   %reldir%/ov-fcn-handle.cc \
-  %reldir%/ov-fcn-inline.cc \
   %reldir%/ov-fcn.cc \
   %reldir%/ov-float.cc \
   %reldir%/ov-flt-complex.cc \
--- a/libinterp/octave-value/ov-base.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-base.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -914,15 +914,6 @@
   return nullptr;
 }
 
-octave_fcn_inline *
-octave_base_value::fcn_inline_value (bool silent)
-{
-  if (! silent)
-    err_wrong_type_arg ("octave_base_value::fcn_inline_value()", type_name ());
-
-  return nullptr;
-}
-
 octave_value_list
 octave_base_value::list_value (void) const
 {
--- a/libinterp/octave-value/ov-base.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-base.h	Thu Jun 11 01:22:45 2020 -0400
@@ -66,7 +66,6 @@
 class octave_user_script;
 class octave_user_code;
 class octave_fcn_handle;
-class octave_fcn_inline;
 class octave_value_list;
 
 enum builtin_type_t
@@ -631,8 +630,6 @@
 
   virtual octave_fcn_handle * fcn_handle_value (bool silent = false);
 
-  virtual octave_fcn_inline * fcn_inline_value (bool silent = false);
-
   virtual octave_value_list list_value (void) const;
 
   virtual octave_value convert_to_str (bool pad = false, bool force = false,
--- a/libinterp/octave-value/ov-builtin.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-builtin.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -43,20 +43,14 @@
                                      "built-in function");
 
 octave_value_list
-octave_builtin::call (octave::tree_evaluator& tw, int nargout,
-                      const octave_value_list& args)
+octave_builtin::execute (octave::tree_evaluator& tw, int nargout,
+                         const octave_value_list& args)
 {
   octave_value_list retval;
 
   if (args.has_magic_colon ())
     error ("invalid use of colon in function argument list");
 
-  octave::unwind_protect frame;
-
-  tw.push_stack_frame (this);
-
-  frame.add_method (tw, &octave::tree_evaluator::pop_stack_frame);
-
   octave::profiler& profiler = tw.get_profiler ();
 
   octave::profiler::enter<octave_builtin> block (profiler, *this);
--- a/libinterp/octave-value/ov-builtin.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-builtin.h	Thu Jun 11 01:22:45 2020 -0400
@@ -96,14 +96,9 @@
 
   bool is_builtin_function (void) const { return true; }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
-
   octave_value_list
-  call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ());
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
 
   octave::jit_type * to_jit (void) const;
 
--- a/libinterp/octave-value/ov-class.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-class.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -28,6 +28,7 @@
 #endif
 
 #include <istream>
+#include <memory>
 #include <ostream>
 
 #include "Array-util.h"
@@ -2028,3 +2029,121 @@
 
   return octave_value();
 }
+
+// The following classes allow us to define "inline" function objects as
+// legacy @class objects (as they appear to be in Matlab) while
+// preserving the is_inline_function and function_value methods that
+// were previously available in the octave_fcn_inline class.  However,
+// inline function objects no longer behave as octave_fcn_handle objects
+// so calling is_function_handle for them no longer returns true.  I see
+// no reasonable way to preserve that behavior.  The goal here is to
+// allow most code that used the old octave_inline_fcn object to
+// continue to work while eliminating the octave_inline_fcn class that
+// was derived from the octave_fcn_handle class.  Making that change
+// appears to be necessary to properly fix function handle behavior and
+// improve Matlab compatibility.  It's unfortunate if this change causes
+// trouble, but I see no better fix.  Ultimately, we should replace all
+// uses of "inline" function objects with anonymous functions.
+
+class octave_inline;
+
+// The following class can be removed once the
+// octave_value::function_value method is removed.
+
+class
+octave_inline_fcn : public octave_function
+{
+public:
+
+  octave_inline_fcn (octave_inline *obj) : m_inline_obj (obj) { }
+
+  // No copying!
+
+  octave_inline_fcn (const octave_inline_fcn& ob) = delete;
+
+  octave_inline_fcn& operator = (const octave_inline_fcn& ob) = delete;
+
+  ~octave_inline_fcn (void) = default;
+
+  // Override default call method because we ultimately use feval to
+  // execute the inline function and that will push a stack frame.
+
+  octave_value_list
+  call (octave::tree_evaluator& tw, int nargout = 0,
+        const octave_value_list& args = octave_value_list ())
+  {
+    return execute (tw, nargout, args);
+  }
+
+  octave_value_list
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
+
+private:
+
+  octave_inline *m_inline_obj;
+};
+
+// Once the octave_inline_fcn class is removed, we should also be able
+// to eliminate the octave_inline class below and replace the
+// octave_value::is_inline_function method with
+//
+// bool octave_value::is_inline_function (void) const
+// {
+//   return class_name () == "inline";
+// }
+
+class
+octave_inline : public octave_class
+{
+public:
+
+  octave_inline (const octave_map& m)
+    : octave_class (m, "inline"), m_fcn_obj (new octave_inline_fcn (this))
+  { }
+
+  octave_inline (const octave_inline&) = default;
+
+  ~octave_inline (void) = default;
+
+  octave_base_value * clone (void) const { return new octave_inline (*this); }
+
+  octave_base_value * empty_clone (void) const
+  {
+    return new octave_inline (octave_map (map_keys ()));
+  }
+
+  bool is_inline_function (void) const { return true; }
+
+  octave_function * function_value (bool)
+  {
+    return m_fcn_obj.get ();
+  }
+
+private:
+
+  std::shared_ptr<octave_inline_fcn> m_fcn_obj;
+};
+
+octave_value_list
+octave_inline_fcn::execute (octave::tree_evaluator& tw, int nargout,
+                            const octave_value_list& args)
+{
+  octave::interpreter& interp = tw.get_interpreter ();
+
+  return interp.feval (octave_value (m_inline_obj, true), args, nargout);
+}
+
+
+DEFUN (__inline_ctor__, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {} __inline_ctor__ (@var{prop_struct})
+Internal function.
+
+Implements final construction for inline objects.
+@end deftypefn */)
+{
+  // Input validation has already been done in input.m.
+
+  return octave_value (new octave_inline (args(0).map_value ()));
+}
--- a/libinterp/octave-value/ov-class.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-class.h	Thu Jun 11 01:22:45 2020 -0400
@@ -59,6 +59,11 @@
       parent_list (), obsolete_copies (0)
   { }
 
+  octave_class (const octave_map& m, const std::string& id)
+    : octave_base_value (), map (m), c_name (id),
+      parent_list (), obsolete_copies (0)
+  { }
+
   octave_class (const octave_map& m, const std::string& id,
                 const std::list<std::string>& plist)
     : octave_base_value (), map (m), c_name (id),
--- a/libinterp/octave-value/ov-classdef.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-classdef.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -389,6 +389,25 @@
   return octave::to_ov (octave::lookup_class (cls));
 }
 
+bool octave_classdef_meta::is_classdef_method (const std::string& cname) const
+{
+  bool retval = false;
+
+  if (object.is_method ())
+    {
+      if (cname.empty ())
+        retval = true;
+      else
+        {
+          octave::cdef_method meth (object);
+
+          return meth.is_defined_in_class (cname);
+        }
+    }
+
+  return retval;
+}
+
 bool octave_classdef_meta::is_classdef_constructor (const std::string& cname) const
 {
   bool retval = false;
@@ -433,8 +452,9 @@
 }
 
 octave_value_list
-octave_classdef_superclass_ref::call (octave::tree_evaluator& tw,
-                                      int nargout, const octave_value_list& idx)
+octave_classdef_superclass_ref::execute (octave::tree_evaluator& tw,
+                                         int nargout,
+                                         const octave_value_list& idx)
 {
   octave_value_list retval;
 
--- a/libinterp/octave-value/ov-classdef.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-classdef.h	Thu Jun 11 01:22:45 2020 -0400
@@ -195,14 +195,18 @@
     return object.meta_subsref (type, idx, nargout);
   }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
+  // Override default call method because we don't push a new stack
+  // frame for this operation on classdef_meta objects.
 
-  octave_value_list call (octave::tree_evaluator&, int nargout,
+  octave_value_list call (octave::tree_evaluator& tw, int nargout,
                           const octave_value_list& args)
   {
+    return execute (tw, nargout, args);
+  }
+
+  octave_value_list execute (octave::tree_evaluator&, int nargout,
+                             const octave_value_list& args)
+  {
     // Emulate ()-type meta subsref
 
     std::list<octave_value_list> idx (1, args);
@@ -214,6 +218,8 @@
   bool accepts_postfix_index (char type) const
   { return object.meta_accepts_postfix_index (type); }
 
+  bool is_classdef_method (const std::string& cname = "") const;
+
   bool is_classdef_constructor (const std::string& cname = "") const;
 
   std::string doc_string (const std::string& meth_name) const;
@@ -243,13 +249,17 @@
 
   octave_function * function_value (bool = false) { return this; }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
+  // Override default call method because we don't push a new stack
+  // frame for this operation on classdef_superclass_ref objects.
 
-  octave_value_list
-  call (octave::tree_evaluator& tw, int nargout, const octave_value_list& idx);
+  octave_value_list call (octave::tree_evaluator& tw, int nargout,
+                          const octave_value_list& args)
+  {
+    return execute (tw, nargout, args);
+  }
+
+  octave_value_list execute (octave::tree_evaluator& tw, int nargout,
+                             const octave_value_list& idx);
 
 private:
 
--- a/libinterp/octave-value/ov-fcn-handle.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-fcn-handle.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -50,6 +50,7 @@
 #include "oct-hdf5.h"
 #include "oct-map.h"
 #include "ov-base.h"
+#include "ov-cell.h"
 #include "ov-fcn-handle.h"
 #include "ov-usr-fcn.h"
 #include "parse.h"
@@ -63,6 +64,7 @@
 #include "pt-misc.h"
 #include "pt-pr-code.h"
 #include "pt-stmt.h"
+#include "stack-frame.h"
 #include "syminfo.h"
 #include "symscope.h"
 #include "unwind-prot.h"
@@ -82,316 +84,2412 @@
 
 const std::string octave_fcn_handle::anonymous ("@<anonymous>");
 
-octave_fcn_handle::octave_fcn_handle (const octave::symbol_scope& scope,
-                                      const std::string& n)
-  : m_fcn (), m_obj (), m_name (n), m_scope (scope), m_is_nested (false),
-    m_closure_frames (nullptr), m_dispatch_class ()
+namespace octave
 {
-  if (! m_name.empty () && m_name[0] == '@')
-    m_name = m_name.substr (1);
-
-  size_t pos = m_name.find ('.');
-
-  if (pos != std::string::npos)
+  class invalid_fcn_handle : public base_fcn_handle
+  {
+  public:
+
+    invalid_fcn_handle (void) : base_fcn_handle ("<invalid>") { }
+
+    invalid_fcn_handle (const invalid_fcn_handle&) = default;
+
+    ~invalid_fcn_handle (void) = default;
+
+    invalid_fcn_handle * clone (void) const
+    {
+      return new invalid_fcn_handle (*this);
+    }
+
+    std::string type (void) const { return "<invalid>"; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+  };
+
+  // Create a handle to an unnamed internal function.  There will be no
+  // way to save and reload it.  See, for example, the F__fltk_check__
+  // function in __init_fltk__.cc.
+
+  class internal_fcn_handle : public base_fcn_handle
+  {
+  public:
+
+    internal_fcn_handle (const octave_value& fcn)
+      : base_fcn_handle ("<internal>"), m_fcn (fcn)
+    { }
+
+    internal_fcn_handle (const internal_fcn_handle&) = default;
+
+    ~internal_fcn_handle (void) = default;
+
+    internal_fcn_handle * clone (void) const
+    {
+      return new internal_fcn_handle (*this);
+    }
+
+    std::string type (void) const { return "<internal>"; }
+
+    bool is_internal (void) const { return true; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    // FIXME: These must go away.  They don't do the right thing for
+    // scoping or overloads.
+    octave_function * function_value (bool = false)
+    {
+      return m_fcn.function_value ();
+    }
+
+    octave_user_function * user_function_value (bool = false)
+    {
+      return m_fcn.user_function_value ();
+    }
+
+    octave_value fcn_val (void) { return m_fcn; }
+
+    // Should be const.
+    octave_scalar_map info (void);
+
+    friend bool is_equal_to (const internal_fcn_handle& fh1,
+                             const internal_fcn_handle& fh2);
+
+  private:
+
+    octave_value m_fcn;
+  };
+
+  class simple_fcn_handle : public base_fcn_handle
+  {
+  public:
+
+    // FIXME: octaveroot is temporary information used when loading
+    // handles.  Can we avoid using it in the constructor?
+
+    simple_fcn_handle (const std::string& name = "",
+                       const std::string& file = "",
+                       const std::string& /*octaveroot*/ = "")
+      : base_fcn_handle (name, file), m_fcn ()
+    { }
+
+    simple_fcn_handle (const octave_value& fcn, const std::string& name)
+      : base_fcn_handle (name), m_fcn (fcn)
     {
-      // If we are looking at
-      //
-      //   obj . meth
-      //
-      // Store the object so that calling METH for OBJ will work even if
-      // it is done outside of the scope whre OBJ was initially defined
-      // or if OBJ is cleared before the method call is made through the
-      // function handle.
-
-      std::string obj_name = m_name.substr (0, pos);
-
-      octave::interpreter& interp
-        = octave::__get_interpreter__ ("octave_fcn_handle::octave_fcn_handle");
-
-      octave_value val = interp.varval (obj_name);
-
-      if (val.is_classdef_object ())
-        m_obj = val;
+      if (m_fcn.is_defined ())
+        {
+          octave_function *fcn = m_fcn.function_value ();
+
+          if (fcn)
+            m_file = fcn->fcn_file_name ();
+        }
+    }
+
+    simple_fcn_handle (const simple_fcn_handle&) = default;
+
+    ~simple_fcn_handle (void) = default;
+
+    simple_fcn_handle * clone (void) const
+    {
+      return new simple_fcn_handle (*this);
     }
-}
-
-octave_fcn_handle::octave_fcn_handle (const octave::symbol_scope& scope,
-                                      const octave_value& f,
-                                      const std::string& n)
-  : m_fcn (f), m_obj (), m_name (n), m_scope (scope), m_is_nested (false),
-    m_closure_frames (nullptr), m_dispatch_class ()
-{
-  octave_user_function *uf = m_fcn.user_function_value (true);
-
-  if (uf && m_name != anonymous)
+
+    std::string type (void) const { return "simple"; }
+
+    bool is_simple (void) const { return true; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    // FIXME: These must go away.  They don't do the right thing for
+    // scoping or overloads.
+    octave_function * function_value (bool);
+
+    octave_user_function * user_function_value (bool);
+
+    octave_value fcn_val (void);
+
+    // Should be const.
+    octave_scalar_map info (void);
+
+    bool save_ascii (std::ostream& os);
+
+    bool load_ascii (std::istream& is);
+
+    bool save_binary (std::ostream& os, bool save_as_floats);
+
+    bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
+
+    bool save_hdf5 (octave_hdf5_id loc_hid, const char *name, bool save_as_floats);
+
+    bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
+                    octave_hdf5_id& type_hid);
+
+    void print_raw (std::ostream& os, bool pr_as_read_syntax,
+                    int current_print_indent_level) const;
+
+    friend bool is_equal_to (const simple_fcn_handle& fh1,
+                             const simple_fcn_handle& fh2);
+
+  private:
+
+    octave_value m_fcn;
+  };
+
+  class scoped_fcn_handle : public base_fcn_handle
+  {
+  public:
+
+    // FIXME: octaveroot is temporary information used when loading
+    // handles.  Can we avoid using it in the constructor?
+
+    scoped_fcn_handle (const std::string& name = "",
+                       const std::string& file = "",
+                       const std::string& /*octaveroot*/ = "")
+      : base_fcn_handle (name, file)
+    { }
+
+    scoped_fcn_handle (const octave_value& fcn, const std::string& name,
+                       const std::list<std::string>& parentage);
+
+    scoped_fcn_handle (const scoped_fcn_handle&) = default;
+
+    ~scoped_fcn_handle (void) = default;
+
+    scoped_fcn_handle * clone (void) const
+    {
+      return new scoped_fcn_handle (*this);
+    }
+
+    std::string type (void) const { return "scopedfunction"; }
+
+    bool is_scoped (void) const { return true; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    // FIXME: These must go away.  They don't do the right thing for
+    // scoping or overloads.
+    octave_function * function_value (bool = false)
+    {
+      return m_fcn.function_value ();
+    }
+
+    octave_user_function * user_function_value (bool = false)
     {
-      octave::symbol_scope uf_scope = uf->scope ();
-
-      if (uf_scope)
-        uf_scope.cache_name (m_name);
+      return m_fcn.user_function_value ();
+    }
+
+    octave_value fcn_val (void) { return m_fcn; }
+
+    // Should be const.
+    octave_scalar_map info (void);
+
+    bool save_ascii (std::ostream& os);
+
+    bool load_ascii (std::istream& is);
+
+    bool save_binary (std::ostream& os, bool save_as_floats);
+
+    bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
+
+    bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
+
+    bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
+                    octave_hdf5_id& type_hid);
+
+    void print_raw (std::ostream&, bool pr_as_read_syntax,
+                    int current_print_indent_level) const;
+
+    friend bool is_equal_to (const scoped_fcn_handle& fh1,
+                             const scoped_fcn_handle& fh2);
+
+  protected:
+
+    void find_function (void);
+
+    // The function we are handling.
+    octave_value m_fcn;
+
+    // List of parent function names.  The first element is the name of
+    // m_fcn.
+    std::list<std::string> m_parentage;
+  };
+
+  class nested_fcn_handle : public base_fcn_handle
+  {
+  public:
+
+    // FIXME: octaveroot is temporary information used when loading
+    // handles.  Can we avoid using it in the constructor?
+
+    nested_fcn_handle (const std::string& name = "",
+                       const std::string& file = "",
+                       const std::string& /*octaveroot*/ = "")
+      : base_fcn_handle (name, file)
+    { }
+
+    nested_fcn_handle (const octave_value& fcn, const std::string& name,
+                       const std::shared_ptr<stack_frame>& closure_frames);
+
+    nested_fcn_handle (const nested_fcn_handle&) = default;
+
+    ~nested_fcn_handle (void) = default;
+
+    nested_fcn_handle * clone (void) const
+    {
+      return new nested_fcn_handle (*this);
     }
 
-  if (uf && uf->is_nested_function () && ! uf->is_subfunction ())
-    m_is_nested = true;
-}
-
-octave_fcn_handle::octave_fcn_handle (const octave_value& f,
-                                      const std::string& n)
-  : m_fcn (f), m_obj (), m_name (n), m_scope (), m_is_nested (false),
-    m_closure_frames (nullptr), m_dispatch_class ()
-{
-  octave_user_function *uf = m_fcn.user_function_value (true);
-
-  if (uf && m_name != anonymous)
+    std::string type (void) const { return "nested"; }
+
+    bool is_nested (void) const { return true; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    // FIXME: These must go away.  They don't do the right thing for
+    // scoping or overloads.
+    octave_function * function_value (bool = false)
+    {
+      return m_fcn.function_value ();
+    }
+
+    octave_user_function * user_function_value (bool = false)
     {
-      octave::symbol_scope uf_scope = uf->scope ();
-
-      if (uf_scope)
-        uf_scope.cache_name (m_name);
+      return m_fcn.user_function_value ();
+    }
+
+    octave_value fcn_val (void) { return m_fcn; }
+
+    octave_value workspace (void) const;
+
+    // Should be const.
+    octave_scalar_map info (void);
+
+    bool save_ascii (std::ostream& os);
+
+    bool load_ascii (std::istream& is);
+
+    bool save_binary (std::ostream& os, bool save_as_floats);
+
+    bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
+
+    bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
+
+    bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
+                    octave_hdf5_id& type_hid);
+
+    void print_raw (std::ostream&, bool pr_as_read_syntax,
+                    int current_print_indent_level) const;
+
+    friend bool is_equal_to (const nested_fcn_handle& fh1,
+                             const nested_fcn_handle& fh2);
+
+  protected:
+
+    // The function we are handling.
+    octave_value m_fcn;
+
+    // Pointer to closure stack frames.
+    std::shared_ptr<stack_frame> m_closure_frames;
+  };
+
+  class class_simple_fcn_handle : public base_fcn_handle
+  {
+  public:
+
+    // FIXME: octaveroot is temporary information used when loading
+    // handles.  Can we avoid using it in the constructor?
+
+    class_simple_fcn_handle (const std::string& name = "",
+                             const std::string& file = "",
+                             const std::string& /*octaveroot*/ = "")
+      : base_fcn_handle (name, file)
+    { }
+
+    // FIXME: is the method name supposed to be just the method name or
+    // also contain the object name?
+
+    class_simple_fcn_handle (const octave_value& fcn,
+                             const std::string& class_nm,
+                             const std::string& meth_nm);
+
+    class_simple_fcn_handle (const octave_value& obj, const octave_value& fcn,
+                             const std::string& class_nm,
+                             const std::string& meth_nm);
+
+    class_simple_fcn_handle (const class_simple_fcn_handle&) = default;
+
+    ~class_simple_fcn_handle (void) = default;
+
+    class_simple_fcn_handle * clone (void) const
+    {
+      return new class_simple_fcn_handle (*this);
     }
 
-  if (uf && uf->is_nested_function () && ! uf->is_subfunction ())
-    m_is_nested = true;
-}
-
-octave_fcn_handle::~octave_fcn_handle (void)
-{
-  if (m_closure_frames)
+    std::string type (void) const { return "classsimple"; }
+
+    bool is_class_simple (void) const { return true; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    // FIXME: These must go away.  They don't do the right thing for
+    // scoping or overloads.
+    octave_function * function_value (bool = false)
+    {
+      return m_fcn.function_value ();
+    }
+
+    octave_user_function * user_function_value (bool = false)
     {
-      while (m_closure_frames->size () > 0)
-        {
-          octave::stack_frame *elt = m_closure_frames->back ();
-
-          delete elt;
-
-          m_closure_frames->pop_back ();
-        }
-
-      delete m_closure_frames;
+      return m_fcn.user_function_value ();
+    }
+
+    octave_value fcn_val (void) { return m_fcn; }
+
+    // Should be const.
+    octave_scalar_map info (void);
+
+    std::string dispatch_class (void) const { return m_dispatch_class; }
+
+    bool save_ascii (std::ostream& os);
+
+    bool load_ascii (std::istream& is);
+
+    bool save_binary (std::ostream& os, bool save_as_floats);
+
+    bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
+
+    bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
+
+    bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
+                    octave_hdf5_id& type_hid);
+
+    void print_raw (std::ostream&, bool pr_as_read_syntax,
+                    int current_print_indent_level) const;
+
+    friend bool is_equal_to (const class_simple_fcn_handle& fh1,
+                             const class_simple_fcn_handle& fh2);
+
+  protected:
+
+    // The object containing the method we are handing.
+    octave_value m_obj;
+
+    // The method we are handling.
+    octave_value m_fcn;
+
+    // Name of the class that m_fcn belongs to.
+    std::string m_dispatch_class;
+  };
+
+  class anonymous_fcn_handle : public base_fcn_handle
+  {
+  public:
+
+    static const std::string anonymous;
+
+    // Setting NAME here is a bit of a kluge to cope with a bad choice
+    // made to append the number of local variables to the @<anonymous>
+    // tag in the binary file format.  See also the save_binary and
+    // load_binary functions.
+
+    anonymous_fcn_handle (const std::string& name = "")
+      : base_fcn_handle (name)
+    { }
+
+    anonymous_fcn_handle (const octave_value& fcn,
+                          const stack_frame::local_vars_map& local_vars);
+
+    anonymous_fcn_handle (const anonymous_fcn_handle&) = default;
+
+    ~anonymous_fcn_handle (void) = default;
+
+    anonymous_fcn_handle * clone (void) const
+    {
+      return new anonymous_fcn_handle (*this);
     }
-}
-
-octave_value_list
-octave_fcn_handle::subsref (const std::string& type,
+
+    std::string type (void) const { return "anonymous"; }
+
+    bool is_anonymous (void) const { return true; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    // FIXME: These must go away.  They don't do the right thing for
+    // scoping or overloads.
+    octave_function * function_value (bool = false)
+    {
+      return m_fcn.function_value ();
+    }
+
+    octave_user_function * user_function_value (bool = false)
+    {
+      return m_fcn.user_function_value ();
+    }
+
+    octave_value fcn_val (void) { return m_fcn; }
+
+    octave_value workspace (void) const;
+
+    // Should be const.
+    octave_scalar_map info (void);
+
+    bool save_ascii (std::ostream& os);
+
+    bool load_ascii (std::istream& is);
+
+    bool save_binary (std::ostream& os, bool save_as_floats);
+
+    bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
+
+    bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
+
+    bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
+                    octave_hdf5_id& type_hid);
+
+    void print_raw (std::ostream&, bool pr_as_read_syntax,
+                    int current_print_indent_level) const;
+
+    // Anonymous function handles are printed without a newline.
+    bool print_as_scalar (void) const { return false; }
+
+    bool parse (const std::string& fcn_text);
+
+    friend bool is_equal_to (const anonymous_fcn_handle& fh1,
+                             const anonymous_fcn_handle& fh2);
+
+  protected:
+
+    // The function we are handling.
+    octave_value m_fcn;
+
+    // List of captured variable values for anonymous fucntions.
+    stack_frame::local_vars_map m_local_vars;
+  };
+
+  const std::string anonymous_fcn_handle::anonymous ("@<anonymous>");
+
+  extern bool is_equal_to (const anonymous_fcn_handle& fh1,
+                           const anonymous_fcn_handle& fh2);
+
+  static void err_invalid_fcn_handle (const std::string& name)
+  {
+    error ("invalid function handle, unable to find function for @%s",
+           name.c_str ());
+  }
+
+  octave_value_list
+  base_fcn_handle::subsref (const std::string& type,
                             const std::list<octave_value_list>& idx,
                             int nargout)
-{
-  octave_value_list retval;
-
-  switch (type[0])
-    {
-    case '(':
-      {
-        int tmp_nargout = (type.length () > 1 && nargout == 0) ? 1 : nargout;
-
-        retval = call (tmp_nargout, idx.front ());
-      }
-      break;
-
-    case '{':
-    case '.':
+  {
+    octave_value_list retval;
+
+    switch (type[0])
       {
-        std::string tnm = type_name ();
-        error ("%s cannot be indexed with %c", tnm.c_str (), type[0]);
-      }
-      break;
-
-    default:
-      panic_impossible ();
-    }
-
-  // FIXME: perhaps there should be an
-  // octave_value_list::next_subsref member function?  See also
-  // octave_builtin::subsref.
-
-  if (idx.size () > 1)
-    retval = retval(0).next_subsref (nargout, type, idx);
-
-  return retval;
-}
-
-static void
-err_invalid_fcn_handle (const std::string& name)
-{
-  error ("%s: invalid function handle", name.c_str ());
-}
-
-octave_value_list
-octave_fcn_handle::call (int nargout, const octave_value_list& args)
-{
-  // FIXME: if m_name has a '.' in the name, lookup first component.  If
-  // it is a classdef meta object, then build TYPE and IDX arguments and
-  // make a subsref call using them.
-
-  octave_value fcn_to_call = m_fcn;
-
-  octave::interpreter& interp
-    = octave::__get_interpreter__ ("octave_fcn_handle::call");
-
-  if (! fcn_to_call.is_defined ())
-    {
-      // The following code is similar to part of
-      // tree_evaluator::visit_index_expression but simpler because it
-      // handles a more restricted case.
-
-      octave::symbol_table& symtab = interp.get_symbol_table ();
-
-      size_t pos = m_name.find ('.');
-
-      if (pos != std::string::npos)
+      case '(':
         {
-          // We can have one of
-          //
-          //   pkg-list . fcn  (args)
-          //   pkg-list . cls . meth (args)
-          //   cls . meth  (args)
-
-          // Evaluate package elements until we find a function,
-          // classdef object, or classdef_meta object that is not a
-          // package.  An object may only appear as the first element,
-          // then it must be followed directly by a function name.
-
-          size_t beg = 0;
-          size_t end = pos;
-
-          std::vector<std::string> idx_elts;
-
-          while (true)
-            {
-              end = m_name.find ('.', beg);
-
-              idx_elts.push_back (m_name.substr (beg, end-beg));
-
-              if (end == std::string::npos)
-                break;
-
-              beg = end+1;
-            }
-
-          size_t n_elts = idx_elts.size ();
-
-          bool have_object = false;
-          octave_value partial_expr_val;
-
-          if (m_obj.is_defined ())
-            {
-              // The first element was already defined elsewhere,
-              // possibly in the scope where the function handle was
-              // created.
-
-              partial_expr_val = m_obj;
-
-              if (m_obj.is_classdef_object ())
-                have_object = true;
-              else
-                err_invalid_fcn_handle (m_name);
-            }
-          else
-            {
-              // Lazy evaluation.  The first element was not known to be
-              // defined as an object in the scope where the handle was
-              // created.  See if there is a definition in the current
-              // scope.
-
-              partial_expr_val = interp.varval (idx_elts[0]);
-            }
-
-          if (partial_expr_val.is_defined ())
-            {
-              if (! partial_expr_val.is_classdef_object () || n_elts != 2)
-                err_invalid_fcn_handle (m_name);
-
-              have_object = true;
-            }
-          else
-            partial_expr_val
-              = symtab.find_function (idx_elts[0], ovl (), m_scope);
-
-          std::string type;
-          std::list<octave_value_list> arg_list;
-
-          for (size_t i = 1; i < n_elts; i++)
-            {
-              if (partial_expr_val.is_package ())
-                {
-                  if (have_object)
+          int tmp_nargout = (type.length () > 1 && nargout == 0) ? 1 : nargout;
+
+          retval = call (tmp_nargout, idx.front ());
+        }
+        break;
+
+      case '{':
+      case '.':
+        error ("function handle cannot be indexed with %c", type[0]);
+        break;
+
+      default:
+        panic_impossible ();
+      }
+
+    // FIXME: perhaps there should be an
+    // octave_value_list::next_subsref member function?  See also
+    // octave_builtin::subsref.
+
+    if (idx.size () > 1)
+      retval = retval(0).next_subsref (nargout, type, idx);
+
+    return retval;
+  }
+
+  octave_value
+  base_fcn_handle::convert_to_str_internal (bool, bool, char type) const
+  {
+    std::ostringstream buf;
+    print_raw (buf, true, 0);
+    return octave_value (buf.str (), type);
+  }
+
+  bool
+  base_fcn_handle::save_ascii (std::ostream&)
+  {
+    unimplemented ("save", "text");
+
+    return true;
+  }
+
+  bool
+  base_fcn_handle::load_ascii (std::istream&)
+  {
+    unimplemented ("load", "text");
+
+    return true;
+  }
+
+  bool
+  base_fcn_handle::save_binary (std::ostream&, bool)
+  {
+    unimplemented ("save", "binary");
+
+    return true;
+  }
+
+  bool
+  base_fcn_handle::load_binary (std::istream&, bool, mach_info::float_format)
+  {
+    unimplemented ("load", "binary");
+
+    return true;
+  }
+
+  bool
+  base_fcn_handle::save_hdf5 (octave_hdf5_id, const char *, bool)
+  {
+    unimplemented ("save", "hdf5");
+
+    return true;
+  }
+
+  bool
+  base_fcn_handle::load_hdf5 (octave_hdf5_id&, octave_hdf5_id&, octave_hdf5_id&)
+  {
+    unimplemented ("load", "hdf5");
+
+    return true;
+  }
+
+  void base_fcn_handle::warn_load (const char *file_type) const
+  {
+    std::string obj_type = type ();
+
+    warning_with_id
+      ("Octave:load-save-unavailable",
+       "%s: loading %s files not available in this version of Octave",
+       obj_type.c_str (), file_type);
+  }
+
+  void base_fcn_handle::warn_save (const char *file_type) const
+  {
+    std::string obj_type = type ();
+
+    warning_with_id
+      ("Octave:load-save-unavailable",
+       "%s: saving %s files not available in this version of Octave",
+       obj_type.c_str (), file_type);
+  }
+
+  void base_fcn_handle::unimplemented (const char *op, const char *fmt) const
+  {
+    std::string htype = type ();
+
+    warning ("%s for %s handles with %s format is not implemented",
+             op, htype.c_str (), fmt);
+  }
+
+  octave_value_list
+  invalid_fcn_handle::call (int, const octave_value_list&)
+  {
+    error ("invalid call to invalid function handle");
+  }
+
+  octave_value_list
+  internal_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    interpreter& interp = __get_interpreter__ ("internal_fcn_handle::call");
+
+    return interp.feval (m_fcn, args, nargout);
+  }
+
+  octave_scalar_map internal_fcn_handle::info (void)
+  {
+    octave_scalar_map m;
+
+    m.setfield ("function", fcn_name ());
+    m.setfield ("type", type ());
+    m.setfield ("file", "");
+
+    return m;
+  }
+
+  bool is_equal_to (const internal_fcn_handle& fh1,
+                    const internal_fcn_handle& fh2)
+  {
+    if (fh1.m_name == fh2.m_name
+        && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
+      return fh1.m_fcn.is_copy_of (fh2.m_fcn);
+    else
+      return false;
+  }
+
+  octave_value_list
+  simple_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    // FIXME: if m_name has a '.' in the name, lookup first component.  If
+    // it is a classdef meta object, then build TYPE and IDX arguments and
+    // make a subsref call using them.
+
+    interpreter& interp = __get_interpreter__ ("simple_fcn_handle::call");
+
+    octave_value fcn_to_call;
+
+    // The following code is similar to part of
+    // tree_evaluator::visit_index_expression but simpler because it
+    // handles a more restricted case.
+
+    symbol_table& symtab = interp.get_symbol_table ();
+
+    size_t pos = m_name.find ('.');
+
+    if (pos != std::string::npos)
+      {
+        // FIXME: check to see which of these cases actually work in
+        // Octave and Matlab.  For the last two, assume handle is
+        // created before object is defined as an object.
+        //
+        // We can have one of
+        //
+        //   pkg-list . fcn  (args)
+        //   pkg-list . cls . meth (args)
+        //   class-name . method  (args)
+        //   class-name . static-method  (args)
+        //   object . method  (args)
+        //   object . static-method  (args)
+
+        // Evaluate package elements until we find a function,
+        // classdef object, or classdef_meta object that is not a
+        // package.  An object may only appear as the first element,
+        // then it must be followed directly by a function name.
+
+        size_t beg = 0;
+        size_t end = pos;
+
+        std::vector<std::string> idx_elts;
+
+        while (true)
+          {
+            end = m_name.find ('.', beg);
+
+            idx_elts.push_back (m_name.substr (beg, end-beg));
+
+            if (end == std::string::npos)
+              break;
+
+            beg = end+1;
+          }
+
+        size_t n_elts = idx_elts.size ();
+
+        bool have_object = false;
+        octave_value partial_expr_val;
+
+        // Lazy evaluation.  The first element was not known to be defined
+        // as an object in the scope where the handle was created.  See if
+        // there is a definition in the current scope.
+
+        partial_expr_val = interp.varval (idx_elts[0]);
+
+        if (partial_expr_val.is_defined ())
+          {
+            if (! partial_expr_val.is_classdef_object () || n_elts != 2)
+              err_invalid_fcn_handle (m_name);
+
+            have_object = true;
+          }
+        else
+          partial_expr_val = symtab.find_function (idx_elts[0], ovl ());
+
+        std::string type;
+        std::list<octave_value_list> arg_list;
+
+        for (size_t i = 1; i < n_elts; i++)
+          {
+            if (partial_expr_val.is_package ())
+              {
+                if (have_object)
+                  err_invalid_fcn_handle (m_name);
+
+                type = ".";
+                arg_list.push_back (ovl (idx_elts[i]));
+
+                try
+                  {
+                    // Silently ignore extra output values.
+
+                    octave_value_list tmp_list
+                      = partial_expr_val.subsref (type, arg_list, 0);
+
+                    partial_expr_val
+                      = tmp_list.length () ? tmp_list(0) : octave_value ();
+
+                    if (partial_expr_val.is_cs_list ())
+                      err_invalid_fcn_handle (m_name);
+
+                    arg_list.clear ();
+                  }
+                catch (index_exception&)
+                  {
                     err_invalid_fcn_handle (m_name);
-
-                  type = ".";
-                  arg_list.push_back (ovl (idx_elts[i]));
-
-                  try
-                    {
-                      // Silently ignore extra output values.
-
-                      octave_value_list tmp_list
-                        = partial_expr_val.subsref (type, arg_list, 0);
-
-                      partial_expr_val
-                        = tmp_list.length () ? tmp_list(0) : octave_value ();
-
-                      if (partial_expr_val.is_cs_list ())
-                        err_invalid_fcn_handle (m_name);
-
-                      arg_list.clear ();
-                    }
-                  catch (octave::index_exception&)
-                    {
-                      err_invalid_fcn_handle (m_name);
-                    }
-                }
-              else if (have_object || partial_expr_val.is_classdef_meta ())
-                {
-                  // Object or class name must be the next to the last
-                  // element (it was the previous one, so if this is the
-                  // final element, it should be a classdef method,
-                  // but we'll let the classdef or classdef_meta subsref
-                  // function sort that out.
-
-                  if (i != n_elts-1)
-                    err_invalid_fcn_handle (m_name);
-
-                  type = ".(";
-                  arg_list.push_back (ovl (idx_elts[i]));
-                  arg_list.push_back (args);
-
-                  return partial_expr_val.subsref (type, arg_list, nargout);
-                }
-              else
-                err_invalid_fcn_handle (m_name);
-            }
-
-          // If we get here, we must have a function to call.
-
-          if (! partial_expr_val.is_function ())
-            err_invalid_fcn_handle (m_name);
-
-          fcn_to_call = partial_expr_val;
-        }
-      else
-        fcn_to_call = symtab.find_function (m_name, args, m_scope);
-    }
-
-  if (! fcn_to_call.is_defined ())
-    err_invalid_fcn_handle (m_name);
-
-  octave::stack_frame *closure_context = nullptr;
-
-  if (m_closure_frames && m_closure_frames->size () > 0)
-    closure_context = m_closure_frames->front ();
-
-  octave::tree_evaluator& tw = interp.get_evaluator ();
-
-  octave_function *of = fcn_to_call.function_value ();
-
-  octave::unwind_protect frame;
-
-  frame.add_method (tw, &octave::tree_evaluator::set_dispatch_class,
-                    std::string ());
-
-  tw.set_dispatch_class (m_dispatch_class);
-
-  return of->call (tw, nargout, args, closure_context);
+                  }
+              }
+            else if (have_object || partial_expr_val.is_classdef_meta ())
+              {
+                // Object or class name must be the next to the last
+                // element (it was the previous one, so if this is the
+                // final element, it should be a classdef method,
+                // but we'll let the classdef or classdef_meta subsref
+                // function sort that out.
+
+                if (i != n_elts-1)
+                  err_invalid_fcn_handle (m_name);
+
+                type = ".(";
+                arg_list.push_back (ovl (idx_elts[i]));
+                arg_list.push_back (args);
+
+                return partial_expr_val.subsref (type, arg_list, nargout);
+              }
+            else
+              err_invalid_fcn_handle (m_name);
+          }
+
+        // If we get here, we must have a function to call.
+
+        if (! partial_expr_val.is_function ())
+          err_invalid_fcn_handle (m_name);
+
+        fcn_to_call = partial_expr_val;
+      }
+    else
+      fcn_to_call = symtab.find_function (m_name, args);
+
+    if (! fcn_to_call.is_defined ())
+      err_invalid_fcn_handle (m_name);
+
+    return interp.feval (fcn_to_call, args, nargout);
+  }
+
+  octave_function * simple_fcn_handle::function_value (bool)
+  {
+    if (m_fcn.is_defined ())
+      return m_fcn.function_value ();
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("simple_fcn_handle::function_value");
+
+    // FIXME: is caching the correct thing to do?
+    // Cache this value so that the pointer will be valid as long as the
+    // function handle object is valid.
+
+    m_fcn = symtab.find_function (m_name, octave_value_list ());
+
+    return m_fcn.is_defined () ? m_fcn.function_value () : nullptr;
+  }
+
+  octave_user_function * simple_fcn_handle::user_function_value (bool)
+  {
+    if (m_fcn.is_defined ())
+      return m_fcn.user_function_value ();
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("simple_fcn_handle::user_function_value");
+
+    // FIXME: is caching the correct thing to do?
+    // Cache this value so that the pointer will be valid as long as the
+    // function handle object is valid.
+
+    m_fcn = symtab.find_user_function (m_name);
+
+    return m_fcn.is_defined () ? m_fcn.user_function_value () : nullptr;
+  }
+
+  octave_value simple_fcn_handle::fcn_val (void)
+  {
+    if (m_fcn.is_defined ())
+      return m_fcn;
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("simple_fcn_handle::user_function_value");
+
+    // FIXME: is caching the correct thing to do?
+    // Cache this value so that the pointer will be valid as long as the
+    // function handle object is valid.
+
+    m_fcn = symtab.find_user_function (m_name);
+
+    return m_fcn;
+  }
+
+  octave_scalar_map simple_fcn_handle::info (void)
+  {
+    octave_scalar_map m;
+
+    m.setfield ("function", fcn_name ());
+    m.setfield ("type", type ());
+    // When is FILE defined for simple function handles?
+    m.setfield ("file", file ());
+
+    return m;
+  }
+
+  bool simple_fcn_handle::save_ascii (std::ostream& os)
+  {
+    os << "# octaveroot: " << config::octave_exec_home () << "\n";
+
+    std::string fnm = file ();
+    if (! fnm.empty ())
+      os << "# path: " << fnm << "\n";
+
+    os << "# subtype: " << type () << "\n";
+
+    os << m_name << "\n";
+
+    return true;
+  }
+
+  bool simple_fcn_handle::load_ascii (std::istream& is)
+  {
+    // FIXME: If m_file is not empty, try to load the file and define
+    // the function?  Is it an error if that fails?  Or should this job
+    // always be deferred until the handle is used?
+
+    return is.good ();
+  }
+
+  bool simple_fcn_handle::save_binary (std::ostream& os, bool)
+  {
+    std::ostringstream nmbuf;
+
+    // When is FILE defined for simple function handles?
+    std::string fnm;
+
+    nmbuf << m_name << "@<simple>\n" << config::octave_exec_home ()
+          << "\n" << fnm;
+
+    std::string buf_str = nmbuf.str ();
+    int32_t tmp = buf_str.length ();
+    os.write (reinterpret_cast<char *> (&tmp), 4);
+    os.write (buf_str.c_str (), buf_str.length ());
+
+    return true;
+  }
+
+  bool simple_fcn_handle::load_binary (std::istream& is, bool,
+                                       octave::mach_info::float_format)
+  {
+    return is.good ();
+  }
+
+  bool simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
+                                     bool)
+  {
+#if defined (HAVE_HDF5)
+
+    bool retval = true;
+
+    hid_t group_hid = -1;
+#if defined (HAVE_HDF5_18)
+    group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
+                           octave_H5P_DEFAULT);
+#else
+    group_hid = H5Gcreate (loc_id, name, 0);
+#endif
+    if (group_hid < 0)
+      return false;
+
+    hid_t space_hid, data_hid, type_hid;
+    space_hid = data_hid = type_hid = -1;
+
+    // attach the type of the variable
+    type_hid = H5Tcopy (H5T_C_S1);
+    H5Tset_size (type_hid, m_name.length () + 1);
+    if (type_hid < 0)
+      {
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
+    hdims[0] = 0;
+    hdims[1] = 0;
+    space_hid = H5Screate_simple (0, hdims, nullptr);
+    if (space_hid < 0)
+      {
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+#if defined (HAVE_HDF5_18)
+    data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
+                          octave_H5P_DEFAULT, octave_H5P_DEFAULT,
+                          octave_H5P_DEFAULT);
+#else
+    data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
+                          octave_H5P_DEFAULT);
+#endif
+    if (data_hid < 0
+        || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
+                     octave_H5P_DEFAULT, m_name.c_str ()) < 0)
+      {
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+    H5Dclose (data_hid);
+
+    std::string octaveroot = config::octave_exec_home ();
+
+    // When is FILE defined for simple fucntion handles?
+    std::string fpath;
+
+    H5Sclose (space_hid);
+    hdims[0] = 1;
+    hdims[1] = octaveroot.length ();
+    space_hid = H5Screate_simple (0, hdims, nullptr);
+    if (space_hid < 0)
+      {
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    H5Tclose (type_hid);
+    type_hid = H5Tcopy (H5T_C_S1);
+    H5Tset_size (type_hid, octaveroot.length () + 1);
+#if defined (HAVE_HDF5_18)
+    hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT",
+                            type_hid, space_hid, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
+#else
+    hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT",
+                            type_hid, space_hid, octave_H5P_DEFAULT);
+#endif
+
+    if (a_id >= 0)
+      {
+        retval = (H5Awrite (a_id, type_hid, octaveroot.c_str ()) >= 0);
+
+        H5Aclose (a_id);
+      }
+    else
+      {
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    H5Sclose (space_hid);
+    hdims[0] = 1;
+    hdims[1] = fpath.length ();
+    space_hid = H5Screate_simple (0, hdims, nullptr);
+    if (space_hid < 0)
+      {
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    H5Tclose (type_hid);
+    type_hid = H5Tcopy (H5T_C_S1);
+    H5Tset_size (type_hid, fpath.length () + 1);
+
+#if defined (HAVE_HDF5_18)
+    a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid,
+                      octave_H5P_DEFAULT, octave_H5P_DEFAULT);
+#else
+    a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid, octave_H5P_DEFAULT);
+#endif
+
+    if (a_id >= 0)
+      {
+        retval = (H5Awrite (a_id, type_hid, fpath.c_str ()) >= 0);
+
+        H5Aclose (a_id);
+      }
+    else
+      retval = false;
+
+    H5Sclose (space_hid);
+    H5Tclose (type_hid);
+    H5Gclose (group_hid);
+
+    return retval;
+
+#else
+
+    octave_unused_parameter (loc_id);
+    octave_unused_parameter (name);
+
+    warn_save ("hdf5");
+
+    return false;
+
+#endif
+  }
+
+  bool simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
+                                     octave_hdf5_id& space_hid,
+                                     octave_hdf5_id& type_hid)
+  {
+#if defined (HAVE_HDF5)
+
+    unimplemented ("load", "hdf5");
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return true;
+
+#else
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return false;
+
+#endif
+  }
+
+  void simple_fcn_handle::print_raw (std::ostream& os, bool pr_as_read_syntax,
+                                     int current_print_indent_level) const
+  {
+    octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
+                           current_print_indent_level);
+  }
+
+  bool is_equal_to (const simple_fcn_handle& fh1, const simple_fcn_handle& fh2)
+  {
+    if (fh1.m_name == fh2.m_name)
+      {
+        if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
+          return fh1.m_fcn.is_copy_of (fh2.m_fcn);
+
+        if (fh1.m_fcn.is_undefined () && fh2.m_fcn.is_undefined ())
+          return true;
+      }
+
+    return false;
+  }
+
+  scoped_fcn_handle::scoped_fcn_handle (const octave_value& fcn,
+                                        const std::string& name,
+                                        const std::list<std::string>& parentage)
+    : base_fcn_handle (name), m_fcn (fcn), m_parentage (parentage)
+  {
+    // FIXME: should it be an error if FCN is undefined?
+
+    if (m_fcn.is_defined ())
+      {
+        octave_function *fcn = m_fcn.function_value ();
+
+        if (fcn)
+          m_file = fcn->fcn_file_name ();
+      }
+
+    m_parentage.push_front (name);
+  }
+
+  octave_value_list
+  scoped_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    // FIXME: we aren't really using the scope yet.  Hmm.
+
+    interpreter& interp = __get_interpreter__ ("simple_fcn_handle::call");
+
+    if (! m_fcn.is_defined ())
+      {
+        // Try to find it?
+
+        find_function ();
+      }
+
+    if (! m_fcn.is_defined ())
+      err_invalid_fcn_handle (m_name);
+
+    return interp.feval (m_fcn, args, nargout);
+  }
+
+  octave_scalar_map scoped_fcn_handle::info (void)
+  {
+    octave_scalar_map m;
+
+    m.setfield ("function", fcn_name ());
+    m.setfield ("type", type ());
+    m.setfield ("file", file ());
+
+    m.setfield ("parentage", Cell (m_parentage));
+
+    return m;
+  }
+
+  bool scoped_fcn_handle::save_ascii (std::ostream& os)
+  {
+    os << "# octaveroot: " << config::octave_exec_home () << "\n";
+
+    std::string fnm = file ();
+    if (! fnm.empty ())
+      os << "# path: " << fnm << "\n";
+
+    os << "# subtype: " << type () << "\n";
+
+    os << m_name << "\n";
+
+    octave_value tmp = Cell (m_parentage);
+    tmp.save_ascii (os);
+
+    return os.good ();
+  }
+
+  bool scoped_fcn_handle::load_ascii (std::istream& is)
+  {
+    octave_cell ov_cell;
+    ov_cell.load_ascii (is);
+
+    if (ov_cell.iscellstr ())
+      {
+        Array<std::string> cellstr_val = ov_cell.cellstr_value ();
+
+        for (octave_idx_type i = 0; i < cellstr_val.numel (); i++)
+          m_parentage.push_back (cellstr_val(i));
+      }
+
+    return is.good ();
+  }
+
+  bool scoped_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
+  {
+    std::ostringstream nmbuf;
+
+    std::string fnm = file ();
+
+    nmbuf << m_name << "@<scopedfunction>\n" << config::octave_exec_home ()
+          << "\n" << fnm;
+
+    std::string buf_str = nmbuf.str ();
+    int32_t len = buf_str.length ();
+    os.write (reinterpret_cast<char *> (&len), 4);
+    os.write (buf_str.c_str (), buf_str.length ());
+
+    octave_value tmp = Cell (m_parentage);
+    tmp.save_binary (os, save_as_floats);
+
+    return os.good ();
+  }
+
+  bool scoped_fcn_handle::load_binary (std::istream& is, bool swap,
+                                       octave::mach_info::float_format fmt)
+  {
+    octave_cell ov_cell;
+    ov_cell.load_binary (is, swap, fmt);
+
+    if (ov_cell.iscellstr ())
+      {
+        Array<std::string> cellstr_val = ov_cell.cellstr_value ();
+
+        for (octave_idx_type i = 0; i < cellstr_val.numel (); i++)
+          m_parentage.push_back (cellstr_val(i));
+      }
+
+    return is.good ();
+  }
+
+  bool scoped_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
+                                     bool)
+  {
+#if defined (HAVE_HDF5)
+
+    unimplemented ("save", "hdf5");
+
+    // FIXME: save parentage.
+
+    octave_unused_parameter (loc_id);
+    octave_unused_parameter (name);
+
+    return true;
+
+#else
+
+    octave_unused_parameter (loc_id);
+    octave_unused_parameter (name);
+
+    warn_save ("hdf5");
+
+    return false;
+
+#endif
+  }
+
+  bool scoped_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
+                                     octave_hdf5_id& space_hid,
+                                     octave_hdf5_id& type_hid)
+  {
+#if defined (HAVE_HDF5)
+
+    unimplemented ("load", "hdf5");
+
+    // FIXME: load parentage.
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return true;
+
+#else
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return false;
+
+#endif
+  }
+
+  void scoped_fcn_handle::print_raw (std::ostream& os,
+                                     bool pr_as_read_syntax,
+                                     int current_print_indent_level) const
+  {
+    octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
+                           current_print_indent_level);
+  }
+
+ bool is_equal_to (const scoped_fcn_handle& fh1, const scoped_fcn_handle& fh2)
+  {
+    if (fh1.m_name == fh2.m_name
+        && fh2.m_parentage == fh2.m_parentage
+        && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
+      return fh1.m_fcn.is_copy_of (fh2.m_fcn);
+    else
+      return false;
+  }
+
+  void scoped_fcn_handle::find_function (void)
+  {
+    // Since a scoped function is not visible by itself, try to load the
+    // file named in m_file then find and define the scoped function.
+    // It is not an error if this fails.  We can report later that the
+    // handle is invalid.
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("scoped_fcn_handle::find_function");
+
+    if (m_parentage.size () == 1)
+      {
+        std::string dir_name = sys::file_ops::dirname (m_file);
+
+        size_t pos = dir_name.find_last_of (sys::file_ops::dir_sep_chars ());
+
+        if (pos != std::string::npos)
+          dir_name = dir_name.substr (0, pos);
+        else if (dir_name == "private")
+          dir_name = ".";
+
+        std::string fcn_name = m_parentage.front ();
+
+        // FIXME: Does dir_name need to be in the load path for this to work?
+
+        m_fcn = symtab.find_private_function (dir_name, m_name);
+
+        // FIXME: Verify that it is a private function?
+      }
+    else
+      {
+        std::string primary_parent_name = m_parentage.back ();
+
+        octave_value ov_parent_fcn
+          = symtab.find_user_function (primary_parent_name);
+
+        if (ov_parent_fcn.is_defined ())
+          {
+            octave_user_function *fcn = ov_parent_fcn.user_function_value ();
+
+            if (fcn)
+              {
+                std::string file_name = fcn->fcn_file_name ();
+
+                std::string oct_home = config::octave_exec_home ();
+
+                if (file_name.substr (0, oct_home.size ()) == oct_home)
+                  file_name = file_name.substr (oct_home.size ());
+
+                octave_value subfun = fcn->find_subfunction (m_name);
+
+                if (subfun.is_defined ())
+                  m_fcn = subfun;
+              }
+          }
+      }
+  }
+
+  nested_fcn_handle::nested_fcn_handle (const octave_value& fcn,
+                                        const std::string& name,
+                                        const std::shared_ptr<stack_frame>& closure_frames)
+    : base_fcn_handle (name), m_fcn (fcn), m_closure_frames (closure_frames)
+  { }
+
+  octave_value_list
+  nested_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    tree_evaluator& tw = __get_evaluator__ ("nested_fcn_handle::call");
+
+    octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
+
+    tw.push_stack_frame (oct_usr_fcn, m_closure_frames);
+
+    unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+    return oct_usr_fcn->execute (tw, nargout, args);
+  }
+
+  octave_value nested_fcn_handle::workspace (void) const
+  {
+    return m_closure_frames->workspace ();
+  }
+
+  octave_scalar_map nested_fcn_handle::info (void)
+  {
+    octave_scalar_map m;
+
+    m.setfield ("function", fcn_name ());
+    m.setfield ("type", type ());
+    m.setfield ("file", "");
+    m.setfield ("workspace", workspace ());
+
+    return m;
+  }
+
+  // FIXME: For save, we need a way to save the (possibly shared)
+  // workspace.  For load, we need a way to load and link to the
+  // (possibly shared) workspace that was saved.
+  //
+  // Since a nested function is not visible by itself, do we need to try
+  // to load the file named in m_file then find and define the function?
+  // Is it an error if that fails?  Or should this job always be
+  // deferred until the handle is used?
+
+  bool nested_fcn_handle::save_ascii (std::ostream& os)
+  {
+    unimplemented ("save", "text");
+
+    octave_unused_parameter (os);
+
+    return true;
+  }
+
+  bool nested_fcn_handle::load_ascii (std::istream& is)
+  {
+    unimplemented ("load", "text");
+
+    octave_unused_parameter (is);
+
+    return true;
+  }
+
+  bool nested_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
+  {
+    unimplemented ("save", "binary");
+
+    octave_unused_parameter (os);
+    octave_unused_parameter (save_as_floats);
+
+    return true;
+  }
+
+  bool nested_fcn_handle::load_binary (std::istream& is, bool swap,
+                                       mach_info::float_format fmt)
+  {
+    unimplemented ("load", "binary");
+
+    octave_unused_parameter (is);
+    octave_unused_parameter (swap);
+    octave_unused_parameter (fmt);
+
+    return true;
+  }
+
+  bool nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
+                                     bool)
+  {
+#if defined (HAVE_HDF5)
+
+    unimplemented ("save", "hdf5");
+
+    octave_unused_parameter (loc_id);
+    octave_unused_parameter (name);
+
+    return true;
+
+#else
+
+    octave_unused_parameter (loc_id);
+    octave_unused_parameter (name);
+
+    warn_save ("hdf5");
+
+    return false;
+
+#endif
+  }
+
+  bool nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
+                                     octave_hdf5_id& space_hid,
+                                     octave_hdf5_id& type_hid)
+  {
+#if defined (HAVE_HDF5)
+
+    unimplemented ("load", "hdf5");
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return true;
+
+#else
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return false;
+
+#endif
+  }
+
+  void nested_fcn_handle::print_raw (std::ostream& os,
+                                     bool pr_as_read_syntax,
+                                     int current_print_indent_level) const
+  {
+    octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
+                           current_print_indent_level);
+  }
+
+  bool is_equal_to (const nested_fcn_handle& fh1, const nested_fcn_handle& fh2)
+  {
+    if (fh1.m_name == fh2.m_name
+        && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
+      return fh1.m_fcn.is_copy_of (fh2.m_fcn);
+    else
+      return false;
+  }
+
+  class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& fcn,
+                                                    const std::string& class_nm,
+                                                    const std::string& meth_nm)
+    : base_fcn_handle (meth_nm), m_obj (), m_fcn (fcn),
+      m_dispatch_class (class_nm)
+  { }
+
+  class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& obj,
+                                                    const octave_value& fcn,
+                                                    const std::string& class_nm,
+                                                    const std::string& meth_nm)
+    : base_fcn_handle (meth_nm), m_obj (obj), m_fcn (fcn),
+      m_dispatch_class (class_nm)
+  { }
+
+  octave_value_list
+  class_simple_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    interpreter& interp = __get_interpreter__ ("class_simple_fcn_handle::call");
+
+    if (m_obj.is_defined ())
+      {
+        octave_value_list tmp_args = args;
+        tmp_args.prepend (m_obj);
+
+        return interp.feval (m_fcn, tmp_args, nargout);
+      }
+
+    // FIXME: is this the best approach?  Should we be saving current
+    // dispatch class and restoring that value instead of
+    // unconditionally setting it to "" when we return from this
+    // function?
+
+    tree_evaluator& tw = interp.get_evaluator ();
+
+    unwind_action act ([&tw] () { tw.set_dispatch_class (""); });
+
+    tw.set_dispatch_class (m_dispatch_class);
+
+    return interp.feval (m_fcn, args, nargout);
+  }
+
+  octave_scalar_map class_simple_fcn_handle::info (void)
+  {
+    octave_scalar_map m;
+
+    m.setfield ("function", fcn_name ());
+    m.setfield ("type", type ());
+    m.setfield ("file", "");
+    m.setfield ("class", dispatch_class ());
+
+    return m;
+  }
+
+  // FIXME: Since a class method is not visible by itself, do we need to
+  // try to load the file named in m_file then find and define the
+  // function?  Is it an error if that fails?  Or should this job always
+  // be deferred until the handle is used?
+
+  bool class_simple_fcn_handle::save_ascii (std::ostream& os)
+  {
+    unimplemented ("save", "text");
+
+    octave_unused_parameter (os);
+
+    return true;
+  }
+
+  bool class_simple_fcn_handle::load_ascii (std::istream& is)
+  {
+    unimplemented ("load", "text");
+
+    octave_unused_parameter (is);
+
+    return true;
+  }
+
+  bool class_simple_fcn_handle::save_binary (std::ostream& os,
+                                             bool save_as_floats)
+  {
+    unimplemented ("save", "binary");
+
+    octave_unused_parameter (os);
+    octave_unused_parameter (save_as_floats);
+
+    return true;
+  }
+
+  bool class_simple_fcn_handle::load_binary (std::istream& is, bool swap,
+                                             mach_info::float_format fmt)
+  {
+    unimplemented ("load", "binary");
+
+    octave_unused_parameter (is);
+    octave_unused_parameter (swap);
+    octave_unused_parameter (fmt);
+
+    return true;
+  }
+
+  bool class_simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
+                                           const char *name, bool)
+  {
+#if defined (HAVE_HDF5)
+
+    unimplemented ("save", "hdf5");
+
+    octave_unused_parameter (loc_id);
+    octave_unused_parameter (name);
+
+    return true;
+
+#else
+
+    octave_unused_parameter (loc_id);
+    octave_unused_parameter (name);
+
+    warn_save ("hdf5");
+
+    return false;
+
+#endif
+  }
+
+  bool class_simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
+                                           octave_hdf5_id& space_hid,
+                                           octave_hdf5_id& type_hid)
+  {
+#if defined (HAVE_HDF5)
+
+    unimplemented ("load", "hdf5");
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return true;
+
+#else
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return false;
+
+#endif
+  }
+
+  void class_simple_fcn_handle::print_raw (std::ostream& os,
+                                           bool pr_as_read_syntax,
+                                           int current_print_indent_level) const
+  {
+    octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
+                           current_print_indent_level);
+  }
+
+  bool is_equal_to (const class_simple_fcn_handle& fh1,
+                    const class_simple_fcn_handle& fh2)
+  {
+    // FIXME: Also need to check object values are equivalent?
+
+    if (fh1.m_name == fh2.m_name
+        && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
+      return fh1.m_fcn.is_copy_of (fh2.m_fcn);
+    else
+      return false;
+  }
+
+  anonymous_fcn_handle::anonymous_fcn_handle (const octave_value& fcn,
+                                              const stack_frame::local_vars_map& local_vars)
+    : base_fcn_handle (anonymous), m_fcn (fcn), m_local_vars (local_vars)
+  { }
+
+  octave_value_list
+  anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    tree_evaluator& tw = __get_evaluator__ ("anonymous_fcn_handle::call");
+
+    octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
+
+    tw.push_stack_frame (oct_usr_fcn, m_local_vars);
+
+    unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+    return oct_usr_fcn->execute (tw, nargout, args);
+  }
+
+  octave_value anonymous_fcn_handle::workspace (void) const
+  {
+    octave_scalar_map ws;
+
+    for (const auto& nm_val : m_local_vars)
+      ws.assign (nm_val.first, nm_val.second);
+
+    return ws;
+  }
+
+  octave_scalar_map anonymous_fcn_handle::info (void)
+  {
+    octave_scalar_map m;
+
+    std::ostringstream buf;
+    print_raw (buf, true, 0);
+    m.setfield ("name", buf.str ());
+
+    m.setfield ("type", type ());
+    m.setfield ("file", "");
+    m.setfield ("workspace", Cell (workspace ()));
+    m.setfield ("within_file_path", "");
+
+    return m;
+  }
+
+  bool anonymous_fcn_handle::save_ascii (std::ostream& os)
+  {
+    // FIXME: can we ensure that m_fcn is always defined?
+
+    if (m_fcn.is_undefined ())
+      return false;
+
+    os << m_name << "\n";
+
+    print_raw (os, true, 0);
+    os << "\n";
+
+    size_t varlen = m_local_vars.size ();
+
+    if (varlen > 0)
+      {
+        os << "# length: " << varlen << "\n";
+
+        for (const auto& nm_val : m_local_vars)
+          {
+            if (! save_text_data (os, nm_val.second, nm_val.first, false, 0))
+              return ! os.fail ();
+          }
+      }
+
+    return true;
+  }
+
+  bool anonymous_fcn_handle::load_ascii (std::istream& is)
+  {
+    skip_preceeding_newline (is);
+
+    std::string buf;
+
+    if (is)
+      {
+        // Get a line of text whitespace characters included, leaving
+        // newline in the stream.
+
+        buf = read_until_newline (is, true);
+      }
+
+    std::streampos pos = is.tellg ();
+
+    unwind_protect_safe frame;
+
+    // Set up temporary scope to use for evaluating the text that
+    // defines the anonymous function.
+
+    interpreter& interp
+      = __get_interpreter__ ("anonymous_fcn_handle::load_ascii");
+
+    tree_evaluator& tw = interp.get_evaluator ();
+
+    tw.push_dummy_scope (buf);
+    frame.add_method (tw, &tree_evaluator::pop_scope);
+
+    octave_idx_type len = 0;
+
+    if (extract_keyword (is, "length", len, true) && len >= 0)
+      {
+        if (len > 0)
+          {
+            for (octave_idx_type i = 0; i < len; i++)
+              {
+                octave_value t2;
+                bool dummy;
+
+                std::string name = read_text_data (is, "", dummy, t2, i);
+
+                if (! is)
+                  error ("load: failed to load anonymous function handle");
+
+                m_local_vars[name] = t2;
+              }
+          }
+      }
+    else
+      {
+        is.seekg (pos);
+        is.clear ();
+      }
+
+    if (is)
+      return parse (buf);
+
+    return false;
+  }
+
+  bool anonymous_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
+  {
+    // FIXME: can we ensure that m_fcn is always defined?
+
+    if (m_fcn.is_undefined ())
+      return false;
+
+    std::ostringstream nmbuf;
+
+    size_t varlen = m_local_vars.size ();
+
+    nmbuf << anonymous;
+    if (varlen > 0)
+      nmbuf << ' ' << varlen;
+
+    std::string buf_str = nmbuf.str ();
+    int32_t tmp = buf_str.length ();
+    os.write (reinterpret_cast<char *> (&tmp), 4);
+    os.write (buf_str.c_str (), buf_str.length ());
+
+    std::ostringstream buf;
+    print_raw (buf, true, 0);
+    std::string stmp = buf.str ();
+    tmp = stmp.length ();
+    os.write (reinterpret_cast<char *> (&tmp), 4);
+    os.write (stmp.c_str (), stmp.length ());
+
+    if (varlen > 0)
+      {
+        for (const auto& nm_val : m_local_vars)
+          {
+            if (! save_binary_data (os, nm_val.second, nm_val.first,
+                                    "", 0, save_as_floats))
+              return ! os.fail ();
+          }
+      }
+
+    return true;
+  }
+
+  bool anonymous_fcn_handle::load_binary (std::istream& is, bool swap,
+                                          mach_info::float_format fmt)
+  {
+    // Read extra characters in m_name as the number of local variable
+    // values in this anonymous function.
+
+    octave_idx_type len = 0;
+    size_t anl = anonymous.length ();
+    if (m_name.length () > anl)
+      {
+        std::istringstream nm_is (m_name.substr (anl));
+        nm_is >> len;
+
+        // Anonymous functons don't have names.  We just used this
+        // string as temporary storage to pass the number of local
+        // variable values.
+
+        m_name = "";
+      }
+
+    int32_t tmp;
+
+    if (! is.read (reinterpret_cast<char *> (&tmp), 4))
+      return false;
+    if (swap)
+      swap_bytes<4> (&tmp);
+
+    OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
+    // is.get (ctmp2, tmp+1, 0); caused is.eof () to be true though
+    // effectively not reading over file end
+    is.read (ctmp2, tmp);
+    ctmp2[tmp] = 0;
+
+    unwind_protect_safe frame;
+
+    // Set up temporary scope to use for evaluating the text that
+    // defines the anonymous function.
+
+    interpreter& interp
+      = __get_interpreter__ ("anonymous_fcn_handle::load_binary");
+
+    tree_evaluator& tw = interp.get_evaluator ();
+
+    tw.push_dummy_scope (ctmp2);
+    frame.add_method (tw, &tree_evaluator::pop_scope);
+
+    if (len > 0)
+      {
+        for (octave_idx_type i = 0; i < len; i++)
+          {
+            octave_value t2;
+            bool dummy;
+            std::string doc;
+
+            std::string name
+              = read_binary_data (is, swap, fmt, "", dummy, t2, doc);
+
+            if (! is)
+              error ("load: failed to load anonymous function handle");
+
+            m_local_vars[name] = t2;
+          }
+      }
+
+    if (is)
+      return parse (ctmp2);
+
+    return false;
+  }
+
+  bool anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
+                                        const char *name, bool save_as_floats)
+  {
+#if defined (HAVE_HDF5)
+
+    bool retval = true;
+
+    hid_t group_hid = -1;
+#if defined (HAVE_HDF5_18)
+    group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
+                           octave_H5P_DEFAULT);
+#else
+    group_hid = H5Gcreate (loc_id, name, 0);
+#endif
+    if (group_hid < 0)
+      return false;
+
+    hid_t space_hid, data_hid, type_hid;
+    space_hid = data_hid = type_hid = -1;
+
+    // attach the type of the variable
+    type_hid = H5Tcopy (H5T_C_S1);
+    H5Tset_size (type_hid, m_name.length () + 1);
+    if (type_hid < 0)
+      {
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
+    hdims[0] = 0;
+    hdims[1] = 0;
+    space_hid = H5Screate_simple (0, hdims, nullptr);
+    if (space_hid < 0)
+      {
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+#if defined (HAVE_HDF5_18)
+    data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
+                          octave_H5P_DEFAULT, octave_H5P_DEFAULT,
+                          octave_H5P_DEFAULT);
+#else
+    data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
+                          octave_H5P_DEFAULT);
+#endif
+    if (data_hid < 0
+        || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
+                     octave_H5P_DEFAULT, m_name.c_str ()) < 0)
+      {
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+    H5Dclose (data_hid);
+
+    std::ostringstream buf;
+    print_raw (buf, true, 0);
+    std::string stmp = buf.str ();
+
+    // attach the type of the variable
+    H5Tset_size (type_hid, stmp.length () + 1);
+    if (type_hid < 0)
+      {
+        H5Sclose (space_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+#if defined (HAVE_HDF5_18)
+    data_hid = H5Dcreate (group_hid, "fcn",  type_hid, space_hid,
+                          octave_H5P_DEFAULT, octave_H5P_DEFAULT,
+                          octave_H5P_DEFAULT);
+#else
+    data_hid = H5Dcreate (group_hid, "fcn",  type_hid, space_hid,
+                          octave_H5P_DEFAULT);
+#endif
+    if (data_hid < 0
+        || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
+                     octave_H5P_DEFAULT, stmp.c_str ()) < 0)
+      {
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    H5Dclose (data_hid);
+
+    size_t varlen = m_local_vars.size ();
+
+    if (varlen > 0)
+      {
+        hid_t as_id = H5Screate (H5S_SCALAR);
+
+        if (as_id >= 0)
+          {
+#if defined (HAVE_HDF5_18)
+            hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE",
+                                    H5T_NATIVE_IDX, as_id,
+                                    octave_H5P_DEFAULT, octave_H5P_DEFAULT);
+
+#else
+            hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE",
+                                    H5T_NATIVE_IDX, as_id, octave_H5P_DEFAULT);
+#endif
+
+            if (a_id >= 0)
+              {
+                retval = (H5Awrite (a_id, H5T_NATIVE_IDX, &varlen) >= 0);
+
+                H5Aclose (a_id);
+              }
+            else
+              retval = false;
+
+            H5Sclose (as_id);
+          }
+        else
+          retval = false;
+#if defined (HAVE_HDF5_18)
+        data_hid = H5Gcreate (group_hid, "symbol table",
+                              octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
+#else
+        data_hid = H5Gcreate (group_hid, "symbol table", 0);
+#endif
+        if (data_hid < 0)
+          {
+            H5Sclose (space_hid);
+            H5Tclose (type_hid);
+            H5Gclose (group_hid);
+            return false;
+          }
+
+        for (const auto& nm_val : m_local_vars)
+          {
+            if (! add_hdf5_data (data_hid, nm_val.second, nm_val.first,
+                                 "", false, save_as_floats))
+              break;
+          }
+
+        H5Gclose (data_hid);
+      }
+
+    H5Sclose (space_hid);
+    H5Tclose (type_hid);
+    H5Gclose (group_hid);
+
+    return retval;
+
+#else
+
+    octave_unused_parameter (loc_id);
+    octave_unused_parameter (name);
+    octave_unused_parameter (save_as_floats);
+
+    warn_save ("hdf5");
+
+    return false;
+
+#endif
+  }
+
+  bool anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
+                                        octave_hdf5_id& space_hid,
+                                        octave_hdf5_id& type_hid)
+  {
+#if defined (HAVE_HDF5)
+
+    bool success = true;
+
+#if defined (HAVE_HDF5_18)
+    hid_t data_hid = H5Dopen (group_hid, "fcn", octave_H5P_DEFAULT);
+#else
+    hid_t data_hid = H5Dopen (group_hid, "fcn");
+#endif
+
+    if (data_hid < 0)
+      {
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    H5Tclose (type_hid);
+    type_hid = H5Dget_type (data_hid);
+    hid_t type_class_hid = H5Tget_class (type_hid);
+
+    if (type_class_hid != H5T_STRING)
+      {
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Dclose (data_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    H5Sclose (space_hid);
+    space_hid = H5Dget_space (data_hid);
+    hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
+
+    if (rank != 0)
+      {
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Dclose (data_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    int slen = H5Tget_size (type_hid);
+    if (slen < 0)
+      {
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Dclose (data_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+
+    OCTAVE_LOCAL_BUFFER (char, fcn_tmp, slen);
+
+    // create datatype for (null-terminated) string to read into:
+    hid_t st_id = H5Tcopy (H5T_C_S1);
+    H5Tset_size (st_id, slen);
+
+    if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
+                 octave_H5P_DEFAULT, fcn_tmp)
+        < 0)
+      {
+        H5Tclose (st_id);
+        H5Sclose (space_hid);
+        H5Tclose (type_hid);
+        H5Dclose (data_hid);
+        H5Gclose (group_hid);
+        return false;
+      }
+    H5Tclose (st_id);
+    H5Dclose (data_hid);
+
+    octave_idx_type len = 0;
+
+    // we have to pull some shenanigans here to make sure
+    // HDF5 doesn't print out all sorts of error messages if we
+    // call H5Aopen for a non-existing attribute
+
+    H5E_auto_t err_func;
+    void *err_func_data;
+
+    // turn off error reporting temporarily, but save the error
+    // reporting function:
+#if defined (HAVE_HDF5_18)
+    H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data);
+    H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
+#else
+    H5Eget_auto (&err_func, &err_func_data);
+    H5Eset_auto (nullptr, nullptr);
+#endif
+
+    hid_t attr_id = H5Aopen_name (group_hid, "SYMBOL_TABLE");
+
+    if (attr_id >= 0)
+      {
+        if (H5Aread (attr_id, H5T_NATIVE_IDX, &len) < 0)
+          success = false;
+
+        H5Aclose (attr_id);
+      }
+
+    // restore error reporting:
+#if defined (HAVE_HDF5_18)
+    H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data);
+#else
+    H5Eset_auto (err_func, err_func_data);
+#endif
+
+    unwind_protect_safe frame;
+
+    // Set up temporary scope to use for evaluating the text that
+    // defines the anonymous function.
+
+    interpreter& interp
+      = __get_interpreter__ ("anonymous_fcn_handle::load_hdf5");
+
+    tree_evaluator& tw = interp.get_evaluator ();
+
+    tw.push_dummy_scope (fcn_tmp);
+    frame.add_method (tw, &tree_evaluator::pop_scope);
+
+    if (len > 0 && success)
+      {
+        hsize_t num_obj = 0;
+#if defined (HAVE_HDF5_18)
+        data_hid = H5Gopen (group_hid, "symbol table", octave_H5P_DEFAULT);
+#else
+        data_hid = H5Gopen (group_hid, "symbol table");
+#endif
+        H5Gget_num_objs (data_hid, &num_obj);
+        H5Gclose (data_hid);
+
+        if (num_obj != static_cast<hsize_t> (len))
+          error ("load: failed to load anonymous function handle");
+
+        hdf5_callback_data dsub;
+        int current_item = 0;
+        for (octave_idx_type i = 0; i < len; i++)
+          {
+            if (hdf5_h5g_iterate (group_hid, "symbol table", &current_item,
+                                  &dsub) <= 0)
+              error ("load: failed to load anonymous function handle");
+
+            m_local_vars[dsub.name] = dsub.tc;
+          }
+      }
+
+    if (success)
+      return parse (fcn_tmp);
+
+    return false;
+
+#else
+
+    octave_unused_parameter (group_hid);
+    octave_unused_parameter (space_hid);
+    octave_unused_parameter (type_hid);
+
+    return false;
+
+#endif
+  }
+
+  void anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const
+  {
+    tree_print_code tpc (os);
+
+    octave_user_function *f = m_fcn.user_function_value ();
+
+    if (! f)
+      error ("invalid anonymous function handle");
+
+    os << "@";
+
+    // The parameter list should always be valid for anonymous
+    // functions, so we should always call accept for it, and it will
+    // print the parens for us.
+
+    tree_parameter_list *p = f->parameter_list ();
+
+    if (p)
+      p->accept (tpc);
+
+    os << " ";
+
+    tree_statement_list *b = f->body ();
+
+    assert (b->length () == 1);
+
+    tree_statement *s = b->front ();
+
+    if (! s)
+      error ("invalid anonymous function handle");
+
+    assert (s->is_expression ());
+
+    tree_expression *e = s->expression ();
+
+    if (! e)
+      error ("invalid anonymous function handle");
+
+    tpc.print_fcn_handle_body (e);
+  }
+
+  bool
+  anonymous_fcn_handle::parse (const std::string& fcn_text)
+  {
+    // FIXME: If evaluation of the string gives us an anonymous function
+    // handle object, then why extract the function and create a new
+    // anonymous function object?  Why not just attach the workspace
+    // values to the object returned by eval_string?  This code is also is
+    // duplicated in read_mat5_binary_element in ls-mat5.cc.
+
+    interpreter& interp = __get_interpreter__ ("anonymous_fcn_handle::parse");
+
+    // Set up temporary scope to use for evaluating the text that defines
+    // the anonymous function so that we don't pick up values of random
+    // variables that might be in the current scope.
+
+    tree_evaluator& tw = interp.get_evaluator ();
+    tw.push_dummy_scope ("read_mat5_binary_element");
+
+    unwind_action act ([&tw] () { tw.pop_scope (); });
+
+    int parse_status;
+    octave_value anonymous_fcn_handle
+      = interp.eval_string (fcn_text, true, parse_status);
+
+    if (parse_status != 0)
+      return false;
+
+    octave_fcn_handle *fh = anonymous_fcn_handle.fcn_handle_value ();
+
+    if (! fh)
+      return false;
+
+    m_fcn = fh->fcn_val ();
+
+    octave_user_function *uf = m_fcn.user_function_value (true);
+
+    if (uf)
+      {
+        symbol_scope uf_scope = uf->scope ();
+
+        if (uf_scope)
+          uf_scope.cache_name (m_name);
+      }
+
+    return true;
+  }
+
+  bool is_equal_to (const anonymous_fcn_handle& fh1,
+                    const anonymous_fcn_handle& fh2)
+  {
+    if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
+      return fh1.m_fcn.is_copy_of (fh2.m_fcn);
+    else
+      return false;
+  }
+}
+
+octave_fcn_handle::octave_fcn_handle (void)
+  : octave_base_value (), m_rep (new octave::invalid_fcn_handle ())
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const octave_value& fcn)
+  : octave_base_value (), m_rep (new octave::internal_fcn_handle (fcn))
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const std::string& name)
+  : octave_base_value (), m_rep (new octave::simple_fcn_handle (name))
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const octave_value& fcn,
+                                      const std::string& name)
+  : octave_base_value (), m_rep (new octave::simple_fcn_handle (fcn, name))
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const octave_value& fcn,
+                                      const std::string& class_nm,
+                                      const std::string& meth_nm)
+  : octave_base_value (),
+    m_rep (new octave::class_simple_fcn_handle (fcn, class_nm, meth_nm))
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const octave_value& obj,
+                                      const octave_value& fcn,
+                                      const std::string& class_nm,
+                                      const std::string& meth_nm)
+  : octave_base_value (),
+    m_rep (new octave::class_simple_fcn_handle (obj, fcn, class_nm, meth_nm))
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const octave_value& fcn,
+                                      const std::string& name,
+                                      const std::list<std::string>& parentage)
+  : octave_base_value (),
+    m_rep (new octave::scoped_fcn_handle (fcn, name, parentage))
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const octave_value& fcn,
+                                      const std::string& name,
+                                      const std::shared_ptr<octave::stack_frame>& closure_frames)
+  : octave_base_value (),
+    m_rep (new octave::nested_fcn_handle (fcn, name, closure_frames))
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const octave_value& fcn,
+                                      const octave::stack_frame::local_vars_map& local_vars)
+  : octave_base_value (),
+    m_rep (new octave::anonymous_fcn_handle (fcn, local_vars))
+{ }
+
+octave_fcn_handle::octave_fcn_handle (octave::base_fcn_handle *rep)
+  : octave_base_value (), m_rep (rep)
+{ }
+
+octave_fcn_handle::octave_fcn_handle (const octave_fcn_handle& fh)
+  : octave_base_value (fh)
+{
+  m_rep = fh.m_rep->clone ();
 }
 
 dim_vector
@@ -401,319 +2499,31 @@
   return dv;
 }
 
-octave_function * octave_fcn_handle::function_value (bool)
-{
-  if (m_fcn.is_defined ())
-    return m_fcn.function_value ();
-
-  octave::symbol_table& symtab
-    = octave::__get_symbol_table__ ("octave_fcn_handle::set_fcn");
-
-  // Cache this value so that the pointer will be valid as long as the
-  // function handle object is valid.
-
-  m_generic_fcn = symtab.find_function (m_name, octave_value_list (), m_scope);
-
-  return (m_generic_fcn.is_defined ()
-          ? m_generic_fcn.function_value () : nullptr);
-}
-
-octave_user_function * octave_fcn_handle::user_function_value (bool)
-{
-  if (m_fcn.is_defined ())
-    return m_fcn.user_function_value ();
-
-  octave::symbol_table& symtab
-    = octave::__get_symbol_table__ ("octave_fcn_handle::set_fcn");
-
-  // Cache this value so that the pointer will be valid as long as the
-  // function handle object is valid.
-
-  m_generic_fcn = symtab.find_user_function (m_name);
-
-  return (m_generic_fcn.is_defined ()
-          ? m_generic_fcn.user_function_value () : nullptr);
-}
-
-// Save call stack frames for handles to nested functions.
-
-void
-octave_fcn_handle::push_closure_context (octave::tree_evaluator& tw)
-{
-  if (! m_closure_frames)
-    m_closure_frames = new std::list<octave::stack_frame *> ();
-
-  octave::stack_frame& curr_frame = tw.get_current_stack_frame ();
-
-  octave::stack_frame *dup_frame = curr_frame.dup ();
-
-  if (! m_closure_frames->empty ())
-    {
-      octave::stack_frame *top_frame = m_closure_frames->back ();
-
-      // Arrange for static and access links in the top stack frame (the
-      // last one saved before this one) to point to the new duplicated
-      // frame.  This way we will look up through the duplicated frames
-      // when evaluating the function.
-
-      top_frame->set_closure_links (dup_frame);
-    }
-
-  m_closure_frames->push_back (dup_frame);
-}
-
-octave_value
-octave_fcn_handle::workspace (void) const
-{
-  if (m_name == anonymous)
-    {
-      octave_user_function *fu = m_fcn.user_function_value ();
-
-      octave_scalar_map ws;
-
-      if (fu)
-        {
-          for (const auto& nm_val : fu->local_var_init_vals ())
-            ws.assign (nm_val.first, nm_val.second);
-        }
-
-      return ws;
-    }
-  else if (m_closure_frames)
-    {
-      octave_idx_type num_frames = m_closure_frames->size ();
-
-      Cell ws_frames (num_frames, 1);
-
-      octave_idx_type i = 0;
-
-      for (auto elt : *m_closure_frames)
-        {
-          octave::symbol_info_list symbols = elt->all_variables ();
-
-          octave_scalar_map ws;
-
-          for (auto sym_name : symbols.names ())
-            {
-              octave_value val = symbols.varval (sym_name);
-
-              if (val.is_defined ())
-                ws.assign (sym_name, val);
-            }
-
-          ws_frames(i++) = ws;
-        }
-
-      return ws_frames;
-    }
-
-  return Cell ();
-}
-
-bool
-octave_fcn_handle::is_equal_to (const octave_fcn_handle& h) const
-{
-  if (m_fcn.is_defined () && h.m_fcn.is_defined ())
-    return m_fcn.is_copy_of (h.m_fcn);
-  else if (m_fcn.is_undefined () && h.m_fcn.is_undefined ())
-    return m_name == h.m_name;
-  else
-    return false;
-}
-
-bool
-octave_fcn_handle::set_fcn (const std::string& octaveroot,
-                            const std::string& fpath)
-{
-  if (octaveroot.length () != 0
-      && fpath.length () >= octaveroot.length ()
-      && fpath.substr (0, octaveroot.length ()) == octaveroot
-      && octave::config::octave_exec_home () != octaveroot)
-    {
-      // First check if just replacing matlabroot is enough
-      std::string str
-        = (octave::config::octave_exec_home ()
-           + fpath.substr (octaveroot.length ()));
-      octave::sys::file_stat fs (str);
-
-      if (fs.exists ())
-        {
-          size_t xpos = str.find_last_of (octave::sys::file_ops::dir_sep_chars ());
-
-          std::string dir_name = str.substr (0, xpos);
-
-          octave_value ov_fcn
-            = octave::load_fcn_from_file (str, dir_name, "", "", m_name);
-
-          if (ov_fcn.is_undefined ())
-            error ("function handle points to non-existent function");
-
-          m_fcn = octave_value (new octave_fcn_handle (ov_fcn, m_name));
-        }
-      else
-        {
-          // Next just search for it anywhere in the system path
-          std::list<std::string> names;
-          names.push_back (m_name + ".oct");
-          names.push_back (m_name + ".mex");
-          names.push_back (m_name + ".m");
-
-          octave::load_path& lp
-            = octave::__get_load_path__ ("octave_fcn_handle::set_fcn");
-
-          octave::directory_path p (lp.system_path ());
-
-          str = octave::sys::env::make_absolute (p.find_first_of (names));
-
-          size_t xpos = str.find_last_of (octave::sys::file_ops::dir_sep_chars ());
-
-          std::string dir_name = str.substr (0, xpos);
-
-          octave_value ov_fcn
-            = octave::load_fcn_from_file (str, dir_name, "", "", m_name);
-
-          if (ov_fcn.is_undefined ())
-            error ("function handle points to non-existent function");
-
-          m_fcn = octave_value (new octave_fcn_handle (ov_fcn, m_name));
-        }
-    }
-  else
-    {
-      if (fpath.length () > 0)
-        {
-          size_t xpos = fpath.find_last_of (octave::sys::file_ops::dir_sep_chars ());
-
-          std::string dir_name = fpath.substr (0, xpos);
-
-          octave_value ov_fcn
-            = octave::load_fcn_from_file (fpath, dir_name, "", "", m_name);
-
-          if (ov_fcn.is_undefined ())
-            error ("function handle points to non-existent function");
-
-          m_fcn = octave_value (new octave_fcn_handle (ov_fcn, m_name));
-        }
-      else
-        {
-          octave::symbol_table& symtab
-            = octave::__get_symbol_table__ ("octave_fcn_handle::set_fcn");
-
-          m_fcn = symtab.find_function (m_name);
-
-          if (! m_fcn.is_function ())
-            error ("function handle points to non-existent function");
-        }
-    }
-
-  return true;
-}
-
-octave_value
-octave_fcn_handle::convert_to_str_internal (bool, bool, char type) const
-{
-  std::ostringstream buf;
-  print_raw (buf, true);
-  return octave_value (buf.str (), type);
-}
-
 bool
 octave_fcn_handle::save_ascii (std::ostream& os)
 {
-  if (m_name == anonymous)
-    {
-      if (m_fcn.is_undefined ())
-        return false;
-
-      octave_user_function *f = m_fcn.user_function_value ();
-
-      octave_user_function::local_vars_map local_vars
-        = f->local_var_init_vals ();
-
-      size_t varlen = local_vars.size ();
-
-      os << m_name << "\n";
-
-      print_raw (os, true);
-      os << "\n";
-
-      if (varlen > 0)
-        {
-          os << "# length: " << varlen << "\n";
-
-          for (const auto& nm_val : local_vars)
-            {
-              if (! save_text_data (os, nm_val.second, nm_val.first, false, 0))
-                return ! os.fail ();
-            }
-        }
-    }
-  else
-    {
-      octave_function *f = function_value ();
-      std::string fnm = (f ? f->fcn_file_name () : "");
-
-      os << "# octaveroot: " << octave::config::octave_exec_home () << "\n";
-      if (! fnm.empty ())
-        os << "# path: " << fnm << "\n";
-      os << m_name << "\n";
-    }
-
-  return true;
-}
-
-bool
-octave_fcn_handle::parse_anon_fcn_handle (const std::string& fcn_text)
-{
-  bool success = true;
-
-  octave::interpreter& interp
-    = octave::__get_interpreter__ ("octave_fcn_handle::parse_anon_fcn_handle");
-
-  int parse_status;
-  octave_value anon_fcn_handle
-    = interp.eval_string (fcn_text, true, parse_status);
-
-  if (parse_status == 0)
-    {
-      octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value ();
-
-      if (fh)
-        {
-          m_fcn = fh->m_fcn;
-
-          octave_user_function *uf = m_fcn.user_function_value (true);
-
-          if (uf)
-            {
-              octave::symbol_scope uf_scope = uf->scope ();
-
-              if (uf_scope)
-                uf_scope.cache_name (m_name);
-            }
-        }
-      else
-        success = false;
-    }
-  else
-    success = false;
-
-  return success;
+  return m_rep->save_ascii (os);
 }
 
 bool
 octave_fcn_handle::load_ascii (std::istream& is)
 {
-  bool success = true;
+  octave::base_fcn_handle *new_rep = nullptr;
+
+  // Read enough to detect type then create new rep object and dispatch
+  // to finish loading object.
 
   std::streampos pos = is.tellg ();
+
   std::string octaveroot = extract_keyword (is, "octaveroot", true);
   if (octaveroot.empty ())
     {
       is.seekg (pos);
       is.clear ();
     }
+
   pos = is.tellg ();
+
   std::string fpath = extract_keyword (is, "path", true);
   if (fpath.empty ())
     {
@@ -721,143 +2531,95 @@
       is.clear ();
     }
 
-  is >> m_name;
-
-  if (m_name == anonymous)
+  if (! (octaveroot.empty () || fpath.empty ()))
+    {
+      size_t len = octaveroot.size ();
+      if (octaveroot == fpath.substr (0, len))
+        fpath = octave::config::octave_exec_home () + fpath.substr (len);
+    }
+
+  pos = is.tellg ();
+
+  std::string subtype = extract_keyword (is, "subtype", true);
+  if (subtype.empty ())
     {
-      skip_preceeding_newline (is);
-
-      std::string buf;
-
-      if (is)
-        {
-
-          // Get a line of text whitespace characters included, leaving
-          // newline in the stream.
-          buf = read_until_newline (is, true);
-
-        }
-
-      pos = is.tellg ();
-
-      octave::unwind_protect_safe frame;
-
-      // Set up temporary scope to use for evaluating the text that
-      // defines the anonymous function.
-
-      octave::interpreter& interp
-        = octave::__get_interpreter__ ("octave_fcn_handle::load_ascii");
-
-      octave::tree_evaluator& tw = interp.get_evaluator ();
-
-      tw.push_dummy_scope (buf);
-      frame.add_method (tw, &octave::tree_evaluator::pop_scope);
-
-      octave_idx_type len = 0;
-
-      if (extract_keyword (is, "length", len, true) && len >= 0)
-        {
-          if (len > 0)
-            {
-              for (octave_idx_type i = 0; i < len; i++)
-                {
-                  octave_value t2;
-                  bool dummy;
-
-                  std::string name
-                    = read_text_data (is, "", dummy, t2, i);
-
-                  if (! is)
-                    error ("load: failed to load anonymous function handle");
-
-                  interp.assign (name, t2);
-                }
-            }
-        }
+      is.seekg (pos);
+      is.clear ();
+
+      // We have a legacy file that can contain either an anonymous
+      // function or a simple function handle.
+
+      std::string name;
+      is >> name;
+
+      if (name == anonymous)
+        new_rep = new octave::anonymous_fcn_handle ();
       else
-        {
-          is.seekg (pos);
-          is.clear ();
-        }
-
-      if (is && success)
-        success = parse_anon_fcn_handle (buf);
-      else
-        success = false;
+        new_rep = new octave::simple_fcn_handle (name, fpath, octaveroot);
     }
   else
-    success = set_fcn (octaveroot, fpath);
-
-  return success;
+    {
+      // Load individual function handle types.
+
+      if (subtype == "simple")
+        {
+          std::string name;
+          is >> name;
+
+          new_rep = new octave::simple_fcn_handle (name, fpath, octaveroot);
+        }
+      else if (subtype == "scopedfunction")
+        {
+          std::string name;
+          is >> name;
+
+          new_rep = new octave::scoped_fcn_handle (name, fpath, octaveroot);
+        }
+      else if (subtype == "anonymous")
+        new_rep = new octave::anonymous_fcn_handle ();
+      else if (subtype == "nested")
+        {
+          std::string name;
+          is >> name;
+
+          new_rep = new octave::nested_fcn_handle (name, fpath, octaveroot);
+        }
+      else if (subtype == "classsimple")
+        {
+          std::string name;
+          is >> name;
+
+          new_rep = new octave::class_simple_fcn_handle (name, fpath, octaveroot);
+        }
+    }
+
+  if (! new_rep)
+    return false;
+
+  if (! new_rep->load_ascii (is))
+    {
+      delete new_rep;
+      return false;
+    }
+
+  delete m_rep;
+  m_rep = new_rep;
+
+  return true;
 }
 
 bool
 octave_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
 {
-  if (m_name == anonymous)
-    {
-      std::ostringstream nmbuf;
-
-      if (m_fcn.is_undefined ())
-        return false;
-
-      octave_user_function *f = m_fcn.user_function_value ();
-
-      octave_user_function::local_vars_map local_vars
-        = f->local_var_init_vals ();
-
-      size_t varlen = local_vars.size ();
-
-      if (varlen > 0)
-        nmbuf << m_name << ' ' << varlen;
-      else
-        nmbuf << m_name;
-
-      std::string buf_str = nmbuf.str ();
-      int32_t tmp = buf_str.length ();
-      os.write (reinterpret_cast<char *> (&tmp), 4);
-      os.write (buf_str.c_str (), buf_str.length ());
-
-      std::ostringstream buf;
-      print_raw (buf, true);
-      std::string stmp = buf.str ();
-      tmp = stmp.length ();
-      os.write (reinterpret_cast<char *> (&tmp), 4);
-      os.write (stmp.c_str (), stmp.length ());
-
-      if (varlen > 0)
-        {
-          for (const auto& nm_val : local_vars)
-            {
-              if (! save_binary_data (os, nm_val.second, nm_val.first,
-                                      "", 0, save_as_floats))
-                return ! os.fail ();
-            }
-        }
-    }
-  else
-    {
-      std::ostringstream nmbuf;
-
-      octave_function *f = function_value ();
-      std::string fnm = (f ? f->fcn_file_name () : "");
-
-      nmbuf << m_name << "\n" << octave::config::octave_exec_home () << "\n" << fnm;
-
-      std::string buf_str = nmbuf.str ();
-      int32_t tmp = buf_str.length ();
-      os.write (reinterpret_cast<char *> (&tmp), 4);
-      os.write (buf_str.c_str (), buf_str.length ());
-    }
-
-  return true;
+  return m_rep->save_binary (os, save_as_floats);
 }
 
 bool
 octave_fcn_handle::load_binary (std::istream& is, bool swap,
                                 octave::mach_info::float_format fmt)
 {
-  bool success = true;
+  // Read enough to detect type then create new rep object and dispatch
+  // to finish loading object.
 
   int32_t tmp;
   if (! is.read (reinterpret_cast<char *> (&tmp), 4))
@@ -870,359 +2632,109 @@
   // effectively not reading over file end
   is.read (ctmp1, tmp);
   ctmp1[tmp] = 0;
-  m_name = std::string (ctmp1);
+  std::string name (ctmp1);
 
   if (! is)
     return false;
 
+  octave::base_fcn_handle *new_rep = nullptr;
+
   size_t anl = anonymous.length ();
 
-  if (m_name.length () >= anl && m_name.substr (0, anl) == anonymous)
+  if (name.length () >= anl && name.substr (0, anl) == anonymous)
     {
-      octave_idx_type len = 0;
-
-      if (m_name.length () > anl)
-        {
-          std::istringstream nm_is (m_name.substr (anl));
-          nm_is >> len;
-          m_name = m_name.substr (0, anl);
-        }
-
-      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
-        return false;
-      if (swap)
-        swap_bytes<4> (&tmp);
-
-      OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
-      // is.get (ctmp2, tmp+1, 0); caused is.eof () to be true though
-      // effectively not reading over file end
-      is.read (ctmp2, tmp);
-      ctmp2[tmp] = 0;
-
-      octave::unwind_protect_safe frame;
-
-      // Set up temporary scope to use for evaluating the text that
-      // defines the anonymous function.
-
-      octave::interpreter& interp
-        = octave::__get_interpreter__ ("octave_fcn_handle::load_binary");
-
-      octave::tree_evaluator& tw = interp.get_evaluator ();
-
-      tw.push_dummy_scope (ctmp2);
-      frame.add_method (tw, &octave::tree_evaluator::pop_scope);
-
-      if (len > 0)
-        {
-          for (octave_idx_type i = 0; i < len; i++)
-            {
-              octave_value t2;
-              bool dummy;
-              std::string doc;
-
-              std::string name
-                = read_binary_data (is, swap, fmt, "", dummy, t2, doc);
-
-              if (! is)
-                error ("load: failed to load anonymous function handle");
-
-              interp.assign (name, t2);
-            }
-        }
-
-      if (is && success)
-        success = parse_anon_fcn_handle (ctmp2);
-      else
-        success = false;
+      // Even with extra info stored in the function name, anonymous
+      // functions look the same.  Note that NAME here may have the
+      // number of local variables appended.  We decode that inside the
+      // load_binary function.
+
+      new_rep = new octave::anonymous_fcn_handle (name);
     }
   else
     {
+      // Unpack extra info stored with the function name and load
+      // individual function handle types.
+      // FIXME: is there a better way?
+
       std::string octaveroot;
       std::string fpath;
-
-      if (m_name.find_first_of ('\n') != std::string::npos)
+      std::string subtype = "simple";
+
+      if (name.find_first_of ('\n') != std::string::npos)
+        {
+          size_t pos1 = name.find_first_of ('\n');
+          size_t pos2 = name.find_first_of ('\n', pos1 + 1);
+          octaveroot = name.substr (pos1 + 1, pos2 - pos1 - 1);
+          fpath = name.substr (pos2 + 1);
+          name = name.substr (0, pos1);
+        }
+
+      size_t pos1 = name.find ('@');
+      if (pos1 != std::string::npos)
         {
-          size_t pos1 = m_name.find_first_of ('\n');
-          size_t pos2 = m_name.find_first_of ('\n', pos1 + 1);
-          octaveroot = m_name.substr (pos1 + 1, pos2 - pos1 - 1);
-          fpath = m_name.substr (pos2 + 1);
-          m_name = m_name.substr (0, pos1);
+          if (name[pos1+1] == '<')
+            {
+              size_t pos2 = name.find ('>', pos1 + 2);
+
+              if (pos2 != std::string::npos)
+                subtype = name.substr (pos1 + 2, pos2 - pos1 - 2);
+            }
+
+          name = name.substr (0, pos1);
         }
 
-      success = set_fcn (octaveroot, fpath);
+      // Anonymous should have been handled above so it is not in the
+      // following list.
+
+      if (subtype == "simple")
+        new_rep = new octave::simple_fcn_handle (name, fpath, octaveroot);
+      else if (subtype == "scopedfunction")
+        new_rep = new octave::scoped_fcn_handle (name, fpath, octaveroot);
+      else if (subtype == "nested")
+        new_rep = new octave::nested_fcn_handle (name, fpath, octaveroot);
+      else if (subtype == "classsimple")
+        new_rep = new octave::class_simple_fcn_handle (name, fpath, octaveroot);
     }
 
-  return success;
+  if (! new_rep)
+    return false;
+
+  if (! new_rep->load_binary (is, swap, fmt))
+    {
+      delete new_rep;
+      return false;
+    }
+
+  delete m_rep;
+  m_rep = new_rep;
+
+  return true;
 }
 
 bool
 octave_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
                               bool save_as_floats)
 {
-#if defined (HAVE_HDF5)
-
-  bool retval = true;
-
-  hid_t group_hid = -1;
-#if defined (HAVE_HDF5_18)
-  group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
-                         octave_H5P_DEFAULT);
-#else
-  group_hid = H5Gcreate (loc_id, name, 0);
-#endif
-  if (group_hid < 0)
-    return false;
-
-  hid_t space_hid, data_hid, type_hid;
-  space_hid = data_hid = type_hid = -1;
-
-  // attach the type of the variable
-  type_hid = H5Tcopy (H5T_C_S1);
-  H5Tset_size (type_hid, m_name.length () + 1);
-  if (type_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
-  hdims[0] = 0;
-  hdims[1] = 0;
-  space_hid = H5Screate_simple (0, hdims, nullptr);
-  if (space_hid < 0)
-    {
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
-                        octave_H5P_DEFAULT, octave_H5P_DEFAULT,
-                        octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
-                        octave_H5P_DEFAULT);
-#endif
-  if (data_hid < 0
-      || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
-                   octave_H5P_DEFAULT, m_name.c_str ()) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-  H5Dclose (data_hid);
-
-  if (m_name == anonymous)
-    {
-      std::ostringstream buf;
-      print_raw (buf, true);
-      std::string stmp = buf.str ();
-
-      // attach the type of the variable
-      H5Tset_size (type_hid, stmp.length () + 1);
-      if (type_hid < 0)
-        {
-          H5Sclose (space_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-#if defined (HAVE_HDF5_18)
-      data_hid = H5Dcreate (group_hid, "fcn",  type_hid, space_hid,
-                            octave_H5P_DEFAULT, octave_H5P_DEFAULT,
-                            octave_H5P_DEFAULT);
-#else
-      data_hid = H5Dcreate (group_hid, "fcn",  type_hid, space_hid,
-                            octave_H5P_DEFAULT);
-#endif
-      if (data_hid < 0
-          || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
-                       octave_H5P_DEFAULT, stmp.c_str ()) < 0)
-        {
-          H5Sclose (space_hid);
-          H5Tclose (type_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-      H5Dclose (data_hid);
-
-      octave_user_function *f = m_fcn.user_function_value ();
-
-      octave_user_function::local_vars_map local_vars
-        = f->local_var_init_vals ();
-
-      size_t varlen = local_vars.size ();
-
-      if (varlen > 0)
-        {
-          hid_t as_id = H5Screate (H5S_SCALAR);
-
-          if (as_id >= 0)
-            {
-#if defined (HAVE_HDF5_18)
-              hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE",
-                                      H5T_NATIVE_IDX, as_id,
-                                      octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-
-#else
-              hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE",
-                                      H5T_NATIVE_IDX, as_id, octave_H5P_DEFAULT);
-#endif
-
-              if (a_id >= 0)
-                {
-                  retval = (H5Awrite (a_id, H5T_NATIVE_IDX, &varlen) >= 0);
-
-                  H5Aclose (a_id);
-                }
-              else
-                retval = false;
-
-              H5Sclose (as_id);
-            }
-          else
-            retval = false;
-#if defined (HAVE_HDF5_18)
-          data_hid = H5Gcreate (group_hid, "symbol table",
-                                octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-          data_hid = H5Gcreate (group_hid, "symbol table", 0);
-#endif
-          if (data_hid < 0)
-            {
-              H5Sclose (space_hid);
-              H5Tclose (type_hid);
-              H5Gclose (group_hid);
-              return false;
-            }
-
-          for (const auto& nm_val : local_vars)
-            {
-              if (! add_hdf5_data (data_hid, nm_val.second, nm_val.first,
-                                   "", false, save_as_floats))
-                break;
-            }
-          H5Gclose (data_hid);
-        }
-    }
-  else
-    {
-      std::string octaveroot = octave::config::octave_exec_home ();
-
-      octave_function *f = function_value ();
-      std::string fpath = (f ? f->fcn_file_name () : "");
-
-      H5Sclose (space_hid);
-      hdims[0] = 1;
-      hdims[1] = octaveroot.length ();
-      space_hid = H5Screate_simple (0, hdims, nullptr);
-      if (space_hid < 0)
-        {
-          H5Tclose (type_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-      H5Tclose (type_hid);
-      type_hid = H5Tcopy (H5T_C_S1);
-      H5Tset_size (type_hid, octaveroot.length () + 1);
-#if defined (HAVE_HDF5_18)
-      hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT",
-                              type_hid, space_hid, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-      hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT",
-                              type_hid, space_hid, octave_H5P_DEFAULT);
-#endif
-
-      if (a_id >= 0)
-        {
-          retval = (H5Awrite (a_id, type_hid, octaveroot.c_str ()) >= 0);
-
-          H5Aclose (a_id);
-        }
-      else
-        {
-          H5Sclose (space_hid);
-          H5Tclose (type_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-      H5Sclose (space_hid);
-      hdims[0] = 1;
-      hdims[1] = fpath.length ();
-      space_hid = H5Screate_simple (0, hdims, nullptr);
-      if (space_hid < 0)
-        {
-          H5Tclose (type_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-      H5Tclose (type_hid);
-      type_hid = H5Tcopy (H5T_C_S1);
-      H5Tset_size (type_hid, fpath.length () + 1);
-
-#if defined (HAVE_HDF5_18)
-      a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid,
-                        octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-      a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid, octave_H5P_DEFAULT);
-#endif
-
-      if (a_id >= 0)
-        {
-          retval = (H5Awrite (a_id, type_hid, fpath.c_str ()) >= 0);
-
-          H5Aclose (a_id);
-        }
-      else
-        retval = false;
-    }
-
-  H5Sclose (space_hid);
-  H5Tclose (type_hid);
-  H5Gclose (group_hid);
-
-  return retval;
-
-#else
-  octave_unused_parameter (loc_id);
-  octave_unused_parameter (name);
-  octave_unused_parameter (save_as_floats);
-
-  warn_save ("hdf5");
-
-  return false;
-#endif
+  return m_rep->save_hdf5 (loc_id, name, save_as_floats);
 }
 
 bool
-octave_fcn_handle::load_hdf5 (octave_hdf5_id loc_id, const char *name)
+octave_fcn_handle::load_hdf5 (octave_hdf5_id loc_id, const char *name_arg)
 {
 #if defined (HAVE_HDF5)
 
-  bool success = true;
-
-  hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id;
-  hsize_t rank;
-  int slen;
-
 #if defined (HAVE_HDF5_18)
-  group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
+  hid_t group_hid = H5Gopen (loc_id, name_arg, octave_H5P_DEFAULT);
 #else
-  group_hid = H5Gopen (loc_id, name);
+  hid_t group_hid = H5Gopen (loc_id, name_arg);
 #endif
   if (group_hid < 0)
     return false;
 
 #if defined (HAVE_HDF5_18)
-  data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
+  hid_t data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
 #else
-  data_hid = H5Dopen (group_hid, "nm");
+  hid_t data_hid = H5Dopen (group_hid, "nm");
 #endif
 
   if (data_hid < 0)
@@ -1231,8 +2743,8 @@
       return false;
     }
 
-  type_hid = H5Dget_type (data_hid);
-  type_class_hid = H5Tget_class (type_hid);
+  hid_t type_hid = H5Dget_type (data_hid);
+  hid_t type_class_hid = H5Tget_class (type_hid);
 
   if (type_class_hid != H5T_STRING)
     {
@@ -1242,8 +2754,8 @@
       return false;
     }
 
-  space_hid = H5Dget_space (data_hid);
-  rank = H5Sget_simple_extent_ndims (space_hid);
+  hid_t space_hid = H5Dget_space (data_hid);
+  hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
 
   if (rank != 0)
     {
@@ -1254,7 +2766,7 @@
       return false;
     }
 
-  slen = H5Tget_size (type_hid);
+  int slen = H5Tget_size (type_hid);
   if (slen < 0)
     {
       H5Sclose (space_hid);
@@ -1267,7 +2779,7 @@
   OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
 
   // create datatype for (null-terminated) string to read into:
-  st_id = H5Tcopy (H5T_C_S1);
+  hid_t st_id = H5Tcopy (H5T_C_S1);
   H5Tset_size (st_id, slen);
 
   if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
@@ -1283,260 +2795,95 @@
     }
   H5Tclose (st_id);
   H5Dclose (data_hid);
-  m_name = nm_tmp;
-
-  if (m_name == anonymous)
+
+  std::string name (nm_tmp);
+
+  octave::base_fcn_handle *new_rep = nullptr;
+
+  if (name == anonymous)
     {
-#if defined (HAVE_HDF5_18)
-      data_hid = H5Dopen (group_hid, "fcn", octave_H5P_DEFAULT);
-#else
-      data_hid = H5Dopen (group_hid, "fcn");
-#endif
-
-      if (data_hid < 0)
-        {
-          H5Sclose (space_hid);
-          H5Tclose (type_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-      H5Tclose (type_hid);
-      type_hid = H5Dget_type (data_hid);
-      type_class_hid = H5Tget_class (type_hid);
-
-      if (type_class_hid != H5T_STRING)
-        {
-          H5Sclose (space_hid);
-          H5Tclose (type_hid);
-          H5Dclose (data_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-      H5Sclose (space_hid);
-      space_hid = H5Dget_space (data_hid);
-      rank = H5Sget_simple_extent_ndims (space_hid);
-
-      if (rank != 0)
-        {
-          H5Sclose (space_hid);
-          H5Tclose (type_hid);
-          H5Dclose (data_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-      slen = H5Tget_size (type_hid);
-      if (slen < 0)
-        {
-          H5Sclose (space_hid);
-          H5Tclose (type_hid);
-          H5Dclose (data_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-
-      OCTAVE_LOCAL_BUFFER (char, fcn_tmp, slen);
-
-      // create datatype for (null-terminated) string to read into:
-      st_id = H5Tcopy (H5T_C_S1);
-      H5Tset_size (st_id, slen);
-
-      if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
-                   octave_H5P_DEFAULT, fcn_tmp)
-          < 0)
-        {
-          H5Tclose (st_id);
-          H5Sclose (space_hid);
-          H5Tclose (type_hid);
-          H5Dclose (data_hid);
-          H5Gclose (group_hid);
-          return false;
-        }
-      H5Tclose (st_id);
-      H5Dclose (data_hid);
-
-      octave_idx_type len = 0;
-
-      // we have to pull some shenanigans here to make sure
-      // HDF5 doesn't print out all sorts of error messages if we
-      // call H5Aopen for a non-existing attribute
-
-      H5E_auto_t err_func;
-      void *err_func_data;
-
-      // turn off error reporting temporarily, but save the error
-      // reporting function:
-#if defined (HAVE_HDF5_18)
-      H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data);
-      H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
-#else
-      H5Eget_auto (&err_func, &err_func_data);
-      H5Eset_auto (nullptr, nullptr);
-#endif
-
-      hid_t attr_id = H5Aopen_name (group_hid, "SYMBOL_TABLE");
-
-      if (attr_id >= 0)
-        {
-          if (H5Aread (attr_id, H5T_NATIVE_IDX, &len) < 0)
-            success = false;
-
-          H5Aclose (attr_id);
-        }
-
-      // restore error reporting:
-#if defined (HAVE_HDF5_18)
-      H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data);
-#else
-      H5Eset_auto (err_func, err_func_data);
-#endif
-
-      octave::unwind_protect_safe frame;
-
-      // Set up temporary scope to use for evaluating the text that
-      // defines the anonymous function.
-
-      octave::interpreter& interp
-        = octave::__get_interpreter__ ("octave_fcn_handle::load_hdf5");
-
-      octave::tree_evaluator& tw = interp.get_evaluator ();
-
-      tw.push_dummy_scope (fcn_tmp);
-      frame.add_method (tw, &octave::tree_evaluator::pop_scope);
-
-      if (len > 0 && success)
-        {
-          hsize_t num_obj = 0;
-#if defined (HAVE_HDF5_18)
-          data_hid = H5Gopen (group_hid, "symbol table", octave_H5P_DEFAULT);
-#else
-          data_hid = H5Gopen (group_hid, "symbol table");
-#endif
-          H5Gget_num_objs (data_hid, &num_obj);
-          H5Gclose (data_hid);
-
-          if (num_obj != static_cast<hsize_t> (len))
-            error ("load: failed to load anonymous function handle");
-
-          hdf5_callback_data dsub;
-          int current_item = 0;
-          for (octave_idx_type i = 0; i < len; i++)
-            {
-              if (hdf5_h5g_iterate (group_hid, "symbol table", &current_item,
-                                    &dsub) <= 0)
-                error ("load: failed to load anonymous function handle");
-
-              interp.assign (dsub.name, dsub.tc);
-            }
-        }
-
-      if (success)
-        success = parse_anon_fcn_handle (fcn_tmp);
+      // Even with extra info stored in the function name, anonymous
+      // functions look the same.
+
+      new_rep = new octave::anonymous_fcn_handle ();
     }
   else
     {
+      // Unpack extra info stored with the function name and load
+      // individual function handle types.
+      // FIXME: is there a better way?
+
       std::string octaveroot;
       std::string fpath;
-
-      // we have to pull some shenanigans here to make sure
-      // HDF5 doesn't print out all sorts of error messages if we
-      // call H5Aopen for a non-existing attribute
-
-      H5E_auto_t err_func;
-      void *err_func_data;
-
-      // turn off error reporting temporarily, but save the error
-      // reporting function:
-#if defined (HAVE_HDF5_18)
-      H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data);
-      H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
-#else
-      H5Eget_auto (&err_func, &err_func_data);
-      H5Eset_auto (nullptr, nullptr);
-#endif
-
-      hid_t attr_id = H5Aopen_name (group_hid, "OCTAVEROOT");
-      if (attr_id >= 0)
+      std::string subtype = "simple";
+
+      if (name.find_first_of ('\n') != std::string::npos)
         {
-          H5Tclose (type_hid);
-          type_hid = H5Aget_type (attr_id);
-          type_class_hid = H5Tget_class (type_hid);
-
-          if (type_class_hid != H5T_STRING)
-            success = false;
-          else
+          size_t pos1 = name.find_first_of ('\n');
+          size_t pos2 = name.find_first_of ('\n', pos1 + 1);
+          octaveroot = name.substr (pos1 + 1, pos2 - pos1 - 1);
+          fpath = name.substr (pos2 + 1);
+          name = name.substr (0, pos1);
+        }
+
+      size_t pos1 = name.find ('@');
+      if (pos1 != std::string::npos)
+        {
+          if (name[pos1+1] == '<')
             {
-              slen = H5Tget_size (type_hid);
-              st_id = H5Tcopy (H5T_C_S1);
-              H5Tset_size (st_id, slen);
-              OCTAVE_LOCAL_BUFFER (char, root_tmp, slen);
-
-              if (H5Aread (attr_id, st_id, root_tmp) < 0)
-                success = false;
-              else
-                octaveroot = root_tmp;
-
-              H5Tclose (st_id);
+              size_t pos2 = name.find ('>', pos1 + 2);
+
+              if (pos2 != std::string::npos)
+                subtype = name.substr (pos1 + 2, pos2 - pos1 - 2);
             }
 
-          H5Aclose (attr_id);
+          name = name.substr (0, pos1);
         }
 
-      if (success)
+      // Anonymous should have been handled above so it is not in the
+      // following list.
+
+      if (subtype == "simple")
+        new_rep = new octave::simple_fcn_handle (name, fpath, octaveroot);
+      else if (subtype == "scopedfunction")
+        new_rep = new octave::scoped_fcn_handle (name, fpath, octaveroot);
+      else if (subtype == "nested")
+        new_rep = new octave::nested_fcn_handle (name, fpath, octaveroot);
+      else if (subtype == "classsimple")
+        new_rep = new octave::class_simple_fcn_handle (name, fpath, octaveroot);
+    }
+
+  bool status = false;
+
+  if (new_rep)
+    {
+      if (new_rep->load_hdf5 (group_hid, space_hid, type_hid))
         {
-          attr_id = H5Aopen_name (group_hid, "FILE");
-          if (attr_id >= 0)
-            {
-              H5Tclose (type_hid);
-              type_hid = H5Aget_type (attr_id);
-              type_class_hid = H5Tget_class (type_hid);
-
-              if (type_class_hid != H5T_STRING)
-                success = false;
-              else
-                {
-                  slen = H5Tget_size (type_hid);
-                  st_id = H5Tcopy (H5T_C_S1);
-                  H5Tset_size (st_id, slen);
-                  OCTAVE_LOCAL_BUFFER (char, path_tmp, slen);
-
-                  if (H5Aread (attr_id, st_id, path_tmp) < 0)
-                    success = false;
-                  else
-                    fpath = path_tmp;
-
-                  H5Tclose (st_id);
-                }
-
-              H5Aclose (attr_id);
-            }
+          delete m_rep;
+          m_rep = new_rep;
+          status = true;
         }
-
-      // restore error reporting:
-#if defined (HAVE_HDF5_18)
-      H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data);
-#else
-      H5Eset_auto (err_func, err_func_data);
-#endif
-
-      success = (success ? set_fcn (octaveroot, fpath) : success);
+      else
+        delete new_rep;
     }
 
+  // FIXME: manage these with an unwind_action object?
+
   H5Tclose (type_hid);
   H5Sclose (space_hid);
   H5Gclose (group_hid);
 
-  return success;
+  return status;
 
 #else
+
   octave_unused_parameter (loc_id);
-  octave_unused_parameter (name);
+  octave_unused_parameter (name_arg);
 
   warn_load ("hdf5");
 
   return false;
+
 #endif
 }
 
@@ -1639,248 +2986,54 @@
 void
 octave_fcn_handle::print_raw (std::ostream& os, bool pr_as_read_syntax) const
 {
-  bool printed = false;
-
-  if (m_name == anonymous)
-    {
-      octave::tree_print_code tpc (os);
-
-      // FCN is const because this member function is, so we can't
-      // use it to call user_function_value, so we make a copy first.
-
-      octave_value ftmp = m_fcn;
-
-      octave_user_function *f = ftmp.user_function_value ();
-
-      if (f)
-        {
-          os << "@";
-
-          // The parameter list should always be valid for anonymous
-          // functions, so we should always call accept for it, and it
-          // will print the parens for us.
-
-          octave::tree_parameter_list *p = f->parameter_list ();
-
-          if (p)
-            p->accept (tpc);
-
-          os << " ";
-
-          octave::tree_statement_list *b = f->body ();
-
-          assert (b->length () == 1);
-
-          octave::tree_statement *s = b->front ();
-
-          if (s)
-            {
-              assert (s->is_expression ());
-
-              octave::tree_expression *e = s->expression ();
-
-              if (e)
-                tpc.print_fcn_handle_body (e);
-            }
-
-          printed = true;
-        }
-    }
-
-  if (! printed)
-    octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
-                           current_print_indent_level ());
+  m_rep->print_raw (os, pr_as_read_syntax, current_print_indent_level ());
+}
+
+bool
+is_equal_to (const octave_fcn_handle& fh1, const octave_fcn_handle& fh2)
+{
+  // FIXME: Maybe there is a better way?  Possibly by using typeid or
+  // typeindex?
+
+  // Don't include invalid_fcn_handle in the list of types to compare.
+  // Consider them to be like NaN values so comparisons between any two
+  // invalid handles are always false.
+
+  if (fh1.is_internal () && fh2.is_internal ())
+    return is_equal_to (*dynamic_cast<octave::internal_fcn_handle *> (fh1.get_rep ()),
+                        *dynamic_cast<octave::internal_fcn_handle *> (fh2.get_rep ()));
+  else if (fh1.is_simple () && fh2.is_simple ())
+    return is_equal_to (*dynamic_cast<octave::simple_fcn_handle *> (fh1.get_rep ()),
+                        *dynamic_cast<octave::simple_fcn_handle *> (fh2.get_rep ()));
+  else if (fh1.is_scoped () && fh2.is_scoped ())
+    return is_equal_to (*dynamic_cast<octave::scoped_fcn_handle *> (fh1.get_rep ()),
+                        *dynamic_cast<octave::scoped_fcn_handle *> (fh2.get_rep ()));
+  else if (fh1.is_nested () && fh2.is_nested ())
+    return is_equal_to (*dynamic_cast<octave::nested_fcn_handle *> (fh1.get_rep ()),
+                        *dynamic_cast<octave::nested_fcn_handle *> (fh2.get_rep ()));
+  else if (fh1.is_class_simple () && fh2.is_class_simple ())
+    return is_equal_to (*dynamic_cast<octave::class_simple_fcn_handle *> (fh1.get_rep ()),
+                        *dynamic_cast<octave::class_simple_fcn_handle *> (fh2.get_rep ()));
+  else if (fh1.is_anonymous () && fh2.is_anonymous ())
+    return is_equal_to (*dynamic_cast<octave::anonymous_fcn_handle *> (fh1.get_rep ()),
+                        *dynamic_cast<octave::anonymous_fcn_handle *> (fh2.get_rep ()));
+  else
+    return false;
 }
 
 namespace octave
 {
-  // Hmm, should this function be a member of the interpreter class,
-  // possibly forwarded to an actual implementation in the
-  // tree_evaluator class?
+  // DEPRECATED in Octave 6.
 
   octave_value
   make_fcn_handle (interpreter& interp, const std::string& nm)
   {
-    octave_value retval;
-
-    // Bow to the god of compatibility.
-
-    // FIXME: it seems ugly to put this here, but there is no single
-    // function in the parser that converts from the operator name to
-    // the corresponding function name.  At least try to do it without N
-    // string compares.
-
-    std::string tnm = nm;
-
-    size_t len = nm.length ();
-
-    if (len == 3 && nm == ".**")
-      tnm = "power";
-    else if (len == 2)
-      {
-        if (nm[0] == '.')
-          {
-            switch (nm[1])
-              {
-              case '\'':
-                tnm = "transpose";
-                break;
-
-              case '+':
-                tnm = "plus";
-                break;
-
-              case '-':
-                tnm = "minus";
-                break;
-
-              case '*':
-                tnm = "times";
-                break;
-
-              case '/':
-                tnm = "rdivide";
-                break;
-
-              case '^':
-                tnm = "power";
-                break;
-
-              case '\\':
-                tnm = "ldivide";
-                break;
-              }
-          }
-        else if (nm[1] == '=')
-          {
-            switch (nm[0])
-              {
-              case '<':
-                tnm = "le";
-                break;
-
-              case '=':
-                tnm = "eq";
-                break;
-
-              case '>':
-                tnm = "ge";
-                break;
-
-              case '~':
-              case '!':
-                tnm = "ne";
-                break;
-              }
-          }
-        else if (nm == "**")
-          tnm = "mpower";
-      }
-    else if (len == 1)
-      {
-        switch (nm[0])
-          {
-          case '~':
-          case '!':
-            tnm = "not";
-            break;
-
-          case '\'':
-            tnm = "ctranspose";
-            break;
-
-          case '+':
-            tnm = "plus";
-            break;
-
-          case '-':
-            tnm = "minus";
-            break;
-
-          case '*':
-            tnm = "mtimes";
-            break;
-
-          case '/':
-            tnm = "mrdivide";
-            break;
-
-          case '^':
-            tnm = "mpower";
-            break;
-
-          case '\\':
-            tnm = "mldivide";
-            break;
-
-          case '<':
-            tnm = "lt";
-            break;
-
-          case '>':
-            tnm = "gt";
-            break;
-
-          case '&':
-            tnm = "and";
-            break;
-
-          case '|':
-            tnm = "or";
-            break;
-          }
-      }
-
     tree_evaluator& tw = interp.get_evaluator ();
 
-    symbol_scope curr_scope = tw.get_current_scope ();
-
-    octave_fcn_handle *fh = new octave_fcn_handle (curr_scope, tnm);
-
-    std::string dispatch_class;
-
-    if (tw.is_class_method_executing (dispatch_class)
-        || tw.is_class_constructor_executing (dispatch_class))
-      fh->set_dispatch_class (dispatch_class);
-
-    return octave_value (fh);
+    return tw.make_fcn_handle (nm);
   }
 }
 
-/*
-%!test
-%! x = {".**", "power";
-%!      ".'", "transpose";
-%!      ".+", "plus";
-%!      ".-", "minus";
-%!      ".*", "times";
-%!      "./", "rdivide";
-%!      ".^", "power";
-%!      ".\\", "ldivide";
-%!      "<=", "le";
-%!      "==", "eq";
-%!      ">=", "ge";
-%!      "!=", "ne";
-%!      "~=", "ne";
-%!      "**", "mpower";
-%!      "~", "not";
-%!      "!", "not";
-%!      "\'", "ctranspose";
-%!      "+", "plus";
-%!      "-", "minus";
-%!      "*", "mtimes";
-%!      "/", "mrdivide";
-%!      "^", "mpower";
-%!      "\\", "mldivide";
-%!      "<", "lt";
-%!      ">", "gt";
-%!      "&", "and";
-%!      "|", "or"};
-%! for i = 1:rows (x)
-%!   assert (functions (str2func (x{i,1})).function, x{i,2});
-%! endfor
-*/
-
 DEFUN (functions, args, ,
        doc: /* -*- texinfo -*-
 @deftypefn {} {@var{s} =} functions (@var{fcn_handle})
@@ -1937,62 +3090,7 @@
 
   octave_fcn_handle *fh = args(0).xfcn_handle_value ("functions: FCN_HANDLE argument must be a function handle object");
 
-  octave_function *fcn = (fh ? fh->function_value () : nullptr);
-
-  if (! fcn)
-    error ("functions: FCN_HANDLE is not a valid function handle object");
-
-  octave_scalar_map m;
-
-  std::string fh_nm = fh->fcn_name ();
-
-  if (fh_nm == octave_fcn_handle::anonymous)
-    {
-      std::ostringstream buf;
-      fh->print_raw (buf);
-      m.setfield ("function", buf.str ());
-
-      m.setfield ("type", "anonymous");
-    }
-  else
-    {
-      m.setfield ("function", fh_nm);
-
-      if (fcn->is_subfunction ())
-        {
-          m.setfield ("type", "subfunction");
-          Cell parentage (dim_vector (1, 2));
-          parentage.elem (0) = fh_nm;
-          parentage.elem (1) = fcn->parent_fcn_name ();
-          m.setfield ("parentage", octave_value (parentage));
-        }
-      else if (fcn->is_private_function ())
-        m.setfield ("type", "private");
-      else if (fh->is_nested ())
-        m.setfield ("type", "nested");
-      else
-        m.setfield ("type", "simple");
-    }
-
-  std::string nm = fcn->fcn_file_name ();
-
-  if (fh_nm == octave_fcn_handle::anonymous)
-    {
-      m.setfield ("file", nm);
-
-      m.setfield ("workspace", fh->workspace ());
-    }
-  else if (fcn->is_user_function () || fcn->is_user_script ())
-    {
-      octave_function *fu = fh->function_value ();
-      m.setfield ("file", fu->fcn_file_name ());
-
-      m.setfield ("workspace", fh->workspace ());
-    }
-  else
-    m.setfield ("file", "");
-
-  return ovl (m);
+  return ovl (fh->info ());
 }
 
 DEFUN (func2str, args, ,
@@ -2015,7 +3113,7 @@
 
   std::string fh_nm = fh->fcn_name ();
 
-  if (fh_nm == octave_fcn_handle::anonymous)
+  if (fh->is_anonymous ())
     {
       std::ostringstream buf;
 
@@ -2037,7 +3135,7 @@
 Previous versions of Octave accepted an optional second argument,
 @qcode{"global"}, that caused str2func to ignore locally visible
 functions.  This option is no longer supported.
-@seealso{func2str, inline, functions}
+@seealso{func2str, functions}
 @end deftypefn */)
 {
   int nargin = args.length ();
@@ -2045,18 +3143,31 @@
   if (nargin < 1 || nargin > 2)
     print_usage ();
 
-  std::string nm = args(0).xstring_value ("str2func: FCN_NAME must be a string");
-
-  octave_value retval;
+  std::string nm
+    = args(0).xstring_value ("str2func: FCN_NAME must be a string");
+
+  if (nm.empty ())
+    error ("str2func: invalid function name");
 
   if (nm[0] == '@')
     {
+      // Unlike the anonymous_fcn_handle::parse method, don't set up
+      // temporary scope to use for evaluating the text that defines
+      // the anonymous function.  Here we want
+      //
+      //   str2fun ("@(args) expr")
+      //
+      // to behave the same as if
+      //
+      //   @(args) expr
+      //
+      // were evaluated in the current scope.
+
       int parse_status;
-      octave_value anon_fcn_handle
-        = interp.eval_string (nm, true, parse_status);
+      octave_value afh = interp.eval_string (nm, true, parse_status);
 
       if (parse_status == 0)
-        retval = anon_fcn_handle;
+        return afh;
     }
   else
     {
@@ -2064,10 +3175,12 @@
         warning_with_id ("Octave:str2func-global-argument",
                          "str2func: second argument ignored");
 
-      retval = octave::make_fcn_handle (interp, nm);
+      octave::tree_evaluator& tw = interp.get_evaluator ();
+
+      return tw.make_fcn_handle (nm);
     }
 
-  return retval;
+  return ovl ();
 }
 
 /*
@@ -2116,12 +3229,10 @@
 }
 
 /*
-%!shared fh, finline
+%!shared fh
 %! fh = @(x) x;
-%! finline = inline ("x");
 
 %!assert (is_function_handle (fh))
-%!assert (is_function_handle (finline))
 %!assert (! is_function_handle ({fh}))
 %!assert (! is_function_handle (1))
 
--- a/libinterp/octave-value/ov-fcn-handle.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-fcn-handle.h	Thu Jun 11 01:22:45 2020 -0400
@@ -32,20 +32,130 @@
 #include <list>
 #include <string>
 
+#include "oct-map.h"
 #include "ov-base.h"
 #include "ov-fcn.h"
 #include "ov-typeinfo.h"
+#include "stack-frame.h"
 #include "symscope.h"
 
 namespace octave
 {
   class interpreter;
-  class stack_frame;
   class tree_evaluator;
+
+  // Function handles.
+
+  class base_fcn_handle
+  {
+  public:
+
+    base_fcn_handle (const std::string& name = "",
+                     const std::string& file = "")
+      : m_name (name), m_file (file)
+    { }
+
+    base_fcn_handle (const base_fcn_handle&) = default;
+
+    virtual ~base_fcn_handle (void) = default;
+
+    virtual base_fcn_handle * clone (void) const = 0;
+
+    virtual std::string type (void) const = 0;
+
+    virtual bool is_internal (void) const { return false; }
+
+    virtual bool is_simple (void) const { return false; }
+
+    virtual bool is_scoped (void) const { return false; }
+
+    virtual bool is_nested (void) const { return false; }
+
+    virtual bool is_class_simple (void) const { return false; }
+
+    virtual bool is_anonymous (void) const { return false; }
+
+    std::string fcn_name (void) const { return m_name; }
+
+    std::string file (void) const { return m_file; }
+
+    octave_value_list
+    subsref (const std::string& type, const std::list<octave_value_list>& idx,
+             int nargout);
+
+    virtual octave_value_list
+    call (int nargout, const octave_value_list& args) = 0;
+
+    // FIXME: These must go away.  They don't do the right thing for
+    // scoping or overloads.
+    virtual octave_function * function_value (bool = false)
+    {
+      return nullptr;
+    }
+
+    virtual octave_user_function * user_function_value (bool = false)
+    {
+      return nullptr;
+    }
+
+    virtual octave_value fcn_val (void) { return octave_value (); }
+
+    virtual octave_value workspace (void) const { return octave_value (); }
+
+    // Should be const.
+    virtual octave_scalar_map info (void) { return octave_scalar_map (); }
+
+    virtual void set_dispatch_class (const std::string& /*class_name*/) { }
+
+    virtual std::string get_dispatch_class (void) const { return ""; }
+
+    octave_value convert_to_str_internal (bool pad, bool force, char type) const;
+
+    virtual bool save_ascii (std::ostream& os);
+
+    virtual bool load_ascii (std::istream& is);
+
+    virtual bool save_binary (std::ostream& os, bool save_as_floats);
+
+    virtual bool load_binary (std::istream& is, bool swap,
+                              mach_info::float_format fmt);
+
+    virtual bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
+                            bool save_as_floats);
+
+    virtual bool load_hdf5 (octave_hdf5_id& group_hid,
+                            octave_hdf5_id& space_hid,
+                            octave_hdf5_id& type_hid);
+
+    virtual void print_raw (std::ostream&, bool /*pr_as_read_syntax*/,
+                            int /*current_print_indent_level*/) const
+    { }
+
+    // Function handles are printed without a newline by default.
+    virtual bool print_as_scalar (void) const { return true; }
+
+    virtual bool
+    set_fcn (const std::string& /*octaveroot*/, const std::string& /*fpath*/)
+    {
+      return false;
+    }
+
+  protected:
+
+    void warn_load (const char *file_type) const;
+    void warn_save (const char *file_type) const;
+
+    void unimplemented (const char *op, const char *fmt) const;
+
+    // The name of the handle, not including the "@", or the text of the
+    // anonymous function.
+    std::string m_name;
+
+    // The name of the file where the named function was defined.
+    std::string m_file;
+  };
 }
 
-// Function handles.
-
 class
 OCTINTERP_API
 octave_fcn_handle : public octave_base_value
@@ -54,28 +164,56 @@
 
   static const std::string anonymous;
 
-  octave_fcn_handle (void)
-    : m_fcn (), m_obj (), m_name (), m_scope (), m_is_nested (false),
-      m_closure_frames (nullptr), m_dispatch_class ()
-  { }
+  // Creates an invalid function handle.  Used to create generic
+  // function handle objects when loading function handles.  Further
+  // dispatch happens in the octave_fcn_handle load/save functions.
+  octave_fcn_handle (void);
+
+  // Create a handle to a built-in or internal function.
+  octave_fcn_handle (const octave_value& fcn);
 
-  octave_fcn_handle (const octave::symbol_scope& scope, const std::string& n);
+  // Create a simple function handle that is not bound to a function.
+  // Lookup happens when a function call is attempted.
+  octave_fcn_handle (const std::string& name);
+
+  // Create a simple function handle that is bound to a function.
+  octave_fcn_handle (const octave_value& fcn, const std::string& name);
+
+  // Create a function handle bound to a class method.
+  octave_fcn_handle (const octave_value& fcn, const std::string& class_nm,
+                     const std::string& meth_nm);
 
-  octave_fcn_handle (const octave::symbol_scope& scope,
-                     const octave_value& f,
-                     const std::string& n = anonymous);
+  // Create a function handle bound to a class method.
+  octave_fcn_handle (const octave_value& obj, const octave_value& fcn,
+                     const std::string& class_nm,
+                     const std::string& meth_nm);
+
+  // Create a function handle bound to a scoped function.
+  octave_fcn_handle (const octave_value& fcn, const std::string& name,
+                     const std::list<std::string>& parentage);
 
-  octave_fcn_handle (const octave_value& f,
-                     const std::string& n = anonymous);
+  // Create a handle to a nested function.
+  octave_fcn_handle (const octave_value& fcn, const std::string& name,
+                     const std::shared_ptr<octave::stack_frame>& closure_frames);
 
-  octave_fcn_handle (const octave_fcn_handle& fh) = default;
+  // Create an anonymous function handle with local variable values
+  // provided in LOCAL_VARS.
+  octave_fcn_handle (const octave_value& fcn,
+                     const octave::stack_frame::local_vars_map& local_vars);
 
-  ~octave_fcn_handle (void);
+  octave_fcn_handle (const octave_fcn_handle& fh);
+
+  ~octave_fcn_handle (void) = default;
 
   octave_base_value * clone (void) const
-  { return new octave_fcn_handle (*this); }
+  {
+    return new octave_fcn_handle (*this);
+  }
+
   octave_base_value * empty_clone (void) const
-  { return new octave_fcn_handle (); }
+  {
+    return new octave_fcn_handle ();
+  }
 
   // We don't need to override all three forms of subsref.  The using
   // declaration will avoid warnings about partially-overloaded virtual
@@ -91,43 +229,71 @@
 
   octave_value_list subsref (const std::string& type,
                              const std::list<octave_value_list>& idx,
-                             int nargout);
+                             int nargout)
+  {
+    return m_rep->subsref (type, idx, nargout);
+  }
+
+  octave_value_list call (int nargout, const octave_value_list& args);
 
   bool is_defined (void) const { return true; }
 
   bool is_function_handle (void) const { return true; }
 
-  bool is_anonymous (void) const { return m_name == anonymous; }
+  bool is_internal (void) const { return m_rep->is_internal (); }
+
+  bool is_simple (void) const { return m_rep->is_simple (); }
+
+  bool is_scoped (void) const { return m_rep->is_scoped (); }
 
-  bool is_nested (void) const { return m_is_nested; }
+  bool is_nested (void) const { return m_rep->is_nested (); }
+
+  bool is_class_simple (void) const { return m_rep->is_class_simple (); }
+
+  bool is_anonymous (void) const { return m_rep->is_anonymous (); }
 
   dim_vector dims (void) const;
 
   // FIXME: These must go away.  They don't do the right thing for
   // scoping or overloads.
-  octave_function * function_value (bool = false);
-  octave_user_function * user_function_value (bool = false);
+  octave_function * function_value (bool = false)
+  {
+    return m_rep->function_value ();
+  }
+
+  octave_user_function * user_function_value (bool = false)
+  {
+    return m_rep->user_function_value ();
+  }
 
   octave_fcn_handle * fcn_handle_value (bool = false) { return this; }
 
-  octave_value fcn_val (void) const { return m_fcn; }
+  octave_value fcn_val (void) { return m_rep->fcn_val (); }
 
-  std::string fcn_name (void) const { return m_name; }
+  // FCN_NAME should be eliminated.
+  std::string fcn_name (void) const { return m_rep->fcn_name (); }
 
-  void push_closure_context (octave::tree_evaluator& tw);
+  octave_value workspace (void) const
+  {
+    return m_rep->workspace ();
+  }
 
-  octave_value workspace (void) const;
+  octave_scalar_map info (void) { return m_rep->info (); }
 
   void set_dispatch_class (const std::string& class_name)
   {
-    m_dispatch_class = class_name;
+    m_rep->set_dispatch_class (class_name);
   }
 
-  std::string get_dispatch_class (void) const { return m_dispatch_class; }
+  std::string get_dispatch_class (void) const
+  {
+    return m_rep->get_dispatch_class ();
+  }
 
-  bool is_equal_to (const octave_fcn_handle&) const;
-
-  octave_value convert_to_str_internal (bool pad, bool force, char type) const;
+  octave_value convert_to_str_internal (bool pad, bool force, char type) const
+  {
+    return m_rep->convert_to_str_internal (pad, force, type);
+  }
 
   bool save_ascii (std::ostream& os);
 
@@ -147,57 +313,28 @@
   void print_raw (std::ostream& os, bool pr_as_read_syntax = false) const;
 
   // Simple function handles are printed without a newline.
-  bool print_as_scalar (void) const { return m_name != anonymous; }
+  bool print_as_scalar (void) const { return m_rep->print_as_scalar (); }
+
+  friend bool
+  is_equal_to (const octave_fcn_handle& fh1, const octave_fcn_handle& fh2);
 
 private:
 
-  bool set_fcn (const std::string& octaveroot, const std::string& fpath);
+  octave::base_fcn_handle *m_rep;
+
+  octave_fcn_handle (octave::base_fcn_handle *rep);
+
+  octave::base_fcn_handle * get_rep (void) const { return m_rep; }
 
   DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
-
-protected:
-
-  // The function we are handling (this should be valid for handles to
-  // anonymous functions and some other special cases).  Otherwise, we
-  // perform dynamic lookup based on the name of the function we are
-  // handling and the scope where the function handle object was created.
-  octave_value m_fcn;
-
-  // If defined, this is the classdef object corresponding to the
-  // classdef method we are handling.
-  octave_value m_obj;
-
-  // The function we would find without considering argument types.  We
-  // cache this value so that the function_value and user_function_value
-  // methods may continue to work.
-  octave_value m_generic_fcn;
-
-  // The name of the handle, not including the "@".
-  std::string m_name;
+};
 
-  // The scope where this object was defined.
-  octave::symbol_scope m_scope;
-
-  // TRUE means this is a handle to a nested function.
-  bool m_is_nested;
-
-  // Saved stack frames for handles to nested functions.  This allows us
-  // to access non-locals and other context info when calling nested
-  // functions indirectly through handles.
-  std::list<octave::stack_frame *> *m_closure_frames;
-
-  // The name of the class in which this handle was created, if any.
-  // Used to determine access permission when the referenced function is
-  // called.
-  std::string m_dispatch_class;
-
-  bool parse_anon_fcn_handle (const std::string& fcn_text);
-
-  virtual octave_value_list call (int nargout, const octave_value_list& args);
-};
+extern bool
+is_equal_to (const octave_fcn_handle& fh1, const octave_fcn_handle& fh2);
 
 namespace octave
 {
+  OCTAVE_DEPRECATED (6, "use 'tree_evaluator::make_fcn_handle' instead")
   extern octave_value
   make_fcn_handle (interpreter& interp, const std::string& name);
 }
--- a/libinterp/octave-value/ov-fcn-inline.cc	Tue Jun 09 14:11:13 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1003 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2004-2020 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/>.
-//
-// In addition to the terms of the GPL, you are permitted to link
-// this program with any Open Source program, as defined by the
-// Open Source Initiative (www.opensource.org)
-//
-////////////////////////////////////////////////////////////////////////
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <istream>
-#include <ostream>
-#include <sstream>
-#include <vector>
-
-#include "oct-locbuf.h"
-
-#include "defun.h"
-#include "error.h"
-#include "errwarn.h"
-#include "interpreter-private.h"
-#include "interpreter.h"
-#include "oct-hdf5.h"
-#include "oct-map.h"
-#include "ov-base.h"
-#include "ov-fcn-inline.h"
-#include "ov-usr-fcn.h"
-#include "parse.h"
-#include "pr-output.h"
-#include "variables.h"
-
-#include "byte-swap.h"
-#include "ls-ascii-helper.h"
-#include "ls-oct-text.h"
-#include "ls-hdf5.h"
-#include "ls-utils.h"
-
-
-DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_fcn_inline,
-                                     "inline function",
-                                     "function_handle");
-
-octave_fcn_inline::octave_fcn_inline (const std::string& f,
-                                      const string_vector& a,
-                                      const std::string& n)
-  : octave_fcn_handle (n), m_text (f), m_args (a)
-{
-  // Form a string representing the function.
-
-  std::ostringstream buf;
-
-  buf << "@(";
-
-  for (int i = 0; i < m_args.numel (); i++)
-    {
-      if (i > 0)
-        buf << ", ";
-
-      buf << m_args(i);
-    }
-
-  buf << ") " << m_text;
-
-  octave::interpreter& interp
-    = octave::__get_interpreter__ ("octave_fcn_inline::octave_fcn_inline");
-
-  int parse_status;
-  octave_value anon_fcn_handle
-    = interp.eval_string (buf.str (), true, parse_status);
-
-  if (parse_status == 0)
-    {
-      octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value ();
-
-      if (fh)
-        {
-          m_fcn = fh->fcn_val ();
-
-          octave_user_function *uf = m_fcn.user_function_value ();
-
-          if (uf)
-            {
-              octave::tree_evaluator& tw = interp.get_evaluator ();
-
-              octave_function *curr_fcn = tw.current_function ();
-
-              if (curr_fcn)
-                {
-                  octave::symbol_scope parent_scope
-                    = curr_fcn->parent_fcn_scope ();
-
-                  if (! parent_scope)
-                    parent_scope = curr_fcn->scope ();
-
-                  uf->stash_parent_fcn_scope (parent_scope);
-                }
-            }
-        }
-    }
-
-  if (m_fcn.is_undefined ())
-    error ("inline: unable to define function");
-}
-
-// This function is supplied to allow a Matlab style class structure
-// to be returned..
-octave_map
-octave_fcn_inline::map_value (void) const
-{
-  octave_scalar_map m;
-
-  m.assign ("version", 1.0);
-  m.assign ("isEmpty", 0.0);
-  m.assign ("expr", fcn_text ());
-
-  string_vector args = fcn_arg_names ();
-
-  m.assign ("numArgs", args.numel ());
-  m.assign ("args", args);
-
-  std::ostringstream buf;
-
-  for (int i = 0; i < args.numel (); i++)
-    buf << args(i) << " = INLINE_INPUTS_{" << i + 1 << "}; ";
-
-  m.assign ("inputExpr", buf.str ());
-
-  return m;
-}
-
-bool
-octave_fcn_inline::save_ascii (std::ostream& os)
-{
-  os << "# nargs: " <<  m_args.numel () << "\n";
-  for (int i = 0; i < m_args.numel (); i++)
-    os << m_args(i) << "\n";
-  if (m_name.length () < 1)
-    // Write an invalid value to flag empty fcn handle name.
-    os << "0\n";
-  else
-    os << m_name << "\n";
-  os << m_text << "\n";
-  return true;
-}
-
-bool
-octave_fcn_inline::load_ascii (std::istream& is)
-{
-  int nargs;
-  if (extract_keyword (is, "nargs", nargs, true))
-    {
-      m_args.resize (nargs);
-      for (int i = 0; i < nargs; i++)
-        is >> m_args(i);
-      is >> m_name;
-      if (m_name == "0")
-        m_name = "";
-
-      skip_preceeding_newline (is);
-
-      std::string buf;
-
-      if (is)
-        {
-
-          // Get a line of text whitespace characters included,
-          // leaving newline in the stream.
-          buf = read_until_newline (is, true);
-        }
-
-      m_text = buf;
-
-      octave_fcn_inline tmp (m_text, m_args, m_name);
-      m_fcn = tmp.m_fcn;
-
-      return true;
-    }
-  else
-    return false;
-}
-
-bool
-octave_fcn_inline::save_binary (std::ostream& os, bool)
-{
-  int32_t tmp = m_args.numel ();
-  os.write (reinterpret_cast<char *> (&tmp), 4);
-  for (int i = 0; i < m_args.numel (); i++)
-    {
-      tmp = m_args(i).length ();
-      os.write (reinterpret_cast<char *> (&tmp), 4);
-      os.write (m_args(i).c_str (), m_args(i).length ());
-    }
-  tmp = m_name.length ();
-  os.write (reinterpret_cast<char *> (&tmp), 4);
-  os.write (m_name.c_str (), m_name.length ());
-  tmp = m_text.length ();
-  os.write (reinterpret_cast<char *> (&tmp), 4);
-  os.write (m_text.c_str (), m_text.length ());
-  return true;
-}
-
-bool
-octave_fcn_inline::load_binary (std::istream& is, bool swap,
-                                octave::mach_info::float_format)
-{
-  int32_t nargs;
-  if (! is.read (reinterpret_cast<char *> (&nargs), 4))
-    return false;
-  if (swap)
-    swap_bytes<4> (&nargs);
-
-  if (nargs < 1)
-    return false;
-  else
-    {
-      int32_t tmp;
-      m_args.resize (nargs);
-      for (int i = 0; i < nargs; i++)
-        {
-          if (! is.read (reinterpret_cast<char *> (&tmp), 4))
-            return false;
-          if (swap)
-            swap_bytes<4> (&tmp);
-
-          OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1);
-          is.read (ctmp, tmp);
-          m_args(i) = std::string (ctmp);
-
-          if (! is)
-            return false;
-        }
-
-      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
-        return false;
-      if (swap)
-        swap_bytes<4> (&tmp);
-
-      OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1);
-      is.read (ctmp1, tmp);
-      m_name = std::string (ctmp1);
-
-      if (! is)
-        return false;
-
-      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
-        return false;
-      if (swap)
-        swap_bytes<4> (&tmp);
-
-      OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
-      is.read (ctmp2, tmp);
-      m_text = std::string (ctmp2);
-
-      if (! is)
-        return false;
-
-      octave_fcn_inline ftmp (m_text, m_args, m_name);
-      m_fcn = ftmp.m_fcn;
-    }
-  return true;
-}
-
-bool
-octave_fcn_inline::save_hdf5 (octave_hdf5_id loc_id, const char *name,
-                              bool /* save_as_floats */)
-{
-  bool retval = false;
-
-#if defined (HAVE_HDF5)
-
-  hid_t group_hid = -1;
-
-#if defined (HAVE_HDF5_18)
-  group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
-                         octave_H5P_DEFAULT);
-#else
-  group_hid = H5Gcreate (loc_id, name, 0);
-#endif
-  if (group_hid < 0) return false;
-
-  size_t len = 0;
-  for (int i = 0; i < m_args.numel (); i++)
-    if (len < m_args(i).length ())
-      len = m_args(i).length ();
-
-  hid_t space_hid, data_hid, type_hid;
-  space_hid = data_hid = type_hid = -1;
-
-  // FIXME: Is there a better way of saving string vectors,
-  //        than a null padded matrix?
-
-  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
-
-  // Octave uses column-major, while HDF5 uses row-major ordering
-  hdims[1] = m_args.numel ();
-  hdims[0] = len + 1;
-
-  space_hid = H5Screate_simple (2, hdims, nullptr);
-  if (space_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid,
-                        octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid,
-                        octave_H5P_DEFAULT);
-#endif
-  if (data_hid < 0)
-    {
-      H5Sclose (space_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (char, s, m_args.numel () * (len + 1));
-
-  // Save the args as a null terminated list
-  for (int i = 0; i < m_args.numel (); i++)
-    {
-      const char *cptr = m_args(i).c_str ();
-      for (size_t j = 0; j < m_args(i).length (); j++)
-        s[i*(len+1)+j] = *cptr++;
-      s[m_args(i).length ()] = '\0';
-    }
-
-  retval = H5Dwrite (data_hid, H5T_NATIVE_CHAR, octave_H5S_ALL, octave_H5S_ALL,
-                     octave_H5P_DEFAULT, s) >= 0;
-
-  H5Dclose (data_hid);
-  H5Sclose (space_hid);
-
-  if (! retval)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  // attach the type of the variable
-  type_hid = H5Tcopy (H5T_C_S1);
-  H5Tset_size (type_hid, m_name.length () + 1);
-  if (type_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  hdims[0] = 0;
-  space_hid = H5Screate_simple (0, hdims, nullptr);
-  if (space_hid < 0)
-    {
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
-                        octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
-                        octave_H5P_DEFAULT);
-#endif
-  if (data_hid < 0
-      || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
-                   octave_H5P_DEFAULT, m_name.c_str ()) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-  H5Dclose (data_hid);
-
-  // attach the type of the variable
-  H5Tset_size (type_hid, m_text.length () + 1);
-  if (type_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dcreate (group_hid, "iftext",  type_hid, space_hid,
-                        octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dcreate (group_hid, "iftext",  type_hid, space_hid,
-                        octave_H5P_DEFAULT);
-#endif
-  if (data_hid < 0
-      || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
-                   octave_H5P_DEFAULT, m_text.c_str ()) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  H5Dclose (data_hid);
-  H5Sclose (space_hid);
-  H5Tclose (type_hid);
-  H5Gclose (group_hid);
-
-#else
-  octave_unused_parameter (loc_id);
-  octave_unused_parameter (name);
-
-  warn_save ("hdf5");
-#endif
-
-  return retval;
-}
-
-bool
-octave_fcn_inline::load_hdf5 (octave_hdf5_id loc_id, const char *name)
-{
-#if defined (HAVE_HDF5)
-
-  hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id;
-  hsize_t rank;
-  int slen;
-
-#if defined (HAVE_HDF5_18)
-  group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
-#else
-  group_hid = H5Gopen (loc_id, name);
-#endif
-  if (group_hid < 0) return false;
-
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dopen (group_hid, "args");
-#endif
-  space_hid = H5Dget_space (data_hid);
-  rank = H5Sget_simple_extent_ndims (space_hid);
-
-  if (rank != 2)
-    {
-      H5Dclose (data_hid);
-      H5Sclose (space_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
-  OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
-
-  H5Sget_simple_extent_dims (space_hid, hdims, maxdims);
-
-  m_args.resize (hdims[1]);
-
-  OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]);
-
-  if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL,
-               octave_H5P_DEFAULT, s1) < 0)
-    {
-      H5Dclose (data_hid);
-      H5Sclose (space_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  H5Dclose (data_hid);
-  H5Sclose (space_hid);
-
-  for (size_t i = 0; i < hdims[1]; i++)
-    m_args(i) = std::string (s1 + i*hdims[0]);
-
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dopen (group_hid, "nm");
-#endif
-
-  if (data_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  type_hid = H5Dget_type (data_hid);
-  type_class_hid = H5Tget_class (type_hid);
-
-  if (type_class_hid != H5T_STRING)
-    {
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  space_hid = H5Dget_space (data_hid);
-  rank = H5Sget_simple_extent_ndims (space_hid);
-
-  if (rank != 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  slen = H5Tget_size (type_hid);
-  if (slen < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
-
-  // create datatype for (null-terminated) string to read into:
-  st_id = H5Tcopy (H5T_C_S1);
-  H5Tset_size (st_id, slen);
-
-  if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
-               octave_H5P_DEFAULT, nm_tmp) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-  H5Tclose (st_id);
-  H5Dclose (data_hid);
-  m_name = nm_tmp;
-
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dopen (group_hid, "iftext");
-#endif
-
-  if (data_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  type_hid = H5Dget_type (data_hid);
-  type_class_hid = H5Tget_class (type_hid);
-
-  if (type_class_hid != H5T_STRING)
-    {
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  space_hid = H5Dget_space (data_hid);
-  rank = H5Sget_simple_extent_ndims (space_hid);
-
-  if (rank != 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  slen = H5Tget_size (type_hid);
-  if (slen < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen);
-
-  // create datatype for (null-terminated) string to read into:
-  st_id = H5Tcopy (H5T_C_S1);
-  H5Tset_size (st_id, slen);
-
-  if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
-               octave_H5P_DEFAULT, iftext_tmp) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-  H5Tclose (st_id);
-  H5Dclose (data_hid);
-  m_text = iftext_tmp;
-
-  octave_fcn_inline ftmp (m_text, m_args, m_name);
-  m_fcn = ftmp.m_fcn;
-
-  return true;
-
-#else
-  octave_unused_parameter (loc_id);
-  octave_unused_parameter (name);
-
-  warn_load ("hdf5");
-
-  return false;
-#endif
-}
-
-void
-octave_fcn_inline::print (std::ostream& os, bool pr_as_read_syntax)
-{
-  print_raw (os, pr_as_read_syntax);
-  newline (os);
-}
-
-void
-octave_fcn_inline::print_raw (std::ostream& os, bool pr_as_read_syntax) const
-{
-  std::ostringstream buf;
-
-  if (m_name.empty ())
-    buf << "f(";
-  else
-    buf << m_name << '(';
-
-  for (int i = 0; i < m_args.numel (); i++)
-    {
-      if (i)
-        buf << ", ";
-
-      buf << m_args(i);
-    }
-
-  buf << ") = " << m_text;
-
-  octave_print_internal (os, buf.str (), pr_as_read_syntax,
-                         current_print_indent_level ());
-}
-
-octave_value
-octave_fcn_inline::convert_to_str_internal (bool, bool, char type) const
-{
-  return octave_value (fcn_text (), type);
-}
-
-DEFUNX ("inline", Finline, args, ,
-        doc: /* -*- texinfo -*-
-@deftypefn  {} {} inline (@var{str})
-@deftypefnx {} {} inline (@var{str}, @var{arg1}, @dots{})
-@deftypefnx {} {} inline (@var{str}, @var{n})
-Create an inline function from the character string @var{str}.
-
-If called with a single argument, the arguments of the generated function
-are extracted from the function itself.  The generated function arguments
-will then be in alphabetical order.  It should be noted that i and j are
-ignored as arguments due to the ambiguity between their use as a variable or
-their use as an built-in constant.  All arguments followed by a parenthesis
-are considered to be functions.  If no arguments are found, a function
-taking a single argument named @code{x} will be created.
-
-If the second and subsequent arguments are character strings, they are the
-names of the arguments of the function.
-
-If the second argument is an integer @var{n}, the arguments are
-@qcode{"x"}, @qcode{"P1"}, @dots{}, @qcode{"P@var{N}"}.
-
-Programming Note: The use of @code{inline} is discouraged and it may be
-removed from a future version of Octave.  The preferred way to create
-functions from strings is through the use of anonymous functions
-(@pxref{Anonymous Functions}) or @code{str2func}.
-@seealso{argnames, formula, vectorize, str2func}
-@end deftypefn */)
-{
-  int nargin = args.length ();
-
-  if (nargin == 0)
-    print_usage ();
-
-  std::string fun = args(0).xstring_value ("inline: STR argument must be a string");
-
-  string_vector fargs;
-
-  if (nargin == 1)
-    {
-      bool is_arg = false;
-      bool in_string = false;
-      std::string tmp_arg;
-      size_t i = 0;
-      size_t fun_length = fun.length ();
-
-      while (i < fun_length)
-        {
-          bool terminate_arg = false;
-          char c = fun[i++];
-
-          if (in_string)
-            {
-              if (c == '\'' || c == '\"')
-                in_string = false;
-            }
-          else if (c == '\'' || c == '\"')
-            {
-              in_string = true;
-              if (is_arg)
-                terminate_arg = true;
-            }
-          else if (! isalpha (c) && c != '_')
-            if (! is_arg)
-              continue;
-            else if (isdigit (c))
-              tmp_arg.append (1, c);
-            else
-              {
-                // Before we do anything remove trailing whitespaces.
-                while (i < fun_length && isspace (c))
-                  c = fun[i++];
-
-                // Do we have a variable or a function?
-                if (c != '(')
-                  terminate_arg = true;
-                else
-                  {
-                    tmp_arg = "";
-                    is_arg = false;
-                  }
-              }
-          else if (! is_arg)
-            {
-              if (c == 'e' || c == 'E')
-                {
-                  // possible number in exponent form, not arg
-                  if (isdigit (fun[i]) || fun[i] == '-' || fun[i] == '+')
-                    continue;
-                }
-              is_arg = true;
-              tmp_arg.append (1, c);
-            }
-          else
-            {
-              tmp_arg.append (1, c);
-            }
-
-          if (terminate_arg || (i == fun_length && is_arg))
-            {
-              bool have_arg = false;
-
-              for (int j = 0; j < fargs.numel (); j++)
-                if (tmp_arg == fargs (j))
-                  {
-                    have_arg = true;
-                    break;
-                  }
-
-              if (! have_arg && tmp_arg != "i" && tmp_arg != "j"
-                  && tmp_arg != "NaN" && tmp_arg != "nan"
-                  && tmp_arg != "Inf" && tmp_arg != "inf"
-                  && tmp_arg != "NA" && tmp_arg != "pi"
-                  && tmp_arg != "e" && tmp_arg != "eps")
-                fargs.append (tmp_arg);
-
-              tmp_arg = "";
-              is_arg = false;
-            }
-        }
-
-      // Sort the arguments into ASCII order.
-      fargs.sort ();
-
-      if (fargs.isempty ())
-        fargs.append (std::string ("x"));
-
-    }
-  else if (nargin == 2 && args(1).isnumeric ())
-    {
-      if (! args(1).is_scalar_type ())
-        error ("inline: N must be an integer");
-
-      int n = args(1).xint_value ("inline: N must be an integer");
-
-      if (n < 0)
-        error ("inline: N must be a positive integer or zero");
-
-      fargs.resize (n+1);
-
-      fargs(0) = "x";
-
-      for (int i = 1; i < n+1; i++)
-        {
-          std::ostringstream buf;
-          buf << 'P' << i;
-          fargs(i) = buf.str ();
-        }
-    }
-  else
-    {
-      fargs.resize (nargin - 1);
-
-      for (int i = 1; i < nargin; i++)
-        {
-          std::string s = args(i).xstring_value ("inline: additional arguments must be strings");
-          fargs(i-1) = s;
-        }
-    }
-
-  return ovl (new octave_fcn_inline (fun, fargs));
-}
-
-/*
-%!shared fn
-%! fn = inline ("x.^2 + 1");
-%!assert (feval (fn, 6), 37)
-%!assert (fn (6), 37)
-%!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10)
-%!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5)
-%!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5)
-
-## Test input validation
-%!error inline ()
-%!error <STR argument must be a string> inline (1)
-%!error <N must be an integer> inline ("2", ones (2,2))
-%!error <N must be a positive integer> inline ("2", -1)
-%!error <additional arguments must be strings> inline ("2", "x", -1, "y")
-*/
-
-DEFUN (formula, args, ,
-       doc: /* -*- texinfo -*-
-@deftypefn {} {} formula (@var{fun})
-Return a character string representing the inline function @var{fun}.
-
-Note that @code{char (@var{fun})} is equivalent to
-@code{formula (@var{fun})}.
-@seealso{char, argnames, inline, vectorize}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  octave_fcn_inline *fn = args(0).fcn_inline_value (true);
-
-  if (! fn)
-    error ("formula: FUN must be an inline function");
-
-  return ovl (fn->fcn_text ());
-}
-
-/*
-%!assert (formula (fn), "x.^2 + 1")
-%!assert (formula (fn), char (fn))
-
-## Test input validation
-%!error formula ()
-%!error formula (1, 2)
-%!error <FUN must be an inline function> formula (1)
-*/
-
-DEFUN (argnames, args, ,
-       doc: /* -*- texinfo -*-
-@deftypefn {} {} argnames (@var{fun})
-Return a cell array of character strings containing the names of the
-arguments of the inline function @var{fun}.
-@seealso{inline, formula, vectorize}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  octave_fcn_inline *fn = args(0).fcn_inline_value (true);
-
-  if (! fn)
-    error ("argnames: FUN must be an inline function");
-
-  string_vector t1 = fn->fcn_arg_names ();
-
-  Cell t2 (dim_vector (t1.numel (), 1));
-
-  for (int i = 0; i < t1.numel (); i++)
-    t2(i) = t1(i);
-
-  return ovl (t2);
-}
-
-/*
-%!assert (argnames (fn), {"x"})
-%!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"})
-%!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"})
-
-## Test input validation
-%!error argnames ()
-%!error argnames (1, 2)
-%!error <FUN must be an inline function> argnames (1)
-*/
-
-DEFUN (vectorize, args, ,
-       doc: /* -*- texinfo -*-
-@deftypefn {} {} vectorize (@var{fun})
-Create a vectorized version of the inline function @var{fun} by replacing
-all occurrences of @code{*}, @code{/}, etc., with @code{.*}, @code{./}, etc.
-
-This may be useful, for example, when using inline functions with numerical
-integration or optimization where a vector-valued function is expected.
-
-@example
-@group
-fcn = vectorize (inline ("x^2 - 1"))
-   @result{} fcn = f(x) = x.^2 - 1
-quadv (fcn, 0, 3)
-   @result{} 6
-@end group
-@end example
-@seealso{inline, formula, argnames}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  std::string old_func;
-  octave_fcn_inline *old = nullptr;
-  bool func_is_string = true;
-
-  if (args(0).is_string ())
-    old_func = args(0).string_value ();
-  else
-    {
-      func_is_string = false;
-
-      old = args(0).fcn_inline_value (true);
-      if (! old)
-        error ("vectorize: FUN must be a string or inline function");
-
-      old_func = old->fcn_text ();
-    }
-
-  size_t len = old_func.length ();
-  std::string new_func;
-  new_func.reserve (len + 10);
-
-  size_t i = 0;
-  while (i < len)
-    {
-      char t1 = old_func[i];
-
-      if (t1 == '*' || t1 == '/' || t1 == '\\' || t1 == '^')
-        {
-          if (i && old_func[i-1] != '.')
-            new_func.push_back ('.');
-
-          // Special case for ** operator.
-          if (t1 == '*' && i < (len - 1) && old_func[i+1] == '*')
-            {
-              new_func.push_back ('*');
-              i++;
-            }
-        }
-      new_func.push_back (t1);
-      i++;
-    }
-
-  if (func_is_string)
-    return ovl (new_func);
-  else
-    return ovl (new octave_fcn_inline (new_func, old->fcn_arg_names ()));
-}
-
-/*
-%!assert (char (vectorize (fn)), "x.^2 + 1")
-%!assert (char (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z")
-%!assert (char (vectorize (inline ("2**x^5"))), "2.**x.^5")
-%!assert (vectorize ("x.^2 + 1"), "x.^2 + 1")
-%!assert (vectorize ("1e-3*y + 2e4*z"), "1e-3.*y + 2e4.*z")
-%!assert (vectorize ("2**x^5"), "2.**x.^5")
-
-## Test input validation
-%!error vectorize ()
-%!error vectorize (1, 2)
-%!error <FUN must be a string or inline function> vectorize (1)
-*/
--- a/libinterp/octave-value/ov-fcn-inline.h	Tue Jun 09 14:11:13 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2004-2020 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 (octave_ov_fcn_inline_h)
-#define octave_ov_fcn_inline_h 1
-
-#include "octave-config.h"
-
-#include <iosfwd>
-#include <string>
-
-#include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-fcn.h"
-#include "ov-typeinfo.h"
-#include "ov-fcn-handle.h"
-
-// Inline functions.
-
-class
-OCTINTERP_API
-octave_fcn_inline : public octave_fcn_handle
-{
-public:
-
-  octave_fcn_inline (void)
-    : octave_fcn_handle (), m_text (), m_args () { }
-
-  octave_fcn_inline (const std::string& f, const string_vector& a,
-                     const std::string& n = "");
-
-  octave_fcn_inline (const octave_fcn_inline& fi)
-    : octave_fcn_handle (fi), m_text (fi.m_text), m_args (fi.m_args) { }
-
-  ~octave_fcn_inline (void) = default;
-
-  octave_base_value * clone (void) const
-  { return new octave_fcn_inline (*this); }
-  octave_base_value * empty_clone (void) const
-  { return new octave_fcn_inline (); }
-
-  bool is_inline_function (void) const { return true; }
-
-  octave_fcn_inline * fcn_inline_value (bool = false) { return this; }
-
-  std::string fcn_text (void) const { return m_text; }
-
-  string_vector fcn_arg_names (void) const { return m_args; }
-
-  octave_value convert_to_str_internal (bool, bool, char) const;
-
-  octave_map map_value (void) const;
-
-  bool save_ascii (std::ostream& os);
-
-  bool load_ascii (std::istream& is);
-
-  bool save_binary (std::ostream& os, bool save_as_floats);
-
-  bool load_binary (std::istream& is, bool swap,
-                    octave::mach_info::float_format fmt);
-
-  bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
-
-  bool load_hdf5 (octave_hdf5_id loc_id, const char *name);
-
-  void print (std::ostream& os, bool pr_as_read_syntax = false);
-
-  void print_raw (std::ostream& os, bool pr_as_read_syntax = false) const;
-
-private:
-
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
-
-  // The expression of an inline function.
-  std::string m_text;
-
-  // The args of an inline function.
-  string_vector m_args;
-};
-
-#endif
--- a/libinterp/octave-value/ov-fcn.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-fcn.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -27,10 +27,12 @@
 #  include "config.h"
 #endif
 
+#include "unwind-prot.h"
+
 #include "error.h"
 #include "ovl.h"
 #include "ov-fcn.h"
-
+#include "pt-eval.h"
 
 octave_base_value *
 octave_function::clone (void) const
@@ -46,11 +48,11 @@
 
 octave_value_list
 octave_function::call (octave::tree_evaluator& tw, int nargout,
-                       const octave_value_list& args,
-                       octave::stack_frame *closure_context)
+                       const octave_value_list& args)
 {
-  if (closure_context)
-    panic_impossible ();
+  tw.push_stack_frame (this);
 
-  return call (tw, nargout, args);
+  octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+  return execute (tw, nargout, args);
 }
--- a/libinterp/octave-value/ov-fcn.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-fcn.h	Thu Jun 11 01:22:45 2020 -0400
@@ -87,6 +87,11 @@
   virtual octave::symbol_scope parent_fcn_scope (void) const
   { return octave::symbol_scope (); }
 
+  virtual std::list<std::string> parent_fcn_names (void) const
+  {
+    return std::list<std::string> ();
+  }
+
   virtual void mark_fcn_file_up_to_date (const octave::sys::time&) { }
 
   virtual octave::symbol_scope scope (void) { return octave::symbol_scope (); }
@@ -99,6 +104,10 @@
 
   virtual int call_depth (void) const { return 0; }
 
+  virtual bool is_nested_function (void) const { return false; }
+
+  virtual bool is_parent_function (void) const { return false; }
+
   virtual bool is_subfunction (void) const { return false; }
 
   bool is_class_constructor (const std::string& cname = "") const
@@ -227,14 +236,16 @@
   virtual bool accepts_postfix_index (char type) const
   { return (type == '('); }
 
+  // Push new stack frame (if necessary) and execute function.
   virtual octave_value_list
   call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ()) = 0;
+        const octave_value_list& args = octave_value_list ());
 
+  // Execute function without pushing new stack frame (assumes that has
+  // already been done).
   virtual octave_value_list
-  call (octave::tree_evaluator& tw, int nargout,
-        const octave_value_list& args,
-        octave::stack_frame *closure_context);
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ()) = 0;
 
 protected:
 
--- a/libinterp/octave-value/ov-mex-fcn.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-mex-fcn.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -91,20 +91,14 @@
           int nargout);
 
 octave_value_list
-octave_mex_function::call (octave::tree_evaluator& tw, int nargout,
-                           const octave_value_list& args)
+octave_mex_function::execute (octave::tree_evaluator& tw, int nargout,
+                              const octave_value_list& args)
 {
   octave_value_list retval;
 
   if (args.has_magic_colon ())
     error ("invalid use of colon in function argument list");
 
-  octave::unwind_protect frame;
-
-  tw.push_stack_frame (this);
-
-  frame.add_method (tw, &octave::tree_evaluator::pop_stack_frame);
-
   octave::profiler& profiler = tw.get_profiler ();
 
   octave::profiler::enter<octave_mex_function> block (profiler, *this);
--- a/libinterp/octave-value/ov-mex-fcn.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-mex-fcn.h	Thu Jun 11 01:22:45 2020 -0400
@@ -92,14 +92,9 @@
 
   bool use_interleaved_complex (void) const { return m_interleaved; }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
-
   octave_value_list
-  call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ());
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
 
   void atexit (void (*fcn) (void)) { m_exit_fcn_ptr = fcn; }
 
--- a/libinterp/octave-value/ov-typeinfo.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-typeinfo.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -991,8 +991,6 @@
 %!assert (typeinfo (@sin), "function handle")
 %!assert (typeinfo (@(x) x), "function handle")
 
-%!assert (typeinfo (inline ("x^2")), "inline function")
-
 %!assert (typeinfo (single (1)), "float scalar")
 %!assert (typeinfo (single (i)), "float complex scalar")
 %!assert (typeinfo (single ([1, 2])), "float matrix")
--- a/libinterp/octave-value/ov-usr-fcn.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-usr-fcn.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -173,10 +173,26 @@
     : octave_user_code (fnm, nm, scope, nullptr, ds)
 { }
 
+// We must overload the call method so that we call the proper
+// push_stack_frame method, which is overloaded for pointers to
+// octave_function, octave_user_function, and octave_user_script
+// objects.
+
 octave_value_list
 octave_user_script::call (octave::tree_evaluator& tw, int nargout,
                           const octave_value_list& args)
 {
+  tw.push_stack_frame (this);
+
+  octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+  return execute (tw, nargout, args);
+}
+
+octave_value_list
+octave_user_script::execute (octave::tree_evaluator& tw, int nargout,
+                             const octave_value_list& args)
+{
   return tw.execute_user_script (*this, nargout, args);
 }
 
@@ -197,11 +213,9 @@
 
 octave_user_function::octave_user_function
   (const octave::symbol_scope& scope, octave::tree_parameter_list *pl,
-   octave::tree_parameter_list *rl, octave::tree_statement_list *cl,
-   const local_vars_map& lviv)
+   octave::tree_parameter_list *rl, octave::tree_statement_list *cl)
   : octave_user_code ("", "", scope, cl, ""),
     param_list (pl), ret_list (rl),
-    m_local_var_init_vals (lviv),
     lead_comm (), trail_comm (),
     location_line (0), location_column (0),
     parent_name (), system_fcn_file (false),
@@ -459,12 +473,27 @@
   return retval;
 }
 
+// We must overload the call method so that we call the proper
+// push_stack_frame method, which is overloaded for pointers to
+// octave_function, octave_user_function, and octave_user_script
+// objects.
+
 octave_value_list
 octave_user_function::call (octave::tree_evaluator& tw, int nargout,
-                            const octave_value_list& args,
-                            octave::stack_frame *closure_frames)
+                            const octave_value_list& args)
 {
-  return tw.execute_user_function (*this, nargout, args, closure_frames);
+  tw.push_stack_frame (this);
+
+  octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+  return execute (tw, nargout, args);
+}
+
+octave_value_list
+octave_user_function::execute (octave::tree_evaluator& tw, int nargout,
+                               const octave_value_list& args)
+{
+  return tw.execute_user_function (*this, nargout, args);
 }
 
 void
--- a/libinterp/octave-value/ov-usr-fcn.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov-usr-fcn.h	Thu Jun 11 01:22:45 2020 -0400
@@ -186,15 +186,19 @@
 
   bool is_user_script (void) const { return true; }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_user_code::call;
+  // We must overload the call method so that we call the proper
+  // push_stack_frame method, which is overloaded for pointers to
+  // octave_function, octave_user_function, and octave_user_script
+  // objects.
 
   octave_value_list
   call (octave::tree_evaluator& tw, int nargout = 0,
         const octave_value_list& args = octave_value_list ());
 
+  octave_value_list
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
+
   void accept (octave::tree_walker& tw);
 
 private:
@@ -209,13 +213,10 @@
 {
 public:
 
-  typedef std::map<std::string, octave_value> local_vars_map;
-
   octave_user_function (const octave::symbol_scope& scope = octave::symbol_scope (),
                         octave::tree_parameter_list *pl = nullptr,
                         octave::tree_parameter_list *rl = nullptr,
-                        octave::tree_statement_list *cl = nullptr,
-                        const local_vars_map& lviv = local_vars_map ());
+                        octave::tree_statement_list *cl = nullptr);
 
   // No copying!
 
@@ -272,6 +273,11 @@
     return m_scope.parent_scope ();
   }
 
+  std::list<std::string> parent_fcn_names (void) const
+  {
+    return m_scope.parent_fcn_names ();
+  }
+
   void mark_as_system_fcn_file (void);
 
   bool is_system_fcn_file (void) const { return system_fcn_file; }
@@ -372,16 +378,18 @@
             ? (cname.empty () ? true : cname == dispatch_class ()) : false);
   }
 
+  // We must overload the call method so that we call the proper
+  // push_stack_frame method, which is overloaded for pointers to
+  // octave_function, octave_user_function, and octave_user_script
+  // objects.
+
   octave_value_list
   call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ())
-  {
-    return call (tw, nargout, args, nullptr);
-  }
+        const octave_value_list& args = octave_value_list ());
 
   octave_value_list
-  call (octave::tree_evaluator& tw, int nargout,
-        const octave_value_list& args, octave::stack_frame *);
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
 
   octave::tree_parameter_list * parameter_list (void) { return param_list; }
 
@@ -391,11 +399,6 @@
 
   octave::comment_list * trailing_comment (void) { return trail_comm; }
 
-  const local_vars_map& local_var_init_vals (void) const
-  {
-    return m_local_var_init_vals;
-  }
-
   // If is_special_expr is true, retrieve the sigular expression that forms the
   // body.  May be null (even if is_special_expr is true).
   octave::tree_expression * special_expr (void);
@@ -431,9 +434,6 @@
   // this function.
   octave::tree_parameter_list *ret_list;
 
-  // For anonymous function values inherited from parent scope.
-  local_vars_map m_local_var_init_vals;
-
   // The comments preceding the FUNCTION token.
   octave::comment_list *lead_comm;
 
--- a/libinterp/octave-value/ov.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -75,7 +75,6 @@
 #include "ov-dld-fcn.h"
 #include "ov-usr-fcn.h"
 #include "ov-fcn-handle.h"
-#include "ov-fcn-inline.h"
 #include "ov-typeinfo.h"
 #include "ov-null-mat.h"
 #include "ov-lazy-idx.h"
@@ -1694,12 +1693,6 @@
   return rep->fcn_handle_value (silent);
 }
 
-octave_fcn_inline *
-octave_value::fcn_inline_value (bool silent) const
-{
-  return rep->fcn_inline_value (silent);
-}
-
 octave_value_list
 octave_value::list_value (void) const
 {
@@ -2119,7 +2112,6 @@
 XVALUE_EXTRACTOR (octave_user_script *, xuser_script_value, user_script_value)
 XVALUE_EXTRACTOR (octave_user_code *, xuser_code_value, user_code_value)
 XVALUE_EXTRACTOR (octave_fcn_handle *, xfcn_handle_value, fcn_handle_value)
-XVALUE_EXTRACTOR (octave_fcn_inline *, xfcn_inline_value, fcn_inline_value)
 
 XVALUE_EXTRACTOR (octave_value_list, xlist_value, list_value)
 
@@ -3031,7 +3023,6 @@
   octave_user_function::register_type (ti);
   octave_dld_function::register_type (ti);
   octave_fcn_handle::register_type (ti);
-  octave_fcn_inline::register_type (ti);
   octave_float_scalar::register_type (ti);
   octave_float_complex::register_type (ti);
   octave_float_matrix::register_type (ti);
--- a/libinterp/octave-value/ov.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/octave-value/ov.h	Thu Jun 11 01:22:45 2020 -0400
@@ -57,7 +57,6 @@
 class octave_function;
 class octave_user_function;
 class octave_fcn_handle;
-class octave_fcn_inline;
 class octave_value_list;
 
 #include "mxtypes.h"
@@ -976,8 +975,6 @@
 
   octave_fcn_handle * fcn_handle_value (bool silent = false) const;
 
-  octave_fcn_inline * fcn_inline_value (bool silent = false) const;
-
   octave_value_list list_value (void) const;
 
   ColumnVector column_vector_value (bool frc_str_conv = false,
@@ -1195,7 +1192,6 @@
   octave_user_script * xuser_script_value (const char *fmt, ...) const;
   octave_user_code * xuser_code_value (const char *fmt, ...) const;
   octave_fcn_handle * xfcn_handle_value (const char *fmt, ...) const;
-  octave_fcn_inline * xfcn_inline_value (const char *fmt, ...) const;
 
   octave_value_list xlist_value (const char *fmt, ...) const;
 
--- a/libinterp/operators/op-fcn.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/operators/op-fcn.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -40,7 +40,7 @@
   const octave_fcn_handle& v1 = dynamic_cast<const octave_fcn_handle&> (a1);
   const octave_fcn_handle& v2 = dynamic_cast<const octave_fcn_handle&> (a2);
 
-  return v1.is_equal_to (v2);
+  return is_equal_to (v1, v2);
 }
 
 DEFBINOP (ne, fcn_handle, fcn_handle)
@@ -48,7 +48,7 @@
   const octave_fcn_handle& v1 = dynamic_cast<const octave_fcn_handle&> (a1);
   const octave_fcn_handle& v2 = dynamic_cast<const octave_fcn_handle&> (a2);
 
-  return ! v1.is_equal_to (v2);
+  return ! is_equal_to (v1, v2);
 }
 
 void
--- a/libinterp/parse-tree/oct-lvalue.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/parse-tree/oct-lvalue.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -36,7 +36,7 @@
 {
   bool octave_lvalue::is_defined (void) const
   {
-    return ! is_black_hole () && m_frame.is_defined (m_sym);
+    return ! is_black_hole () && m_frame->is_defined (m_sym);
   }
 
   bool octave_lvalue::is_undefined (void) const
@@ -46,14 +46,14 @@
 
   void octave_lvalue::define (const octave_value& v)
   {
-    m_frame.assign (m_sym, v);
+    m_frame->assign (m_sym, v);
   }
 
   void octave_lvalue::assign (octave_value::assign_op op,
                               const octave_value& rhs)
   {
     if (! is_black_hole ())
-      m_frame.assign (op, m_sym, m_type, m_idx, rhs);
+      m_frame->assign (op, m_sym, m_type, m_idx, rhs);
   }
 
   void octave_lvalue::set_index (const std::string& t,
@@ -97,12 +97,12 @@
   void octave_lvalue::do_unary_op (octave_value::unary_op op)
   {
     if (! is_black_hole ())
-      m_frame.do_non_const_unary_op (op, m_sym, m_type, m_idx);
+      m_frame->do_non_const_unary_op (op, m_sym, m_type, m_idx);
   }
 
   octave_value octave_lvalue::value (void) const
   {
     return (is_black_hole ()
-            ? octave_value () : m_frame.value (m_sym, m_type, m_idx));
+            ? octave_value () : m_frame->value (m_sym, m_type, m_idx));
   }
 }
--- a/libinterp/parse-tree/oct-lvalue.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/parse-tree/oct-lvalue.h	Thu Jun 11 01:22:45 2020 -0400
@@ -40,7 +40,8 @@
   {
   public:
 
-    octave_lvalue (const symbol_record& sr, stack_frame& frame)
+    octave_lvalue (const symbol_record& sr,
+                   const std::shared_ptr<stack_frame>& frame)
       : m_sym (sr), m_frame (frame), m_black_hole (false),
         m_type (), m_idx (), m_nel (1)
     { }
@@ -87,7 +88,7 @@
 
     symbol_record m_sym;
 
-    stack_frame& m_frame;
+    std::shared_ptr<stack_frame> m_frame;
 
     bool m_black_hole;
 
--- a/libinterp/parse-tree/oct-parse.yy	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/parse-tree/oct-parse.yy	Thu Jun 11 01:22:45 2020 -0400
@@ -2530,7 +2530,10 @@
 
     if (! m_lexer.m_reading_script_file && m_curr_fcn_depth == 0
         && ! m_parsing_subfunctions)
-      m_primary_fcn_scope = m_lexer.m_symtab_context.curr_scope ();
+        {
+          m_primary_fcn_scope = m_lexer.m_symtab_context.curr_scope ();
+          m_primary_fcn_scope.mark_primary_fcn_scope ();
+        }
 
     if (m_lexer.m_reading_script_file && m_curr_fcn_depth > 0)
       {
@@ -3722,15 +3725,16 @@
 
     if (fcn)
       {
-        std::string nm = fcn->name ();
+        std::string fcn_nm = fcn->name ();
         std::string file = fcn->fcn_file_name ();
 
-        std::string tmp = nm;
+        std::string tmp = fcn_nm;
         if (! file.empty ())
           tmp += ": " + file;
 
         symbol_scope fcn_scope = fcn->scope ();
         fcn_scope.cache_name (tmp);
+        fcn_scope.cache_fcn_name (fcn_nm);
         fcn_scope.cache_fcn_file_name (file);
         fcn_scope.cache_dir_name (m_lexer.m_dir_name);
 
@@ -3754,14 +3758,27 @@
                 symbol_scope pscope = m_function_scopes.parent_scope ();
                 fcn_scope.set_parent (pscope);
                 fcn_scope.set_primary_parent (m_primary_fcn_scope);
-                pscope.install_nestfunction (nm, ov_fcn, fcn_scope);
+                pscope.install_nestfunction (fcn_nm, ov_fcn, fcn_scope);
+
+                // For nested functions, the list of parent functions is
+                // set in symbol_scope::update_nest.
               }
             else
               {
                 fcn->mark_as_subfunction ();
-                m_subfunction_names.push_back (nm);
+                m_subfunction_names.push_back (fcn_nm);
                 fcn_scope.set_parent (m_primary_fcn_scope);
-                m_primary_fcn_scope.install_subfunction (nm, ov_fcn);
+                if (m_parsing_subfunctions)
+                  fcn_scope.set_primary_parent (m_primary_fcn_scope);
+                m_primary_fcn_scope.install_subfunction (fcn_nm, ov_fcn);
+
+                // Prepend name of primary fucntion to list of parent
+                // functions (if any) for subfunction.
+
+                std::list<std::string> plst
+                  = fcn_scope.parent_fcn_names ();
+                plst.push_front (m_primary_fcn_scope.fcn_name ());
+                fcn_scope.cache_parent_fcn_names (plst);
               }
           }
 
--- a/libinterp/parse-tree/pt-eval.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/parse-tree/pt-eval.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -61,6 +61,7 @@
 #include "pt-anon-scopes.h"
 #include "pt-eval.h"
 #include "pt-tm-const.h"
+#include "stack-frame.h"
 #include "symtab.h"
 #include "unwind-prot.h"
 #include "utils.h"
@@ -187,7 +188,7 @@
 
             if (! silent)
               {
-                stack_frame *frm = tw.current_user_frame ();
+                std::shared_ptr<stack_frame> frm = tw.current_user_frame ();
 
                 frm->display_stopped_in_message (buf);
               }
@@ -881,6 +882,364 @@
     return retval;
   }
 
+  // If NAME is an operator (like "+", "-", ...), convert it to the
+  // corresponding function name ("plus", "minus", ...).
+
+  static std::string
+  get_operator_function_name (const std::string& name)
+  {
+    // Bow to the god of compatibility.
+
+    // FIXME: it seems ugly to put this here, but there is no single
+    // function in the parser that converts from the operator name to
+    // the corresponding function name.  At least try to do it without N
+    // string compares.
+
+    size_t len = name.length ();
+
+    if (len == 3 && name == ".**")
+      return "power";
+    else if (len == 2)
+      {
+        if (name[0] == '.')
+          {
+            switch (name[1])
+              {
+              case '\'':
+                return "transpose";
+
+              case '+':
+                return "plus";
+
+              case '-':
+                return "minus";
+
+              case '*':
+                return "times";
+
+              case '/':
+                return "rdivide";
+
+              case '^':
+                return "power";
+
+              case '\\':
+                return "ldivide";
+
+              default:
+                break;
+              }
+          }
+        else if (name[1] == '=')
+          {
+            switch (name[0])
+              {
+              case '<':
+                return "le";
+
+              case '=':
+                return "eq";
+
+              case '>':
+                return "ge";
+
+              case '~':
+              case '!':
+                return "ne";
+
+              default:
+                break;
+              }
+          }
+        else if (name == "**")
+          return "mpower";
+      }
+    else if (len == 1)
+      {
+        switch (name[0])
+          {
+          case '~':
+          case '!':
+            return "not";
+
+          case '\'':
+            return "ctranspose";
+
+          case '+':
+            return "plus";
+
+          case '-':
+            return "minus";
+
+          case '*':
+            return "mtimes";
+
+          case '/':
+            return "mrdivide";
+
+          case '^':
+            return "mpower";
+
+          case '\\':
+            return "mldivide";
+
+          case '<':
+            return "lt";
+
+          case '>':
+            return "gt";
+
+          case '&':
+            return "and";
+
+          case '|':
+            return "or";
+
+          default:
+            break;
+          }
+      }
+
+    return name;
+  }
+
+  // Creates a function handle that takes into account the context,
+  // finding local, nested, private, or sub functions.
+
+  octave_value
+  tree_evaluator::make_fcn_handle (const std::string& name)
+  {
+    octave_value retval;
+
+    // The str2func function can create a function handle with the name
+    // of an operator (for example, "+").  If so, it is converted to the
+    // name of the corresponding function ("+" -> "plus") and we create
+    // a simple function handle using that name.
+
+    std::string fcn_name = get_operator_function_name (name);
+
+    // If FCN_NAME is different from NAME, then NAME is an operator.  As
+    // of version 2020a, Matlab apparently uses the function name
+    // corresponding to the operator to search for private and local
+    // functions in the current scope but not(!) nested functions.
+
+    bool name_is_operator = fcn_name != name;
+
+    size_t pos = fcn_name.find ('.');
+
+    if (pos != std::string::npos)
+      {
+        // Recognize (some of?  which ones?) the following cases
+        // and create something other than a simple function handle?
+        // Should we just be checking for the last two when the first
+        // element of the dot-separated list is an object?  If so, then
+        // should this syntax be limited to a dot-separated list with
+        // exactly two elements?
+        //
+        //   object . method
+        //   object . static-method
+        //
+        // Code to do that duplicates some of simple_fcn_handle::call.
+
+        // Only accept expressions that contain one '.' separator.
+
+        // FIXME: The logic here is a bit complicated.  Is there a good
+        // way to simplify it?
+
+        std::string meth_nm = fcn_name.substr (pos+1);
+
+        if (meth_nm.find ('.') == std::string::npos)
+          {
+            std::string obj_nm = fcn_name.substr (0, pos);
+
+            // If obj_nm is an object in the current scope with a
+            // method named meth_nm, create a classsimple handle.
+
+            octave_value object = varval (obj_nm);
+
+            if (object.is_defined () && object.is_classdef_object ())
+              {
+                octave_classdef *cdef = object.classdef_object_value ();
+
+                if (cdef)
+                  {
+                    std::string class_nm = cdef->class_name ();
+
+                    cdef_object cdef_obj = cdef->get_object ();
+
+                    cdef_class cls = cdef_obj.get_class ();
+
+                    cdef_method meth = cls.find_method (meth_nm);
+
+                    if (meth.ok ())
+                      {
+                        // If the method we found is static, create a
+                        // new function name from the class name and
+                        // method name and create a simple function
+                        // handle below.  Otherwise, create a class
+                        // simple function handle.
+
+                        if (meth.is_static ())
+                          fcn_name = class_nm + '.' + meth_nm;
+                        else
+                          {
+                            octave_value meth_fcn = meth.get_function ();
+
+                            octave_fcn_handle *fh
+                              = new octave_fcn_handle (object, meth_fcn,
+                                                       class_nm, meth_nm);
+
+                            return octave_value (fh);
+                          }
+                      }
+                  }
+              }
+          }
+
+        // We didn't match anything above, so create handle to SIMPLE
+        // package function or static class method.  Function resolution
+        // is performed when the handle is used.
+
+        return octave_value (new octave_fcn_handle (fcn_name));
+      }
+
+    // If the function name refers to a sub/local/private function or a
+    // class method/constructor, create scoped function handle that is
+    // bound to that function.  Use the same precedence list as
+    // fcn_info::find but limit search to the following types of
+    // functions:
+    //
+    //   nested functions (and subfunctions)
+    //   local functions in the current file
+    //   private function
+    //   class method
+    //
+    // For anything else we create a simple function handle that will be
+    // resolved dynamically in the scope where it is evaluated.
+
+    symbol_scope curr_scope = get_current_scope ();
+
+    symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+    if (curr_scope)
+      {
+        octave_value ov_fcn
+          = symtab.find_scoped_function (fcn_name, curr_scope);
+
+        if (ov_fcn.is_defined ())
+          {
+            octave_function *fcn = ov_fcn.function_value ();
+
+            if (fcn->is_nested_function ())
+              {
+                if (! name_is_operator)
+                  {
+                    // Get current stack frame and return handle to nested
+                    // function.
+
+                    std::shared_ptr<stack_frame> frame
+                      = m_call_stack.get_current_stack_frame ();
+
+                    octave_fcn_handle *fh
+                      = new octave_fcn_handle (ov_fcn, fcn_name, frame);
+
+                    return octave_value (fh);
+                  }
+              }
+            else if (fcn->is_subfunction ()
+                     /* || fcn->is_localfunction () */
+                     || fcn->is_private_function ())
+              {
+                // Create handle to SCOPED function (sub/local function
+                // or private function).
+
+                std::list<std::string> parentage = fcn->parent_fcn_names ();
+
+                octave_fcn_handle *fh
+                  = new octave_fcn_handle (ov_fcn, fcn_name, parentage);
+
+                return octave_value (fh);
+              }
+          }
+
+        // If name is operator, we are in Fstr2func, so skip the stack
+        // frame for that function.
+
+        bool skip_first = name_is_operator;
+        octave_function *curr_fcn = current_function (skip_first);
+
+        if (curr_fcn && (curr_fcn->is_class_method ()
+                         || curr_fcn->is_class_constructor ()))
+          {
+            std::string dispatch_class = curr_fcn->dispatch_class ();
+
+            octave_value ov_meth
+              = symtab.find_method (fcn_name, dispatch_class);
+
+            if (ov_meth.is_defined ())
+              {
+                octave_function *fcn = ov_meth.function_value ();
+
+                // FIXME: do we need to check that it is a method of
+                // dispatch_class, or is it sufficient to just check
+                // that it is a method?
+
+                if (fcn->is_class_method ())
+                  {
+                    // Create CLASSSIMPLE handle to method.
+
+                    octave_fcn_handle *fh
+                      = new octave_fcn_handle (ov_meth, dispatch_class,
+                                               fcn_name);
+
+                    return octave_value (fh);
+                  }
+              }
+          }
+      }
+
+    octave_value ov_fcn = symtab.find_user_function (fcn_name);
+
+    // Create handle to SIMPLE function.  If the function is not found
+    // now, then we will look for it again when the handle is used.
+
+    return octave_value (new octave_fcn_handle (ov_fcn, fcn_name));
+  }
+
+/*
+%!test
+%! x = {".**", "power";
+%!      ".'", "transpose";
+%!      ".+", "plus";
+%!      ".-", "minus";
+%!      ".*", "times";
+%!      "./", "rdivide";
+%!      ".^", "power";
+%!      ".\\", "ldivide";
+%!      "<=", "le";
+%!      "==", "eq";
+%!      ">=", "ge";
+%!      "!=", "ne";
+%!      "~=", "ne";
+%!      "**", "mpower";
+%!      "~", "not";
+%!      "!", "not";
+%!      "\'", "ctranspose";
+%!      "+", "plus";
+%!      "-", "minus";
+%!      "*", "mtimes";
+%!      "/", "mrdivide";
+%!      "^", "mpower";
+%!      "\\", "mldivide";
+%!      "<", "lt";
+%!      ">", "gt";
+%!      "&", "and";
+%!      "|", "or"};
+%! for i = 1:rows (x)
+%!   assert (functions (str2func (x{i,1})).function, x{i,2});
+%! endfor
+*/
+
   octave_value
   tree_evaluator::evaluate (tree_decl_elt *elt)
   {
@@ -894,17 +1253,19 @@
   bool
   tree_evaluator::is_variable (const std::string& name) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_variable (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_variable (name);
   }
 
   bool
   tree_evaluator::is_local_variable (const std::string& name) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_local_variable (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_local_variable (name);
   }
 
   bool
@@ -941,49 +1302,55 @@
   bool
   tree_evaluator::is_variable (const symbol_record& sym) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_variable (sym);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_variable (sym);
   }
 
   bool
   tree_evaluator::is_defined (const symbol_record& sym) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_defined (sym);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_defined (sym);
   }
 
   bool tree_evaluator::is_global (const std::string& name) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_global (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_global (name);
   }
 
   octave_value
   tree_evaluator::varval (const symbol_record& sym) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.varval (sym);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->varval (sym);
   }
 
   octave_value
   tree_evaluator::varval (const std::string& name) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.varval (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->varval (name);
   }
 
   void tree_evaluator::install_variable (const std::string& name,
                                          const octave_value& value,
                                          bool global)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.install_variable (name, value, global);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->install_variable (name, value, global);
   }
 
   octave_value
@@ -1021,9 +1388,10 @@
   void
   tree_evaluator::assign (const std::string& name, const octave_value& val)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.assign (name, val);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->assign (name, val);
   }
 
   void
@@ -1520,16 +1888,20 @@
   }
 
   void tree_evaluator::push_stack_frame (octave_user_function *fcn,
-                                         unwind_protect *up_frame,
-                                         stack_frame *closure_frames)
+                                         const std::shared_ptr<stack_frame>& closure_frames)
   {
-    m_call_stack.push (fcn, up_frame, closure_frames);
+    m_call_stack.push (fcn, closure_frames);
   }
 
-  void tree_evaluator::push_stack_frame (octave_user_script *script,
-                                         unwind_protect *up_frame)
+  void tree_evaluator::push_stack_frame (octave_user_function *fcn,
+                                         const stack_frame::local_vars_map& local_vars)
   {
-    m_call_stack.push (script, up_frame);
+    m_call_stack.push (fcn, local_vars);
+  }
+
+  void tree_evaluator::push_stack_frame (octave_user_script *script)
+  {
+    m_call_stack.push (script);
   }
 
   void tree_evaluator::push_stack_frame (octave_function *fcn)
@@ -1564,7 +1936,7 @@
 
   void tree_evaluator::debug_where (std::ostream& os) const
   {
-    stack_frame *frm = m_call_stack.current_user_frame ();
+    std::shared_ptr<stack_frame> frm = m_call_stack.current_user_frame ();
 
     frm->display_stopped_in_message (os);
   }
@@ -1574,7 +1946,7 @@
     return m_call_stack.current_user_code ();
   }
 
-  unwind_protect * tree_evaluator::curr_fcn_unwind_protect_frame (void) const
+  unwind_protect * tree_evaluator::curr_fcn_unwind_protect_frame (void)
   {
     return m_call_stack.curr_fcn_unwind_protect_frame ();
   }
@@ -1636,13 +2008,13 @@
     return m_call_stack.is_class_constructor_executing (dclass);
   }
 
-  std::list<stack_frame *>
+  std::list<std::shared_ptr<stack_frame>>
   tree_evaluator::backtrace_frames (octave_idx_type& curr_user_frame) const
   {
     return m_call_stack.backtrace_frames (curr_user_frame);
   }
 
-  std::list<stack_frame *>
+  std::list<std::shared_ptr<stack_frame>>
   tree_evaluator::backtrace_frames (void) const
   {
     return m_call_stack.backtrace_frames ();
@@ -1782,9 +2154,10 @@
 
   octave_value tree_evaluator::find (const std::string& name)
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    octave_value val = frame.varval (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    octave_value val = frame->varval (name);
 
     if (val.is_defined ())
       return val;
@@ -1793,7 +2166,7 @@
     // subfunctions if we are currently executing a function defined
     // from a .m file.
 
-    octave_value fcn = frame.find_subfunction (name);
+    octave_value fcn = frame->find_subfunction (name);
 
     if (fcn.is_defined ())
       return fcn;
@@ -1805,37 +2178,42 @@
 
   void tree_evaluator::clear_objects (void)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_objects ();
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_objects ();
   }
 
   void tree_evaluator::clear_variable (const std::string& name)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_variable (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_variable (name);
   }
 
   void tree_evaluator::clear_variable_pattern (const std::string& pattern)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_variable_pattern (pattern);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_variable_pattern (pattern);
   }
 
   void tree_evaluator::clear_variable_regexp (const std::string& pattern)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_variable_regexp (pattern);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_variable_regexp (pattern);
   }
 
   void tree_evaluator::clear_variables (void)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_variables ();
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_variables ();
   }
 
   void tree_evaluator::clear_global_variable (const std::string& name)
@@ -2315,26 +2693,15 @@
     if (! cmd_list)
       return retval;
 
-    unwind_protect frame;
-
     if (m_call_stack.size () >= static_cast<size_t> (m_max_recursion_depth))
       error ("max_recursion_depth exceeded");
 
-    m_call_stack.push (&user_script, &frame);
-
-    // Set pointer to the current unwind_protect frame to allow
-    // certain builtins register simple cleanup in a very optimized manner.
-    // This is *not* intended as a general-purpose on-cleanup mechanism,
-
-    frame.add_method (m_call_stack, &call_stack::pop);
-
-    frame.protect_var (m_statement_context);
-    m_statement_context = SC_SCRIPT;
+    unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_SCRIPT);
 
     profiler::enter<octave_user_script> block (m_profiler, user_script);
 
     if (echo ())
-      push_echo_state (frame, tree_evaluator::ECHO_SCRIPTS, file_name);
+      push_echo_state (tree_evaluator::ECHO_SCRIPTS, file_name);
 
     cmd_list->accept (*this);
 
@@ -2357,8 +2724,7 @@
   octave_value_list
   tree_evaluator::execute_user_function (octave_user_function& user_function,
                                          int nargout,
-                                         const octave_value_list& xargs,
-                                         stack_frame *closure_frames)
+                                         const octave_value_list& xargs)
   {
     octave_value_list retval;
 
@@ -2390,25 +2756,13 @@
       return retval;
 #endif
 
-    unwind_protect frame;
-
     if (m_call_stack.size () >= static_cast<size_t> (m_max_recursion_depth))
       error ("max_recursion_depth exceeded");
 
-    // Save old and set current symbol table context, for
-    // eval_undefined_error().
-
-    m_call_stack.push (&user_function, &frame, closure_frames);
-
-    frame.add_method (m_call_stack, &call_stack::pop);
-
     bind_auto_fcn_vars (xargs.name_tags (), args.length (),
                         nargout, user_function.takes_varargs (),
                         user_function.all_va_args (args));
 
-    if (user_function.is_anonymous_function ())
-      init_local_fcn_vars (user_function);
-
     tree_parameter_list *param_list = user_function.parameter_list ();
 
     if (param_list && ! param_list->varargs_only ())
@@ -2428,37 +2782,23 @@
         define_parameter_list_from_arg_vector (ret_list, ret_args);
       }
 
-    // Force parameter list to be undefined when this function exits.
-    // Doing so decrements the reference counts on the values of local
-    // variables that are also named function parameters.
-
-    //    if (param_list)
-    //      frame.add_method (this, &tree_evaluator::undefine_parameter_list,
-    //                        param_list);
-
-    // Force return list to be undefined when this function exits.
-    // Doing so decrements the reference counts on the values of local
-    // variables that are also named values returned by this function.
-
-    //    if (ret_list)
-    //      frame.add_method (this, &tree_evaluator::undefine_parameter_list,
-    //                        ret_list);
-
-    frame.add_method (&user_function,
-                      &octave_user_function::restore_warning_states);
+    unwind_action act2 ([&user_function] () {
+                          user_function.restore_warning_states ();
+                        });
 
     // Evaluate the commands that make up the function.
 
-    frame.protect_var (m_statement_context);
-    m_statement_context = SC_FUNCTION;
-
-    frame.add_method (m_call_stack, &call_stack::clear_current_frame_values);
+    unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_FUNCTION);
+
+    unwind_action act1 ([this] () {
+                          m_call_stack.clear_current_frame_values ();
+                        });
 
     {
       profiler::enter<octave_user_function> block (m_profiler, user_function);
 
       if (echo ())
-        push_echo_state (frame, tree_evaluator::ECHO_FUNCTIONS,
+        push_echo_state (tree_evaluator::ECHO_FUNCTIONS,
                          user_function.fcn_file_name ());
 
       if (user_function.is_special_expr ())
@@ -2504,25 +2844,6 @@
                                                       varargout);
       }
 
-    if (user_function.is_nested_function ()
-        || user_function.is_parent_function ())
-      {
-        // Copy current stack frame to handles to nested functions.
-
-        for (octave_idx_type i = 0; i < retval.length (); i++)
-          {
-            octave_value val = retval(i);
-
-            if (val.is_function_handle ())
-              {
-                octave_fcn_handle *fh = val.fcn_handle_value ();
-
-                if (fh)
-                  fh->push_closure_context (*this);
-              }
-          }
-      }
-
     return retval;
   }
 
@@ -3537,13 +3858,17 @@
   }
 
   void
-  tree_evaluator::push_echo_state (unwind_protect& frame, int type,
-                                   const std::string& file_name,
+  tree_evaluator::push_echo_state (int type, const std::string& file_name,
                                    size_t pos)
   {
-    push_echo_state_cleanup (frame);
-
-    set_echo_state (type, file_name, pos);
+    unwind_protect *frame = m_call_stack.curr_fcn_unwind_protect_frame ();
+
+    if (frame)
+      {
+        push_echo_state_cleanup (*frame);
+
+        set_echo_state (type, file_name, pos);
+      }
   }
 
   void
@@ -3847,17 +4172,6 @@
       assign ("varargin", va_args.cell_value ());
   }
 
-  void tree_evaluator::init_local_fcn_vars (octave_user_function& user_fcn)
-  {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    const octave_user_function::local_vars_map& lviv
-      = user_fcn.local_var_init_vals ();
-
-    for (const auto& nm_ov : lviv)
-      frame.assign (nm_ov.first, nm_ov.second);
-  }
-
   std::string
   tree_evaluator::check_autoload_file (const std::string& nm) const
   {
--- a/libinterp/parse-tree/pt-eval.h	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/parse-tree/pt-eval.h	Thu Jun 11 01:22:45 2020 -0400
@@ -42,6 +42,7 @@
 #include "ovl.h"
 #include "profiler.h"
 #include "pt-walk.h"
+#include "stack-frame.h"
 
 class octave_user_code;
 
@@ -211,8 +212,7 @@
 
     octave_value_list
     execute_user_function (octave_user_function& user_function,
-                           int nargout, const octave_value_list& args,
-                           stack_frame *closure_frames = nullptr);
+                           int nargout, const octave_value_list& args);
 
     void visit_octave_user_function_header (octave_user_function&);
 
@@ -298,6 +298,8 @@
 
     Matrix ignored_fcn_outputs (void) const;
 
+    octave_value make_fcn_handle (const std::string& nm);
+
     octave_value evaluate (tree_decl_elt *);
 
     void install_variable (const std::string& name,
@@ -377,27 +379,23 @@
     void push_stack_frame (const symbol_scope& scope);
 
     void push_stack_frame (octave_user_function *fcn,
-                           unwind_protect *up_frame,
-                           stack_frame *closure_frames = nullptr);
+                           const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
-    void push_stack_frame (octave_user_script *script,
-                           unwind_protect *up_frame);
+    void push_stack_frame (octave_user_function *fcn,
+                           const stack_frame::local_vars_map& local_vars);
+
+    void push_stack_frame (octave_user_script *script);
 
     void push_stack_frame (octave_function *fcn);
 
     void pop_stack_frame (void);
 
-    const stack_frame& get_current_stack_frame (void) const
+    std::shared_ptr<stack_frame> get_current_stack_frame (void) const
     {
       return m_call_stack.get_current_stack_frame ();
     }
 
-    stack_frame& get_current_stack_frame (void)
-    {
-      return m_call_stack.get_current_stack_frame ();
-    }
-
-    stack_frame * current_user_frame (void) const
+    std::shared_ptr<stack_frame> current_user_frame (void) const
     {
       return m_call_stack.current_user_frame ();
     }
@@ -418,7 +416,7 @@
 
     octave_user_code * current_user_code (void) const;
 
-    unwind_protect * curr_fcn_unwind_protect_frame (void) const;
+    unwind_protect * curr_fcn_unwind_protect_frame (void);
 
     // Current function that we are debugging.
     octave_user_code * debug_user_code (void) const;
@@ -443,10 +441,10 @@
 
     bool is_class_constructor_executing (std::string& dispatch_class) const;
 
-    std::list<stack_frame *>
+    std::list<std::shared_ptr<stack_frame>>
     backtrace_frames (octave_idx_type& curr_user_frame) const;
 
-    std::list<stack_frame *> backtrace_frames () const;
+    std::list<std::shared_ptr<stack_frame>> backtrace_frames () const;
 
     std::list<frame_info> backtrace_info (octave_idx_type& curr_user_frame,
                                           bool print_subfn = true) const;
@@ -711,8 +709,8 @@
 
     std::list<octave_lvalue> make_lvalue_list (tree_argument_list *);
 
-    void push_echo_state (unwind_protect& frame, int type,
-                          const std::string& file_name, size_t pos = 1);
+    void push_echo_state (int type, const std::string& file_name,
+                          size_t pos = 1);
 
   private:
 
@@ -745,8 +743,6 @@
                              int nargout, bool takes_varargs,
                              const octave_value_list& va_args);
 
-    void init_local_fcn_vars (octave_user_function& user_fcn);
-
     std::string check_autoload_file (const std::string& nm) const;
 
     interpreter& m_interpreter;
--- a/libinterp/parse-tree/pt-fcn-handle.cc	Tue Jun 09 14:11:13 2020 -0700
+++ b/libinterp/parse-tree/pt-fcn-handle.cc	Thu Jun 11 01:22:45 2020 -0400
@@ -32,6 +32,7 @@
 #include "interpreter-private.h"
 #include "pt-anon-scopes.h"
 #include "pt-fcn-handle.h"
+#include "stack-frame.h"
 
 namespace octave
 {
@@ -62,7 +63,7 @@
   octave_value
   tree_fcn_handle::evaluate (tree_evaluator& tw, int)
   {
-    return make_fcn_handle (tw.get_interpreter (), m_name);
+    return tw.make_fcn_handle (m_name);
   }
 
   tree_anon_fcn_handle::~tree_anon_fcn_handle (void)
@@ -130,23 +131,23 @@
 
     std::set<std::string> free_vars = anon_fcn_ctx.free_variables ();
 
-    octave_user_function::local_vars_map local_var_init_vals;
+    stack_frame::local_vars_map local_vars;
 
     call_stack& cs = tw.get_call_stack ();
 
-    stack_frame& frame = cs.get_current_stack_frame ();
+    std::shared_ptr<stack_frame> frame = cs.get_current_stack_frame ();
 
     for (auto& name : free_vars)
       {
-        octave_value val = frame.varval (name);
+        octave_value val = frame->varval (name);
 
         if (val.is_defined ())
-          local_var_init_vals[name] = val;
+          local_vars[name] = val;
       }
 
     octave_user_function *af
       = new octave_user_function (new_scope, param_list_dup, ret_list,
-                                  stmt_list, local_var_init_vals);
+                                  stmt_list);
 
     octave_function *curr_fcn = cs.current_function ();
 
@@ -174,10 +175,7 @@
 
     octave_value ov_fcn (af);
 
-    // octave_value fh (octave_fcn_binder::maybe_binder (ov_fcn, m_interpreter));
-
-    return octave_value (new octave_fcn_handle
-                         (ov_fcn, octave_fcn_handle::anonymous));
+    return octave_value (new octave_fcn_handle (ov_fcn, local_vars));
   }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/argnames.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,16 @@
+## -*- texinfo -*-
+## @deftypefn {} {} argnames (@var{fun})
+## Return a cell array of character strings containing the names of the
+## arguments of the inline function @var{fun}.
+## @seealso{inline, formula, vectorize}
+## @end deftypefn
+
+function args = argnames (obj)
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  args = obj.args;
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/char.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,18 @@
+## -*- texinfo -*-
+## @deftypefn {} {} char (@var{fun})
+## Return a character string representing the inline function @var{fun}.
+##
+## Note that @code{char (@var{fun})} is equivalent to
+## @code{formula (@var{fun})}.
+## @seealso{char, argnames, inline, vectorize}
+## @end deftypefn
+
+function expr = char (obj)
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  expr = obj.expr;
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/feval.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,36 @@
+########################################################################
+##
+## Copyright (C) 2020 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/>.
+##
+########################################################################
+
+function retval = feval (fcn, varargin)
+
+  if (nargin < 1)
+    print_usage ();
+  endif
+
+  fh = eval (sprintf ("@(%s) %s", strjoin (fcn.args, ","), fcn.expr));
+
+  retval = fh (varargin{:});
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/formula.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,18 @@
+## -*- texinfo -*-
+## @deftypefn {} {} formula (@var{fun})
+## Return a character string representing the inline function @var{fun}.
+##
+## Note that @code{char (@var{fun})} is equivalent to
+## @code{formula (@var{fun})}.
+## @seealso{char, argnames, inline, vectorize}
+## @end deftypefn
+
+function expr = formula (obj)
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  expr = obj.expr;
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/inline.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,190 @@
+########################################################################
+##
+## Copyright (C) 2020 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/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn  {} {} inline (@var{str})
+## @deftypefnx {} {} inline (@var{str}, @var{arg1}, @dots{})
+## @deftypefnx {} {} inline (@var{str}, @var{n})
+##
+## This function is obsolete.  Use anonymous functions
+## (@pxref{Anonymous Functions}) instead.
+##
+## Create an inline function from the character string @var{str}.
+##
+## If called with a single argument, the arguments of the generated
+## function are extracted from the function itself.  The generated
+## function arguments will then be in alphabetical order.  It should be
+## noted that i and j are ignored as arguments due to the ambiguity
+## between their use as a variable or their use as an built-in constant.
+## All arguments followed by a parenthesis are considered to be
+## functions.  If no arguments are found, a function taking a single
+## argument named @code{x} will be created.
+##
+## If the second and subsequent arguments are character strings, they
+## are the names of the arguments of the function.
+##
+## If the second argument is an integer @var{n}, the arguments are
+## @qcode{"x"}, @qcode{"P1"}, @dots{}, @qcode{"P@var{N}"}.
+##
+## @strong{Caution:} the use of @code{inline} is discouraged and it may
+## be removed from a future version of Octave.  The preferred way to
+## create functions from strings is through the use of anonymous
+## functions (@pxref{Anonymous Functions}) or @code{str2func}.
+## @seealso{argnames, formula, vectorize, str2func}
+## @end deftypefn
+
+function obj = inline (expr, varargin)
+
+  if (nargin == 0)
+    print_usage ();
+  endif
+
+  if (! ischar (expr))
+    error ("inline: EXPR must be a string");
+  endif
+
+  if (nargin == 1)
+    args = parse_expr_for_args (expr);
+  elseif (nargin == 2)
+    n = varargin{1};
+    if (isnumeric (n))
+      if (isscalar (n) && fix (n) == n)
+        if (n > 0)
+          args = strsplit (["x", sprintf(":P%d", 1:n)], ":");
+        else
+          error ("inline: N must be a positive integer");
+        endif
+      else
+        error ("inline: N must be an integer");
+      endif
+    else
+      args = {"x"};
+    endif
+  elseif (iscellstr (varargin))
+    args = varargin;
+  else
+    error ("inline: additional arguments must be strings");
+  endif
+
+  p.expr = expr;
+  p.args = args(:);
+  p.numArgs = numel (args);
+  tmp = [args; num2cell(1:numel(args))];
+  p.inputExpr = sprintf ("%s = INLINE_INPUTS_{%d}; ", tmp{:});
+  p.isEmpty = false;
+  p.version = 1;
+
+  obj = __inline_ctor__ (p);
+
+endfunction
+
+## The following function was translated directly from the original C++
+## version.  Yes, it will be slow, but the use of inline functions is
+## strongly discouraged anyway, and most expressions will probably be
+## short.  It may also be buggy.  Well, don't use this object!  Use
+## function handles instead!
+
+function args = parse_expr_for_args (expr)
+
+  persistent symbols_to_skip = {"i", "j", "NaN", "nan", "Inf", "inf", ...
+                                "NA", "pi", "e", "eps"};
+
+  is_arg = false;
+  in_string = false;
+  tmp_arg = "";
+  i = 1;
+  expr_length = length (expr);
+  args = {};
+
+  while (i <= expr_length)
+
+    terminate_arg = false;
+    c = expr(i++);
+
+    if (in_string)
+      if (c == "'" || c == '"')
+        in_string = false;
+      endif
+    elseif (c == "'" || c == '"')
+      in_string = true;
+      if (is_arg)
+        terminate_arg = true;
+      endif
+    elseif (! isalpha (c) && c != "_")
+      if (! is_arg)
+        continue;
+      elseif (isdigit (c))
+        tmp_arg(end+1) = c;
+      else
+        ## Before we do anything remove trailing whitespaces.
+        while (i <= expr_length && isspace (c))
+          c = expr(i++);
+        endwhile
+
+        ## Do we have a variable or a function?
+        if (c != "(")
+          terminate_arg = true;
+        else
+          tmp_arg = "";
+          is_arg = false;
+        endif
+      endif
+    elseif (! is_arg)
+      if (c == "e" || c == "E")
+        ## Possible number in exponent form, not arg.
+        if (isdigit (expr(i)) || expr(i) == "-" || expr(i) == "+")
+          continue;
+        endif
+      endif
+      is_arg = true;
+      tmp_arg(end+1) = c;
+    else
+      tmp_arg(end+1) = c;
+    endif
+
+    if (terminate_arg || (i == expr_length+1 && is_arg))
+      have_arg = false;
+      if (any (strcmp (tmp_arg, args)))
+        have_arg = true;
+      endif
+
+      if (! (have_arg || any (strcmp (tmp_arg, symbols_to_skip))))
+        args{end+1} = tmp_arg;
+      endif
+
+      tmp_arg = "";
+      is_arg = false;
+    endif
+
+  endwhile
+
+  ## Sort the arguments into ASCII order.
+  args = sort (args);
+
+  if (isempty (args))
+    args = {"x"};
+  endif
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/module.mk	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,11 @@
+## Automake fails to process "include %reldir%/module.mk" in the directory
+## above.  All of the commands which would normally be in this file were
+## manually placed in scripts/legacy/module.mk to avoid using the "include"
+## directive.
+##
+## This is an Automake bug.  Automake has switched to a Perl backend which uses
+## the following pattern to detect a path:
+##
+## my $PATH_PATTERN = '(\w|[+/.-])+';
+##
+## This pattern only includes alphanumeric, '_', and [+/.-], but not "@".
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/subsref.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,52 @@
+########################################################################
+##
+## Copyright (C) 2020 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/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn {} {@var{value} =} subsref (@var{fcn}, @var{idx})
+## Perform subscripted function call on the inline function object @var{fcn}.
+## @end deftypefn
+
+function retval = subsref (fcn, idx)
+
+  if (nargin != 2)
+    print_usage ();
+  endif
+
+  if (isempty (idx))
+    error ("@inline/subsref: missing index");
+  endif
+
+  if (strcmp (idx(1).type, "()"))
+    args = idx.subs;
+    if (numel (args) > 0)
+      retval = feval (fcn, args{:});
+    else
+      retval = feval (fcn);
+    endif
+  else
+    error ("@inline/subsref: invalid subscript type");
+  endif
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/vectorize.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,62 @@
+## -*- texinfo -*-
+## @deftypefn {} {} vectorize (@var{fun})
+## Create a vectorized version of the inline function @var{fun} by
+## replacing all occurrences of @code{*}, @code{/}, etc., with
+## @code{.*}, @code{./}, etc.
+##
+## This may be useful, for example, when using inline functions with
+## numerical integration or optimization where a vector-valued function
+## is expected.
+##
+## @example
+## @group
+## fcn = vectorize (inline ("x^2 - 1"))
+##    @result{} fcn = f(x) = x.^2 - 1
+## quadv (fcn, 0, 3)
+##    @result{} 6
+## @end group
+## @end example
+## @seealso{inline, formula, argnames}
+## @end deftypefn
+
+## The following function was translated directly from the original C++
+## version.  Yes, it will be slow, but the use of inline functions is
+## strongly discouraged anyway, and most expressions will probably be
+## short.  It may also be buggy.  Well, don't use this object!  Use
+## function handles instead!
+
+function fcn = vectorize (obj)
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  new_expr = "";
+
+  expr = obj.expr;
+  len = length (expr);
+  i = 1;
+
+  while (i <= len)
+    c = expr(i);
+
+    if (c == "*" || c == "/" || c == "\\" || c == "^")
+      if (i > 1 && expr(i-1) != ".")
+        new_expr(end+1) = ".";
+      endif
+
+      ## Special case for ** operator.
+      if (c == '*' && i < (len - 1) && expr(i+1) == '*')
+        new_expr(end+1) = "*";
+        i++;
+      endif
+    endif
+
+    new_expr(end+1) = c;
+    i++;
+
+  endwhile
+
+  fcn = inline (new_expr);
+
+endfunction
--- a/scripts/legacy/module.mk	Tue Jun 09 14:11:13 2020 -0700
+++ b/scripts/legacy/module.mk	Thu Jun 11 01:22:45 2020 -0400
@@ -12,6 +12,31 @@
   %reldir%/strread.m \
   %reldir%/textread.m
 
+## include %reldir%/@inline/module.mk
+## The include above fails because Automake cannot process the '@' character.
+## As a work around, the contents of %reldir%/@inline/module.mk are placed directly
+## in this module.mk file.
+scripts_EXTRA_DIST += %reldir%/@inline/module.mk
+######################## include %reldir%/@inline/module.mk ########################
+FCN_FILE_DIRS += %reldir%/@inline
+
+%canon_reldir%_@inline_FCN_FILES = \
+  %reldir%/@inline/argnames.m \
+  %reldir%/@inline/formula.m \
+  %reldir%/@inline/inline.m \
+  %reldir%/@inline/vectorize.m
+
+%canon_reldir%_@inlinedir = $(fcnfiledir)/@inline
+
+%canon_reldir%_@inline_DATA = $(%canon_reldir%_@inline_FCN_FILES)
+
+FCN_FILES += $(%canon_reldir%_@inline_FCN_FILES)
+
+PKG_ADD_FILES += %reldir%/@inline/PKG_ADD
+
+DIRSTAMP_FILES += %reldir%/@inline/$(octave_dirstamp)
+####################### end include %reldir%/@inline/module.mk #####################
+
 %canon_reldir%dir = $(fcnfiledir)/legacy
 
 %canon_reldir%_DATA = $(%canon_reldir%_FCN_FILES)
--- a/scripts/module.mk	Tue Jun 09 14:11:13 2020 -0700
+++ b/scripts/module.mk	Thu Jun 11 01:22:45 2020 -0400
@@ -45,7 +45,7 @@
 ## The include above fails because Automake cannot process the '@' character.
 ## As a work around, the contents of %reldir%/@ftp/module.mk are placed directly
 ## in this module.mk file.
-%canon_reldir%_EXTRA_DIST += %reldir%/@ftp/module.mk
+scripts_EXTRA_DIST += %reldir%/@ftp/module.mk
 ######################## include %reldir%/@ftp/module.mk ########################
 FCN_FILE_DIRS += %reldir%/@ftp
 
--- a/scripts/plot/draw/fplot.m	Tue Jun 09 14:11:13 2020 -0700
+++ b/scripts/plot/draw/fplot.m	Thu Jun 11 01:22:45 2020 -0400
@@ -83,11 +83,9 @@
 ##
 ## @code{fplot} performance is better when the function accepts and returns a
 ## vector argument.  Consider this when writing user-defined functions and use
-## element-by-element operators such as @code{.*}, @code{./}, etc.  See the
-## function @code{vectorize} for potentially converting inline or anonymous
-## functions to vectorized versions.
+## element-by-element operators such as @code{.*}, @code{./}, etc.
 ##
-## @seealso{ezplot, plot, vectorize}
+## @seealso{ezplot, plot}
 ## @end deftypefn
 
 function [X, Y] = fplot (varargin)
@@ -99,8 +97,8 @@
   endif
 
   fn = varargin{1};
-  if (strcmp (typeinfo (fn), "inline function"))
-    fn = vectorize (fn);
+  if (isa (fn, "inline"))
+    fn = vectorize (inline (fn));
     nam = formula (fn);
   elseif (is_function_handle (fn))
     nam = func2str (fn);
--- a/scripts/plot/draw/private/__ezplot__.m	Tue Jun 09 14:11:13 2020 -0700
+++ b/scripts/plot/draw/private/__ezplot__.m	Thu Jun 11 01:22:45 2020 -0400
@@ -84,7 +84,7 @@
     endif
   endif
 
-  if (strcmp (typeinfo (fun), "inline function"))
+  if (isa (fun, "inline"))
     argids = argnames (fun);
     if (isplot && length (argids) == 2)
       nargs = 2;
@@ -147,7 +147,7 @@
       yarg = args{2};
     endif
   else
-    error ("%s: F must be string, inline function, or function handle", ezfunc);
+    error ("%s: F must be a string or function handle", ezfunc);
   endif
 
   if (nargin > 2 || (nargin == 2 && isplot))
@@ -165,7 +165,7 @@
         error ("%s: expecting a function of %d arguments", ezfunc, nargs);
       endif
       fstry = formula (funy);
-    elseif (strcmp (typeinfo (funy), "inline function"))
+    elseif (isa (funy, "inline"))
       parametric = true;
       if (numel (argnames (funy)) != nargs)
         error ("%s: expecting a function of %d arguments", ezfunc, nargs);
@@ -210,7 +210,7 @@
           error ("%s: expecting a function of %d arguments", ezfunc, nargs);
         endif
         fstrz = formula (funz);
-      elseif (strcmp (typeinfo (funz), "inline function"))
+      elseif (isa (funz, "inline"))
         if (numel (argnames (funz)) != nargs)
           error ("%s: expecting a function of %d arguments", ezfunc, nargs);
         endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fcn-handle/bug-51567.tst	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,5 @@
+%!shared a
+%! a = bug51567 ();
+%!
+%!assert <51567> (a.doit_1 (), 13);
+%!assert <51567> (a.doit_2 (), 42);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fcn-handle/bug-57941.tst	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,9 @@
+%!test <*57941>
+%! [r1, r2] = bug57941a (2);
+%! assert (r1, 6);
+%! assert (r2, 24);
+
+%!test <*57941>
+%! [fh1, fh2] = bug57941b (2);
+%! assert (fh1 (3), 6);
+%! assert (fh2 (3, 4), 24);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fcn-handle/bug51567.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,24 @@
+classdef bug51567 < handle
+  properties
+    fh1;
+    fh2;
+  endproperties
+  methods
+    function obj = bug51567 (self)
+      obj.fh1 = str2func ("bar");
+      obj.fh2 = @baz;
+    endfunction
+    function r = bar (obj)
+      r = 13;
+    endfunction
+    function r = baz (obj)
+      r = 42;
+    endfunction
+    function r = doit_1 (obj)
+      r = obj.fh1 ();
+    endfunction
+    function r = doit_2 (obj)
+      r = obj.fh2 ();
+    endfunction
+  endmethods
+endclassdef
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fcn-handle/bug57941a.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,12 @@
+function [r1, r2] = bug57941a (A)
+  fh1 = @nested1;
+  function z = nested1 (x)
+    z = A * x;
+  end
+  fh2 = @nested2;
+  function z = nested2 (x,y)
+    z = A * x .* y;
+  end
+  r1 = fh1 (3);
+  r2 = fh2 (3, 4);
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fcn-handle/bug57941b.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,10 @@
+function [fh1, fh2] = bug57941b (A)
+  fh1 = @nested1;
+  function z = nested1 (x)
+    z = A * x;
+  end
+  fh2 = @nested2;
+  function z = nested2 (x,y)
+    z = A * x .* y;
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fcn-handle/bug58519.tst	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,5 @@
+%!test <58519>
+%! fieldname = "a";
+%! structure = struct (fieldname, 42);
+%! anonfunc = @ () structure.(fieldname);
+%! assert (anonfunc (), 42)
--- a/test/fcn-handle/module.mk	Tue Jun 09 14:11:13 2020 -0700
+++ b/test/fcn-handle/module.mk	Thu Jun 11 01:22:45 2020 -0400
@@ -10,13 +10,21 @@
   %reldir%/@fhdr_other/getsize_loop.m \
   %reldir%/@fhdr_parent/fhdr_parent.m \
   %reldir%/@fhdr_parent/numel.m \
+  %reldir%/bug-51567.tst \
+  %reldir%/bug51567.m \
   %reldir%/bug51709_a.m \
   %reldir%/bug51709_c.m \
+  %reldir%/bug57941.tst \
+  %reldir%/bug58519.tst \
+  %reldir%/bug57941a.m \
+  %reldir%/bug57941b.m \
   %reldir%/derived-resolution.tst \
   %reldir%/f1.m \
   %reldir%/keyword.tst \
   %reldir%/object-method.tst \
   %reldir%/package-function.tst \
+  %reldir%/shared-ctx.tst \
+  %reldir%/shared_ctx.m \
   %reldir%/static-method.tst
 
 TEST_FILES += $(fcn_handle_TEST_FILES)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fcn-handle/shared-ctx.tst	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,15 @@
+## Test that multiple handles to nested functions created in the same
+## context share that call stack context, but that separately created
+## handles have a separate (shared) context.
+
+%!test
+%! [add10, sub10, mul10, div10] = shared_ctx (10);
+%! [add100, sub100, mul100, div100] = shared_ctx (100);
+%! assert (add10 (2), 12);
+%! assert (add100 (20), 120);
+%! assert (sub10 (4), 8);
+%! assert (sub100 (40), 80);
+%! assert (mul10 (5), 40);
+%! assert (mul100 (50), 4000);
+%! assert (div10 (4), 10);
+%! assert (div100 (40), 100);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fcn-handle/shared_ctx.m	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,22 @@
+function [add, sub, mul, div] = shared_ctx (val)
+  add = @add_fun;
+  sub = @sub_fun;
+  mul = @mul_fun;
+  div = @div_fun;
+  function r = add_fun (x)
+    val += x;
+    r = val;
+  endfunction
+  function r = sub_fun (x)
+    val -= x;
+    r = val;
+  endfunction
+  function r = mul_fun (x)
+    val *= x;
+    r = val;
+  endfunction
+  function r = div_fun (x)
+    val /= x;
+    r = val;
+  endfunction
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/inline-fcn.tst	Thu Jun 11 01:22:45 2020 -0400
@@ -0,0 +1,33 @@
+## INLINE contstructor
+
+%!shared fn
+%! fn = inline ("x.^2 + 1");
+%!assert (feval (fn, 6), 37)
+%!assert (fn (6), 37)
+%!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10)
+%!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5)
+%!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5)
+
+## Test input validation
+%!error inline ()
+%!error <EXPR must be a string> inline (1)
+%!error <N must be an integer> inline ("2", ones (2,2))
+%!error <N must be a positive integer> inline ("2", -1)
+%!error <additional arguments must be strings> inline ("2", "x", -1, "y")
+
+## FORMULA
+
+%!assert (formula (fn), "x.^2 + 1")
+%!assert (formula (fn), char (fn))
+
+## ARGNAMES
+
+%!assert (argnames (fn), {"x"})
+%!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"})
+%!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"})
+
+## VECTORIZE
+
+%!assert (formula (vectorize (fn)), "x.^2 + 1")
+%!assert (formula (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z")
+%!assert (formula (vectorize (inline ("2**x^5"))), "2.**x.^5")
--- a/test/module.mk	Tue Jun 09 14:11:13 2020 -0700
+++ b/test/module.mk	Thu Jun 11 01:22:45 2020 -0400
@@ -31,6 +31,7 @@
   %reldir%/global.tst \
   %reldir%/if.tst \
   %reldir%/index.tst \
+  %reldir%/inline-fcn.tst \
   %reldir%/integer.tst \
   %reldir%/io.tst \
   %reldir%/jit.tst \