changeset 28439:e760fef2829c stable

refactor octave_fcn_handle class * ov-fcn-handle.h, ov-fcn-handle.cc (class octave_fcn_handle): split octave_fcn_handle internally into separate sub-classes for the following types of function handles: simple, scoped, nested, classsimple, and anonymous. Update all uses. * load-path.cc (load_path::package_info::find_private_fcn): Don't search for private files that are not already in the private function map. * ls-mat5.cc (read_mat5_binary_element): Update handling of local variables for anonymous functions. * stack-frame.h, stack-frame.cc (stack_frame::set_closure_links, stack_frame::dup, compiled_fcn_stack_frame::dup, script_stack_frame::dup, user_fcn_stack_frame::dup, scope_stack_frame::dup): Delete unnecessary functions. * ov-fcn.h (octave_function::is_nested_function, octave_function::is_parent_function): New virtual functions.
author John W. Eaton <jwe@octave.org>
date Wed, 29 Apr 2020 14:10:27 -0400
parents 55f82d23fe5e
children 23fe97205db5
files libinterp/corefcn/load-path.cc libinterp/corefcn/ls-mat5.cc libinterp/corefcn/stack-frame.cc libinterp/corefcn/stack-frame.h libinterp/dldfcn/__init_fltk__.cc libinterp/octave-value/cdef-class.cc libinterp/octave-value/cdef-manager.cc libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-fcn-handle.h libinterp/octave-value/ov-fcn.h libinterp/operators/op-fcn.cc libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-fcn-handle.cc
diffstat 13 files changed, 3083 insertions(+), 1611 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/load-path.cc	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/corefcn/load-path.cc	Wed Apr 29 14:10:27 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-mat5.cc	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/corefcn/ls-mat5.cc	Wed Apr 29 14:10:27 2020 -0400
@@ -70,6 +70,7 @@
 #include "pager.h"
 #include "parse.h"
 #include "pt-eval.h"
+#include "stack-frame.h"
 #include "sysdep.h"
 #include "unwind-prot.h"
 #include "utils.h"
@@ -900,6 +901,7 @@
                                                         "", "", fname);
 
                         if (ov_fcn.is_defined ())
+                          // XXX FCN_HANDLE: SIMPLE/SCOPED
                           tc = octave_value (new octave_fcn_handle (ov_fcn, fname));
                       }
                     else
@@ -926,6 +928,7 @@
                                                         "", "", fname);
 
                         if (ov_fcn.is_defined ())
+                          // XXX FCN_HANDLE: SIMPLE/SCOPED
                           tc = octave_value (new octave_fcn_handle (ov_fcn, fname));
                         else
                           {
@@ -948,6 +951,7 @@
                                                     "", "", fname);
 
                     if (ov_fcn.is_defined ())
+                      // XXX FCN_HANDLE: SIMPLE/SCOPED
                       tc = octave_value (new octave_fcn_handle (ov_fcn, fname));
                     else
                       {
@@ -977,14 +981,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)
               {
@@ -995,10 +992,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);
@@ -1011,7 +1025,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");
--- a/libinterp/corefcn/stack-frame.cc	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/corefcn/stack-frame.cc	Wed Apr 29 14:10:27 2020 -0400
@@ -69,8 +69,6 @@
 
     ~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
@@ -189,8 +187,6 @@
       delete m_unwind_protect_frame;
     }
 
-    script_stack_frame * dup (void) const;
-
     bool is_user_script_frame (void) const { return true; }
 
     static std::shared_ptr<stack_frame>
@@ -430,8 +426,6 @@
       delete m_unwind_protect_frame;
     }
 
-    user_fcn_stack_frame * dup (void) const;
-
     bool is_user_fcn_frame (void) const { return true; }
 
     static std::shared_ptr<stack_frame>
@@ -513,8 +507,6 @@
 
     ~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; }
@@ -1430,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;
@@ -1464,12 +1450,6 @@
     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 ();
@@ -2058,12 +2038,6 @@
       }
   }
 
-  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.
 
@@ -2353,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	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/corefcn/stack-frame.h	Wed Apr 29 14:10:27 2020 -0400
@@ -180,8 +180,6 @@
 
     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.
@@ -307,12 +305,6 @@
     std::shared_ptr<stack_frame>
     access_link (void) const {return m_access_link; }
 
-    void set_closure_links (const std::shared_ptr<stack_frame>& dup_frame)
-    {
-      m_static_link = dup_frame;
-      m_access_link = dup_frame;
-    }
-
     virtual size_t size (void) const;
 
     virtual void resize (size_t);
--- a/libinterp/dldfcn/__init_fltk__.cc	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/dldfcn/__init_fltk__.cc	Wed Apr 29 14:10:27 2020 -0400
@@ -2532,7 +2532,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	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/octave-value/cdef-class.cc	Wed Apr 29 14:10:27 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;
@@ -979,12 +979,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	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/octave-value/cdef-manager.cc	Wed Apr 29 14:10:27 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/ov-fcn-handle.cc	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.cc	Wed Apr 29 14:10:27 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"
@@ -83,381 +84,2412 @@
 
 const std::string octave_fcn_handle::anonymous ("@<anonymous>");
 
