# HG changeset patch # User John W. Eaton # Date 1591852965 14400 # Node ID b743a63e2dabd51377fca1bb66aa33e63b141cbf # Parent f5c9bb5955e7c9fddef5c3c3f115201e11b43b79# Parent bdd52f5e41702c65e2332d467395a730b8996a3f maint: merge stable to default. diff -r f5c9bb5955e7 -r b743a63e2dab doc/interpreter/external.txi --- a/doc/interpreter/external.txi Tue Jun 09 14:11:13 2020 -0700 +++ b/doc/interpreter/external.txi Thu Jun 11 01:22:45 2020 -0400 @@ -917,8 +917,6 @@ @item Anonymous Function Handle -@item Inline Function - @item String @end enumerate @@ -938,8 +936,6 @@ @result{} 0.84147 funcdemo (@@(x) sin (x), 1) @result{} 0.84147 -funcdemo (inline ("sin (x)"), 1) -@result{} 0.84147 funcdemo ("sin", 1) @result{} 0.84147 funcdemo (@@atan2, 1, 1) @@ -1698,8 +1694,7 @@ @end group @end example -Note that it is not possible to use function handles or inline functions -within a mex-file. +Note that it is not possible to use function handles within a mex-file. @c @node Application Programming Interface for Mex-Files @c @subsection Application Programming Interface for Mex-Files diff -r f5c9bb5955e7 -r b743a63e2dab doc/interpreter/func.txi --- a/doc/interpreter/func.txi Tue Jun 09 14:11:13 2020 -0700 +++ b/doc/interpreter/func.txi Thu Jun 11 01:22:45 2020 -0400 @@ -40,7 +40,7 @@ * Validating Arguments:: * Function Files:: * Script Files:: -* Function Handles Anonymous Functions Inline Functions:: +* Function Handles and Anonymous Functions:: * Commands:: * Organization of Functions:: @end menu @@ -1733,11 +1733,10 @@ @end group @end example -@node Function Handles Anonymous Functions Inline Functions -@section Function Handles, Anonymous Functions, Inline Functions +@node Function Handles and Anonymous Functions +@section Function Handles and Anonymous Functions @cindex handle, function handles @cindex anonymous functions -@cindex inline, inline functions It can be very convenient store a function in a variable so that it can be passed to a different function. For example, a function that @@ -1747,7 +1746,6 @@ @menu * Function Handles:: * Anonymous Functions:: -* Inline Functions:: @end menu @node Function Handles @@ -1887,34 +1885,6 @@ @xref{Operator Overloading}, for a list of operators which also have a functional form. -@node Inline Functions -@subsection Inline Functions - -An inline function is created from a string containing the function -body using the @code{inline} function. The following code defines the -function @math{f(x) = x^2 + 2}. - -@example -f = inline ("x^2 + 2"); -@end example - -@noindent -After this it is possible to evaluate @math{f} at any @math{x} by -writing @code{f(x)}. - -@strong{Caution}: @sc{matlab} has begun the process of deprecating inline -functions. At some point in the future support will be dropped and eventually -Octave will follow @sc{matlab} and also remove inline functions. Use anonymous -functions in all new code. - -@DOCSTRING(inline) - -@DOCSTRING(argnames) - -@DOCSTRING(formula) - -@DOCSTRING(symvar) - @node Commands @section Commands diff -r f5c9bb5955e7 -r b743a63e2dab doc/interpreter/octave.texi --- a/doc/interpreter/octave.texi Tue Jun 09 14:11:13 2020 -0700 +++ b/doc/interpreter/octave.texi Thu Jun 11 01:22:45 2020 -0400 @@ -411,7 +411,7 @@ * Default Arguments:: * Function Files:: * Script Files:: -* Function Handles Anonymous Functions Inline Functions:: +* Function Handles and Anonymous Functions:: * Commands:: * Organization of Functions:: @@ -446,11 +446,10 @@ * HTML Markup:: * LaTeX Markup:: -Function Handles Anonymous Functions Inline Functions +Function Handles and Anonymous Functions * Function Handles:: * Anonymous Functions:: -* Inline Functions:: Errors and Warnings diff -r f5c9bb5955e7 -r b743a63e2dab doc/interpreter/plot.txi --- a/doc/interpreter/plot.txi Tue Jun 09 14:11:13 2020 -0700 +++ b/doc/interpreter/plot.txi Thu Jun 11 01:22:45 2020 -0400 @@ -332,12 +332,11 @@ @subsubsection Two-dimensional Function Plotting @cindex plotting, two-dimensional functions -Octave can plot a function from a function handle, inline function, or -string defining the function without the user needing to explicitly -create the data to be plotted. The function @code{fplot} also generates -two-dimensional plots with linear axes using a function name and limits -for the range of the x-coordinate instead of the x and y data. For -example, +Octave can plot a function from a function handle or string defining the +function without the user needing to explicitly create the data to be +plotted. The function @code{fplot} also generates two-dimensional plots +with linear axes using a function name and limits for the range of the +x-coordinate instead of the x and y data. For example, @example @group @@ -1891,7 +1890,7 @@ as an anonymous function, or as a string representing an Octave command. The latter syntax is not recommended since syntax errors will only occur when the string is evaluated. -@xref{Function Handles Anonymous Functions Inline Functions, , Function Handles section}. +@xref{Function Handles and Anonymous Functions, , Function Handles section}. This can then be associated with an object either at the object's creation, or later with the @code{set} function. For example, diff -r f5c9bb5955e7 -r b743a63e2dab doc/interpreter/quad.txi --- a/doc/interpreter/quad.txi Tue Jun 09 14:11:13 2020 -0700 +++ b/doc/interpreter/quad.txi Thu Jun 11 01:22:45 2020 -0400 @@ -140,18 +140,12 @@ is reasonably accurate (to see why, examine what happens to the result if you move the lower bound to 0.1, then 0.01, then 0.001, etc.). -The function @qcode{"f"} can be the string name of a function, a function -handle, or an inline function. These options make it quite easy to do -integration without having to fully define a function in an m-file. For -example: +The function @qcode{"f"} can be the string name of a function or a +function handle. These options make it quite easy to do integration +without having to fully define a function in an m-file. For example: @example @group -# Verify integral (x^3) = x^4/4 -f = inline ("x.^3"); -quadgk (f, 0, 1) - @result{} 0.25000 - # Verify gamma function = (n-1)! for n = 4 f = @@(x) x.^3 .* exp (-x); quadcc (f, 0, Inf) diff -r f5c9bb5955e7 -r b743a63e2dab doc/interpreter/vectorize.txi --- a/doc/interpreter/vectorize.txi Tue Jun 09 14:11:13 2020 -0700 +++ b/doc/interpreter/vectorize.txi Thu Jun 11 01:22:45 2020 -0400 @@ -130,11 +130,7 @@ which exploits the fact that @code{a > 5} produces a boolean index. Use elementwise vector operators whenever possible to avoid looping -(operators like @code{.*} and @code{.^}). @xref{Arithmetic Ops}. For -simple inline functions, the @code{vectorize} function can do this -automatically. - -@DOCSTRING(vectorize) +(operators like @code{.*} and @code{.^}). @xref{Arithmetic Ops}. Also exploit broadcasting in these elementwise operators both to avoid looping and unnecessary intermediate memory allocations. diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/Cell.cc --- a/libinterp/corefcn/Cell.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/Cell.cc Thu Jun 11 01:22:45 2020 -0400 @@ -63,6 +63,23 @@ } } +Cell::Cell (const std::list& sl) + : Array () +{ + octave_idx_type n = sl.size (); + + if (n > 0) + { + resize (dim_vector (n, 1)); + + octave_value *dst = fortran_vec (); + auto p = sl.begin (); + + for (octave_idx_type i = 0; i < n; i++) + dst[i] = *p++; + } +} + Cell::Cell (const Array& sa) : Array (sa.dims ()) { diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/Cell.h --- a/libinterp/corefcn/Cell.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/Cell.h Thu Jun 11 01:22:45 2020 -0400 @@ -28,6 +28,7 @@ #include "octave-config.h" +#include #include #include "Array.h" @@ -90,6 +91,8 @@ } } + Cell (const std::list& sl); + Cell (const Array& sa); Cell (const dim_vector& dv, const string_vector& sv, bool trim = false); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/call-stack.cc --- a/libinterp/corefcn/call-stack.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/call-stack.cc Thu Jun 11 01:22:45 2020 -0400 @@ -44,9 +44,7 @@ #include "pager.h" #include "parse.h" #include "stack-frame.h" -#include "stack-frame-walker.h" #include "syminfo.h" -#include "syminfo-accumulator.h" #include "symrec.h" #include "symscope.h" #include "variables.h" @@ -96,7 +94,7 @@ if (! m_cs.empty ()) { - const stack_frame *elt = m_cs[m_curr_frame]; + const std::shared_ptr elt = m_cs[m_curr_frame]; retval = elt->line (); } @@ -109,7 +107,7 @@ if (! m_cs.empty ()) { - const stack_frame *elt = m_cs[m_curr_frame]; + const std::shared_ptr elt = m_cs[m_curr_frame]; retval = elt->column (); } @@ -124,7 +122,7 @@ if (xframe > 0) { - const stack_frame *elt = m_cs[xframe]; + const std::shared_ptr elt = m_cs[xframe]; octave_function *f = elt->function (); @@ -143,7 +141,7 @@ if (xframe > 0) { - const stack_frame *elt = m_cs[xframe]; + const std::shared_ptr elt = m_cs[xframe]; octave_function *f = elt->function (); @@ -167,7 +165,7 @@ if (xframe > 0) { - const stack_frame *elt = m_cs[xframe]; + const std::shared_ptr elt = m_cs[xframe]; octave_function *f = elt->function (); @@ -183,7 +181,7 @@ return -1; } - unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void) const + unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void) { // Start at current frame. @@ -191,7 +189,7 @@ if (xframe > 0) { - const stack_frame *elt = m_cs[xframe]; + const std::shared_ptr elt = m_cs[xframe]; octave_function *f = elt->function (); @@ -214,7 +212,7 @@ while (i != 0) { - const stack_frame *elt = m_cs[i--]; + const std::shared_ptr elt = m_cs[i--]; octave_function *f = elt->function (); @@ -240,7 +238,7 @@ while (i != 0) { - const stack_frame *elt = m_cs[i--]; + const std::shared_ptr elt = m_cs[i--]; octave_function *f = elt->function (); @@ -270,7 +268,7 @@ while (i != 0) { - const stack_frame *elt = m_cs[i--]; + const std::shared_ptr elt = m_cs[i--]; octave_function *f = elt->function (); @@ -333,7 +331,7 @@ while (p != m_cs.cbegin ()) { - const stack_frame *elt = *(--p); + const std::shared_ptr elt = *(--p); octave_function *f = elt->function (); @@ -347,11 +345,12 @@ return retval; } - stack_frame * call_stack::get_static_link (size_t prev_frame) const + std::shared_ptr + call_stack::get_static_link (size_t prev_frame) const { // FIXME: is there a better way? - stack_frame *slink = nullptr; + std::shared_ptr slink; if (m_curr_frame > 0) { @@ -375,16 +374,16 @@ if (m_curr_frame > static_cast (m_max_stack_depth)) error ("max_stack_depth exceeded"); - stack_frame *slink = get_static_link (prev_frame); + std::shared_ptr slink = get_static_link (prev_frame); - stack_frame *new_frame - = new scope_stack_frame (m_evaluator, scope, m_curr_frame, slink); + std::shared_ptr + new_frame (stack_frame::create (m_evaluator, scope, m_curr_frame, slink)); m_cs.push_back (new_frame); } - void call_stack::push (octave_user_function *fcn, unwind_protect *up_frame, - stack_frame *closure_frames) + void call_stack::push (octave_user_function *fcn, + const std::shared_ptr& closure_frames) { size_t prev_frame = m_curr_frame; m_curr_frame = m_cs.size (); @@ -393,16 +392,17 @@ if (m_curr_frame > static_cast (m_max_stack_depth)) error ("max_stack_depth exceeded"); - stack_frame *slink = get_static_link (prev_frame); + std::shared_ptr slink = get_static_link (prev_frame); - stack_frame *new_frame - = new user_fcn_stack_frame (m_evaluator, fcn, up_frame, m_curr_frame, - slink, closure_frames); + std::shared_ptr + new_frame (stack_frame::create (m_evaluator, fcn, m_curr_frame, slink, + closure_frames)); m_cs.push_back (new_frame); } - void call_stack::push (octave_user_script *script, unwind_protect *up_frame) + void call_stack::push (octave_user_function *fcn, + const stack_frame::local_vars_map& local_vars) { size_t prev_frame = m_curr_frame; m_curr_frame = m_cs.size (); @@ -411,11 +411,29 @@ if (m_curr_frame > static_cast (m_max_stack_depth)) error ("max_stack_depth exceeded"); - stack_frame *slink = get_static_link (prev_frame); + std::shared_ptr slink = get_static_link (prev_frame); + + std::shared_ptr + new_frame (stack_frame::create (m_evaluator, fcn, m_curr_frame, slink, + local_vars)); + + m_cs.push_back (new_frame); + } - stack_frame *new_frame - = new script_stack_frame (m_evaluator, script, up_frame, m_curr_frame, - slink); + void call_stack::push (octave_user_script *script) + { + size_t prev_frame = m_curr_frame; + m_curr_frame = m_cs.size (); + + // m_max_stack_depth should never be less than zero. + if (m_curr_frame > static_cast (m_max_stack_depth)) + error ("max_stack_depth exceeded"); + + std::shared_ptr slink = get_static_link (prev_frame); + + std::shared_ptr + new_frame (stack_frame::create (m_evaluator, script, m_curr_frame, + slink)); m_cs.push_back (new_frame); } @@ -429,10 +447,10 @@ if (m_curr_frame > static_cast (m_max_stack_depth)) error ("max_stack_depth exceeded"); - stack_frame *slink = get_static_link (prev_frame); + std::shared_ptr slink = get_static_link (prev_frame); - stack_frame *new_frame - = new compiled_fcn_stack_frame (m_evaluator, fcn, m_curr_frame, slink); + std::shared_ptr + new_frame (stack_frame::create (m_evaluator, fcn, m_curr_frame, slink)); m_cs.push_back (new_frame); } @@ -449,7 +467,7 @@ if (verbose) { - const stack_frame *elt = m_cs[n]; + const std::shared_ptr elt = m_cs[n]; elt->display_stopped_in_message (octave_stdout); } @@ -462,7 +480,7 @@ { size_t user_frame = m_curr_frame; - stack_frame *frm = m_cs[user_frame]; + std::shared_ptr frm = m_cs[user_frame]; if (! (frm->is_user_fcn_frame () || frm->is_user_script_frame () || frm->is_scope_frame ())) @@ -475,7 +493,7 @@ return user_frame; } - stack_frame *call_stack::current_user_frame (void) const + std::shared_ptr call_stack::current_user_frame (void) const { size_t frame = find_current_user_frame (); @@ -501,7 +519,7 @@ return start; } - stack_frame *frm = m_cs[start]; + std::shared_ptr frm = m_cs[start]; if (! (frm && (frm->is_user_fcn_frame () || frm->is_user_script_frame () @@ -605,10 +623,10 @@ m_curr_frame = 0; } - std::list + std::list> call_stack::backtrace_frames (octave_idx_type& curr_user_frame) const { - std::list frames; + std::list> frames; // curr_frame is the index to the current frame in the overall call // stack, which includes any compiled function frames and scope @@ -621,7 +639,7 @@ for (size_t n = m_cs.size () - 1; n > 0; n--) { - stack_frame *frm = m_cs[n]; + std::shared_ptr frm = m_cs[n]; if (frm->is_user_script_frame () || frm->is_user_fcn_frame () || frm->is_scope_frame ()) @@ -639,7 +657,7 @@ return frames; } - std::list + std::list> call_stack::backtrace_frames (void) const { octave_idx_type curr_user_frame = -1; @@ -651,11 +669,12 @@ call_stack::backtrace_info (octave_idx_type& curr_user_frame, bool print_subfn) const { - std::list frames = backtrace_frames (curr_user_frame); + std::list> frames + = backtrace_frames (curr_user_frame); std::list retval; - for (const auto *frm : frames) + for (const auto& frm : frames) { if (frm->is_user_script_frame () || frm->is_user_fcn_frame () || frm->is_scope_frame ()) @@ -679,7 +698,8 @@ octave_map call_stack::backtrace (octave_idx_type& curr_user_frame, bool print_subfn) const { - std::list frames = backtrace_frames (curr_user_frame); + std::list> frames + = backtrace_frames (curr_user_frame); size_t nframes = frames.size (); @@ -692,7 +712,7 @@ octave_idx_type k = 0; - for (const auto *frm : frames) + for (const auto& frm : frames) { if (frm->is_user_script_frame () || frm->is_user_fcn_frame () || frm->is_scope_frame ()) @@ -728,15 +748,13 @@ if (m_cs.size () > 1) { - stack_frame *elt = m_cs.back (); + std::shared_ptr elt = m_cs.back (); - stack_frame *caller = elt->static_link (); + std::shared_ptr caller = elt->static_link (); m_curr_frame = caller->index (); m_cs.pop_back (); - - delete elt; } } @@ -976,39 +994,9 @@ bool have_regexp, bool return_list, bool verbose, const std::string& msg) { - symbol_info_accumulator sym_inf_accum (patterns, have_regexp); - - m_cs[m_curr_frame]->accept (sym_inf_accum); - - if (return_list) - { - if (verbose) - return sym_inf_accum.map_value (); - else - return Cell (string_vector (sym_inf_accum.names ())); - } - else if (! sym_inf_accum.is_empty ()) - { - - if (msg.empty ()) - octave_stdout << "Variables visible from the current scope:\n"; - else - octave_stdout << msg; - - if (verbose) - sym_inf_accum.display (octave_stdout, - m_evaluator.whos_line_format ()); - else - { - octave_stdout << "\n"; - string_vector names (sym_inf_accum.names ()); - names.list_in_columns (octave_stdout); - } - - octave_stdout << "\n"; - } - - return octave_value (); + return m_cs[m_curr_frame]->who (patterns, have_regexp, return_list, + verbose, m_evaluator.whos_line_format (), + msg); } octave_value call_stack::do_global_who_two (const string_vector& patterns, diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/call-stack.h --- a/libinterp/corefcn/call-stack.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/call-stack.h Thu Jun 11 01:22:45 2020 -0400 @@ -29,6 +29,7 @@ #include "octave-config.h" #include +#include #include class octave_function; @@ -55,7 +56,7 @@ { public: - typedef std::deque stack_frames; + typedef std::deque> stack_frames; typedef stack_frames::iterator iterator; typedef stack_frames::const_iterator const_iterator; @@ -87,14 +88,9 @@ size_t size (void) const { return m_cs.size (); } - const stack_frame& get_current_stack_frame (void) const + std::shared_ptr get_current_stack_frame (void) const { - return *(m_cs[m_curr_frame]); - } - - stack_frame& get_current_stack_frame (void) - { - return *(m_cs[m_curr_frame]); + return m_cs[m_curr_frame]; } symbol_scope top_scope (void) const @@ -121,10 +117,7 @@ octave_function *retval = nullptr; if (m_cs.size () > n) - { - stack_frame *elt = m_cs[n]; - retval = elt->function (); - } + retval = m_cs[n]->function (); return retval; } @@ -132,7 +125,7 @@ // User code caller. octave_user_code * current_user_code (void) const; - unwind_protect * curr_fcn_unwind_protect_frame (void) const; + unwind_protect * curr_fcn_unwind_protect_frame (void); // Line in user code caller. int current_user_code_line (void) const; @@ -160,14 +153,17 @@ // Return TRUE if all elements on the call stack are scripts. bool all_scripts (void) const; - stack_frame * get_static_link (size_t prev_frame) const; + std::shared_ptr get_static_link (size_t prev_frame) const; void push (const symbol_scope& scope); - void push (octave_user_function *fcn, unwind_protect *up_frame, - stack_frame *closure_frames = nullptr); + void push (octave_user_function *fcn, + const std::shared_ptr& closure_frames = std::shared_ptr ()); - void push (octave_user_script *script, unwind_protect *up_frame); + void push (octave_user_function *fcn, + const stack_frame::local_vars_map& local_vars); + + void push (octave_user_script *script); void push (octave_function *fcn); @@ -175,7 +171,7 @@ { if (! m_cs.empty ()) { - stack_frame *elt = m_cs.back (); + std::shared_ptr elt = m_cs.back (); elt->line (l); elt->column (c); @@ -186,7 +182,7 @@ { if (! m_cs.empty ()) { - stack_frame *elt = m_cs.back (); + std::shared_ptr elt = m_cs.back (); elt->line (l); } @@ -196,7 +192,7 @@ { if (! m_cs.empty ()) { - stack_frame *elt = m_cs.back (); + std::shared_ptr elt = m_cs.back (); elt->column (c); } @@ -210,7 +206,8 @@ } size_t find_current_user_frame (void) const; - stack_frame *current_user_frame (void) const; + + std::shared_ptr current_user_frame (void) const; size_t dbupdown (size_t start, int n, bool verbose); size_t dbupdown (int n = -1, bool verbose = false); @@ -219,12 +216,12 @@ void goto_base_frame (void); - std::list + std::list> backtrace_frames (octave_idx_type& curr_user_frame) const; // List of raw stack frames. - std::list backtrace_frames (void) const; + std::list> backtrace_frames (void) const; // List of stack_info objects that can be used in liboctave and // stored in the execution_exception object. diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/cellfun.cc --- a/libinterp/corefcn/cellfun.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/cellfun.cc Thu Jun 11 01:22:45 2020 -0400 @@ -452,7 +452,7 @@ // more specific function class, so this can result in fewer polymorphic // function calls as the function gets called for each value of the array. { - if (func.is_function_handle ()) + if (func.is_function_handle () || func.is_inline_function ()) { // We can't check for overloads now. Is there something else we // should be doing instead? @@ -1156,7 +1156,7 @@ if (! symbol_table_lookup) { - if (func.is_function_handle ()) + if (func.is_function_handle () || func.class_name () == "inline") { // We can't check for overloads now. Is there something // else we should be doing instead? diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/fcn-info.cc --- a/libinterp/corefcn/fcn-info.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/fcn-info.cc Thu Jun 11 01:22:45 2020 -0400 @@ -625,11 +625,8 @@ } octave_value - fcn_info::fcn_info_rep::xfind (const symbol_scope& search_scope, - const octave_value_list& args) + fcn_info::fcn_info_rep::find_scoped_function (const symbol_scope& search_scope) { - // Subfunction, local function, or private function. - if (search_scope) { // Subfunction. @@ -664,50 +661,80 @@ // Private function. - std::string dir_name = search_scope.dir_name (); + return find_private_function (search_scope.dir_name ()); + } + + return octave_value (); + } - if (! dir_name.empty ()) + octave_value + fcn_info::fcn_info_rep::find_private_function (const std::string& dir_name) + { + if (! dir_name.empty ()) + { + auto q = private_functions.find (dir_name); + + if (q == private_functions.end ()) { - auto q = private_functions.find (dir_name); + octave_value val = load_private_function (dir_name); - if (q == private_functions.end ()) + if (val.is_defined ()) + return val; + } + else + { + octave_value& fval = q->second; + + if (fval.is_defined ()) + out_of_date_check (fval, "", false); + + if (fval.is_defined ()) + return fval; + else { octave_value val = load_private_function (dir_name); if (val.is_defined ()) return val; } - else - { - octave_value& fval = q->second; - - if (fval.is_defined ()) - out_of_date_check (fval, "", false); - - if (fval.is_defined ()) - return fval; - else - { - octave_value val = load_private_function (dir_name); - - if (val.is_defined ()) - return val; - } - } } } - // Class methods. + return octave_value (); + } + octave_value + fcn_info::fcn_info_rep::find_method (const octave_value_list& args) + { if (! args.empty ()) { std::string dispatch_type = get_dispatch_type (args); - octave_value fcn = find_method (dispatch_type); + return find_method (dispatch_type); + } + + return octave_value (); + } + + octave_value + fcn_info::fcn_info_rep::xfind (const symbol_scope& search_scope, + const octave_value_list& args) + { + // Subfunction, local function, or private function. - if (fcn.is_defined ()) - return fcn; - } + octave_value fcn; + + fcn = find_scoped_function (search_scope); + + if (fcn.is_defined ()) + return fcn; + + // Class methods. + + fcn = find_method (args); + + if (fcn.is_defined ()) + return fcn; // Class constructors. The class name and function name are the same. @@ -745,7 +772,7 @@ // Autoload? - octave_value fcn = find_autoload (); + fcn = find_autoload (); if (fcn.is_defined ()) return fcn; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/fcn-info.h --- a/libinterp/corefcn/fcn-info.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/fcn-info.h Thu Jun 11 01:22:45 2020 -0400 @@ -89,8 +89,14 @@ octave_value builtin_find (const symbol_scope& search_scope); + octave_value find_scoped_function (const symbol_scope& search_scope); + + octave_value find_private_function (const std::string& dir_name); + octave_value find_method (const std::string& dispatch_type); + octave_value find_method (const octave_value_list& args); + octave_value find_autoload (void); octave_value find_package (void); @@ -255,6 +261,16 @@ return m_rep->builtin_find (search_scope); } + octave_value find_scoped_function (const symbol_scope& search_scope) const + { + return m_rep->find_scoped_function (search_scope); + } + + octave_value find_private_function (const std::string& dir_name) const + { + return m_rep->find_private_function (dir_name); + } + octave_value find_method (const std::string& dispatch_type) const { return m_rep->find_method (dispatch_type); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/help.cc --- a/libinterp/corefcn/help.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/help.cc Thu Jun 11 01:22:45 2020 -0400 @@ -828,30 +828,14 @@ // Find the main function we are in. octave::tree_evaluator& tw = interp.get_evaluator (); - octave_user_code *parent_fcn = tw.debug_user_code (); + octave_user_code *caller = tw.debug_user_code (); - if (! parent_fcn) + if (! caller) return ovl (retval); - // Find the subfunctions of this function. - // 1) subfunction_names contains only valid subfunctions - // 2) subfunctions contains both nested functions and subfunctions - const std::list names = parent_fcn->subfunction_names (); - const std::map h = parent_fcn->subfunctions (); - - size_t sz = names.size (); - retval.resize (dim_vector (sz, 1)); + octave::symbol_scope scope = caller->scope (); - // loop over them. - size_t i = 0; - for (const auto& nm : names) - { - std::map::const_iterator nm_fcn = h.find (nm); - if (nm_fcn != h.end ()) - retval(i++) = octave_value (new octave_fcn_handle (nm_fcn->second, nm)); - } - - return ovl (retval); + return ovl (Cell (scope.localfunctions ())); } /* diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/interpreter-private.cc --- a/libinterp/corefcn/interpreter-private.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/interpreter-private.cc Thu Jun 11 01:22:45 2020 -0400 @@ -44,7 +44,7 @@ #include "load-path.h" #include "load-save.h" #include "ov.h" -#include "ov-fcn-inline.h" +#include "ovl.h" #include "pager.h" #include "symtab.h" @@ -208,6 +208,9 @@ return get_function_handle (interp, arg, parameter_names); } + // May return a function handle object, inline function object, or + // function object. + octave_value get_function_handle (interpreter& interp, const octave_value& arg, const std::list& parameter_names) @@ -228,12 +231,19 @@ if (fcn.is_defined ()) return fcn; - fcn = octave_value (new octave_fcn_inline (fstr, parameter_names)); - // Possibly warn here that passing the function body in a // character string is discouraged. - return fcn; + octave_value_list args (parameter_names.size () + 1); + octave_idx_type i = 0; + args(i++) = fstr; + for (const auto& pname : parameter_names) + args(i++) = pname; + + octave_value_list tmp = interp.feval ("inline", args, 1); + + if (tmp.length () > 0) + return tmp(0); } return octave_value (); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/interpreter.cc --- a/libinterp/corefcn/interpreter.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/interpreter.cc Thu Jun 11 01:22:45 2020 -0400 @@ -1535,7 +1535,7 @@ { return feval (val.function_value (), args, nargout); } - else if (val.is_function_handle ()) + else if (val.is_function_handle () || val.is_inline_function ()) { // This covers function handles, inline functions, and anonymous // functions. @@ -1586,6 +1586,11 @@ return feval (f_arg, tmp_args, nargout); } + octave_value interpreter::make_function_handle (const std::string& name) + { + return m_evaluator.make_fcn_handle (name); + } + void interpreter::install_variable (const std::string& name, const octave_value& value, bool global) { diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/interpreter.h --- a/libinterp/corefcn/interpreter.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/interpreter.h Thu Jun 11 01:22:45 2020 -0400 @@ -371,6 +371,8 @@ octave_value_list feval (const octave_value_list& args, int nargout = 0); + octave_value make_function_handle (const std::string& name); + void install_variable (const std::string& name, const octave_value& value, bool global); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/load-path.cc --- a/libinterp/corefcn/load-path.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/load-path.cc Thu Jun 11 01:22:45 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; } diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/ls-hdf5.cc --- a/libinterp/corefcn/ls-hdf5.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/ls-hdf5.cc Thu Jun 11 01:22:45 2020 -0400 @@ -54,6 +54,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter.h" #include "interpreter-private.h" #include "load-save.h" #include "oct-hdf5.h" @@ -433,6 +434,212 @@ return type_id; } +static herr_t +load_inline_fcn (hid_t loc_id, const char *name, octave_value& retval) +{ +#if defined (HAVE_HDF5) + + 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); +#else + group_hid = H5Gopen (loc_id, name); +#endif + if (group_hid < 0) return -1; + +#if defined (HAVE_HDF5_18) + data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT); +#else + data_hid = H5Dopen (group_hid, "args"); +#endif + space_hid = H5Dget_space (data_hid); + rank = H5Sget_simple_extent_ndims (space_hid); + + if (rank != 2) + { + H5Dclose (data_hid); + H5Sclose (space_hid); + H5Gclose (group_hid); + return -1; + } + + OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank); + OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank); + + H5Sget_simple_extent_dims (space_hid, hdims, maxdims); + + octave_value_list args (hdims[1]+1); + + OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]); + + if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL, + octave_H5P_DEFAULT, s1) < 0) + { + H5Dclose (data_hid); + H5Sclose (space_hid); + H5Gclose (group_hid); + return -1; + } + + H5Dclose (data_hid); + H5Sclose (space_hid); + + for (size_t i = 0; i < hdims[1]; i++) + args(i+1) = std::string (s1 + i*hdims[0]); + +#if defined (HAVE_HDF5_18) + data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT); +#else + data_hid = H5Dopen (group_hid, "nm"); +#endif + + if (data_hid < 0) + { + H5Gclose (group_hid); + return -1; + } + + type_hid = H5Dget_type (data_hid); + type_class_hid = H5Tget_class (type_hid); + + if (type_class_hid != H5T_STRING) + { + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + 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 -1; + } + + slen = H5Tget_size (type_hid); + if (slen < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + OCTAVE_LOCAL_BUFFER (char, nm_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, nm_tmp) < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return -1; + } + H5Tclose (st_id); + H5Dclose (data_hid); + // NAME is obsolete and unused. + // std::string name (nm_tmp); + +#if defined (HAVE_HDF5_18) + data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT); +#else + data_hid = H5Dopen (group_hid, "iftext"); +#endif + + if (data_hid < 0) + { + H5Gclose (group_hid); + return -1; + } + + type_hid = H5Dget_type (data_hid); + type_class_hid = H5Tget_class (type_hid); + + if (type_class_hid != H5T_STRING) + { + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + 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 -1; + } + + slen = H5Tget_size (type_hid); + if (slen < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Dclose (data_hid); + H5Gclose (group_hid); + return -1; + } + + OCTAVE_LOCAL_BUFFER (char, iftext_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, iftext_tmp) < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return -1; + } + H5Tclose (st_id); + H5Dclose (data_hid); + + args(0) = std::string (iftext_tmp); + + octave::interpreter& interp + = octave::__get_interpreter__ ("load_inline_fcn"); + + octave_value_list tmp = interp.feval ("inline", args, 1); + + if (tmp.length () > 0) + { + retval = tmp(0); + return 1; + } + +#else + octave_unused_parameter (loc_id); + octave_unused_parameter (name); + octave_unused_parameter (retval); + + warn_load ("hdf5"); +#endif + + return -1; +} + // This function is designed to be passed to H5Giterate, which calls it // on each data item in an HDF5 file. For the item whose name is NAME in // the group GROUP_ID, this function sets dv->tc to an Octave representation @@ -534,9 +741,16 @@ H5Tclose (st_id); H5Dclose (data_id); - d->tc = type_info.lookup_type (typ); + if (std::string (typ) == "inline function") + { + retval = load_inline_fcn (subgroup_id, name, d->tc); + } + else + { + d->tc = type_info.lookup_type (typ); - retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1); + retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1); + } // check for OCTAVE_GLOBAL attribute: d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL"); @@ -545,6 +759,10 @@ } else { + // It seems that this block only applies to an old list type + // and that we shouldn't need to handle the old inline + // function type here. + // an HDF5 group is treated as an octave structure by // default (since that preserves name information), and an // octave list otherwise. @@ -565,6 +783,10 @@ } else if (info.type == H5G_DATASET && ident_valid) { + // It seems that this block only applies to an old version of + // Octave HDF5 files and that it is probably not important to + // handle the old inline function type here. + // For backwards compatibility. #if defined (HAVE_HDF5_18) data_id = H5Dopen (group_id, name, octave_H5P_DEFAULT); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/ls-mat5.cc --- a/libinterp/corefcn/ls-mat5.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/ls-mat5.cc Thu Jun 11 01:22:45 2020 -0400 @@ -66,12 +66,12 @@ #include "oct-map.h" #include "ov-cell.h" #include "ov-class.h" -#include "ov-fcn-inline.h" #include "ov.h" #include "ovl.h" #include "pager.h" #include "parse.h" #include "pt-eval.h" +#include "stack-frame.h" #include "sysdep.h" #include "unwind-prot.h" #include "utils.h" @@ -862,8 +862,13 @@ if (ftype == "simple" || ftype == "scopedfunction") { if (fpath.empty ()) - // We have a builtin function - tc = make_fcn_handle (interp, fname); + { + octave::tree_evaluator& tw = interp.get_evaluator (); + + // We have a builtin function + // XXX FCN_HANDLE: SIMPLE/SCOPED + tc = tw.make_fcn_handle (fname); + } else { std::string mroot = m0.contents ("matlabroot").string_value (); @@ -897,6 +902,7 @@ "", "", fname); if (ov_fcn.is_defined ()) + // XXX FCN_HANDLE: SIMPLE/SCOPED tc = octave_value (new octave_fcn_handle (ov_fcn, fname)); } else @@ -923,6 +929,7 @@ "", "", fname); if (ov_fcn.is_defined ()) + // XXX FCN_HANDLE: SIMPLE/SCOPED tc = octave_value (new octave_fcn_handle (ov_fcn, fname)); else { @@ -945,6 +952,7 @@ "", "", fname); if (ov_fcn.is_defined ()) + // XXX FCN_HANDLE: SIMPLE/SCOPED tc = octave_value (new octave_fcn_handle (ov_fcn, fname)); else { @@ -974,14 +982,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) { @@ -992,10 +993,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); @@ -1008,7 +1026,8 @@ if (! fh) error ("load: failed to load anonymous function handle"); - tc = new octave_fcn_handle (fh->fcn_val (), "@"); + // XXX FCN_HANDLE: ANONYMOUS + tc = octave_value (new octave_fcn_handle (fh->fcn_val (), local_vars)); } else error ("load: invalid function handle type"); @@ -1175,60 +1194,50 @@ if (isclass) { - if (classname == "inline") + octave::cdef_manager& cdm = interp.get_cdef_manager (); + + if (cdm.find_class (classname, false, true).ok ()) { - // inline is not an object in Octave but rather an - // overload of a function handle. Special case. - tc = new octave_fcn_inline (m.contents ("expr")(0).string_value (), - m.contents ("args")(0).string_value ()); + tc = m; + warning_with_id ("Octave:load:classdef-to-struct", + "load: classdef element has been converted to a struct"); } else { - octave::cdef_manager& cdm = interp.get_cdef_manager (); - - if (cdm.find_class (classname, false, true).ok ()) + octave_class *cls + = new octave_class (m, classname, + std::list ()); + + if (cls->reconstruct_exemplar ()) { - tc = m; - warning_with_id ("Octave:load:classdef-to-struct", - "load: classdef element has been converted to a struct"); + + if (! cls->reconstruct_parents ()) + warning_with_id ("Octave:load:classdef-object-inheritance", + "load: unable to reconstruct object inheritance"); + + tc = cls; + + octave::load_path& lp = interp.get_load_path (); + + if (lp.find_method (classname, "loadobj") != "") + { + try + { + octave_value_list tmp = octave::feval ("loadobj", tc, 1); + + tc = tmp(0); + } + catch (const octave::execution_exception&) + { + goto data_read_error; + } + } } else { - octave_class *cls - = new octave_class (m, classname, - std::list ()); - - if (cls->reconstruct_exemplar ()) - { - - if (! cls->reconstruct_parents ()) - warning_with_id ("Octave:load:classdef-object-inheritance", - "load: unable to reconstruct object inheritance"); - - tc = cls; - - octave::load_path& lp = interp.get_load_path (); - - if (lp.find_method (classname, "loadobj") != "") - { - try - { - octave_value_list tmp = octave::feval ("loadobj", tc, 1); - - tc = tmp(0); - } - catch (const octave::execution_exception&) - { - goto data_read_error; - } - } - } - else - { - tc = m; - warning_with_id ("Octave:load:classdef-to-struct", - "load: element has been converted to a structure"); - } + tc = m; + warning_with_id ("Octave:load:classdef-to-struct", + "load: element has been converted to a structure"); } } } diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/ls-oct-binary.cc --- a/libinterp/corefcn/ls-oct-binary.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/ls-oct-binary.cc Thu Jun 11 01:22:45 2020 -0400 @@ -44,6 +44,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter.h" #include "interpreter-private.h" #include "load-save.h" #include "ls-oct-binary.h" @@ -56,6 +57,78 @@ #include "variables.h" #include "version.h" +static bool +load_inline_fcn (std::istream& is, bool swap, octave::mach_info::float_format, + octave_value& retval) +{ + int32_t nargs; + if (! is.read (reinterpret_cast (&nargs), 4)) + return false; + if (swap) + swap_bytes<4> (&nargs); + + if (nargs < 1) + return false; + else + { + int32_t tmp; + octave_value_list args (nargs+1); + for (int i = 0; i < nargs; i++) + { + if (! is.read (reinterpret_cast (&tmp), 4)) + return false; + if (swap) + swap_bytes<4> (&tmp); + + OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1); + is.read (ctmp, tmp); + args(i+1) = std::string (ctmp); + + if (! is) + return false; + } + + if (! is.read (reinterpret_cast (&tmp), 4)) + return false; + if (swap) + swap_bytes<4> (&tmp); + + OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1); + is.read (ctmp1, tmp); + // NAME is obsolete and unused. + // std::string name (ctmp1); + + if (! is) + return false; + + if (! is.read (reinterpret_cast (&tmp), 4)) + return false; + if (swap) + swap_bytes<4> (&tmp); + + OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1); + is.read (ctmp2, tmp); + + if (is) + { + args(0) = std::string (ctmp2); + + octave::interpreter& interp + = octave::__get_interpreter__ ("load_inline_fcn"); + + octave_value_list tmp = interp.feval ("inline", args, 1); + + if (tmp.length () > 0) + { + retval = tmp(0); + return true; + } + } + } + + return false; +} + // Extract one value (scalar, matrix, string, etc.) from stream IS and // place it in TC, returning the name of the variable. If the value // is tagged as global in the file, return TRUE in GLOBAL. If SWAP @@ -238,6 +311,15 @@ error ("load: trouble reading binary file '%s'", filename.c_str ()); s[len] = '\0'; std::string typ = s; + + if (typ == "inline function") + { + // Special case for loading old octave_inline_fcn objects. + if (! load_inline_fcn (is, swap, fmt, tc)) + error ("load: trouble reading binary file '%s'", filename.c_str ()); + return retval; + } + tc = type_info.lookup_type (typ); } break; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/ls-oct-text.cc --- a/libinterp/corefcn/ls-oct-text.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/ls-oct-text.cc Thu Jun 11 01:22:45 2020 -0400 @@ -52,6 +52,7 @@ #include "defun.h" #include "error.h" #include "errwarn.h" +#include "interpreter.h" #include "interpreter-private.h" #include "load-save.h" #include "ls-ascii-helper.h" @@ -235,6 +236,53 @@ #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == (t)) +static octave_value +load_inline_fcn (std::istream& is, const std::string& filename) +{ + int nargs; + if (extract_keyword (is, "nargs", nargs, true)) + { + std::string name; + octave_value_list args (nargs+1); + for (int i = 0; i < nargs; i++) + { + std::string tmp; + is >> tmp; + args(i+1) = tmp; + } + is >> name; + if (name == "0") + name = ""; + + 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); + } + + if (is) + { + args(0) = std::string (buf); + + octave::interpreter& interp + = octave::__get_interpreter__ ("load_inline_fcn"); + + octave_value_list tmp = interp.feval ("inline", args, 1); + + if (tmp.length () > 0) + return tmp(0); + } + } + + error ("load: trouble reading ascii file '%s'", filename.c_str ()); +} + std::string read_text_data (std::istream& is, const std::string& filename, bool& global, octave_value& tc, octave_idx_type count, @@ -280,6 +328,12 @@ // Special case for backward compatibility. A small bit of cruft if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array")) tc = charMatrix (); + else if (SUBSTRING_COMPARE_EQ (typ, 0, 15, "inline function")) + { + // Special case for loading old octave_inline_fcn objects. + tc = load_inline_fcn (is, filename); + return name; + } else { octave::type_info& type_info diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/module.mk --- a/libinterp/corefcn/module.mk Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/module.mk Thu Jun 11 01:22:45 2020 -0400 @@ -83,9 +83,7 @@ %reldir%/sparse-xdiv.h \ %reldir%/sparse-xpow.h \ %reldir%/stack-frame.h \ - %reldir%/stack-frame-walker.h \ %reldir%/syminfo.h \ - %reldir%/syminfo-accumulator.h \ %reldir%/symrec.h \ %reldir%/symscope.h \ %reldir%/symtab.h \ diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/stack-frame-walker.h --- a/libinterp/corefcn/stack-frame-walker.h Tue Jun 09 14:11:13 2020 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2019-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or . -// -// This file is part of Octave. -// -// Octave is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Octave is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Octave; see the file COPYING. If not, see -// . -// -//////////////////////////////////////////////////////////////////////// - -#if ! defined (octave_stack_frame_walker_h) -#define octave_stack_frame_walker_h 1 - -#include "octave-config.h" - -namespace octave -{ - class compiled_fcn_stack_frame; - class script_stack_frame; - class user_fcn_stack_frame; - class scope_stack_frame; - - class stack_frame_walker - { - protected: - - stack_frame_walker (void) { } - - virtual ~stack_frame_walker (void) = default; - - public: - - // No copying! - - stack_frame_walker (const stack_frame_walker&) = delete; - - stack_frame_walker& operator = (const stack_frame_walker&) = delete; - - virtual void - visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0; - - virtual void - visit_script_stack_frame (script_stack_frame&) = 0; - - virtual void - visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0; - - virtual void - visit_scope_stack_frame (scope_stack_frame&) = 0; - }; -} - -#endif diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/stack-frame.cc --- a/libinterp/corefcn/stack-frame.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/stack-frame.cc Thu Jun 11 01:22:45 2020 -0400 @@ -42,15 +42,506 @@ #include "parse.h" #include "pt-eval.h" #include "stack-frame.h" -#include "stack-frame-walker.h" #include "syminfo.h" -#include "syminfo-accumulator.h" #include "symrec.h" #include "symscope.h" #include "variables.h" namespace octave { + class compiled_fcn_stack_frame : public stack_frame + { + public: + + compiled_fcn_stack_frame (void) = delete; + + compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn, + size_t index, + const std::shared_ptr& static_link) + : stack_frame (tw, index, static_link, static_link->access_link ()), + m_fcn (fcn) + { } + + compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default; + + compiled_fcn_stack_frame& + operator = (const compiled_fcn_stack_frame& elt) = delete; + + ~compiled_fcn_stack_frame (void) = default; + + bool is_compiled_fcn_frame (void) const { return true; } + + symbol_scope get_scope (void) const + { + return m_static_link->get_scope (); + } + + octave_function * function (void) const { return m_fcn; } + + symbol_record lookup_symbol (const std::string& name) const + { + return m_static_link->lookup_symbol (name); + } + + symbol_record insert_symbol (const std::string& name) + { + return m_static_link->insert_symbol (name); + } + + stack_frame::scope_flags scope_flag (const symbol_record& sym) const + { + // Look in closest stack frame that contains values (either the + // top scope, or a user-defined function or script). + + return m_static_link->scope_flag (sym); + } + + void set_auto_fcn_var (auto_var_type avt, const octave_value& val) + { + m_static_link->set_auto_fcn_var (avt, val); + } + + octave_value get_auto_fcn_var (auto_var_type avt) const + { + return m_static_link->get_auto_fcn_var (avt); + } + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using stack_frame::varval; + using stack_frame::varref; + + octave_value varval (const symbol_record& sym) const + { + // Look in closest stack frame that contains values (either the + // top scope, or a user-defined function or script). + + return m_static_link->varval (sym); + } + + octave_value& varref (const symbol_record& sym) + { + // Look in closest stack frame that contains values (either the + // top scope, or a user-defined function or script). + + return m_static_link->varref (sym); + } + + void mark_scope (const symbol_record& sym, scope_flags flag) + { + // Look in closest stack frame that contains values (either the + // top scope, or a user-defined function or script). + + m_static_link->mark_scope (sym, flag); + } + + void display (bool follow = true) const; + + void accept (stack_frame_walker& sfw); + + private: + + // Compiled function object associated with this stack frame. + // Should always be a built-in, .oct or .mex file function and + // should always be valid. + octave_function *m_fcn; + }; + + // Scripts have a symbol_scope object to store the set of variables + // in the script, but values for those variables are stored in the + // stack frame corresponding to the nearest calling function or in + // the top-level scope (the evaluation stack frame). + // + // Accessing values in a scope requires a mapping from the index of + // the variable for the script scope to the list of values in the + // evaluation frame(s). The frame offset tells us how many access + // links we must follow to find the stack frame that holds the + // value. The value offset is the index into the vector of values + // in that stack frame that we should use to find the value. + // + // Frame and value offsets are set in this stack frame when it is + // created using information from the script and enclosing scopes. + // + // If a script is invoked in a nested function context, the frame + // offsets for individual values may be different. Some may be + // accessed from the invoking function and some may come from a + // parent function. + + class script_stack_frame : public stack_frame + { + public: + + script_stack_frame (void) = delete; + + script_stack_frame (tree_evaluator& tw, octave_user_script *script, + size_t index, + const std::shared_ptr& static_link); + + script_stack_frame (const script_stack_frame& elt) = default; + + script_stack_frame& operator = (const script_stack_frame& elt) = delete; + + ~script_stack_frame (void) + { + delete m_unwind_protect_frame; + } + + bool is_user_script_frame (void) const { return true; } + + static std::shared_ptr + get_access_link (const std::shared_ptr& static_link); + + static size_t get_num_symbols (octave_user_script *script); + + void set_script_offsets (void); + + void set_script_offsets_internal (const std::map& symbols); + + void resize_and_update_script_offsets (const symbol_record& sym); + + symbol_scope get_scope (void) const { return m_script->scope (); } + + octave_function * function (void) const { return m_script; } + + unwind_protect * unwind_protect_frame (void); + + symbol_record lookup_symbol (const std::string& name) const; + + symbol_record insert_symbol (const std::string&); + + size_t size (void) const { return m_lexical_frame_offsets.size (); } + + void resize (size_t size) + { + m_lexical_frame_offsets.resize (size, 0); + m_value_offsets.resize (size, 0); + } + + void get_val_offsets_with_insert (const symbol_record& sym, + size_t& frame_offset, + size_t& data_offset); + + bool get_val_offsets_internal (const symbol_record& sym, + size_t& frame_offset, + size_t& data_offset) const; + + bool get_val_offsets (const symbol_record& sym, size_t& frame_offset, + size_t& data_offset) const; + + scope_flags scope_flag (const symbol_record& sym) const; + + void set_auto_fcn_var (auto_var_type avt, const octave_value& val) + { + m_access_link->set_auto_fcn_var (avt, val); + } + + octave_value get_auto_fcn_var (auto_var_type avt) const + { + return m_access_link->get_auto_fcn_var (avt); + } + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using stack_frame::varval; + using stack_frame::varref; + + octave_value varval (const symbol_record& sym) const; + + octave_value& varref (const symbol_record& sym); + + void mark_scope (const symbol_record& sym, scope_flags flag); + + void display (bool follow = true) const; + + void accept (stack_frame_walker& sfw); + + private: + + // Script object associated with this stack frame. Should always + // be valid. + octave_user_script *m_script; + + // The nearest unwind protect frame that was active when this + // stack frame was created. Should always be valid. + unwind_protect *m_unwind_protect_frame; + + // Mapping between the symbols in the symbol_scope object of the + // script to the stack frame in which the script is executed. The + // frame offsets may be greater than one if the script is executed + // in a nested function context. + + std::vector m_lexical_frame_offsets; + std::vector m_value_offsets; + }; + + // Base class for values and offsets shared by user_fcn and scope + // frames. + + class base_value_stack_frame : public stack_frame + { + public: + + base_value_stack_frame (void) = delete; + + base_value_stack_frame (tree_evaluator& tw, size_t num_symbols, + size_t index, + const std::shared_ptr& static_link, + const std::shared_ptr& access_link) + : stack_frame (tw, index, static_link, access_link), + m_values (num_symbols, octave_value ()), + m_flags (num_symbols, LOCAL), + m_auto_vars (NUM_AUTO_VARS, octave_value ()) + { } + + base_value_stack_frame (const base_value_stack_frame& elt) = default; + + base_value_stack_frame& + operator = (const base_value_stack_frame& elt) = delete; + + ~base_value_stack_frame (void) = default; + + size_t size (void) const + { + return m_values.size (); + } + + void resize (size_t size) + { + m_values.resize (size, octave_value ()); + m_flags.resize (size, LOCAL); + } + + stack_frame::scope_flags get_scope_flag (size_t data_offset) const + { + return m_flags.at (data_offset); + } + + void set_scope_flag (size_t data_offset, scope_flags flag) + { + m_flags.at (data_offset) = flag; + } + + octave_value get_auto_fcn_var (auto_var_type avt) const + { + return m_auto_vars.at (avt); + } + + void set_auto_fcn_var (auto_var_type avt, const octave_value& val) + { + m_auto_vars.at (avt) = val; + } + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using stack_frame::varval; + using stack_frame::varref; + + octave_value varval (size_t data_offset) const + { + return m_values.at (data_offset); + } + + octave_value& varref (size_t data_offset) + { + return m_values.at (data_offset); + } + + void display (bool follow = true) const; + + protected: + + // Variable values. This array is indexed by the data_offset + // value stored in the symbol_record objects of the scope + // associated with this stack frame. + std::vector m_values; + + // The type of each variable (local, global, persistent) of each + // value. This array is indexed by the data_offset value stored + // in the symbol_record objects of the scope associated with this + // stack frame. Local values are found in the M_VALUES array. + // Global values are stored in the tree_evaluator object that contains + // the stack frame. Persistent values are stored in the function + // scope corresponding to the stack frame. + std::vector m_flags; + + // A fixed list of Automatic variables created for this function. + // The elements of this vector correspond to the auto_var_type + // enum. + std::vector m_auto_vars; + }; + + // User-defined functions have a symbol_scope object to store the set + // of variables in the function and values are stored in the stack + // frame corresponding to the invocation of the function or one of + // its parents. The frame offset tells us how many access links we + // must follow to find the stack frame that holds the value. The + // value offset is the index into the vector of values in that stack + // frame that we should use to find the value. + // + // Frame and value offsets are determined when the corresponding + // function is parsed. + + class user_fcn_stack_frame : public base_value_stack_frame + { + public: + + user_fcn_stack_frame (void) = delete; + + user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn, + size_t index, + const std::shared_ptr& static_link, + const std::shared_ptr& access_link = std::shared_ptr ()) + : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link, + (access_link + ? access_link + : get_access_link (fcn, static_link))), + m_fcn (fcn), m_unwind_protect_frame (nullptr) + { } + + user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn, + size_t index, + const std::shared_ptr& static_link, + const local_vars_map& local_vars) + : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link, + get_access_link (fcn, static_link)), + m_fcn (fcn), m_unwind_protect_frame (nullptr) + { + // Initialize local variable values. + + for (const auto& nm_ov : local_vars) + assign (nm_ov.first, nm_ov.second); + } + + user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default; + + user_fcn_stack_frame& + operator = (const user_fcn_stack_frame& elt) = delete; + + ~user_fcn_stack_frame (void) + { + delete m_unwind_protect_frame; + } + + bool is_user_fcn_frame (void) const { return true; } + + static std::shared_ptr + get_access_link (octave_user_function *fcn, + const std::shared_ptr& static_link); + + static size_t get_num_symbols (octave_user_function *fcn) + { + symbol_scope fcn_scope = fcn->scope (); + + return fcn_scope.num_symbols (); + } + + void clear_values (void); + + symbol_scope get_scope (void) const { return m_fcn->scope (); } + + octave_function * function (void) const { return m_fcn; } + + unwind_protect * unwind_protect_frame (void); + + symbol_record lookup_symbol (const std::string& name) const; + + symbol_record insert_symbol (const std::string&); + + scope_flags scope_flag (const symbol_record& sym) const; + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using base_value_stack_frame::varval; + using base_value_stack_frame::varref; + + octave_value varval (const symbol_record& sym) const; + + octave_value& varref (const symbol_record& sym); + + void mark_scope (const symbol_record& sym, scope_flags flag); + + void display (bool follow = true) const; + + void accept (stack_frame_walker& sfw); + + private: + + // User-defined object associated with this stack frame. Should + // always be valid. + octave_user_function *m_fcn; + + // The nearest unwind protect frame that was active when this + // stack frame was created. Should always be valid. + unwind_protect *m_unwind_protect_frame; + }; + + // Pure scope stack frames (primarily the top-level workspace) have + // a set of variables and values are stored in the stack frame. All + // variable accesses are direct as there are no parent stack frames. + // + // Value offsets are determined when the corresponding variable is + // entered into the symbol_scope object corresponding to the frame. + + class scope_stack_frame : public base_value_stack_frame + { + public: + + scope_stack_frame (void) = delete; + + scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope, + size_t index, + const std::shared_ptr& static_link) + : base_value_stack_frame (tw, scope.num_symbols (), index, + static_link, nullptr), + m_scope (scope) + { } + + scope_stack_frame (const scope_stack_frame& elt) = default; + + scope_stack_frame& operator = (const scope_stack_frame& elt) = delete; + + ~scope_stack_frame (void) = default; + + bool is_scope_frame (void) const { return true; } + + symbol_scope get_scope (void) const { return m_scope; } + + symbol_record lookup_symbol (const std::string& name) const + { + return m_scope.lookup_symbol (name); + } + + symbol_record insert_symbol (const std::string&); + + scope_flags scope_flag (const symbol_record& sym) const; + + // We only need to override one of each of these functions. The + // using declaration will avoid warnings about partially-overloaded + // virtual functions. + using base_value_stack_frame::varval; + using base_value_stack_frame::varref; + + octave_value varval (const symbol_record& sym) const; + + octave_value& varref (const symbol_record& sym); + + void mark_scope (const symbol_record& sym, scope_flags flag); + + void display (bool follow = true) const; + + void accept (stack_frame_walker& sfw); + + private: + + // The scope object associated with this stack frame. + symbol_scope m_scope; + }; + // FIXME: There should probably be a display method for the script, // fcn, and scope objects and the script and function objects should // be responsible for displaying the scopes they contain. @@ -78,6 +569,35 @@ } } + class stack_frame_walker + { + protected: + + stack_frame_walker (void) { } + + virtual ~stack_frame_walker (void) = default; + + public: + + // No copying! + + stack_frame_walker (const stack_frame_walker&) = delete; + + stack_frame_walker& operator = (const stack_frame_walker&) = delete; + + virtual void + visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0; + + virtual void + visit_script_stack_frame (script_stack_frame&) = 0; + + virtual void + visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0; + + virtual void + visit_scope_stack_frame (scope_stack_frame&) = 0; + }; + class symbol_cleaner : public stack_frame_walker { public: @@ -112,7 +632,7 @@ // link for a compiled_fcn_stack_frame be the same as the static // link? - stack_frame *slink = frame.static_link (); + std::shared_ptr slink = frame.static_link (); if (slink) slink->accept (*this); @@ -120,7 +640,7 @@ void visit_script_stack_frame (script_stack_frame& frame) { - stack_frame *alink = frame.access_link (); + std::shared_ptr alink = frame.access_link (); if (alink) alink->accept (*this); @@ -130,7 +650,7 @@ { clean_frame (frame); - stack_frame *alink = frame.access_link (); + std::shared_ptr alink = frame.access_link (); if (alink) alink->accept (*this); @@ -140,7 +660,7 @@ { clean_frame (frame); - stack_frame *alink = frame.access_link (); + std::shared_ptr alink = frame.access_link (); if (alink) alink->accept (*this); @@ -237,6 +757,309 @@ std::set m_cleared_names; }; + class symbol_info_accumulator : public stack_frame_walker + { + public: + + symbol_info_accumulator (const std::string& pattern, + bool have_regexp = false) + : stack_frame_walker (), m_patterns (pattern), m_match_all (false), + m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (), + m_found_names () + { } + + symbol_info_accumulator (const string_vector& patterns, + bool have_regexp = false) + : stack_frame_walker (), m_patterns (patterns), m_match_all (false), + m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (), + m_found_names () + { } + + symbol_info_accumulator (bool match_all = true, bool first_only = true) + : stack_frame_walker (), m_patterns (), m_match_all (match_all), + m_first_only (first_only), m_have_regexp (false), + m_sym_inf_list (), m_found_names () + { } + + symbol_info_accumulator (const symbol_info_accumulator&) = delete; + + symbol_info_accumulator& operator = (const symbol_info_accumulator&) = delete; + + ~symbol_info_accumulator (void) = default; + + bool is_empty (void) const + { + for (const auto& nm_sil : m_sym_inf_list) + { + const symbol_info_list& lst = nm_sil.second; + + if (! lst.empty ()) + return false; + } + + return true; + } + + std::list names (void) const + { + std::list retval; + + for (const auto& nm_sil : m_sym_inf_list) + { + const symbol_info_list& lst = nm_sil.second; + + std::list nm_list = lst.names (); + + for (const auto& nm : nm_list) + retval.push_back (nm); + } + + return retval; + } + + symbol_info_list symbol_info (void) const + { + symbol_info_list retval; + + for (const auto& nm_sil : m_sym_inf_list) + { + const symbol_info_list& lst = nm_sil.second; + + for (const auto& syminf : lst) + retval.push_back (syminf); + } + + return retval; + } + + octave_map map_value (void) const + { + octave_map retval; + + // FIXME: is there a better way to concatenate structures? + + size_t n_frames = m_sym_inf_list.size (); + + OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames); + + size_t j = 0; + for (const auto& nm_sil : m_sym_inf_list) + { + std::string scope_name = nm_sil.first; + const symbol_info_list& lst = nm_sil.second; + + map_list[j] = lst.map_value (scope_name, n_frames-j); + + j++; + } + + return octave_map::cat (-1, n_frames, map_list); + } + + void display (std::ostream& os, const std::string& format) const + { + for (const auto& nm_sil : m_sym_inf_list) + { + os << "\nvariables in scope: " << nm_sil.first << "\n\n"; + + const symbol_info_list& lst = nm_sil.second; + + lst.display (os, format); + } + } + + void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame) + { + // This one follows static link always. Hmm, should the access + // link for a compiled_fcn_stack_frame be the same as the static + // link? + + std::shared_ptr slink = frame.static_link (); + + if (slink) + slink->accept (*this); + } + + void visit_script_stack_frame (script_stack_frame& frame) + { + std::shared_ptr alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame) + { + append_list (frame); + + std::shared_ptr alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + void visit_scope_stack_frame (scope_stack_frame& frame) + { + append_list (frame); + + std::shared_ptr alink = frame.access_link (); + + if (alink) + alink->accept (*this); + } + + private: + + typedef std::pair syminf_list_elt; + + // FIXME: the following is too complex and duplicates too much + // code. Maybe it should be split up so we have separate classes + // that do each job that is needed? + + std::list + filter (stack_frame& frame, const std::list& symbols) + { + std::list new_symbols; + + if (m_match_all) + { + for (const auto& sym : symbols) + { + if (frame.is_defined (sym)) + { + std::string name = sym.name (); + + if (m_first_only + && m_found_names.find (name) != m_found_names.end ()) + continue; + + m_found_names.insert (name); + + new_symbols.push_back (sym); + } + } + } + else if (m_have_regexp) + { + octave_idx_type npatterns = m_patterns.numel (); + + for (octave_idx_type j = 0; j < npatterns; j++) + { + std::string pattern = m_patterns[j]; + + regexp pat (pattern); + + for (const auto& sym : symbols) + { + std::string name = sym.name (); + + if (pat.is_match (name) && frame.is_defined (sym)) + { + if (m_first_only + && m_found_names.find (name) != m_found_names.end ()) + continue; + + m_found_names.insert (name); + + new_symbols.push_back (sym); + } + } + } + } + else + { + octave_idx_type npatterns = m_patterns.numel (); + + for (octave_idx_type j = 0; j < npatterns; j++) + { + std::string pattern = m_patterns[j]; + + glob_match pat (pattern); + + for (const auto& sym : symbols) + { + std::string name = sym.name (); + + if (pat.match (name) && frame.is_defined (sym)) + { + if (m_first_only + && m_found_names.find (name) == m_found_names.end ()) + continue; + + m_found_names.insert (name); + + new_symbols.push_back (sym); + } + } + } + } + + return new_symbols; + } + + void append_list (stack_frame& frame) + { + symbol_scope scope = frame.get_scope (); + + std::list symbols = scope.symbol_list (); + + if (m_match_all || ! m_patterns.empty ()) + symbols = filter (frame, symbols); + + symbol_info_list syminf_list = frame.make_symbol_info_list (symbols); + + m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list)); + } + + string_vector m_patterns; + + bool m_match_all; + bool m_first_only; + bool m_have_regexp; + + std::list> m_sym_inf_list; + + std::set m_found_names; + }; + + stack_frame * stack_frame::create (tree_evaluator& tw, octave_function *fcn, + size_t index, + const std::shared_ptr& static_link) + { + return new compiled_fcn_stack_frame (tw, fcn, index, static_link); + } + + stack_frame * stack_frame::create (tree_evaluator& tw, + octave_user_script *script, + size_t index, + const std::shared_ptr& static_link) + { + return new script_stack_frame (tw, script, index, static_link); + } + + stack_frame * stack_frame::create (tree_evaluator& tw, + octave_user_function *fcn, size_t index, + const std::shared_ptr& static_link, + const std::shared_ptr& access_link) + { + return new user_fcn_stack_frame (tw, fcn, index, static_link, access_link); + } + + stack_frame * stack_frame::create (tree_evaluator& tw, + octave_user_function *fcn, size_t index, + const std::shared_ptr& static_link, + const local_vars_map& local_vars) + { + return new user_fcn_stack_frame (tw, fcn, index, static_link, local_vars); + } + + stack_frame * stack_frame::create (tree_evaluator& tw, + const symbol_scope& scope, size_t index, + const std::shared_ptr& static_link) + { + return new scope_stack_frame (tw, scope, index, static_link); + } + // This function is only implemented for user_fcn stack frames and // only called for those objects using unwind_protect and the // call_stack::clear_current_frame_values function. Anything else @@ -268,6 +1091,45 @@ return symbol_stats; } + octave_value stack_frame::who (const string_vector& patterns, + bool have_regexp, bool return_list, + bool verbose, const std::string& whos_line_fmt, + const std::string& msg) + { + symbol_info_accumulator sym_inf_accum (patterns, have_regexp); + + accept (sym_inf_accum); + + if (return_list) + { + if (verbose) + return sym_inf_accum.map_value (); + else + return Cell (string_vector (sym_inf_accum.names ())); + } + else if (! sym_inf_accum.is_empty ()) + { + + if (msg.empty ()) + octave_stdout << "Variables visible from the current scope:\n"; + else + octave_stdout << msg; + + if (verbose) + sym_inf_accum.display (octave_stdout, whos_line_fmt); + else + { + octave_stdout << "\n"; + string_vector names (sym_inf_accum.names ()); + names.list_in_columns (octave_stdout); + } + + octave_stdout << "\n"; + } + + return octave_value (); + } + // Return first occurrence of variables in current stack frame and any // parent frames reachable through access links. @@ -280,6 +1142,41 @@ return sia.symbol_info (); } + octave_value stack_frame::workspace (void) + { + std::list ws_list; + + stack_frame *frame = this; + + while (frame) + { + octave::symbol_info_list symbols = frame->all_variables (); + + octave_scalar_map ws; + + for (const auto& sym_name : symbols.names ()) + { + octave_value val = symbols.varval (sym_name); + + if (val.is_defined ()) + ws.assign (sym_name, val); + } + + ws_list.push_back (ws); + + std::shared_ptr nxt = frame->access_link (); + frame = nxt.get (); + } + + Cell ws_frames (ws_list.size (), 1); + + octave_idx_type i = 0; + for (const auto& elt : ws_list) + ws_frames(i++) = elt; + + return ws_frames; + } + // FIXME: Should this function also find any variables in parent // scopes accessible through access_links? @@ -493,14 +1390,14 @@ os << "static link: "; if (m_static_link) - os << m_static_link; + os << m_static_link.get (); else os << "NULL"; os << std::endl; os << "access link: "; if (m_access_link) - os << m_access_link; + os << m_access_link.get (); else os << "NULL"; os << std::endl; @@ -515,7 +1412,7 @@ return; os << "FOLLOWING ACCESS LINKS:" << std::endl; - const stack_frame *frm = access_link (); + std::shared_ptr frm = access_link (); while (frm) { frm->display (false); @@ -525,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; @@ -549,23 +1440,16 @@ script_stack_frame::script_stack_frame (tree_evaluator& tw, octave_user_script *script, - unwind_protect *up_frame, size_t index, - stack_frame *static_link) + const std::shared_ptr& static_link) : stack_frame (tw, index, static_link, get_access_link (static_link)), - m_script (script), m_unwind_protect_frame (up_frame), + m_script (script), m_unwind_protect_frame (nullptr), m_lexical_frame_offsets (get_num_symbols (script), 1), m_value_offsets (get_num_symbols (script), 0) { 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 (); @@ -702,15 +1586,13 @@ // If this is a nested scope, set access_link to nearest parent // stack frame that corresponds to the lexical parent of this scope. - stack_frame * - script_stack_frame::get_access_link (stack_frame *static_link) + std::shared_ptr + script_stack_frame::get_access_link (const std::shared_ptr& static_link) { - stack_frame *alink = nullptr; - // If this script is called from another script, set access // link to ultimate parent stack frame. - alink = static_link; + std::shared_ptr alink = static_link; while (alink->is_user_script_frame ()) { @@ -723,6 +1605,14 @@ return alink; } + unwind_protect * script_stack_frame::unwind_protect_frame (void) + { + if (! m_unwind_protect_frame) + m_unwind_protect_frame = new unwind_protect (); + + return m_unwind_protect_frame; + } + symbol_record script_stack_frame::lookup_symbol (const std::string& name) const { symbol_scope scope = get_scope (); @@ -977,7 +1867,10 @@ const stack_frame *frame = this; for (size_t i = 0; i < frame_offset; i++) - frame = frame->access_link (); + { + std::shared_ptr nxt = frame->access_link (); + frame = nxt.get (); + } if (! frame) error ("internal error: invalid access link in function call stack"); @@ -1004,7 +1897,10 @@ const stack_frame *frame = this; for (size_t i = 0; i < frame_offset; i++) - frame = frame->access_link (); + { + std::shared_ptr nxt = frame->access_link (); + frame = nxt.get (); + } if (! frame) error ("internal error: invalid access link in function call stack"); @@ -1043,7 +1939,10 @@ stack_frame *frame = this; for (size_t i = 0; i < frame_offset; i++) - frame = frame->access_link (); + { + std::shared_ptr nxt = frame->access_link (); + frame = nxt.get (); + } if (data_offset >= frame->size ()) frame->resize (data_offset+1); @@ -1080,7 +1979,7 @@ if (frame_offset > 1) error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used"); - stack_frame *frame = access_link (); + std::shared_ptr frame = access_link (); if (data_offset >= frame->size ()) frame->resize (data_offset+1); @@ -1139,20 +2038,14 @@ } } - 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. - stack_frame * + std::shared_ptr user_fcn_stack_frame::get_access_link (octave_user_function *fcn, - stack_frame *static_link) + const std::shared_ptr& static_link) { - stack_frame *alink = nullptr; + std::shared_ptr alink; symbol_scope fcn_scope = fcn->scope (); @@ -1230,6 +2123,14 @@ } } + unwind_protect * user_fcn_stack_frame::unwind_protect_frame (void) + { + if (! m_unwind_protect_frame) + m_unwind_protect_frame = new unwind_protect (); + + return m_unwind_protect_frame; + } + symbol_record user_fcn_stack_frame::lookup_symbol (const std::string& name) const { const stack_frame *frame = this; @@ -1243,7 +2144,8 @@ if (sym) return sym; - frame = frame->access_link (); + std::shared_ptr nxt = frame->access_link (); + frame = nxt.get (); } return symbol_record (); @@ -1299,7 +2201,10 @@ const stack_frame *frame = this; for (size_t i = 0; i < frame_offset; i++) - frame = frame->access_link (); + { + std::shared_ptr nxt = frame->access_link (); + frame = nxt.get (); + } if (! frame) error ("internal error: invalid access link in function call stack"); @@ -1321,7 +2226,10 @@ const stack_frame *frame = this; for (size_t i = 0; i < frame_offset; i++) - frame = frame->access_link (); + { + std::shared_ptr nxt = frame->access_link (); + frame = nxt.get (); + } if (! frame) error ("internal error: invalid access link in function call stack"); @@ -1359,7 +2267,10 @@ stack_frame *frame = this; for (size_t i = 0; i < frame_offset; i++) - frame = frame->access_link (); + { + std::shared_ptr nxt = frame->access_link (); + frame = nxt.get (); + } if (data_offset >= frame->size ()) frame->resize (data_offset+1); @@ -1416,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 diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/stack-frame.h --- a/libinterp/corefcn/stack-frame.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/stack-frame.h Thu Jun 11 01:22:45 2020 -0400 @@ -32,6 +32,7 @@ #include #include #include +#include #include class octave_value; @@ -39,8 +40,6 @@ #include "error.h" #include "ov-fcn.h" -#include "ov-fcn.h" -#include "ov-fcn-handle.h" #include "ov-usr-fcn.h" #include "syminfo.h" #include "symscope.h" @@ -104,17 +103,14 @@ class symbol_info_list; class unwind_protect; - class compiled_fcn_stack_frame; - class script_stack_frame; - class user_fcn_stack_frame; - class scope_stack_frame; - class stack_frame_walker; class stack_frame { public: + typedef std::map local_vars_map; + // Markers indicating the type of a variable. Values for local // variables are stored in the stack frame. Values for // global variables are stored in the tree_evaluator object that @@ -144,20 +140,46 @@ stack_frame (void) = delete; stack_frame (tree_evaluator& tw, size_t index, - stack_frame *static_link, stack_frame *access_link) + const std::shared_ptr& static_link, + const std::shared_ptr& access_link) : m_evaluator (tw), m_line (-1), m_column (-1), m_index (index), m_static_link (static_link), m_access_link (access_link), m_dispatch_class () { } + // Compiled function. + static stack_frame * + create (tree_evaluator& tw, octave_function *fcn, size_t index, + const std::shared_ptr& static_link); + + // Script. + static stack_frame * + create (tree_evaluator& tw, octave_user_script *script, size_t index, + const std::shared_ptr& static_link); + + // User-defined function. + static stack_frame * + create (tree_evaluator& tw, octave_user_function *fcn, size_t index, + const std::shared_ptr& static_link, + const std::shared_ptr& access_link = std::shared_ptr ()); + + // Anonymous user-defined function with init vars. + static stack_frame * + create (tree_evaluator& tw, octave_user_function *fcn, size_t index, + const std::shared_ptr& static_link, + const local_vars_map& local_vars); + + // Scope. + static stack_frame * + create (tree_evaluator& tw, const symbol_scope& scope, size_t index, + const std::shared_ptr& static_link); + stack_frame (const stack_frame& elt) = default; stack_frame& operator = (const stack_frame& elt) = delete; 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. @@ -198,7 +220,7 @@ retval = parent_fcn_name + '>'; if (fcn->is_anonymous_function ()) - retval += octave_fcn_handle::anonymous; + retval += "@"; else retval += fcn->name (); } @@ -212,14 +234,20 @@ virtual octave_function * function (void) const { return nullptr; } - virtual unwind_protect * - unwind_protect_frame (void) const { return nullptr; } + virtual unwind_protect * unwind_protect_frame (void) { return nullptr; } symbol_info_list make_symbol_info_list (const std::list& symrec_list) const; + octave_value who (const string_vector& patterns, bool have_regexp, + bool return_list, bool verbose, + const std::string& whos_line_fmt, + const std::string& msg); + symbol_info_list all_variables (void); + octave_value workspace (void); + std::list variable_names (void) const; // Look for named symbol visible from current scope. Don't @@ -271,15 +299,11 @@ mark_global (sym); } - stack_frame * static_link (void) const {return m_static_link; } - - stack_frame * access_link (void) const {return m_access_link; } + std::shared_ptr + static_link (void) const {return m_static_link; } - void set_closure_links (stack_frame *dup_frame) - { - m_static_link = dup_frame; - m_access_link = dup_frame; - } + std::shared_ptr + access_link (void) const {return m_access_link; } virtual size_t size (void) const; @@ -534,495 +558,17 @@ // Pointer to the nearest parent frame that contains variable // information (script, function, or scope). - stack_frame *m_static_link; + std::shared_ptr m_static_link; // Pointer to the nearest lexical parent frame. Used to access // non-local variables for nested and anonymous functions or as a // link to the parent frame in which a script is executed. - stack_frame *m_access_link; + std::shared_ptr m_access_link; // Allow function handles to temporarily store their dispatch class // in the call stack. std::string m_dispatch_class; }; - - class compiled_fcn_stack_frame : public stack_frame - { - public: - - compiled_fcn_stack_frame (void) = delete; - - compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn, - size_t index, stack_frame *static_link) - : stack_frame (tw, index, static_link, static_link->access_link ()), - m_fcn (fcn) - { } - - compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default; - - compiled_fcn_stack_frame& - operator = (const compiled_fcn_stack_frame& elt) = delete; - - ~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 - { - return m_static_link->get_scope (); - } - - octave_function * function (void) const { return m_fcn; } - - symbol_record lookup_symbol (const std::string& name) const - { - return m_static_link->lookup_symbol (name); - } - - symbol_record insert_symbol (const std::string& name) - { - return m_static_link->insert_symbol (name); - } - - stack_frame::scope_flags scope_flag (const symbol_record& sym) const - { - // Look in closest stack frame that contains values (either the - // top scope, or a user-defined function or script). - - return m_static_link->scope_flag (sym); - } - - void set_auto_fcn_var (auto_var_type avt, const octave_value& val) - { - m_static_link->set_auto_fcn_var (avt, val); - } - - octave_value get_auto_fcn_var (auto_var_type avt) const - { - return m_static_link->get_auto_fcn_var (avt); - } - - // We only need to override one of each of these functions. The - // using declaration will avoid warnings about partially-overloaded - // virtual functions. - using stack_frame::varval; - using stack_frame::varref; - - octave_value varval (const symbol_record& sym) const - { - // Look in closest stack frame that contains values (either the - // top scope, or a user-defined function or script). - - return m_static_link->varval (sym); - } - - octave_value& varref (const symbol_record& sym) - { - // Look in closest stack frame that contains values (either the - // top scope, or a user-defined function or script). - - return m_static_link->varref (sym); - } - - void mark_scope (const symbol_record& sym, scope_flags flag) - { - // Look in closest stack frame that contains values (either the - // top scope, or a user-defined function or script). - - m_static_link->mark_scope (sym, flag); - } - - void display (bool follow = true) const; - - void accept (stack_frame_walker& sfw); - - private: - - // Compiled function object associated with this stack frame. - // Should always be a built-in, .oct or .mex file function and - // should always be valid. - octave_function *m_fcn; - }; - - // Scripts have a symbol_scope object to store the set of variables - // in the script, but values for those variables are stored in the - // stack frame corresponding to the nearest calling function or in - // the top-level scope (the evaluation stack frame). - // - // Accessing values in a scope requires a mapping from the index of - // the variable for the script scope to the list of values in the - // evaluation frame(s). The frame offset tells us how many access - // links we must follow to find the stack frame that holds the - // value. The value offset is the index into the vector of values - // in that stack frame that we should use to find the value. - // - // Frame and value offsets are set in this stack frame when it is - // created using information from the script and enclosing scopes. - // - // If a script is invoked in a nested function context, the frame - // offsets for individual values may be different. Some may be - // accessed from the invoking function and some may come from a - // parent function. - - class script_stack_frame : public stack_frame - { - public: - - script_stack_frame (void) = delete; - - script_stack_frame (tree_evaluator& tw, octave_user_script *script, - unwind_protect *up_frame, size_t index, - stack_frame *static_link); - - script_stack_frame (const script_stack_frame& elt) = default; - - script_stack_frame& operator = (const script_stack_frame& elt) = delete; - - ~script_stack_frame (void) = default; - - script_stack_frame * dup (void) const; - - bool is_user_script_frame (void) const { return true; } - - static stack_frame * get_access_link (stack_frame *static_link); - - static size_t get_num_symbols (octave_user_script *script); - - void set_script_offsets (void); - - void set_script_offsets_internal (const std::map& symbols); - - void resize_and_update_script_offsets (const symbol_record& sym); - - symbol_scope get_scope (void) const { return m_script->scope (); } - - octave_function * function (void) const { return m_script; } - - unwind_protect * - unwind_protect_frame (void) const { return m_unwind_protect_frame; } - - symbol_record lookup_symbol (const std::string& name) const; - - symbol_record insert_symbol (const std::string&); - - size_t size (void) const { return m_lexical_frame_offsets.size (); } - - void resize (size_t size) - { - m_lexical_frame_offsets.resize (size, 0); - m_value_offsets.resize (size, 0); - } - - void get_val_offsets_with_insert (const symbol_record& sym, - size_t& frame_offset, - size_t& data_offset); - - bool get_val_offsets_internal (const symbol_record& sym, - size_t& frame_offset, - size_t& data_offset) const; - - bool get_val_offsets (const symbol_record& sym, size_t& frame_offset, - size_t& data_offset) const; - - scope_flags scope_flag (const symbol_record& sym) const; - - void set_auto_fcn_var (auto_var_type avt, const octave_value& val) - { - m_access_link->set_auto_fcn_var (avt, val); - } - - octave_value get_auto_fcn_var (auto_var_type avt) const - { - return m_access_link->get_auto_fcn_var (avt); - } - - // We only need to override one of each of these functions. The - // using declaration will avoid warnings about partially-overloaded - // virtual functions. - using stack_frame::varval; - using stack_frame::varref; - - octave_value varval (const symbol_record& sym) const; - - octave_value& varref (const symbol_record& sym); - - void mark_scope (const symbol_record& sym, scope_flags flag); - - void display (bool follow = true) const; - - void accept (stack_frame_walker& sfw); - - private: - - // Script object associated with this stack frame. Should always - // be valid. - octave_user_script *m_script; - - // The nearest unwind protect frame that was active when this - // stack frame was created. Should always be valid. - unwind_protect *m_unwind_protect_frame; - - // Mapping between the symbols in the symbol_scope object of the - // script to the stack frame in which the script is executed. The - // frame offsets may be greater than one if the script is executed - // in a nested function context. - - std::vector m_lexical_frame_offsets; - std::vector m_value_offsets; - }; - - // Base class for values and offsets shared by user_fcn and scope - // frames. - - class base_value_stack_frame : public stack_frame - { - public: - - base_value_stack_frame (void) = delete; - - base_value_stack_frame (tree_evaluator& tw, size_t num_symbols, - size_t index, stack_frame *static_link, - stack_frame *access_link) - : stack_frame (tw, index, static_link, access_link), - m_values (num_symbols, octave_value ()), - m_flags (num_symbols, LOCAL), - m_auto_vars (NUM_AUTO_VARS, octave_value ()) - { } - - base_value_stack_frame (const base_value_stack_frame& elt) = default; - - base_value_stack_frame& - operator = (const base_value_stack_frame& elt) = delete; - - ~base_value_stack_frame (void) = default; - - size_t size (void) const - { - return m_values.size (); - } - - void resize (size_t size) - { - m_values.resize (size, octave_value ()); - m_flags.resize (size, LOCAL); - } - - stack_frame::scope_flags get_scope_flag (size_t data_offset) const - { - return m_flags.at (data_offset); - } - - void set_scope_flag (size_t data_offset, scope_flags flag) - { - m_flags.at (data_offset) = flag; - } - - octave_value get_auto_fcn_var (auto_var_type avt) const - { - return m_auto_vars.at (avt); - } - - void set_auto_fcn_var (auto_var_type avt, const octave_value& val) - { - m_auto_vars.at (avt) = val; - } - - // We only need to override one of each of these functions. The - // using declaration will avoid warnings about partially-overloaded - // virtual functions. - using stack_frame::varval; - using stack_frame::varref; - - octave_value varval (size_t data_offset) const - { - return m_values.at (data_offset); - } - - octave_value& varref (size_t data_offset) - { - return m_values.at (data_offset); - } - - void display (bool follow = true) const; - - protected: - - // Variable values. This array is indexed by the data_offset - // value stored in the symbol_record objects of the scope - // associated with this stack frame. - std::vector m_values; - - // The type of each variable (local, global, persistent) of each - // value. This array is indexed by the data_offset value stored - // in the symbol_record objects of the scope associated with this - // stack frame. Local values are found in the M_VALUES array. - // Global values are stored in the tree_evaluator object that contains - // the stack frame. Persistent values are stored in the function - // scope corresponding to the stack frame. - std::vector m_flags; - - // A fixed list of Automatic variables created for this function. - // The elements of this vector correspond to the auto_var_type - // enum. - std::vector m_auto_vars; - }; - - // User-defined functions have a symbol_scope object to store the set - // of variables in the function and values are stored in the stack - // frame corresponding to the invocation of the function or one of - // its parents. The frame offset tells us how many access links we - // must follow to find the stack frame that holds the value. The - // value offset is the index into the vector of values in that stack - // frame that we should use to find the value. - // - // Frame and value offsets are determined when the corresponding - // function is parsed. - - class user_fcn_stack_frame : public base_value_stack_frame - { - public: - - user_fcn_stack_frame (void) = delete; - - user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn, - unwind_protect *up_frame, size_t index, - stack_frame *static_link, - stack_frame *access_link = nullptr) - : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link, - (access_link - ? access_link - : get_access_link (fcn, static_link))), - m_fcn (fcn), m_unwind_protect_frame (up_frame) - { } - - user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default; - - user_fcn_stack_frame& - operator = (const user_fcn_stack_frame& elt) = delete; - - ~user_fcn_stack_frame (void) = default; - - user_fcn_stack_frame * dup (void) const; - - bool is_user_fcn_frame (void) const { return true; } - - static stack_frame * - get_access_link (octave_user_function *fcn, stack_frame *static_link); - - static size_t get_num_symbols (octave_user_function *fcn) - { - symbol_scope fcn_scope = fcn->scope (); - - return fcn_scope.num_symbols (); - } - - void clear_values (void); - - symbol_scope get_scope (void) const { return m_fcn->scope (); } - - octave_function * function (void) const { return m_fcn; } - - unwind_protect * - unwind_protect_frame (void) const { return m_unwind_protect_frame; } - - symbol_record lookup_symbol (const std::string& name) const; - - symbol_record insert_symbol (const std::string&); - - scope_flags scope_flag (const symbol_record& sym) const; - - // We only need to override one of each of these functions. The - // using declaration will avoid warnings about partially-overloaded - // virtual functions. - using base_value_stack_frame::varval; - using base_value_stack_frame::varref; - - octave_value varval (const symbol_record& sym) const; - - octave_value& varref (const symbol_record& sym); - - void mark_scope (const symbol_record& sym, scope_flags flag); - - void display (bool follow = true) const; - - void accept (stack_frame_walker& sfw); - - private: - - // User-defined object associated with this stack frame. Should - // always be valid. - octave_user_function *m_fcn; - - // The nearest unwind protect frame that was active when this - // stack frame was created. Should always be valid. - unwind_protect *m_unwind_protect_frame; - }; - - // Pure scope stack frames (primarily the top-level workspace) have - // a set of variables and values are stored in the stack frame. All - // variable accesses are direct as there are no parent stack frames. - // - // Value offsets are determined when the corresponding variable is - // entered into the symbol_scope object corresponding to the frame. - - class scope_stack_frame : public base_value_stack_frame - { - public: - - scope_stack_frame (void) = delete; - - scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope, - size_t index, stack_frame *static_link) - : base_value_stack_frame (tw, scope.num_symbols (), index, - static_link, nullptr), - m_scope (scope) - { } - - scope_stack_frame (const scope_stack_frame& elt) = default; - - scope_stack_frame& operator = (const scope_stack_frame& elt) = delete; - - ~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; } - - symbol_record lookup_symbol (const std::string& name) const - { - return m_scope.lookup_symbol (name); - } - - symbol_record insert_symbol (const std::string&); - - scope_flags scope_flag (const symbol_record& sym) const; - - // We only need to override one of each of these functions. The - // using declaration will avoid warnings about partially-overloaded - // virtual functions. - using base_value_stack_frame::varval; - using base_value_stack_frame::varref; - - octave_value varval (const symbol_record& sym) const; - - octave_value& varref (const symbol_record& sym); - - void mark_scope (const symbol_record& sym, scope_flags flag); - - void display (bool follow = true) const; - - void accept (stack_frame_walker& sfw); - - private: - - // The scope object associated with this stack frame. - symbol_scope m_scope; - }; } #endif diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/syminfo-accumulator.h --- a/libinterp/corefcn/syminfo-accumulator.h Tue Jun 09 14:11:13 2020 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,307 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2019-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or . -// -// This file is part of Octave. -// -// Octave is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Octave is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Octave; see the file COPYING. If not, see -// . -// -//////////////////////////////////////////////////////////////////////// - -#if ! defined (octave_syminfo_accumulator_h) -#define octave_syminfo_accumulator_h 1 - -#include "octave-config.h" - -#include - -#include "oct-map.h" -#include "ov.h" -#include "stack-frame.h" -#include "syminfo.h" -#include "symrec.h" - -namespace octave -{ - class symbol_info_accumulator : public stack_frame_walker - { - public: - - symbol_info_accumulator (const std::string& pattern, - bool have_regexp = false) - : stack_frame_walker (), m_patterns (pattern), m_match_all (false), - m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (), - m_found_names () - { } - - symbol_info_accumulator (const string_vector& patterns, - bool have_regexp = false) - : stack_frame_walker (), m_patterns (patterns), m_match_all (false), - m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (), - m_found_names () - { } - - symbol_info_accumulator (bool match_all = true, bool first_only = true) - : stack_frame_walker (), m_patterns (), m_match_all (match_all), - m_first_only (first_only), m_have_regexp (false), - m_sym_inf_list (), m_found_names () - { } - - symbol_info_accumulator (const symbol_info_accumulator&) = delete; - - symbol_info_accumulator& operator = (const symbol_info_accumulator&) = delete; - - ~symbol_info_accumulator (void) = default; - - bool is_empty (void) const - { - for (const auto& nm_sil : m_sym_inf_list) - { - const symbol_info_list& lst = nm_sil.second; - - if (! lst.empty ()) - return false; - } - - return true; - } - - std::list names (void) const - { - std::list retval; - - for (const auto& nm_sil : m_sym_inf_list) - { - const symbol_info_list& lst = nm_sil.second; - - std::list nm_list = lst.names (); - - for (const auto& nm : nm_list) - retval.push_back (nm); - } - - return retval; - } - - symbol_info_list symbol_info (void) const - { - symbol_info_list retval; - - for (const auto& nm_sil : m_sym_inf_list) - { - const symbol_info_list& lst = nm_sil.second; - - for (const auto& syminf : lst) - retval.push_back (syminf); - } - - return retval; - } - - octave_map map_value (void) const - { - octave_map retval; - - // FIXME: is there a better way to concatenate structures? - - size_t n_frames = m_sym_inf_list.size (); - - OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames); - - size_t j = 0; - for (const auto& nm_sil : m_sym_inf_list) - { - std::string scope_name = nm_sil.first; - const symbol_info_list& lst = nm_sil.second; - - map_list[j] = lst.map_value (scope_name, n_frames-j); - - j++; - } - - return octave_map::cat (-1, n_frames, map_list); - } - - void display (std::ostream& os, const std::string& format) const - { - for (const auto& nm_sil : m_sym_inf_list) - { - os << "\nvariables in scope: " << nm_sil.first << "\n\n"; - - const symbol_info_list& lst = nm_sil.second; - - lst.display (os, format); - } - } - - void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame) - { - // This one follows static link always. Hmm, should the access - // link for a compiled_fcn_stack_frame be the same as the static - // link? - - stack_frame *slink = frame.static_link (); - - if (slink) - slink->accept (*this); - } - - void visit_script_stack_frame (script_stack_frame& frame) - { - stack_frame *alink = frame.access_link (); - - if (alink) - alink->accept (*this); - } - - void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame) - { - append_list (frame); - - stack_frame *alink = frame.access_link (); - - if (alink) - alink->accept (*this); - } - - void visit_scope_stack_frame (scope_stack_frame& frame) - { - append_list (frame); - - stack_frame *alink = frame.access_link (); - - if (alink) - alink->accept (*this); - } - - private: - - typedef std::pair syminf_list_elt; - - // FIXME: the following is too complex and duplicates too much - // code. Maybe it should be split up so we have separate classes - // that do each job that is needed? - - std::list - filter (stack_frame& frame, const std::list& symbols) - { - std::list new_symbols; - - if (m_match_all) - { - for (const auto& sym : symbols) - { - if (frame.is_defined (sym)) - { - std::string name = sym.name (); - - if (m_first_only - && m_found_names.find (name) != m_found_names.end ()) - continue; - - m_found_names.insert (name); - - new_symbols.push_back (sym); - } - } - } - else if (m_have_regexp) - { - octave_idx_type npatterns = m_patterns.numel (); - - for (octave_idx_type j = 0; j < npatterns; j++) - { - std::string pattern = m_patterns[j]; - - regexp pat (pattern); - - for (const auto& sym : symbols) - { - std::string name = sym.name (); - - if (pat.is_match (name) && frame.is_defined (sym)) - { - if (m_first_only - && m_found_names.find (name) != m_found_names.end ()) - continue; - - m_found_names.insert (name); - - new_symbols.push_back (sym); - } - } - } - } - else - { - octave_idx_type npatterns = m_patterns.numel (); - - for (octave_idx_type j = 0; j < npatterns; j++) - { - std::string pattern = m_patterns[j]; - - glob_match pat (pattern); - - for (const auto& sym : symbols) - { - std::string name = sym.name (); - - if (pat.match (name) && frame.is_defined (sym)) - { - if (m_first_only - && m_found_names.find (name) == m_found_names.end ()) - continue; - - m_found_names.insert (name); - - new_symbols.push_back (sym); - } - } - } - } - - return new_symbols; - } - - void append_list (stack_frame& frame) - { - symbol_scope scope = frame.get_scope (); - - std::list symbols = scope.symbol_list (); - - if (m_match_all || ! m_patterns.empty ()) - symbols = filter (frame, symbols); - - symbol_info_list syminf_list = frame.make_symbol_info_list (symbols); - - m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list)); - } - - string_vector m_patterns; - - bool m_match_all; - bool m_first_only; - bool m_have_regexp; - - std::list> m_sym_inf_list; - - std::set m_found_names; - }; -} - -#endif diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/symscope.cc --- a/libinterp/corefcn/symscope.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/symscope.cc Thu Jun 11 01:22:45 2020 -0400 @@ -92,6 +92,43 @@ return p->second; } + std::list symbol_scope_rep::localfunctions (void) const + { + std::list retval; + + // Find the subfunctions of this function (which should be the + // primary parent function for this scope). + + // 1) m_subfunction_names contains only valid subfunctions + // 2) m_subfunctions contains both nested functions and subfunctions + + // loop over them. + + for (const auto& nm : m_subfunction_names) + { + auto nm_fcn_iter = m_subfunctions.find (nm); + + if (nm_fcn_iter != m_subfunctions.end ()) + { + octave_value ov_fcn = nm_fcn_iter->second; + octave_user_code *fcn = ov_fcn.user_code_value (); + + if (! fcn) + continue; + + octave::symbol_scope scope = fcn->scope (); + + std::list plst = scope.parent_fcn_names (); + + octave_fcn_handle *fh = new octave_fcn_handle (ov_fcn, nm, plst); + + retval.push_back (octave_value (fh)); + } + } + + return retval; + } + octave_value symbol_scope_rep::dump (void) const { @@ -243,8 +280,14 @@ m_is_static = true; } + std::list plst = parent_fcn_names (); + plst.push_front (m_fcn_name); + for (auto& scope_obj : m_children) - scope_obj.update_nest (); + { + scope_obj.cache_parent_fcn_names (plst); + scope_obj.update_nest (); + } } bool symbol_scope_rep::look_nonlocal (const std::string& name, @@ -280,4 +323,21 @@ return false; } + + std::list symbol_scope::localfunctions (void) const + { + if (! m_rep) + return std::list (); + + if (is_primary_fcn_scope ()) + return m_rep->localfunctions (); + + std::shared_ptr ppsr + = m_rep->primary_parent_scope_rep (); + + if (! ppsr) + return std::list (); + + return ppsr->localfunctions (); + } } diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/symscope.h --- a/libinterp/corefcn/symscope.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/symscope.h Thu Jun 11 01:22:45 2020 -0400 @@ -66,10 +66,11 @@ subfunctions_iterator; symbol_scope_rep (const std::string& name = "") - : m_name (name), m_symbols (), m_subfunctions (), - m_persistent_values (), m_code (nullptr), m_fcn_file_name (), - m_dir_name (), m_parent (), m_primary_parent (), m_children (), - m_nesting_depth (0), m_is_static (false) + : m_name (name), m_symbols (), m_subfunctions (), m_persistent_values (), + m_code (nullptr), m_fcn_name (), m_parent_fcn_names (), + m_fcn_file_name (), m_dir_name (), m_parent (), m_primary_parent (), + m_children (), m_nesting_depth (0), m_is_static (false), + m_is_primary_fcn_scope (false) { // All scopes have ans as the first symbol, initially undefined. @@ -126,6 +127,8 @@ new_sid->m_persistent_values = m_persistent_values; new_sid->m_subfunction_names = m_subfunction_names; new_sid->m_code = m_code; + new_sid->m_fcn_name = m_fcn_name; + new_sid->m_parent_fcn_names = m_parent_fcn_names; new_sid->m_fcn_file_name = m_fcn_file_name; new_sid->m_dir_name = m_dir_name; new_sid->m_parent = m_parent; @@ -133,6 +136,7 @@ new_sid->m_children = m_children; new_sid->m_nesting_depth = m_nesting_depth; new_sid->m_is_static = m_is_static; + new_sid->m_is_primary_fcn_scope = m_is_primary_fcn_scope; return new_sid; } @@ -213,6 +217,7 @@ nm_sf.second.unlock (); } + // Pairs of name, function objects. std::map subfunctions (void) const { return m_subfunctions; @@ -240,12 +245,28 @@ return m_subfunction_names; } + std::list localfunctions (void) const; + octave_value dump (void) const; std::string name (void) const { return m_name; } void cache_name (const std::string& name) { m_name = name; } + std::string fcn_name (void) const { return m_fcn_name; } + + void cache_fcn_name (const std::string& name) { m_fcn_name = name; } + + std::list parent_fcn_names (void) const + { + return m_parent_fcn_names; + } + + void cache_parent_fcn_names (const std::list& names) + { + m_parent_fcn_names = names; + } + octave_user_code *user_code (void) const { return m_code; } void set_user_code (octave_user_code *code) { m_code = code; } @@ -259,11 +280,15 @@ m_fcn_file_name = name; } + std::string fcn_file_name (void) const { return m_fcn_file_name; } + void cache_dir_name (const std::string& name); - std::string fcn_file_name (void) const { return m_fcn_file_name; } + std::string dir_name (void) const { return m_dir_name; } - std::string dir_name (void) const { return m_dir_name; } + void mark_primary_fcn_scope (void) { m_is_primary_fcn_scope = true; } + + bool is_primary_fcn_scope (void) const { return m_is_primary_fcn_scope; } bool is_relative (const std::shared_ptr& scope) const; @@ -313,6 +338,14 @@ octave_user_code *m_code; + //! Simple name of the function corresponding to this scope. + + std::string m_fcn_name; + + //! List Simple names of the parent functions corresponding to this scope. + + std::list m_parent_fcn_names; + //! The file name associated with m_code. std::string m_fcn_file_name; @@ -341,6 +374,9 @@ //! If true then no variables can be added. bool m_is_static; + + //! If true, this is the scope of a primary function. + bool m_is_primary_fcn_scope; }; class symbol_scope @@ -532,6 +568,9 @@ return m_rep ? m_rep->subfunction_names () : std::list (); } + // List of function handle objects. + std::list localfunctions (void) const; + octave_value dump (void) const { return m_rep ? m_rep->dump () : octave_value (); @@ -548,6 +587,28 @@ m_rep->cache_name (name); } + std::string fcn_name (void) const + { + return m_rep ? m_rep->fcn_name () : ""; + } + + void cache_fcn_name (const std::string& name) + { + if (m_rep) + m_rep->cache_fcn_name (name); + } + + std::list parent_fcn_names (void) const + { + return m_rep ? m_rep->parent_fcn_names () : std::list (); + } + + void cache_parent_fcn_names (const std::list& names) + { + if (m_rep) + m_rep->cache_parent_fcn_names (names); + } + octave_user_code * user_code (void) const { return m_rep ? m_rep->user_code () : nullptr; @@ -593,6 +654,17 @@ return m_rep ? m_rep->dir_name () : ""; } + void mark_primary_fcn_scope (void) + { + if (m_rep) + m_rep->mark_primary_fcn_scope (); + } + + bool is_primary_fcn_scope (void) const + { + return m_rep ? m_rep->is_primary_fcn_scope () : false; + } + bool is_relative (const symbol_scope& scope) const { return m_rep ? m_rep->is_relative (scope.get_rep ()) : false; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/symtab.cc --- a/libinterp/corefcn/symtab.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/symtab.cc Thu Jun 11 01:22:45 2020 -0400 @@ -71,6 +71,54 @@ return val.is_defined (); } + octave_value + symbol_table::find_scoped_function (const std::string& name, + const symbol_scope& search_scope) + { + if (name.empty ()) + return octave_value (); + + fcn_table_const_iterator p = m_fcn_table.find (name); + + if (p != m_fcn_table.end ()) + return p->second.find_scoped_function (search_scope); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.find_scoped_function (search_scope); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; + } + } + + octave_value + symbol_table::find_private_function (const std::string& dir_name, + const std::string& name) + { + if (name.empty ()) + return octave_value (); + + fcn_table_const_iterator p = m_fcn_table.find (name); + + if (p != m_fcn_table.end ()) + return p->second.find_private_function (dir_name); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.find_private_function (dir_name); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; + } + } + // FIXME: this function only finds legacy class methods, not // classdef methods. @@ -101,8 +149,19 @@ { fcn_table_const_iterator p = m_fcn_table.find (name); - return (p != m_fcn_table.end () - ? p->second.find_built_in_function () : octave_value ()); + if (p != m_fcn_table.end ()) + return p->second.find_built_in_function (); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.find_built_in_function (); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; + } } octave_value symbol_table::find_autoload (const std::string& name) @@ -112,8 +171,19 @@ auto p = m_fcn_table.find (name); - return (p != m_fcn_table.end () - ? p->second.find_autoload () : octave_value ()); + if (p != m_fcn_table.end ()) + return p->second.find_autoload (); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.find_autoload (); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; + } } octave_value @@ -222,8 +292,19 @@ auto p = m_fcn_table.find (name); - return (p != m_fcn_table.end () - ? p->second.find_user_function () : octave_value ()); + if (p != m_fcn_table.end ()) + return p->second.find_user_function (); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.find_user_function (); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; + } } octave_value symbol_table::find_cmdline_function (const std::string& name) @@ -233,8 +314,19 @@ auto p = m_fcn_table.find (name); - return (p != m_fcn_table.end () - ? p->second.find_cmdline_function () : octave_value ()); + if (p != m_fcn_table.end ()) + return p->second.find_cmdline_function (); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.find_cmdline_function (); + + if (fcn.is_defined ()) + m_fcn_table[name] = finfo; + + return fcn; + } } void symbol_table::install_cmdline_function (const std::string& name, diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/corefcn/symtab.h --- a/libinterp/corefcn/symtab.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/corefcn/symtab.h Thu Jun 11 01:22:45 2020 -0400 @@ -72,6 +72,12 @@ bool is_built_in_function_name (const std::string& name); + octave_value find_scoped_function (const std::string& name, + const symbol_scope& search_scope); + + octave_value find_private_function (const std::string& dir_name, + const std::string& name); + // FIXME: this function only finds legacy class methods, not // classdef methods. octave_value find_method (const std::string& name, diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/dldfcn/__init_fltk__.cc --- a/libinterp/dldfcn/__init_fltk__.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/dldfcn/__init_fltk__.cc Thu Jun 11 01:22:45 2020 -0400 @@ -2531,7 +2531,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); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/cdef-class.cc --- a/libinterp/octave-value/cdef-class.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/cdef-class.cc Thu Jun 11 01:22:45 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; @@ -981,12 +981,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); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/cdef-manager.cc --- a/libinterp/octave-value/cdef-manager.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/cdef-manager.cc Thu Jun 11 01:22:45 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 diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/module.mk --- a/libinterp/octave-value/module.mk Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/module.mk Thu Jun 11 01:22:45 2020 -0400 @@ -43,7 +43,6 @@ %reldir%/ov-cx-mat.h \ %reldir%/ov-dld-fcn.h \ %reldir%/ov-fcn-handle.h \ - %reldir%/ov-fcn-inline.h \ %reldir%/ov-fcn.h \ %reldir%/ov-float.h \ %reldir%/ov-flt-complex.h \ @@ -108,7 +107,6 @@ %reldir%/ov-cx-mat.cc \ %reldir%/ov-dld-fcn.cc \ %reldir%/ov-fcn-handle.cc \ - %reldir%/ov-fcn-inline.cc \ %reldir%/ov-fcn.cc \ %reldir%/ov-float.cc \ %reldir%/ov-flt-complex.cc \ diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-base.cc --- a/libinterp/octave-value/ov-base.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-base.cc Thu Jun 11 01:22:45 2020 -0400 @@ -914,15 +914,6 @@ return nullptr; } -octave_fcn_inline * -octave_base_value::fcn_inline_value (bool silent) -{ - if (! silent) - err_wrong_type_arg ("octave_base_value::fcn_inline_value()", type_name ()); - - return nullptr; -} - octave_value_list octave_base_value::list_value (void) const { diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-base.h --- a/libinterp/octave-value/ov-base.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-base.h Thu Jun 11 01:22:45 2020 -0400 @@ -66,7 +66,6 @@ class octave_user_script; class octave_user_code; class octave_fcn_handle; -class octave_fcn_inline; class octave_value_list; enum builtin_type_t @@ -631,8 +630,6 @@ virtual octave_fcn_handle * fcn_handle_value (bool silent = false); - virtual octave_fcn_inline * fcn_inline_value (bool silent = false); - virtual octave_value_list list_value (void) const; virtual octave_value convert_to_str (bool pad = false, bool force = false, diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-builtin.cc --- a/libinterp/octave-value/ov-builtin.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-builtin.cc Thu Jun 11 01:22:45 2020 -0400 @@ -43,20 +43,14 @@ "built-in function"); octave_value_list -octave_builtin::call (octave::tree_evaluator& tw, int nargout, - const octave_value_list& args) +octave_builtin::execute (octave::tree_evaluator& tw, int nargout, + const octave_value_list& args) { octave_value_list retval; if (args.has_magic_colon ()) error ("invalid use of colon in function argument list"); - octave::unwind_protect frame; - - tw.push_stack_frame (this); - - frame.add_method (tw, &octave::tree_evaluator::pop_stack_frame); - octave::profiler& profiler = tw.get_profiler (); octave::profiler::enter block (profiler, *this); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-builtin.h --- a/libinterp/octave-value/ov-builtin.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-builtin.h Thu Jun 11 01:22:45 2020 -0400 @@ -96,14 +96,9 @@ bool is_builtin_function (void) const { return true; } - // We don't need to override both forms of the call method. The using - // declaration will avoid warnings about partially-overloaded virtual - // functions. - using octave_function::call; - octave_value_list - call (octave::tree_evaluator& tw, int nargout = 0, - const octave_value_list& args = octave_value_list ()); + execute (octave::tree_evaluator& tw, int nargout = 0, + const octave_value_list& args = octave_value_list ()); octave::jit_type * to_jit (void) const; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-class.cc --- a/libinterp/octave-value/ov-class.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-class.cc Thu Jun 11 01:22:45 2020 -0400 @@ -28,6 +28,7 @@ #endif #include +#include #include #include "Array-util.h" @@ -2028,3 +2029,121 @@ return octave_value(); } + +// The following classes allow us to define "inline" function objects as +// legacy @class objects (as they appear to be in Matlab) while +// preserving the is_inline_function and function_value methods that +// were previously available in the octave_fcn_inline class. However, +// inline function objects no longer behave as octave_fcn_handle objects +// so calling is_function_handle for them no longer returns true. I see +// no reasonable way to preserve that behavior. The goal here is to +// allow most code that used the old octave_inline_fcn object to +// continue to work while eliminating the octave_inline_fcn class that +// was derived from the octave_fcn_handle class. Making that change +// appears to be necessary to properly fix function handle behavior and +// improve Matlab compatibility. It's unfortunate if this change causes +// trouble, but I see no better fix. Ultimately, we should replace all +// uses of "inline" function objects with anonymous functions. + +class octave_inline; + +// The following class can be removed once the +// octave_value::function_value method is removed. + +class +octave_inline_fcn : public octave_function +{ +public: + + octave_inline_fcn (octave_inline *obj) : m_inline_obj (obj) { } + + // No copying! + + octave_inline_fcn (const octave_inline_fcn& ob) = delete; + + octave_inline_fcn& operator = (const octave_inline_fcn& ob) = delete; + + ~octave_inline_fcn (void) = default; + + // Override default call method because we ultimately use feval to + // execute the inline function and that will push a stack frame. + + octave_value_list + call (octave::tree_evaluator& tw, int nargout = 0, + const octave_value_list& args = octave_value_list ()) + { + return execute (tw, nargout, args); + } + + octave_value_list + execute (octave::tree_evaluator& tw, int nargout = 0, + const octave_value_list& args = octave_value_list ()); + +private: + + octave_inline *m_inline_obj; +}; + +// Once the octave_inline_fcn class is removed, we should also be able +// to eliminate the octave_inline class below and replace the +// octave_value::is_inline_function method with +// +// bool octave_value::is_inline_function (void) const +// { +// return class_name () == "inline"; +// } + +class +octave_inline : public octave_class +{ +public: + + octave_inline (const octave_map& m) + : octave_class (m, "inline"), m_fcn_obj (new octave_inline_fcn (this)) + { } + + octave_inline (const octave_inline&) = default; + + ~octave_inline (void) = default; + + octave_base_value * clone (void) const { return new octave_inline (*this); } + + octave_base_value * empty_clone (void) const + { + return new octave_inline (octave_map (map_keys ())); + } + + bool is_inline_function (void) const { return true; } + + octave_function * function_value (bool) + { + return m_fcn_obj.get (); + } + +private: + + std::shared_ptr m_fcn_obj; +}; + +octave_value_list +octave_inline_fcn::execute (octave::tree_evaluator& tw, int nargout, + const octave_value_list& args) +{ + octave::interpreter& interp = tw.get_interpreter (); + + return interp.feval (octave_value (m_inline_obj, true), args, nargout); +} + + +DEFUN (__inline_ctor__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __inline_ctor__ (@var{prop_struct}) +Internal function. + +Implements final construction for inline objects. +@end deftypefn */) +{ + // Input validation has already been done in input.m. + + return octave_value (new octave_inline (args(0).map_value ())); +} diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-class.h --- a/libinterp/octave-value/ov-class.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-class.h Thu Jun 11 01:22:45 2020 -0400 @@ -59,6 +59,11 @@ parent_list (), obsolete_copies (0) { } + octave_class (const octave_map& m, const std::string& id) + : octave_base_value (), map (m), c_name (id), + parent_list (), obsolete_copies (0) + { } + octave_class (const octave_map& m, const std::string& id, const std::list& plist) : octave_base_value (), map (m), c_name (id), diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-classdef.cc --- a/libinterp/octave-value/ov-classdef.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-classdef.cc Thu Jun 11 01:22:45 2020 -0400 @@ -389,6 +389,25 @@ return octave::to_ov (octave::lookup_class (cls)); } +bool octave_classdef_meta::is_classdef_method (const std::string& cname) const +{ + bool retval = false; + + if (object.is_method ()) + { + if (cname.empty ()) + retval = true; + else + { + octave::cdef_method meth (object); + + return meth.is_defined_in_class (cname); + } + } + + return retval; +} + bool octave_classdef_meta::is_classdef_constructor (const std::string& cname) const { bool retval = false; @@ -433,8 +452,9 @@ } octave_value_list -octave_classdef_superclass_ref::call (octave::tree_evaluator& tw, - int nargout, const octave_value_list& idx) +octave_classdef_superclass_ref::execute (octave::tree_evaluator& tw, + int nargout, + const octave_value_list& idx) { octave_value_list retval; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-classdef.h --- a/libinterp/octave-value/ov-classdef.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-classdef.h Thu Jun 11 01:22:45 2020 -0400 @@ -195,14 +195,18 @@ return object.meta_subsref (type, idx, nargout); } - // We don't need to override both forms of the call method. The using - // declaration will avoid warnings about partially-overloaded virtual - // functions. - using octave_function::call; + // Override default call method because we don't push a new stack + // frame for this operation on classdef_meta objects. - octave_value_list call (octave::tree_evaluator&, int nargout, + octave_value_list call (octave::tree_evaluator& tw, int nargout, const octave_value_list& args) { + return execute (tw, nargout, args); + } + + octave_value_list execute (octave::tree_evaluator&, int nargout, + const octave_value_list& args) + { // Emulate ()-type meta subsref std::list idx (1, args); @@ -214,6 +218,8 @@ bool accepts_postfix_index (char type) const { return object.meta_accepts_postfix_index (type); } + bool is_classdef_method (const std::string& cname = "") const; + bool is_classdef_constructor (const std::string& cname = "") const; std::string doc_string (const std::string& meth_name) const; @@ -243,13 +249,17 @@ octave_function * function_value (bool = false) { return this; } - // We don't need to override both forms of the call method. The using - // declaration will avoid warnings about partially-overloaded virtual - // functions. - using octave_function::call; + // Override default call method because we don't push a new stack + // frame for this operation on classdef_superclass_ref objects. - octave_value_list - call (octave::tree_evaluator& tw, int nargout, const octave_value_list& idx); + octave_value_list call (octave::tree_evaluator& tw, int nargout, + const octave_value_list& args) + { + return execute (tw, nargout, args); + } + + octave_value_list execute (octave::tree_evaluator& tw, int nargout, + const octave_value_list& idx); private: diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-fcn-handle.cc --- a/libinterp/octave-value/ov-fcn-handle.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-fcn-handle.cc Thu Jun 11 01:22:45 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" @@ -63,6 +64,7 @@ #include "pt-misc.h" #include "pt-pr-code.h" #include "pt-stmt.h" +#include "stack-frame.h" #include "syminfo.h" #include "symscope.h" #include "unwind-prot.h" @@ -82,316 +84,2412 @@ const std::string octave_fcn_handle::anonymous ("@"); -octave_fcn_handle::octave_fcn_handle (const octave::symbol_scope& scope, - const std::string& n) - : m_fcn (), m_obj (), m_name (n), m_scope (scope), m_is_nested (false), - m_closure_frames (nullptr), m_dispatch_class () +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_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 ""; } + + 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 (""), 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 ""; } + + 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 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; + 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); } -} - -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 (nullptr), m_dispatch_class () -{ - octave_user_function *uf = m_fcn.user_function_value (true); - - if (uf && m_name != anonymous) + + 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& 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) { - octave::symbol_scope uf_scope = uf->scope (); - - if (uf_scope) - uf_scope.cache_name (m_name); + 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 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& 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); } - 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 (nullptr), m_dispatch_class () -{ - octave_user_function *uf = m_fcn.user_function_value (true); - - if (uf && m_name != anonymous) + 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) { - octave::symbol_scope uf_scope = uf->scope (); - - if (uf_scope) - uf_scope.cache_name (m_name); + 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; + + 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 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); } - if (uf && uf->is_nested_function () && ! uf->is_subfunction ()) - m_is_nested = true; -} - -octave_fcn_handle::~octave_fcn_handle (void) -{ - if (m_closure_frames) + 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) { - while (m_closure_frames->size () > 0) - { - octave::stack_frame *elt = m_closure_frames->back (); - - delete elt; - - m_closure_frames->pop_back (); - } - - delete m_closure_frames; + 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 @ + // 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); } -} - -octave_value_list -octave_fcn_handle::subsref (const std::string& type, + + 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) + { + 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 ("@"); + + 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& 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 '.': + { + octave_value_list retval; + + switch (type[0]) { - 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) -{ - // 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) + case '(': { - // 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 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 arg_list; - - for (size_t i = 1; i < n_elts; i++) - { - if (partial_expr_val.is_package ()) - { - if (have_object) + 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 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 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); - - 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::stack_frame *closure_context = nullptr; - - if (m_closure_frames && m_closure_frames->size () > 0) - closure_context = m_closure_frames->front (); - - octave::tree_evaluator& tw = interp.get_evaluator (); - - octave_function *of = fcn_to_call.function_value (); - - octave::unwind_protect frame; - - frame.add_method (tw, &octave::tree_evaluator::set_dispatch_class, - std::string ()); - - tw.set_dispatch_class (m_dispatch_class); - - return of->call (tw, nargout, args, closure_context); + } + } + 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 << "@\n" << config::octave_exec_home () + << "\n" << fnm; + + std::string buf_str = nmbuf.str (); + int32_t tmp = buf_str.length (); + os.write (reinterpret_cast (&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& 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 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 << "@\n" << config::octave_exec_home () + << "\n" << fnm; + + std::string buf_str = nmbuf.str (); + int32_t len = buf_str.length (); + os.write (reinterpret_cast (&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 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& 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 (&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 (&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 (&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 (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 (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& 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& 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) + : 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_rep = fh.m_rep->clone (); } dim_vector @@ -401,319 +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) -{ - if (! m_closure_frames) - m_closure_frames = new std::list (); - - octave::stack_frame& curr_frame = tw.get_current_stack_frame (); - - octave::stack_frame *dup_frame = curr_frame.dup (); - - if (! m_closure_frames->empty ()) - { - octave::stack_frame *top_frame = m_closure_frames->back (); - - // Arrange for static and access links in the top stack frame (the - // last one saved before this one) to point to the new duplicated - // frame. This way we will look up through the duplicated frames - // when evaluating the function. - - top_frame->set_closure_links (dup_frame); - } - - m_closure_frames->push_back (dup_frame); -} - -octave_value -octave_fcn_handle::workspace (void) const -{ - if (m_name == anonymous) - { - octave_user_function *fu = m_fcn.user_function_value (); - - octave_scalar_map ws; - - if (fu) - { - for (const auto& nm_val : fu->local_var_init_vals ()) - ws.assign (nm_val.first, nm_val.second); - } - - return ws; - } - else if (m_closure_frames) - { - octave_idx_type num_frames = m_closure_frames->size (); - - Cell ws_frames (num_frames, 1); - - octave_idx_type i = 0; - - for (auto elt : *m_closure_frames) - { - octave::symbol_info_list symbols = elt->all_variables (); - - octave_scalar_map ws; - - for (auto sym_name : symbols.names ()) - { - octave_value val = symbols.varval (sym_name); - - if (val.is_defined ()) - ws.assign (sym_name, val); - } - - ws_frames(i++) = ws; - } - - return ws_frames; - } - - return 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 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; - - octave_user_function *f = m_fcn.user_function_value (); - - octave_user_function::local_vars_map local_vars - = f->local_var_init_vals (); - - size_t varlen = local_vars.size (); - - os << m_name << "\n"; - - print_raw (os, true); - os << "\n"; - - if (varlen > 0) - { - os << "# length: " << varlen << "\n"; - - for (const auto& nm_val : 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; - - 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 ()) { @@ -721,143 +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; - - octave_user_function *f = m_fcn.user_function_value (); - - octave_user_function::local_vars_map local_vars - = f->local_var_init_vals (); - - size_t varlen = local_vars.size (); - - 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 (&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 (&tmp), 4); - os.write (stmp.c_str (), stmp.length ()); - - if (varlen > 0) - { - for (const auto& nm_val : 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 (&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 (&tmp), 4)) @@ -870,359 +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 (&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); - - octave_user_function *f = m_fcn.user_function_value (); - - octave_user_function::local_vars_map local_vars - = f->local_var_init_vals (); - - size_t varlen = 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 : 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) @@ -1231,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) { @@ -1242,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) { @@ -1254,7 +2766,7 @@ return false; } - slen = H5Tget_size (type_hid); + int slen = H5Tget_size (type_hid); if (slen < 0) { H5Sclose (space_hid); @@ -1267,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, @@ -1283,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 (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 } @@ -1639,248 +2986,54 @@ 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 (fh1.get_rep ()), + *dynamic_cast (fh2.get_rep ())); + else if (fh1.is_simple () && fh2.is_simple ()) + return is_equal_to (*dynamic_cast (fh1.get_rep ()), + *dynamic_cast (fh2.get_rep ())); + else if (fh1.is_scoped () && fh2.is_scoped ()) + return is_equal_to (*dynamic_cast (fh1.get_rep ()), + *dynamic_cast (fh2.get_rep ())); + else if (fh1.is_nested () && fh2.is_nested ()) + return is_equal_to (*dynamic_cast (fh1.get_rep ()), + *dynamic_cast (fh2.get_rep ())); + else if (fh1.is_class_simple () && fh2.is_class_simple ()) + return is_equal_to (*dynamic_cast (fh1.get_rep ()), + *dynamic_cast (fh2.get_rep ())); + else if (fh1.is_anonymous () && fh2.is_anonymous ()) + return is_equal_to (*dynamic_cast (fh1.get_rep ()), + *dynamic_cast (fh2.get_rep ())); + else + return false; } namespace octave { - // Hmm, should this function be a member of the interpreter class, - // possibly forwarded to an actual implementation in the - // tree_evaluator class? + // DEPRECATED in Octave 6. octave_value make_fcn_handle (interpreter& interp, const std::string& nm) { - octave_value retval; - - // Bow to the god of compatibility. - - // FIXME: it seems ugly to put this here, but there is no single - // function in the parser that converts from the operator name to - // the corresponding function name. At least try to do it without N - // string compares. - - std::string tnm = nm; - - size_t len = nm.length (); - - if (len == 3 && nm == ".**") - tnm = "power"; - else if (len == 2) - { - if (nm[0] == '.') - { - switch (nm[1]) - { - case '\'': - tnm = "transpose"; - break; - - case '+': - tnm = "plus"; - break; - - case '-': - tnm = "minus"; - break; - - case '*': - tnm = "times"; - break; - - case '/': - tnm = "rdivide"; - break; - - case '^': - tnm = "power"; - break; - - case '\\': - tnm = "ldivide"; - break; - } - } - else if (nm[1] == '=') - { - switch (nm[0]) - { - case '<': - tnm = "le"; - break; - - case '=': - tnm = "eq"; - break; - - case '>': - tnm = "ge"; - break; - - case '~': - case '!': - tnm = "ne"; - break; - } - } - else if (nm == "**") - tnm = "mpower"; - } - else if (len == 1) - { - switch (nm[0]) - { - case '~': - case '!': - tnm = "not"; - break; - - case '\'': - tnm = "ctranspose"; - break; - - case '+': - tnm = "plus"; - break; - - case '-': - tnm = "minus"; - break; - - case '*': - tnm = "mtimes"; - break; - - case '/': - tnm = "mrdivide"; - break; - - case '^': - tnm = "mpower"; - break; - - case '\\': - tnm = "mldivide"; - break; - - case '<': - tnm = "lt"; - break; - - case '>': - tnm = "gt"; - break; - - case '&': - tnm = "and"; - break; - - case '|': - tnm = "or"; - break; - } - } - tree_evaluator& tw = interp.get_evaluator (); - symbol_scope curr_scope = tw.get_current_scope (); - - octave_fcn_handle *fh = new octave_fcn_handle (curr_scope, tnm); - - std::string dispatch_class; - - if (tw.is_class_method_executing (dispatch_class) - || tw.is_class_constructor_executing (dispatch_class)) - fh->set_dispatch_class (dispatch_class); - - return octave_value (fh); + return tw.make_fcn_handle (nm); } } -/* -%!test -%! x = {".**", "power"; -%! ".'", "transpose"; -%! ".+", "plus"; -%! ".-", "minus"; -%! ".*", "times"; -%! "./", "rdivide"; -%! ".^", "power"; -%! ".\\", "ldivide"; -%! "<=", "le"; -%! "==", "eq"; -%! ">=", "ge"; -%! "!=", "ne"; -%! "~=", "ne"; -%! "**", "mpower"; -%! "~", "not"; -%! "!", "not"; -%! "\'", "ctranspose"; -%! "+", "plus"; -%! "-", "minus"; -%! "*", "mtimes"; -%! "/", "mrdivide"; -%! "^", "mpower"; -%! "\\", "mldivide"; -%! "<", "lt"; -%! ">", "gt"; -%! "&", "and"; -%! "|", "or"}; -%! for i = 1:rows (x) -%! assert (functions (str2func (x{i,1})).function, x{i,2}); -%! endfor -*/ - DEFUN (functions, args, , doc: /* -*- texinfo -*- @deftypefn {} {@var{s} =} functions (@var{fcn_handle}) @@ -1937,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, , @@ -2015,7 +3113,7 @@ std::string fh_nm = fh->fcn_name (); - if (fh_nm == octave_fcn_handle::anonymous) + if (fh->is_anonymous ()) { std::ostringstream buf; @@ -2037,7 +3135,7 @@ Previous versions of Octave accepted an optional second argument, @qcode{"global"}, that caused str2func to ignore locally visible functions. This option is no longer supported. -@seealso{func2str, inline, functions} +@seealso{func2str, functions} @end deftypefn */) { int nargin = args.length (); @@ -2045,18 +3143,31 @@ if (nargin < 1 || nargin > 2) print_usage (); - std::string nm = args(0).xstring_value ("str2func: FCN_NAME must be a string"); - - octave_value retval; + std::string nm + = args(0).xstring_value ("str2func: FCN_NAME must be a string"); + + if (nm.empty ()) + error ("str2func: invalid function name"); if (nm[0] == '@') { + // 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 + // + // str2fun ("@(args) expr") + // + // to behave the same as if + // + // @(args) expr + // + // were evaluated in the current scope. + int parse_status; - octave_value anon_fcn_handle - = interp.eval_string (nm, true, parse_status); + octave_value afh = interp.eval_string (nm, true, parse_status); if (parse_status == 0) - retval = anon_fcn_handle; + return afh; } else { @@ -2064,10 +3175,12 @@ warning_with_id ("Octave:str2func-global-argument", "str2func: second argument ignored"); - retval = octave::make_fcn_handle (interp, nm); + octave::tree_evaluator& tw = interp.get_evaluator (); + + return tw.make_fcn_handle (nm); } - return retval; + return ovl (); } /* @@ -2116,12 +3229,10 @@ } /* -%!shared fh, finline +%!shared fh %! fh = @(x) x; -%! finline = inline ("x"); %!assert (is_function_handle (fh)) -%!assert (is_function_handle (finline)) %!assert (! is_function_handle ({fh})) %!assert (! is_function_handle (1)) diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-fcn-handle.h --- a/libinterp/octave-value/ov-fcn-handle.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-fcn-handle.h Thu Jun 11 01:22:45 2020 -0400 @@ -32,20 +32,130 @@ #include #include +#include "oct-map.h" #include "ov-base.h" #include "ov-fcn.h" #include "ov-typeinfo.h" +#include "stack-frame.h" #include "symscope.h" namespace octave { class interpreter; - class stack_frame; 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& 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; + } + + 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; + }; } -// Function handles. - class OCTINTERP_API octave_fcn_handle : public octave_base_value @@ -54,28 +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 (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); + + // Create a handle to a built-in or internal function. + octave_fcn_handle (const octave_value& fcn); - octave_fcn_handle (const octave::symbol_scope& scope, const std::string& n); + // 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); + + // 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_fcn_handle (const octave::symbol_scope& scope, - const octave_value& f, - const std::string& n = anonymous); + // 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); + + // Create a function handle bound to a scoped function. + octave_fcn_handle (const octave_value& fcn, const std::string& name, + const std::list& parentage); - octave_fcn_handle (const octave_value& f, - const std::string& n = anonymous); + // Create a handle to a nested function. + octave_fcn_handle (const octave_value& fcn, const std::string& name, + const std::shared_ptr& closure_frames); - octave_fcn_handle (const octave_fcn_handle& fh) = default; + // 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_fcn_handle (void); + octave_fcn_handle (const octave_fcn_handle& fh); + + ~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 @@ -91,43 +229,71 @@ octave_value_list subsref (const std::string& type, const std::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); @@ -147,57 +313,28 @@ 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::list *m_closure_frames; - - // The name of the class in which this handle was created, if any. - // Used to determine access permission when the referenced function is - // 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 { + OCTAVE_DEPRECATED (6, "use 'tree_evaluator::make_fcn_handle' instead") extern octave_value make_fcn_handle (interpreter& interp, const std::string& name); } diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-fcn-inline.cc --- a/libinterp/octave-value/ov-fcn-inline.cc Tue Jun 09 14:11:13 2020 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1003 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2004-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or . -// -// This file is part of Octave. -// -// Octave is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Octave is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Octave; see the file COPYING. If not, see -// . -// -// In addition to the terms of the GPL, you are permitted to link -// this program with any Open Source program, as defined by the -// Open Source Initiative (www.opensource.org) -// -//////////////////////////////////////////////////////////////////////// - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include -#include -#include -#include - -#include "oct-locbuf.h" - -#include "defun.h" -#include "error.h" -#include "errwarn.h" -#include "interpreter-private.h" -#include "interpreter.h" -#include "oct-hdf5.h" -#include "oct-map.h" -#include "ov-base.h" -#include "ov-fcn-inline.h" -#include "ov-usr-fcn.h" -#include "parse.h" -#include "pr-output.h" -#include "variables.h" - -#include "byte-swap.h" -#include "ls-ascii-helper.h" -#include "ls-oct-text.h" -#include "ls-hdf5.h" -#include "ls-utils.h" - - -DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_fcn_inline, - "inline function", - "function_handle"); - -octave_fcn_inline::octave_fcn_inline (const std::string& f, - const string_vector& a, - const std::string& n) - : octave_fcn_handle (n), m_text (f), m_args (a) -{ - // Form a string representing the function. - - std::ostringstream buf; - - buf << "@("; - - for (int i = 0; i < m_args.numel (); i++) - { - if (i > 0) - buf << ", "; - - buf << m_args(i); - } - - buf << ") " << m_text; - - octave::interpreter& interp - = octave::__get_interpreter__ ("octave_fcn_inline::octave_fcn_inline"); - - int parse_status; - octave_value anon_fcn_handle - = interp.eval_string (buf.str (), true, parse_status); - - if (parse_status == 0) - { - octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value (); - - if (fh) - { - m_fcn = fh->fcn_val (); - - octave_user_function *uf = m_fcn.user_function_value (); - - if (uf) - { - octave::tree_evaluator& tw = interp.get_evaluator (); - - octave_function *curr_fcn = tw.current_function (); - - if (curr_fcn) - { - octave::symbol_scope parent_scope - = curr_fcn->parent_fcn_scope (); - - if (! parent_scope) - parent_scope = curr_fcn->scope (); - - uf->stash_parent_fcn_scope (parent_scope); - } - } - } - } - - if (m_fcn.is_undefined ()) - error ("inline: unable to define function"); -} - -// This function is supplied to allow a Matlab style class structure -// to be returned.. -octave_map -octave_fcn_inline::map_value (void) const -{ - octave_scalar_map m; - - m.assign ("version", 1.0); - m.assign ("isEmpty", 0.0); - m.assign ("expr", fcn_text ()); - - string_vector args = fcn_arg_names (); - - m.assign ("numArgs", args.numel ()); - m.assign ("args", args); - - std::ostringstream buf; - - for (int i = 0; i < args.numel (); i++) - buf << args(i) << " = INLINE_INPUTS_{" << i + 1 << "}; "; - - m.assign ("inputExpr", buf.str ()); - - return m; -} - -bool -octave_fcn_inline::save_ascii (std::ostream& os) -{ - os << "# nargs: " << m_args.numel () << "\n"; - for (int i = 0; i < m_args.numel (); i++) - os << m_args(i) << "\n"; - if (m_name.length () < 1) - // Write an invalid value to flag empty fcn handle name. - os << "0\n"; - else - os << m_name << "\n"; - os << m_text << "\n"; - return true; -} - -bool -octave_fcn_inline::load_ascii (std::istream& is) -{ - int nargs; - if (extract_keyword (is, "nargs", nargs, true)) - { - m_args.resize (nargs); - for (int i = 0; i < nargs; i++) - is >> m_args(i); - is >> m_name; - if (m_name == "0") - m_name = ""; - - 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); - } - - m_text = buf; - - octave_fcn_inline tmp (m_text, m_args, m_name); - m_fcn = tmp.m_fcn; - - return true; - } - else - return false; -} - -bool -octave_fcn_inline::save_binary (std::ostream& os, bool) -{ - int32_t tmp = m_args.numel (); - os.write (reinterpret_cast (&tmp), 4); - for (int i = 0; i < m_args.numel (); i++) - { - tmp = m_args(i).length (); - os.write (reinterpret_cast (&tmp), 4); - os.write (m_args(i).c_str (), m_args(i).length ()); - } - tmp = m_name.length (); - os.write (reinterpret_cast (&tmp), 4); - os.write (m_name.c_str (), m_name.length ()); - tmp = m_text.length (); - os.write (reinterpret_cast (&tmp), 4); - os.write (m_text.c_str (), m_text.length ()); - return true; -} - -bool -octave_fcn_inline::load_binary (std::istream& is, bool swap, - octave::mach_info::float_format) -{ - int32_t nargs; - if (! is.read (reinterpret_cast (&nargs), 4)) - return false; - if (swap) - swap_bytes<4> (&nargs); - - if (nargs < 1) - return false; - else - { - int32_t tmp; - m_args.resize (nargs); - for (int i = 0; i < nargs; i++) - { - if (! is.read (reinterpret_cast (&tmp), 4)) - return false; - if (swap) - swap_bytes<4> (&tmp); - - OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1); - is.read (ctmp, tmp); - m_args(i) = std::string (ctmp); - - if (! is) - return false; - } - - if (! is.read (reinterpret_cast (&tmp), 4)) - return false; - if (swap) - swap_bytes<4> (&tmp); - - OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1); - is.read (ctmp1, tmp); - m_name = std::string (ctmp1); - - if (! is) - return false; - - if (! is.read (reinterpret_cast (&tmp), 4)) - return false; - if (swap) - swap_bytes<4> (&tmp); - - OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1); - is.read (ctmp2, tmp); - m_text = std::string (ctmp2); - - if (! is) - return false; - - octave_fcn_inline ftmp (m_text, m_args, m_name); - m_fcn = ftmp.m_fcn; - } - return true; -} - -bool -octave_fcn_inline::save_hdf5 (octave_hdf5_id loc_id, const char *name, - bool /* save_as_floats */) -{ - bool retval = false; - -#if defined (HAVE_HDF5) - - 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; - - size_t len = 0; - for (int i = 0; i < m_args.numel (); i++) - if (len < m_args(i).length ()) - len = m_args(i).length (); - - hid_t space_hid, data_hid, type_hid; - space_hid = data_hid = type_hid = -1; - - // FIXME: Is there a better way of saving string vectors, - // than a null padded matrix? - - OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2); - - // Octave uses column-major, while HDF5 uses row-major ordering - hdims[1] = m_args.numel (); - hdims[0] = len + 1; - - space_hid = H5Screate_simple (2, hdims, nullptr); - if (space_hid < 0) - { - H5Gclose (group_hid); - return false; - } -#if defined (HAVE_HDF5_18) - data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid, - octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else - data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid, - octave_H5P_DEFAULT); -#endif - if (data_hid < 0) - { - H5Sclose (space_hid); - H5Gclose (group_hid); - return false; - } - - OCTAVE_LOCAL_BUFFER (char, s, m_args.numel () * (len + 1)); - - // Save the args as a null terminated list - for (int i = 0; i < m_args.numel (); i++) - { - const char *cptr = m_args(i).c_str (); - for (size_t j = 0; j < m_args(i).length (); j++) - s[i*(len+1)+j] = *cptr++; - s[m_args(i).length ()] = '\0'; - } - - retval = H5Dwrite (data_hid, H5T_NATIVE_CHAR, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, s) >= 0; - - H5Dclose (data_hid); - H5Sclose (space_hid); - - if (! retval) - { - H5Gclose (group_hid); - return false; - } - - // 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; - } - - hdims[0] = 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); - - // attach the type of the variable - H5Tset_size (type_hid, m_text.length () + 1); - if (type_hid < 0) - { - H5Gclose (group_hid); - return false; - } - -#if defined (HAVE_HDF5_18) - data_hid = H5Dcreate (group_hid, "iftext", type_hid, space_hid, - octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT); -#else - data_hid = H5Dcreate (group_hid, "iftext", 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_text.c_str ()) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - - H5Dclose (data_hid); - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - -#else - octave_unused_parameter (loc_id); - octave_unused_parameter (name); - - warn_save ("hdf5"); -#endif - - return retval; -} - -bool -octave_fcn_inline::load_hdf5 (octave_hdf5_id loc_id, const char *name) -{ -#if defined (HAVE_HDF5) - - 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); -#else - group_hid = H5Gopen (loc_id, name); -#endif - if (group_hid < 0) return false; - -#if defined (HAVE_HDF5_18) - data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT); -#else - data_hid = H5Dopen (group_hid, "args"); -#endif - space_hid = H5Dget_space (data_hid); - rank = H5Sget_simple_extent_ndims (space_hid); - - if (rank != 2) - { - H5Dclose (data_hid); - H5Sclose (space_hid); - H5Gclose (group_hid); - return false; - } - - OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank); - OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank); - - H5Sget_simple_extent_dims (space_hid, hdims, maxdims); - - m_args.resize (hdims[1]); - - OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]); - - if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL, - octave_H5P_DEFAULT, s1) < 0) - { - H5Dclose (data_hid); - H5Sclose (space_hid); - H5Gclose (group_hid); - return false; - } - - H5Dclose (data_hid); - H5Sclose (space_hid); - - for (size_t i = 0; i < hdims[1]; i++) - m_args(i) = std::string (s1 + i*hdims[0]); - -#if defined (HAVE_HDF5_18) - data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT); -#else - data_hid = H5Dopen (group_hid, "nm"); -#endif - - if (data_hid < 0) - { - H5Gclose (group_hid); - return false; - } - - type_hid = H5Dget_type (data_hid); - type_class_hid = H5Tget_class (type_hid); - - if (type_class_hid != H5T_STRING) - { - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - 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, nm_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, nm_tmp) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - H5Tclose (st_id); - H5Dclose (data_hid); - m_name = nm_tmp; - -#if defined (HAVE_HDF5_18) - data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT); -#else - data_hid = H5Dopen (group_hid, "iftext"); -#endif - - if (data_hid < 0) - { - H5Gclose (group_hid); - return false; - } - - type_hid = H5Dget_type (data_hid); - type_class_hid = H5Tget_class (type_hid); - - if (type_class_hid != H5T_STRING) - { - H5Tclose (type_hid); - H5Dclose (data_hid); - H5Gclose (group_hid); - return false; - } - - 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, iftext_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, iftext_tmp) < 0) - { - H5Sclose (space_hid); - H5Tclose (type_hid); - H5Gclose (group_hid); - return false; - } - H5Tclose (st_id); - H5Dclose (data_hid); - m_text = iftext_tmp; - - octave_fcn_inline ftmp (m_text, m_args, m_name); - m_fcn = ftmp.m_fcn; - - return true; - -#else - octave_unused_parameter (loc_id); - octave_unused_parameter (name); - - warn_load ("hdf5"); - - return false; -#endif -} - -void -octave_fcn_inline::print (std::ostream& os, bool pr_as_read_syntax) -{ - print_raw (os, pr_as_read_syntax); - newline (os); -} - -void -octave_fcn_inline::print_raw (std::ostream& os, bool pr_as_read_syntax) const -{ - std::ostringstream buf; - - if (m_name.empty ()) - buf << "f("; - else - buf << m_name << '('; - - for (int i = 0; i < m_args.numel (); i++) - { - if (i) - buf << ", "; - - buf << m_args(i); - } - - buf << ") = " << m_text; - - octave_print_internal (os, buf.str (), pr_as_read_syntax, - current_print_indent_level ()); -} - -octave_value -octave_fcn_inline::convert_to_str_internal (bool, bool, char type) const -{ - return octave_value (fcn_text (), type); -} - -DEFUNX ("inline", Finline, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} inline (@var{str}) -@deftypefnx {} {} inline (@var{str}, @var{arg1}, @dots{}) -@deftypefnx {} {} inline (@var{str}, @var{n}) -Create an inline function from the character string @var{str}. - -If called with a single argument, the arguments of the generated function -are extracted from the function itself. The generated function arguments -will then be in alphabetical order. It should be noted that i and j are -ignored as arguments due to the ambiguity between their use as a variable or -their use as an built-in constant. All arguments followed by a parenthesis -are considered to be functions. If no arguments are found, a function -taking a single argument named @code{x} will be created. - -If the second and subsequent arguments are character strings, they are the -names of the arguments of the function. - -If the second argument is an integer @var{n}, the arguments are -@qcode{"x"}, @qcode{"P1"}, @dots{}, @qcode{"P@var{N}"}. - -Programming Note: The use of @code{inline} is discouraged and it may be -removed from a future version of Octave. The preferred way to create -functions from strings is through the use of anonymous functions -(@pxref{Anonymous Functions}) or @code{str2func}. -@seealso{argnames, formula, vectorize, str2func} -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin == 0) - print_usage (); - - std::string fun = args(0).xstring_value ("inline: STR argument must be a string"); - - string_vector fargs; - - if (nargin == 1) - { - bool is_arg = false; - bool in_string = false; - std::string tmp_arg; - size_t i = 0; - size_t fun_length = fun.length (); - - while (i < fun_length) - { - bool terminate_arg = false; - char c = fun[i++]; - - if (in_string) - { - if (c == '\'' || c == '\"') - in_string = false; - } - else if (c == '\'' || c == '\"') - { - in_string = true; - if (is_arg) - terminate_arg = true; - } - else if (! isalpha (c) && c != '_') - if (! is_arg) - continue; - else if (isdigit (c)) - tmp_arg.append (1, c); - else - { - // Before we do anything remove trailing whitespaces. - while (i < fun_length && isspace (c)) - c = fun[i++]; - - // Do we have a variable or a function? - if (c != '(') - terminate_arg = true; - else - { - tmp_arg = ""; - is_arg = false; - } - } - else if (! is_arg) - { - if (c == 'e' || c == 'E') - { - // possible number in exponent form, not arg - if (isdigit (fun[i]) || fun[i] == '-' || fun[i] == '+') - continue; - } - is_arg = true; - tmp_arg.append (1, c); - } - else - { - tmp_arg.append (1, c); - } - - if (terminate_arg || (i == fun_length && is_arg)) - { - bool have_arg = false; - - for (int j = 0; j < fargs.numel (); j++) - if (tmp_arg == fargs (j)) - { - have_arg = true; - break; - } - - if (! have_arg && tmp_arg != "i" && tmp_arg != "j" - && tmp_arg != "NaN" && tmp_arg != "nan" - && tmp_arg != "Inf" && tmp_arg != "inf" - && tmp_arg != "NA" && tmp_arg != "pi" - && tmp_arg != "e" && tmp_arg != "eps") - fargs.append (tmp_arg); - - tmp_arg = ""; - is_arg = false; - } - } - - // Sort the arguments into ASCII order. - fargs.sort (); - - if (fargs.isempty ()) - fargs.append (std::string ("x")); - - } - else if (nargin == 2 && args(1).isnumeric ()) - { - if (! args(1).is_scalar_type ()) - error ("inline: N must be an integer"); - - int n = args(1).xint_value ("inline: N must be an integer"); - - if (n < 0) - error ("inline: N must be a positive integer or zero"); - - fargs.resize (n+1); - - fargs(0) = "x"; - - for (int i = 1; i < n+1; i++) - { - std::ostringstream buf; - buf << 'P' << i; - fargs(i) = buf.str (); - } - } - else - { - fargs.resize (nargin - 1); - - for (int i = 1; i < nargin; i++) - { - std::string s = args(i).xstring_value ("inline: additional arguments must be strings"); - fargs(i-1) = s; - } - } - - return ovl (new octave_fcn_inline (fun, fargs)); -} - -/* -%!shared fn -%! fn = inline ("x.^2 + 1"); -%!assert (feval (fn, 6), 37) -%!assert (fn (6), 37) -%!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10) -%!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5) -%!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5) - -## Test input validation -%!error inline () -%!error inline (1) -%!error inline ("2", ones (2,2)) -%!error inline ("2", -1) -%!error inline ("2", "x", -1, "y") -*/ - -DEFUN (formula, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} formula (@var{fun}) -Return a character string representing the inline function @var{fun}. - -Note that @code{char (@var{fun})} is equivalent to -@code{formula (@var{fun})}. -@seealso{char, argnames, inline, vectorize} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - octave_fcn_inline *fn = args(0).fcn_inline_value (true); - - if (! fn) - error ("formula: FUN must be an inline function"); - - return ovl (fn->fcn_text ()); -} - -/* -%!assert (formula (fn), "x.^2 + 1") -%!assert (formula (fn), char (fn)) - -## Test input validation -%!error formula () -%!error formula (1, 2) -%!error formula (1) -*/ - -DEFUN (argnames, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} argnames (@var{fun}) -Return a cell array of character strings containing the names of the -arguments of the inline function @var{fun}. -@seealso{inline, formula, vectorize} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - octave_fcn_inline *fn = args(0).fcn_inline_value (true); - - if (! fn) - error ("argnames: FUN must be an inline function"); - - string_vector t1 = fn->fcn_arg_names (); - - Cell t2 (dim_vector (t1.numel (), 1)); - - for (int i = 0; i < t1.numel (); i++) - t2(i) = t1(i); - - return ovl (t2); -} - -/* -%!assert (argnames (fn), {"x"}) -%!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"}) -%!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"}) - -## Test input validation -%!error argnames () -%!error argnames (1, 2) -%!error argnames (1) -*/ - -DEFUN (vectorize, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} vectorize (@var{fun}) -Create a vectorized version of the inline function @var{fun} by replacing -all occurrences of @code{*}, @code{/}, etc., with @code{.*}, @code{./}, etc. - -This may be useful, for example, when using inline functions with numerical -integration or optimization where a vector-valued function is expected. - -@example -@group -fcn = vectorize (inline ("x^2 - 1")) - @result{} fcn = f(x) = x.^2 - 1 -quadv (fcn, 0, 3) - @result{} 6 -@end group -@end example -@seealso{inline, formula, argnames} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - std::string old_func; - octave_fcn_inline *old = nullptr; - bool func_is_string = true; - - if (args(0).is_string ()) - old_func = args(0).string_value (); - else - { - func_is_string = false; - - old = args(0).fcn_inline_value (true); - if (! old) - error ("vectorize: FUN must be a string or inline function"); - - old_func = old->fcn_text (); - } - - size_t len = old_func.length (); - std::string new_func; - new_func.reserve (len + 10); - - size_t i = 0; - while (i < len) - { - char t1 = old_func[i]; - - if (t1 == '*' || t1 == '/' || t1 == '\\' || t1 == '^') - { - if (i && old_func[i-1] != '.') - new_func.push_back ('.'); - - // Special case for ** operator. - if (t1 == '*' && i < (len - 1) && old_func[i+1] == '*') - { - new_func.push_back ('*'); - i++; - } - } - new_func.push_back (t1); - i++; - } - - if (func_is_string) - return ovl (new_func); - else - return ovl (new octave_fcn_inline (new_func, old->fcn_arg_names ())); -} - -/* -%!assert (char (vectorize (fn)), "x.^2 + 1") -%!assert (char (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z") -%!assert (char (vectorize (inline ("2**x^5"))), "2.**x.^5") -%!assert (vectorize ("x.^2 + 1"), "x.^2 + 1") -%!assert (vectorize ("1e-3*y + 2e4*z"), "1e-3.*y + 2e4.*z") -%!assert (vectorize ("2**x^5"), "2.**x.^5") - -## Test input validation -%!error vectorize () -%!error vectorize (1, 2) -%!error vectorize (1) -*/ diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-fcn-inline.h --- a/libinterp/octave-value/ov-fcn-inline.h Tue Jun 09 14:11:13 2020 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2004-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or . -// -// This file is part of Octave. -// -// Octave is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Octave is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Octave; see the file COPYING. If not, see -// . -// -//////////////////////////////////////////////////////////////////////// - -#if ! defined (octave_ov_fcn_inline_h) -#define octave_ov_fcn_inline_h 1 - -#include "octave-config.h" - -#include -#include - -#include "ov-base.h" -#include "ov-base-mat.h" -#include "ov-fcn.h" -#include "ov-typeinfo.h" -#include "ov-fcn-handle.h" - -// Inline functions. - -class -OCTINTERP_API -octave_fcn_inline : public octave_fcn_handle -{ -public: - - octave_fcn_inline (void) - : octave_fcn_handle (), m_text (), m_args () { } - - octave_fcn_inline (const std::string& f, const string_vector& a, - const std::string& n = ""); - - octave_fcn_inline (const octave_fcn_inline& fi) - : octave_fcn_handle (fi), m_text (fi.m_text), m_args (fi.m_args) { } - - ~octave_fcn_inline (void) = default; - - octave_base_value * clone (void) const - { return new octave_fcn_inline (*this); } - octave_base_value * empty_clone (void) const - { return new octave_fcn_inline (); } - - bool is_inline_function (void) const { return true; } - - octave_fcn_inline * fcn_inline_value (bool = false) { return this; } - - std::string fcn_text (void) const { return m_text; } - - string_vector fcn_arg_names (void) const { return m_args; } - - octave_value convert_to_str_internal (bool, bool, char) const; - - octave_map map_value (void) const; - - 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, - octave::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 loc_id, const char *name); - - void print (std::ostream& os, bool pr_as_read_syntax = false); - - void print_raw (std::ostream& os, bool pr_as_read_syntax = false) const; - -private: - - DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA - - // The expression of an inline function. - std::string m_text; - - // The args of an inline function. - string_vector m_args; -}; - -#endif diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-fcn.cc --- a/libinterp/octave-value/ov-fcn.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-fcn.cc Thu Jun 11 01:22:45 2020 -0400 @@ -27,10 +27,12 @@ # include "config.h" #endif +#include "unwind-prot.h" + #include "error.h" #include "ovl.h" #include "ov-fcn.h" - +#include "pt-eval.h" octave_base_value * octave_function::clone (void) const @@ -46,11 +48,11 @@ octave_value_list octave_function::call (octave::tree_evaluator& tw, int nargout, - const octave_value_list& args, - octave::stack_frame *closure_context) + const octave_value_list& args) { - if (closure_context) - panic_impossible (); + tw.push_stack_frame (this); - return call (tw, nargout, args); + octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); }); + + return execute (tw, nargout, args); } diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-fcn.h --- a/libinterp/octave-value/ov-fcn.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-fcn.h Thu Jun 11 01:22:45 2020 -0400 @@ -87,6 +87,11 @@ virtual octave::symbol_scope parent_fcn_scope (void) const { return octave::symbol_scope (); } + virtual std::list parent_fcn_names (void) const + { + return std::list (); + } + virtual void mark_fcn_file_up_to_date (const octave::sys::time&) { } virtual octave::symbol_scope scope (void) { return octave::symbol_scope (); } @@ -99,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 @@ -227,14 +236,16 @@ virtual bool accepts_postfix_index (char type) const { return (type == '('); } + // Push new stack frame (if necessary) and execute function. virtual octave_value_list call (octave::tree_evaluator& tw, int nargout = 0, - const octave_value_list& args = octave_value_list ()) = 0; + const octave_value_list& args = octave_value_list ()); + // Execute function without pushing new stack frame (assumes that has + // already been done). virtual octave_value_list - call (octave::tree_evaluator& tw, int nargout, - const octave_value_list& args, - octave::stack_frame *closure_context); + execute (octave::tree_evaluator& tw, int nargout = 0, + const octave_value_list& args = octave_value_list ()) = 0; protected: diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-mex-fcn.cc --- a/libinterp/octave-value/ov-mex-fcn.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-mex-fcn.cc Thu Jun 11 01:22:45 2020 -0400 @@ -91,20 +91,14 @@ int nargout); octave_value_list -octave_mex_function::call (octave::tree_evaluator& tw, int nargout, - const octave_value_list& args) +octave_mex_function::execute (octave::tree_evaluator& tw, int nargout, + const octave_value_list& args) { octave_value_list retval; if (args.has_magic_colon ()) error ("invalid use of colon in function argument list"); - octave::unwind_protect frame; - - tw.push_stack_frame (this); - - frame.add_method (tw, &octave::tree_evaluator::pop_stack_frame); - octave::profiler& profiler = tw.get_profiler (); octave::profiler::enter block (profiler, *this); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-mex-fcn.h --- a/libinterp/octave-value/ov-mex-fcn.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-mex-fcn.h Thu Jun 11 01:22:45 2020 -0400 @@ -92,14 +92,9 @@ bool use_interleaved_complex (void) const { return m_interleaved; } - // We don't need to override both forms of the call method. The using - // declaration will avoid warnings about partially-overloaded virtual - // functions. - using octave_function::call; - octave_value_list - call (octave::tree_evaluator& tw, int nargout = 0, - const octave_value_list& args = octave_value_list ()); + execute (octave::tree_evaluator& tw, int nargout = 0, + const octave_value_list& args = octave_value_list ()); void atexit (void (*fcn) (void)) { m_exit_fcn_ptr = fcn; } diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-typeinfo.cc --- a/libinterp/octave-value/ov-typeinfo.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-typeinfo.cc Thu Jun 11 01:22:45 2020 -0400 @@ -991,8 +991,6 @@ %!assert (typeinfo (@sin), "function handle") %!assert (typeinfo (@(x) x), "function handle") -%!assert (typeinfo (inline ("x^2")), "inline function") - %!assert (typeinfo (single (1)), "float scalar") %!assert (typeinfo (single (i)), "float complex scalar") %!assert (typeinfo (single ([1, 2])), "float matrix") diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-usr-fcn.cc --- a/libinterp/octave-value/ov-usr-fcn.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-usr-fcn.cc Thu Jun 11 01:22:45 2020 -0400 @@ -173,10 +173,26 @@ : octave_user_code (fnm, nm, scope, nullptr, ds) { } +// We must overload the call method so that we call the proper +// push_stack_frame method, which is overloaded for pointers to +// octave_function, octave_user_function, and octave_user_script +// objects. + octave_value_list octave_user_script::call (octave::tree_evaluator& tw, int nargout, const octave_value_list& args) { + tw.push_stack_frame (this); + + octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); }); + + return execute (tw, nargout, args); +} + +octave_value_list +octave_user_script::execute (octave::tree_evaluator& tw, int nargout, + const octave_value_list& args) +{ return tw.execute_user_script (*this, nargout, args); } @@ -197,11 +213,9 @@ octave_user_function::octave_user_function (const octave::symbol_scope& scope, octave::tree_parameter_list *pl, - octave::tree_parameter_list *rl, octave::tree_statement_list *cl, - const local_vars_map& lviv) + octave::tree_parameter_list *rl, octave::tree_statement_list *cl) : octave_user_code ("", "", scope, cl, ""), param_list (pl), ret_list (rl), - m_local_var_init_vals (lviv), lead_comm (), trail_comm (), location_line (0), location_column (0), parent_name (), system_fcn_file (false), @@ -459,12 +473,27 @@ return retval; } +// We must overload the call method so that we call the proper +// push_stack_frame method, which is overloaded for pointers to +// octave_function, octave_user_function, and octave_user_script +// objects. + octave_value_list octave_user_function::call (octave::tree_evaluator& tw, int nargout, - const octave_value_list& args, - octave::stack_frame *closure_frames) + const octave_value_list& args) { - return tw.execute_user_function (*this, nargout, args, closure_frames); + tw.push_stack_frame (this); + + octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); }); + + return execute (tw, nargout, args); +} + +octave_value_list +octave_user_function::execute (octave::tree_evaluator& tw, int nargout, + const octave_value_list& args) +{ + return tw.execute_user_function (*this, nargout, args); } void diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov-usr-fcn.h --- a/libinterp/octave-value/ov-usr-fcn.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov-usr-fcn.h Thu Jun 11 01:22:45 2020 -0400 @@ -186,15 +186,19 @@ bool is_user_script (void) const { return true; } - // We don't need to override both forms of the call method. The using - // declaration will avoid warnings about partially-overloaded virtual - // functions. - using octave_user_code::call; + // We must overload the call method so that we call the proper + // push_stack_frame method, which is overloaded for pointers to + // octave_function, octave_user_function, and octave_user_script + // objects. octave_value_list call (octave::tree_evaluator& tw, int nargout = 0, const octave_value_list& args = octave_value_list ()); + octave_value_list + execute (octave::tree_evaluator& tw, int nargout = 0, + const octave_value_list& args = octave_value_list ()); + void accept (octave::tree_walker& tw); private: @@ -209,13 +213,10 @@ { public: - typedef std::map local_vars_map; - octave_user_function (const octave::symbol_scope& scope = octave::symbol_scope (), octave::tree_parameter_list *pl = nullptr, octave::tree_parameter_list *rl = nullptr, - octave::tree_statement_list *cl = nullptr, - const local_vars_map& lviv = local_vars_map ()); + octave::tree_statement_list *cl = nullptr); // No copying! @@ -272,6 +273,11 @@ return m_scope.parent_scope (); } + std::list parent_fcn_names (void) const + { + return m_scope.parent_fcn_names (); + } + void mark_as_system_fcn_file (void); bool is_system_fcn_file (void) const { return system_fcn_file; } @@ -372,16 +378,18 @@ ? (cname.empty () ? true : cname == dispatch_class ()) : false); } + // We must overload the call method so that we call the proper + // push_stack_frame method, which is overloaded for pointers to + // octave_function, octave_user_function, and octave_user_script + // objects. + octave_value_list call (octave::tree_evaluator& tw, int nargout = 0, - const octave_value_list& args = octave_value_list ()) - { - return call (tw, nargout, args, nullptr); - } + const octave_value_list& args = octave_value_list ()); octave_value_list - call (octave::tree_evaluator& tw, int nargout, - const octave_value_list& args, octave::stack_frame *); + execute (octave::tree_evaluator& tw, int nargout = 0, + const octave_value_list& args = octave_value_list ()); octave::tree_parameter_list * parameter_list (void) { return param_list; } @@ -391,11 +399,6 @@ octave::comment_list * trailing_comment (void) { return trail_comm; } - const local_vars_map& local_var_init_vals (void) const - { - return m_local_var_init_vals; - } - // If is_special_expr is true, retrieve the sigular expression that forms the // body. May be null (even if is_special_expr is true). octave::tree_expression * special_expr (void); @@ -431,9 +434,6 @@ // this function. octave::tree_parameter_list *ret_list; - // For anonymous function values inherited from parent scope. - local_vars_map m_local_var_init_vals; - // The comments preceding the FUNCTION token. octave::comment_list *lead_comm; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov.cc --- a/libinterp/octave-value/ov.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov.cc Thu Jun 11 01:22:45 2020 -0400 @@ -75,7 +75,6 @@ #include "ov-dld-fcn.h" #include "ov-usr-fcn.h" #include "ov-fcn-handle.h" -#include "ov-fcn-inline.h" #include "ov-typeinfo.h" #include "ov-null-mat.h" #include "ov-lazy-idx.h" @@ -1694,12 +1693,6 @@ return rep->fcn_handle_value (silent); } -octave_fcn_inline * -octave_value::fcn_inline_value (bool silent) const -{ - return rep->fcn_inline_value (silent); -} - octave_value_list octave_value::list_value (void) const { @@ -2119,7 +2112,6 @@ XVALUE_EXTRACTOR (octave_user_script *, xuser_script_value, user_script_value) XVALUE_EXTRACTOR (octave_user_code *, xuser_code_value, user_code_value) XVALUE_EXTRACTOR (octave_fcn_handle *, xfcn_handle_value, fcn_handle_value) -XVALUE_EXTRACTOR (octave_fcn_inline *, xfcn_inline_value, fcn_inline_value) XVALUE_EXTRACTOR (octave_value_list, xlist_value, list_value) @@ -3031,7 +3023,6 @@ octave_user_function::register_type (ti); octave_dld_function::register_type (ti); octave_fcn_handle::register_type (ti); - octave_fcn_inline::register_type (ti); octave_float_scalar::register_type (ti); octave_float_complex::register_type (ti); octave_float_matrix::register_type (ti); diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/octave-value/ov.h --- a/libinterp/octave-value/ov.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/octave-value/ov.h Thu Jun 11 01:22:45 2020 -0400 @@ -57,7 +57,6 @@ class octave_function; class octave_user_function; class octave_fcn_handle; -class octave_fcn_inline; class octave_value_list; #include "mxtypes.h" @@ -976,8 +975,6 @@ octave_fcn_handle * fcn_handle_value (bool silent = false) const; - octave_fcn_inline * fcn_inline_value (bool silent = false) const; - octave_value_list list_value (void) const; ColumnVector column_vector_value (bool frc_str_conv = false, @@ -1195,7 +1192,6 @@ octave_user_script * xuser_script_value (const char *fmt, ...) const; octave_user_code * xuser_code_value (const char *fmt, ...) const; octave_fcn_handle * xfcn_handle_value (const char *fmt, ...) const; - octave_fcn_inline * xfcn_inline_value (const char *fmt, ...) const; octave_value_list xlist_value (const char *fmt, ...) const; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/operators/op-fcn.cc --- a/libinterp/operators/op-fcn.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/operators/op-fcn.cc Thu Jun 11 01:22:45 2020 -0400 @@ -40,7 +40,7 @@ const octave_fcn_handle& v1 = dynamic_cast (a1); const octave_fcn_handle& v2 = dynamic_cast (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 (a1); const octave_fcn_handle& v2 = dynamic_cast (a2); - return ! v1.is_equal_to (v2); + return ! is_equal_to (v1, v2); } void diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/parse-tree/oct-lvalue.cc --- a/libinterp/parse-tree/oct-lvalue.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/parse-tree/oct-lvalue.cc Thu Jun 11 01:22:45 2020 -0400 @@ -36,7 +36,7 @@ { bool octave_lvalue::is_defined (void) const { - return ! is_black_hole () && m_frame.is_defined (m_sym); + return ! is_black_hole () && m_frame->is_defined (m_sym); } bool octave_lvalue::is_undefined (void) const @@ -46,14 +46,14 @@ void octave_lvalue::define (const octave_value& v) { - m_frame.assign (m_sym, v); + m_frame->assign (m_sym, v); } void octave_lvalue::assign (octave_value::assign_op op, const octave_value& rhs) { if (! is_black_hole ()) - m_frame.assign (op, m_sym, m_type, m_idx, rhs); + m_frame->assign (op, m_sym, m_type, m_idx, rhs); } void octave_lvalue::set_index (const std::string& t, @@ -97,12 +97,12 @@ void octave_lvalue::do_unary_op (octave_value::unary_op op) { if (! is_black_hole ()) - m_frame.do_non_const_unary_op (op, m_sym, m_type, m_idx); + m_frame->do_non_const_unary_op (op, m_sym, m_type, m_idx); } octave_value octave_lvalue::value (void) const { return (is_black_hole () - ? octave_value () : m_frame.value (m_sym, m_type, m_idx)); + ? octave_value () : m_frame->value (m_sym, m_type, m_idx)); } } diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/parse-tree/oct-lvalue.h --- a/libinterp/parse-tree/oct-lvalue.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/parse-tree/oct-lvalue.h Thu Jun 11 01:22:45 2020 -0400 @@ -40,7 +40,8 @@ { public: - octave_lvalue (const symbol_record& sr, stack_frame& frame) + octave_lvalue (const symbol_record& sr, + const std::shared_ptr& frame) : m_sym (sr), m_frame (frame), m_black_hole (false), m_type (), m_idx (), m_nel (1) { } @@ -87,7 +88,7 @@ symbol_record m_sym; - stack_frame& m_frame; + std::shared_ptr m_frame; bool m_black_hole; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/parse-tree/oct-parse.yy --- a/libinterp/parse-tree/oct-parse.yy Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/parse-tree/oct-parse.yy Thu Jun 11 01:22:45 2020 -0400 @@ -2530,7 +2530,10 @@ if (! m_lexer.m_reading_script_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) - m_primary_fcn_scope = m_lexer.m_symtab_context.curr_scope (); + { + m_primary_fcn_scope = m_lexer.m_symtab_context.curr_scope (); + m_primary_fcn_scope.mark_primary_fcn_scope (); + } if (m_lexer.m_reading_script_file && m_curr_fcn_depth > 0) { @@ -3722,15 +3725,16 @@ if (fcn) { - std::string nm = fcn->name (); + std::string fcn_nm = fcn->name (); std::string file = fcn->fcn_file_name (); - std::string tmp = nm; + std::string tmp = fcn_nm; if (! file.empty ()) tmp += ": " + file; symbol_scope fcn_scope = fcn->scope (); fcn_scope.cache_name (tmp); + fcn_scope.cache_fcn_name (fcn_nm); fcn_scope.cache_fcn_file_name (file); fcn_scope.cache_dir_name (m_lexer.m_dir_name); @@ -3754,14 +3758,27 @@ symbol_scope pscope = m_function_scopes.parent_scope (); fcn_scope.set_parent (pscope); fcn_scope.set_primary_parent (m_primary_fcn_scope); - pscope.install_nestfunction (nm, ov_fcn, fcn_scope); + pscope.install_nestfunction (fcn_nm, ov_fcn, fcn_scope); + + // For nested functions, the list of parent functions is + // set in symbol_scope::update_nest. } else { fcn->mark_as_subfunction (); - m_subfunction_names.push_back (nm); + m_subfunction_names.push_back (fcn_nm); fcn_scope.set_parent (m_primary_fcn_scope); - m_primary_fcn_scope.install_subfunction (nm, ov_fcn); + if (m_parsing_subfunctions) + fcn_scope.set_primary_parent (m_primary_fcn_scope); + m_primary_fcn_scope.install_subfunction (fcn_nm, ov_fcn); + + // Prepend name of primary fucntion to list of parent + // functions (if any) for subfunction. + + std::list plst + = fcn_scope.parent_fcn_names (); + plst.push_front (m_primary_fcn_scope.fcn_name ()); + fcn_scope.cache_parent_fcn_names (plst); } } diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/parse-tree/pt-eval.cc --- a/libinterp/parse-tree/pt-eval.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/parse-tree/pt-eval.cc Thu Jun 11 01:22:45 2020 -0400 @@ -61,6 +61,7 @@ #include "pt-anon-scopes.h" #include "pt-eval.h" #include "pt-tm-const.h" +#include "stack-frame.h" #include "symtab.h" #include "unwind-prot.h" #include "utils.h" @@ -187,7 +188,7 @@ if (! silent) { - stack_frame *frm = tw.current_user_frame (); + std::shared_ptr frm = tw.current_user_frame (); frm->display_stopped_in_message (buf); } @@ -881,6 +882,364 @@ return retval; } + // If NAME is an operator (like "+", "-", ...), convert it to the + // corresponding function name ("plus", "minus", ...). + + static std::string + get_operator_function_name (const std::string& name) + { + // Bow to the god of compatibility. + + // FIXME: it seems ugly to put this here, but there is no single + // function in the parser that converts from the operator name to + // the corresponding function name. At least try to do it without N + // string compares. + + size_t len = name.length (); + + if (len == 3 && name == ".**") + return "power"; + else if (len == 2) + { + if (name[0] == '.') + { + switch (name[1]) + { + case '\'': + return "transpose"; + + case '+': + return "plus"; + + case '-': + return "minus"; + + case '*': + return "times"; + + case '/': + return "rdivide"; + + case '^': + return "power"; + + case '\\': + return "ldivide"; + + default: + break; + } + } + else if (name[1] == '=') + { + switch (name[0]) + { + case '<': + return "le"; + + case '=': + return "eq"; + + case '>': + return "ge"; + + case '~': + case '!': + return "ne"; + + default: + break; + } + } + else if (name == "**") + return "mpower"; + } + else if (len == 1) + { + switch (name[0]) + { + case '~': + case '!': + return "not"; + + case '\'': + return "ctranspose"; + + case '+': + return "plus"; + + case '-': + return "minus"; + + case '*': + return "mtimes"; + + case '/': + return "mrdivide"; + + case '^': + return "mpower"; + + case '\\': + return "mldivide"; + + case '<': + return "lt"; + + case '>': + return "gt"; + + case '&': + return "and"; + + case '|': + return "or"; + + default: + break; + } + } + + 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 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 (); + + 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 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 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)); + } + +/* +%!test +%! x = {".**", "power"; +%! ".'", "transpose"; +%! ".+", "plus"; +%! ".-", "minus"; +%! ".*", "times"; +%! "./", "rdivide"; +%! ".^", "power"; +%! ".\\", "ldivide"; +%! "<=", "le"; +%! "==", "eq"; +%! ">=", "ge"; +%! "!=", "ne"; +%! "~=", "ne"; +%! "**", "mpower"; +%! "~", "not"; +%! "!", "not"; +%! "\'", "ctranspose"; +%! "+", "plus"; +%! "-", "minus"; +%! "*", "mtimes"; +%! "/", "mrdivide"; +%! "^", "mpower"; +%! "\\", "mldivide"; +%! "<", "lt"; +%! ">", "gt"; +%! "&", "and"; +%! "|", "or"}; +%! for i = 1:rows (x) +%! assert (functions (str2func (x{i,1})).function, x{i,2}); +%! endfor +*/ + octave_value tree_evaluator::evaluate (tree_decl_elt *elt) { @@ -894,17 +1253,19 @@ bool tree_evaluator::is_variable (const std::string& name) const { - const stack_frame& frame = m_call_stack.get_current_stack_frame (); - - return frame.is_variable (name); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + return frame->is_variable (name); } bool tree_evaluator::is_local_variable (const std::string& name) const { - const stack_frame& frame = m_call_stack.get_current_stack_frame (); - - return frame.is_local_variable (name); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + return frame->is_local_variable (name); } bool @@ -941,49 +1302,55 @@ bool tree_evaluator::is_variable (const symbol_record& sym) const { - const stack_frame& frame = m_call_stack.get_current_stack_frame (); - - return frame.is_variable (sym); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + return frame->is_variable (sym); } bool tree_evaluator::is_defined (const symbol_record& sym) const { - const stack_frame& frame = m_call_stack.get_current_stack_frame (); - - return frame.is_defined (sym); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + return frame->is_defined (sym); } bool tree_evaluator::is_global (const std::string& name) const { - const stack_frame& frame = m_call_stack.get_current_stack_frame (); - - return frame.is_global (name); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + return frame->is_global (name); } octave_value tree_evaluator::varval (const symbol_record& sym) const { - const stack_frame& frame = m_call_stack.get_current_stack_frame (); - - return frame.varval (sym); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + return frame->varval (sym); } octave_value tree_evaluator::varval (const std::string& name) const { - const stack_frame& frame = m_call_stack.get_current_stack_frame (); - - return frame.varval (name); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + return frame->varval (name); } void tree_evaluator::install_variable (const std::string& name, const octave_value& value, bool global) { - stack_frame& frame = m_call_stack.get_current_stack_frame (); - - return frame.install_variable (name, value, global); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + return frame->install_variable (name, value, global); } octave_value @@ -1021,9 +1388,10 @@ void tree_evaluator::assign (const std::string& name, const octave_value& val) { - stack_frame& frame = m_call_stack.get_current_stack_frame (); - - frame.assign (name, val); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + frame->assign (name, val); } void @@ -1520,16 +1888,20 @@ } void tree_evaluator::push_stack_frame (octave_user_function *fcn, - unwind_protect *up_frame, - stack_frame *closure_frames) + const std::shared_ptr& closure_frames) { - m_call_stack.push (fcn, up_frame, closure_frames); + m_call_stack.push (fcn, closure_frames); } - void tree_evaluator::push_stack_frame (octave_user_script *script, - unwind_protect *up_frame) + void tree_evaluator::push_stack_frame (octave_user_function *fcn, + const stack_frame::local_vars_map& local_vars) { - m_call_stack.push (script, up_frame); + m_call_stack.push (fcn, local_vars); + } + + void tree_evaluator::push_stack_frame (octave_user_script *script) + { + m_call_stack.push (script); } void tree_evaluator::push_stack_frame (octave_function *fcn) @@ -1564,7 +1936,7 @@ void tree_evaluator::debug_where (std::ostream& os) const { - stack_frame *frm = m_call_stack.current_user_frame (); + std::shared_ptr frm = m_call_stack.current_user_frame (); frm->display_stopped_in_message (os); } @@ -1574,7 +1946,7 @@ return m_call_stack.current_user_code (); } - unwind_protect * tree_evaluator::curr_fcn_unwind_protect_frame (void) const + unwind_protect * tree_evaluator::curr_fcn_unwind_protect_frame (void) { return m_call_stack.curr_fcn_unwind_protect_frame (); } @@ -1636,13 +2008,13 @@ return m_call_stack.is_class_constructor_executing (dclass); } - std::list + std::list> tree_evaluator::backtrace_frames (octave_idx_type& curr_user_frame) const { return m_call_stack.backtrace_frames (curr_user_frame); } - std::list + std::list> tree_evaluator::backtrace_frames (void) const { return m_call_stack.backtrace_frames (); @@ -1782,9 +2154,10 @@ octave_value tree_evaluator::find (const std::string& name) { - const stack_frame& frame = m_call_stack.get_current_stack_frame (); - - octave_value val = frame.varval (name); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + octave_value val = frame->varval (name); if (val.is_defined ()) return val; @@ -1793,7 +2166,7 @@ // subfunctions if we are currently executing a function defined // from a .m file. - octave_value fcn = frame.find_subfunction (name); + octave_value fcn = frame->find_subfunction (name); if (fcn.is_defined ()) return fcn; @@ -1805,37 +2178,42 @@ void tree_evaluator::clear_objects (void) { - stack_frame& frame = m_call_stack.get_current_stack_frame (); - - frame.clear_objects (); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + frame->clear_objects (); } void tree_evaluator::clear_variable (const std::string& name) { - stack_frame& frame = m_call_stack.get_current_stack_frame (); - - frame.clear_variable (name); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + frame->clear_variable (name); } void tree_evaluator::clear_variable_pattern (const std::string& pattern) { - stack_frame& frame = m_call_stack.get_current_stack_frame (); - - frame.clear_variable_pattern (pattern); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + frame->clear_variable_pattern (pattern); } void tree_evaluator::clear_variable_regexp (const std::string& pattern) { - stack_frame& frame = m_call_stack.get_current_stack_frame (); - - frame.clear_variable_regexp (pattern); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + frame->clear_variable_regexp (pattern); } void tree_evaluator::clear_variables (void) { - stack_frame& frame = m_call_stack.get_current_stack_frame (); - - frame.clear_variables (); + std::shared_ptr frame + = m_call_stack.get_current_stack_frame (); + + frame->clear_variables (); } void tree_evaluator::clear_global_variable (const std::string& name) @@ -2315,26 +2693,15 @@ if (! cmd_list) return retval; - unwind_protect frame; - if (m_call_stack.size () >= static_cast (m_max_recursion_depth)) error ("max_recursion_depth exceeded"); - m_call_stack.push (&user_script, &frame); - - // Set pointer to the current unwind_protect frame to allow - // certain builtins register simple cleanup in a very optimized manner. - // This is *not* intended as a general-purpose on-cleanup mechanism, - - frame.add_method (m_call_stack, &call_stack::pop); - - frame.protect_var (m_statement_context); - m_statement_context = SC_SCRIPT; + unwind_protect_var upv (m_statement_context, SC_SCRIPT); profiler::enter block (m_profiler, user_script); if (echo ()) - push_echo_state (frame, tree_evaluator::ECHO_SCRIPTS, file_name); + push_echo_state (tree_evaluator::ECHO_SCRIPTS, file_name); cmd_list->accept (*this); @@ -2357,8 +2724,7 @@ octave_value_list tree_evaluator::execute_user_function (octave_user_function& user_function, int nargout, - const octave_value_list& xargs, - stack_frame *closure_frames) + const octave_value_list& xargs) { octave_value_list retval; @@ -2390,25 +2756,13 @@ return retval; #endif - unwind_protect frame; - if (m_call_stack.size () >= static_cast (m_max_recursion_depth)) error ("max_recursion_depth exceeded"); - // Save old and set current symbol table context, for - // eval_undefined_error(). - - m_call_stack.push (&user_function, &frame, closure_frames); - - frame.add_method (m_call_stack, &call_stack::pop); - bind_auto_fcn_vars (xargs.name_tags (), args.length (), nargout, user_function.takes_varargs (), user_function.all_va_args (args)); - if (user_function.is_anonymous_function ()) - init_local_fcn_vars (user_function); - tree_parameter_list *param_list = user_function.parameter_list (); if (param_list && ! param_list->varargs_only ()) @@ -2428,37 +2782,23 @@ define_parameter_list_from_arg_vector (ret_list, ret_args); } - // Force parameter list to be undefined when this function exits. - // Doing so decrements the reference counts on the values of local - // variables that are also named function parameters. - - // if (param_list) - // frame.add_method (this, &tree_evaluator::undefine_parameter_list, - // param_list); - - // Force return list to be undefined when this function exits. - // Doing so decrements the reference counts on the values of local - // variables that are also named values returned by this function. - - // if (ret_list) - // frame.add_method (this, &tree_evaluator::undefine_parameter_list, - // ret_list); - - frame.add_method (&user_function, - &octave_user_function::restore_warning_states); + unwind_action act2 ([&user_function] () { + user_function.restore_warning_states (); + }); // Evaluate the commands that make up the function. - frame.protect_var (m_statement_context); - m_statement_context = SC_FUNCTION; - - frame.add_method (m_call_stack, &call_stack::clear_current_frame_values); + unwind_protect_var upv (m_statement_context, SC_FUNCTION); + + unwind_action act1 ([this] () { + m_call_stack.clear_current_frame_values (); + }); { profiler::enter block (m_profiler, user_function); if (echo ()) - push_echo_state (frame, tree_evaluator::ECHO_FUNCTIONS, + push_echo_state (tree_evaluator::ECHO_FUNCTIONS, user_function.fcn_file_name ()); if (user_function.is_special_expr ()) @@ -2504,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; } @@ -3537,13 +3858,17 @@ } void - tree_evaluator::push_echo_state (unwind_protect& frame, int type, - const std::string& file_name, + tree_evaluator::push_echo_state (int type, const std::string& file_name, size_t pos) { - push_echo_state_cleanup (frame); - - set_echo_state (type, file_name, pos); + unwind_protect *frame = m_call_stack.curr_fcn_unwind_protect_frame (); + + if (frame) + { + push_echo_state_cleanup (*frame); + + set_echo_state (type, file_name, pos); + } } void @@ -3847,17 +4172,6 @@ assign ("varargin", va_args.cell_value ()); } - void tree_evaluator::init_local_fcn_vars (octave_user_function& user_fcn) - { - stack_frame& frame = m_call_stack.get_current_stack_frame (); - - const octave_user_function::local_vars_map& lviv - = user_fcn.local_var_init_vals (); - - for (const auto& nm_ov : lviv) - frame.assign (nm_ov.first, nm_ov.second); - } - std::string tree_evaluator::check_autoload_file (const std::string& nm) const { diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/parse-tree/pt-eval.h --- a/libinterp/parse-tree/pt-eval.h Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/parse-tree/pt-eval.h Thu Jun 11 01:22:45 2020 -0400 @@ -42,6 +42,7 @@ #include "ovl.h" #include "profiler.h" #include "pt-walk.h" +#include "stack-frame.h" class octave_user_code; @@ -211,8 +212,7 @@ octave_value_list execute_user_function (octave_user_function& user_function, - int nargout, const octave_value_list& args, - stack_frame *closure_frames = nullptr); + int nargout, const octave_value_list& args); void visit_octave_user_function_header (octave_user_function&); @@ -298,6 +298,8 @@ Matrix ignored_fcn_outputs (void) const; + octave_value make_fcn_handle (const std::string& nm); + octave_value evaluate (tree_decl_elt *); void install_variable (const std::string& name, @@ -377,27 +379,23 @@ void push_stack_frame (const symbol_scope& scope); void push_stack_frame (octave_user_function *fcn, - unwind_protect *up_frame, - stack_frame *closure_frames = nullptr); + const std::shared_ptr& closure_frames = std::shared_ptr ()); - void push_stack_frame (octave_user_script *script, - unwind_protect *up_frame); + void push_stack_frame (octave_user_function *fcn, + const stack_frame::local_vars_map& local_vars); + + void push_stack_frame (octave_user_script *script); void push_stack_frame (octave_function *fcn); void pop_stack_frame (void); - const stack_frame& get_current_stack_frame (void) const + std::shared_ptr get_current_stack_frame (void) const { return m_call_stack.get_current_stack_frame (); } - stack_frame& get_current_stack_frame (void) - { - return m_call_stack.get_current_stack_frame (); - } - - stack_frame * current_user_frame (void) const + std::shared_ptr current_user_frame (void) const { return m_call_stack.current_user_frame (); } @@ -418,7 +416,7 @@ octave_user_code * current_user_code (void) const; - unwind_protect * curr_fcn_unwind_protect_frame (void) const; + unwind_protect * curr_fcn_unwind_protect_frame (void); // Current function that we are debugging. octave_user_code * debug_user_code (void) const; @@ -443,10 +441,10 @@ bool is_class_constructor_executing (std::string& dispatch_class) const; - std::list + std::list> backtrace_frames (octave_idx_type& curr_user_frame) const; - std::list backtrace_frames () const; + std::list> backtrace_frames () const; std::list backtrace_info (octave_idx_type& curr_user_frame, bool print_subfn = true) const; @@ -711,8 +709,8 @@ std::list make_lvalue_list (tree_argument_list *); - void push_echo_state (unwind_protect& frame, int type, - const std::string& file_name, size_t pos = 1); + void push_echo_state (int type, const std::string& file_name, + size_t pos = 1); private: @@ -745,8 +743,6 @@ int nargout, bool takes_varargs, const octave_value_list& va_args); - void init_local_fcn_vars (octave_user_function& user_fcn); - std::string check_autoload_file (const std::string& nm) const; interpreter& m_interpreter; diff -r f5c9bb5955e7 -r b743a63e2dab libinterp/parse-tree/pt-fcn-handle.cc --- a/libinterp/parse-tree/pt-fcn-handle.cc Tue Jun 09 14:11:13 2020 -0700 +++ b/libinterp/parse-tree/pt-fcn-handle.cc Thu Jun 11 01:22:45 2020 -0400 @@ -32,6 +32,7 @@ #include "interpreter-private.h" #include "pt-anon-scopes.h" #include "pt-fcn-handle.h" +#include "stack-frame.h" namespace octave { @@ -62,7 +63,7 @@ octave_value tree_fcn_handle::evaluate (tree_evaluator& tw, int) { - return make_fcn_handle (tw.get_interpreter (), m_name); + return tw.make_fcn_handle (m_name); } tree_anon_fcn_handle::~tree_anon_fcn_handle (void) @@ -130,23 +131,23 @@ std::set free_vars = anon_fcn_ctx.free_variables (); - octave_user_function::local_vars_map local_var_init_vals; + stack_frame::local_vars_map local_vars; call_stack& cs = tw.get_call_stack (); - stack_frame& frame = cs.get_current_stack_frame (); + std::shared_ptr frame = cs.get_current_stack_frame (); for (auto& name : free_vars) { - octave_value val = frame.varval (name); + octave_value val = frame->varval (name); if (val.is_defined ()) - local_var_init_vals[name] = val; + local_vars[name] = val; } octave_user_function *af = new octave_user_function (new_scope, param_list_dup, ret_list, - stmt_list, local_var_init_vals); + stmt_list); octave_function *curr_fcn = cs.current_function (); @@ -174,10 +175,7 @@ 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, octave_fcn_handle::anonymous)); + return octave_value (new octave_fcn_handle (ov_fcn, local_vars)); } } diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/@inline/argnames.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/argnames.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,16 @@ +## -*- texinfo -*- +## @deftypefn {} {} argnames (@var{fun}) +## Return a cell array of character strings containing the names of the +## arguments of the inline function @var{fun}. +## @seealso{inline, formula, vectorize} +## @end deftypefn + +function args = argnames (obj) + + if (nargin != 1) + print_usage (); + endif + + args = obj.args; + +endfunction diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/@inline/char.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/char.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,18 @@ +## -*- texinfo -*- +## @deftypefn {} {} char (@var{fun}) +## Return a character string representing the inline function @var{fun}. +## +## Note that @code{char (@var{fun})} is equivalent to +## @code{formula (@var{fun})}. +## @seealso{char, argnames, inline, vectorize} +## @end deftypefn + +function expr = char (obj) + + if (nargin != 1) + print_usage (); + endif + + expr = obj.expr; + +endfunction diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/@inline/feval.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/feval.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,36 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +function retval = feval (fcn, varargin) + + if (nargin < 1) + print_usage (); + endif + + fh = eval (sprintf ("@(%s) %s", strjoin (fcn.args, ","), fcn.expr)); + + retval = fh (varargin{:}); + +endfunction diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/@inline/formula.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/formula.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,18 @@ +## -*- texinfo -*- +## @deftypefn {} {} formula (@var{fun}) +## Return a character string representing the inline function @var{fun}. +## +## Note that @code{char (@var{fun})} is equivalent to +## @code{formula (@var{fun})}. +## @seealso{char, argnames, inline, vectorize} +## @end deftypefn + +function expr = formula (obj) + + if (nargin != 1) + print_usage (); + endif + + expr = obj.expr; + +endfunction diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/@inline/inline.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/inline.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,190 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +## -*- texinfo -*- +## @deftypefn {} {} inline (@var{str}) +## @deftypefnx {} {} inline (@var{str}, @var{arg1}, @dots{}) +## @deftypefnx {} {} inline (@var{str}, @var{n}) +## +## This function is obsolete. Use anonymous functions +## (@pxref{Anonymous Functions}) instead. +## +## Create an inline function from the character string @var{str}. +## +## If called with a single argument, the arguments of the generated +## function are extracted from the function itself. The generated +## function arguments will then be in alphabetical order. It should be +## noted that i and j are ignored as arguments due to the ambiguity +## between their use as a variable or their use as an built-in constant. +## All arguments followed by a parenthesis are considered to be +## functions. If no arguments are found, a function taking a single +## argument named @code{x} will be created. +## +## If the second and subsequent arguments are character strings, they +## are the names of the arguments of the function. +## +## If the second argument is an integer @var{n}, the arguments are +## @qcode{"x"}, @qcode{"P1"}, @dots{}, @qcode{"P@var{N}"}. +## +## @strong{Caution:} the use of @code{inline} is discouraged and it may +## be removed from a future version of Octave. The preferred way to +## create functions from strings is through the use of anonymous +## functions (@pxref{Anonymous Functions}) or @code{str2func}. +## @seealso{argnames, formula, vectorize, str2func} +## @end deftypefn + +function obj = inline (expr, varargin) + + if (nargin == 0) + print_usage (); + endif + + if (! ischar (expr)) + error ("inline: EXPR must be a string"); + endif + + if (nargin == 1) + args = parse_expr_for_args (expr); + elseif (nargin == 2) + n = varargin{1}; + if (isnumeric (n)) + if (isscalar (n) && fix (n) == n) + if (n > 0) + args = strsplit (["x", sprintf(":P%d", 1:n)], ":"); + else + error ("inline: N must be a positive integer"); + endif + else + error ("inline: N must be an integer"); + endif + else + args = {"x"}; + endif + elseif (iscellstr (varargin)) + args = varargin; + else + error ("inline: additional arguments must be strings"); + endif + + p.expr = expr; + p.args = args(:); + p.numArgs = numel (args); + tmp = [args; num2cell(1:numel(args))]; + p.inputExpr = sprintf ("%s = INLINE_INPUTS_{%d}; ", tmp{:}); + p.isEmpty = false; + p.version = 1; + + obj = __inline_ctor__ (p); + +endfunction + +## The following function was translated directly from the original C++ +## version. Yes, it will be slow, but the use of inline functions is +## strongly discouraged anyway, and most expressions will probably be +## short. It may also be buggy. Well, don't use this object! Use +## function handles instead! + +function args = parse_expr_for_args (expr) + + persistent symbols_to_skip = {"i", "j", "NaN", "nan", "Inf", "inf", ... + "NA", "pi", "e", "eps"}; + + is_arg = false; + in_string = false; + tmp_arg = ""; + i = 1; + expr_length = length (expr); + args = {}; + + while (i <= expr_length) + + terminate_arg = false; + c = expr(i++); + + if (in_string) + if (c == "'" || c == '"') + in_string = false; + endif + elseif (c == "'" || c == '"') + in_string = true; + if (is_arg) + terminate_arg = true; + endif + elseif (! isalpha (c) && c != "_") + if (! is_arg) + continue; + elseif (isdigit (c)) + tmp_arg(end+1) = c; + else + ## Before we do anything remove trailing whitespaces. + while (i <= expr_length && isspace (c)) + c = expr(i++); + endwhile + + ## Do we have a variable or a function? + if (c != "(") + terminate_arg = true; + else + tmp_arg = ""; + is_arg = false; + endif + endif + elseif (! is_arg) + if (c == "e" || c == "E") + ## Possible number in exponent form, not arg. + if (isdigit (expr(i)) || expr(i) == "-" || expr(i) == "+") + continue; + endif + endif + is_arg = true; + tmp_arg(end+1) = c; + else + tmp_arg(end+1) = c; + endif + + if (terminate_arg || (i == expr_length+1 && is_arg)) + have_arg = false; + if (any (strcmp (tmp_arg, args))) + have_arg = true; + endif + + if (! (have_arg || any (strcmp (tmp_arg, symbols_to_skip)))) + args{end+1} = tmp_arg; + endif + + tmp_arg = ""; + is_arg = false; + endif + + endwhile + + ## Sort the arguments into ASCII order. + args = sort (args); + + if (isempty (args)) + args = {"x"}; + endif + +endfunction diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/@inline/module.mk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/module.mk Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,11 @@ +## Automake fails to process "include %reldir%/module.mk" in the directory +## above. All of the commands which would normally be in this file were +## manually placed in scripts/legacy/module.mk to avoid using the "include" +## directive. +## +## This is an Automake bug. Automake has switched to a Perl backend which uses +## the following pattern to detect a path: +## +## my $PATH_PATTERN = '(\w|[+/.-])+'; +## +## This pattern only includes alphanumeric, '_', and [+/.-], but not "@". diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/@inline/subsref.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/subsref.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,52 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +## -*- texinfo -*- +## @deftypefn {} {@var{value} =} subsref (@var{fcn}, @var{idx}) +## Perform subscripted function call on the inline function object @var{fcn}. +## @end deftypefn + +function retval = subsref (fcn, idx) + + if (nargin != 2) + print_usage (); + endif + + if (isempty (idx)) + error ("@inline/subsref: missing index"); + endif + + if (strcmp (idx(1).type, "()")) + args = idx.subs; + if (numel (args) > 0) + retval = feval (fcn, args{:}); + else + retval = feval (fcn); + endif + else + error ("@inline/subsref: invalid subscript type"); + endif + +endfunction diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/@inline/vectorize.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/legacy/@inline/vectorize.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,62 @@ +## -*- texinfo -*- +## @deftypefn {} {} vectorize (@var{fun}) +## Create a vectorized version of the inline function @var{fun} by +## replacing all occurrences of @code{*}, @code{/}, etc., with +## @code{.*}, @code{./}, etc. +## +## This may be useful, for example, when using inline functions with +## numerical integration or optimization where a vector-valued function +## is expected. +## +## @example +## @group +## fcn = vectorize (inline ("x^2 - 1")) +## @result{} fcn = f(x) = x.^2 - 1 +## quadv (fcn, 0, 3) +## @result{} 6 +## @end group +## @end example +## @seealso{inline, formula, argnames} +## @end deftypefn + +## The following function was translated directly from the original C++ +## version. Yes, it will be slow, but the use of inline functions is +## strongly discouraged anyway, and most expressions will probably be +## short. It may also be buggy. Well, don't use this object! Use +## function handles instead! + +function fcn = vectorize (obj) + + if (nargin != 1) + print_usage (); + endif + + new_expr = ""; + + expr = obj.expr; + len = length (expr); + i = 1; + + while (i <= len) + c = expr(i); + + if (c == "*" || c == "/" || c == "\\" || c == "^") + if (i > 1 && expr(i-1) != ".") + new_expr(end+1) = "."; + endif + + ## Special case for ** operator. + if (c == '*' && i < (len - 1) && expr(i+1) == '*') + new_expr(end+1) = "*"; + i++; + endif + endif + + new_expr(end+1) = c; + i++; + + endwhile + + fcn = inline (new_expr); + +endfunction diff -r f5c9bb5955e7 -r b743a63e2dab scripts/legacy/module.mk --- a/scripts/legacy/module.mk Tue Jun 09 14:11:13 2020 -0700 +++ b/scripts/legacy/module.mk Thu Jun 11 01:22:45 2020 -0400 @@ -12,6 +12,31 @@ %reldir%/strread.m \ %reldir%/textread.m +## include %reldir%/@inline/module.mk +## The include above fails because Automake cannot process the '@' character. +## As a work around, the contents of %reldir%/@inline/module.mk are placed directly +## in this module.mk file. +scripts_EXTRA_DIST += %reldir%/@inline/module.mk +######################## include %reldir%/@inline/module.mk ######################## +FCN_FILE_DIRS += %reldir%/@inline + +%canon_reldir%_@inline_FCN_FILES = \ + %reldir%/@inline/argnames.m \ + %reldir%/@inline/formula.m \ + %reldir%/@inline/inline.m \ + %reldir%/@inline/vectorize.m + +%canon_reldir%_@inlinedir = $(fcnfiledir)/@inline + +%canon_reldir%_@inline_DATA = $(%canon_reldir%_@inline_FCN_FILES) + +FCN_FILES += $(%canon_reldir%_@inline_FCN_FILES) + +PKG_ADD_FILES += %reldir%/@inline/PKG_ADD + +DIRSTAMP_FILES += %reldir%/@inline/$(octave_dirstamp) +####################### end include %reldir%/@inline/module.mk ##################### + %canon_reldir%dir = $(fcnfiledir)/legacy %canon_reldir%_DATA = $(%canon_reldir%_FCN_FILES) diff -r f5c9bb5955e7 -r b743a63e2dab scripts/module.mk --- a/scripts/module.mk Tue Jun 09 14:11:13 2020 -0700 +++ b/scripts/module.mk Thu Jun 11 01:22:45 2020 -0400 @@ -45,7 +45,7 @@ ## The include above fails because Automake cannot process the '@' character. ## As a work around, the contents of %reldir%/@ftp/module.mk are placed directly ## in this module.mk file. -%canon_reldir%_EXTRA_DIST += %reldir%/@ftp/module.mk +scripts_EXTRA_DIST += %reldir%/@ftp/module.mk ######################## include %reldir%/@ftp/module.mk ######################## FCN_FILE_DIRS += %reldir%/@ftp diff -r f5c9bb5955e7 -r b743a63e2dab scripts/plot/draw/fplot.m --- a/scripts/plot/draw/fplot.m Tue Jun 09 14:11:13 2020 -0700 +++ b/scripts/plot/draw/fplot.m Thu Jun 11 01:22:45 2020 -0400 @@ -83,11 +83,9 @@ ## ## @code{fplot} performance is better when the function accepts and returns a ## vector argument. Consider this when writing user-defined functions and use -## element-by-element operators such as @code{.*}, @code{./}, etc. See the -## function @code{vectorize} for potentially converting inline or anonymous -## functions to vectorized versions. +## element-by-element operators such as @code{.*}, @code{./}, etc. ## -## @seealso{ezplot, plot, vectorize} +## @seealso{ezplot, plot} ## @end deftypefn function [X, Y] = fplot (varargin) @@ -99,8 +97,8 @@ endif fn = varargin{1}; - if (strcmp (typeinfo (fn), "inline function")) - fn = vectorize (fn); + if (isa (fn, "inline")) + fn = vectorize (inline (fn)); nam = formula (fn); elseif (is_function_handle (fn)) nam = func2str (fn); diff -r f5c9bb5955e7 -r b743a63e2dab scripts/plot/draw/private/__ezplot__.m --- a/scripts/plot/draw/private/__ezplot__.m Tue Jun 09 14:11:13 2020 -0700 +++ b/scripts/plot/draw/private/__ezplot__.m Thu Jun 11 01:22:45 2020 -0400 @@ -84,7 +84,7 @@ endif endif - if (strcmp (typeinfo (fun), "inline function")) + if (isa (fun, "inline")) argids = argnames (fun); if (isplot && length (argids) == 2) nargs = 2; @@ -147,7 +147,7 @@ yarg = args{2}; endif else - error ("%s: F must be string, inline function, or function handle", ezfunc); + error ("%s: F must be a string or function handle", ezfunc); endif if (nargin > 2 || (nargin == 2 && isplot)) @@ -165,7 +165,7 @@ error ("%s: expecting a function of %d arguments", ezfunc, nargs); endif fstry = formula (funy); - elseif (strcmp (typeinfo (funy), "inline function")) + elseif (isa (funy, "inline")) parametric = true; if (numel (argnames (funy)) != nargs) error ("%s: expecting a function of %d arguments", ezfunc, nargs); @@ -210,7 +210,7 @@ error ("%s: expecting a function of %d arguments", ezfunc, nargs); endif fstrz = formula (funz); - elseif (strcmp (typeinfo (funz), "inline function")) + elseif (isa (funz, "inline")) if (numel (argnames (funz)) != nargs) error ("%s: expecting a function of %d arguments", ezfunc, nargs); endif diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/bug-51567.tst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/bug-51567.tst Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,5 @@ +%!shared a +%! a = bug51567 (); +%! +%!assert <51567> (a.doit_1 (), 13); +%!assert <51567> (a.doit_2 (), 42); diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/bug-57941.tst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/bug-57941.tst Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,9 @@ +%!test <*57941> +%! [r1, r2] = bug57941a (2); +%! assert (r1, 6); +%! assert (r2, 24); + +%!test <*57941> +%! [fh1, fh2] = bug57941b (2); +%! assert (fh1 (3), 6); +%! assert (fh2 (3, 4), 24); diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/bug51567.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/bug51567.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,24 @@ +classdef bug51567 < handle + properties + fh1; + fh2; + endproperties + methods + function obj = bug51567 (self) + obj.fh1 = str2func ("bar"); + obj.fh2 = @baz; + endfunction + function r = bar (obj) + r = 13; + endfunction + function r = baz (obj) + r = 42; + endfunction + function r = doit_1 (obj) + r = obj.fh1 (); + endfunction + function r = doit_2 (obj) + r = obj.fh2 (); + endfunction + endmethods +endclassdef diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/bug57941a.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/bug57941a.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,12 @@ +function [r1, r2] = bug57941a (A) + fh1 = @nested1; + function z = nested1 (x) + z = A * x; + end + fh2 = @nested2; + function z = nested2 (x,y) + z = A * x .* y; + end + r1 = fh1 (3); + r2 = fh2 (3, 4); +end diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/bug57941b.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/bug57941b.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,10 @@ +function [fh1, fh2] = bug57941b (A) + fh1 = @nested1; + function z = nested1 (x) + z = A * x; + end + fh2 = @nested2; + function z = nested2 (x,y) + z = A * x .* y; + end +end diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/bug58519.tst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/bug58519.tst Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,5 @@ +%!test <58519> +%! fieldname = "a"; +%! structure = struct (fieldname, 42); +%! anonfunc = @ () structure.(fieldname); +%! assert (anonfunc (), 42) diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/module.mk --- a/test/fcn-handle/module.mk Tue Jun 09 14:11:13 2020 -0700 +++ b/test/fcn-handle/module.mk Thu Jun 11 01:22:45 2020 -0400 @@ -10,13 +10,21 @@ %reldir%/@fhdr_other/getsize_loop.m \ %reldir%/@fhdr_parent/fhdr_parent.m \ %reldir%/@fhdr_parent/numel.m \ + %reldir%/bug-51567.tst \ + %reldir%/bug51567.m \ %reldir%/bug51709_a.m \ %reldir%/bug51709_c.m \ + %reldir%/bug57941.tst \ + %reldir%/bug58519.tst \ + %reldir%/bug57941a.m \ + %reldir%/bug57941b.m \ %reldir%/derived-resolution.tst \ %reldir%/f1.m \ %reldir%/keyword.tst \ %reldir%/object-method.tst \ %reldir%/package-function.tst \ + %reldir%/shared-ctx.tst \ + %reldir%/shared_ctx.m \ %reldir%/static-method.tst TEST_FILES += $(fcn_handle_TEST_FILES) diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/shared-ctx.tst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/shared-ctx.tst Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,15 @@ +## Test that multiple handles to nested functions created in the same +## context share that call stack context, but that separately created +## handles have a separate (shared) context. + +%!test +%! [add10, sub10, mul10, div10] = shared_ctx (10); +%! [add100, sub100, mul100, div100] = shared_ctx (100); +%! assert (add10 (2), 12); +%! assert (add100 (20), 120); +%! assert (sub10 (4), 8); +%! assert (sub100 (40), 80); +%! assert (mul10 (5), 40); +%! assert (mul100 (50), 4000); +%! assert (div10 (4), 10); +%! assert (div100 (40), 100); diff -r f5c9bb5955e7 -r b743a63e2dab test/fcn-handle/shared_ctx.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/shared_ctx.m Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,22 @@ +function [add, sub, mul, div] = shared_ctx (val) + add = @add_fun; + sub = @sub_fun; + mul = @mul_fun; + div = @div_fun; + function r = add_fun (x) + val += x; + r = val; + endfunction + function r = sub_fun (x) + val -= x; + r = val; + endfunction + function r = mul_fun (x) + val *= x; + r = val; + endfunction + function r = div_fun (x) + val /= x; + r = val; + endfunction +endfunction diff -r f5c9bb5955e7 -r b743a63e2dab test/inline-fcn.tst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/inline-fcn.tst Thu Jun 11 01:22:45 2020 -0400 @@ -0,0 +1,33 @@ +## INLINE contstructor + +%!shared fn +%! fn = inline ("x.^2 + 1"); +%!assert (feval (fn, 6), 37) +%!assert (fn (6), 37) +%!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10) +%!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5) +%!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5) + +## Test input validation +%!error inline () +%!error inline (1) +%!error inline ("2", ones (2,2)) +%!error inline ("2", -1) +%!error inline ("2", "x", -1, "y") + +## FORMULA + +%!assert (formula (fn), "x.^2 + 1") +%!assert (formula (fn), char (fn)) + +## ARGNAMES + +%!assert (argnames (fn), {"x"}) +%!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"}) +%!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"}) + +## VECTORIZE + +%!assert (formula (vectorize (fn)), "x.^2 + 1") +%!assert (formula (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z") +%!assert (formula (vectorize (inline ("2**x^5"))), "2.**x.^5") diff -r f5c9bb5955e7 -r b743a63e2dab test/module.mk --- a/test/module.mk Tue Jun 09 14:11:13 2020 -0700 +++ b/test/module.mk Thu Jun 11 01:22:45 2020 -0400 @@ -31,6 +31,7 @@ %reldir%/global.tst \ %reldir%/if.tst \ %reldir%/index.tst \ + %reldir%/inline-fcn.tst \ %reldir%/integer.tst \ %reldir%/io.tst \ %reldir%/jit.tst \