changeset 28429:8eb8ba8aff9a stable

refactor octave_function call method This change is a step toward keeping stack frames for closures (handles to nested functions) separate from the functions themselves. * call-stack.h, call-stack.cc (call_stack::curr_fcn_unwind_protect_frame: No longer const. Update all uses. (call_stack::push): Dliminate unwind protect frame argument. Change all uses. * stack-frame.h, stack-frame.cc (script_stack_frame, user_fcn_stack_frame): Create uniwnd_protect_frame only if needed. * ov-builtin.h, ov-builtin.cc (octave_builtin::execute): New function that executes function without pushing stack frame. (octave_builtin::call): Delete. * ov-class.h, ov-class.cc (octave_inline_fcn::execute): New function. (octave_inline_fcn::call): Call execute. * ov-classdef.h, ov-classdef.cc (octave_classdef_meta::execute): New function. (octave_classdef_meta::call): Call execute. (octave_classdef_superclass_ref::execute): New function. (octave_classdef_superclass_ref::call): Call execute. * ov-fcn-handle.cc (octave_fcn_handle::call): When calling a function that has closure_frames, push stack frame here instead of inside octave_user_function object. * ov-fcn.h (octave_function::execute): New pure virtual function. * ov-fcn.h, ov-fcn.cc (octave_function::call): Define only one variant, without closure frames as an argument. Default version simply pushes stack frame and calls execute. * ov-mex-fcn.h, ov-mex-fcn.cc (octave_mex_function::execute): Rename from call. Don't push stack frame here. * ov-usr-fcn.h, ov-usr-fcn.cc (octave_user_script::call, octave_user_function::call): Push stack frame and call execute. (octave_user_script::execute, octave_user_function::execute): New function. * pt-eval.h, pt-eval.cc (tree_evaluator::push_stack_frame): Eliminate unwind protect frame argument. Change all uses. (tree_evaluator::execute_user_script): Update. (tree_evaluator::execute_user_function): Update. Don't push stack frame here.
author John W. Eaton <jwe@octave.org>
date Mon, 30 Mar 2020 10:43:47 -0400
parents e9a12be5fd79
children 5bfa8e018704
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-builtin.cc libinterp/octave-value/ov-builtin.h libinterp/octave-value/ov-class.cc libinterp/octave-value/ov-classdef.cc libinterp/octave-value/ov-classdef.h libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-fcn.cc libinterp/octave-value/ov-fcn.h libinterp/octave-value/ov-mex-fcn.cc libinterp/octave-value/ov-mex-fcn.h libinterp/octave-value/ov-usr-fcn.cc libinterp/octave-value/ov-usr-fcn.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h
diffstat 18 files changed, 207 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/call-stack.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/corefcn/call-stack.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -181,7 +181,7 @@
     return -1;
   }
 
-  unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void) const
+  unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void)
   {
     // Start at current frame.
 
@@ -382,7 +382,7 @@
     m_cs.push_back (new_frame);
   }
 
