changeset 28426:9a3deb17b4ea stable

use shared_ptr for stack frames in call stack and for accesss and static links * call-stack.h, call-stack.cc: Use std::shared_ptr<stack_frame> instead of bare pointer for elements of call stack. Change all uses. * stack-frame.h, stack-frame.cc (stack_frame::static_link, stack_frame::access_link): Use std::shared_ptr<stack_frame> instead of bare pointer. Change all uses. (stack_frame::workspace): New function. * ov-fcn-handle.h, ov-fcn-handle.cc (octave_fcn_handle::m_closure_frames): Now a std::shared_ptr<stack_frame> object instead of a list of copied stack frames. Change all uses. (octave_fcn_handle::push_closure_context): Simply store std::shared_ptr to current stack frame. (octave_fcn_handle::workspace): Call stack_frame::workspace to get workspace info when we have closure frames.
author John W. Eaton <jwe@octave.org>
date Sat, 25 Apr 2020 13:17:11 -0400
parents a5be4fc661d6
children 7a8c69c4eb55
files libinterp/corefcn/call-stack.cc libinterp/corefcn/call-stack.h libinterp/corefcn/stack-frame.cc libinterp/corefcn/stack-frame.h libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-fcn-handle.h libinterp/octave-value/ov-fcn.cc libinterp/octave-value/ov-fcn.h libinterp/octave-value/ov-usr-fcn.cc libinterp/octave-value/ov-usr-fcn.h libinterp/parse-tree/oct-lvalue.cc libinterp/parse-tree/oct-lvalue.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h libinterp/parse-tree/pt-fcn-handle.cc
diffstat 15 files changed, 276 insertions(+), 267 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/call-stack.cc	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/corefcn/call-stack.cc	Sat Apr 25 13:17:11 2020 -0400
@@ -94,7 +94,7 @@
 
     if (! m_cs.empty ())
       {
-        const stack_frame *elt = m_cs[m_curr_frame];
+        const std::shared_ptr<stack_frame> elt = m_cs[m_curr_frame];
         retval = elt->line ();
       }
 
@@ -107,7 +107,7 @@
 
     if (! m_cs.empty ())
       {
-        const stack_frame *elt = m_cs[m_curr_frame];
+        const std::shared_ptr<stack_frame> elt = m_cs[m_curr_frame];
         retval = elt->column ();
       }
 
@@ -122,7 +122,7 @@
 
     if (xframe > 0)
       {
-        const stack_frame *elt = m_cs[xframe];
+        const std::shared_ptr<stack_frame> elt = m_cs[xframe];
 
         octave_function *f = elt->function ();
 
@@ -141,7 +141,7 @@
 
     if (xframe > 0)
       {
-        const stack_frame *elt = m_cs[xframe];
+        const std::shared_ptr<stack_frame> elt = m_cs[xframe];
 
         octave_function *f = elt->function ();
 
@@ -165,7 +165,7 @@
 
     if (xframe > 0)
       {
-        const stack_frame *elt = m_cs[xframe];
+        const std::shared_ptr<stack_frame> elt = m_cs[xframe];
 
         octave_function *f = elt->function ();
 
@@ -189,7 +189,7 @@
 
     if (xframe > 0)
       {
-        const stack_frame *elt = m_cs[xframe];
+        const std::shared_ptr<stack_frame> elt = m_cs[xframe];
 
         octave_function *f = elt->function ();
 
@@ -212,7 +212,7 @@
 
     while (i != 0)
       {
-        const stack_frame *elt = m_cs[i--];
+        const std::shared_ptr<stack_frame> elt = m_cs[i--];
 
         octave_function *f = elt->function ();
 
@@ -238,7 +238,7 @@
 
     while (i != 0)
       {
-        const stack_frame *elt = m_cs[i--];
+        const std::shared_ptr<stack_frame> elt = m_cs[i--];
 
         octave_function *f = elt->function ();
 
@@ -268,7 +268,7 @@
 
     while (i != 0)
       {
-        const stack_frame *elt = m_cs[i--];
+        const std::shared_ptr<stack_frame> elt = m_cs[i--];
 
         octave_function *f = elt->function ();
 
@@ -331,7 +331,7 @@
 
     while (p != m_cs.cbegin ())
       {
-        const stack_frame *elt = *(--p);
+        const std::shared_ptr<stack_frame> elt = *(--p);
 
         octave_function *f = elt->function ();
 
@@ -345,11 +345,12 @@
     return retval;
   }
 
-  stack_frame * call_stack::get_static_link (size_t prev_frame) const
+  std::shared_ptr<stack_frame>
+  call_stack::get_static_link (size_t prev_frame) const
   {
     // FIXME: is there a better way?
 
-    stack_frame *slink = nullptr;
+    std::shared_ptr<stack_frame> slink;
 
     if (m_curr_frame > 0)
       {
@@ -373,16 +374,16 @@
     if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    stack_frame *slink = get_static_link (prev_frame);
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
-    stack_frame *new_frame
-      = stack_frame::create (m_evaluator, scope, m_curr_frame, slink);
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, scope, m_curr_frame, slink));
 
     m_cs.push_back (new_frame);
   }
 
   void call_stack::push (octave_user_function *fcn, unwind_protect *up_frame,
-                         stack_frame *closure_frames)
+                         const std::shared_ptr<stack_frame>& closure_frames)
   {
     size_t prev_frame = m_curr_frame;
     m_curr_frame = m_cs.size ();
@@ -391,11 +392,11 @@
     if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    stack_frame *slink = get_static_link (prev_frame);
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
-    stack_frame *new_frame
-      = stack_frame::create (m_evaluator, fcn, up_frame, m_curr_frame,
-                             slink, closure_frames);
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, fcn, up_frame, m_curr_frame,
+                                      slink, closure_frames));
 
     m_cs.push_back (new_frame);
   }
@@ -409,11 +410,11 @@
     if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    stack_frame *slink = get_static_link (prev_frame);
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
-    stack_frame *new_frame
-      = stack_frame::create (m_evaluator, script, up_frame, m_curr_frame,
-                             slink);
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, script, up_frame,
+                                      m_curr_frame, slink));
 
     m_cs.push_back (new_frame);
   }
