diff libinterp/corefcn/symscope.cc @ 26661:cf9e10ce3351

move variable values from symbol_record objects to stack_frame objects Apologies for the massive commit. I see no way to untangle these changes into a set of smaller incremental changes in a way that would be more useful. Previously, handling data for recursive function calls was managed by a stack of values in the symbol_record class and an auxiliary integer variable was used for managing the recursion depth (context_id). Now, values for local variables are in the stack_frame class and recursion is handled naturally by the call_stack as a new stack frame is added to the call_stack object for any call to a function or a script. Values for internal function call information (nargin, nargout, etc.) are now stored specially in the stack_frame object. Values for global variables are now stored in a map in the call_stack object. Values for persistent variables are stored in the corresponding octave_user_function object. Access to non-local variables inside nested functions is managed through pointers to stack_frame objects for the parent function scopes. The new implementation more closely resembles the techniques described in standard compiler literature. These changes should make it easier to create proper closures and finally solve bug #39257 (handles to nested functions are not yet supported). They may also make it easier to implement JIT compiler, though that is probably still a long way off. The new stack-frame.h file has some details about the new implementation of stack frames that should help in understanding how things work now. Describing each change to each file and function will probably not provide much greater understanding of the changes and would be quite tedious to write so I am omitting them.
author John W. Eaton <jwe@octave.org>
date Mon, 28 Jan 2019 18:01:46 +0000
parents 00f796120a6d
children b442ec6dda5c
line wrap: on
line diff
--- a/libinterp/corefcn/symscope.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/symscope.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -34,66 +34,30 @@
 #include "ov-usr-fcn.h"
 #include "symrec.h"
 #include "symscope.h"
