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;