@@ -427,10 +428,10 @@
     if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    stack_frame *slink = get_static_link (prev_frame);
+    std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
-    stack_frame *new_frame
-      = stack_frame::create (m_evaluator, fcn, m_curr_frame, slink);
+    std::shared_ptr<stack_frame>
+      new_frame (stack_frame::create (m_evaluator, fcn, m_curr_frame, slink));
 
     m_cs.push_back (new_frame);
   }
@@ -447,7 +448,7 @@
 
         if (verbose)
           {
-            const stack_frame *elt = m_cs[n];
+            const std::shared_ptr<stack_frame> elt = m_cs[n];
 
             elt->display_stopped_in_message (octave_stdout);
           }
@@ -460,7 +461,7 @@
   {
     size_t user_frame = m_curr_frame;
 
-    stack_frame *frm = m_cs[user_frame];
+    std::shared_ptr<stack_frame> frm = m_cs[user_frame];
 
     if (! (frm->is_user_fcn_frame () || frm->is_user_script_frame ()
            || frm->is_scope_frame ()))
@@ -473,7 +474,7 @@
     return user_frame;
   }
 
-  stack_frame *call_stack::current_user_frame (void) const
+  std::shared_ptr<stack_frame> call_stack::current_user_frame (void) const
   {
     size_t frame = find_current_user_frame ();
 
@@ -499,7 +500,7 @@
         return start;
       }
 