-#include "symtab.h"
 #include "utils.h"
 
 namespace octave
 {
-  void symbol_scope_rep::install_auto_fcn_vars (void)
+  symbol_record symbol_scope_rep::insert_local (const std::string& name)
   {
-    install_auto_fcn_var (".argn.");
-    install_auto_fcn_var (".ignored.");
-    install_auto_fcn_var (".nargin.");
-    install_auto_fcn_var (".nargout.");
-    install_auto_fcn_var (".saved_warning_states.");
-  }
+    symbol_record sym (name);
 
-  void symbol_scope_rep::install_auto_fcn_var (const std::string& name)
-  {
-    insert (name, true);
-    mark_hidden (name);
-    mark_automatic (name);
+    insert_symbol_record (sym);
+
+    return sym;
   }
 
-  octave_value
-  symbol_scope_rep::find (const std::string& name)
+  void symbol_scope_rep::insert_symbol_record (symbol_record& sr)
   {
-    symbol_table& symtab
-      = __get_symbol_table__ ("symbol_scope_rep::find");
-
-    // Variable.
-
-    table_iterator p = m_symbols.find (name);
-
-    if (p != m_symbols.end ())
-      {
-        symbol_record sr = p->second;
+    size_t data_offset = num_symbols ();
+    std::string name = sr.name ();
 
-        if (sr.is_global ())
-          return symtab.global_varval (name);
-        else
-          {
-            octave_value val = sr.varval (m_context);
-
-            if (val.is_defined ())
-              return val;
-          }
-      }
+    sr.set_data_offset (data_offset);
 
-    // Subfunction.  I think it only makes sense to check for
-    // subfunctions if we are currently executing a function defined
-    // from a .m file.
-
-    octave_value fcn = find_subfunction (name);
-
-    if (fcn.is_defined ())
-      return fcn;
-
-    return symtab.fcn_table_find (name, ovl ());
+    m_symbols[name] = sr;
   }
 
-  symbol_record&
-  symbol_scope_rep::insert (const std::string& name, bool force_add)
+  symbol_record symbol_scope_rep::insert (const std::string& name)
   {
     table_iterator p = m_symbols.find (name);
 
@@ -101,13 +65,20 @@
       {
         symbol_record ret (name);
 
+        size_t data_offset = num_symbols ();
+
+        ret.set_data_offset (data_offset);
+
         auto t_parent = m_parent.lock ();
 
-        if (is_nested () && t_parent && t_parent->look_nonlocal (name, ret))
+        size_t offset = 0;
+
+        if (is_nested () && t_parent
+            && t_parent->look_nonlocal (name, offset, ret))
           return m_symbols[name] = ret;
         else
           {
-            if (m_is_static && ! force_add)
+            if (m_is_static)
               ret.mark_added_static ();
 
             return m_symbols[name] = ret;
@@ -139,13 +110,23 @@
     for (const auto& nm_sr : m_symbols)
       {
         std::string nm = nm_sr.first;
-        const symbol_record& sr = nm_sr.second;
-        info_map[nm] = sr.dump (m_context);
+        symbol_record sr = nm_sr.second;
+        info_map[nm] = sr.dump ();
       }
 
     return octave_value (info_map);
   }
 
+  std::list<symbol_record> symbol_scope_rep::symbol_list (void) const
+  {
+    std::list<symbol_record> retval;
+
+    for (const auto& nm_sr : m_symbols)
+      retval.push_back (nm_sr.second);
+
+    return retval;
+  }
+
   octave_value
   symbol_scope_rep::find_subfunction (const std::string& name) const
   {
@@ -194,18 +175,30 @@
         // Since is_nested is true, the following should always return a
         // valid scope.
 
+        auto t_parent = m_parent.lock ();
+
+        if (t_parent)
+          {
+            // SCOPE is the parent of this scope: this scope is a child
+            // of SCOPE.
+
+            if (t_parent == scope)
+              return true;
+          }
+
         auto t_primary_parent = m_primary_parent.lock ();
 
         if (t_primary_parent)
           {
             // SCOPE is the primary parent of this scope: this scope is a
-            // child of SCOPE.
+            // child (or grandchild) of SCOPE.
             if (t_primary_parent == scope)
               return true;
 
             // SCOPE and this scope share the same primary parent: they are
-            // siblings.
-            if (t_primary_parent == scope->primary_parent_scope_rep ())
+            // siblings (or cousins)
+            auto scope_primary_parent = scope->primary_parent_scope_rep ();
+            if (t_primary_parent == scope_primary_parent)
               return true;
           }
       }
@@ -213,8 +206,7 @@
     return false;
   }
 
-  void
-  symbol_scope_rep::update_nest (void)
+  void symbol_scope_rep::update_nest (void)
   {
     auto t_parent = m_parent.lock ();
 
@@ -225,12 +217,10 @@
           {
             symbol_record& ours = nm_sr.second;
 
-            if (! ours.is_formal ()
-                && is_nested () && t_parent->look_nonlocal (nm_sr.first, ours))
-              {
-                if (ours.is_global () || ours.is_persistent ())
-                  error ("global and persistent may only be used in the topmost level in which a nested variable is used");
-              }
+            size_t offset = 0;
+
+            if (! ours.is_formal () && is_nested ())
+              t_parent->look_nonlocal (nm_sr.first, offset, ours);
           }
 
         // The scopes of nested functions are static.
@@ -247,40 +237,37 @@
       scope_obj.update_nest ();
   }
 
-  bool
-  symbol_scope_rep::look_nonlocal (const std::string& name,
-                                   symbol_record& result)
+  bool symbol_scope_rep::look_nonlocal (const std::string& name,
+                                        size_t offset, symbol_record& result)
   {
+    offset++;
+
     table_iterator p = m_symbols.find (name);
+
     if (p == m_symbols.end ())
       {
         auto t_parent = m_parent.lock ();
 
         if (is_nested () && t_parent)
-          return t_parent->look_nonlocal (name, result);
+          return t_parent->look_nonlocal (name, offset, result);
       }
-    else if (! p->second.is_automatic ())
+    else
       {
-        result.bind_fwd_rep (shared_from_this (), p->second);
+        // Add scope offsets because the one we found may be used in
+        // this scope but initially from another parent scope beyond
+        // that.  The parent offset will already point to the first
+        // occurrence because we do the overall nesting update from the
+        // parent function down through the lists of all children.
+
+        size_t t_frame_offset = offset + p->second.frame_offset ();
+        size_t t_data_offset = p->second.data_offset ();
+
+        result.set_frame_offset (t_frame_offset);
+        result.set_data_offset (t_data_offset);
+
         return true;
       }
 
     return false;
   }
-
-  void
-  symbol_scope_rep::bind_script_symbols
-    (const std::shared_ptr<symbol_scope_rep>& curr_scope)
-  {
-    for (auto& nm_sr : m_symbols)
-      nm_sr.second.bind_fwd_rep (curr_scope,
-                                 curr_scope->find_symbol (nm_sr.first));
-  }
-
-  void
-  symbol_scope_rep::unbind_script_symbols (void)
-  {
-    for (auto& nm_sr : m_symbols)
-      nm_sr.second.unbind_fwd_rep ();
-  }
 }