changeset 15237:44d6ffdf9479

Disallow new variables in nested functions (bug #36271) * src/ov-usr-fcn.cc (octave_user_function::bind_automatic_vars): Use force_varref. * src/variables.cc (bind_ans): Use force_varref. * src/pt-id.cc (tree_identifier::rvalue, tree_identifier::lvalue): Identify static workspace errors. * src/pt-id.h (tree_identifier::static_workspace_error): New function. * src/symtab.cc (symbol_table::do_update_nest): Mark static workspaces. * src/symtab.h (symbol_table::symbol_record::symbol_record_rep::is_added_static, symbol_table::symbol_record::symbol_record_rep::mark_added_static, symbol_table::symbol_record::symbol_record_rep::unmark_added_static, symbol_table::symbol_record::is_added_static, symbol_table::symbol_record::mark_added_static, symbol_table::symbol_record::unmark_added_static, symbol_table::force_varref): New functions. (symbol_table::symbol_table): Initialize static_workspace to false. (symbol_table::do_insert): Added force_add parameter. Mark records as added_static. (symbol_table::do_varref): Added force_add parameter. * test/nest/test_nest.m: Added nest_eval tests. * test/nest/module.mk: Added nest/nest_eval.m. * test/nest/nest_eval.m: New file.
author Max Brister <max@2bass.com>
date Tue, 17 Apr 2012 21:24:20 -0600
parents ab3d4c1affee
children d65ef0fc5e05
files libinterp/interpfcn/symtab.cc libinterp/interpfcn/symtab.h libinterp/interpfcn/variables.cc libinterp/octave-value/ov-usr-fcn.cc libinterp/parse-tree/pt-id.cc libinterp/parse-tree/pt-id.h test/nest/module.mk test/nest/nest_eval.m test/nest/test_nest.m
diffstat 9 files changed, 75 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/interpfcn/symtab.cc
+++ b/libinterp/interpfcn/symtab.cc
@@ -1492,8 +1492,11 @@
         }
     }
   else if (nest_children.size ())
-    for (table_iterator ti = table.begin (); ti != table.end (); ++ti)
-      ti->second.set_curr_fcn (curr_fcn);
+    {
+      static_workspace = true;
+      for (table_iterator ti = table.begin (); ti != table.end (); ++ti)
+        ti->second.set_curr_fcn (curr_fcn);
+    }
 
   for (std::vector<symbol_table*>::iterator iter = nest_children.begin ();
        iter != nest_children.end (); ++iter)
--- a/libinterp/interpfcn/symtab.h
+++ b/libinterp/interpfcn/symtab.h
@@ -199,6 +199,10 @@
     // temporary variables forced into symbol table for parsing
     static const unsigned int forced = 128;
 
+    // this symbol may NOT become a variable.
+    // (symbol added to a static workspace)
+    static const unsigned int added_static = 256;
+
   private:
 
     class
@@ -348,6 +352,7 @@
       bool is_global (void) const { return storage_class & global; }
       bool is_persistent (void) const { return storage_class & persistent; }
       bool is_forced (void) const { return storage_class & forced; }
+      bool is_added_static (void) const {return storage_class & added_static; }
 
       void mark_local (void) { storage_class |= local; }
       void mark_automatic (void) { storage_class |= automatic; }
@@ -369,6 +374,7 @@
           storage_class |= persistent;
       }
       void mark_forced (void) { storage_class |= forced; }
+      void mark_added_static (void) { storage_class |= added_static; }
 
       void unmark_local (void) { storage_class &= ~local; }
       void unmark_automatic (void) { storage_class &= ~automatic; }
@@ -378,6 +384,7 @@
       void unmark_global (void) { storage_class &= ~global; }
       void unmark_persistent (void) { storage_class &= ~persistent; }
       void unmark_forced (void) { storage_class &= ~forced; }