-    stack_frame *frm = m_cs[start];
+    std::shared_ptr<stack_frame> frm = m_cs[start];
 
     if (! (frm && (frm->is_user_fcn_frame ()
                    || frm->is_user_script_frame ()
@@ -603,10 +604,10 @@
       m_curr_frame = 0;
   }
 
-  std::list<stack_frame *>
+  std::list<std::shared_ptr<stack_frame>>
   call_stack::backtrace_frames (octave_idx_type& curr_user_frame) const
   {
-    std::list<stack_frame *> frames;
+    std::list<std::shared_ptr<stack_frame>> frames;
 
     // curr_frame is the index to the current frame in the overall call
     // stack, which includes any compiled function frames and scope
@@ -619,7 +620,7 @@
 
     for (size_t n = m_cs.size () - 1; n > 0; n--)
       {
-        stack_frame *frm = m_cs[n];
+        std::shared_ptr<stack_frame> frm = m_cs[n];
 
         if (frm->is_user_script_frame () || frm->is_user_fcn_frame ()
             || frm->is_scope_frame ())
@@ -637,7 +638,7 @@
     return frames;
   }
 
-  std::list<stack_frame *>
+  std::list<std::shared_ptr<stack_frame>>
   call_stack::backtrace_frames (void) const
   {
     octave_idx_type curr_user_frame = -1;
@@ -649,11 +650,12 @@
   call_stack::backtrace_info (octave_idx_type& curr_user_frame,
                               bool print_subfn) const
   {
-    std::list<stack_frame *> frames = backtrace_frames (curr_user_frame);
+    std::list<std::shared_ptr<stack_frame>> frames
+      = backtrace_frames (curr_user_frame);
 
     std::list<frame_info> retval;
 
-    for (const auto *frm : frames)
+    for (const auto& frm : frames)
       {
         if (frm->is_user_script_frame () || frm->is_user_fcn_frame ()
             || frm->is_scope_frame ())
@@ -677,7 +679,8 @@
   octave_map call_stack::backtrace (octave_idx_type& curr_user_frame,
                                     bool print_subfn) const
   {
-    std::list<stack_frame *> frames = backtrace_frames (curr_user_frame);
+    std::list<std::shared_ptr<stack_frame>> frames
+      = backtrace_frames (curr_user_frame);
 
     size_t nframes = frames.size ();
 
@@ -690,7 +693,7 @@
 
     octave_idx_type k = 0;
 
-    for (const auto *frm : frames)
+    for (const auto& frm : frames)
       {
         if (frm->is_user_script_frame () || frm->is_user_fcn_frame ()
             || frm->is_scope_frame ())
@@ -726,15 +729,13 @@
 
     if (m_cs.size () > 1)
       {
-        stack_frame *elt = m_cs.back ();
+        std::shared_ptr<stack_frame> elt = m_cs.back ();
 
-        stack_frame *caller = elt->static_link ();
+        std::shared_ptr<stack_frame> caller = elt->static_link ();
 
         m_curr_frame = caller->index ();
 
         m_cs.pop_back ();
-
-        delete elt;
       }
   }
 
--- a/libinterp/corefcn/call-stack.h	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/corefcn/call-stack.h	Sat Apr 25 13:17:11 2020 -0400
@@ -29,6 +29,7 @@
 #include "octave-config.h"
 
 #include <deque>
+#include <memory>
 #include <string>
 
 class octave_function;
@@ -55,7 +56,7 @@
   {
   public:
 
-    typedef std::deque<stack_frame *> stack_frames;
+    typedef std::deque<std::shared_ptr<stack_frame>> stack_frames;
 
     typedef stack_frames::iterator iterator;
     typedef stack_frames::const_iterator const_iterator;
@@ -87,14 +88,9 @@
 
     size_t size (void) const { return m_cs.size (); }
 
-    const stack_frame& get_current_stack_frame (void) const
+    std::shared_ptr<stack_frame> get_current_stack_frame (void) const
     {
-      return *(m_cs[m_curr_frame]);
-    }
-
-    stack_frame& get_current_stack_frame (void)
-    {
-      return *(m_cs[m_curr_frame]);
+      return m_cs[m_curr_frame];
     }
 
     symbol_scope top_scope (void) const
@@ -121,10 +117,7 @@
       octave_function *retval = nullptr;
 
       if (m_cs.size () > n)
-        {
-          stack_frame *elt = m_cs[n];
-          retval = elt->function ();
-        }
+        retval = m_cs[n]->function ();
 
       return retval;
     }
@@ -160,12 +153,12 @@
     // Return TRUE if all elements on the call stack are scripts.
     bool all_scripts (void) const;
 
-    stack_frame * get_static_link (size_t prev_frame) const;
+    std::shared_ptr<stack_frame> get_static_link (size_t prev_frame) const;
 
     void push (const symbol_scope& scope);
 
     void push (octave_user_function *fcn, unwind_protect *up_frame,
-               stack_frame *closure_frames = nullptr);
+               const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
     void push (octave_user_script *script, unwind_protect *up_frame);
 
@@ -175,7 +168,7 @@
     {
       if (! m_cs.empty ())
         {
-          stack_frame *elt = m_cs.back ();
+          std::shared_ptr<stack_frame> elt = m_cs.back ();
 
           elt->line (l);
           elt->column (c);
@@ -186,7 +179,7 @@
     {
       if (! m_cs.empty ())
         {
-          stack_frame *elt = m_cs.back ();
+          std::shared_ptr<stack_frame> elt = m_cs.back ();
 
           elt->line (l);
         }
@@ -196,7 +189,7 @@
     {
       if (! m_cs.empty ())
         {
-          stack_frame *elt = m_cs.back ();
+          std::shared_ptr<stack_frame> elt = m_cs.back ();
 
           elt->column (c);
         }
@@ -210,7 +203,8 @@
     }
 
     size_t find_current_user_frame (void) const;
-    stack_frame *current_user_frame (void) const;
+
+    std::shared_ptr<stack_frame> current_user_frame (void) const;
 
     size_t dbupdown (size_t start, int n, bool verbose);
     size_t dbupdown (int n = -1, bool verbose = false);
@@ -219,12 +213,12 @@
 
     void goto_base_frame (void);
 
-    std::list<stack_frame *>
+    std::list<std::shared_ptr<stack_frame>>
     backtrace_frames (octave_idx_type& curr_user_frame) const;
 
     // List of raw stack frames.
 
-    std::list<stack_frame *> backtrace_frames (void) const;
+    std::list<std::shared_ptr<stack_frame>> backtrace_frames (void) const;
 
     // List of stack_info objects that can be used in liboctave and
     // stored in the execution_exception object.
--- a/libinterp/corefcn/stack-frame.cc	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/corefcn/stack-frame.cc	Sat Apr 25 13:17:11 2020 -0400
@@ -56,7 +56,8 @@
     compiled_fcn_stack_frame (void) = delete;
 
     compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn,
-                              size_t index, stack_frame *static_link)
+                              size_t index,
+                              const std::shared_ptr<stack_frame>& static_link)
       : stack_frame (tw, index, static_link, static_link->access_link ()),
         m_fcn (fcn)
     { }
@@ -177,7 +178,7 @@
 
     script_stack_frame (tree_evaluator& tw, octave_user_script *script,
                         unwind_protect *up_frame, size_t index,
-                        stack_frame *static_link);
+                        const std::shared_ptr<stack_frame>& static_link);
 
     script_stack_frame (const script_stack_frame& elt) = default;
 
@@ -189,7 +190,8 @@
 
     bool is_user_script_frame (void) const { return true; }
 
-    static stack_frame * get_access_link (stack_frame *static_link);
+    static std::shared_ptr<stack_frame>
+    get_access_link (const std::shared_ptr<stack_frame>& static_link);
 
     static size_t get_num_symbols (octave_user_script *script);
 
@@ -287,8 +289,9 @@
     base_value_stack_frame (void) = delete;
 
     base_value_stack_frame (tree_evaluator& tw, size_t num_symbols,
-                            size_t index, stack_frame *static_link,
-                            stack_frame *access_link)
+                            size_t index,
+                            const std::shared_ptr<stack_frame>& static_link,
+                            const std::shared_ptr<stack_frame>& access_link)
       : stack_frame (tw, index, static_link, access_link),
         m_values (num_symbols, octave_value ()),
         m_flags (num_symbols, LOCAL),
@@ -392,8 +395,8 @@
 
     user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
                           unwind_protect *up_frame, size_t index,
-                          stack_frame *static_link,
-                          stack_frame *access_link = nullptr)
+                          const std::shared_ptr<stack_frame>& static_link,
+                          const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ())
       : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link,
                                 (access_link
                                  ? access_link
@@ -412,8 +415,9 @@
 
     bool is_user_fcn_frame (void) const { return true; }
 
-    static stack_frame *
-    get_access_link (octave_user_function *fcn, stack_frame *static_link);
+    static std::shared_ptr<stack_frame>
+    get_access_link (octave_user_function *fcn,
+                     const std::shared_ptr<stack_frame>& static_link);
 
     static size_t get_num_symbols (octave_user_function *fcn)
     {
@@ -478,7 +482,8 @@
     scope_stack_frame (void) = delete;
 
     scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope,
-                       size_t index, stack_frame *static_link)
+                       size_t index,
+                       const std::shared_ptr<stack_frame>& static_link)
       : base_value_stack_frame (tw, scope.num_symbols (), index,
                                 static_link, nullptr),
         m_scope (scope)
@@ -617,7 +622,7 @@
       // link for a compiled_fcn_stack_frame be the same as the static
       // link?
 
-      stack_frame *slink = frame.static_link ();
+      std::shared_ptr<stack_frame> slink = frame.static_link ();
 
       if (slink)
         slink->accept (*this);
@@ -625,7 +630,7 @@
 
     void visit_script_stack_frame (script_stack_frame& frame)
     {
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -635,7 +640,7 @@
     {
       clean_frame (frame);
 
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -645,7 +650,7 @@
     {
       clean_frame (frame);
 
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -859,7 +864,7 @@
       // link for a compiled_fcn_stack_frame be the same as the static
       // link?
 
-      stack_frame *slink = frame.static_link ();
+      std::shared_ptr<stack_frame> slink = frame.static_link ();
 
       if (slink)
         slink->accept (*this);
@@ -867,7 +872,7 @@
 
     void visit_script_stack_frame (script_stack_frame& frame)
     {
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -877,7 +882,7 @@
     {
       append_list (frame);
 
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -887,7 +892,7 @@
     {
       append_list (frame);
 
-      stack_frame *alink = frame.access_link ();
+      std::shared_ptr<stack_frame> alink = frame.access_link ();
 
       if (alink)
         alink->accept (*this);
@@ -1008,7 +1013,8 @@
   };
 
   stack_frame * stack_frame::create (tree_evaluator& tw, octave_function *fcn,
-                                     size_t index, stack_frame *static_link)
+                                     size_t index,
+                                     const std::shared_ptr<stack_frame>& static_link)
   {
     return new compiled_fcn_stack_frame (tw, fcn, index, static_link);
   }
@@ -1016,7 +1022,7 @@
   stack_frame * stack_frame::create (tree_evaluator& tw,
                                      octave_user_script *script,
                                      unwind_protect *up_frame, size_t index,
-                                     stack_frame *static_link)
+                                     const std::shared_ptr<stack_frame>& static_link)
   {
     return new script_stack_frame (tw, script, up_frame, index, static_link);
   }
@@ -1024,8 +1030,8 @@
   stack_frame * stack_frame::create (tree_evaluator& tw,
                                      octave_user_function *fcn,
                                      unwind_protect *up_frame, size_t index,
-                                     stack_frame *static_link,
-                                     stack_frame *access_link)
+                                     const std::shared_ptr<stack_frame>& static_link,
+                                     const std::shared_ptr<stack_frame>& access_link)
   {
     return new user_fcn_stack_frame (tw, fcn, up_frame, index, static_link,
                                      access_link);
@@ -1033,7 +1039,7 @@
 
   stack_frame * stack_frame::create (tree_evaluator& tw,
                                      const symbol_scope& scope, size_t index,
-                                     stack_frame *static_link)
+                                     const std::shared_ptr<stack_frame>& static_link)
   {
     return new scope_stack_frame (tw, scope, index, static_link);
   }
@@ -1120,6 +1126,41 @@
     return sia.symbol_info ();
   }
 
+  octave_value stack_frame::workspace (void)
+  {
+    std::list<octave_scalar_map> ws_list;
+
+    stack_frame *frame = this;
+
+    while (frame)
+      {
+        octave::symbol_info_list symbols = frame->all_variables ();
+
+        octave_scalar_map ws;
+
+        for (const auto& sym_name : symbols.names ())
+          {
+            octave_value val = symbols.varval (sym_name);
+
+            if (val.is_defined ())
+              ws.assign (sym_name, val);
+          }
+
+        ws_list.push_back (ws);
+
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
+
+    Cell ws_frames (ws_list.size (), 1);
+
+    octave_idx_type i = 0;
+    for (const auto& elt : ws_list)
+      ws_frames(i++) = elt;
+
+    return ws_frames;
+  }
+
   // FIXME: Should this function also find any variables in parent
   // scopes accessible through access_links?
 
@@ -1333,14 +1374,14 @@
 
     os << "static link: ";
     if (m_static_link)
-      os << m_static_link;
+      os << m_static_link.get ();
     else
       os << "NULL";
     os << std::endl;
 
     os << "access link: ";
     if (m_access_link)
-      os << m_access_link;
+      os << m_access_link.get ();
     else
       os << "NULL";
     os << std::endl;
@@ -1355,7 +1396,7 @@
       return;
 
     os << "FOLLOWING ACCESS LINKS:" << std::endl;
-    const stack_frame *frm = access_link ();
+    std::shared_ptr<stack_frame> frm = access_link ();
     while (frm)
       {
         frm->display (false);
@@ -1391,7 +1432,7 @@
                                           octave_user_script *script,
                                           unwind_protect *up_frame,
                                           size_t index,
-                                          stack_frame *static_link)
+                                          const std::shared_ptr<stack_frame>& static_link)
     : stack_frame (tw, index, static_link, get_access_link (static_link)),
       m_script (script), m_unwind_protect_frame (up_frame),
       m_lexical_frame_offsets (get_num_symbols (script), 1),
@@ -1542,15 +1583,13 @@
   // If this is a nested scope, set access_link to nearest parent
   // stack frame that corresponds to the lexical parent of this scope.
 
-  stack_frame *
-  script_stack_frame::get_access_link (stack_frame *static_link)
+  std::shared_ptr<stack_frame>
+  script_stack_frame::get_access_link (const std::shared_ptr<stack_frame>& static_link)
   {
-    stack_frame *alink = nullptr;
-
     // If this script is called from another script, set access
     // link to ultimate parent stack frame.
 
-    alink = static_link;
+    std::shared_ptr<stack_frame> alink = static_link;
 
     while (alink->is_user_script_frame ())
       {
@@ -1817,7 +1856,10 @@
     const stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (! frame)
       error ("internal error: invalid access link in function call stack");
@@ -1844,7 +1886,10 @@
     const stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (! frame)
       error ("internal error: invalid access link in function call stack");
@@ -1883,7 +1928,10 @@
     stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (data_offset >= frame->size ())
       frame->resize (data_offset+1);
@@ -1920,7 +1968,7 @@
     if (frame_offset > 1)
       error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used");
 
-    stack_frame *frame = access_link ();
+    std::shared_ptr<stack_frame> frame = access_link ();
 
     if (data_offset >= frame->size ())
       frame->resize (data_offset+1);
@@ -1988,11 +2036,11 @@
   // If this is a nested scope, set access_link to nearest parent
   // stack frame that corresponds to the lexical parent of this scope.
 
-  stack_frame *
+  std::shared_ptr<stack_frame>
   user_fcn_stack_frame::get_access_link (octave_user_function *fcn,
-                                         stack_frame *static_link)
+                                         const std::shared_ptr<stack_frame>& static_link)
   {
-    stack_frame *alink = nullptr;
+    std::shared_ptr<stack_frame> alink;
 
     symbol_scope fcn_scope = fcn->scope ();
 
@@ -2083,7 +2131,8 @@
         if (sym)
           return sym;
 
-        frame = frame->access_link ();
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
       }
 
     return symbol_record ();
@@ -2139,7 +2188,10 @@
     const stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (! frame)
       error ("internal error: invalid access link in function call stack");
@@ -2161,7 +2213,10 @@
     const stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (! frame)
       error ("internal error: invalid access link in function call stack");
@@ -2199,7 +2254,10 @@
     stack_frame *frame = this;
 
     for (size_t i = 0; i < frame_offset; i++)
-      frame = frame->access_link ();
+      {
+        std::shared_ptr<stack_frame> nxt = frame->access_link ();
+        frame = nxt.get ();
+      }
 
     if (data_offset >= frame->size ())
       frame->resize (data_offset+1);
--- a/libinterp/corefcn/stack-frame.h	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/corefcn/stack-frame.h	Sat Apr 25 13:17:11 2020 -0400
@@ -32,6 +32,7 @@
 #include <iosfwd>
 #include <list>
 #include <map>
+#include <memory>
 #include <string>
 
 class octave_value;
@@ -139,7 +140,8 @@
     stack_frame (void) = delete;
 
     stack_frame (tree_evaluator& tw, size_t index,
-                 stack_frame *static_link, stack_frame *access_link)
+                 const std::shared_ptr<stack_frame>& static_link,
+                 const std::shared_ptr<stack_frame>& access_link)
       : m_evaluator (tw), m_line (-1), m_column (-1), m_index (index),
         m_static_link (static_link), m_access_link (access_link),
         m_dispatch_class ()
@@ -148,24 +150,25 @@
     // Compiled function.
     static stack_frame *
     create (tree_evaluator& tw, octave_function *fcn, size_t index,
-            stack_frame *static_link);
+            const std::shared_ptr<stack_frame>& static_link);
 
     // Script.
     static stack_frame *
     create (tree_evaluator& tw, octave_user_script *script,
             unwind_protect *up_frame, size_t index,
-            stack_frame *static_link);
+            const std::shared_ptr<stack_frame>& static_link);
 
     // User-defined function.
     static stack_frame *
     create (tree_evaluator& tw, octave_user_function *fcn,
             unwind_protect *up_frame, size_t index,
-            stack_frame *static_link, stack_frame *access_link = nullptr);
+            const std::shared_ptr<stack_frame>& static_link,
+            const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ());
 
     // Scope.
     static stack_frame *
     create (tree_evaluator& tw, const symbol_scope& scope, size_t index,
-            stack_frame *static_link);
+            const std::shared_ptr<stack_frame>& static_link);
 
     stack_frame (const stack_frame& elt) = default;
 
@@ -242,6 +245,8 @@
 
     symbol_info_list all_variables (void);
 
+    octave_value workspace (void);
+
     std::list<std::string> variable_names (void) const;
 
     // Look for named symbol visible from current scope.  Don't
@@ -293,11 +298,13 @@
       mark_global (sym);
     }
 
