changeset 32449:36d6af1923f7

Don't mix up field and id slots when creating bytecode script frames (bug #64817) Instead of looking up variables by name lineary in the slot name data, use a map. * stack-frame.cc: Use map for id lookup * pt-bytecode-walk.cc: Populate map * pt-bytecode.h: Change set to map * test/compile/bytecode_struct.m: Update tests * test/compile/module.mk: Add new test script * test/compile/script_defines_qweqwe.m: New test script
author Petter T.
date Fri, 27 Oct 2023 18:20:27 +0200
parents a7a7ac88fdc4
children 1f77c3f61aea
files libinterp/corefcn/stack-frame.cc libinterp/parse-tree/pt-bytecode-walk.cc libinterp/parse-tree/pt-bytecode.h test/compile/bytecode_struct.m test/compile/module.mk test/compile/script_defines_qweqwe.m
diffstat 6 files changed, 64 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/stack-frame.cc	Fri Oct 27 18:20:21 2023 +0200
+++ b/libinterp/corefcn/stack-frame.cc	Fri Oct 27 18:20:27 2023 +0200
@@ -1079,29 +1079,14 @@
 
   symbol_record lookup_symbol (const std::string& name) const
   {
-    int local_offset = -1;
     scope_flags flag = LOCAL;
 
-    for (int i = 0; i < static_cast<int> (m_size); i++)
+    auto it = m_unwind_data->m_map_user_locals_names_to_slot.find (name);
+
+    if (it != m_unwind_data->m_map_user_locals_names_to_slot.end ())
       {
-        if (m_name_data [i] == name)
-          {
-            local_offset = i;
-
-            bool is_global = slot_is_global (local_offset);
-            bool is_pers = slot_is_persistent (local_offset);
-
-            if (is_global)
-              flag = GLOBAL;
-            else if (is_pers)
-              flag = PERSISTENT;
-
-            break;
-          }
-      }
-
-    if (local_offset >= 0)
-      {
+        int local_offset = it->second;
+
         symbol_record ret (name, flag);
 
         size_t frame_offset;
@@ -1139,7 +1124,7 @@
       }
 
     // Search the "scope" object of this and any nested frame
-    // The scope object will have e.g. variables added by scripts or eval
+    // The scope object will have e.g. variables added by scripts, global declares or eval
     const stack_frame *frame = this;
     std::size_t frame_cntr = 0;
     while (frame)
@@ -1150,9 +1135,10 @@
 
         if (sym)
           {
-            // Return symbol record with adjusted frame offset (relative to the one lookup is done on)
+            // Return symbol record with adjusted frame offset relative
+            // to the one lookup is done on, i.e. the 'this' frame.
             symbol_record new_sym = sym.dup ();
-            new_sym.set_frame_offset (frame_cntr);
+            new_sym.set_frame_offset (new_sym.frame_offset () + frame_cntr);
             return new_sym;
           }
 
@@ -1460,8 +1446,9 @@
         // Move all user symbol values from the parent frame to the eval frame.
         // Replace the values in the parent frame with a pointer-like object "octave_value_ref_vmlocal"
         // pointing to the eval frame.
-        for (std::string id_name : parent_frame_bc->m_unwind_data->m_set_user_locals_names)
+        for (const auto &kv : parent_frame_bc->m_unwind_data->m_map_user_locals_names_to_slot)
           {
+            const std::string &id_name = kv.first;
             symbol_record sr_eval = eval_frame->lookup_symbol (id_name);
             if (!sr_eval.is_valid ())
               sr_eval = eval_frame->insert_symbol (id_name);
@@ -1506,8 +1493,9 @@
     // Move all user symbols from the eval frame to the current frame we are entering.
     // Replace the moved values in the eval frame with a pointer-like object "octave_value_ref_vmlocal"
     // pointing to the current frame.
-    for (std::string id_name : m_unwind_data->m_set_user_locals_names)
+    for (const auto &kv : m_unwind_data->m_map_user_locals_names_to_slot)
       {
+        const std::string &id_name = kv.first;
         symbol_record sr_eval = eval_frame->lookup_symbol (id_name);
         if (!sr_eval.is_valid ())
           sr_eval = eval_frame->insert_symbol (id_name);
@@ -1554,10 +1542,12 @@
     bool eval_frame_is_bytecode = eval_frame->is_bytecode_fcn_frame ();
 
     // Move all user symbols from the current frame to the eval frame.
-    for (std::string id_name : m_unwind_data->m_set_user_locals_names)
+    for (const auto &kv : m_unwind_data->m_map_user_locals_names_to_slot)
       {
+        const std::string &id_name = kv.first;
         symbol_record sr_eval = eval_frame->lookup_symbol (id_name);
         CHECK_PANIC (sr_eval.is_valid ());
+
         octave_value *ov_eval = sr_eval.frame_offset () ?
                                   &eval_frame->varref (sr_eval, false) :
                                   &eval_frame->varref (sr_eval.data_offset (), false);
@@ -1595,8 +1585,9 @@
         // Move all values the parent frame needs to it from the eval frame.
         // In the eval frame, put a pointer-like object "octave_value_ref_vmlocal"
         // pointing to the parent frame
-        for (std::string id_name : parent_frame_bc->m_unwind_data->m_set_user_locals_names)
+        for (const auto &kv : parent_frame_bc->m_unwind_data->m_map_user_locals_names_to_slot)
           {
+            const std::string &id_name = kv.first;
             symbol_record sr_eval = eval_frame->lookup_symbol (id_name);
             CHECK_PANIC (sr_eval.is_valid ());
             octave_value *ov_eval = sr_eval.frame_offset () ?
--- a/libinterp/parse-tree/pt-bytecode-walk.cc	Fri Oct 27 18:20:21 2023 +0200
+++ b/libinterp/parse-tree/pt-bytecode-walk.cc	Fri Oct 27 18:20:27 2023 +0200
@@ -2418,10 +2418,10 @@
   add_id_to_table("%nargout");
 
   // We always need the magic id "ans"
-  add_id_to_table ("ans");
-  // m_set_user_locals_names keeps track of what user symbols to borrow from and return back to
+  int slot_ans = add_id_to_table ("ans");
+  // m_map_user_locals_names_to_slot keeps track of what user symbols to borrow from and return back to
   // the eval_frame when the script frame is pushed and popped.
-  m_code.m_unwind_data.m_set_user_locals_names.insert ("ans");
+  m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({"ans", slot_ans});
 
   // We add all identifiers in the body to the id-table. We also
   // make a map mapping the interpreters frame offset of a id
@@ -2445,7 +2445,7 @@
           m_code.m_unwind_data.m_external_frame_offset_to_internal[frame_offset][offset] = slot;
 
           if (frame_offset == 0)
-            m_code.m_unwind_data.m_set_user_locals_names.insert (name);
+            m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({name, slot});
         }
     }
 
@@ -2630,17 +2630,19 @@
     {
       for (auto it = returns->begin (); it != returns->end (); it++)
         {
-          std::string name = (*it)->name();
           tree_identifier *id = (*it)->ident ();
           CHECK_NONNULL (id);
-          add_id_to_table (id->name ());
+          std::string name = id->name ();
+          int slot = add_id_to_table (name);
+          m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({name, slot});
         }
     }
+
   if (m_varargout)
-    add_id_to_table ("varargout"); // Not in the returns list. Need to be last
-
-  // The function itself is put after the arg outs
-  /* add_id_to_table (fcn.name ()); */
+  {
+    int slot = add_id_to_table ("varargout"); // Not in the returns list. Need to be last
+    m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({"varargout", slot});
+  }
 
   // Then the arguments
   for (std::string name : v_paras)
@@ -2678,11 +2680,13 @@
           continue;
         }
 
-      add_id_to_table (name);
+      int slot = add_id_to_table (name);
+      m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({name, slot});
     }
 
   // We always need the magic id "ans"
-  add_id_to_table ("ans");
+  int slot_ans = add_id_to_table ("ans");
+  m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({"ans", slot_ans});
 
   // We add all identifiers in the body to the id-table. We also
   // make a map mapping the interpreters frame offset of a id
@@ -2705,7 +2709,7 @@
           m_code.m_unwind_data.m_external_frame_offset_to_internal[frame_offset][offset] = slot;
 
           if (frame_offset == 0)
-            m_code.m_unwind_data.m_set_user_locals_names.insert (name);
+            m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({name, slot});
         }
     }
   // We need the arguments and return id:s in the map too.
