Mercurial > jwe > octave
changeset 26965:b033cf021048
allow @obj.meth to work (bug #51709)
* ov-fcn-handle.cc (octave_fcn_handle::call): Allow obj.meth to work
for both static and ordinary classdef methods.
(octave_fcn_handle::m_obj): New data member.
(octave_fcn_handle::octave_fcn_handle): Also store m_obj if the first
element of a fully-qualified name is defined as an object when
creating the function handle object.
* test/fcn-handle/bug51709_c.m, test/fcn-handle/object-method.tst:
New files.
* test/fcn-handle/module.mk: Update.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 23 Mar 2019 14:20:27 -0500 |
parents | 1a79f289ca33 |
children | b57d596b909b |
files | libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-fcn-handle.h test/fcn-handle/bug51709_c.m test/fcn-handle/module.mk test/fcn-handle/object-method.tst |
diffstat | 5 files changed, 176 insertions(+), 26 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/octave-value/ov-fcn-handle.cc Fri Mar 22 15:58:46 2019 -0700 +++ b/libinterp/octave-value/ov-fcn-handle.cc Sat Mar 23 14:20:27 2019 -0500 @@ -83,9 +83,42 @@ const std::string octave_fcn_handle::anonymous ("@<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) +{ + if (! m_name.empty () && m_name[0] == '@') + m_name = m_name.substr (1); + + size_t pos = m_name.find ('.'); + + if (pos != std::string::npos) + { + // 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; + } +} + +octave_fcn_handle::octave_fcn_handle (const octave::symbol_scope& scope, const octave_value& f, const std::string& n) - : m_fcn (f), m_name (n), m_scope (scope), m_is_nested (false), + : m_fcn (f), m_obj (), m_name (n), m_scope (scope), m_is_nested (false), m_closure_frames (nullptr) { octave_user_function *uf = m_fcn.user_function_value (true); @@ -104,7 +137,7 @@ octave_fcn_handle::octave_fcn_handle (const octave_value& f, const std::string& n) - : m_fcn (f), m_name (n), m_scope (), m_is_nested (false), + : m_fcn (f), m_obj (), m_name (n), m_scope (), m_is_nested (false), m_closure_frames (nullptr) { octave_user_function *uf = m_fcn.user_function_value (true); @@ -198,8 +231,10 @@ // tree_evaluator::visit_index_expression but simpler because it // handles a more restricted case. - octave::symbol_table& symtab - = octave::__get_symbol_table__ ("octave_fcn_handle::call"); + octave::interpreter& interp + = octave::__get_interpreter__ ("octave_fcn_handle::call"); + + octave::symbol_table& symtab = interp.get_symbol_table (); size_t pos = m_name.find ('.'); @@ -211,9 +246,10 @@ // pkg-list . cls . meth (args) // cls . meth (args) - // Evaluate package elements until we find a function or a - // classdef_meta object that is not a package. - // subsref call. + // 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; @@ -232,18 +268,55 @@ beg = end+1; } - octave_value partial_expr_val - = symtab.find_function (idx_elts[0], ovl (), m_scope); + size_t n_elts = idx_elts.size (); + + bool have_object = false; + octave_value partial_expr_val; + + if (m_obj.is_defined ()) + { + // The first element was already defined elsewhere, + // possibly in the scope where the function handle was + // created. + + partial_expr_val = m_obj; + + if (m_obj.is_classdef_object ()) + have_object = true; + else + err_invalid_fcn_handle (m_name); + } + else + { + // Lazy evaluation. The first element was not known to be + // defined as an object in the scope where the handle was + // created. See if there is a definition in the current + // scope. + + partial_expr_val = interp.varval (idx_elts[0]); + } + + if (partial_expr_val.is_defined ()) + { + if (! partial_expr_val.is_classdef_object () || n_elts != 2) + err_invalid_fcn_handle (m_name); + + have_object = true; + } + else + partial_expr_val + = symtab.find_function (idx_elts[0], ovl (), m_scope); std::string type; std::list<octave_value_list> arg_list; - size_t n_elts = idx_elts.size (); - 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])); @@ -267,13 +340,13 @@ err_invalid_fcn_handle (m_name); } } - else if (partial_expr_val.is_classdef_meta ()) + else if (have_object || partial_expr_val.is_classdef_meta ()) { - // 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 as static classdef method, - // but we'll let the classdef_meta subsref function - // sort that out. + // 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);
--- a/libinterp/octave-value/ov-fcn-handle.h Fri Mar 22 15:58:46 2019 -0700 +++ b/libinterp/octave-value/ov-fcn-handle.h Sat Mar 23 14:20:27 2019 -0500 @@ -57,17 +57,11 @@ static const std::string anonymous; octave_fcn_handle (void) - : m_fcn (), m_name (), m_scope (), m_is_nested (false), + : m_fcn (), m_obj (), m_name (), m_scope (), m_is_nested (false), m_closure_frames (nullptr) { } - octave_fcn_handle (const octave::symbol_scope& scope, const std::string& n) - : m_fcn (), m_name (n), m_scope (scope), m_is_nested (false), - m_closure_frames (nullptr) - { - if (! m_name.empty () && m_name[0] == '@') - m_name = m_name.substr (1); - } + octave_fcn_handle (const octave::symbol_scope& scope, const std::string& n); octave_fcn_handle (const octave::symbol_scope& scope, const octave_value& f, @@ -159,9 +153,13 @@ // 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 funtion handle object was created. + // 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/bug51709_c.m Sat Mar 23 14:20:27 2019 -0500 @@ -0,0 +1,12 @@ +classdef bug51709_c + methods + function out = meth (varargin) + out = {"meth", varargin}; + endfunction + endmethods + methods (Static) + function out = smeth (varargin) + out = {"smeth", varargin}; + endfunction + endmethods +endclassdef
--- a/test/fcn-handle/module.mk Fri Mar 22 15:58:46 2019 -0700 +++ b/test/fcn-handle/module.mk Sat Mar 23 14:20:27 2019 -0500 @@ -11,8 +11,10 @@ %reldir%/@fhdr_parent/fhdr_parent.m \ %reldir%/@fhdr_parent/numel.m \ %reldir%/bug51709_a.m \ + %reldir%/bug51709_c.m \ %reldir%/derived-resolution.tst \ %reldir%/f1.m \ + %reldir%/object-method.tst \ %reldir%/package-function.tst \ %reldir%/static-method.tst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fcn-handle/object-method.tst Sat Mar 23 14:20:27 2019 -0500 @@ -0,0 +1,65 @@ +## Copyright (C) 2019 John W. Eaton +## +## 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 +## <https://www.gnu.org/licenses/>. + +%!function [fhm, fhsm] = foo () +%! obj = bug51709_c (); +%! fhm = @obj.meth; +%! fhsm = @obj.smeth; +%!endfunction + +%!test <51709> +%! [fhm, fhsm] = foo (); +%! +%! out = fhm (42); +%! assert (out{1}, "meth"); +%! tmp = out{2}; +%! assert (numel (tmp), 2); +%! assert (isobject (tmp{1})); +%! assert (tmp{2}, 42); + +%!test <51709> +%! [fhm, fhsm] = foo (); +%! +%! out = fhsm (42); +%! assert (out{1}, "smeth"); +%! tmp = out{2}; +%! assert (numel (tmp), 1); +%! assert (tmp{1}, 42); + +%!test <51709> +%! fhm = @obj.meth; +%! +%! obj = bug51709_c (); +%! +%! out = fhm (42); +%! assert (out{1}, "meth"); +%! tmp = out{2}; +%! assert (numel (tmp), 2); +%! assert (isobject (tmp{1})); +%! assert (tmp{2}, 42); + +%!test <51709> +%! fhsm = @obj.smeth; +%! +%! obj = bug51709_c (); +%! +%! out = fhsm (42); +%! assert (out{1}, "smeth"); +%! tmp = out{2}; +%! assert (numel (tmp), 1); +%! assert (tmp{1}, 42);