-    stack_frame * static_link (void) const {return m_static_link; }
+    std::shared_ptr<stack_frame>
+    static_link (void) const {return m_static_link; }
 
-    stack_frame * access_link (void) const {return m_access_link; }
+    std::shared_ptr<stack_frame>
+    access_link (void) const {return m_access_link; }
 
-    void set_closure_links (stack_frame *dup_frame)
+    void set_closure_links (const std::shared_ptr<stack_frame>& dup_frame)
     {
       m_static_link = dup_frame;
       m_access_link = dup_frame;
@@ -556,12 +563,12 @@
 
     // Pointer to the nearest parent frame that contains variable
     // information (script, function, or scope).
-    stack_frame *m_static_link;
+    std::shared_ptr<stack_frame> m_static_link;
 
     // Pointer to the nearest lexical parent frame.  Used to access
     // non-local variables for nested and anonymous functions or as a
     // link to the parent frame in which a script is executed.
-    stack_frame *m_access_link;
+    std::shared_ptr<stack_frame> m_access_link;
 
     // Allow function handles to temporarily store their dispatch class
     // in the call stack.
--- a/libinterp/octave-value/ov-fcn-handle.cc	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.cc	Sat Apr 25 13:17:11 2020 -0400
@@ -85,7 +85,7 @@
 octave_fcn_handle::octave_fcn_handle (const octave::symbol_scope& scope,
                                       const std::string& n)
   : m_fcn (), m_obj (), m_name (n), m_scope (scope), m_is_nested (false),
-    m_closure_frames (nullptr), m_dispatch_class ()
+    m_closure_frames (), m_dispatch_class ()
 {
   if (! m_name.empty () && m_name[0] == '@')
     m_name = m_name.substr (1);
@@ -119,7 +119,7 @@
                                       const octave_value& f,
                                       const std::string& n)
   : m_fcn (f), m_obj (), m_name (n), m_scope (scope), m_is_nested (false),
-    m_closure_frames (nullptr), m_dispatch_class ()
+    m_closure_frames (), m_dispatch_class ()
 {
   octave_user_function *uf = m_fcn.user_function_value (true);
 
@@ -138,7 +138,7 @@
 octave_fcn_handle::octave_fcn_handle (const octave_value& f,
                                       const std::string& n)
   : m_fcn (f), m_obj (), m_name (n), m_scope (), m_is_nested (false),
-    m_closure_frames (nullptr), m_dispatch_class ()
+    m_closure_frames (), m_dispatch_class ()
 {
   octave_user_function *uf = m_fcn.user_function_value (true);
 
@@ -154,23 +154,6 @@
     m_is_nested = true;
 }
 
-octave_fcn_handle::~octave_fcn_handle (void)
-{
-  if (m_closure_frames)
-    {
-      while (m_closure_frames->size () > 0)
-        {
-          octave::stack_frame *elt = m_closure_frames->back ();
-
-          delete elt;
-
-          m_closure_frames->pop_back ();
-        }
-
-      delete m_closure_frames;
-    }
-}
-
 octave_value_list
 octave_fcn_handle::subsref (const std::string& type,
                             const std::list<octave_value_list>& idx,
@@ -375,11 +358,6 @@
   if (! fcn_to_call.is_defined ())
     err_invalid_fcn_handle (m_name);
 
-  octave::stack_frame *closure_context = nullptr;
-
-  if (m_closure_frames && m_closure_frames->size () > 0)
-    closure_context = m_closure_frames->front ();
-
   octave::tree_evaluator& tw = interp.get_evaluator ();
 
   octave_function *of = fcn_to_call.function_value ();
@@ -391,7 +369,7 @@
 
   tw.set_dispatch_class (m_dispatch_class);
 
-  return of->call (tw, nargout, args, closure_context);
+  return of->call (tw, nargout, args, m_closure_frames);
 }
 
 dim_vector
@@ -440,26 +418,7 @@
 void
 octave_fcn_handle::push_closure_context (octave::tree_evaluator& tw)
 {
-  if (! m_closure_frames)
-    m_closure_frames = new std::list<octave::stack_frame *> ();
-
-  octave::stack_frame& curr_frame = tw.get_current_stack_frame ();
-
-  octave::stack_frame *dup_frame = curr_frame.dup ();
-
-  if (! m_closure_frames->empty ())
-    {
-      octave::stack_frame *top_frame = m_closure_frames->back ();
-
-      // Arrange for static and access links in the top stack frame (the
-      // last one saved before this one) to point to the new duplicated
-      // frame.  This way we will look up through the duplicated frames
-      // when evaluating the function.
-
-      top_frame->set_closure_links (dup_frame);
-    }
-
-  m_closure_frames->push_back (dup_frame);
+  m_closure_frames = tw.get_current_stack_frame ();
 }
 
 octave_value
@@ -481,30 +440,7 @@
     }
   else if (m_closure_frames)
     {
-      octave_idx_type num_frames = m_closure_frames->size ();
-
-      Cell ws_frames (num_frames, 1);
-
-      octave_idx_type i = 0;
-
-      for (auto elt : *m_closure_frames)
-        {
-          octave::symbol_info_list symbols = elt->all_variables ();
-
-          octave_scalar_map ws;
-
-          for (auto sym_name : symbols.names ())
-            {
-              octave_value val = symbols.varval (sym_name);
-
-              if (val.is_defined ())
-                ws.assign (sym_name, val);
-            }
-
-          ws_frames(i++) = ws;
-        }
-
-      return ws_frames;
+      return m_closure_frames->workspace ();
     }
 
   return Cell ();
--- a/libinterp/octave-value/ov-fcn-handle.h	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.h	Sat Apr 25 13:17:11 2020 -0400
@@ -56,7 +56,7 @@
 
   octave_fcn_handle (void)
     : m_fcn (), m_obj (), m_name (), m_scope (), m_is_nested (false),
-      m_closure_frames (nullptr), m_dispatch_class ()
+      m_closure_frames (), m_dispatch_class ()
   { }
 
   octave_fcn_handle (const octave::symbol_scope& scope, const std::string& n);
@@ -70,7 +70,7 @@
 
   octave_fcn_handle (const octave_fcn_handle& fh) = default;
 
-  ~octave_fcn_handle (void);
+  ~octave_fcn_handle (void) = default;
 
   octave_base_value * clone (void) const
   { return new octave_fcn_handle (*this); }
@@ -184,7 +184,7 @@
   // Saved stack frames for handles to nested functions.  This allows us
   // to access non-locals and other context info when calling nested
   // functions indirectly through handles.
-  std::list<octave::stack_frame *> *m_closure_frames;
+  std::shared_ptr<octave::stack_frame> m_closure_frames;
 
   // The name of the class in which this handle was created, if any.
   // Used to determine access permission when the referenced function is
--- a/libinterp/octave-value/ov-fcn.cc	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/octave-value/ov-fcn.cc	Sat Apr 25 13:17:11 2020 -0400
@@ -47,7 +47,7 @@
 octave_value_list
 octave_function::call (octave::tree_evaluator& tw, int nargout,
                        const octave_value_list& args,
-                       octave::stack_frame *closure_context)
+                       const std::shared_ptr<octave::stack_frame>& closure_context)
 {
   if (closure_context)
     panic_impossible ();
--- a/libinterp/octave-value/ov-fcn.h	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/octave-value/ov-fcn.h	Sat Apr 25 13:17:11 2020 -0400
@@ -230,7 +230,7 @@
   virtual octave_value_list
   call (octave::tree_evaluator& tw, int nargout,
         const octave_value_list& args,
-        octave::stack_frame *closure_context);
+        const std::shared_ptr<octave::stack_frame>& closure_context);
 
 protected:
 