@@ -2716,8 +2720,10 @@
           CHECK_NONNULL (*it);
           tree_identifier *id = (*it)->ident ();
           int offset = id->symbol ().data_offset ();
-          int slot = SLOT (id->name ());
+          std::string name = id->name ();
+          int slot = SLOT (name);
           m_code.m_unwind_data.m_external_frame_offset_to_internal[0][offset] = slot;
+          m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({name, slot});
 
           // If the parameter has an init expression e.g.
           // "function foo (a = sin (pi))"
@@ -2728,12 +2734,12 @@
               auto v_names_offsets = collect_idnames_walker::collect_id_names (*init_expr);
               for (auto name_offset : v_names_offsets)
                 {
-                  std::string name = name_offset.m_name;
+                  std::string name_i = name_offset.m_name;
                   int offset_i = name_offset.m_offset;
-                  add_id_to_table (name);
-                  int slot_i = SLOT (name);
+                  int slot_i = add_id_to_table (name_i);
 
                   m_code.m_unwind_data.m_external_frame_offset_to_internal[0][offset_i] = slot_i;
+                  m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({name_i, slot_i});
                 }
             }
         }
@@ -2791,6 +2797,8 @@
 
       int slot = add_id_to_table (fn_name);
       m_code.m_unwind_data.m_external_frame_offset_to_internal[frame_offset][offset] = slot;