+      void unmark_added_static (void) { storage_class &= ~added_static; }
 
       void init_persistent (void)
       {
@@ -535,6 +542,7 @@
     bool is_inherited (void) const { return rep->is_inherited (); }
     bool is_persistent (void) const { return rep->is_persistent (); }
     bool is_forced (void) const { return rep->is_forced (); }
+    bool is_added_static (void) const { return rep->is_added_static (); }
 
     void mark_local (void) { rep->mark_local (); }
     void mark_automatic (void) { rep->mark_automatic (); }
@@ -544,6 +552,7 @@
     void mark_global (void) { rep->mark_global (); }
     void mark_persistent (void) { rep->mark_persistent (); }
     void mark_forced (void) { rep->mark_forced (); }
+    void mark_added_static (void) { rep->mark_added_static (); }
 
     void unmark_local (void) { rep->unmark_local (); }
     void unmark_automatic (void) { rep->unmark_automatic (); }
@@ -553,6 +562,7 @@
     void unmark_global (void) { rep->unmark_global (); }
     void unmark_persistent (void) { rep->unmark_persistent (); }
     void unmark_forced (void) { rep->unmark_forced (); }
+    void unmark_added_static (void) { rep->unmark_added_static (); }
 
     void init_persistent (void) { rep->init_persistent (); }
 
@@ -1207,13 +1217,23 @@
 
   static octave_value& varref (const std::string& name,
                                scope_id scope = xcurrent_scope,
-                               context_id context = xdefault_context)
+                               context_id context = xdefault_context,
+                               bool force_add = false)
   {
     static octave_value foobar;
 
     symbol_table *inst = get_instance (scope);
 
-    return inst ? inst->do_varref (name, context) : foobar;
+    return inst ? inst->do_varref (name, context, force_add) : foobar;
+  }
+
+  // Convenience function to greatly simplify
+  // octave_user_function::bind_automatic_vars
+  static octave_value& force_varref (const std::string& name,
+                                     scope_id scope = xcurrent_scope,
+                                     context_id context = xdefault_context)
+  {
+    return varref (name, scope, context, true);
   }
 
   static octave_value varval (const std::string& name,
@@ -2110,6 +2130,9 @@
   // The associated user code (may be null).
   octave_user_function *curr_fcn;
 
+  // If true then no variables can be added.
+  bool static_workspace;
+
   // Map from names of global variables to values.
   static std::map<std::string, octave_value> global_table;
 
@@ -2150,7 +2173,7 @@
 
   symbol_table (scope_id scope)
     : my_scope (scope), table_name (), table (), nest_children (), nest_parent (0),
-    curr_fcn (0), persistent_table () { }
+    curr_fcn (0), static_workspace (false), persistent_table () { }
 
   ~symbol_table (void) { }
 
@@ -2278,18 +2301,23 @@
 
   octave_value do_builtin_find (const std::string& name);
 
-  symbol_record& do_insert (const std::string& name)
+  symbol_record& do_insert (const std::string& name, bool force_add = false)
   {
     table_iterator p = table.find (name);
 
     if (p == table.end ())
       {
-        symbol_record parent_symbol;
-
-        if (nest_parent && nest_parent->look_nonlocal (name, parent_symbol))
-          return table[name] = parent_symbol;
+        symbol_record ret (my_scope, name);
+
+        if (nest_parent && nest_parent->look_nonlocal (name, ret))
+          return table[name] = ret;
         else
-          return table[name] = symbol_record (my_scope, name, octave_value ());
+          {
+            if (static_workspace && ! force_add)
+              ret.mark_added_static ();
+
+            return table[name] = ret;
+          }
       }
     else
       return p->second;
@@ -2309,13 +2337,13 @@
       p->second.force_variable (context);
   }
 
-  octave_value& do_varref (const std::string& name, context_id context)
+  octave_value& do_varref (const std::string& name, context_id context, bool force_add)
   {
     table_iterator p = table.find (name);
 
     if (p == table.end ())
       {
-        symbol_record& sr = do_insert (name);
+        symbol_record& sr = do_insert (name, force_add);
 
         return sr.varref (context);
       }
--- a/libinterp/interpfcn/variables.cc
+++ b/libinterp/interpfcn/variables.cc
@@ -1881,7 +1881,7 @@
         }
       else
         {
-          symbol_table::varref (ans) = val;
+          symbol_table::force_varref (ans) = val;
 
           if (print)
             val.print_with_name (octave_stdout, ans);
--- a/libinterp/octave-value/ov-usr-fcn.cc
+++ b/libinterp/octave-value/ov-usr-fcn.cc
@@ -581,8 +581,8 @@
       // which might be redefined in a function.  Keep the old argn name
       // for backward compatibility of functions that use it directly.
 
-      symbol_table::varref ("argn") = arg_names;
-      symbol_table::varref (".argn.") = Cell (arg_names);
+      symbol_table::force_varref ("argn") = arg_names;
+      symbol_table::force_varref (".argn.") = Cell (arg_names);
 
       symbol_table::mark_hidden (".argn.");
 
@@ -590,8 +590,8 @@
       symbol_table::mark_automatic (".argn.");
     }
 
-  symbol_table::varref (".nargin.") = nargin;
-  symbol_table::varref (".nargout.") = nargout;
+  symbol_table::force_varref (".nargin.") = nargin;
+  symbol_table::force_varref (".nargout.") = nargout;
 
   symbol_table::mark_hidden (".nargin.");
   symbol_table::mark_hidden (".nargout.");
--- a/libinterp/parse-tree/pt-id.cc
+++ b/libinterp/parse-tree/pt-id.cc
@@ -94,6 +94,8 @@
           retval = val;
         }
     }
