Mercurial > octave
diff libinterp/corefcn/fcn-info.cc @ 24263:3b302b2890d7
disentangle symbol_record, scope, and fcn_info from symbol_table class
* fcn-info.cc, fcn-info.h, scope.cc, scope.h, symrec.cc, symrec.h:
New files extracted from symtab.h and symtab.cc.
* libinterp/corefcn/module.mk: Update.
* symrec.cc (symbol_record::symbol_record_rep::xglobal_varref):
Don't access private symbol_table internals directly.
* scope.h, scope.cc (scope::find, scope::builtin_find,
scope::clear_global, scope::clear_global_pattern):
Don't access private symbol_table internals directly.
* symtab.h, symtab.cc (symbol_table::builtin_find): Don't forward to
current scope. Look directly in fcn_info table.
(symbol_table::global_varref, symbol_table::fcn_table_find,
symbol_table::erase_global, symbol_table::erase_global_pattern):
New functions.
* scope.h (scope::context_id): New typedef.
* symrec.h (symbol_record::context_id): New typedef.
* symtab.h (symbol_table::context_id): Update.
* symtab.h, symtab.cc (symbol_table::dummy_octave_value):
Delete static data member.
* symtab.h (symbol_table::context_id): Delete typedef.
(symbol_table::symbol_record, symbol_table::scope,
symbol_table::fcn_info): New typedefs.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Thu, 16 Nov 2017 16:06:31 -0500 |
parents | |
children | f494b87d2a93 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/fcn-info.cc Thu Nov 16 16:06:31 2017 -0500 @@ -0,0 +1,845 @@ +/* + +Copyright (C) 1993-2017 John W. Eaton +Copyright (C) 2009 VZLU Prague, a.s. + +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 +<http://www.gnu.org/licenses/>. + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include "file-ops.h" + +#include "fcn-info.h" +#include "interpreter-private.h" +#include "interpreter.h" +#include "load-path.h" +#include "ov-fcn.h" +#include "ov-usr-fcn.h" +#include "parse.h" +#include "scope.h" +#include "symrec.h" +#include "symtab.h" + +namespace octave +{ + octave_value + fcn_info::fcn_info_rep::load_private_function (const std::string& dir_name) + { + octave_value retval; + + load_path& lp + = __get_load_path__ ("fcn_info::fcn_info_rep::load_private_function"); + + std::string file_name = lp.find_private_fcn (dir_name, name); + + if (file_name.empty ()) + return retval; + + octave_value ov_fcn = load_fcn_from_file (file_name, dir_name); + + if (ov_fcn.is_undefined ()) + return retval; + + octave_function *tmpfcn = ov_fcn.function_value (); + + if (! tmpfcn) + return retval; + + std::string class_name; + + size_t pos = dir_name.find_last_of (sys::file_ops::dir_sep_chars ()); + + if (pos != std::string::npos) + { + std::string tmp = dir_name.substr (pos+1); + + if (tmp[0] == '@') + class_name = tmp.substr (1); + } + + tmpfcn->mark_as_private_function (class_name); + + private_functions[dir_name] = ov_fcn; + + return ov_fcn; + } + + octave_value + fcn_info::fcn_info_rep::load_class_constructor (void) + { + octave_value retval; + + std::string dir_name; + + load_path& lp + = __get_load_path__ ("fcn_info::fcn_info_rep::load_class_constructor"); + + std::string file_name = lp.find_method (name, name, dir_name, package_name); + + if (! file_name.empty ()) + { + octave_value ov_fcn + = load_fcn_from_file (file_name, dir_name, name, + package_name); + + if (ov_fcn.is_defined ()) + { + // Note: ov_fcn may be an octave_classdef_meta object instead + // of the actual constructor function. + + retval = ov_fcn; + + class_constructors[name] = retval; + class_methods[name] = retval; + } + } + else + { + // Classdef constructors can be defined anywhere in the path, not + // necessarily in @-folders. Look for a normal function and load it. + // If the loaded function is a classdef constructor, store it as such + // and restore function_on_path to its previous value. + + octave_value old_function_on_path = function_on_path; + + octave_value maybe_cdef_ctor = find_user_function (); + + if (maybe_cdef_ctor.is_defined ()) + { + octave_function *fcn = maybe_cdef_ctor.function_value (true); + + if (fcn && fcn->is_classdef_constructor ()) + { + retval = maybe_cdef_ctor; + + class_constructors[name] = retval; + class_methods[name] = retval; + + function_on_path = old_function_on_path; + } + } + } + + return retval; + } + + octave_value + fcn_info::fcn_info_rep::load_class_method (const std::string& dispatch_type) + { + octave_value retval; + + if (full_name () == dispatch_type) + retval = load_class_constructor (); + else + { + cdef_manager& cdm + = __get_cdef_manager__ ("fcn_info::fcn_info_rep::load_class_method"); + + octave_function *cm = cdm.find_method_symbol (name, dispatch_type); + + if (cm) + retval = octave_value (cm); + + if (! retval.is_defined ()) + { + std::string dir_name; + + load_path& lp = __get_load_path__ ("fcn_info::fcn_info_rep::load_class_method"); + + std::string file_name = lp.find_method (dispatch_type, name, + dir_name); + + if (! file_name.empty ()) + { + octave_value ov_fcn + = load_fcn_from_file (file_name, dir_name, + dispatch_type); + + if (ov_fcn.is_defined ()) + { + octave_function *tmpfcn = ov_fcn.function_value (); + + if (tmpfcn && tmpfcn->is_class_method (dispatch_type)) + { + retval = ov_fcn; + + class_methods[dispatch_type] = retval; + } + } + } + + if (retval.is_undefined ()) + { + // Search parent classes + + symbol_table& symtab + = __get_symbol_table__ ("fcn_info::fcn_info_rep::load_class_method"); + + const std::list<std::string>& plist = + symtab.parent_classes (dispatch_type); + + std::list<std::string>::const_iterator it = plist.begin (); + + while (it != plist.end ()) + { + retval = find_method (*it); + + if (retval.is_defined ()) + { + class_methods[dispatch_type] = retval; + break; + } + + it++; + } + } + + if (retval.is_undefined ()) + { + // Search for built-in functions that are declared to + // handle specific types. + + if (built_in_function.is_defined ()) + { + octave_function *fcn = built_in_function.function_value (); + + if (fcn && fcn->handles_dispatch_class (dispatch_type)) + { + retval = built_in_function; + + class_methods[dispatch_type] = retval; + } + } + } + } + } + + return retval; + } +} + +// :-) JWE, can you parse this? Returns a 2D array with second dimension equal +// to btyp_num_types (static constant). Only the leftmost dimension can be +// variable in C/C++. Typedefs are boring. + +static builtin_type_t (*build_sup_table (void))[btyp_num_types] +{ + static builtin_type_t sup_table[btyp_num_types][btyp_num_types]; + for (int i = 0; i < btyp_num_types; i++) + for (int j = 0; j < btyp_num_types; j++) + { + builtin_type_t ityp = static_cast<builtin_type_t> (i); + builtin_type_t jtyp = static_cast<builtin_type_t> (j); + // FIXME: Is this really right? + bool use_j = + (jtyp == btyp_func_handle || ityp == btyp_bool + || (btyp_isarray (ityp) + && (! btyp_isarray (jtyp) + || (btyp_isinteger (jtyp) && ! btyp_isinteger (ityp)) + || ((ityp == btyp_double || ityp == btyp_complex + || ityp == btyp_char) + && (jtyp == btyp_float + || jtyp == btyp_float_complex))))); + + sup_table[i][j] = (use_j ? jtyp : ityp); + } + + return sup_table; +} + +namespace octave +{ + std::string + get_dispatch_type (const octave_value_list& args, + builtin_type_t& builtin_type) + { + static builtin_type_t (*sup_table)[btyp_num_types] = build_sup_table (); + std::string dispatch_type; + + int n = args.length (); + + if (n > 0) + { + int i = 0; + builtin_type = args(0).builtin_type (); + if (builtin_type != btyp_unknown) + { + for (i = 1; i < n; i++) + { + builtin_type_t bti = args(i).builtin_type (); + if (bti != btyp_unknown) + builtin_type = sup_table[builtin_type][bti]; + else + { + builtin_type = btyp_unknown; + break; + } + } + } + + if (builtin_type == btyp_unknown) + { + // There's a non-builtin class in the argument list. + dispatch_type = args(i).class_name (); + + symbol_table& symtab = __get_symbol_table__ ("get_dispatch_type"); + + for (int j = i+1; j < n; j++) + { + octave_value arg = args(j); + + if (arg.builtin_type () == btyp_unknown) + { + std::string cname = arg.class_name (); + + // Only switch to type of ARG if it is marked superior + // to the current DISPATCH_TYPE. + if (! symtab.is_superiorto (dispatch_type, cname) + && symtab.is_superiorto (cname, dispatch_type)) + dispatch_type = cname; + } + } + } + else + dispatch_type = btyp_class_name[builtin_type]; + } + else + builtin_type = btyp_unknown; + + return dispatch_type; + } + + std::string + get_dispatch_type (const octave_value_list& args) + { + builtin_type_t builtin_type; + return get_dispatch_type (args, builtin_type); + } + + // Find function definition according to the following precedence list: + // + // private function + // class method + // class constructor + // command-line function + // autoload function + // function on the path + // built-in function + // + // Matlab documentation states that constructors have higher precedence + // than methods, but that does not seem to be the case. + + octave_value + fcn_info::fcn_info_rep::find (const octave_value_list& args, bool local_funcs) + { + octave_value retval = xfind (args, local_funcs); + + if (retval.is_undefined ()) + { + // It is possible that the user created a file on the fly since + // the last prompt or chdir, so try updating the load path and + // searching again. + + load_path& lp = __get_load_path__ ("fcn_info::fcn_info_rep::find"); + + lp.update (); + + retval = xfind (args, local_funcs); + } + + return retval; + } + + octave_value + fcn_info::fcn_info_rep::xfind (const octave_value_list& args, + bool local_funcs) + { + if (local_funcs) + { + scope *curr_scope + = __get_current_scope__ ("fcn_info::fcn_info_rep::xfind"); + + octave_user_function *current_fcn + = curr_scope ? curr_scope->function () : nullptr; + + // Local function. + + if (current_fcn) + { + std::string fcn_file = current_fcn->fcn_file_name (); + + // For anonymous functions we look at the parent scope so that if + // they were defined within class methods and use local functions + // (helper functions) we can still use those anonymous functions + + if (current_fcn->is_anonymous_function ()) + { + if (fcn_file.empty () + && curr_scope->parent_scope () != nullptr + && curr_scope->parent_scope ()->function () != nullptr) + fcn_file + = curr_scope->parent_scope ()->function ()->fcn_file_name(); + } + + if (! fcn_file.empty ()) + { + str_val_iterator r = local_functions.find (fcn_file); + + if (r != local_functions.end ()) + { + // We shouldn't need an out-of-date check here since + // local functions may ultimately be called only from + // a primary function or method defined in the same + // file. + + return r->second; + } + } + } + + // Private function. + + if (current_fcn) + { + std::string dir_name = current_fcn->dir_name (); + + if (! dir_name.empty ()) + { + str_val_iterator q = private_functions.find (dir_name); + + if (q == private_functions.end ()) + { + 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. + + if (! args.empty ()) + { + std::string dispatch_type = get_dispatch_type (args); + + octave_value fcn = find_method (dispatch_type); + + if (fcn.is_defined ()) + return fcn; + } + + // Class constructors. The class name and function name are the same. + + str_val_iterator q = class_constructors.find (name); + + if (q == class_constructors.end ()) + { + octave_value val = load_class_constructor (); + + if (val.is_defined ()) + return val; + } + else + { + octave_value& fval = q->second; + + if (fval.is_defined ()) + out_of_date_check (fval, name); + + if (fval.is_defined ()) + return fval; + else + { + octave_value val = load_class_constructor (); + + if (val.is_defined ()) + return val; + } + } + + // Command-line function. + + if (cmdline_function.is_defined ()) + return cmdline_function; + + // Autoload? + + octave_value fcn = find_autoload (); + + if (fcn.is_defined ()) + return fcn; + + // Function on the path. + + fcn = find_user_function (); + + if (fcn.is_defined ()) + return fcn; + + // Package + + fcn = find_package (); + + if (fcn.is_defined ()) + return fcn; + + // Built-in function (might be undefined). + + return built_in_function; + } + + // Find the definition of NAME according to the following precedence + // list: + // + // built-in function + // function on the path + // autoload function + // command-line function + // private function + // subfunction + + // This function is used to implement the "builtin" function, which + // searches for "built-in" functions. In Matlab, "builtin" only + // returns functions that are actually built-in to the interpreter. + // But since the list of built-in functions is different in Octave and + // Matlab, we also search up the precedence list until we find + // something that matches. Note that we are only searching by name, + // so class methods and constructors are skipped. + + octave_value + fcn_info::fcn_info_rep::builtin_find (void) + { + octave_value retval = x_builtin_find (); + + if (! retval.is_defined ()) + { + // It is possible that the user created a file on the fly since + // the last prompt or chdir, so try updating the load path and + // searching again. + + load_path& lp = __get_load_path__ ("fcn_info::fcn_info_rep::builtin_find"); + + lp.update (); + + retval = x_builtin_find (); + } + + return retval; + } + + octave_value + fcn_info::fcn_info_rep::x_builtin_find (void) + { + // Built-in function. + if (built_in_function.is_defined ()) + return built_in_function; + + // Function on the path. + + octave_value fcn = find_user_function (); + + if (fcn.is_defined ()) + return fcn; + + // Autoload? + + fcn = find_autoload (); + + if (fcn.is_defined ()) + return fcn; + + // Command-line function. + + if (cmdline_function.is_defined ()) + return cmdline_function; + + // Private function. + + scope *curr_scope + = __get_current_scope__ ("fcn_info::fcn_info_rep::x_builtin_find"); + + octave_user_function *current_fcn = curr_scope ? curr_scope->function () : nullptr; + + if (current_fcn) + { + std::string dir_name = current_fcn->dir_name (); + + if (! dir_name.empty ()) + { + str_val_iterator q = private_functions.find (dir_name); + + if (q == private_functions.end ()) + { + 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); + + if (fval.is_defined ()) + return fval; + else + { + octave_value val = load_private_function (dir_name); + + if (val.is_defined ()) + return val; + } + } + } + } + + // Local function. + + if (current_fcn) + { + std::string fcn_file = current_fcn->fcn_file_name (); + + if (! fcn_file.empty ()) + { + str_val_iterator r = local_functions.find (fcn_file); + + if (r != local_functions.end ()) + { + // We shouldn't need an out-of-date check here since local + // functions may ultimately be called only from a primary + // function or method defined in the same file. + + return r->second; + } + } + } + + // Subfunction. I think it only makes sense to check for + // subfunctions if we are currently executing a function defined + // from a .m file. + + if (curr_scope) + { + octave_value val = curr_scope->find_subfunction (name); + + if (val.is_defined ()) + return val; + } + + return octave_value (); + } + + octave_value + fcn_info::fcn_info_rep::find_method (const std::string& dispatch_type) + { + octave_value retval; + + str_val_iterator q = class_methods.find (dispatch_type); + + if (q == class_methods.end ()) + { + octave_value val = load_class_method (dispatch_type); + + if (val.is_defined ()) + return val; + } + else + { + octave_value& fval = q->second; + + if (fval.is_defined ()) + out_of_date_check (fval, dispatch_type); + + if (fval.is_defined ()) + return fval; + else + { + octave_value val = load_class_method (dispatch_type); + + if (val.is_defined ()) + return val; + } + } + + return retval; + } + + octave_value + fcn_info::fcn_info_rep::find_autoload (void) + { + // Autoloaded function. + + if (autoload_function.is_defined ()) + out_of_date_check (autoload_function); + + if (! autoload_function.is_defined ()) + { + std::string file_name = lookup_autoload (name); + + if (! file_name.empty ()) + { + size_t pos = file_name.find_last_of (sys::file_ops::dir_sep_chars ()); + + std::string dir_name = file_name.substr (0, pos); + + octave_value ov_fcn + = load_fcn_from_file (file_name, dir_name, "", "", + name, true); + + if (ov_fcn.is_defined ()) + autoload_function = octave_value (ov_fcn); + } + } + + return autoload_function; + } + + octave_value + fcn_info::fcn_info_rep::find_user_function (void) + { + // Function on the path. + + if (function_on_path.is_defined ()) + out_of_date_check (function_on_path); + + if (function_on_path.is_undefined ()) + { + std::string dir_name; + + load_path& lp = __get_load_path__ ("fcn_info::fcn_info_rep::find_user_function"); + + + std::string file_name = lp.find_fcn (name, dir_name, package_name); + + if (! file_name.empty ()) + { + octave_value ov_fcn + = load_fcn_from_file (file_name, dir_name, "", + package_name); + + if (ov_fcn.is_defined ()) + function_on_path = ov_fcn; + } + } + + return function_on_path; + } + + octave_value + fcn_info::fcn_info_rep::find_package (void) + { + // FIXME: implement correct way to check out of date package + //if (package.is_defined ()) + // out_of_date_check (package); + + if (package.is_undefined ()) + { + cdef_manager& cdm + = __get_cdef_manager__ ("fcn_info::fcn_info_rep::find_package"); + + octave_function *fcn = cdm.find_package_symbol (full_name ()); + + if (fcn) + package = octave_value (fcn); + } + + return package; + } + + void + fcn_info::fcn_info_rep::install_built_in_dispatch (const std::string& klass) + { + if (built_in_function.is_defined ()) + { + octave_function *fcn = built_in_function.function_value (); + + if (fcn) + { + if (fcn->handles_dispatch_class (klass)) + warning ("install_built_in_dispatch: '%s' already defined for class '%s'", + name.c_str (), klass.c_str ()); + else + fcn->push_dispatch_class (klass); + } + } + else + error ("install_built_in_dispatch: '%s' is not a built-in function", + name.c_str ()); + } + + octave_value + fcn_info::fcn_info_rep::dump (void) const + { + std::map<std::string, octave_value> m + = {{ "name", full_name () }, + { "refcount", count.value () }, + { "package", package.dump () }, + { "local_functions", dump_function_map (local_functions) }, + { "private_functions", dump_function_map (private_functions) }, + { "class_methods", dump_function_map (class_methods) }, + { "class_constructors", dump_function_map (class_constructors) }, + { "cmdline_function", cmdline_function.dump () }, + { "autoload_function", autoload_function.dump () }, + { "function_on_path", function_on_path.dump () }, + { "built_in_function", built_in_function.dump () }}; + + return octave_value (m); + } + + octave_value + dump_function_map (const std::map<std::string, octave_value>& fcn_map) + { + if (fcn_map.empty ()) + return octave_value (Matrix ()); + + std::map<std::string, octave_value> info_map; + + for (const auto& nm_fcn : fcn_map) + { + std::string nm = nm_fcn.first; + const octave_value& fcn = nm_fcn.second; + info_map[nm] = fcn.dump (); + } + + return octave_value (info_map); + } +}