--- a/libinterp/octave-value/ov-usr-fcn.cc	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.cc	Sat Apr 25 13:17:11 2020 -0400
@@ -462,7 +462,7 @@
 octave_value_list
 octave_user_function::call (octave::tree_evaluator& tw, int nargout,
                             const octave_value_list& args,
-                            octave::stack_frame *closure_frames)
+                            const std::shared_ptr<octave::stack_frame>& closure_frames)
 {
   return tw.execute_user_function (*this, nargout, args, closure_frames);
 }
--- a/libinterp/octave-value/ov-usr-fcn.h	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.h	Sat Apr 25 13:17:11 2020 -0400
@@ -381,7 +381,8 @@
 
   octave_value_list
   call (octave::tree_evaluator& tw, int nargout,
-        const octave_value_list& args, octave::stack_frame *);
+        const octave_value_list& args,
+        const std::shared_ptr<octave::stack_frame>&);
 
   octave::tree_parameter_list * parameter_list (void) { return param_list; }
 
--- a/libinterp/parse-tree/oct-lvalue.cc	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/parse-tree/oct-lvalue.cc	Sat Apr 25 13:17:11 2020 -0400
@@ -36,7 +36,7 @@
 {
   bool octave_lvalue::is_defined (void) const
   {
-    return ! is_black_hole () && m_frame.is_defined (m_sym);
+    return ! is_black_hole () && m_frame->is_defined (m_sym);
   }
 
   bool octave_lvalue::is_undefined (void) const
@@ -46,14 +46,14 @@
 
   void octave_lvalue::define (const octave_value& v)
   {
-    m_frame.assign (m_sym, v);
+    m_frame->assign (m_sym, v);
   }
 
   void octave_lvalue::assign (octave_value::assign_op op,
                               const octave_value& rhs)
   {
     if (! is_black_hole ())
-      m_frame.assign (op, m_sym, m_type, m_idx, rhs);
+      m_frame->assign (op, m_sym, m_type, m_idx, rhs);
   }
 
   void octave_lvalue::set_index (const std::string& t,
@@ -97,12 +97,12 @@
   void octave_lvalue::do_unary_op (octave_value::unary_op op)
   {
     if (! is_black_hole ())
-      m_frame.do_non_const_unary_op (op, m_sym, m_type, m_idx);
+      m_frame->do_non_const_unary_op (op, m_sym, m_type, m_idx);
   }
 
   octave_value octave_lvalue::value (void) const
   {
     return (is_black_hole ()
-            ? octave_value () : m_frame.value (m_sym, m_type, m_idx));
+            ? octave_value () : m_frame->value (m_sym, m_type, m_idx));
   }
 }