+  else if (sym->is_added_static ())
+    static_workspace_error ();
   else
     eval_undefined_error ();
 
@@ -116,6 +118,9 @@
 octave_lvalue
 tree_identifier::lvalue (void)
 {
+  if (sym->is_added_static ())
+    static_workspace_error ();
+
   return octave_lvalue (&(sym->varref ()));
 }
 
--- a/libinterp/parse-tree/pt-id.h
+++ b/libinterp/parse-tree/pt-id.h
@@ -109,6 +109,12 @@
 
   void eval_undefined_error (void);
 
+  void static_workspace_error (void)
+  {
+    ::error ("can not add variable \"%s\" to a static workspace",
+             name ().c_str ());
+  }
+
   tree_identifier *dup (symbol_table::scope_id scope,
                         symbol_table::context_id context) const;
 
--- a/test/nest/module.mk
+++ b/test/nest/module.mk
@@ -1,6 +1,7 @@
 nest_FCN_FILES = \
   nest/arg_nest.m \
   nest/arg_ret.m \
+  nest/nest_eval.m \
   nest/no_closure.m \
   nest/persistent_nest.m \
   nest/recursive_nest.m \
new file mode 100644
--- /dev/null
+++ b/test/nest/nest_eval.m
@@ -0,0 +1,8 @@
+function x = nest_eval (a, b)
+  eval (a);
+  nested ();
+
+  function nested ()
+    eval (b);
+  endfunction
+endfunction
--- a/test/nest/test_nest.m
+++ b/test/nest/test_nest.m
@@ -47,7 +47,12 @@
 %!test
 %! scope3;
 
+%!assert (nest_eval ("x = 5;", "x = 6;"), 6);
+%!assert (nest_eval ("x = 5;", "y = 6;"), 5);
+%!assert (nest_eval ("x = -5; x = abs (x);", "y = 6;"), 5);
+
 %!error <D' undefined near line 7> scope2
 %!error <handles to nested functions are not yet supported> no_closure (0)
 %!error <handles to nested functions are not yet supported> no_closure (1)
-
+%!error <can not add variable "y" to a static workspace> nest_eval ("y = 5;", "")
+%!error <can not add variable "y" to a static workspace> nest_eval ("y;", "")