+
+      m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({fn_name, slot});
     }
 
   // Add the parents' function names for nested functions
@@ -2810,6 +2818,8 @@
 
           int slot = add_id_to_table (parent_name);
           m_code.m_unwind_data.m_external_frame_offset_to_internal[frame_offset][offset] = slot;
+
+          m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({parent_name, slot});
         }
     }
 
@@ -2880,6 +2890,8 @@
               // Increasing number for fake external offset
               int fake_external_offset = m_code.m_unwind_data.m_external_frame_offset_to_internal[1].size ();
               m_code.m_unwind_data.m_external_frame_offset_to_internal[1][fake_external_offset] = slot_added;
+
+              m_code.m_unwind_data.m_map_user_locals_names_to_slot.insert ({name, slot_added});
             }
 
           int slot = SLOT (name);
--- a/libinterp/parse-tree/pt-bytecode.h	Fri Oct 27 18:20:21 2023 +0200
+++ b/libinterp/parse-tree/pt-bytecode.h	Fri Oct 27 18:20:27 2023 +0200
@@ -273,7 +273,7 @@
   std::map<int, tree*> m_ip_to_tree;
   std::vector<arg_name_entry> m_argname_entries;
   std::vector<std::map<int,int>> m_external_frame_offset_to_internal;
-  std::set<std::string> m_set_user_locals_names;
+  std::map<std::string, int> m_map_user_locals_names_to_slot;
 
   struct nested_var_offset { int m_depth; int m_slot_parent; int m_slot_nested; };
   std::vector<nested_var_offset> m_v_nested_vars;
--- a/test/compile/bytecode_struct.m	Fri Oct 27 18:20:21 2023 +0200
+++ b/test/compile/bytecode_struct.m	Fri Oct 27 18:20:27 2023 +0200
@@ -17,8 +17,22 @@
   % Test word command struct subref
 
   __printf_assert__ ("%d ", suby.b);
+
+  % Bug 64817
+  test_bug_64817;
 end
 
 function a = suby ()
   a.b = 4;
 end
+
+function test_bug_64817
+  % Field id slot collided with variable slot when making
+  % the script frame.
+
+  script_defines_qweqwe; % Just does "qweqwe = 0;"
+
+  if 0
+    asd.qweqwe; % 'qweqwe' is a field here
+  end
+end
--- a/test/compile/module.mk	Fri Oct 27 18:20:21 2023 +0200
+++ b/test/compile/module.mk	Fri Oct 27 18:20:27 2023 +0200
@@ -48,6 +48,7 @@
   %reldir%/inputname_args.m \
   %reldir%/just_call_handle_with_arg.m \
   %reldir%/return_isargout.m \
+  %reldir%/script_defines_qweqwe.m \
   %reldir%/script1.m \
   %reldir%/script11.m \
   %reldir%/script2.m \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/compile/script_defines_qweqwe.m	Fri Oct 27 18:20:27 2023 +0200
@@ -0,0 +1,1 @@
+qweqwe = 0;
\ No newline at end of file