Mercurial > octave
changeset 23054:564e959a0e89
avoid invalid nested function and subfunctions definitions (bug #50014)
* parse.h, oct-parse.in.yy (base_parser::parent_scope_info): New class.
(base_parser::function_scopes): Use new parent_scope_info class for
this data member.
* oct-parse.in.yy: Change all uses of function_scopes for new type.
(fcn_name): Issue error message and abort parse if duplicate nestted
function or subfunction name is detect.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 14 Jan 2017 10:33:28 -0500 |
parents | b443bfa3bfea |
children | 3fc927d86fe6 |
files | libinterp/parse-tree/oct-parse.in.yy libinterp/parse-tree/parse.h |
diffstat | 2 files changed, 149 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/parse-tree/oct-parse.in.yy Mon Jan 16 16:00:18 2017 -0500 +++ b/libinterp/parse-tree/oct-parse.in.yy Sat Jan 14 10:33:28 2017 -0500 @@ -1243,8 +1243,7 @@ lexer.symtab_context.push (symbol_table::alloc_scope ()); - parser.function_scopes.push_back - (lexer.symtab_context.curr_scope ()); + parser.function_scopes.push (lexer.symtab_context.curr_scope ()); if (! lexer.reading_script_file && parser.curr_fcn_depth == 1 @@ -1462,7 +1461,17 @@ fcn_name : identifier { - std::string id_name = $1->name (); + std::string id = $1->name (); + + if (! parser.function_scopes.name_current_scope (id)) + { + parser.bison_error ("duplicate subfunction or nested function name", + $1->line (), $1->column ()); + + delete $1; + + YYABORT; + } lexer.parsed_function_name.top () = true; lexer.maybe_classdef_get_set_method = false; @@ -2020,6 +2029,90 @@ namespace octave { + size_t + base_parser::parent_scope_info::size (void) const + { + return info.size (); + } + + void + base_parser::parent_scope_info::push (const value_type& elt) + { + info.push_back (elt); + } + + void + base_parser::parent_scope_info::push (symbol_table::scope_id id) + { + push (value_type (id, "")); + } + + void + base_parser::parent_scope_info::pop (void) + { + info.pop_back (); + } + + bool + base_parser::parent_scope_info::name_ok (const std::string& name) + { + // Name can't be the same as any parent function or any other + // function we've already seen. We could maintain a complex + // tree structure of names, or we can just store the set of + // full names of all the functions, which must be unique. + + std::string full_name; + + for (size_t i = 0; i < size()-1; i++) + { + const value_type& elt = info[i]; + + if (name == elt.second) + return false; + + full_name += elt.second + ">"; + } + + full_name += name; + + if (all_names.find (full_name) != all_names.end ()) + return false; + + all_names.insert (full_name); + + return true; + } + + bool + base_parser::parent_scope_info::name_current_scope (const std::string& name) + { + if (! name_ok (name)) + return false; + + if (size () > 0) + info.back().second = name; + + return true; + } + + symbol_table::scope_id + base_parser::parent_scope_info::parent_scope (void) const + { + return size () > 1 ? info[size()-2].first : 0; + } + + std::string + base_parser::parent_scope_info::parent_name (void) const + { + return info[size()-2].second; + } + + void base_parser::parent_scope_info::clear (void) + { + info.clear (); + all_names.clear (); + } + base_parser::base_parser (base_lexer& lxr) : endfunction_found (false), autoloading (false), fcn_file_from_relative_lookup (false), parsing_subfunctions (false), @@ -3191,7 +3284,7 @@ fcn->stash_parent_fcn_name (lexer.fcn_file_name); if (curr_fcn_depth > 1) - fcn->stash_parent_fcn_scope (function_scopes[function_scopes.size ()-2]); + fcn->stash_parent_fcn_scope (function_scopes.parent_scope ()); else fcn->stash_parent_fcn_scope (primary_fcn_scope); } @@ -3274,7 +3367,7 @@ if (endfunction_found && function_scopes.size () > 1) { symbol_table::scope_id pscope - = function_scopes[function_scopes.size ()-2]; + = function_scopes.parent_scope (); symbol_table::install_nestfunction (nm, octave_value (fcn), pscope); @@ -3321,7 +3414,7 @@ parsing_subfunctions = true; curr_fcn_depth--; - function_scopes.pop_back (); + function_scopes.pop (); lexer.defining_func--; lexer.parsed_function_name.pop ();
--- a/libinterp/parse-tree/parse.h Mon Jan 16 16:00:18 2017 -0500 +++ b/libinterp/parse-tree/parse.h Sat Jan 14 10:33:28 2017 -0500 @@ -29,9 +29,9 @@ #include <string> -#include <stack> -#include <vector> +#include <deque> #include <map> +#include <set> #include "lex.h" #include "symtab.h" @@ -147,6 +147,52 @@ class base_parser { + private: + + class parent_scope_info + { + public: + + typedef std::pair<symbol_table::scope_id, std::string> value_type; + + typedef std::deque<value_type>::iterator iterator; + typedef std::deque<value_type>::const_iterator const_iterator; + + typedef std::deque<value_type>::reverse_iterator reverse_iterator; + typedef std::deque<value_type>::const_reverse_iterator const_reverse_iterator; + + parent_scope_info (void) = default; + + parent_scope_info (const parent_scope_info&) = default; + + parent_scope_info& operator = (const parent_scope_info&) = default; + + ~parent_scope_info (void) = default; + + size_t size (void) const; + + void push (const value_type& elt); + + void push (symbol_table::scope_id id); + + void pop (void); + + bool name_ok (const std::string& name); + + bool name_current_scope (const std::string& name); + + symbol_table::scope_id parent_scope (void) const; + + std::string parent_name (void) const; + + void clear (void); + + private: + + std::deque<value_type> info; + std::set<std::string> all_names; + }; + public: base_parser (base_lexer& lxr); @@ -442,10 +488,8 @@ // in a package directory (+-directory). std::string curr_package_name; - // A stack holding the nested function scopes being parsed. - // We don't use std::stack, because we want the clear method. Also, we - // must access one from the top - std::vector<symbol_table::scope_id> function_scopes; + // Nested function scopes and names currently being parsed. + parent_scope_info function_scopes; // Pointer to the primary user function or user script function. octave_function *primary_fcn_ptr;