-  void call_stack::push (octave_user_function *fcn, unwind_protect *up_frame,
+  void call_stack::push (octave_user_function *fcn,
                          const std::shared_ptr<stack_frame>& closure_frames)
   {
     size_t prev_frame = m_curr_frame;
@@ -395,13 +395,13 @@
     std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
     std::shared_ptr<stack_frame>
-      new_frame (stack_frame::create (m_evaluator, fcn, up_frame, m_curr_frame,
-                                      slink, closure_frames));
+      new_frame (stack_frame::create (m_evaluator, fcn, m_curr_frame, slink,
+                                      closure_frames));
 
     m_cs.push_back (new_frame);
   }
 
-  void call_stack::push (octave_user_script *script, unwind_protect *up_frame)
+  void call_stack::push (octave_user_script *script)
   {
     size_t prev_frame = m_curr_frame;
     m_curr_frame = m_cs.size ();
@@ -413,8 +413,8 @@
     std::shared_ptr<stack_frame> slink = get_static_link (prev_frame);
 
     std::shared_ptr<stack_frame>
-      new_frame (stack_frame::create (m_evaluator, script, up_frame,
-                                      m_curr_frame, slink));
+      new_frame (stack_frame::create (m_evaluator, script, m_curr_frame,
+                                      slink));
 
     m_cs.push_back (new_frame);
   }
--- a/libinterp/corefcn/call-stack.h	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/corefcn/call-stack.h	Mon Mar 30 10:43:47 2020 -0400
@@ -125,7 +125,7 @@
     // User code caller.
     octave_user_code * current_user_code (void) const;
 
-    unwind_protect * curr_fcn_unwind_protect_frame (void) const;
+    unwind_protect * curr_fcn_unwind_protect_frame (void);
 
     // Line in user code caller.
     int current_user_code_line (void) const;
@@ -157,10 +157,10 @@
 
     void push (const symbol_scope& scope);
 
-    void push (octave_user_function *fcn, unwind_protect *up_frame,
+    void push (octave_user_function *fcn,
                const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
-    void push (octave_user_script *script, unwind_protect *up_frame);
+    void push (octave_user_script *script);
 
     void push (octave_function *fcn);
 
--- a/libinterp/corefcn/stack-frame.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/corefcn/stack-frame.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -177,14 +177,17 @@
     script_stack_frame (void) = delete;
 
     script_stack_frame (tree_evaluator& tw, octave_user_script *script,
-                        unwind_protect *up_frame, size_t index,
+                        size_t index,
                         const std::shared_ptr<stack_frame>& static_link);
 
     script_stack_frame (const script_stack_frame& elt) = default;
 
     script_stack_frame& operator = (const script_stack_frame& elt) = delete;
 
-    ~script_stack_frame (void) = default;
+    ~script_stack_frame (void)
+    {
+      delete m_unwind_protect_frame;
+    }
 
     script_stack_frame * dup (void) const;
 
@@ -206,8 +209,7 @@
 
     octave_function * function (void) const { return m_script; }
 
-    unwind_protect *
-    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
+    unwind_protect * unwind_protect_frame (void);
 
     symbol_record lookup_symbol (const std::string& name) const;
 
@@ -394,14 +396,14 @@
     user_fcn_stack_frame (void) = delete;
 
     user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
-                          unwind_protect *up_frame, size_t index,
+                          size_t index,
                           const std::shared_ptr<stack_frame>& static_link,
                           const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ())
       : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link,
                                 (access_link
                                  ? access_link
                                  : get_access_link (fcn, static_link))),
-        m_fcn (fcn), m_unwind_protect_frame (up_frame)
+        m_fcn (fcn), m_unwind_protect_frame (nullptr)
     { }
 
     user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default;
@@ -409,7 +411,10 @@
     user_fcn_stack_frame&
     operator = (const user_fcn_stack_frame& elt) = delete;
 
-    ~user_fcn_stack_frame (void) = default;
+    ~user_fcn_stack_frame (void)
+    {
+      delete m_unwind_protect_frame;
+    }
 
     user_fcn_stack_frame * dup (void) const;
 
@@ -432,8 +437,7 @@
 
     octave_function * function (void) const { return m_fcn; }
 
-    unwind_protect *
-    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
+    unwind_protect * unwind_protect_frame (void);
 
     symbol_record lookup_symbol (const std::string& name) const;
 
@@ -1021,20 +1025,18 @@
 
   stack_frame * stack_frame::create (tree_evaluator& tw,
                                      octave_user_script *script,
-                                     unwind_protect *up_frame, size_t index,
+                                     size_t index,
                                      const std::shared_ptr<stack_frame>& static_link)
   {
-    return new script_stack_frame (tw, script, up_frame, index, static_link);
+    return new script_stack_frame (tw, script, index, static_link);
   }
 
   stack_frame * stack_frame::create (tree_evaluator& tw,
-                                     octave_user_function *fcn,
-                                     unwind_protect *up_frame, size_t index,
+                                     octave_user_function *fcn, size_t index,
                                      const std::shared_ptr<stack_frame>& static_link,
                                      const std::shared_ptr<stack_frame>& access_link)
   {
-    return new user_fcn_stack_frame (tw, fcn, up_frame, index, static_link,
-                                     access_link);
+    return new user_fcn_stack_frame (tw, fcn, index, static_link, access_link);
   }
 
   stack_frame * stack_frame::create (tree_evaluator& tw,
@@ -1430,11 +1432,10 @@
 
   script_stack_frame::script_stack_frame (tree_evaluator& tw,
                                           octave_user_script *script,
-                                          unwind_protect *up_frame,
                                           size_t index,
                                           const std::shared_ptr<stack_frame>& static_link)
     : stack_frame (tw, index, static_link, get_access_link (static_link)),
-      m_script (script), m_unwind_protect_frame (up_frame),
+      m_script (script), m_unwind_protect_frame (nullptr),
       m_lexical_frame_offsets (get_num_symbols (script), 1),
       m_value_offsets (get_num_symbols (script), 0)
   {
@@ -1602,6 +1603,14 @@
     return alink;
   }
 
+  unwind_protect * script_stack_frame::unwind_protect_frame (void)
+  {
+    if (! m_unwind_protect_frame)
+      m_unwind_protect_frame = new unwind_protect ();
+
+    return m_unwind_protect_frame;
+  }
+
   symbol_record script_stack_frame::lookup_symbol (const std::string& name) const
   {
     symbol_scope scope = get_scope ();
@@ -2118,6 +2127,14 @@
       }
   }
 
+  unwind_protect * user_fcn_stack_frame::unwind_protect_frame (void)
+  {
+    if (! m_unwind_protect_frame)
+      m_unwind_protect_frame = new unwind_protect ();
+
+    return m_unwind_protect_frame;
+  }
+
   symbol_record user_fcn_stack_frame::lookup_symbol (const std::string& name) const
   {
     const stack_frame *frame = this;
--- a/libinterp/corefcn/stack-frame.h	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/corefcn/stack-frame.h	Mon Mar 30 10:43:47 2020 -0400
@@ -154,14 +154,12 @@
 
     // Script.
     static stack_frame *
-    create (tree_evaluator& tw, octave_user_script *script,
-            unwind_protect *up_frame, size_t index,
+    create (tree_evaluator& tw, octave_user_script *script, size_t index,
             const std::shared_ptr<stack_frame>& static_link);
 
     // User-defined function.
     static stack_frame *
-    create (tree_evaluator& tw, octave_user_function *fcn,
-            unwind_protect *up_frame, size_t index,
+    create (tree_evaluator& tw, octave_user_function *fcn, size_t index,
             const std::shared_ptr<stack_frame>& static_link,
             const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ());
 
@@ -232,8 +230,7 @@
 
     virtual octave_function * function (void) const { return nullptr; }
 
-    virtual unwind_protect *
-    unwind_protect_frame (void) const { return nullptr; }
+    virtual unwind_protect * unwind_protect_frame (void) { return nullptr; }
 
     symbol_info_list
     make_symbol_info_list (const std::list<symbol_record>& symrec_list) const;
--- a/libinterp/octave-value/ov-builtin.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-builtin.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -43,20 +43,14 @@
                                      "built-in function");
 
 octave_value_list
-octave_builtin::call (octave::tree_evaluator& tw, int nargout,
-                      const octave_value_list& args)
+octave_builtin::execute (octave::tree_evaluator& tw, int nargout,
+                         const octave_value_list& args)
 {
   octave_value_list retval;
 
   if (args.has_magic_colon ())
     error ("invalid use of colon in function argument list");
 
-  octave::unwind_protect frame;
-
-  tw.push_stack_frame (this);
-
-  frame.add_method (tw, &octave::tree_evaluator::pop_stack_frame);
-
   octave::profiler& profiler = tw.get_profiler ();
 
   octave::profiler::enter<octave_builtin> block (profiler, *this);
--- a/libinterp/octave-value/ov-builtin.h	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-builtin.h	Mon Mar 30 10:43:47 2020 -0400
@@ -96,14 +96,9 @@
 
   bool is_builtin_function (void) const { return true; }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
-
   octave_value_list
-  call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ());
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
 
   octave::jit_type * to_jit (void) const;
 
--- a/libinterp/octave-value/ov-class.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-class.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -2065,14 +2065,19 @@
 
   ~octave_inline_fcn (void) = default;
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
+  // Override default call method because we ultimately use feval to
+  // execute the inline function and that will push a stack frame.
 
   octave_value_list
   call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ());
+        const octave_value_list& args = octave_value_list ())
+  {
+    return execute (tw, nargout, args);
+  }
+
+  octave_value_list
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
 
 private:
 
@@ -2121,8 +2126,8 @@
 };
 
 octave_value_list