--- a/libinterp/parse-tree/oct-lvalue.h	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/parse-tree/oct-lvalue.h	Sat Apr 25 13:17:11 2020 -0400
@@ -40,7 +40,8 @@
   {
   public:
 
-    octave_lvalue (const symbol_record& sr, stack_frame& frame)
+    octave_lvalue (const symbol_record& sr,
+                   const std::shared_ptr<stack_frame>& frame)
       : m_sym (sr), m_frame (frame), m_black_hole (false),
         m_type (), m_idx (), m_nel (1)
     { }
@@ -87,7 +88,7 @@
 
     symbol_record m_sym;
 
-    stack_frame& m_frame;
+    std::shared_ptr<stack_frame> m_frame;
 
     bool m_black_hole;
 
--- a/libinterp/parse-tree/pt-eval.cc	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Sat Apr 25 13:17:11 2020 -0400
@@ -187,7 +187,7 @@
 
             if (! silent)
               {
-                stack_frame *frm = tw.current_user_frame ();
+                std::shared_ptr<stack_frame> frm = tw.current_user_frame ();
 
                 frm->display_stopped_in_message (buf);
               }
@@ -894,17 +894,19 @@
   bool
   tree_evaluator::is_variable (const std::string& name) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_variable (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_variable (name);
   }
 
   bool
   tree_evaluator::is_local_variable (const std::string& name) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_local_variable (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_local_variable (name);
   }
 
   bool
@@ -941,49 +943,55 @@
   bool
   tree_evaluator::is_variable (const symbol_record& sym) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_variable (sym);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_variable (sym);
   }
 
   bool
   tree_evaluator::is_defined (const symbol_record& sym) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_defined (sym);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_defined (sym);
   }
 
   bool tree_evaluator::is_global (const std::string& name) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.is_global (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->is_global (name);
   }
 
   octave_value
   tree_evaluator::varval (const symbol_record& sym) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.varval (sym);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->varval (sym);
   }
 
   octave_value
   tree_evaluator::varval (const std::string& name) const
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.varval (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->varval (name);
   }
 
   void tree_evaluator::install_variable (const std::string& name,
                                          const octave_value& value,
                                          bool global)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    return frame.install_variable (name, value, global);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    return frame->install_variable (name, value, global);
   }
 
   octave_value
