Mercurial > octave
changeset 28439:e760fef2829c stable
refactor octave_fcn_handle class
* ov-fcn-handle.h, ov-fcn-handle.cc (class octave_fcn_handle):
split octave_fcn_handle internally into separate sub-classes for the
following types of function handles: simple, scoped, nested,
classsimple, and anonymous. Update all uses.
* load-path.cc (load_path::package_info::find_private_fcn):
Don't search for private files that are not already in the private
function map.
* ls-mat5.cc (read_mat5_binary_element): Update handling of local
variables for anonymous functions.
* stack-frame.h, stack-frame.cc (stack_frame::set_closure_links,
stack_frame::dup, compiled_fcn_stack_frame::dup,
script_stack_frame::dup, user_fcn_stack_frame::dup,
scope_stack_frame::dup): Delete unnecessary functions.
* ov-fcn.h (octave_function::is_nested_function,
octave_function::is_parent_function): New virtual functions.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Wed, 29 Apr 2020 14:10:27 -0400 |
parents | 55f82d23fe5e |
children | 23fe97205db5 |
files | libinterp/corefcn/load-path.cc libinterp/corefcn/ls-mat5.cc libinterp/corefcn/stack-frame.cc libinterp/corefcn/stack-frame.h libinterp/dldfcn/__init_fltk__.cc libinterp/octave-value/cdef-class.cc libinterp/octave-value/cdef-manager.cc libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-fcn-handle.h libinterp/octave-value/ov-fcn.h libinterp/operators/op-fcn.cc libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-fcn-handle.cc |
diffstat | 13 files changed, 3083 insertions(+), 1611 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/load-path.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/corefcn/load-path.cc Wed Apr 29 14:10:27 2020 -0400 @@ -185,46 +185,6 @@ return retval; } - static std::string find_private_fcn_file (const std::string& dir, - const std::string& fcn, - int type) - { - std::string nm - = sys::file_ops::concat (sys::file_ops::concat (dir, "private"), fcn); - - if (type & load_path::OCT_FILE) - { - std::string fnm = nm + ".oct"; - - sys::file_stat fs (fnm); - - if (fs.exists () && fs.is_reg ()) - return fnm; - } - - if (type & load_path::MEX_FILE) - { - std::string fnm = nm + ".mex"; - - sys::file_stat fs (fnm); - - if (fs.exists () && fs.is_reg ()) - return fnm; - } - - if (type & load_path::M_FILE) - { - std::string fnm = nm + ".m"; - - sys::file_stat fs (fnm); - - if (fs.exists () && fs.is_reg ()) - return fnm; - } - - return ""; - } - // True if a path is contained in a path list separated by path_sep_char static bool @@ -1696,9 +1656,8 @@ std::string retval; // update (); - std::string canon_dir = sys::canonicalize_file_name (dir); - - const_private_fcn_map_iterator q = private_fcn_map.find (canon_dir); + + const_private_fcn_map_iterator q = private_fcn_map.find (dir); if (q != private_fcn_map.end ()) { @@ -1709,15 +1668,13 @@ if (p != fcn_file_map.end ()) { std::string fname - = sys::file_ops::concat (sys::file_ops::concat (canon_dir, "private"), fcn); + = sys::file_ops::concat (sys::file_ops::concat (dir, "private"), fcn); if (check_file_type (fname, type, p->second, fcn, "load_path::find_private_fcn")) retval = fname; } } - else - retval = find_private_fcn_file (canon_dir, fcn, type); return retval; }
--- a/libinterp/corefcn/ls-mat5.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/corefcn/ls-mat5.cc Wed Apr 29 14:10:27 2020 -0400 @@ -70,6 +70,7 @@ #include "pager.h" #include "parse.h" #include "pt-eval.h" +#include "stack-frame.h" #include "sysdep.h" #include "unwind-prot.h" #include "utils.h" @@ -900,6 +901,7 @@ "", "", fname); if (ov_fcn.is_defined ()) + // XXX FCN_HANDLE: SIMPLE/SCOPED tc = octave_value (new octave_fcn_handle (ov_fcn, fname)); } else @@ -926,6 +928,7 @@ "", "", fname); if (ov_fcn.is_defined ()) + // XXX FCN_HANDLE: SIMPLE/SCOPED tc = octave_value (new octave_fcn_handle (ov_fcn, fname)); else { @@ -948,6 +951,7 @@ "", "", fname); if (ov_fcn.is_defined ()) + // XXX FCN_HANDLE: SIMPLE/SCOPED tc = octave_value (new octave_fcn_handle (ov_fcn, fname)); else { @@ -977,14 +981,7 @@ tc2 = m2.contents ("MCOS").cell_value ()(1 + off).cell_value ()(1); m2 = tc2.scalar_map_value (); - octave::unwind_protect_safe frame; - - // Set up temporary scope to use for evaluating the text - // that defines the anonymous function. - - octave::tree_evaluator& tw = interp.get_evaluator (); - tw.push_dummy_scope ("read_mat5_binary_element"); - frame.add_method (tw, &octave::tree_evaluator::pop_scope); + octave::stack_frame::local_vars_map local_vars; if (m2.nfields () > 0) { @@ -995,10 +992,27 @@ std::string key = m2.key (p0); octave_value val = m2.contents (p0); - interp.assign (key, val); + local_vars[key] = val; } } + // Set up temporary scope to use for evaluating the text + // that defines the anonymous function so that we don't + // pick up values of random variables that might be in the + // current scope. + + octave::tree_evaluator& tw = interp.get_evaluator (); + tw.push_dummy_scope ("read_mat5_binary_element"); + + octave::unwind_action act ([&tw] () { tw.pop_scope (); }); + + // FIXME: If evaluation of the string gives us an anonymous + // function handle object, then why extract the function and + // create a new anonymous function object? Why not just + // attach the workspace values to the object returned by + // eval_string? This code is also is duplicated in + // anon_fcn_handle::parse_anon_fcn_handle. + int parse_status; octave_value anon_fcn_handle = interp.eval_string (fname.substr (4), true, parse_status); @@ -1011,7 +1025,8 @@ if (! fh) error ("load: failed to load anonymous function handle"); - tc = new octave_fcn_handle (fh->fcn_val (), "@<anonymous>"); + // XXX FCN_HANDLE: ANONYMOUS + tc = octave_value (new octave_fcn_handle (fh->fcn_val (), local_vars)); } else error ("load: invalid function handle type");
--- a/libinterp/corefcn/stack-frame.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/corefcn/stack-frame.cc Wed Apr 29 14:10:27 2020 -0400 @@ -69,8 +69,6 @@ ~compiled_fcn_stack_frame (void) = default; - compiled_fcn_stack_frame * dup (void) const; - bool is_compiled_fcn_frame (void) const { return true; } symbol_scope get_scope (void) const @@ -189,8 +187,6 @@ delete m_unwind_protect_frame; } - script_stack_frame * dup (void) const; - bool is_user_script_frame (void) const { return true; } static std::shared_ptr<stack_frame> @@ -430,8 +426,6 @@ delete m_unwind_protect_frame; } - user_fcn_stack_frame * dup (void) const; - bool is_user_fcn_frame (void) const { return true; } static std::shared_ptr<stack_frame> @@ -513,8 +507,6 @@ ~scope_stack_frame (void) = default; - scope_stack_frame * dup (void) const; - bool is_scope_frame (void) const { return true; } symbol_scope get_scope (void) const { return m_scope; } @@ -1430,12 +1422,6 @@ } } - compiled_fcn_stack_frame * - compiled_fcn_stack_frame::dup (void) const - { - return new compiled_fcn_stack_frame (*this); - } - void compiled_fcn_stack_frame::display (bool follow) const { std::ostream& os = octave_stdout; @@ -1464,12 +1450,6 @@ set_script_offsets (); } - script_stack_frame * - script_stack_frame::dup (void) const - { - return new script_stack_frame (*this); - } - size_t script_stack_frame::get_num_symbols (octave_user_script *script) { symbol_scope script_scope = script->scope (); @@ -2058,12 +2038,6 @@ } } - user_fcn_stack_frame * - user_fcn_stack_frame::dup (void) const - { - return new user_fcn_stack_frame (*this); - } - // If this is a nested scope, set access_link to nearest parent // stack frame that corresponds to the lexical parent of this scope. @@ -2353,12 +2327,6 @@ sfw.visit_user_fcn_stack_frame (*this); } - scope_stack_frame * - scope_stack_frame::dup (void) const - { - return new scope_stack_frame (*this); - } - symbol_record scope_stack_frame::insert_symbol (const std::string& name) { // There is no access link for scope frames, so there is no other
--- a/libinterp/corefcn/stack-frame.h Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/corefcn/stack-frame.h Wed Apr 29 14:10:27 2020 -0400 @@ -180,8 +180,6 @@ virtual ~stack_frame (void) = default; - virtual stack_frame * dup (void) const = 0; - // FIXME: It would be nice to eliminate these but there are a few // places where we still need to know the specific type of the // stack frame that we are handling. @@ -307,12 +305,6 @@ std::shared_ptr<stack_frame> access_link (void) const {return m_access_link; } - void set_closure_links (const std::shared_ptr<stack_frame>& dup_frame) - { - m_static_link = dup_frame; - m_access_link = dup_frame; - } - virtual size_t size (void) const; virtual void resize (size_t);
--- a/libinterp/dldfcn/__init_fltk__.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/dldfcn/__init_fltk__.cc Wed Apr 29 14:10:27 2020 -0400 @@ -2532,7 +2532,8 @@ toolkit_loaded = true; octave_value fcn (new octave_builtin (F__fltk_check__)); - octave_value fcn_handle (new octave_fcn_handle (fcn, "@__fltk_check__")); + octave_value fcn_handle (new octave_fcn_handle (fcn)); + octave_value_list id = Fadd_input_event_hook (interp, fcn_handle, 1); fltk->set_input_event_hook_id (id);
--- a/libinterp/octave-value/cdef-class.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/octave-value/cdef-class.cc Wed Apr 29 14:10:27 2020 -0400 @@ -63,18 +63,18 @@ namespace octave { static octave_value - make_fcn_handle (interpreter& interp, const octave_value& fcn, - const std::string& nm) + make_fcn_handle (const octave_value& fcn, const std::string& meth_name, + const std::string& class_name) { octave_value retval; if (fcn.is_defined ()) { - tree_evaluator& tw = interp.get_evaluator (); + // FCN_HANDLE: METHOD + octave_fcn_handle *fh + = new octave_fcn_handle (fcn, class_name, meth_name); - symbol_scope curr_scope = tw.get_current_scope (); - - retval = octave_value (new octave_fcn_handle (curr_scope, fcn, nm)); + retval = octave_value (fh); } return retval; @@ -979,12 +979,10 @@ if (mprefix == "get.") get_methods[mname.substr (4)] - = make_fcn_handle (interp, mtd, - full_class_name + '>' + mname); + = make_fcn_handle (mtd, mname, full_class_name); else if (mprefix == "set.") set_methods[mname.substr (4)] - = make_fcn_handle (interp, mtd, - full_class_name + '>' + mname); + = make_fcn_handle (mtd, mname, full_class_name); else { cdef_method meth = cdm.make_method (retval, mname, mtd);
--- a/libinterp/octave-value/cdef-manager.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/octave-value/cdef-manager.cc Wed Apr 29 14:10:27 2020 -0400 @@ -39,9 +39,7 @@ { octave_value fcn (new octave_builtin (ff, nm)); - octave_value fcn_handle (new octave_fcn_handle (fcn, nm)); - - return fcn_handle; + return octave_value (new octave_fcn_handle (fcn)); } static octave_value_list
--- a/libinterp/octave-value/ov-fcn-handle.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/octave-value/ov-fcn-handle.cc Wed Apr 29 14:10:27 2020 -0400 @@ -50,6 +50,7 @@ #include "oct-hdf5.h" #include "oct-map.h" #include "ov-base.h" +#include "ov-cell.h" #include "ov-fcn-handle.h" #include "ov-usr-fcn.h" #include "parse.h" @@ -83,381 +84,2412 @@ const std::string octave_fcn_handle::anonymous ("@<anonymous>"); -octave_fcn_handle::octave_fcn_handle (const std::string& n) - : m_fcn (), m_obj (), m_name (n), m_scope (), m_is_nested (false), - m_closure_frames (), m_local_vars (nullptr), m_dispatch_class () +namespace octave { - if (! m_name.empty () && m_name[0] == '@') - m_name = m_name.substr (1); - - size_t pos = m_name.find ('.'); - - if (pos != std::string::npos) + class invalid_fcn_handle : public base_fcn_handle + { + public: + + invalid_fcn_handle (void) : base_fcn_handle ("<invalid>") { } + + invalid_fcn_handle (const invalid_fcn_handle&) = default; + + ~invalid_fcn_handle (void) = default; + + invalid_fcn_handle * clone (void) const + { + return new invalid_fcn_handle (*this); + } + + std::string type (void) const { return "<invalid>"; } + + octave_value_list call (int nargout, const octave_value_list& args); + }; + + // Create a handle to an unnamed internal function. There will be no + // way to save and reload it. See, for example, the F__fltk_check__ + // function in __init_fltk__.cc. + + class internal_fcn_handle : public base_fcn_handle + { + public: + + internal_fcn_handle (const octave_value& fcn) + : base_fcn_handle ("<internal>"), m_fcn (fcn) + { } + + internal_fcn_handle (const internal_fcn_handle&) = default; + + ~internal_fcn_handle (void) = default; + + internal_fcn_handle * clone (void) const + { + return new internal_fcn_handle (*this); + } + + std::string type (void) const { return "<internal>"; } + + bool is_internal (void) const { return true; } + + octave_value_list call (int nargout, const octave_value_list& args); + + // FIXME: These must go away. They don't do the right thing for + // scoping or overloads. + octave_function * function_value (bool = false) + { + return m_fcn.function_value (); + } + + octave_user_function * user_function_value (bool = false) + { + return m_fcn.user_function_value (); + } + + octave_value fcn_val (void) { return m_fcn; } + + // Should be const. + octave_scalar_map info (void); + + friend bool is_equal_to (const internal_fcn_handle& fh1, + const internal_fcn_handle& fh2); + + private: + + octave_value m_fcn; + }; + + class simple_fcn_handle : public base_fcn_handle + { + public: + + // FIXME: octaveroot is temporary information used when loading + // handles. Can we avoid using it in the constructor? + + simple_fcn_handle (const std::string& name = "", + const std::string& file = "", + const std::string& /*octaveroot*/ = "") + : base_fcn_handle (name, file), m_fcn () + { } + + simple_fcn_handle (const octave_value& fcn, const std::string& name) + : base_fcn_handle (name), m_fcn (fcn) + { + if (m_fcn.is_defined ()) + { + octave_function *fcn = m_fcn.function_value (); + + if (fcn) + m_file = fcn->fcn_file_name (); + } + } + + simple_fcn_handle (const simple_fcn_handle&) = default; + + ~simple_fcn_handle (void) = default; + + simple_fcn_handle * clone (void) const + { + return new simple_fcn_handle (*this); + } + + std::string type (void) const { return "simple"; } + + bool is_simple (void) const { return true; } + + octave_value_list call (int nargout, const octave_value_list& args); + + // FIXME: These must go away. They don't do the right thing for + // scoping or overloads. + octave_function * function_value (bool); + + octave_user_function * user_function_value (bool); + + octave_value fcn_val (void); + + // Should be const. + octave_scalar_map info (void); + + bool save_ascii (std::ostream& os); + + bool load_ascii (std::istream& is); + + bool save_binary (std::ostream& os, bool save_as_floats); + + bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt); + + bool save_hdf5 (octave_hdf5_id loc_hid, const char *name, bool save_as_floats); + + bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid); + + void print_raw (std::ostream& os, bool pr_as_read_syntax, + int current_print_indent_level) const; + + friend bool is_equal_to (const simple_fcn_handle& fh1, + const simple_fcn_handle& fh2); + + private: + + octave_value m_fcn; + }; + + class scoped_fcn_handle : public base_fcn_handle + { + public: + + // FIXME: octaveroot is temporary information used when loading + // handles. Can we avoid using it in the constructor? + + scoped_fcn_handle (const std::string& name = "", + const std::string& file = "", + const std::string& /*octaveroot*/ = "") + : base_fcn_handle (name, file) + { } + + scoped_fcn_handle (const octave_value& fcn, const std::string& name, + const std::list<std::string>& parentage); + + scoped_fcn_handle (const scoped_fcn_handle&) = default; + + ~scoped_fcn_handle (void) = default; + + scoped_fcn_handle * clone (void) const + { + return new scoped_fcn_handle (*this); + } + + std::string type (void) const { return "scopedfunction"; } + + bool is_scoped (void) const { return true; } + + octave_value_list call (int nargout, const octave_value_list& args); + + // FIXME: These must go away. They don't do the right thing for + // scoping or overloads. + octave_function * function_value (bool = false) + { + return m_fcn.function_value (); + } + + octave_user_function * user_function_value (bool = false) { - // If we are looking at - // - // obj . meth - // - // Store the object so that calling METH for OBJ will work even if - // it is done outside of the scope whre OBJ was initially defined - // or if OBJ is cleared before the method call is made through the - // function handle. - - std::string obj_name = m_name.substr (0, pos); - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_handle::octave_fcn_handle"); - - octave_value val = interp.varval (obj_name); - - if (val.is_classdef_object ()) - m_obj = val; + return m_fcn.user_function_value (); + } + + octave_value fcn_val (void) { return m_fcn; } + + // Should be const. + octave_scalar_map info (void); + + bool save_ascii (std::ostream& os); + + bool load_ascii (std::istream& is); + + bool save_binary (std::ostream& os, bool save_as_floats); + + bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt); + + bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats); + + bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid); + + void print_raw (std::ostream&, bool pr_as_read_syntax, + int current_print_indent_level) const; + + friend bool is_equal_to (const scoped_fcn_handle& fh1, + const scoped_fcn_handle& fh2); + + protected: + + void find_function (void); + + // The function we are handling. + octave_value m_fcn; + + // List of parent function names. The first element is the name of + // m_fcn. + std::list<std::string> m_parentage; + }; + + class nested_fcn_handle : public base_fcn_handle + { + public: + + // FIXME: octaveroot is temporary information used when loading + // handles. Can we avoid using it in the constructor? + + nested_fcn_handle (const std::string& name = "", + const std::string& file = "", + const std::string& /*octaveroot*/ = "") + : base_fcn_handle (name, file) + { } + + nested_fcn_handle (const octave_value& fcn, const std::string& name, + const std::shared_ptr<stack_frame>& closure_frames); + + nested_fcn_handle (const nested_fcn_handle&) = default; + + ~nested_fcn_handle (void) = default; + + nested_fcn_handle * clone (void) const + { + return new nested_fcn_handle (*this); + } + + std::string type (void) const { return "nested"; } + + bool is_nested (void) const { return true; } + + octave_value_list call (int nargout, const octave_value_list& args); + + // FIXME: These must go away. They don't do the right thing for + // scoping or overloads. + octave_function * function_value (bool = false) + { + return m_fcn.function_value (); + } + + octave_user_function * user_function_value (bool = false) + { + return m_fcn.user_function_value (); } -} - -octave_fcn_handle::octave_fcn_handle (const octave::symbol_scope& scope, - const std::string& n) - : m_fcn (), m_obj (), m_name (n), m_scope (scope), m_is_nested (false), - m_closure_frames (), m_local_vars (nullptr), m_dispatch_class () -{ - if (! m_name.empty () && m_name[0] == '@') - m_name = m_name.substr (1); - - size_t pos = m_name.find ('.'); - - if (pos != std::string::npos) + + octave_value fcn_val (void) { return m_fcn; } + + octave_value workspace (void) const; + + // Should be const. + octave_scalar_map info (void); + + bool save_ascii (std::ostream& os); + + bool load_ascii (std::istream& is); + + bool save_binary (std::ostream& os, bool save_as_floats); + + bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt); + + bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats); + + bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid); + + void print_raw (std::ostream&, bool pr_as_read_syntax, + int current_print_indent_level) const; + + friend bool is_equal_to (const nested_fcn_handle& fh1, + const nested_fcn_handle& fh2); + + protected: + + // The function we are handling. + octave_value m_fcn; + + // Pointer to closure stack frames. + std::shared_ptr<stack_frame> m_closure_frames; + }; + + class class_simple_fcn_handle : public base_fcn_handle + { + public: + + // FIXME: octaveroot is temporary information used when loading + // handles. Can we avoid using it in the constructor? + + class_simple_fcn_handle (const std::string& name = "", + const std::string& file = "", + const std::string& /*octaveroot*/ = "") + : base_fcn_handle (name, file) + { } + + // FIXME: is the method name supposed to be just the method name or + // also contain the object name? + + class_simple_fcn_handle (const octave_value& fcn, + const std::string& class_nm, + const std::string& meth_nm); + + class_simple_fcn_handle (const octave_value& obj, const octave_value& fcn, + const std::string& class_nm, + const std::string& meth_nm); + + class_simple_fcn_handle (const class_simple_fcn_handle&) = default; + + ~class_simple_fcn_handle (void) = default; + + class_simple_fcn_handle * clone (void) const + { + return new class_simple_fcn_handle (*this); + } + + std::string type (void) const { return "classsimple"; } + + bool is_class_simple (void) const { return true; } + + octave_value_list call (int nargout, const octave_value_list& args); + + // FIXME: These must go away. They don't do the right thing for + // scoping or overloads. + octave_function * function_value (bool = false) + { + return m_fcn.function_value (); + } + + octave_user_function * user_function_value (bool = false) + { + return m_fcn.user_function_value (); + } + + octave_value fcn_val (void) { return m_fcn; } + + // Should be const. + octave_scalar_map info (void); + + std::string dispatch_class (void) const { return m_dispatch_class; } + + bool save_ascii (std::ostream& os); + + bool load_ascii (std::istream& is); + + bool save_binary (std::ostream& os, bool save_as_floats); + + bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt); + + bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats); + + bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid); + + void print_raw (std::ostream&, bool pr_as_read_syntax, + int current_print_indent_level) const; + + friend bool is_equal_to (const class_simple_fcn_handle& fh1, + const class_simple_fcn_handle& fh2); + + protected: + + // The object containing the method we are handing. + octave_value m_obj; + + // The method we are handling. + octave_value m_fcn; + + // Name of the class that m_fcn belongs to. + std::string m_dispatch_class; + }; + + class anonymous_fcn_handle : public base_fcn_handle + { + public: + + static const std::string anonymous; + + // Setting NAME here is a bit of a kluge to cope with a bad choice + // made to append the number of local variables to the @<anonymous> + // tag in the binary file format. See also the save_binary and + // load_binary functions. + + anonymous_fcn_handle (const std::string& name = "") + : base_fcn_handle (name) + { } + + anonymous_fcn_handle (const octave_value& fcn, + const stack_frame::local_vars_map& local_vars); + + anonymous_fcn_handle (const anonymous_fcn_handle&) = default; + + ~anonymous_fcn_handle (void) = default; + + anonymous_fcn_handle * clone (void) const + { + return new anonymous_fcn_handle (*this); + } + + std::string type (void) const { return "anonymous"; } + + bool is_anonymous (void) const { return true; } + + octave_value_list call (int nargout, const octave_value_list& args); + + // FIXME: These must go away. They don't do the right thing for + // scoping or overloads. + octave_function * function_value (bool = false) + { + return m_fcn.function_value (); + } + + octave_user_function * user_function_value (bool = false) { - // If we are looking at - // - // obj . meth - // - // Store the object so that calling METH for OBJ will work even if - // it is done outside of the scope whre OBJ was initially defined - // or if OBJ is cleared before the method call is made through the - // function handle. - - std::string obj_name = m_name.substr (0, pos); - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_handle::octave_fcn_handle"); - - octave_value val = interp.varval (obj_name); - - if (val.is_classdef_object ()) - m_obj = val; + return m_fcn.user_function_value (); } + + octave_value fcn_val (void) { return m_fcn; } + + octave_value workspace (void) const; + + // Should be const. + octave_scalar_map info (void); + + bool save_ascii (std::ostream& os); + + bool load_ascii (std::istream& is); + + bool save_binary (std::ostream& os, bool save_as_floats); + + bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt); + + bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats); + + bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid); + + void print_raw (std::ostream&, bool pr_as_read_syntax, + int current_print_indent_level) const; + + // Anonymous function handles are printed without a newline. + bool print_as_scalar (void) const { return false; } + + bool parse (const std::string& fcn_text); + + friend bool is_equal_to (const anonymous_fcn_handle& fh1, + const anonymous_fcn_handle& fh2); + + protected: + + // The function we are handling. + octave_value m_fcn; + + // List of captured variable values for anonymous fucntions. + stack_frame::local_vars_map m_local_vars; + }; + + const std::string anonymous_fcn_handle::anonymous ("@<anonymous>"); + + extern bool is_equal_to (const anonymous_fcn_handle& fh1, + const anonymous_fcn_handle& fh2); + + static void err_invalid_fcn_handle (const std::string& name) + { + error ("invalid function handle, unable to find function for @%s", + name.c_str ()); + } + + octave_value_list + base_fcn_handle::subsref (const std::string& type, + const std::list<octave_value_list>& idx, + int nargout) + { + octave_value_list retval; + + switch (type[0]) + { + case '(': + { + int tmp_nargout = (type.length () > 1 && nargout == 0) ? 1 : nargout; + + retval = call (tmp_nargout, idx.front ()); + } + break; + + case '{': + case '.': + error ("function handle cannot be indexed with %c", type[0]); + break; + + default: + panic_impossible (); + } + + // FIXME: perhaps there should be an + // octave_value_list::next_subsref member function? See also + // octave_builtin::subsref. + + if (idx.size () > 1) + retval = retval(0).next_subsref (nargout, type, idx); + + return retval; + } + + octave_value + base_fcn_handle::convert_to_str_internal (bool, bool, char type) const + { + std::ostringstream buf; + print_raw (buf, true, 0); + return octave_value (buf.str (), type); + } + + bool + base_fcn_handle::save_ascii (std::ostream&) + { + unimplemented ("save", "text"); + + return true; + } + + bool + base_fcn_handle::load_ascii (std::istream&) + { + unimplemented ("load", "text"); + + return true; + } + + bool + base_fcn_handle::save_binary (std::ostream&, bool) + { + unimplemented ("save", "binary"); + + return true; + } + + bool + base_fcn_handle::load_binary (std::istream&, bool, mach_info::float_format) + { + unimplemented ("load", "binary"); + + return true; + } + + bool + base_fcn_handle::save_hdf5 (octave_hdf5_id, const char *, bool) + { + unimplemented ("save", "hdf5"); + + return true; + } + + bool + base_fcn_handle::load_hdf5 (octave_hdf5_id&, octave_hdf5_id&, octave_hdf5_id&) + { + unimplemented ("load", "hdf5"); + + return true; + } + + void base_fcn_handle::warn_load (const char *file_type) const + { + std::string obj_type = type (); + + warning_with_id + ("Octave:load-save-unavailable", + "%s: loading %s files not available in this version of Octave", + obj_type.c_str (), file_type); + } + + void base_fcn_handle::warn_save (const char *file_type) const + { + std::string obj_type = type (); + + warning_with_id + ("Octave:load-save-unavailable", + "%s: saving %s files not available in this version of Octave", + obj_type.c_str (), file_type); + } + + void base_fcn_handle::unimplemented (const char *op, const char *fmt) const + { + std::string htype = type (); + + warning ("%s for %s handles with %s format is not implemented", + op, htype.c_str (), fmt); + } + + octave_value_list + invalid_fcn_handle::call (int, const octave_value_list&) + { + error ("invalid call to invalid function handle"); + } + + octave_value_list + internal_fcn_handle::call (int nargout, const octave_value_list& args) + { + interpreter& interp = __get_interpreter__ ("internal_fcn_handle::call"); + + return interp.feval (m_fcn, args, nargout); + } + + octave_scalar_map internal_fcn_handle::info (void) + { + octave_scalar_map m; + + m.setfield ("function", fcn_name ()); + m.setfield ("type", type ()); + m.setfield ("file", ""); + + return m; + } + + bool is_equal_to (const internal_fcn_handle& fh1, + const internal_fcn_handle& fh2) + { + if (fh1.m_name == fh2.m_name + && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ()) + return fh1.m_fcn.is_copy_of (fh2.m_fcn); + else + return false; + } + + octave_value_list + simple_fcn_handle::call (int nargout, const octave_value_list& args) + { + // FIXME: if m_name has a '.' in the name, lookup first component. If + // it is a classdef meta object, then build TYPE and IDX arguments and + // make a subsref call using them. + + interpreter& interp = __get_interpreter__ ("simple_fcn_handle::call"); + + octave_value fcn_to_call; + + // The following code is similar to part of + // tree_evaluator::visit_index_expression but simpler because it + // handles a more restricted case. + + symbol_table& symtab = interp.get_symbol_table (); + + size_t pos = m_name.find ('.'); + + if (pos != std::string::npos) + { + // FIXME: check to see which of these cases actually work in + // Octave and Matlab. For the last two, assume handle is + // created before object is defined as an object. + // + // We can have one of + // + // pkg-list . fcn (args) + // pkg-list . cls . meth (args) + // class-name . method (args) + // class-name . static-method (args) + // object . method (args) + // object . static-method (args) + + // Evaluate package elements until we find a function, + // classdef object, or classdef_meta object that is not a + // package. An object may only appear as the first element, + // then it must be followed directly by a function name. + + size_t beg = 0; + size_t end = pos; + + std::vector<std::string> idx_elts; + + while (true) + { + end = m_name.find ('.', beg); + + idx_elts.push_back (m_name.substr (beg, end-beg)); + + if (end == std::string::npos) + break; + + beg = end+1; + } + + size_t n_elts = idx_elts.size (); + + bool have_object = false; + octave_value partial_expr_val; + + // Lazy evaluation. The first element was not known to be defined + // as an object in the scope where the handle was created. See if + // there is a definition in the current scope. + + partial_expr_val = interp.varval (idx_elts[0]); + + if (partial_expr_val.is_defined ()) + { + if (! partial_expr_val.is_classdef_object () || n_elts != 2) + err_invalid_fcn_handle (m_name); + + have_object = true; + } + else + partial_expr_val = symtab.find_function (idx_elts[0], ovl ()); + + std::string type; + std::list<octave_value_list> arg_list; + + for (size_t i = 1; i < n_elts; i++) + { + if (partial_expr_val.is_package ()) + { + if (have_object) + err_invalid_fcn_handle (m_name); + + type = "."; + arg_list.push_back (ovl (idx_elts[i])); + + try + { + // Silently ignore extra output values. + + octave_value_list tmp_list + = partial_expr_val.subsref (type, arg_list, 0); + + partial_expr_val + = tmp_list.length () ? tmp_list(0) : octave_value (); + + if (partial_expr_val.is_cs_list ()) + err_invalid_fcn_handle (m_name); + + arg_list.clear (); + } + catch (index_exception&) + { + err_invalid_fcn_handle (m_name); + } + } + else if (have_object || partial_expr_val.is_classdef_meta ()) + { + // Object or class name must be the next to the last + // element (it was the previous one, so if this is the + // final element, it should be a classdef method, + // but we'll let the classdef or classdef_meta subsref + // function sort that out. + + if (i != n_elts-1) + err_invalid_fcn_handle (m_name); + + type = ".("; + arg_list.push_back (ovl (idx_elts[i])); + arg_list.push_back (args); + + return partial_expr_val.subsref (type, arg_list, nargout); + } + else + err_invalid_fcn_handle (m_name); + } + + // If we get here, we must have a function to call. + + if (! partial_expr_val.is_function ()) + err_invalid_fcn_handle (m_name); + + fcn_to_call = partial_expr_val; + } + else + fcn_to_call = symtab.find_function (m_name, args); + + if (! fcn_to_call.is_defined ()) + err_invalid_fcn_handle (m_name); + + return interp.feval (fcn_to_call, args, nargout); + } + + octave_function * simple_fcn_handle::function_value (bool) + { + if (m_fcn.is_defined ()) + return m_fcn.function_value (); + + symbol_table& symtab + = __get_symbol_table__ ("simple_fcn_handle::function_value"); + + // FIXME: is caching the correct thing to do? + // Cache this value so that the pointer will be valid as long as the + // function handle object is valid. + + m_fcn = symtab.find_function (m_name, octave_value_list ()); + + return m_fcn.is_defined () ? m_fcn.function_value () : nullptr; + } + + octave_user_function * simple_fcn_handle::user_function_value (bool) + { + if (m_fcn.is_defined ()) + return m_fcn.user_function_value (); + + symbol_table& symtab + = __get_symbol_table__ ("simple_fcn_handle::user_function_value"); + + // FIXME: is caching the correct thing to do? + // Cache this value so that the pointer will be valid as long as the + // function handle object is valid. + + m_fcn = symtab.find_user_function (m_name); + + return m_fcn.is_defined () ? m_fcn.user_function_value () : nullptr; + } + + octave_value simple_fcn_handle::fcn_val (void) + { + if (m_fcn.is_defined ()) + return m_fcn; + + symbol_table& symtab + = __get_symbol_table__ ("simple_fcn_handle::user_function_value"); + + // FIXME: is caching the correct thing to do? + // Cache this value so that the pointer will be valid as long as the + // function handle object is valid. + + m_fcn = symtab.find_user_function (m_name); + + return m_fcn; + } + + octave_scalar_map simple_fcn_handle::info (void) + { + octave_scalar_map m; + + m.setfield ("function", fcn_name ()); + m.setfield ("type", type ()); + // When is FILE defined for simple function handles? + m.setfield ("file", file ()); + + return m; + } + + bool simple_fcn_handle::save_ascii (std::ostream& os) + { + os << "# octaveroot: " << config::octave_exec_home () << "\n"; + + std::string fnm = file (); + if (! fnm.empty ()) + os << "# path: " << fnm << "\n"; + + os << "# subtype: " << type () << "\n"; + + os << m_name << "\n"; + + return true; + } + + bool simple_fcn_handle::load_ascii (std::istream& is) + { + // FIXME: If m_file is not empty, try to load the file and define + // the function? Is it an error if that fails? Or should this job + // always be deferred until the handle is used? + + return is.good (); + } + + bool simple_fcn_handle::save_binary (std::ostream& os, bool) + { + std::ostringstream nmbuf; + + // When is FILE defined for simple function handles? + std::string fnm; + + nmbuf << m_name << "@<simple>\n" << config::octave_exec_home () + << "\n" << fnm; + + std::string buf_str = nmbuf.str (); + int32_t tmp = buf_str.length (); + os.write (reinterpret_cast<char *> (&tmp), 4); + os.write (buf_str.c_str (), buf_str.length ()); + + return true; + } + + bool simple_fcn_handle::load_binary (std::istream& is, bool, + octave::mach_info::float_format) + { + return is.good (); + } + + bool simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool) + { +#if defined (HAVE_HDF5) + + bool retval = true; + + hid_t group_hid = -1; +#if defined (HAVE_HDF5_18) + group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT, + octave_H5P_DEFAULT); +#else + group_hid = H5Gcreate (loc_id, name, 0); +#endif + if (group_hid < 0) + return false; + + hid_t space_hid, data_hid, type_hid; + space_hid = data_hid = type_hid = -1; + + // attach the type of the variable + type_hid = H5Tcopy (H5T_C_S1); + H5Tset_size (type_hid, m_name.length () + 1); + if (type_hid < 0) + { + H5Gclose (group_hid); + return false; + } + + OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2); + hdims[0] = 0; + hdims[1] = 0; + space_hid = H5Screate_simple (0, hdims, nullptr); + if (space_hid < 0) + { + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } +#if defined (HAVE_HDF5_18) + data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, + octave_H5P_DEFAULT, octave_H5P_DEFAULT, + octave_H5P_DEFAULT); +#else + data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, + octave_H5P_DEFAULT); +#endif + if (data_hid < 0 + || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL, + octave_H5P_DEFAULT, m_name.c_str ()) < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + H5Dclose (data_hid); + + std::string octaveroot = config::octave_exec_home (); + + // When is FILE defined for simple fucntion handles? + std::string fpath; + + H5Sclose (space_hid); + hdims[0] = 1; + hdims[1] = octaveroot.length (); + space_hid = H5Screate_simple (0, hdims, nullptr); + if (space_hid < 0) + { + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + H5Tclose (type_hid); + type_hid = H5Tcopy (H5T_C_S1); + H5Tset_size (type_hid, octaveroot.length () + 1); +#if defined (HAVE_HDF5_18) + hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT", + type_hid, space_hid, octave_H5P_DEFAULT, octave_H5P_DEFAULT); +#else + hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT", + type_hid, space_hid, octave_H5P_DEFAULT); +#endif + + if (a_id >= 0) + { + retval = (H5Awrite (a_id, type_hid, octaveroot.c_str ()) >= 0); + + H5Aclose (a_id); + } + else + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + H5Sclose (space_hid); + hdims[0] = 1; + hdims[1] = fpath.length (); + space_hid = H5Screate_simple (0, hdims, nullptr); + if (space_hid < 0) + { + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + H5Tclose (type_hid); + type_hid = H5Tcopy (H5T_C_S1); + H5Tset_size (type_hid, fpath.length () + 1); + +#if defined (HAVE_HDF5_18) + a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid, + octave_H5P_DEFAULT, octave_H5P_DEFAULT); +#else + a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid, octave_H5P_DEFAULT); +#endif + + if (a_id >= 0) + { + retval = (H5Awrite (a_id, type_hid, fpath.c_str ()) >= 0); + + H5Aclose (a_id); + } + else + retval = false; + + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + + return retval; + +#else + + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + warn_save ("hdf5"); + + return false; + +#endif + } + + bool simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, + octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid) + { +#if defined (HAVE_HDF5) + + unimplemented ("load", "hdf5"); + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return true; + +#else + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return false; + +#endif + } + + void simple_fcn_handle::print_raw (std::ostream& os, bool pr_as_read_syntax, + int current_print_indent_level) const + { + octave_print_internal (os, '@' + m_name, pr_as_read_syntax, + current_print_indent_level); + } + + bool is_equal_to (const simple_fcn_handle& fh1, const simple_fcn_handle& fh2) + { + if (fh1.m_name == fh2.m_name) + { + if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ()) + return fh1.m_fcn.is_copy_of (fh2.m_fcn); + + if (fh1.m_fcn.is_undefined () && fh2.m_fcn.is_undefined ()) + return true; + } + + return false; + } + + scoped_fcn_handle::scoped_fcn_handle (const octave_value& fcn, + const std::string& name, + const std::list<std::string>& parentage) + : base_fcn_handle (name), m_fcn (fcn), m_parentage (parentage) + { + // FIXME: should it be an error if FCN is undefined? + + if (m_fcn.is_defined ()) + { + octave_function *fcn = m_fcn.function_value (); + + if (fcn) + m_file = fcn->fcn_file_name (); + } + + m_parentage.push_front (name); + } + + octave_value_list + scoped_fcn_handle::call (int nargout, const octave_value_list& args) + { + // FIXME: we aren't really using the scope yet. Hmm. + + interpreter& interp = __get_interpreter__ ("simple_fcn_handle::call"); + + if (! m_fcn.is_defined ()) + { + // Try to find it? + + find_function (); + } + + if (! m_fcn.is_defined ()) + err_invalid_fcn_handle (m_name); + + return interp.feval (m_fcn, args, nargout); + } + + octave_scalar_map scoped_fcn_handle::info (void) + { + octave_scalar_map m; + + m.setfield ("function", fcn_name ()); + m.setfield ("type", type ()); + m.setfield ("file", file ()); + + m.setfield ("parentage", Cell (m_parentage)); + + return m; + } + + bool scoped_fcn_handle::save_ascii (std::ostream& os) + { + os << "# octaveroot: " << config::octave_exec_home () << "\n"; + + std::string fnm = file (); + if (! fnm.empty ()) + os << "# path: " << fnm << "\n"; + + os << "# subtype: " << type () << "\n"; + + os << m_name << "\n"; + + octave_value tmp = Cell (m_parentage); + tmp.save_ascii (os); + + return os.good (); + } + + bool scoped_fcn_handle::load_ascii (std::istream& is) + { + octave_cell ov_cell; + ov_cell.load_ascii (is); + + if (ov_cell.iscellstr ()) + { + Array<std::string> cellstr_val = ov_cell.cellstr_value (); + + for (octave_idx_type i = 0; i < cellstr_val.numel (); i++) + m_parentage.push_back (cellstr_val(i)); + } + + return is.good (); + } + + bool scoped_fcn_handle::save_binary (std::ostream& os, bool save_as_floats) + { + std::ostringstream nmbuf; + + std::string fnm = file (); + + nmbuf << m_name << "@<scopedfunction>\n" << config::octave_exec_home () + << "\n" << fnm; + + std::string buf_str = nmbuf.str (); + int32_t len = buf_str.length (); + os.write (reinterpret_cast<char *> (&len), 4); + os.write (buf_str.c_str (), buf_str.length ()); + + octave_value tmp = Cell (m_parentage); + tmp.save_binary (os, save_as_floats); + + return os.good (); + } + + bool scoped_fcn_handle::load_binary (std::istream& is, bool swap, + octave::mach_info::float_format fmt) + { + octave_cell ov_cell; + ov_cell.load_binary (is, swap, fmt); + + if (ov_cell.iscellstr ()) + { + Array<std::string> cellstr_val = ov_cell.cellstr_value (); + + for (octave_idx_type i = 0; i < cellstr_val.numel (); i++) + m_parentage.push_back (cellstr_val(i)); + } + + return is.good (); + } + + bool scoped_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool) + { +#if defined (HAVE_HDF5) + + unimplemented ("save", "hdf5"); + + // FIXME: save parentage. + + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + return true; + +#else + + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + warn_save ("hdf5"); + + return false; + +#endif + } + + bool scoped_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, + octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid) + { +#if defined (HAVE_HDF5) + + unimplemented ("load", "hdf5"); + + // FIXME: load parentage. + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return true; + +#else + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return false; + +#endif + } + + void scoped_fcn_handle::print_raw (std::ostream& os, + bool pr_as_read_syntax, + int current_print_indent_level) const + { + octave_print_internal (os, '@' + m_name, pr_as_read_syntax, + current_print_indent_level); + } + + bool is_equal_to (const scoped_fcn_handle& fh1, const scoped_fcn_handle& fh2) + { + if (fh1.m_name == fh2.m_name + && fh2.m_parentage == fh2.m_parentage + && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ()) + return fh1.m_fcn.is_copy_of (fh2.m_fcn); + else + return false; + } + + void scoped_fcn_handle::find_function (void) + { + // Since a scoped function is not visible by itself, try to load the + // file named in m_file then find and define the scoped function. + // It is not an error if this fails. We can report later that the + // handle is invalid. + + symbol_table& symtab + = __get_symbol_table__ ("scoped_fcn_handle::find_function"); + + if (m_parentage.size () == 1) + { + std::string dir_name = sys::file_ops::dirname (m_file); + + size_t pos = dir_name.find_last_of (sys::file_ops::dir_sep_chars ()); + + if (pos != std::string::npos) + dir_name = dir_name.substr (0, pos); + else if (dir_name == "private") + dir_name = "."; + + std::string fcn_name = m_parentage.front (); + + // FIXME: Does dir_name need to be in the load path for this to work? + + m_fcn = symtab.find_private_function (dir_name, m_name); + + // FIXME: Verify that it is a private function? + } + else + { + std::string primary_parent_name = m_parentage.back (); + + octave_value ov_parent_fcn + = symtab.find_user_function (primary_parent_name); + + if (ov_parent_fcn.is_defined ()) + { + octave_user_function *fcn = ov_parent_fcn.user_function_value (); + + if (fcn) + { + std::string file_name = fcn->fcn_file_name (); + + std::string oct_home = config::octave_exec_home (); + + if (file_name.substr (0, oct_home.size ()) == oct_home) + file_name = file_name.substr (oct_home.size ()); + + octave_value subfun = fcn->find_subfunction (m_name); + + if (subfun.is_defined ()) + m_fcn = subfun; + } + } + } + } + + nested_fcn_handle::nested_fcn_handle (const octave_value& fcn, + const std::string& name, + const std::shared_ptr<stack_frame>& closure_frames) + : base_fcn_handle (name), m_fcn (fcn), m_closure_frames (closure_frames) + { } + + octave_value_list + nested_fcn_handle::call (int nargout, const octave_value_list& args) + { + tree_evaluator& tw = __get_evaluator__ ("nested_fcn_handle::call"); + + octave_user_function *oct_usr_fcn = m_fcn.user_function_value (); + + tw.push_stack_frame (oct_usr_fcn, m_closure_frames); + + unwind_action act ([&tw] () { tw.pop_stack_frame (); }); + + return oct_usr_fcn->execute (tw, nargout, args); + } + + octave_value nested_fcn_handle::workspace (void) const + { + return m_closure_frames->workspace (); + } + + octave_scalar_map nested_fcn_handle::info (void) + { + octave_scalar_map m; + + m.setfield ("function", fcn_name ()); + m.setfield ("type", type ()); + m.setfield ("file", ""); + m.setfield ("workspace", workspace ()); + + return m; + } + + // FIXME: For save, we need a way to save the (possibly shared) + // workspace. For load, we need a way to load and link to the + // (possibly shared) workspace that was saved. + // + // Since a nested function is not visible by itself, do we need to try + // to load the file named in m_file then find and define the function? + // Is it an error if that fails? Or should this job always be + // deferred until the handle is used? + + bool nested_fcn_handle::save_ascii (std::ostream& os) + { + unimplemented ("save", "text"); + + octave_unused_parameter (os); + + return true; + } + + bool nested_fcn_handle::load_ascii (std::istream& is) + { + unimplemented ("load", "text"); + + octave_unused_parameter (is); + + return true; + } + + bool nested_fcn_handle::save_binary (std::ostream& os, bool save_as_floats) + { + unimplemented ("save", "binary"); + + octave_unused_parameter (os); + octave_unused_parameter (save_as_floats); + + return true; + } + + bool nested_fcn_handle::load_binary (std::istream& is, bool swap, + mach_info::float_format fmt) + { + unimplemented ("load", "binary"); + + octave_unused_parameter (is); + octave_unused_parameter (swap); + octave_unused_parameter (fmt); + + return true; + } + + bool nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool) + { +#if defined (HAVE_HDF5) + + unimplemented ("save", "hdf5"); + + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + return true; + +#else + + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + warn_save ("hdf5"); + + return false; + +#endif + } + + bool nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, + octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid) + { +#if defined (HAVE_HDF5) + + unimplemented ("load", "hdf5"); + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return true; + +#else + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return false; + +#endif + } + + void nested_fcn_handle::print_raw (std::ostream& os, + bool pr_as_read_syntax, + int current_print_indent_level) const + { + octave_print_internal (os, '@' + m_name, pr_as_read_syntax, + current_print_indent_level); + } + + bool is_equal_to (const nested_fcn_handle& fh1, const nested_fcn_handle& fh2) + { + if (fh1.m_name == fh2.m_name + && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ()) + return fh1.m_fcn.is_copy_of (fh2.m_fcn); + else + return false; + } + + class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& fcn, + const std::string& class_nm, + const std::string& meth_nm) + : base_fcn_handle (meth_nm), m_obj (), m_fcn (fcn), + m_dispatch_class (class_nm) + { } + + class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& obj, + const octave_value& fcn, + const std::string& class_nm, + const std::string& meth_nm) + : base_fcn_handle (meth_nm), m_obj (obj), m_fcn (fcn), + m_dispatch_class (class_nm) + { } + + octave_value_list + class_simple_fcn_handle::call (int nargout, const octave_value_list& args) + { + interpreter& interp = __get_interpreter__ ("class_simple_fcn_handle::call"); + + if (m_obj.is_defined ()) + { + octave_value_list tmp_args = args; + tmp_args.prepend (m_obj); + + return interp.feval (m_fcn, tmp_args, nargout); + } + + // FIXME: is this the best approach? Should we be saving current + // dispatch class and restoring that value instead of + // unconditionally setting it to "" when we return from this + // function? + + tree_evaluator& tw = interp.get_evaluator (); + + unwind_action act ([&tw] () { tw.set_dispatch_class (""); }); + + tw.set_dispatch_class (m_dispatch_class); + + return interp.feval (m_fcn, args, nargout); + } + + octave_scalar_map class_simple_fcn_handle::info (void) + { + octave_scalar_map m; + + m.setfield ("function", fcn_name ()); + m.setfield ("type", type ()); + m.setfield ("file", ""); + m.setfield ("class", dispatch_class ()); + + return m; + } + + // FIXME: Since a class method is not visible by itself, do we need to + // try to load the file named in m_file then find and define the + // function? Is it an error if that fails? Or should this job always + // be deferred until the handle is used? + + bool class_simple_fcn_handle::save_ascii (std::ostream& os) + { + unimplemented ("save", "text"); + + octave_unused_parameter (os); + + return true; + } + + bool class_simple_fcn_handle::load_ascii (std::istream& is) + { + unimplemented ("load", "text"); + + octave_unused_parameter (is); + + return true; + } + + bool class_simple_fcn_handle::save_binary (std::ostream& os, + bool save_as_floats) + { + unimplemented ("save", "binary"); + + octave_unused_parameter (os); + octave_unused_parameter (save_as_floats); + + return true; + } + + bool class_simple_fcn_handle::load_binary (std::istream& is, bool swap, + mach_info::float_format fmt) + { + unimplemented ("load", "binary"); + + octave_unused_parameter (is); + octave_unused_parameter (swap); + octave_unused_parameter (fmt); + + return true; + } + + bool class_simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, + const char *name, bool) + { +#if defined (HAVE_HDF5) + + unimplemented ("save", "hdf5"); + + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + return true; + +#else + + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + + warn_save ("hdf5"); + + return false; + +#endif + } + + bool class_simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, + octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid) + { +#if defined (HAVE_HDF5) + + unimplemented ("load", "hdf5"); + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return true; + +#else + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return false; + +#endif + } + + void class_simple_fcn_handle::print_raw (std::ostream& os, + bool pr_as_read_syntax, + int current_print_indent_level) const + { + octave_print_internal (os, '@' + m_name, pr_as_read_syntax, + current_print_indent_level); + } + + bool is_equal_to (const class_simple_fcn_handle& fh1, + const class_simple_fcn_handle& fh2) + { + // FIXME: Also need to check object values are equivalent? + + if (fh1.m_name == fh2.m_name + && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ()) + return fh1.m_fcn.is_copy_of (fh2.m_fcn); + else + return false; + } + + anonymous_fcn_handle::anonymous_fcn_handle (const octave_value& fcn, + const stack_frame::local_vars_map& local_vars) + : base_fcn_handle (anonymous), m_fcn (fcn), m_local_vars (local_vars) + { } + + octave_value_list + anonymous_fcn_handle::call (int nargout, const octave_value_list& args) + { + tree_evaluator& tw = __get_evaluator__ ("anonymous_fcn_handle::call"); + + octave_user_function *oct_usr_fcn = m_fcn.user_function_value (); + + tw.push_stack_frame (oct_usr_fcn, m_local_vars); + + unwind_action act ([&tw] () { tw.pop_stack_frame (); }); + + return oct_usr_fcn->execute (tw, nargout, args); + } + + octave_value anonymous_fcn_handle::workspace (void) const + { + octave_scalar_map ws; + + for (const auto& nm_val : m_local_vars) + ws.assign (nm_val.first, nm_val.second); + + return ws; + } + + octave_scalar_map anonymous_fcn_handle::info (void) + { + octave_scalar_map m; + + std::ostringstream buf; + print_raw (buf, true, 0); + m.setfield ("name", buf.str ()); + + m.setfield ("type", type ()); + m.setfield ("file", ""); + m.setfield ("workspace", Cell (workspace ())); + m.setfield ("within_file_path", ""); + + return m; + } + + bool anonymous_fcn_handle::save_ascii (std::ostream& os) + { + // FIXME: can we ensure that m_fcn is always defined? + + if (m_fcn.is_undefined ()) + return false; + + os << m_name << "\n"; + + print_raw (os, true, 0); + os << "\n"; + + size_t varlen = m_local_vars.size (); + + if (varlen > 0) + { + os << "# length: " << varlen << "\n"; + + for (const auto& nm_val : m_local_vars) + { + if (! save_text_data (os, nm_val.second, nm_val.first, false, 0)) + return ! os.fail (); + } + } + + return true; + } + + bool anonymous_fcn_handle::load_ascii (std::istream& is) + { + skip_preceeding_newline (is); + + std::string buf; + + if (is) + { + // Get a line of text whitespace characters included, leaving + // newline in the stream. + + buf = read_until_newline (is, true); + } + + std::streampos pos = is.tellg (); + + unwind_protect_safe frame; + + // Set up temporary scope to use for evaluating the text that + // defines the anonymous function. + + interpreter& interp + = __get_interpreter__ ("anonymous_fcn_handle::load_ascii"); + + tree_evaluator& tw = interp.get_evaluator (); + + tw.push_dummy_scope (buf); + frame.add_method (tw, &tree_evaluator::pop_scope); + + octave_idx_type len = 0; + + if (extract_keyword (is, "length", len, true) && len >= 0) + { + if (len > 0) + { + for (octave_idx_type i = 0; i < len; i++) + { + octave_value t2; + bool dummy; + + std::string name = read_text_data (is, "", dummy, t2, i); + + if (! is) + error ("load: failed to load anonymous function handle"); + + m_local_vars[name] = t2; + } + } + } + else + { + is.seekg (pos); + is.clear (); + } + + if (is) + return parse (buf); + + return false; + } + + bool anonymous_fcn_handle::save_binary (std::ostream& os, bool save_as_floats) + { + // FIXME: can we ensure that m_fcn is always defined? + + if (m_fcn.is_undefined ()) + return false; + + std::ostringstream nmbuf; + + size_t varlen = m_local_vars.size (); + + nmbuf << anonymous; + if (varlen > 0) + nmbuf << ' ' << varlen; + + std::string buf_str = nmbuf.str (); + int32_t tmp = buf_str.length (); + os.write (reinterpret_cast<char *> (&tmp), 4); + os.write (buf_str.c_str (), buf_str.length ()); + + std::ostringstream buf; + print_raw (buf, true, 0); + std::string stmp = buf.str (); + tmp = stmp.length (); + os.write (reinterpret_cast<char *> (&tmp), 4); + os.write (stmp.c_str (), stmp.length ()); + + if (varlen > 0) + { + for (const auto& nm_val : m_local_vars) + { + if (! save_binary_data (os, nm_val.second, nm_val.first, + "", 0, save_as_floats)) + return ! os.fail (); + } + } + + return true; + } + + bool anonymous_fcn_handle::load_binary (std::istream& is, bool swap, + mach_info::float_format fmt) + { + // Read extra characters in m_name as the number of local variable + // values in this anonymous function. + + octave_idx_type len = 0; + size_t anl = anonymous.length (); + if (m_name.length () > anl) + { + std::istringstream nm_is (m_name.substr (anl)); + nm_is >> len; + + // Anonymous functons don't have names. We just used this + // string as temporary storage to pass the number of local + // variable values. + + m_name = ""; + } + + int32_t tmp; + + if (! is.read (reinterpret_cast<char *> (&tmp), 4)) + return false; + if (swap) + swap_bytes<4> (&tmp); + + OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1); + // is.get (ctmp2, tmp+1, 0); caused is.eof () to be true though + // effectively not reading over file end + is.read (ctmp2, tmp); + ctmp2[tmp] = 0; + + unwind_protect_safe frame; + + // Set up temporary scope to use for evaluating the text that + // defines the anonymous function. + + interpreter& interp + = __get_interpreter__ ("anonymous_fcn_handle::load_binary"); + + tree_evaluator& tw = interp.get_evaluator (); + + tw.push_dummy_scope (ctmp2); + frame.add_method (tw, &tree_evaluator::pop_scope); + + if (len > 0) + { + for (octave_idx_type i = 0; i < len; i++) + { + octave_value t2; + bool dummy; + std::string doc; + + std::string name + = read_binary_data (is, swap, fmt, "", dummy, t2, doc); + + if (! is) + error ("load: failed to load anonymous function handle"); + + m_local_vars[name] = t2; + } + } + + if (is) + return parse (ctmp2); + + return false; + } + + bool anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, + const char *name, bool save_as_floats) + { +#if defined (HAVE_HDF5) + + bool retval = true; + + hid_t group_hid = -1; +#if defined (HAVE_HDF5_18) + group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT, + octave_H5P_DEFAULT); +#else + group_hid = H5Gcreate (loc_id, name, 0); +#endif + if (group_hid < 0) + return false; + + hid_t space_hid, data_hid, type_hid; + space_hid = data_hid = type_hid = -1; + + // attach the type of the variable + type_hid = H5Tcopy (H5T_C_S1); + H5Tset_size (type_hid, m_name.length () + 1); + if (type_hid < 0) + { + H5Gclose (group_hid); + return false; + } + + OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2); + hdims[0] = 0; + hdims[1] = 0; + space_hid = H5Screate_simple (0, hdims, nullptr); + if (space_hid < 0) + { + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } +#if defined (HAVE_HDF5_18) + data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, + octave_H5P_DEFAULT, octave_H5P_DEFAULT, + octave_H5P_DEFAULT); +#else + data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, + octave_H5P_DEFAULT); +#endif + if (data_hid < 0 + || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL, + octave_H5P_DEFAULT, m_name.c_str ()) < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + H5Dclose (data_hid); + + std::ostringstream buf; + print_raw (buf, true, 0); + std::string stmp = buf.str (); + + // attach the type of the variable + H5Tset_size (type_hid, stmp.length () + 1); + if (type_hid < 0) + { + H5Sclose (space_hid); + H5Gclose (group_hid); + return false; + } + +#if defined (HAVE_HDF5_18) + data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid, + octave_H5P_DEFAULT, octave_H5P_DEFAULT, + octave_H5P_DEFAULT); +#else + data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid, + octave_H5P_DEFAULT); +#endif + if (data_hid < 0 + || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL, + octave_H5P_DEFAULT, stmp.c_str ()) < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + H5Dclose (data_hid); + + size_t varlen = m_local_vars.size (); + + if (varlen > 0) + { + hid_t as_id = H5Screate (H5S_SCALAR); + + if (as_id >= 0) + { +#if defined (HAVE_HDF5_18) + hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE", + H5T_NATIVE_IDX, as_id, + octave_H5P_DEFAULT, octave_H5P_DEFAULT); + +#else + hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE", + H5T_NATIVE_IDX, as_id, octave_H5P_DEFAULT); +#endif + + if (a_id >= 0) + { + retval = (H5Awrite (a_id, H5T_NATIVE_IDX, &varlen) >= 0); + + H5Aclose (a_id); + } + else + retval = false; + + H5Sclose (as_id); + } + else + retval = false; +#if defined (HAVE_HDF5_18) + data_hid = H5Gcreate (group_hid, "symbol table", + octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT); +#else + data_hid = H5Gcreate (group_hid, "symbol table", 0); +#endif + if (data_hid < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + for (const auto& nm_val : m_local_vars) + { + if (! add_hdf5_data (data_hid, nm_val.second, nm_val.first, + "", false, save_as_floats)) + break; + } + + H5Gclose (data_hid); + } + + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + + return retval; + +#else + + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + octave_unused_parameter (save_as_floats); + + warn_save ("hdf5"); + + return false; + +#endif + } + + bool anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid, + octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid) + { +#if defined (HAVE_HDF5) + + bool success = true; + +#if defined (HAVE_HDF5_18) + hid_t data_hid = H5Dopen (group_hid, "fcn", octave_H5P_DEFAULT); +#else + hid_t data_hid = H5Dopen (group_hid, "fcn"); +#endif + + if (data_hid < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + H5Tclose (type_hid); + type_hid = H5Dget_type (data_hid); + hid_t type_class_hid = H5Tget_class (type_hid); + + if (type_class_hid != H5T_STRING) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return false; + } + + H5Sclose (space_hid); + space_hid = H5Dget_space (data_hid); + hsize_t rank = H5Sget_simple_extent_ndims (space_hid); + + if (rank != 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return false; + } + + int slen = H5Tget_size (type_hid); + if (slen < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return false; + } + + OCTAVE_LOCAL_BUFFER (char, fcn_tmp, slen); + + // create datatype for (null-terminated) string to read into: + hid_t st_id = H5Tcopy (H5T_C_S1); + H5Tset_size (st_id, slen); + + if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL, + octave_H5P_DEFAULT, fcn_tmp) + < 0) + { + H5Tclose (st_id); + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return false; + } + H5Tclose (st_id); + H5Dclose (data_hid); + + octave_idx_type len = 0; + + // we have to pull some shenanigans here to make sure + // HDF5 doesn't print out all sorts of error messages if we + // call H5Aopen for a non-existing attribute + + H5E_auto_t err_func; + void *err_func_data; + + // turn off error reporting temporarily, but save the error + // reporting function: +#if defined (HAVE_HDF5_18) + H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data); + H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr); +#else + H5Eget_auto (&err_func, &err_func_data); + H5Eset_auto (nullptr, nullptr); +#endif + + hid_t attr_id = H5Aopen_name (group_hid, "SYMBOL_TABLE"); + + if (attr_id >= 0) + { + if (H5Aread (attr_id, H5T_NATIVE_IDX, &len) < 0) + success = false; + + H5Aclose (attr_id); + } + + // restore error reporting: +#if defined (HAVE_HDF5_18) + H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data); +#else + H5Eset_auto (err_func, err_func_data); +#endif + + unwind_protect_safe frame; + + // Set up temporary scope to use for evaluating the text that + // defines the anonymous function. + + interpreter& interp + = __get_interpreter__ ("anonymous_fcn_handle::load_hdf5"); + + tree_evaluator& tw = interp.get_evaluator (); + + tw.push_dummy_scope (fcn_tmp); + frame.add_method (tw, &tree_evaluator::pop_scope); + + if (len > 0 && success) + { + hsize_t num_obj = 0; +#if defined (HAVE_HDF5_18) + data_hid = H5Gopen (group_hid, "symbol table", octave_H5P_DEFAULT); +#else + data_hid = H5Gopen (group_hid, "symbol table"); +#endif + H5Gget_num_objs (data_hid, &num_obj); + H5Gclose (data_hid); + + if (num_obj != static_cast<hsize_t> (len)) + error ("load: failed to load anonymous function handle"); + + hdf5_callback_data dsub; + int current_item = 0; + for (octave_idx_type i = 0; i < len; i++) + { + if (hdf5_h5g_iterate (group_hid, "symbol table", ¤t_item, + &dsub) <= 0) + error ("load: failed to load anonymous function handle"); + + m_local_vars[dsub.name] = dsub.tc; + } + } + + if (success) + return parse (fcn_tmp); + + return false; + +#else + + octave_unused_parameter (group_hid); + octave_unused_parameter (space_hid); + octave_unused_parameter (type_hid); + + return false; + +#endif + } + + void anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const + { + tree_print_code tpc (os); + + octave_user_function *f = m_fcn.user_function_value (); + + if (! f) + error ("invalid anonymous function handle"); + + os << "@"; + + // The parameter list should always be valid for anonymous + // functions, so we should always call accept for it, and it will + // print the parens for us. + + tree_parameter_list *p = f->parameter_list (); + + if (p) + p->accept (tpc); + + os << " "; + + tree_statement_list *b = f->body (); + + assert (b->length () == 1); + + tree_statement *s = b->front (); + + if (! s) + error ("invalid anonymous function handle"); + + assert (s->is_expression ()); + + tree_expression *e = s->expression (); + + if (! e) + error ("invalid anonymous function handle"); + + tpc.print_fcn_handle_body (e); + } + + bool + anonymous_fcn_handle::parse (const std::string& fcn_text) + { + // FIXME: If evaluation of the string gives us an anonymous function + // handle object, then why extract the function and create a new + // anonymous function object? Why not just attach the workspace + // values to the object returned by eval_string? This code is also is + // duplicated in read_mat5_binary_element in ls-mat5.cc. + + interpreter& interp = __get_interpreter__ ("anonymous_fcn_handle::parse"); + + // Set up temporary scope to use for evaluating the text that defines + // the anonymous function so that we don't pick up values of random + // variables that might be in the current scope. + + tree_evaluator& tw = interp.get_evaluator (); + tw.push_dummy_scope ("read_mat5_binary_element"); + + unwind_action act ([&tw] () { tw.pop_scope (); }); + + int parse_status; + octave_value anonymous_fcn_handle + = interp.eval_string (fcn_text, true, parse_status); + + if (parse_status != 0) + return false; + + octave_fcn_handle *fh = anonymous_fcn_handle.fcn_handle_value (); + + if (! fh) + return false; + + m_fcn = fh->fcn_val (); + + octave_user_function *uf = m_fcn.user_function_value (true); + + if (uf) + { + symbol_scope uf_scope = uf->scope (); + + if (uf_scope) + uf_scope.cache_name (m_name); + } + + return true; + } + + bool is_equal_to (const anonymous_fcn_handle& fh1, + const anonymous_fcn_handle& fh2) + { + if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ()) + return fh1.m_fcn.is_copy_of (fh2.m_fcn); + else + return false; + } } -octave_fcn_handle::octave_fcn_handle (const octave::symbol_scope& scope, - const octave_value& f, - const std::string& n) - : m_fcn (f), m_obj (), m_name (n), m_scope (scope), m_is_nested (false), - m_closure_frames (), m_local_vars (nullptr), m_dispatch_class () -{ - octave_user_function *uf = m_fcn.user_function_value (true); - - if (uf && m_name != anonymous) - { - octave::symbol_scope uf_scope = uf->scope (); - - if (uf_scope) - uf_scope.cache_name (m_name); - } - - if (uf && uf->is_nested_function () && ! uf->is_subfunction ()) - m_is_nested = true; -} - -octave_fcn_handle::octave_fcn_handle (const octave_value& f, - const std::string& n) - : m_fcn (f), m_obj (), m_name (n), m_scope (), m_is_nested (false), - m_closure_frames (), m_local_vars (nullptr), m_dispatch_class () -{ - octave_user_function *uf = m_fcn.user_function_value (true); - - if (uf && m_name != anonymous) - { - octave::symbol_scope uf_scope = uf->scope (); - - if (uf_scope) - uf_scope.cache_name (m_name); - } - - if (uf && uf->is_nested_function () && ! uf->is_subfunction ()) - m_is_nested = true; -} - -octave_fcn_handle::octave_fcn_handle (const octave_value& f, +octave_fcn_handle::octave_fcn_handle (void) + : octave_base_value (), m_rep (new octave::invalid_fcn_handle ()) +{ } + +octave_fcn_handle::octave_fcn_handle (const octave_value& fcn) + : octave_base_value (), m_rep (new octave::internal_fcn_handle (fcn)) +{ } + +octave_fcn_handle::octave_fcn_handle (const std::string& name) + : octave_base_value (), m_rep (new octave::simple_fcn_handle (name)) +{ } + +octave_fcn_handle::octave_fcn_handle (const octave_value& fcn, + const std::string& name) + : octave_base_value (), m_rep (new octave::simple_fcn_handle (fcn, name)) +{ } + +octave_fcn_handle::octave_fcn_handle (const octave_value& fcn, + const std::string& class_nm, + const std::string& meth_nm) + : octave_base_value (), + m_rep (new octave::class_simple_fcn_handle (fcn, class_nm, meth_nm)) +{ } + +octave_fcn_handle::octave_fcn_handle (const octave_value& obj, + const octave_value& fcn, + const std::string& class_nm, + const std::string& meth_nm) + : octave_base_value (), + m_rep (new octave::class_simple_fcn_handle (obj, fcn, class_nm, meth_nm)) +{ } + +octave_fcn_handle::octave_fcn_handle (const octave_value& fcn, + const std::string& name, + const std::list<std::string>& parentage) + : octave_base_value (), + m_rep (new octave::scoped_fcn_handle (fcn, name, parentage)) +{ } + +octave_fcn_handle::octave_fcn_handle (const octave_value& fcn, + const std::string& name, + const std::shared_ptr<octave::stack_frame>& closure_frames) + : octave_base_value (), + m_rep (new octave::nested_fcn_handle (fcn, name, closure_frames)) +{ } + +octave_fcn_handle::octave_fcn_handle (const octave_value& fcn, const octave::stack_frame::local_vars_map& local_vars) - : m_fcn (f), m_obj (), m_name (anonymous), m_scope (), m_is_nested (false), - m_closure_frames (), m_local_vars (new octave::stack_frame::local_vars_map (local_vars)), - m_dispatch_class () + : octave_base_value (), + m_rep (new octave::anonymous_fcn_handle (fcn, local_vars)) +{ } + +octave_fcn_handle::octave_fcn_handle (octave::base_fcn_handle *rep) + : octave_base_value (), m_rep (rep) { } octave_fcn_handle::octave_fcn_handle (const octave_fcn_handle& fh) - : octave_base_value (fh), m_fcn (fh.m_fcn), m_obj (fh.m_obj), - m_name (fh.m_name), m_scope (fh.m_scope), m_is_nested (fh.m_is_nested), - m_closure_frames (fh.m_closure_frames), - m_local_vars (fh.m_local_vars - ? new octave::stack_frame::local_vars_map (*(fh.m_local_vars)) : nullptr), - m_dispatch_class (fh.m_dispatch_class) -{ } - -octave_fcn_handle::~octave_fcn_handle (void) -{ - delete m_local_vars; -} - -octave_value_list -octave_fcn_handle::subsref (const std::string& type, - const std::list<octave_value_list>& idx, - int nargout) -{ - octave_value_list retval; - - switch (type[0]) - { - case '(': - { - int tmp_nargout = (type.length () > 1 && nargout == 0) ? 1 : nargout; - - retval = call (tmp_nargout, idx.front ()); - } - break; - - case '{': - case '.': - { - std::string tnm = type_name (); - error ("%s cannot be indexed with %c", tnm.c_str (), type[0]); - } - break; - - default: - panic_impossible (); - } - - // FIXME: perhaps there should be an - // octave_value_list::next_subsref member function? See also - // octave_builtin::subsref. - - if (idx.size () > 1) - retval = retval(0).next_subsref (nargout, type, idx); - - return retval; -} - -static void -err_invalid_fcn_handle (const std::string& name) -{ - error ("%s: invalid function handle", name.c_str ()); -} - -octave_value_list -octave_fcn_handle::call (int nargout, const octave_value_list& args) + : octave_base_value (fh) { - // FIXME: if m_name has a '.' in the name, lookup first component. If - // it is a classdef meta object, then build TYPE and IDX arguments and - // make a subsref call using them. - - octave_value fcn_to_call = m_fcn; - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_handle::call"); - - if (! fcn_to_call.is_defined ()) - { - // The following code is similar to part of - // tree_evaluator::visit_index_expression but simpler because it - // handles a more restricted case. - - octave::symbol_table& symtab = interp.get_symbol_table (); - - size_t pos = m_name.find ('.'); - - if (pos != std::string::npos) - { - // We can have one of - // - // pkg-list . fcn (args) - // pkg-list . cls . meth (args) - // cls . meth (args) - - // Evaluate package elements until we find a function, - // classdef object, or classdef_meta object that is not a - // package. An object may only appear as the first element, - // then it must be followed directly by a function name. - - size_t beg = 0; - size_t end = pos; - - std::vector<std::string> idx_elts; - - while (true) - { - end = m_name.find ('.', beg); - - idx_elts.push_back (m_name.substr (beg, end-beg)); - - if (end == std::string::npos) - break; - - beg = end+1; - } - - size_t n_elts = idx_elts.size (); - - bool have_object = false; - octave_value partial_expr_val; - - if (m_obj.is_defined ()) - { - // The first element was already defined elsewhere, - // possibly in the scope where the function handle was - // created. - - partial_expr_val = m_obj; - - if (m_obj.is_classdef_object ()) - have_object = true; - else - err_invalid_fcn_handle (m_name); - } - else - { - // Lazy evaluation. The first element was not known to be - // defined as an object in the scope where the handle was - // created. See if there is a definition in the current - // scope. - - partial_expr_val = interp.varval (idx_elts[0]); - } - - if (partial_expr_val.is_defined ()) - { - if (! partial_expr_val.is_classdef_object () || n_elts != 2) - err_invalid_fcn_handle (m_name); - - have_object = true; - } - else - partial_expr_val - = symtab.find_function (idx_elts[0], ovl (), m_scope); - - std::string type; - std::list<octave_value_list> arg_list; - - for (size_t i = 1; i < n_elts; i++) - { - if (partial_expr_val.is_package ()) - { - if (have_object) - err_invalid_fcn_handle (m_name); - - type = "."; - arg_list.push_back (ovl (idx_elts[i])); - - try - { - // Silently ignore extra output values. - - octave_value_list tmp_list - = partial_expr_val.subsref (type, arg_list, 0); - - partial_expr_val - = tmp_list.length () ? tmp_list(0) : octave_value (); - - if (partial_expr_val.is_cs_list ()) - err_invalid_fcn_handle (m_name); - - arg_list.clear (); - } - catch (octave::index_exception&) - { - err_invalid_fcn_handle (m_name); - } - } - else if (have_object || partial_expr_val.is_classdef_meta ()) - { - // Object or class name must be the next to the last - // element (it was the previous one, so if this is the - // final element, it should be a classdef method, - // but we'll let the classdef or classdef_meta subsref - // function sort that out. - - if (i != n_elts-1) - err_invalid_fcn_handle (m_name); - - type = ".("; - arg_list.push_back (ovl (idx_elts[i])); - arg_list.push_back (args); - - return partial_expr_val.subsref (type, arg_list, nargout); - } - else - err_invalid_fcn_handle (m_name); - } - - // If we get here, we must have a function to call. - - if (! partial_expr_val.is_function ()) - err_invalid_fcn_handle (m_name); - - fcn_to_call = partial_expr_val; - } - else - fcn_to_call = symtab.find_function (m_name, args, m_scope); - } - - if (! fcn_to_call.is_defined ()) - err_invalid_fcn_handle (m_name); - - octave::tree_evaluator& tw = interp.get_evaluator (); - - octave::unwind_action act2 ([&tw] () { tw.set_dispatch_class (""); }); - - tw.set_dispatch_class (m_dispatch_class); - - if (m_closure_frames) - { - if (! fcn_to_call.is_user_function ()) - { - std::string tname = fcn_to_call.type_name (); - error ("internal error: closure frames associated with '%s' object", - tname.c_str ()); - } - - octave_user_function *oct_usr_fcn = fcn_to_call.user_function_value (); - - tw.push_stack_frame (oct_usr_fcn, m_closure_frames); - - octave::unwind_action act1 ([&tw] () { tw.pop_stack_frame (); }); - - return oct_usr_fcn->execute (tw, nargout, args); - } - else if (m_local_vars) - { - if (! fcn_to_call.is_user_function ()) - { - std::string tname = fcn_to_call.type_name (); - error ("internal error: local vars associated with '%s' object", - tname.c_str ()); - } - - octave_user_function *oct_usr_fcn = fcn_to_call.user_function_value (); - - tw.push_stack_frame (oct_usr_fcn, *m_local_vars); - - octave::unwind_action act1 ([&tw] () { tw.pop_stack_frame (); }); - - return oct_usr_fcn->execute (tw, nargout, args); - } - else - { - octave_function *oct_fcn = fcn_to_call.function_value (); - - return oct_fcn->call (tw, nargout, args); - } + m_rep = fh.m_rep->clone (); } dim_vector @@ -467,273 +2499,31 @@ return dv; } -octave_function * octave_fcn_handle::function_value (bool) -{ - if (m_fcn.is_defined ()) - return m_fcn.function_value (); - - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("octave_fcn_handle::set_fcn"); - - // Cache this value so that the pointer will be valid as long as the - // function handle object is valid. - - m_generic_fcn = symtab.find_function (m_name, octave_value_list (), m_scope); - - return (m_generic_fcn.is_defined () - ? m_generic_fcn.function_value () : nullptr); -} - -octave_user_function * octave_fcn_handle::user_function_value (bool) -{ - if (m_fcn.is_defined ()) - return m_fcn.user_function_value (); - - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("octave_fcn_handle::set_fcn"); - - // Cache this value so that the pointer will be valid as long as the - // function handle object is valid. - - m_generic_fcn = symtab.find_user_function (m_name); - - return (m_generic_fcn.is_defined () - ? m_generic_fcn.user_function_value () : nullptr); -} - -// Save call stack frames for handles to nested functions. - -void -octave_fcn_handle::push_closure_context (octave::tree_evaluator& tw) -{ - m_closure_frames = tw.get_current_stack_frame (); -} - -octave_value -octave_fcn_handle::workspace (void) const -{ - if (m_name == anonymous) - { - octave_scalar_map ws; - - if (m_local_vars) - { - for (const auto& nm_val : *m_local_vars) - ws.assign (nm_val.first, nm_val.second); - } - - return ws; - } - else if (m_closure_frames) - { - return m_closure_frames->workspace (); - } - - return Cell (); -} - -bool -octave_fcn_handle::is_equal_to (const octave_fcn_handle& h) const -{ - if (m_fcn.is_defined () && h.m_fcn.is_defined ()) - return m_fcn.is_copy_of (h.m_fcn); - else if (m_fcn.is_undefined () && h.m_fcn.is_undefined ()) - return m_name == h.m_name; - else - return false; -} - -bool -octave_fcn_handle::set_fcn (const std::string& octaveroot, - const std::string& fpath) -{ - if (octaveroot.length () != 0 - && fpath.length () >= octaveroot.length () - && fpath.substr (0, octaveroot.length ()) == octaveroot - && octave::config::octave_exec_home () != octaveroot) - { - // First check if just replacing matlabroot is enough - std::string str - = (octave::config::octave_exec_home () - + fpath.substr (octaveroot.length ())); - octave::sys::file_stat fs (str); - - if (fs.exists ()) - { - size_t xpos = str.find_last_of (octave::sys::file_ops::dir_sep_chars ()); - - std::string dir_name = str.substr (0, xpos); - - octave_value ov_fcn - = octave::load_fcn_from_file (str, dir_name, "", "", m_name); - - if (ov_fcn.is_undefined ()) - error ("function handle points to non-existent function"); - - m_fcn = octave_value (new octave_fcn_handle (ov_fcn, m_name)); - } - else - { - // Next just search for it anywhere in the system path - std::list<std::string> names; - names.push_back (m_name + ".oct"); - names.push_back (m_name + ".mex"); - names.push_back (m_name + ".m"); - - octave::load_path& lp - = octave::__get_load_path__ ("octave_fcn_handle::set_fcn"); - - octave::directory_path p (lp.system_path ()); - - str = octave::sys::env::make_absolute (p.find_first_of (names)); - - size_t xpos = str.find_last_of (octave::sys::file_ops::dir_sep_chars ()); - - std::string dir_name = str.substr (0, xpos); - - octave_value ov_fcn - = octave::load_fcn_from_file (str, dir_name, "", "", m_name); - - if (ov_fcn.is_undefined ()) - error ("function handle points to non-existent function"); - - m_fcn = octave_value (new octave_fcn_handle (ov_fcn, m_name)); - } - } - else - { - if (fpath.length () > 0) - { - size_t xpos = fpath.find_last_of (octave::sys::file_ops::dir_sep_chars ()); - - std::string dir_name = fpath.substr (0, xpos); - - octave_value ov_fcn - = octave::load_fcn_from_file (fpath, dir_name, "", "", m_name); - - if (ov_fcn.is_undefined ()) - error ("function handle points to non-existent function"); - - m_fcn = octave_value (new octave_fcn_handle (ov_fcn, m_name)); - } - else - { - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("octave_fcn_handle::set_fcn"); - - m_fcn = symtab.find_function (m_name); - - if (! m_fcn.is_function ()) - error ("function handle points to non-existent function"); - } - } - - return true; -} - -octave_value -octave_fcn_handle::convert_to_str_internal (bool, bool, char type) const -{ - std::ostringstream buf; - print_raw (buf, true); - return octave_value (buf.str (), type); -} - bool octave_fcn_handle::save_ascii (std::ostream& os) { - if (m_name == anonymous) - { - if (m_fcn.is_undefined ()) - return false; - - os << m_name << "\n"; - - print_raw (os, true); - os << "\n"; - - size_t varlen = m_local_vars ? m_local_vars->size () : 0; - - if (varlen > 0) - { - os << "# length: " << varlen << "\n"; - - for (const auto& nm_val : *m_local_vars) - { - if (! save_text_data (os, nm_val.second, nm_val.first, false, 0)) - return ! os.fail (); - } - } - } - else - { - octave_function *f = function_value (); - std::string fnm = (f ? f->fcn_file_name () : ""); - - os << "# octaveroot: " << octave::config::octave_exec_home () << "\n"; - if (! fnm.empty ()) - os << "# path: " << fnm << "\n"; - os << m_name << "\n"; - } - - return true; -} - -bool -octave_fcn_handle::parse_anon_fcn_handle (const std::string& fcn_text) -{ - bool success = true; - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_handle::parse_anon_fcn_handle"); - - int parse_status; - octave_value anon_fcn_handle - = interp.eval_string (fcn_text, true, parse_status); - - if (parse_status == 0) - { - octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value (); - - if (fh) - { - m_fcn = fh->m_fcn; - - if (fh->m_local_vars) - m_local_vars = new octave::stack_frame::local_vars_map (*(fh->m_local_vars)); - - octave_user_function *uf = m_fcn.user_function_value (true); - - if (uf) - { - octave::symbol_scope uf_scope = uf->scope (); - - if (uf_scope) - uf_scope.cache_name (m_name); - } - } - else - success = false; - } - else - success = false; - - return success; + return m_rep->save_ascii (os); } bool octave_fcn_handle::load_ascii (std::istream& is) { - bool success = true; + octave::base_fcn_handle *new_rep = nullptr; + + // Read enough to detect type then create new rep object and dispatch + // to finish loading object. std::streampos pos = is.tellg (); + std::string octaveroot = extract_keyword (is, "octaveroot", true); if (octaveroot.empty ()) { is.seekg (pos); is.clear (); } + pos = is.tellg (); + std::string fpath = extract_keyword (is, "path", true); if (fpath.empty ()) { @@ -741,138 +2531,95 @@ is.clear (); } - is >> m_name; - - if (m_name == anonymous) + if (! (octaveroot.empty () || fpath.empty ())) + { + size_t len = octaveroot.size (); + if (octaveroot == fpath.substr (0, len)) + fpath = octave::config::octave_exec_home () + fpath.substr (len); + } + + pos = is.tellg (); + + std::string subtype = extract_keyword (is, "subtype", true); + if (subtype.empty ()) { - skip_preceeding_newline (is); - - std::string buf; - - if (is) - { - - // Get a line of text whitespace characters included, leaving - // newline in the stream. - buf = read_until_newline (is, true); - - } - - pos = is.tellg (); - - octave::unwind_protect_safe frame; - - // Set up temporary scope to use for evaluating the text that - // defines the anonymous function. - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_handle::load_ascii"); - - octave::tree_evaluator& tw = interp.get_evaluator (); - - tw.push_dummy_scope (buf); - frame.add_method (tw, &octave::tree_evaluator::pop_scope); - - octave_idx_type len = 0; - - if (extract_keyword (is, "length", len, true) && len >= 0) - { - if (len > 0) - { - for (octave_idx_type i = 0; i < len; i++) - { - octave_value t2; - bool dummy; - - std::string name - = read_text_data (is, "", dummy, t2, i); - - if (! is) - error ("load: failed to load anonymous function handle"); - - interp.assign (name, t2); - } - } - } + is.seekg (pos); + is.clear (); + + // We have a legacy file that can contain either an anonymous + // function or a simple function handle. + + std::string name; + is >> name; + + if (name == anonymous) + new_rep = new octave::anonymous_fcn_handle (); else - { - is.seekg (pos); - is.clear (); - } - - if (is && success) - success = parse_anon_fcn_handle (buf); - else - success = false; + new_rep = new octave::simple_fcn_handle (name, fpath, octaveroot); } else - success = set_fcn (octaveroot, fpath); - - return success; + { + // Load individual function handle types. + + if (subtype == "simple") + { + std::string name; + is >> name; + + new_rep = new octave::simple_fcn_handle (name, fpath, octaveroot); + } + else if (subtype == "scopedfunction") + { + std::string name; + is >> name; + + new_rep = new octave::scoped_fcn_handle (name, fpath, octaveroot); + } + else if (subtype == "anonymous") + new_rep = new octave::anonymous_fcn_handle (); + else if (subtype == "nested") + { + std::string name; + is >> name; + + new_rep = new octave::nested_fcn_handle (name, fpath, octaveroot); + } + else if (subtype == "classsimple") + { + std::string name; + is >> name; + + new_rep = new octave::class_simple_fcn_handle (name, fpath, octaveroot); + } + } + + if (! new_rep) + return false; + + if (! new_rep->load_ascii (is)) + { + delete new_rep; + return false; + } + + delete m_rep; + m_rep = new_rep; + + return true; } bool octave_fcn_handle::save_binary (std::ostream& os, bool save_as_floats) { - if (m_name == anonymous) - { - std::ostringstream nmbuf; - - if (m_fcn.is_undefined ()) - return false; - - size_t varlen = m_local_vars ? m_local_vars->size () : 0; - - if (varlen > 0) - nmbuf << m_name << ' ' << varlen; - else - nmbuf << m_name; - - std::string buf_str = nmbuf.str (); - int32_t tmp = buf_str.length (); - os.write (reinterpret_cast<char *> (&tmp), 4); - os.write (buf_str.c_str (), buf_str.length ()); - - std::ostringstream buf; - print_raw (buf, true); - std::string stmp = buf.str (); - tmp = stmp.length (); - os.write (reinterpret_cast<char *> (&tmp), 4); - os.write (stmp.c_str (), stmp.length ()); - - if (varlen > 0) - { - for (const auto& nm_val : *m_local_vars) - { - if (! save_binary_data (os, nm_val.second, nm_val.first, - "", 0, save_as_floats)) - return ! os.fail (); - } - } - } - else - { - std::ostringstream nmbuf; - - octave_function *f = function_value (); - std::string fnm = (f ? f->fcn_file_name () : ""); - - nmbuf << m_name << "\n" << octave::config::octave_exec_home () << "\n" << fnm; - - std::string buf_str = nmbuf.str (); - int32_t tmp = buf_str.length (); - os.write (reinterpret_cast<char *> (&tmp), 4); - os.write (buf_str.c_str (), buf_str.length ()); - } - - return true; + return m_rep->save_binary (os, save_as_floats); } bool octave_fcn_handle::load_binary (std::istream& is, bool swap, octave::mach_info::float_format fmt) { - bool success = true; + // Read enough to detect type then create new rep object and dispatch + // to finish loading object. int32_t tmp; if (! is.read (reinterpret_cast<char *> (&tmp), 4)) @@ -885,355 +2632,109 @@ // effectively not reading over file end is.read (ctmp1, tmp); ctmp1[tmp] = 0; - m_name = std::string (ctmp1); + std::string name (ctmp1); if (! is) return false; + octave::base_fcn_handle *new_rep = nullptr; + size_t anl = anonymous.length (); - if (m_name.length () >= anl && m_name.substr (0, anl) == anonymous) + if (name.length () >= anl && name.substr (0, anl) == anonymous) { - octave_idx_type len = 0; - - if (m_name.length () > anl) - { - std::istringstream nm_is (m_name.substr (anl)); - nm_is >> len; - m_name = m_name.substr (0, anl); - } - - if (! is.read (reinterpret_cast<char *> (&tmp), 4)) - return false; - if (swap) - swap_bytes<4> (&tmp); - - OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1); - // is.get (ctmp2, tmp+1, 0); caused is.eof () to be true though - // effectively not reading over file end - is.read (ctmp2, tmp); - ctmp2[tmp] = 0; - - octave::unwind_protect_safe frame; - - // Set up temporary scope to use for evaluating the text that - // defines the anonymous function. - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_handle::load_binary"); - - octave::tree_evaluator& tw = interp.get_evaluator (); - - tw.push_dummy_scope (ctmp2); - frame.add_method (tw, &octave::tree_evaluator::pop_scope); - - if (len > 0) - { - for (octave_idx_type i = 0; i < len; i++) - { - octave_value t2; - bool dummy; - std::string doc; - - std::string name - = read_binary_data (is, swap, fmt, "", dummy, t2, doc); - - if (! is) - error ("load: failed to load anonymous function handle"); - - interp.assign (name, t2); - } - } - - if (is && success) - success = parse_anon_fcn_handle (ctmp2); - else - success = false; + // Even with extra info stored in the function name, anonymous + // functions look the same. Note that NAME here may have the + // number of local variables appended. We decode that inside the + // load_binary function. + + new_rep = new octave::anonymous_fcn_handle (name); } else { + // Unpack extra info stored with the function name and load + // individual function handle types. + // FIXME: is there a better way? + std::string octaveroot; std::string fpath; - - if (m_name.find_first_of ('\n') != std::string::npos) + std::string subtype = "simple"; + + if (name.find_first_of ('\n') != std::string::npos) + { + size_t pos1 = name.find_first_of ('\n'); + size_t pos2 = name.find_first_of ('\n', pos1 + 1); + octaveroot = name.substr (pos1 + 1, pos2 - pos1 - 1); + fpath = name.substr (pos2 + 1); + name = name.substr (0, pos1); + } + + size_t pos1 = name.find ('@'); + if (pos1 != std::string::npos) { - size_t pos1 = m_name.find_first_of ('\n'); - size_t pos2 = m_name.find_first_of ('\n', pos1 + 1); - octaveroot = m_name.substr (pos1 + 1, pos2 - pos1 - 1); - fpath = m_name.substr (pos2 + 1); - m_name = m_name.substr (0, pos1); + if (name[pos1+1] == '<') + { + size_t pos2 = name.find ('>', pos1 + 2); + + if (pos2 != std::string::npos) + subtype = name.substr (pos1 + 2, pos2 - pos1 - 2); + } + + name = name.substr (0, pos1); } - success = set_fcn (octaveroot, fpath); + // Anonymous should have been handled above so it is not in the + // following list. + + if (subtype == "simple") + new_rep = new octave::simple_fcn_handle (name, fpath, octaveroot); + else if (subtype == "scopedfunction") + new_rep = new octave::scoped_fcn_handle (name, fpath, octaveroot); + else if (subtype == "nested") + new_rep = new octave::nested_fcn_handle (name, fpath, octaveroot); + else if (subtype == "classsimple") + new_rep = new octave::class_simple_fcn_handle (name, fpath, octaveroot); } - return success; + if (! new_rep) + return false; + + if (! new_rep->load_binary (is, swap, fmt)) + { + delete new_rep; + return false; + } + + delete m_rep; + m_rep = new_rep; + + return true; } bool octave_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats) { -#if defined (HAVE_HDF5) - - bool retval = true; - - hid_t group_hid = -1; -#if defined (HAVE_HDF5_18) - group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT, - octave_H5P_DEFAULT); -#else - group_hid = H5Gcreate (loc_id, name, 0); -#endif - if (group_hid < 0) - return false; - - hid_t space_hid, data_hid, type_hid; - space_hid = data_hid = type_hid = -1; - - // attach the type of the variable - type_hid = H5Tcopy (H5T_C_S1); - H5Tset_size (type_hid, m_name.length () + 1); - if (type_hid < 0) - { - H5Gclose (group_hid); - return false; - } - - OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2); - hdims[0] = 0; - hdims[1] = 0; - space_hid = H5Screate_simple (0, hdims, nullptr); - if (space_hid < 0) - { - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } -#if defined (HAVE_HDF5_18) - data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, - octave_H5P_DEFAULT, octave_H5P_DEFAULT, - octave_H5P_DEFAULT); -#else - data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid, - octave_H5P_DEFAULT); -#endif - if (data_hid < 0 - || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, m_name.c_str ()) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - H5Dclose (data_hid); - - if (m_name == anonymous) - { - std::ostringstream buf; - print_raw (buf, true); - std::string stmp = buf.str (); - - // attach the type of the variable - H5Tset_size (type_hid, stmp.length () + 1); - if (type_hid < 0) - { - H5Sclose (space_hid); - H5Gclose (group_hid); - return false; - } - -#if defined (HAVE_HDF5_18) - data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid, - octave_H5P_DEFAULT, octave_H5P_DEFAULT, - octave_H5P_DEFAULT); -#else - data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid, - octave_H5P_DEFAULT); -#endif - if (data_hid < 0 - || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, stmp.c_str ()) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - - H5Dclose (data_hid); - - size_t varlen = m_local_vars ? m_local_vars->size () : 0; - - if (varlen > 0) - { - hid_t as_id = H5Screate (H5S_SCALAR); - - if (as_id >= 0) - { -#if defined (HAVE_HDF5_18) - hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE", - H5T_NATIVE_IDX, as_id, - octave_H5P_DEFAULT, octave_H5P_DEFAULT); - -#else - hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE", - H5T_NATIVE_IDX, as_id, octave_H5P_DEFAULT); -#endif - - if (a_id >= 0) - { - retval = (H5Awrite (a_id, H5T_NATIVE_IDX, &varlen) >= 0); - - H5Aclose (a_id); - } - else - retval = false; - - H5Sclose (as_id); - } - else - retval = false; -#if defined (HAVE_HDF5_18) - data_hid = H5Gcreate (group_hid, "symbol table", - octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else - data_hid = H5Gcreate (group_hid, "symbol table", 0); -#endif - if (data_hid < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - - for (const auto& nm_val : *m_local_vars) - { - if (! add_hdf5_data (data_hid, nm_val.second, nm_val.first, - "", false, save_as_floats)) - break; - } - - H5Gclose (data_hid); - } - } - else - { - std::string octaveroot = octave::config::octave_exec_home (); - - octave_function *f = function_value (); - std::string fpath = (f ? f->fcn_file_name () : ""); - - H5Sclose (space_hid); - hdims[0] = 1; - hdims[1] = octaveroot.length (); - space_hid = H5Screate_simple (0, hdims, nullptr); - if (space_hid < 0) - { - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - - H5Tclose (type_hid); - type_hid = H5Tcopy (H5T_C_S1); - H5Tset_size (type_hid, octaveroot.length () + 1); -#if defined (HAVE_HDF5_18) - hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT", - type_hid, space_hid, octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else - hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT", - type_hid, space_hid, octave_H5P_DEFAULT); -#endif - - if (a_id >= 0) - { - retval = (H5Awrite (a_id, type_hid, octaveroot.c_str ()) >= 0); - - H5Aclose (a_id); - } - else - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - - H5Sclose (space_hid); - hdims[0] = 1; - hdims[1] = fpath.length (); - space_hid = H5Screate_simple (0, hdims, nullptr); - if (space_hid < 0) - { - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - - H5Tclose (type_hid); - type_hid = H5Tcopy (H5T_C_S1); - H5Tset_size (type_hid, fpath.length () + 1); - -#if defined (HAVE_HDF5_18) - a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid, - octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else - a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid, octave_H5P_DEFAULT); -#endif - - if (a_id >= 0) - { - retval = (H5Awrite (a_id, type_hid, fpath.c_str ()) >= 0); - - H5Aclose (a_id); - } - else - retval = false; - } - - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - - return retval; - -#else - octave_unused_parameter (loc_id); - octave_unused_parameter (name); - octave_unused_parameter (save_as_floats); - - warn_save ("hdf5"); - - return false; -#endif + return m_rep->save_hdf5 (loc_id, name, save_as_floats); } bool -octave_fcn_handle::load_hdf5 (octave_hdf5_id loc_id, const char *name) +octave_fcn_handle::load_hdf5 (octave_hdf5_id loc_id, const char *name_arg) { #if defined (HAVE_HDF5) - bool success = true; - - hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id; - hsize_t rank; - int slen; - #if defined (HAVE_HDF5_18) - group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT); + hid_t group_hid = H5Gopen (loc_id, name_arg, octave_H5P_DEFAULT); #else - group_hid = H5Gopen (loc_id, name); + hid_t group_hid = H5Gopen (loc_id, name_arg); #endif if (group_hid < 0) return false; #if defined (HAVE_HDF5_18) - data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT); + hid_t data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT); #else - data_hid = H5Dopen (group_hid, "nm"); + hid_t data_hid = H5Dopen (group_hid, "nm"); #endif if (data_hid < 0) @@ -1242,8 +2743,8 @@ return false; } - type_hid = H5Dget_type (data_hid); - type_class_hid = H5Tget_class (type_hid); + hid_t type_hid = H5Dget_type (data_hid); + hid_t type_class_hid = H5Tget_class (type_hid); if (type_class_hid != H5T_STRING) { @@ -1253,8 +2754,8 @@ return false; } - space_hid = H5Dget_space (data_hid); - rank = H5Sget_simple_extent_ndims (space_hid); + hid_t space_hid = H5Dget_space (data_hid); + hsize_t rank = H5Sget_simple_extent_ndims (space_hid); if (rank != 0) { @@ -1265,7 +2766,7 @@ return false; } - slen = H5Tget_size (type_hid); + int slen = H5Tget_size (type_hid); if (slen < 0) { H5Sclose (space_hid); @@ -1278,7 +2779,7 @@ OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen); // create datatype for (null-terminated) string to read into: - st_id = H5Tcopy (H5T_C_S1); + hid_t st_id = H5Tcopy (H5T_C_S1); H5Tset_size (st_id, slen); if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL, @@ -1294,260 +2795,95 @@ } H5Tclose (st_id); H5Dclose (data_hid); - m_name = nm_tmp; - - if (m_name == anonymous) + + std::string name (nm_tmp); + + octave::base_fcn_handle *new_rep = nullptr; + + if (name == anonymous) { -#if defined (HAVE_HDF5_18) - data_hid = H5Dopen (group_hid, "fcn", octave_H5P_DEFAULT); -#else - data_hid = H5Dopen (group_hid, "fcn"); -#endif - - if (data_hid < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - - H5Tclose (type_hid); - type_hid = H5Dget_type (data_hid); - type_class_hid = H5Tget_class (type_hid); - - if (type_class_hid != H5T_STRING) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - H5Sclose (space_hid); - space_hid = H5Dget_space (data_hid); - rank = H5Sget_simple_extent_ndims (space_hid); - - if (rank != 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - slen = H5Tget_size (type_hid); - if (slen < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - OCTAVE_LOCAL_BUFFER (char, fcn_tmp, slen); - - // create datatype for (null-terminated) string to read into: - st_id = H5Tcopy (H5T_C_S1); - H5Tset_size (st_id, slen); - - if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, fcn_tmp) - < 0) - { - H5Tclose (st_id); - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - H5Tclose (st_id); - H5Dclose (data_hid); - - octave_idx_type len = 0; - - // we have to pull some shenanigans here to make sure - // HDF5 doesn't print out all sorts of error messages if we - // call H5Aopen for a non-existing attribute - - H5E_auto_t err_func; - void *err_func_data; - - // turn off error reporting temporarily, but save the error - // reporting function: -#if defined (HAVE_HDF5_18) - H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data); - H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr); -#else - H5Eget_auto (&err_func, &err_func_data); - H5Eset_auto (nullptr, nullptr); -#endif - - hid_t attr_id = H5Aopen_name (group_hid, "SYMBOL_TABLE"); - - if (attr_id >= 0) - { - if (H5Aread (attr_id, H5T_NATIVE_IDX, &len) < 0) - success = false; - - H5Aclose (attr_id); - } - - // restore error reporting: -#if defined (HAVE_HDF5_18) - H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data); -#else - H5Eset_auto (err_func, err_func_data); -#endif - - octave::unwind_protect_safe frame; - - // Set up temporary scope to use for evaluating the text that - // defines the anonymous function. - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_handle::load_hdf5"); - - octave::tree_evaluator& tw = interp.get_evaluator (); - - tw.push_dummy_scope (fcn_tmp); - frame.add_method (tw, &octave::tree_evaluator::pop_scope); - - if (len > 0 && success) - { - hsize_t num_obj = 0; -#if defined (HAVE_HDF5_18) - data_hid = H5Gopen (group_hid, "symbol table", octave_H5P_DEFAULT); -#else - data_hid = H5Gopen (group_hid, "symbol table"); -#endif - H5Gget_num_objs (data_hid, &num_obj); - H5Gclose (data_hid); - - if (num_obj != static_cast<hsize_t> (len)) - error ("load: failed to load anonymous function handle"); - - hdf5_callback_data dsub; - int current_item = 0; - for (octave_idx_type i = 0; i < len; i++) - { - if (hdf5_h5g_iterate (group_hid, "symbol table", ¤t_item, - &dsub) <= 0) - error ("load: failed to load anonymous function handle"); - - interp.assign (dsub.name, dsub.tc); - } - } - - if (success) - success = parse_anon_fcn_handle (fcn_tmp); + // Even with extra info stored in the function name, anonymous + // functions look the same. + + new_rep = new octave::anonymous_fcn_handle (); } else { + // Unpack extra info stored with the function name and load + // individual function handle types. + // FIXME: is there a better way? + std::string octaveroot; std::string fpath; - - // we have to pull some shenanigans here to make sure - // HDF5 doesn't print out all sorts of error messages if we - // call H5Aopen for a non-existing attribute - - H5E_auto_t err_func; - void *err_func_data; - - // turn off error reporting temporarily, but save the error - // reporting function: -#if defined (HAVE_HDF5_18) - H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data); - H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr); -#else - H5Eget_auto (&err_func, &err_func_data); - H5Eset_auto (nullptr, nullptr); -#endif - - hid_t attr_id = H5Aopen_name (group_hid, "OCTAVEROOT"); - if (attr_id >= 0) + std::string subtype = "simple"; + + if (name.find_first_of ('\n') != std::string::npos) { - H5Tclose (type_hid); - type_hid = H5Aget_type (attr_id); - type_class_hid = H5Tget_class (type_hid); - - if (type_class_hid != H5T_STRING) - success = false; - else + size_t pos1 = name.find_first_of ('\n'); + size_t pos2 = name.find_first_of ('\n', pos1 + 1); + octaveroot = name.substr (pos1 + 1, pos2 - pos1 - 1); + fpath = name.substr (pos2 + 1); + name = name.substr (0, pos1); + } + + size_t pos1 = name.find ('@'); + if (pos1 != std::string::npos) + { + if (name[pos1+1] == '<') { - slen = H5Tget_size (type_hid); - st_id = H5Tcopy (H5T_C_S1); - H5Tset_size (st_id, slen); - OCTAVE_LOCAL_BUFFER (char, root_tmp, slen); - - if (H5Aread (attr_id, st_id, root_tmp) < 0) - success = false; - else - octaveroot = root_tmp; - - H5Tclose (st_id); + size_t pos2 = name.find ('>', pos1 + 2); + + if (pos2 != std::string::npos) + subtype = name.substr (pos1 + 2, pos2 - pos1 - 2); } - H5Aclose (attr_id); + name = name.substr (0, pos1); } - if (success) + // Anonymous should have been handled above so it is not in the + // following list. + + if (subtype == "simple") + new_rep = new octave::simple_fcn_handle (name, fpath, octaveroot); + else if (subtype == "scopedfunction") + new_rep = new octave::scoped_fcn_handle (name, fpath, octaveroot); + else if (subtype == "nested") + new_rep = new octave::nested_fcn_handle (name, fpath, octaveroot); + else if (subtype == "classsimple") + new_rep = new octave::class_simple_fcn_handle (name, fpath, octaveroot); + } + + bool status = false; + + if (new_rep) + { + if (new_rep->load_hdf5 (group_hid, space_hid, type_hid)) { - attr_id = H5Aopen_name (group_hid, "FILE"); - if (attr_id >= 0) - { - H5Tclose (type_hid); - type_hid = H5Aget_type (attr_id); - type_class_hid = H5Tget_class (type_hid); - - if (type_class_hid != H5T_STRING) - success = false; - else - { - slen = H5Tget_size (type_hid); - st_id = H5Tcopy (H5T_C_S1); - H5Tset_size (st_id, slen); - OCTAVE_LOCAL_BUFFER (char, path_tmp, slen); - - if (H5Aread (attr_id, st_id, path_tmp) < 0) - success = false; - else - fpath = path_tmp; - - H5Tclose (st_id); - } - - H5Aclose (attr_id); - } + delete m_rep; + m_rep = new_rep; + status = true; } - - // restore error reporting: -#if defined (HAVE_HDF5_18) - H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data); -#else - H5Eset_auto (err_func, err_func_data); -#endif - - success = (success ? set_fcn (octaveroot, fpath) : success); + else + delete new_rep; } + // FIXME: manage these with an unwind_action object? + H5Tclose (type_hid); H5Sclose (space_hid); H5Gclose (group_hid); - return success; + return status; #else + octave_unused_parameter (loc_id); - octave_unused_parameter (name); + octave_unused_parameter (name_arg); warn_load ("hdf5"); return false; + #endif } @@ -1650,57 +2986,39 @@ void octave_fcn_handle::print_raw (std::ostream& os, bool pr_as_read_syntax) const { - bool printed = false; - - if (m_name == anonymous) - { - octave::tree_print_code tpc (os); - - // FCN is const because this member function is, so we can't - // use it to call user_function_value, so we make a copy first. - - octave_value ftmp = m_fcn; - - octave_user_function *f = ftmp.user_function_value (); - - if (f) - { - os << "@"; - - // The parameter list should always be valid for anonymous - // functions, so we should always call accept for it, and it - // will print the parens for us. - - octave::tree_parameter_list *p = f->parameter_list (); - - if (p) - p->accept (tpc); - - os << " "; - - octave::tree_statement_list *b = f->body (); - - assert (b->length () == 1); - - octave::tree_statement *s = b->front (); - - if (s) - { - assert (s->is_expression ()); - - octave::tree_expression *e = s->expression (); - - if (e) - tpc.print_fcn_handle_body (e); - } - - printed = true; - } - } - - if (! printed) - octave_print_internal (os, '@' + m_name, pr_as_read_syntax, - current_print_indent_level ()); + m_rep->print_raw (os, pr_as_read_syntax, current_print_indent_level ()); +} + +bool +is_equal_to (const octave_fcn_handle& fh1, const octave_fcn_handle& fh2) +{ + // FIXME: Maybe there is a better way? Possibly by using typeid or + // typeindex? + + // Don't include invalid_fcn_handle in the list of types to compare. + // Consider them to be like NaN values so comparisons between any two + // invalid handles are always false. + + if (fh1.is_internal () && fh2.is_internal ()) + return is_equal_to (*dynamic_cast<octave::internal_fcn_handle *> (fh1.get_rep ()), + *dynamic_cast<octave::internal_fcn_handle *> (fh2.get_rep ())); + else if (fh1.is_simple () && fh2.is_simple ()) + return is_equal_to (*dynamic_cast<octave::simple_fcn_handle *> (fh1.get_rep ()), + *dynamic_cast<octave::simple_fcn_handle *> (fh2.get_rep ())); + else if (fh1.is_scoped () && fh2.is_scoped ()) + return is_equal_to (*dynamic_cast<octave::scoped_fcn_handle *> (fh1.get_rep ()), + *dynamic_cast<octave::scoped_fcn_handle *> (fh2.get_rep ())); + else if (fh1.is_nested () && fh2.is_nested ()) + return is_equal_to (*dynamic_cast<octave::nested_fcn_handle *> (fh1.get_rep ()), + *dynamic_cast<octave::nested_fcn_handle *> (fh2.get_rep ())); + else if (fh1.is_class_simple () && fh2.is_class_simple ()) + return is_equal_to (*dynamic_cast<octave::class_simple_fcn_handle *> (fh1.get_rep ()), + *dynamic_cast<octave::class_simple_fcn_handle *> (fh2.get_rep ())); + else if (fh1.is_anonymous () && fh2.is_anonymous ()) + return is_equal_to (*dynamic_cast<octave::anonymous_fcn_handle *> (fh1.get_rep ()), + *dynamic_cast<octave::anonymous_fcn_handle *> (fh2.get_rep ())); + else + return false; } namespace octave @@ -1772,62 +3090,7 @@ octave_fcn_handle *fh = args(0).xfcn_handle_value ("functions: FCN_HANDLE argument must be a function handle object"); - octave_function *fcn = (fh ? fh->function_value () : nullptr); - - if (! fcn) - error ("functions: FCN_HANDLE is not a valid function handle object"); - - octave_scalar_map m; - - std::string fh_nm = fh->fcn_name (); - - if (fh_nm == octave_fcn_handle::anonymous) - { - std::ostringstream buf; - fh->print_raw (buf); - m.setfield ("function", buf.str ()); - - m.setfield ("type", "anonymous"); - } - else - { - m.setfield ("function", fh_nm); - - if (fcn->is_subfunction ()) - { - m.setfield ("type", "subfunction"); - Cell parentage (dim_vector (1, 2)); - parentage.elem (0) = fh_nm; - parentage.elem (1) = fcn->parent_fcn_name (); - m.setfield ("parentage", octave_value (parentage)); - } - else if (fcn->is_private_function ()) - m.setfield ("type", "private"); - else if (fh->is_nested ()) - m.setfield ("type", "nested"); - else - m.setfield ("type", "simple"); - } - - std::string nm = fcn->fcn_file_name (); - - if (fh_nm == octave_fcn_handle::anonymous) - { - m.setfield ("file", nm); - - m.setfield ("workspace", fh->workspace ()); - } - else if (fcn->is_user_function () || fcn->is_user_script ()) - { - octave_function *fu = fh->function_value (); - m.setfield ("file", fu->fcn_file_name ()); - - m.setfield ("workspace", fh->workspace ()); - } - else - m.setfield ("file", ""); - - return ovl (m); + return ovl (fh->info ()); } DEFUN (func2str, args, , @@ -1850,7 +3113,7 @@ std::string fh_nm = fh->fcn_name (); - if (fh_nm == octave_fcn_handle::anonymous) + if (fh->is_anonymous ()) { std::ostringstream buf; @@ -1888,7 +3151,7 @@ if (nm[0] == '@') { - // Unlike the anon_fcn_handle::parse method, don't set up + // Unlike the anonymous_fcn_handle::parse method, don't set up // temporary scope to use for evaluating the text that defines // the anonymous function. Here we want //
--- a/libinterp/octave-value/ov-fcn-handle.h Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/octave-value/ov-fcn-handle.h Wed Apr 29 14:10:27 2020 -0400 @@ -32,6 +32,7 @@ #include <list> #include <string> +#include "oct-map.h" #include "ov-base.h" #include "ov-fcn.h" #include "ov-typeinfo.h" @@ -42,9 +43,118 @@ { class interpreter; class tree_evaluator; -} + + // Function handles. + + class base_fcn_handle + { + public: + + base_fcn_handle (const std::string& name = "", + const std::string& file = "") + : m_name (name), m_file (file) + { } + + base_fcn_handle (const base_fcn_handle&) = default; + + virtual ~base_fcn_handle (void) = default; + + virtual base_fcn_handle * clone (void) const = 0; + + virtual std::string type (void) const = 0; + + virtual bool is_internal (void) const { return false; } + + virtual bool is_simple (void) const { return false; } + + virtual bool is_scoped (void) const { return false; } + + virtual bool is_nested (void) const { return false; } + + virtual bool is_class_simple (void) const { return false; } + + virtual bool is_anonymous (void) const { return false; } + + std::string fcn_name (void) const { return m_name; } + + std::string file (void) const { return m_file; } + + octave_value_list + subsref (const std::string& type, const std::list<octave_value_list>& idx, + int nargout); + + virtual octave_value_list + call (int nargout, const octave_value_list& args) = 0; + + // FIXME: These must go away. They don't do the right thing for + // scoping or overloads. + virtual octave_function * function_value (bool = false) + { + return nullptr; + } + + virtual octave_user_function * user_function_value (bool = false) + { + return nullptr; + } -// Function handles. + virtual octave_value fcn_val (void) { return octave_value (); } + + virtual octave_value workspace (void) const { return octave_value (); } + + // Should be const. + virtual octave_scalar_map info (void) { return octave_scalar_map (); } + + virtual void set_dispatch_class (const std::string& /*class_name*/) { } + + virtual std::string get_dispatch_class (void) const { return ""; } + + octave_value convert_to_str_internal (bool pad, bool force, char type) const; + + virtual bool save_ascii (std::ostream& os); + + virtual bool load_ascii (std::istream& is); + + virtual bool save_binary (std::ostream& os, bool save_as_floats); + + virtual bool load_binary (std::istream& is, bool swap, + mach_info::float_format fmt); + + virtual bool save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool save_as_floats); + + virtual bool load_hdf5 (octave_hdf5_id& group_hid, + octave_hdf5_id& space_hid, + octave_hdf5_id& type_hid); + + virtual void print_raw (std::ostream&, bool /*pr_as_read_syntax*/, + int /*current_print_indent_level*/) const + { } + + // Function handles are printed without a newline by default. + virtual bool print_as_scalar (void) const { return true; } + + virtual bool + set_fcn (const std::string& /*octaveroot*/, const std::string& /*fpath*/) + { + return false; + } + + protected: + + void warn_load (const char *file_type) const; + void warn_save (const char *file_type) const; + + void unimplemented (const char *op, const char *fmt) const; + + // The name of the handle, not including the "@", or the text of the + // anonymous function. + std::string m_name; + + // The name of the file where the named function was defined. + std::string m_file; + }; +} class OCTINTERP_API @@ -54,42 +164,56 @@ static const std::string anonymous; - octave_fcn_handle (void) - : m_fcn (), m_obj (), m_name (), m_scope (), m_is_nested (false), - m_closure_frames (), m_local_vars (nullptr), m_dispatch_class () - { } + // Creates an invalid function handle. Used to create generic + // function handle objects when loading function handles. Further + // dispatch happens in the octave_fcn_handle load/save functions. + octave_fcn_handle (void); - // OCTAVE_DEPRECATED (6, "foobar-1") + // Create a handle to a built-in or internal function. + octave_fcn_handle (const octave_value& fcn); + + // Create a simple function handle that is not bound to a function. + // Lookup happens when a function call is attempted. octave_fcn_handle (const std::string& name); - // OCTAVE_DEPRECATED (6, "foobar-2") - octave_fcn_handle (const octave::symbol_scope& scope, - const std::string& name); + // Create a simple function handle that is bound to a function. + octave_fcn_handle (const octave_value& fcn, const std::string& name); + + // Create a function handle bound to a class method. + octave_fcn_handle (const octave_value& fcn, const std::string& class_nm, + const std::string& meth_nm); - // OCTAVE_DEPRECATED (6, "foobar-3") - octave_fcn_handle (const octave::symbol_scope& scope, - const octave_value& f, const std::string& name); + // Create a function handle bound to a class method. + octave_fcn_handle (const octave_value& obj, const octave_value& fcn, + const std::string& class_nm, + const std::string& meth_nm); - // OCTAVE_DEPRECATED (6, "foobar-4") - octave_fcn_handle (const octave_value& f, const std::string& name); - - // OCTAVE_DEPRECATED (6, "foobar-5") + // Create a function handle bound to a scoped function. octave_fcn_handle (const octave_value& fcn, const std::string& name, const std::list<std::string>& parentage); - // OCTAVE_DEPRECATED (6, "foobar-6") + // Create a handle to a nested function. + octave_fcn_handle (const octave_value& fcn, const std::string& name, + const std::shared_ptr<octave::stack_frame>& closure_frames); + + // Create an anonymous function handle with local variable values + // provided in LOCAL_VARS. octave_fcn_handle (const octave_value& fcn, const octave::stack_frame::local_vars_map& local_vars); - // OCTAVE_DEPRECATED (6, "foobar-7") octave_fcn_handle (const octave_fcn_handle& fh); - ~octave_fcn_handle (void); + ~octave_fcn_handle (void) = default; octave_base_value * clone (void) const - { return new octave_fcn_handle (*this); } + { + return new octave_fcn_handle (*this); + } + octave_base_value * empty_clone (void) const - { return new octave_fcn_handle (); } + { + return new octave_fcn_handle (); + } // We don't need to override all three forms of subsref. The using // declaration will avoid warnings about partially-overloaded virtual @@ -105,43 +229,71 @@ octave_value_list subsref (const std::string& type, const std::list<octave_value_list>& idx, - int nargout); + int nargout) + { + return m_rep->subsref (type, idx, nargout); + } + + octave_value_list call (int nargout, const octave_value_list& args); bool is_defined (void) const { return true; } bool is_function_handle (void) const { return true; } - bool is_anonymous (void) const { return m_name == anonymous; } + bool is_internal (void) const { return m_rep->is_internal (); } + + bool is_simple (void) const { return m_rep->is_simple (); } + + bool is_scoped (void) const { return m_rep->is_scoped (); } - bool is_nested (void) const { return m_is_nested; } + bool is_nested (void) const { return m_rep->is_nested (); } + + bool is_class_simple (void) const { return m_rep->is_class_simple (); } + + bool is_anonymous (void) const { return m_rep->is_anonymous (); } dim_vector dims (void) const; // FIXME: These must go away. They don't do the right thing for // scoping or overloads. - octave_function * function_value (bool = false); - octave_user_function * user_function_value (bool = false); + octave_function * function_value (bool = false) + { + return m_rep->function_value (); + } + + octave_user_function * user_function_value (bool = false) + { + return m_rep->user_function_value (); + } octave_fcn_handle * fcn_handle_value (bool = false) { return this; } - octave_value fcn_val (void) const { return m_fcn; } + octave_value fcn_val (void) { return m_rep->fcn_val (); } - std::string fcn_name (void) const { return m_name; } + // FCN_NAME should be eliminated. + std::string fcn_name (void) const { return m_rep->fcn_name (); } - void push_closure_context (octave::tree_evaluator& tw); + octave_value workspace (void) const + { + return m_rep->workspace (); + } - octave_value workspace (void) const; + octave_scalar_map info (void) { return m_rep->info (); } void set_dispatch_class (const std::string& class_name) { - m_dispatch_class = class_name; + m_rep->set_dispatch_class (class_name); } - std::string get_dispatch_class (void) const { return m_dispatch_class; } + std::string get_dispatch_class (void) const + { + return m_rep->get_dispatch_class (); + } - bool is_equal_to (const octave_fcn_handle&) const; - - octave_value convert_to_str_internal (bool pad, bool force, char type) const; + octave_value convert_to_str_internal (bool pad, bool force, char type) const + { + return m_rep->convert_to_str_internal (pad, force, type); + } bool save_ascii (std::ostream& os); @@ -161,57 +313,24 @@ void print_raw (std::ostream& os, bool pr_as_read_syntax = false) const; // Simple function handles are printed without a newline. - bool print_as_scalar (void) const { return m_name != anonymous; } + bool print_as_scalar (void) const { return m_rep->print_as_scalar (); } + + friend bool + is_equal_to (const octave_fcn_handle& fh1, const octave_fcn_handle& fh2); private: - bool set_fcn (const std::string& octaveroot, const std::string& fpath); + octave::base_fcn_handle *m_rep; + + octave_fcn_handle (octave::base_fcn_handle *rep); + + octave::base_fcn_handle * get_rep (void) const { return m_rep; } DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA - -protected: - - // The function we are handling (this should be valid for handles to - // anonymous functions and some other special cases). Otherwise, we - // perform dynamic lookup based on the name of the function we are - // handling and the scope where the function handle object was created. - octave_value m_fcn; - - // If defined, this is the classdef object corresponding to the - // classdef method we are handling. - octave_value m_obj; - - // The function we would find without considering argument types. We - // cache this value so that the function_value and user_function_value - // methods may continue to work. - octave_value m_generic_fcn; - - // The name of the handle, not including the "@". - std::string m_name; +}; - // The scope where this object was defined. - octave::symbol_scope m_scope; - - // TRUE means this is a handle to a nested function. - bool m_is_nested; - - // Saved stack frames for handles to nested functions. This allows us - // to access non-locals and other context info when calling nested - // functions indirectly through handles. - std::shared_ptr<octave::stack_frame> m_closure_frames; - - // List of captured variable values for anonymous fucntions. - octave::stack_frame::local_vars_map *m_local_vars; - - // The name of the class in which this handle was created, if any. - // Used to determine access permission when the referenced function is - // called. - std::string m_dispatch_class; - - bool parse_anon_fcn_handle (const std::string& fcn_text); - - virtual octave_value_list call (int nargout, const octave_value_list& args); -}; +extern bool +is_equal_to (const octave_fcn_handle& fh1, const octave_fcn_handle& fh2); namespace octave {
--- a/libinterp/octave-value/ov-fcn.h Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/octave-value/ov-fcn.h Wed Apr 29 14:10:27 2020 -0400 @@ -104,6 +104,10 @@ virtual int call_depth (void) const { return 0; } + virtual bool is_nested_function (void) const { return false; } + + virtual bool is_parent_function (void) const { return false; } + virtual bool is_subfunction (void) const { return false; } bool is_class_constructor (const std::string& cname = "") const
--- a/libinterp/operators/op-fcn.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/operators/op-fcn.cc Wed Apr 29 14:10:27 2020 -0400 @@ -40,7 +40,7 @@ const octave_fcn_handle& v1 = dynamic_cast<const octave_fcn_handle&> (a1); const octave_fcn_handle& v2 = dynamic_cast<const octave_fcn_handle&> (a2); - return v1.is_equal_to (v2); + return is_equal_to (v1, v2); } DEFBINOP (ne, fcn_handle, fcn_handle) @@ -48,7 +48,7 @@ const octave_fcn_handle& v1 = dynamic_cast<const octave_fcn_handle&> (a1); const octave_fcn_handle& v2 = dynamic_cast<const octave_fcn_handle&> (a2); - return ! v1.is_equal_to (v2); + return ! is_equal_to (v1, v2); } void
--- a/libinterp/parse-tree/pt-eval.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/parse-tree/pt-eval.cc Wed Apr 29 14:10:27 2020 -0400 @@ -1003,28 +1003,207 @@ return name; } + // Creates a function handle that takes into account the context, + // finding local, nested, private, or sub functions. + octave_value tree_evaluator::make_fcn_handle (const std::string& name) { octave_value retval; + // The str2func function can create a function handle with the name + // of an operator (for example, "+"). If so, it is converted to the + // name of the corresponding function ("+" -> "plus") and we create + // a simple function handle using that name. + std::string fcn_name = get_operator_function_name (name); - if (fcn_name != name) - return octave_value (new octave_fcn_handle (fcn_name)); + // If FCN_NAME is different from NAME, then NAME is an operator. As + // of version 2020a, Matlab apparently uses the function name + // corresponding to the operator to search for private and local + // functions in the current scope but not(!) nested functions. + + bool name_is_operator = fcn_name != name; + + size_t pos = fcn_name.find ('.'); + + if (pos != std::string::npos) + { + // Recognize (some of? which ones?) the following cases + // and create something other than a simple function handle? + // Should we just be checking for the last two when the first + // element of the dot-separated list is an object? If so, then + // should this syntax be limited to a dot-separated list with + // exactly two elements? + // + // object . method + // object . static-method + // + // Code to do that duplicates some of simple_fcn_handle::call. + + // Only accept expressions that contain one '.' separator. + + // FIXME: The logic here is a bit complicated. Is there a good + // way to simplify it? + + std::string meth_nm = fcn_name.substr (pos+1); + + if (meth_nm.find ('.') == std::string::npos) + { + std::string obj_nm = fcn_name.substr (0, pos); + + // If obj_nm is an object in the current scope with a + // method named meth_nm, create a classsimple handle. + + octave_value object = varval (obj_nm); + + if (object.is_defined () && object.is_classdef_object ()) + { + octave_classdef *cdef = object.classdef_object_value (); + + if (cdef) + { + std::string class_nm = cdef->class_name (); + + cdef_object cdef_obj = cdef->get_object (); + + cdef_class cls = cdef_obj.get_class (); + + cdef_method meth = cls.find_method (meth_nm); + + if (meth.ok ()) + { + // If the method we found is static, create a + // new function name from the class name and + // method name and create a simple function + // handle below. Otherwise, create a class + // simple function handle. + + if (meth.is_static ()) + fcn_name = class_nm + '.' + meth_nm; + else + { + octave_value meth_fcn = meth.get_function (); + + octave_fcn_handle *fh + = new octave_fcn_handle (object, meth_fcn, + class_nm, meth_nm); + + return octave_value (fh); + } + } + } + } + } + + // We didn't match anything above, so create handle to SIMPLE + // package function or static class method. Function resolution + // is performed when the handle is used. + + return octave_value (new octave_fcn_handle (fcn_name)); + } + + // If the function name refers to a sub/local/private function or a + // class method/constructor, create scoped function handle that is + // bound to that function. Use the same precedence list as + // fcn_info::find but limit search to the following types of + // functions: + // + // nested functions (and subfunctions) + // local functions in the current file + // private function + // class method + // + // For anything else we create a simple function handle that will be + // resolved dynamically in the scope where it is evaluated. symbol_scope curr_scope = get_current_scope (); - // FCN_HANDLE: CONTEXT DEPENDENT - octave_fcn_handle *fh = new octave_fcn_handle (curr_scope, name); - - std::string dispatch_class; - - if (is_class_method_executing (dispatch_class) - || is_class_constructor_executing (dispatch_class)) - fh->set_dispatch_class (dispatch_class); - - return octave_value (fh); + symbol_table& symtab = m_interpreter.get_symbol_table (); + + if (curr_scope) + { + octave_value ov_fcn + = symtab.find_scoped_function (fcn_name, curr_scope); + + if (ov_fcn.is_defined ()) + { + octave_function *fcn = ov_fcn.function_value (); + + if (fcn->is_nested_function ()) + { + if (! name_is_operator) + { + // Get current stack frame and return handle to nested + // function. + + std::shared_ptr<stack_frame> frame + = m_call_stack.get_current_stack_frame (); + + octave_fcn_handle *fh + = new octave_fcn_handle (ov_fcn, fcn_name, frame); + + return octave_value (fh); + } + } + else if (fcn->is_subfunction () + /* || fcn->is_localfunction () */ + || fcn->is_private_function ()) + { + // Create handle to SCOPED function (sub/local function + // or private function). + + std::list<std::string> parentage = fcn->parent_fcn_names (); + + octave_fcn_handle *fh + = new octave_fcn_handle (ov_fcn, fcn_name, parentage); + + return octave_value (fh); + } + } + + // If name is operator, we are in Fstr2func, so skip the stack + // frame for that function. + + bool skip_first = name_is_operator; + octave_function *curr_fcn = current_function (skip_first); + + if (curr_fcn && (curr_fcn->is_class_method () + || curr_fcn->is_class_constructor ())) + { + std::string dispatch_class = curr_fcn->dispatch_class (); + + octave_value ov_meth + = symtab.find_method (fcn_name, dispatch_class); + + if (ov_meth.is_defined ()) + { + octave_function *fcn = ov_meth.function_value (); + + // FIXME: do we need to check that it is a method of + // dispatch_class, or is it sufficient to just check + // that it is a method? + + if (fcn->is_class_method ()) + { + // Create CLASSSIMPLE handle to method. + + octave_fcn_handle *fh + = new octave_fcn_handle (ov_meth, dispatch_class, + fcn_name); + + return octave_value (fh); + } + } + } + } + + octave_value ov_fcn = symtab.find_user_function (fcn_name); + + // Create handle to SIMPLE function. If the function is not found + // now, then we will look for it again when the handle is used. + + return octave_value (new octave_fcn_handle (ov_fcn, fcn_name)); } /* @@ -2665,25 +2844,6 @@ varargout); } - if (user_function.is_nested_function () - || user_function.is_parent_function ()) - { - // Copy current stack frame to handles to nested functions. - - for (octave_idx_type i = 0; i < retval.length (); i++) - { - octave_value val = retval(i); - - if (val.is_function_handle ()) - { - octave_fcn_handle *fh = val.fcn_handle_value (); - - if (fh) - fh->push_closure_context (*this); - } - } - } - return retval; }
--- a/libinterp/parse-tree/pt-fcn-handle.cc Wed Jun 10 17:01:31 2020 -0400 +++ b/libinterp/parse-tree/pt-fcn-handle.cc Wed Apr 29 14:10:27 2020 -0400 @@ -63,7 +63,6 @@ octave_value tree_fcn_handle::evaluate (tree_evaluator& tw, int) { - // XXX FCN_HANDLE: CONTEXT DEPENDENT return tw.make_fcn_handle (m_name); } @@ -176,8 +175,6 @@ octave_value ov_fcn (af); - // octave_value fh (octave_fcn_binder::maybe_binder (ov_fcn, m_interpreter)); - return octave_value (new octave_fcn_handle (ov_fcn, local_vars)); } }