-octave_inline_fcn::call (octave::tree_evaluator& tw, int nargout,
-                         const octave_value_list& args)
+octave_inline_fcn::execute (octave::tree_evaluator& tw, int nargout,
+                            const octave_value_list& args)
 {
   octave::interpreter& interp = tw.get_interpreter ();
 
--- a/libinterp/octave-value/ov-classdef.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-classdef.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -410,8 +410,9 @@
 }
 
 octave_value_list
-octave_classdef_superclass_ref::call (octave::tree_evaluator& tw,
-                                      int nargout, const octave_value_list& idx)
+octave_classdef_superclass_ref::execute (octave::tree_evaluator& tw,
+                                         int nargout,
+                                         const octave_value_list& idx)
 {
   octave_value_list retval;
 
--- a/libinterp/octave-value/ov-classdef.h	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-classdef.h	Mon Mar 30 10:43:47 2020 -0400
@@ -195,14 +195,18 @@
     return object.meta_subsref (type, idx, nargout);
   }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
+  // Override default call method because we don't push a new stack
+  // frame for this operation on classdef_meta objects.
 
-  octave_value_list call (octave::tree_evaluator&, int nargout,
+  octave_value_list call (octave::tree_evaluator& tw, int nargout,
                           const octave_value_list& args)
   {
+    return execute (tw, nargout, args);
+  }
+
+  octave_value_list execute (octave::tree_evaluator&, int nargout,
+                             const octave_value_list& args)
+  {
     // Emulate ()-type meta subsref
 
     std::list<octave_value_list> idx (1, args);
@@ -241,13 +245,17 @@
 
   octave_function * function_value (bool = false) { return this; }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
+  // Override default call method because we don't push a new stack
+  // frame for this operation on classdef_superclass_ref objects.
 
-  octave_value_list
-  call (octave::tree_evaluator& tw, int nargout, const octave_value_list& idx);
+  octave_value_list call (octave::tree_evaluator& tw, int nargout,
+                          const octave_value_list& args)
+  {
+    return execute (tw, nargout, args);
+  }
+
+  octave_value_list execute (octave::tree_evaluator& tw, int nargout,
+                             const octave_value_list& idx);
 
 private:
 
--- a/libinterp/octave-value/ov-fcn-handle.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -360,16 +360,33 @@
 
   octave::tree_evaluator& tw = interp.get_evaluator ();
 
-  octave_function *of = fcn_to_call.function_value ();
-
-  octave::unwind_protect frame;
-
-  frame.add_method (tw, &octave::tree_evaluator::set_dispatch_class,
-                    std::string ());
+  octave::unwind_action act2 ([&tw] () { tw.set_dispatch_class (""); });
 
   tw.set_dispatch_class (m_dispatch_class);
 
-  return of->call (tw, nargout, args, m_closure_frames);
+  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
+    {
+      octave_function *oct_fcn = fcn_to_call.function_value ();
+
+      return oct_fcn->call (tw, nargout, args);
+    }
 }
 
 dim_vector
--- a/libinterp/octave-value/ov-fcn.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-fcn.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -27,10 +27,12 @@
 #  include "config.h"
 #endif
 
+#include "unwind-prot.h"
+
 #include "error.h"
 #include "ovl.h"
 #include "ov-fcn.h"
-
+#include "pt-eval.h"
 
 octave_base_value *
 octave_function::clone (void) const
@@ -46,11 +48,11 @@
 
 octave_value_list
 octave_function::call (octave::tree_evaluator& tw, int nargout,
-                       const octave_value_list& args,
-                       const std::shared_ptr<octave::stack_frame>& closure_context)
+                       const octave_value_list& args)
 {
-  if (closure_context)
-    panic_impossible ();
+  tw.push_stack_frame (this);
 
-  return call (tw, nargout, args);
+  octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+  return execute (tw, nargout, args);
 }