-octave_fcn_handle::octave_fcn_handle (const std::string& n)
-  : m_fcn (), m_obj (), m_name (n), m_scope (), m_is_nested (false),
-    m_closure_frames (), m_local_vars (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 (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);
+    }
+
+    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)
     {
-      // 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;
+      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);
+    }
+
+    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)
+    {
+      return m_fcn.user_function_value ();
     }
-}
-
-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 (), m_local_vars (nullptr), m_dispatch_class ()
-{
-  if (! m_name.empty () && m_name[0] == '@')
-    m_name = m_name.substr (1);
-
-  size_t pos = m_name.find ('.');
-
-  if (pos != std::string::npos)
+
+    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);
+    }
+
+    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)
+    {
+      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);
+    }
+
+    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)
     {
-      // 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;
+      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 '.':
+        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);
+                  }
+              }
+            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 (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 (), m_local_vars (nullptr), m_dispatch_class ()
-{
-  octave_user_function *uf = m_fcn.user_function_value (true);
-
-  if (uf && m_name != anonymous)
-    {
-      octave::symbol_scope uf_scope = uf->scope ();
-
-      if (uf_scope)
-        uf_scope.cache_name (m_name);
-    }
-
-  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 (), m_local_vars (nullptr), m_dispatch_class ()
-{
-  octave_user_function *uf = m_fcn.user_function_value (true);
-
-  if (uf && m_name != anonymous)
-    {
-      octave::symbol_scope uf_scope = uf->scope ();
-
-      if (uf_scope)
-        uf_scope.cache_name (m_name);
-    }
-
-  if (uf && uf->is_nested_function () && ! uf->is_subfunction ())
-    m_is_nested = true;
-}
-
-octave_fcn_handle::octave_fcn_handle (const octave_value& f,
+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)
-  : m_fcn (f), m_obj (), m_name (anonymous), m_scope (), m_is_nested (false),
-    m_closure_frames (), m_local_vars (new octave::stack_frame::local_vars_map (local_vars)),
-    m_dispatch_class ()
+  : 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_fcn (fh.m_fcn), m_obj (fh.m_obj),
-    m_name (fh.m_name), m_scope (fh.m_scope), m_is_nested (fh.m_is_nested),
-    m_closure_frames (fh.m_closure_frames),
-    m_local_vars (fh.m_local_vars
-                  ? new octave::stack_frame::local_vars_map (*(fh.m_local_vars)) : nullptr),
-    m_dispatch_class (fh.m_dispatch_class)
-{ }
-
-octave_fcn_handle::~octave_fcn_handle (void)
-{
-  delete m_local_vars;
-}
-
-octave_value_list
-octave_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 '.':
-      {
-        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)
+  : octave_base_value (fh)
 {
-  // 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)
-        {
-          // 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)
-                    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::tree_evaluator& tw = interp.get_evaluator ();
-
-  octave::unwind_action act2 ([&tw] () { tw.set_dispatch_class (""); });
-
-  tw.set_dispatch_class (m_dispatch_class);
-
-  if (m_closure_frames)
-    {
-      if (! fcn_to_call.is_user_function ())
-        {
-          std::string tname = fcn_to_call.type_name ();
-          error ("internal error: closure frames associated with '%s' object",
-                 tname.c_str ());
-        }
-
-      octave_user_function *oct_usr_fcn = fcn_to_call.user_function_value ();
-
-      tw.push_stack_frame (oct_usr_fcn, m_closure_frames);
-
-      octave::unwind_action act1 ([&tw] () { tw.pop_stack_frame (); });
-
-      return oct_usr_fcn->execute (tw, nargout, args);
-    }
-  else if (m_local_vars)
-    {
-      if (! fcn_to_call.is_user_function ())
-        {
-          std::string tname = fcn_to_call.type_name ();
-          error ("internal error: local vars associated with '%s' object",
-                 tname.c_str ());
-        }
-
-      octave_user_function *oct_usr_fcn = fcn_to_call.user_function_value ();
-
-      tw.push_stack_frame (oct_usr_fcn, *m_local_vars);
-
-      octave::unwind_action act1 ([&tw] () { tw.pop_stack_frame (); });
-
-      return oct_usr_fcn->execute (tw, nargout, args);
-    }
-  else
-    {
-      octave_function *oct_fcn = fcn_to_call.function_value ();
-
-      return oct_fcn->call (tw, nargout, args);
-    }
+  m_rep = fh.m_rep->clone ();
 }
 
 dim_vector
@@ -467,273 +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)
-{
-  m_closure_frames = tw.get_current_stack_frame ();
-}
-
-octave_value
-octave_fcn_handle::workspace (void) const
-{
-  if (m_name == anonymous)
-    {
-      octave_scalar_map ws;
-
-      if (m_local_vars)
-        {
-          for (const auto& nm_val : *m_local_vars)
-            ws.assign (nm_val.first, nm_val.second);
-        }
-
-      return ws;
-    }
-  else if (m_closure_frames)
-    {
-      return m_closure_frames->workspace ();
-    }
-
-  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;
-
-      os << m_name << "\n";
-
-      print_raw (os, true);
-      os << "\n";
-
-      size_t varlen = m_local_vars ? m_local_vars->size () : 0;
-
-      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 ();
-            }
-        }
-    }
-  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;
-
-          if (fh->m_local_vars)
-            m_local_vars = new octave::stack_frame::local_vars_map (*(fh->m_local_vars));
-
-          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 ())
     {
@@ -741,138 +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;
-
-      size_t varlen = m_local_vars ? m_local_vars->size () : 0;
-
-      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 : *m_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))
@@ -885,355 +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);
-
-      size_t varlen = m_local_vars ? m_local_vars->size () : 0;
-
-      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);
-        }
-    }
-  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)
@@ -1242,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)
     {
@@ -1253,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)
     {
@@ -1265,7 +2766,7 @@
       return false;
     }
 