@@ -1021,9 +1029,10 @@
   void
   tree_evaluator::assign (const std::string& name, const octave_value& val)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.assign (name, val);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->assign (name, val);
   }
 
   void
@@ -1521,7 +1530,7 @@
 
   void tree_evaluator::push_stack_frame (octave_user_function *fcn,
                                          unwind_protect *up_frame,
-                                         stack_frame *closure_frames)
+                                         const std::shared_ptr<stack_frame>& closure_frames)
   {
     m_call_stack.push (fcn, up_frame, closure_frames);
   }
@@ -1564,7 +1573,7 @@
 
   void tree_evaluator::debug_where (std::ostream& os) const
   {
-    stack_frame *frm = m_call_stack.current_user_frame ();
+    std::shared_ptr<stack_frame> frm = m_call_stack.current_user_frame ();
 
     frm->display_stopped_in_message (os);
   }
@@ -1636,13 +1645,13 @@
     return m_call_stack.is_class_constructor_executing (dclass);
   }
 
-  std::list<stack_frame *>
+  std::list<std::shared_ptr<stack_frame>>
   tree_evaluator::backtrace_frames (octave_idx_type& curr_user_frame) const
   {
     return m_call_stack.backtrace_frames (curr_user_frame);
   }
 
-  std::list<stack_frame *>
+  std::list<std::shared_ptr<stack_frame>>
   tree_evaluator::backtrace_frames (void) const
   {
     return m_call_stack.backtrace_frames ();
@@ -1782,9 +1791,10 @@
 
   octave_value tree_evaluator::find (const std::string& name)
   {
-    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    octave_value val = frame.varval (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    octave_value val = frame->varval (name);
 
     if (val.is_defined ())
       return val;
@@ -1793,7 +1803,7 @@
     // subfunctions if we are currently executing a function defined
     // from a .m file.
 
-    octave_value fcn = frame.find_subfunction (name);
+    octave_value fcn = frame->find_subfunction (name);
 
     if (fcn.is_defined ())
       return fcn;
@@ -1805,37 +1815,42 @@
 
   void tree_evaluator::clear_objects (void)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_objects ();
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_objects ();
   }
 
   void tree_evaluator::clear_variable (const std::string& name)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_variable (name);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_variable (name);
   }
 
   void tree_evaluator::clear_variable_pattern (const std::string& pattern)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_variable_pattern (pattern);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_variable_pattern (pattern);
   }
 
   void tree_evaluator::clear_variable_regexp (const std::string& pattern)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_variable_regexp (pattern);
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_variable_regexp (pattern);
   }
 
   void tree_evaluator::clear_variables (void)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    frame.clear_variables ();
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
+
+    frame->clear_variables ();
   }
 
   void tree_evaluator::clear_global_variable (const std::string& name)