--- a/libinterp/octave-value/ov-fcn.h	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-fcn.h	Mon Mar 30 10:43:47 2020 -0400
@@ -223,14 +223,16 @@
   virtual bool accepts_postfix_index (char type) const
   { return (type == '('); }
 
+  // Push new stack frame (if necessary) and execute function.
   virtual octave_value_list
   call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ()) = 0;
+        const octave_value_list& args = octave_value_list ());
 
+  // Execute function without pushing new stack frame (assumes that has
+  // already been done).
   virtual octave_value_list
-  call (octave::tree_evaluator& tw, int nargout,
-        const octave_value_list& args,
-        const std::shared_ptr<octave::stack_frame>& closure_context);
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ()) = 0;
 
 protected:
 
--- a/libinterp/octave-value/ov-mex-fcn.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-mex-fcn.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -90,20 +90,14 @@
           int nargout);
 
 octave_value_list
-octave_mex_function::call (octave::tree_evaluator& tw, int nargout,
-                           const octave_value_list& args)
+octave_mex_function::execute (octave::tree_evaluator& tw, int nargout,
+                              const octave_value_list& args)
 {
   octave_value_list retval;
 
   if (args.has_magic_colon ())
     error ("invalid use of colon in function argument list");
 
-  octave::unwind_protect frame;
-
-  tw.push_stack_frame (this);
-
-  frame.add_method (tw, &octave::tree_evaluator::pop_stack_frame);
-
   octave::profiler& profiler = tw.get_profiler ();
 
   octave::profiler::enter<octave_mex_function> block (profiler, *this);
--- a/libinterp/octave-value/ov-mex-fcn.h	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-mex-fcn.h	Mon Mar 30 10:43:47 2020 -0400
@@ -88,14 +88,9 @@
 
   bool is_mex_function (void) const { return true; }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_function::call;
-
   octave_value_list
-  call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ());
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
 
   void atexit (void (*fcn) (void)) { m_exit_fcn_ptr = fcn; }
 