-  slen = H5Tget_size (type_hid);
+  int slen = H5Tget_size (type_hid);
   if (slen < 0)
     {
       H5Sclose (space_hid);
@@ -1278,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,
@@ -1294,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
 }
 
@@ -1650,57 +2986,39 @@
 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
@@ -1772,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, ,
@@ -1850,7 +3113,7 @@
 
   std::string fh_nm = fh->fcn_name ();
 
-  if (fh_nm == octave_fcn_handle::anonymous)
+  if (fh->is_anonymous ())
     {
       std::ostringstream buf;
 
@@ -1888,7 +3151,7 @@
 
   if (nm[0] == '@')
     {
-      // Unlike the anon_fcn_handle::parse method, don't set up
+      // 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
       //
--- a/libinterp/octave-value/ov-fcn-handle.h	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.h	Wed Apr 29 14:10:27 2020 -0400
@@ -32,6 +32,7 @@
 #include <list>
 #include <string>
 
+#include "oct-map.h"
 #include "ov-base.h"
 #include "ov-fcn.h"
 #include "ov-typeinfo.h"
@@ -42,9 +43,118 @@
 {
   class interpreter;
   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;
+    }
 
-// Function handles.
+    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;
+  };
+}
 
 class
 OCTINTERP_API
@@ -54,42 +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 (), m_local_vars (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);
 
-  // OCTAVE_DEPRECATED (6, "foobar-1")
+  // Create a handle to a built-in or internal function.
+  octave_fcn_handle (const octave_value& fcn);
+
+  // 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);
 
-  // OCTAVE_DEPRECATED (6, "foobar-2")
-  octave_fcn_handle (const octave::symbol_scope& scope,
-                     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_DEPRECATED (6, "foobar-3")
-  octave_fcn_handle (const octave::symbol_scope& scope,
-                     const octave_value& f, const std::string& name);
+  // 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);
 
-  // OCTAVE_DEPRECATED (6, "foobar-4")
-  octave_fcn_handle (const octave_value& f, const std::string& name);
-
-  // OCTAVE_DEPRECATED (6, "foobar-5")
+  // 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_DEPRECATED (6, "foobar-6")
+  // 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);
+
+  // 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_DEPRECATED (6, "foobar-7")
   octave_fcn_handle (const octave_fcn_handle& fh);
 
-  ~octave_fcn_handle (void);
+  ~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
@@ -105,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);
 
@@ -161,57 +313,24 @@
   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::shared_ptr<octave::stack_frame> m_closure_frames;
-
-  // List of captured variable values for anonymous fucntions.
-  octave::stack_frame::local_vars_map *m_local_vars;
-
-  // 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
 {
--- a/libinterp/octave-value/ov-fcn.h	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/octave-value/ov-fcn.h	Wed Apr 29 14:10:27 2020 -0400
@@ -104,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
--- a/libinterp/operators/op-fcn.cc	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/operators/op-fcn.cc	Wed Apr 29 14:10:27 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/pt-eval.cc	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Wed Apr 29 14:10:27 2020 -0400
@@ -1003,28 +1003,207 @@
     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 != name)
-      return octave_value (new octave_fcn_handle (fcn_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 ();
 
-    // FCN_HANDLE: CONTEXT DEPENDENT
-    octave_fcn_handle *fh = new octave_fcn_handle (curr_scope, name);
-
-    std::string dispatch_class;
-
-    if (is_class_method_executing (dispatch_class)
-        || is_class_constructor_executing (dispatch_class))
-      fh->set_dispatch_class (dispatch_class);
-
-    return octave_value (fh);
+    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));
   }
 
 /*
@@ -2665,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;
   }
 
--- a/libinterp/parse-tree/pt-fcn-handle.cc	Wed Jun 10 17:01:31 2020 -0400
+++ b/libinterp/parse-tree/pt-fcn-handle.cc	Wed Apr 29 14:10:27 2020 -0400
@@ -63,7 +63,6 @@
   octave_value
   tree_fcn_handle::evaluate (tree_evaluator& tw, int)
   {
-    // XXX FCN_HANDLE: CONTEXT DEPENDENT
     return tw.make_fcn_handle (m_name);
   }
 
@@ -176,8 +175,6 @@
 
     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, local_vars));
   }
 }