@@ -2358,7 +2373,7 @@
   tree_evaluator::execute_user_function (octave_user_function& user_function,
                                          int nargout,
                                          const octave_value_list& xargs,
-                                         stack_frame *closure_frames)
+                                         const std::shared_ptr<stack_frame>& closure_frames)
   {
     octave_value_list retval;
 
@@ -3849,13 +3864,14 @@
 
   void tree_evaluator::init_local_fcn_vars (octave_user_function& user_fcn)
   {
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+    std::shared_ptr<stack_frame> frame
+      = m_call_stack.get_current_stack_frame ();
 
     const octave_user_function::local_vars_map& lviv
       = user_fcn.local_var_init_vals ();
 
     for (const auto& nm_ov : lviv)
-      frame.assign (nm_ov.first, nm_ov.second);
+      frame->assign (nm_ov.first, nm_ov.second);
   }
 
   std::string
--- a/libinterp/parse-tree/pt-eval.h	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/parse-tree/pt-eval.h	Sat Apr 25 13:17:11 2020 -0400
@@ -212,7 +212,7 @@
     octave_value_list
     execute_user_function (octave_user_function& user_function,
                            int nargout, const octave_value_list& args,
-                           stack_frame *closure_frames = nullptr);
+                           const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
     void visit_octave_user_function_header (octave_user_function&);
 
@@ -378,7 +378,7 @@
 
     void push_stack_frame (octave_user_function *fcn,
                            unwind_protect *up_frame,
-                           stack_frame *closure_frames = nullptr);
+                           const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
     void push_stack_frame (octave_user_script *script,
                            unwind_protect *up_frame);
@@ -387,17 +387,12 @@
 
     void pop_stack_frame (void);
 
-    const stack_frame& get_current_stack_frame (void) const
+    std::shared_ptr<stack_frame> get_current_stack_frame (void) const
     {
       return m_call_stack.get_current_stack_frame ();
     }
 
-    stack_frame& get_current_stack_frame (void)
-    {
-      return m_call_stack.get_current_stack_frame ();
-    }
-
-    stack_frame * current_user_frame (void) const
+    std::shared_ptr<stack_frame> current_user_frame (void) const
     {
       return m_call_stack.current_user_frame ();
     }
@@ -443,10 +438,10 @@
 
     bool is_class_constructor_executing (std::string& dispatch_class) const;
 
-    std::list<stack_frame *>
+    std::list<std::shared_ptr<stack_frame>>
     backtrace_frames (octave_idx_type& curr_user_frame) const;
 
-    std::list<stack_frame *> backtrace_frames () const;
+    std::list<std::shared_ptr<stack_frame>> backtrace_frames () const;
 
     std::list<frame_info> backtrace_info (octave_idx_type& curr_user_frame,
                                           bool print_subfn = true) const;
--- a/libinterp/parse-tree/pt-fcn-handle.cc	Fri Apr 24 14:21:07 2020 -0400
+++ b/libinterp/parse-tree/pt-fcn-handle.cc	Sat Apr 25 13:17:11 2020 -0400
@@ -134,11 +134,11 @@
 
     call_stack& cs = tw.get_call_stack ();
 
-    stack_frame& frame = cs.get_current_stack_frame ();
+    std::shared_ptr<stack_frame> frame = cs.get_current_stack_frame ();
 
     for (auto& name : free_vars)
       {
-        octave_value val = frame.varval (name);
+        octave_value val = frame->varval (name);
 
         if (val.is_defined ())
           local_var_init_vals[name] = val;