--- a/libinterp/octave-value/ov-usr-fcn.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -173,10 +173,26 @@
     : octave_user_code (fnm, nm, scope, nullptr, ds)
 { }
 
+// We must overload the call method so that we call the proper
+// push_stack_frame method, which is overloaded for pointers to
+// octave_function, octave_user_function, and octave_user_script
+// objects.
+
 octave_value_list
 octave_user_script::call (octave::tree_evaluator& tw, int nargout,
                           const octave_value_list& args)
 {
+  tw.push_stack_frame (this);
+
+  octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+  return execute (tw, nargout, args);
+}
+
+octave_value_list
+octave_user_script::execute (octave::tree_evaluator& tw, int nargout,
+                             const octave_value_list& args)
+{
   return tw.execute_user_script (*this, nargout, args);
 }
 
@@ -459,12 +475,27 @@
   return retval;
 }
 
+// We must overload the call method so that we call the proper
+// push_stack_frame method, which is overloaded for pointers to
+// octave_function, octave_user_function, and octave_user_script
+// objects.
+
 octave_value_list
 octave_user_function::call (octave::tree_evaluator& tw, int nargout,
-                            const octave_value_list& args,
-                            const std::shared_ptr<octave::stack_frame>& closure_frames)
+                            const octave_value_list& args)
 {
-  return tw.execute_user_function (*this, nargout, args, closure_frames);
+  tw.push_stack_frame (this);
+
+  octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+  return execute (tw, nargout, args);
+}
+
+octave_value_list
+octave_user_function::execute (octave::tree_evaluator& tw, int nargout,
+                               const octave_value_list& args)
+{
+  return tw.execute_user_function (*this, nargout, args);
 }
 
 void
--- a/libinterp/octave-value/ov-usr-fcn.h	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.h	Mon Mar 30 10:43:47 2020 -0400
@@ -186,15 +186,19 @@
 
   bool is_user_script (void) const { return true; }
 
-  // We don't need to override both forms of the call method.  The using
-  // declaration will avoid warnings about partially-overloaded virtual
-  // functions.
-  using octave_user_code::call;
+  // We must overload the call method so that we call the proper
+  // push_stack_frame method, which is overloaded for pointers to
+  // octave_function, octave_user_function, and octave_user_script
+  // objects.
 
   octave_value_list
   call (octave::tree_evaluator& tw, int nargout = 0,
         const octave_value_list& args = octave_value_list ());
 
