Mercurial > octave
changeset 29478:e88444be8468
maint: merge stable to default.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Thu, 01 Apr 2021 03:02:22 -0400 |
parents | 34922160bda9 (current diff) 34d06c73b48d (diff) |
children | b99d87eafd4e |
files | libinterp/corefcn/call-stack.cc libinterp/corefcn/stack-frame.cc libinterp/corefcn/stack-frame.h libinterp/octave-value/cdef-object.cc libinterp/octave-value/cdef-object.h libinterp/octave-value/ov-base.h libinterp/octave-value/ov-cell.cc libinterp/octave-value/ov-cell.h libinterp/octave-value/ov-class.cc libinterp/octave-value/ov-class.h libinterp/octave-value/ov-classdef.h libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-struct.cc libinterp/octave-value/ov-struct.h libinterp/octave-value/ov.cc libinterp/octave-value/ov.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h test/module.mk |
diffstat | 25 files changed, 635 insertions(+), 155 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/call-stack.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/corefcn/call-stack.cc Thu Apr 01 03:02:22 2021 -0400 @@ -412,7 +412,8 @@ } void call_stack::push (octave_user_function *fcn, - const stack_frame::local_vars_map& local_vars) + const stack_frame::local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& closure_frames) { size_t new_frame_idx; std::shared_ptr<stack_frame> parent_link; @@ -422,7 +423,8 @@ std::shared_ptr<stack_frame> new_frame (stack_frame::create (m_evaluator, fcn, new_frame_idx, - parent_link, static_link, local_vars)); + parent_link, static_link, local_vars, + closure_frames)); m_cs.push_back (new_frame); @@ -763,6 +765,9 @@ m_curr_frame = caller->index (); + if (elt->is_closure_context ()) + elt->break_closure_cycles (elt); + m_cs.pop_back (); } }
--- a/libinterp/corefcn/call-stack.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/corefcn/call-stack.h Thu Apr 01 03:02:22 2021 -0400 @@ -159,7 +159,8 @@ const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ()); void push (octave_user_function *fcn, - const stack_frame::local_vars_map& local_vars); + const stack_frame::local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ()); void push (octave_user_script *script);
--- a/libinterp/corefcn/stack-frame.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/corefcn/stack-frame.cc Thu Apr 01 03:02:22 2021 -0400 @@ -412,10 +412,13 @@ size_t index, const std::shared_ptr<stack_frame>& parent_link, const std::shared_ptr<stack_frame>& static_link, - const local_vars_map& local_vars) + const local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ()) : base_value_stack_frame (tw, get_num_symbols (fcn), index, parent_link, static_link, - get_access_link (fcn, static_link)), + (access_link + ? access_link + : get_access_link (fcn, static_link))), m_fcn (fcn), m_unwind_protect_frame (nullptr) { // Initialize local variable values. @@ -477,6 +480,8 @@ void accept (stack_frame_walker& sfw); + void break_closure_cycles (const std::shared_ptr<stack_frame>& frame); + private: // User-defined object associated with this stack frame. Should @@ -1061,9 +1066,10 @@ octave_user_function *fcn, size_t index, const std::shared_ptr<stack_frame>& parent_link, const std::shared_ptr<stack_frame>& static_link, - const local_vars_map& local_vars) + const local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& access_link) { - return new user_fcn_stack_frame (tw, fcn, index, parent_link, static_link, local_vars); + return new user_fcn_stack_frame (tw, fcn, index, parent_link, static_link, local_vars, access_link); } stack_frame * stack_frame::create (tree_evaluator& tw, @@ -2352,6 +2358,15 @@ sfw.visit_user_fcn_stack_frame (*this); } + void user_fcn_stack_frame::break_closure_cycles (const std::shared_ptr<stack_frame>& frame) + { + for (auto& val : m_values) + val.break_closure_cycles (frame); + + if (m_access_link) + m_access_link->break_closure_cycles (frame); + } + 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 Mar 31 21:33:46 2021 +0200 +++ b/libinterp/corefcn/stack-frame.h Thu Apr 01 03:02:22 2021 -0400 @@ -143,7 +143,8 @@ const std::shared_ptr<stack_frame>& parent_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_evaluator (tw), m_is_closure_context (false), + m_line (-1), m_column (-1), m_index (index), m_parent_link (parent_link), m_static_link (static_link), m_access_link (access_link), m_dispatch_class () { } @@ -172,7 +173,8 @@ create (tree_evaluator& tw, octave_user_function *fcn, size_t index, const std::shared_ptr<stack_frame>& parent_link, const std::shared_ptr<stack_frame>& static_link, - const local_vars_map& local_vars); + const local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ()); // Scope. static stack_frame * @@ -550,6 +552,11 @@ virtual void accept (stack_frame_walker& sfw) = 0; + virtual void break_closure_cycles (const std::shared_ptr<stack_frame>&) { } + + void mark_closure_context (void) { m_is_closure_context = true; } + bool is_closure_context (void) const { return m_is_closure_context; } + protected: // Reference to the call stack that contains this frame. Global @@ -557,6 +564,10 @@ // immediate access to them. tree_evaluator& m_evaluator; + // TRUE if this stack frame is saved with a handle to a nested + // function (closure). + bool m_is_closure_context; + // The line and column of the source file where this stack frame // was created. Used to print stack traces. int m_line;
--- a/libinterp/octave-value/cdef-object.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/cdef-object.cc Thu Apr 01 03:02:22 2021 -0400 @@ -499,6 +499,13 @@ } } + void + cdef_object_scalar::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame) + { + for (octave_idx_type i = 0; i < map.nfields (); i++) + map.contents(i).break_closure_cycles (frame); + } + octave_value_list cdef_object_scalar::subsref (const std::string& type, const std::list<octave_value_list>& idx,
--- a/libinterp/octave-value/cdef-object.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/cdef-object.h Thu Apr 01 03:02:22 2021 -0400 @@ -120,6 +120,11 @@ err_invalid_object ("get_property"); } + virtual void break_closure_cycles (const std::shared_ptr<octave::stack_frame>&) + { + err_invalid_object ("break_closure_cycles"); + } + virtual octave_value_list subsref (const std::string&, const std::list<octave_value_list>&, int, size_t&, const cdef_class&, bool) @@ -276,6 +281,11 @@ return rep->get_property (idx, pname); } + void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame) + { + rep->break_closure_cycles (frame); + } + octave_value_list subsref (const std::string& type, const std::list<octave_value_list>& idx, int nargout, size_t& skip, const cdef_class& context, @@ -456,6 +466,8 @@ dim_vector dims (void) const { return dim_vector (1, 1); } + void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame); + void put (const std::string& pname, const octave_value& val) { map.assign (pname, val);
--- a/libinterp/octave-value/ov-base.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-base.h Thu Apr 01 03:02:22 2021 -0400 @@ -32,6 +32,7 @@ #include <iosfwd> #include <list> +#include <memory> #include <string> #include "Range.h" @@ -44,6 +45,7 @@ namespace octave { + class stack_frame; class type_info; // FIXME: This is not ideal, but it avoids including @@ -272,6 +274,8 @@ virtual octave_base_value * unique_clone (void) { return clone (); } + virtual void break_closure_cycles (const std::shared_ptr<octave::stack_frame>&) { } + virtual type_conv_info numeric_conversion_function (void) const { return type_conv_info (); }
--- a/libinterp/octave-value/ov-cell.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-cell.cc Thu Apr 01 03:02:22 2021 -0400 @@ -50,6 +50,7 @@ #include "utils.h" #include "ov-base-mat.h" #include "ov-base-mat.cc" +#include "ov-fcn-handle.h" #include "ov-re-mat.h" #include "ov-scalar.h" #include "pr-output.h" @@ -143,6 +144,12 @@ DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_cell, "cell", "cell"); +void octave_cell::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame) +{ + for (octave_idx_type i = 0; i < matrix.numel (); i++) + matrix(i).break_closure_cycles (frame); +} + octave_value_list octave_cell::subsref (const std::string& type, const std::list<octave_value_list>& idx,
--- a/libinterp/octave-value/ov-cell.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-cell.h Thu Apr 01 03:02:22 2021 -0400 @@ -69,6 +69,8 @@ octave_base_value * clone (void) const { return new octave_cell (*this); } octave_base_value * empty_clone (void) const { return new octave_cell (); } + void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame); + #if 0 octave_base_value * try_narrowing_conversion (void); #endif
--- a/libinterp/octave-value/ov-class.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-class.cc Thu Apr 01 03:02:22 2021 -0400 @@ -266,6 +266,18 @@ error ("%s cannot be indexed with %c", nm.c_str (), t); } +void +octave_class::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame) +{ + for (octave_idx_type j = 0; j < m_map.nfields (); j++) + { + Cell& c = m_map.contents (j); + + for (octave_idx_type i = 0; i < c.numel (); i++) + c(i).break_closure_cycles (frame); + } +} + Cell octave_class::dotref (const octave_value_list& idx) {
--- a/libinterp/octave-value/ov-class.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-class.h Thu Apr 01 03:02:22 2021 -0400 @@ -88,6 +88,8 @@ return new octave_class (octave_map (m_map.keys ()), c_name, m_parent_list); } + void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame); + OCTINTERP_API Cell dotref (const octave_value_list& idx); OCTINTERP_API Matrix size (void);
--- a/libinterp/octave-value/ov-classdef.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-classdef.h Thu Apr 01 03:02:22 2021 -0400 @@ -90,6 +90,11 @@ OCTINTERP_API bool is_instance_of (const std::string& cls_name) const; + void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame) + { + m_object.break_closure_cycles (frame); + } + OCTINTERP_API octave_value_list subsref (const std::string& type, const std::list<octave_value_list>& idx, int nargout);
--- a/libinterp/octave-value/ov-fcn-handle.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-fcn-handle.cc Thu Apr 01 03:02:22 2021 -0400 @@ -312,37 +312,29 @@ std::list<std::string> m_parentage; }; - class nested_fcn_handle : public base_fcn_handle + class base_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_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); - } + base_nested_fcn_handle (const octave_value& fcn, const std::string& name) + : base_fcn_handle (name), m_fcn (fcn) + { } std::string type (void) const { return "nested"; } + using base_fcn_handle::is_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) @@ -357,7 +349,7 @@ octave_value fcn_val (void) { return m_fcn; } - octave_value workspace (void) const; + virtual octave_value workspace (void) const = 0; // Should be const. octave_scalar_map info (void); @@ -378,16 +370,98 @@ 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; + }; + + class nested_fcn_handle : public base_nested_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_nested_fcn_handle (name, file, octaveroot) + { } + + nested_fcn_handle (const octave_value& fcn, const std::string& name, + const std::shared_ptr<stack_frame>& stack_context) + : base_nested_fcn_handle (fcn, name), m_stack_context (stack_context) + { + m_stack_context->mark_closure_context (); + } + + nested_fcn_handle (const nested_fcn_handle&) = default; + + ~nested_fcn_handle (void) = default; + + using base_nested_fcn_handle::is_nested; + + bool is_nested (const std::shared_ptr<stack_frame>& frame) const + { + return frame == m_stack_context; + } + + nested_fcn_handle * clone (void) const + { + return new nested_fcn_handle (*this); + } + + octave_value make_weak_nested_handle (void) const; + + octave_value_list call (int nargout, const octave_value_list& args); + + octave_value workspace (void) const; + + friend bool is_equal_to (const nested_fcn_handle& fh1, + const nested_fcn_handle& fh2); + + std::shared_ptr<stack_frame> stack_context (void) const + { + return m_stack_context; + } + + protected: // Pointer to closure stack frames. - std::shared_ptr<stack_frame> m_closure_frames; + std::shared_ptr<stack_frame> m_stack_context; + }; + + class weak_nested_fcn_handle : public base_nested_fcn_handle + { + public: + + weak_nested_fcn_handle (const nested_fcn_handle& nfh) + : base_nested_fcn_handle (nfh), m_stack_context (nfh.stack_context ()) + { } + + weak_nested_fcn_handle (const weak_nested_fcn_handle&) = default; + + ~weak_nested_fcn_handle (void) = default; + + weak_nested_fcn_handle * clone (void) const + { + return new weak_nested_fcn_handle (*this); + } + + bool is_weak_nested (void) const { return true; } + + octave_value_list call (int nargout, const octave_value_list& args); + + octave_value workspace (void) const; + + friend bool is_equal_to (const weak_nested_fcn_handle& fh1, + const weak_nested_fcn_handle& fh2); + + protected: + + // Pointer to closure stack frames. + std::weak_ptr<stack_frame> m_stack_context; }; class class_simple_fcn_handle : public base_fcn_handle @@ -501,7 +575,7 @@ std::string m_dispatch_class; }; - class anonymous_fcn_handle : public base_fcn_handle + class base_anonymous_fcn_handle : public base_fcn_handle { public: @@ -512,28 +586,23 @@ // tag in the binary file format. See also the save_binary and // load_binary functions. - anonymous_fcn_handle (const std::string& name = "") + base_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); - } + base_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) + { } + + base_anonymous_fcn_handle (const base_anonymous_fcn_handle&) = default; + + ~base_anonymous_fcn_handle (void) = default; 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) @@ -548,7 +617,7 @@ octave_value fcn_val (void) { return m_fcn; } - octave_value workspace (void) const; + virtual octave_value workspace (void) const = 0; // Should be const. octave_scalar_map info (void); @@ -574,9 +643,6 @@ 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. @@ -586,7 +652,87 @@ stack_frame::local_vars_map m_local_vars; }; - const std::string anonymous_fcn_handle::anonymous ("@<anonymous>"); + class anonymous_fcn_handle : public base_anonymous_fcn_handle + { + public: + + using base_anonymous_fcn_handle::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_anonymous_fcn_handle (name), m_stack_context () + { } + + anonymous_fcn_handle (const octave_value& fcn, + const stack_frame::local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& stack_context = std::shared_ptr<stack_frame> ()); + + anonymous_fcn_handle (const anonymous_fcn_handle&) = default; + + ~anonymous_fcn_handle (void) = default; + + anonymous_fcn_handle * clone (void) const + { + return new anonymous_fcn_handle (*this); + } + + octave_value make_weak_anonymous_handle (void) const; + + octave_value_list call (int nargout, const octave_value_list& args); + + octave_value workspace (void) const; + + friend bool is_equal_to (const anonymous_fcn_handle& fh1, + const anonymous_fcn_handle& fh2); + + std::shared_ptr<stack_frame> stack_context (void) const + { + return m_stack_context; + } + + protected: + + // Pointer to closure stack frames. + std::shared_ptr<stack_frame> m_stack_context; + }; + + class weak_anonymous_fcn_handle : public base_anonymous_fcn_handle + { + public: + + using base_anonymous_fcn_handle::anonymous; + + weak_anonymous_fcn_handle (const anonymous_fcn_handle& afh) + : base_anonymous_fcn_handle (afh), m_stack_context (afh.stack_context ()) + { } + + weak_anonymous_fcn_handle (const weak_anonymous_fcn_handle&) = default; + + ~weak_anonymous_fcn_handle (void) = default; + + weak_anonymous_fcn_handle * clone (void) const + { + return new weak_anonymous_fcn_handle (*this); + } + + bool is_weak_anonymous (void) const { return true; } + + octave_value_list call (int nargout, const octave_value_list& args); + + octave_value workspace (void) const; + + friend bool is_equal_to (const weak_anonymous_fcn_handle& fh1, + const weak_anonymous_fcn_handle& fh2); + + protected: + + // Pointer to closure stack frames. + std::weak_ptr<stack_frame> m_stack_context; + }; extern bool is_equal_to (const anonymous_fcn_handle& fh1, const anonymous_fcn_handle& fh2); @@ -597,6 +743,20 @@ name.c_str ()); } + octave_value base_fcn_handle::make_weak_nested_handle (void) const + { + std::string type_str = type (); + error ("invalid conversion from %s handle to weak nestead handle", + type_str.c_str ()); + } + + octave_value base_fcn_handle::make_weak_anonymous_handle (void) const + { + std::string type_str = type (); + error ("invalid conversion from %s handle to weak anonymous handle", + type_str.c_str ()); + } + octave_value_list base_fcn_handle::subsref (const std::string& type, const std::list<octave_value_list>& idx, @@ -1500,32 +1660,7 @@ } } - 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 base_nested_fcn_handle::info (void) { octave_scalar_map m; @@ -1546,7 +1681,7 @@ // 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) + bool base_nested_fcn_handle::save_ascii (std::ostream& os) { unimplemented ("save", "text"); @@ -1555,7 +1690,7 @@ return true; } - bool nested_fcn_handle::load_ascii (std::istream& is) + bool base_nested_fcn_handle::load_ascii (std::istream& is) { unimplemented ("load", "text"); @@ -1564,7 +1699,8 @@ return true; } - bool nested_fcn_handle::save_binary (std::ostream& os, bool save_as_floats) + bool base_nested_fcn_handle::save_binary (std::ostream& os, + bool save_as_floats) { unimplemented ("save", "binary"); @@ -1574,8 +1710,8 @@ return true; } - bool nested_fcn_handle::load_binary (std::istream& is, bool swap, - mach_info::float_format fmt) + bool base_nested_fcn_handle::load_binary (std::istream& is, bool swap, + mach_info::float_format fmt) { unimplemented ("load", "binary"); @@ -1586,8 +1722,8 @@ return true; } - bool nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name, - bool) + bool base_nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, + const char *name, bool) { #if defined (HAVE_HDF5) @@ -1610,9 +1746,9 @@ #endif } - bool nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, - octave_hdf5_id& space_hid, - octave_hdf5_id& type_hid) + bool base_nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, + octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid) { #if defined (HAVE_HDF5) @@ -1635,14 +1771,39 @@ #endif } - void nested_fcn_handle::print_raw (std::ostream& os, - bool pr_as_read_syntax, - int current_print_indent_level) const + void base_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); } + octave_value nested_fcn_handle::make_weak_nested_handle (void) const + { + return octave_value (new octave_fcn_handle + (new weak_nested_fcn_handle (*this))); + } + + 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_stack_context); + + 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_stack_context->workspace (); + } + bool is_equal_to (const nested_fcn_handle& fh1, const nested_fcn_handle& fh2) { if (fh1.m_name == fh2.m_name @@ -1652,6 +1813,39 @@ return false; } + octave_value_list + weak_nested_fcn_handle::call (int nargout, const octave_value_list& args) + { + tree_evaluator& tw = __get_evaluator__ ("weak_nested_fcn_handle::call"); + + octave_user_function *oct_usr_fcn = m_fcn.user_function_value (); + + std::shared_ptr<stack_frame> frames = m_stack_context.lock (); + + tw.push_stack_frame (oct_usr_fcn, frames); + + unwind_action act ([&tw] () { tw.pop_stack_frame (); }); + + return oct_usr_fcn->execute (tw, nargout, args); + } + + octave_value weak_nested_fcn_handle::workspace (void) const + { + std::shared_ptr<stack_frame> frames = m_stack_context.lock (); + + return frames ? frames->workspace () : octave_value (); + } + + bool is_equal_to (const weak_nested_fcn_handle& fh1, + const weak_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 std::string& class_nm, const std::string& meth_nm) : base_fcn_handle (meth_nm), m_obj (), m_fcn (), @@ -1830,36 +2024,9 @@ 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) + const std::string base_anonymous_fcn_handle::anonymous ("@<anonymous>"); + + octave_scalar_map base_anonymous_fcn_handle::info (void) { octave_scalar_map m; @@ -1869,13 +2036,13 @@ m.setfield ("type", type ()); m.setfield ("file", ""); - m.setfield ("workspace", Cell (workspace ())); + m.setfield ("workspace", workspace ()); m.setfield ("within_file_path", ""); return m; } - bool anonymous_fcn_handle::save_ascii (std::ostream& os) + bool base_anonymous_fcn_handle::save_ascii (std::ostream& os) { // FIXME: can we ensure that m_fcn is always defined? @@ -1903,7 +2070,7 @@ return true; } - bool anonymous_fcn_handle::load_ascii (std::istream& is) + bool base_anonymous_fcn_handle::load_ascii (std::istream& is) { skip_preceeding_newline (is); @@ -1923,7 +2090,7 @@ // defines the anonymous function. interpreter& interp - = __get_interpreter__ ("anonymous_fcn_handle::load_ascii"); + = __get_interpreter__ ("base_anonymous_fcn_handle::load_ascii"); tree_evaluator& tw = interp.get_evaluator (); @@ -1962,7 +2129,8 @@ return false; } - bool anonymous_fcn_handle::save_binary (std::ostream& os, bool save_as_floats) + bool base_anonymous_fcn_handle::save_binary (std::ostream& os, + bool save_as_floats) { // FIXME: can we ensure that m_fcn is always defined? @@ -2002,8 +2170,8 @@ return true; } - bool anonymous_fcn_handle::load_binary (std::istream& is, bool swap, - mach_info::float_format fmt) + bool base_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. @@ -2039,7 +2207,7 @@ // defines the anonymous function. interpreter& interp - = __get_interpreter__ ("anonymous_fcn_handle::load_binary"); + = __get_interpreter__ ("base_anonymous_fcn_handle::load_binary"); tree_evaluator& tw = interp.get_evaluator (); @@ -2070,8 +2238,9 @@ return false; } - bool anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, - const char *name, bool save_as_floats) + bool base_anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, + const char *name, + bool save_as_floats) { #if defined (HAVE_HDF5) @@ -2235,9 +2404,9 @@ #endif } - bool anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, - octave_hdf5_id& space_hid, - octave_hdf5_id& type_hid) + bool base_anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, + octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid) { #if defined (HAVE_HDF5) @@ -2353,7 +2522,7 @@ // defines the anonymous function. interpreter& interp - = __get_interpreter__ ("anonymous_fcn_handle::load_hdf5"); + = __get_interpreter__ ("base_anonymous_fcn_handle::load_hdf5"); tree_evaluator& tw = interp.get_evaluator (); @@ -2402,7 +2571,7 @@ #endif } - void anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const + void base_anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const { tree_print_code tpc (os); @@ -2443,8 +2612,7 @@ tpc.print_fcn_handle_body (e); } - bool - anonymous_fcn_handle::parse (const std::string& fcn_text) + bool base_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 @@ -2452,7 +2620,8 @@ // 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"); + interpreter& interp + = __get_interpreter__ ("base_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 @@ -2490,6 +2659,62 @@ return true; } + anonymous_fcn_handle::anonymous_fcn_handle (const octave_value& fcn, + const stack_frame::local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& stack_context) + : base_anonymous_fcn_handle (fcn, local_vars), + m_stack_context (stack_context) + { } + + octave_value anonymous_fcn_handle::make_weak_anonymous_handle (void) const + { + return octave_value (new octave_fcn_handle + (new weak_anonymous_fcn_handle (*this))); + } + + 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, m_stack_context); + + 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 local_vars_map; + + for (const auto& nm_val : m_local_vars) + local_vars_map.assign (nm_val.first, nm_val.second); + + // FIXME: it would be more convenient if stack_frame::workspace + // returned a Cell object directly instead of a Cell in an + // octave_value object. + + Cell cell_frames; + + if (m_stack_context) + { + octave_value ov_frames = m_stack_context->workspace (); + cell_frames = ov_frames.cell_value (); + } + + octave_idx_type num_frames = cell_frames.numel (); + // FIXME: It seems there should be a simple way to concatenate cells... + Cell retval = Cell (num_frames+1, 1); + retval(0) = m_local_vars; + for (octave_idx_type i = 0; i < num_frames; i++) + retval(i+1) = cell_frames(i); + + return retval; + } + bool is_equal_to (const anonymous_fcn_handle& fh1, const anonymous_fcn_handle& fh2) { @@ -2498,6 +2723,65 @@ else return false; } + + octave_value_list + weak_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 (); + + std::shared_ptr<stack_frame> frames = m_stack_context.lock (); + + tw.push_stack_frame (oct_usr_fcn, m_local_vars, frames); + + unwind_action act ([&tw] () { tw.pop_stack_frame (); }); + + return oct_usr_fcn->execute (tw, nargout, args); + } + + octave_value weak_anonymous_fcn_handle::workspace (void) const + { + octave_scalar_map local_vars_map; + + for (const auto& nm_val : m_local_vars) + local_vars_map.assign (nm_val.first, nm_val.second); + + // FIXME: it would be more convenient if stack_frame::workspace + // returned a Cell object directly instead of a Cell in an + // octave_value object. + + std::shared_ptr<stack_frame> frames = m_stack_context.lock (); + + Cell cell_frames; + + if (frames) + { + octave_value ov_frames = frames->workspace (); + cell_frames = ov_frames.cell_value (); + } + + octave_idx_type num_frames = cell_frames.numel (); + + // FIXME: It seems there should be a simple way to concatenate + // cells... + Cell retval = Cell (num_frames+1, 1); + retval(0) = m_local_vars; + for (octave_idx_type i = 0; i < num_frames; i++) + retval(i+1) = cell_frames(i); + + return retval; + } + + bool is_equal_to (const weak_anonymous_fcn_handle& fh1, + const weak_anonymous_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_fcn_handle::octave_fcn_handle (void) @@ -2547,15 +2831,16 @@ octave_fcn_handle::octave_fcn_handle (const octave_value& fcn, const std::string& name, - const std::shared_ptr<octave::stack_frame>& closure_frames) + const std::shared_ptr<octave::stack_frame>& stack_context) : octave_base_value (), - m_rep (new octave::nested_fcn_handle (fcn, name, closure_frames)) + m_rep (new octave::nested_fcn_handle (fcn, name, stack_context)) { } octave_fcn_handle::octave_fcn_handle (const octave_value& fcn, - const octave::stack_frame::local_vars_map& local_vars) + const octave::stack_frame::local_vars_map& local_vars, + const std::shared_ptr<octave::stack_frame>& stack_context) : octave_base_value (), - m_rep (new octave::anonymous_fcn_handle (fcn, local_vars)) + m_rep (new octave::anonymous_fcn_handle (fcn, local_vars, stack_context)) { } octave_fcn_handle::octave_fcn_handle (octave::base_fcn_handle *rep)
--- a/libinterp/octave-value/ov-fcn-handle.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-fcn-handle.h Thu Apr 01 03:02:22 2021 -0400 @@ -72,10 +72,23 @@ virtual bool is_nested (void) const { return false; } + virtual bool is_nested (const std::shared_ptr<stack_frame>&) const + { + return false; + } + + virtual bool is_weak_nested (void) const { return false; } + virtual bool is_class_simple (void) const { return false; } virtual bool is_anonymous (void) const { return false; } + virtual bool is_weak_anonymous (void) const { return false; } + + virtual octave_value make_weak_nested_handle (void) const; + + virtual octave_value make_weak_anonymous_handle (void) const; + std::string fcn_name (void) const { return m_name; } std::string file (void) const { return m_file; } @@ -203,7 +216,11 @@ // 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); + const octave::stack_frame::local_vars_map& local_vars, + const std::shared_ptr<octave::stack_frame>& closure_frames + = std::shared_ptr<octave::stack_frame> ()); + + octave_fcn_handle (octave::base_fcn_handle *rep); octave_fcn_handle (const octave_fcn_handle& fh); @@ -254,10 +271,29 @@ bool is_nested (void) const { return m_rep->is_nested (); } + bool is_nested (const std::shared_ptr<octave::stack_frame>& frame) const + { + return m_rep->is_nested (frame); + } + + bool is_weak_nested (void) const { return m_rep->is_weak_nested (); } + bool is_class_simple (void) const { return m_rep->is_class_simple (); } bool is_anonymous (void) const { return m_rep->is_anonymous (); } + bool is_weak_anonymous (void) const { return m_rep->is_weak_anonymous (); } + + octave_value make_weak_nested_handle (void) const + { + return m_rep->make_weak_nested_handle (); + } + + octave_value make_weak_anonymous_handle (void) const + { + return m_rep->make_weak_anonymous_handle (); + } + dim_vector dims (void) const; // FIXME: These must go away. They don't do the right thing for @@ -328,8 +364,6 @@ std::shared_ptr<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.get (); } DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
--- a/libinterp/octave-value/ov-struct.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-struct.cc Thu Apr 01 03:02:22 2021 -0400 @@ -63,6 +63,18 @@ // specified by struct_levels_to_print. static bool Vprint_struct_array_contents = false; +void +octave_struct::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame) +{ + for (octave_idx_type j = 0; j < map.nfields (); j++) + { + Cell& c = map.contents (j); + + for (octave_idx_type i = 0; i < c.numel (); i++) + c(i).break_closure_cycles (frame); + } +} + octave_base_value * octave_struct::try_narrowing_conversion (void) { @@ -1083,6 +1095,13 @@ DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(octave_scalar_struct, "scalar struct", "struct"); +void +octave_scalar_struct::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame) +{ + for (octave_idx_type i = 0; i < map.nfields (); i++) + map.contents(i).break_closure_cycles (frame); +} + octave_value octave_scalar_struct::dotref (const octave_value_list& idx, bool auto_add) {
--- a/libinterp/octave-value/ov-struct.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov-struct.h Thu Apr 01 03:02:22 2021 -0400 @@ -64,6 +64,8 @@ octave_base_value * clone (void) const { return new octave_struct (*this); } octave_base_value * empty_clone (void) const { return new octave_struct (); } + void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame); + octave_base_value * try_narrowing_conversion (void); Cell dotref (const octave_value_list& idx, bool auto_add = false); @@ -192,6 +194,8 @@ octave_base_value * empty_clone (void) const { return new octave_scalar_struct (); } + void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame); + octave_value dotref (const octave_value_list& idx, bool auto_add = false); octave_value subsref (const std::string& type,
--- a/libinterp/octave-value/ov.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov.cc Thu Apr 01 03:02:22 2021 -0400 @@ -1241,6 +1241,33 @@ } void +octave_value::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame) +{ + if (is_function_handle ()) + { + octave_fcn_handle *fhdl = rep->fcn_handle_value (); + + if (fhdl->is_nested (frame) && ! fhdl->is_weak_nested ()) + *this = fhdl->make_weak_nested_handle (); + else if (fhdl->is_anonymous () && ! fhdl->is_weak_anonymous ()) + *this = fhdl->make_weak_anonymous_handle (); + } + else + { + // FIXME: Is there a efficient way to avoid calling make_unique + // if REP doesn't contain any nested function handles? + // + // Probably we should be asking REP to make a modified copy IFF it + // is needed, then replace our REP with that if a copy is made, + // otherwise we leave it alone. + + make_unique (); + + rep->break_closure_cycles (frame); + } +} + +void octave_value::maybe_mutate (void) { octave_base_value *tmp = rep->try_narrowing_conversion ();
--- a/libinterp/octave-value/ov.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/octave-value/ov.h Thu Apr 01 03:02:22 2021 -0400 @@ -33,6 +33,7 @@ #include <iosfwd> #include <string> #include <list> +#include <memory> #include <map> #include "Range.h" @@ -46,6 +47,7 @@ namespace octave { + class stack_frame; class type_info; } @@ -388,6 +390,12 @@ } } + // Convert any nested function handles in this object to use weak + // references to their enclosing stack frame context. Used to break + // shared_ptr reference cycles for handles to nested functions + // (closures). + void break_closure_cycles (const std::shared_ptr<octave::stack_frame>&); + // Simple assignment. octave_value& operator = (const octave_value& a)
--- a/libinterp/parse-tree/pt-eval.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/parse-tree/pt-eval.cc Thu Apr 01 03:02:22 2021 -0400 @@ -2291,9 +2291,10 @@ } void tree_evaluator::push_stack_frame (octave_user_function *fcn, - const stack_frame::local_vars_map& local_vars) - { - m_call_stack.push (fcn, local_vars); + const stack_frame::local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& closure_frames) + { + m_call_stack.push (fcn, local_vars, closure_frames); } void tree_evaluator::push_stack_frame (octave_user_script *script) @@ -3319,11 +3320,6 @@ unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_FUNCTION); - unwind_action act1 ([] (std::shared_ptr<stack_frame> frame) - { - frame->clear_values (); - }, m_call_stack.get_current_stack_frame ()); - tree_statement_list *cmd_list = user_function.body (); if (cmd_list)
--- a/libinterp/parse-tree/pt-eval.h Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/parse-tree/pt-eval.h Thu Apr 01 03:02:22 2021 -0400 @@ -411,7 +411,8 @@ const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ()); void push_stack_frame (octave_user_function *fcn, - const stack_frame::local_vars_map& local_vars); + const stack_frame::local_vars_map& local_vars, + const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ()); void push_stack_frame (octave_user_script *script);
--- a/libinterp/parse-tree/pt-fcn-handle.cc Wed Mar 31 21:33:46 2021 +0200 +++ b/libinterp/parse-tree/pt-fcn-handle.cc Thu Apr 01 03:02:22 2021 -0400 @@ -151,6 +151,8 @@ octave_function *curr_fcn = cs.current_function (); + bool is_nested = false; + if (curr_fcn) { // FIXME: maybe it would be better to just stash curr_fcn @@ -162,6 +164,7 @@ if (curr_fcn->is_parent_function () || curr_fcn->is_nested_function ()) { + is_nested = true; af->mark_as_nested_function (); new_scope.set_nesting_depth (parent_scope.nesting_depth () + 1); } @@ -185,7 +188,9 @@ octave_value ov_fcn (af); - return octave_value (new octave_fcn_handle (ov_fcn, local_vars)); + return (is_nested + ? octave_value (new octave_fcn_handle (ov_fcn, local_vars, frame)) + : octave_value (new octave_fcn_handle (ov_fcn, local_vars))); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug-60237/bug-60237.tst Thu Apr 01 03:02:22 2021 -0400 @@ -0,0 +1,1 @@ +%!assert (bug_60237 (), 5)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug-60237/bug_60237.m Thu Apr 01 03:02:22 2021 -0400 @@ -0,0 +1,11 @@ +function r = bug_60237 () + d = 2; + function c = bm (a) + c = a + d; + endfunction; + r = ancall (@(a) bm (a), 2); +endfunction + +function r = ancall (f, a) + r = f (a) + 1; +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug-60237/module.mk Thu Apr 01 03:02:22 2021 -0400 @@ -0,0 +1,5 @@ +bug_60237_TEST_FILES = \ + %reldir%/bug-60237.tst \ + %reldir%/bug_60237.m + +TEST_FILES += $(bug_60237_TEST_FILES)
--- a/test/module.mk Wed Mar 31 21:33:46 2021 +0200 +++ b/test/module.mk Thu Apr 01 03:02:22 2021 -0400 @@ -93,6 +93,7 @@ include %reldir%/bug-59661/module.mk include %reldir%/bug-59704/module.mk include %reldir%/bug-59937/module.mk +include %reldir%/bug-60237/module.mk include %reldir%/class-concat/module.mk include %reldir%/classdef/module.mk include %reldir%/classdef-multiple-inheritance/module.mk