+  octave_value_list
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
+
   void accept (octave::tree_walker& tw);
 
 private:
@@ -372,17 +376,18 @@
             ? (cname.empty () ? true : cname == dispatch_class ()) : false);
   }
 
+  // We must overload the call method so that we call the proper
+  // push_stack_frame method, which is overloaded for pointers to
+  // octave_function, octave_user_function, and octave_user_script
+  // objects.
+
   octave_value_list
   call (octave::tree_evaluator& tw, int nargout = 0,
-        const octave_value_list& args = octave_value_list ())
-  {
-    return call (tw, nargout, args, nullptr);
-  }
+        const octave_value_list& args = octave_value_list ());
 
   octave_value_list
-  call (octave::tree_evaluator& tw, int nargout,
-        const octave_value_list& args,
-        const std::shared_ptr<octave::stack_frame>&);
+  execute (octave::tree_evaluator& tw, int nargout = 0,
+           const octave_value_list& args = octave_value_list ());
 
   octave::tree_parameter_list * parameter_list (void) { return param_list; }
 
--- a/libinterp/parse-tree/pt-eval.cc	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Mon Mar 30 10:43:47 2020 -0400
@@ -1529,16 +1529,14 @@
   }
 
   void tree_evaluator::push_stack_frame (octave_user_function *fcn,
-                                         unwind_protect *up_frame,
                                          const std::shared_ptr<stack_frame>& closure_frames)
   {
-    m_call_stack.push (fcn, up_frame, closure_frames);
+    m_call_stack.push (fcn, closure_frames);
   }
 
-  void tree_evaluator::push_stack_frame (octave_user_script *script,
-                                         unwind_protect *up_frame)
+  void tree_evaluator::push_stack_frame (octave_user_script *script)
   {
-    m_call_stack.push (script, up_frame);
+    m_call_stack.push (script);
   }
 
   void tree_evaluator::push_stack_frame (octave_function *fcn)
@@ -1583,7 +1581,7 @@
     return m_call_stack.current_user_code ();
   }
 
-  unwind_protect * tree_evaluator::curr_fcn_unwind_protect_frame (void) const
+  unwind_protect * tree_evaluator::curr_fcn_unwind_protect_frame (void)
   {
     return m_call_stack.curr_fcn_unwind_protect_frame ();
   }
@@ -2330,26 +2328,15 @@
     if (! cmd_list)
       return retval;
 
-    unwind_protect frame;
-
     if (m_call_stack.size () >= static_cast<size_t> (m_max_recursion_depth))
       error ("max_recursion_depth exceeded");
 
-    m_call_stack.push (&user_script, &frame);
-
-    // Set pointer to the current unwind_protect frame to allow
-    // certain builtins register simple cleanup in a very optimized manner.
-    // This is *not* intended as a general-purpose on-cleanup mechanism,
-
-    frame.add_method (m_call_stack, &call_stack::pop);
-
-    frame.protect_var (m_statement_context);
-    m_statement_context = SC_SCRIPT;
+    unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_SCRIPT);
 
     profiler::enter<octave_user_script> block (m_profiler, user_script);
 
     if (echo ())
-      push_echo_state (frame, tree_evaluator::ECHO_SCRIPTS, file_name);
+      push_echo_state (tree_evaluator::ECHO_SCRIPTS, file_name);
 
     cmd_list->accept (*this);
 
@@ -2372,8 +2359,7 @@
   octave_value_list
   tree_evaluator::execute_user_function (octave_user_function& user_function,
                                          int nargout,
-                                         const octave_value_list& xargs,
-                                         const std::shared_ptr<stack_frame>& closure_frames)
+                                         const octave_value_list& xargs)
   {
     octave_value_list retval;
 
@@ -2405,18 +2391,9 @@
       return retval;
 #endif
 
-    unwind_protect frame;
-
     if (m_call_stack.size () >= static_cast<size_t> (m_max_recursion_depth))
       error ("max_recursion_depth exceeded");
 
-    // Save old and set current symbol table context, for
-    // eval_undefined_error().
-
-    m_call_stack.push (&user_function, &frame, closure_frames);
-
-    frame.add_method (m_call_stack, &call_stack::pop);
-
     bind_auto_fcn_vars (xargs.name_tags (), args.length (),
                         nargout, user_function.takes_varargs (),
                         user_function.all_va_args (args));
@@ -2443,37 +2420,23 @@
         define_parameter_list_from_arg_vector (ret_list, ret_args);
       }
 
-    // Force parameter list to be undefined when this function exits.
-    // Doing so decrements the reference counts on the values of local
-    // variables that are also named function parameters.
-
-    //    if (param_list)
-    //      frame.add_method (this, &tree_evaluator::undefine_parameter_list,
-    //                        param_list);
-
-    // Force return list to be undefined when this function exits.
-    // Doing so decrements the reference counts on the values of local
-    // variables that are also named values returned by this function.
-
-    //    if (ret_list)
-    //      frame.add_method (this, &tree_evaluator::undefine_parameter_list,
-    //                        ret_list);
-
-    frame.add_method (&user_function,
-                      &octave_user_function::restore_warning_states);
+    unwind_action act2 ([&user_function] () {
+                          user_function.restore_warning_states ();
+                        });
 
     // Evaluate the commands that make up the function.
 
-    frame.protect_var (m_statement_context);
-    m_statement_context = SC_FUNCTION;
-
-    frame.add_method (m_call_stack, &call_stack::clear_current_frame_values);
+    unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_FUNCTION);
+
+    unwind_action act1 ([this] () {
+                          m_call_stack.clear_current_frame_values ();
+                        });
 
     {
       profiler::enter<octave_user_function> block (m_profiler, user_function);
 
       if (echo ())
-        push_echo_state (frame, tree_evaluator::ECHO_FUNCTIONS,
+        push_echo_state (tree_evaluator::ECHO_FUNCTIONS,
                          user_function.fcn_file_name ());
 
       if (user_function.is_special_expr ())
@@ -3552,13 +3515,17 @@
   }
 
   void
-  tree_evaluator::push_echo_state (unwind_protect& frame, int type,
-                                   const std::string& file_name,
+  tree_evaluator::push_echo_state (int type, const std::string& file_name,
                                    size_t pos)
   {
-    push_echo_state_cleanup (frame);
-
-    set_echo_state (type, file_name, pos);
+    unwind_protect *frame = m_call_stack.curr_fcn_unwind_protect_frame ();
+
+    if (frame)
+      {
+        push_echo_state_cleanup (*frame);
+
+        set_echo_state (type, file_name, pos);
+      }
   }
 
   void
--- a/libinterp/parse-tree/pt-eval.h	Mon Mar 23 15:44:30 2020 -0400
+++ b/libinterp/parse-tree/pt-eval.h	Mon Mar 30 10:43:47 2020 -0400
@@ -211,8 +211,7 @@
 
     octave_value_list
     execute_user_function (octave_user_function& user_function,
-                           int nargout, const octave_value_list& args,
-                           const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
+                           int nargout, const octave_value_list& args);
 
     void visit_octave_user_function_header (octave_user_function&);
 
@@ -377,11 +376,9 @@
     void push_stack_frame (const symbol_scope& scope);
 
     void push_stack_frame (octave_user_function *fcn,
-                           unwind_protect *up_frame,
                            const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
-    void push_stack_frame (octave_user_script *script,
-                           unwind_protect *up_frame);
+    void push_stack_frame (octave_user_script *script);
 
     void push_stack_frame (octave_function *fcn);
 
@@ -413,7 +410,7 @@
 
     octave_user_code * current_user_code (void) const;
 
-    unwind_protect * curr_fcn_unwind_protect_frame (void) const;
+    unwind_protect * curr_fcn_unwind_protect_frame (void);
 
     // Current function that we are debugging.
     octave_user_code * debug_user_code (void) const;
@@ -706,8 +703,8 @@
 
     std::list<octave_lvalue> make_lvalue_list (tree_argument_list *);
 
-    void push_echo_state (unwind_protect& frame, int type,
-                          const std::string& file_name, size_t pos = 1);
+    void push_echo_state (int type, const std::string& file_name,
+                          size_t pos = 1);
 
   private: