[PATCH 1/3] Tie life time of IDebugger::Variable and varobjs



Hello,

While working on the variable-monitor feature[1], it appeared that I
needed to work on the handling of the life time of variables that are
backed up by GDB variable objects aka varobjs.

Up to now, when an instance of IDebugger::Variable was peered with a
backend-side (thus GDB-side) Variable object, the varobjs had to be
deleted explicitly by a call to IDebugger::delete_variable.  That
means, deleting the instance of IDebugger::Variable wouldn't
necessarily result in deleting the related varobj.

The way this was used was that either the local variables widget would
call IDebugger::delete_variable for all locals whenever we moved to a
new frame.  Similarly, the variable inspector would call the same
method after the inspection of the variable was done.

With the variable-monitor feature, a variable created in e.g. the
variable inspector could be added to the monitor and could stay in
there for an arbitrary long period of time.  In this case, it becomes
almost necessary to tie the life time of the varobj with that of the
instance of IDebugger::Variable.

This is what the patch below is about.

IDebugger::Variable now stores a pointer to the relevant instance of
IDebugger.  When the Variable object is about to be destroyed, it
checks if the IDebugger is still connected to the target.  If so, it
invokes its delete_variable method, thus deleting the related varobj
it is peered with.  So now, from the point of the view of the client
code, just deleting the instance IDebugger::Variable does the right
thing.

The patch updates the following places that were previously explicitly
managing the varobjs' life time: nmv-varobj-walker.cc,
nmv-local-vars-inspector.cc, nmv-var-inspector.cc.  As the life time
of IDebugger::Variable is managed automatically (via reference counted
smart pointers) I also had to update several regression tests to keep
a reference on some instances of IDebugger::Variable that need to stay
around during the life of the test itself.

I have tested this on and am applying this to master, because I think
that it generally improves the maintainability of the code,
independently from the variable-monitor feature.

[1]: https://bugzilla.gnome.org/show_bug.cgi?id=542503
[2]: https://bugzilla.gnome.org/show_bug.cgi?id=663959


	* src/dbgengine/nmv-i-debugger.h (IDebugger::delete_variable): Add
	an overload declaration that takes the internal name of the
	backend-side variable object to delete.  Add comments to all the
	overloads.
	(IDebugger::DefaultSlot): Move typedef before declaration of
	IDebugger::Variable.
	(IDebugger::Variable::m_debugger): New member.
	(IDebubber::Variable::Variable): Adjust constructors to initialize
	the new m_debugger member.
	(IDebugger::Variable::debugger): New accessor.
	(IDebugger::Variable::~Variable): If the debugging engine is still
	connected to the Delete the backend-side variable
	object if any.
	(IDebugger::variable_deleted_signal): Add comments.
	* src/dbgengine/nmv-gdb-engine.h (GDBEngine::delete_variable):
	Declare new overload.
	* src/dbgengine/nmv-gdb-engine.cc
	(OnCreateVariableHandler::do_handle): Associate the instance of
	IDebugger::Variable with the instance of IDebugger::Variable it
	was created from, iff there is backend-side variable object for
	that variable.
	(OnDeleteVariableHandler::do_handle): Some requests for deleting
	variable can have null instances of IDebugger::Variable
	associated.  Handle those.
	(GDBEngine::delete_variable): Implement the new overload to delete
	a variable object from its "internal name".  Add comments to the
	other overloads.
	* src/dbgengine/nmv-varobj-walker.cc (~VarobjWalker): Remove.
	(delete_varobj_if_necessary): Remove.
	(connect): Remove call to delete_varobj_if_necessary.
	* src/persp/dbgperspective/nmv-local-vars-inspector.cc
	(LocalVarsInspector::Priv::clear_local_variables)
	(LocalVarsInspector::Priv::clear_function_arguments): Remove uses
	of delete_vars_backend_peers.
	(LocalVarsInspector::Priv::delete_vars_backend_peers): Remove.
	* src/persp/dbgperspective/nmv-var-inspector.cc
	(VarInspector::Priv::graphically_set_variable)
	(VarInspector::Priv::~Priv, VarInspector::inspect_variable)
	(VarInspector::clear): Remove use of delete_variable_if_needed.
	(VarInspector::Priv::delete_variable_if_needed): Remove.
	* tests/fooprog.cc (main): Don't name unused parameters.
	* tests/test-var-path-expr.cc (variables): New global
	container of variables.
	(on_variable_deleted_signal): New signal handler.
	(on_breakpoints_set_signal): Clear variables instead of quitting
	the event loop.
	(on_variable_unfolded, on_variable_created): Add the variable to
	the "variables" container.
	(on_stopped_signal): Count the number of variable created.
	Avoid needlessly passing a reference to the debugger engine.
	(test_main): Don't name unused arguments.  Handle the
	IDebugger::variable_deleted_signal event.
	* tests/test-var-walker.cc (VariableList): Rename
	DebuggerVariableList into this.
	(variables): New global variable to hold variables.
	(on_frames_arguments_listed_signal, on_local_variables_listed_signal):
	Don't name unused parameters.  Add function arguments to the
	"variables" global.
	(test_main): Adjust b/c fooprog's main function doesn't have any
	named parameter anymore.  Clear the "variables" global after the
	event loop is closed.
	* tests/test-vars.cc (variables): New global to handle variables.
	(person_var): Rename var_to_delete into this.
	(on_variable_created_signal, on_varible_created_signal2): Add
	variable to the "variables" global.
	(on_changed_variables_listed_signal):  Don't explicitly
	delete_variable anymore.  Keep stepping, and don't hold a
	reference on the person_var reference.
	(on_variable_deleted_signal, on_variable_deleted_signal2): Be
	aware that a_var can be empty for backend-side variable objects.
	(on_stopped_signal): Adjust.
	(test_main):  Don't expect any variable to be have been deleted
	during this test, and clear the "variables" global.
---
 src/dbgengine/nmv-gdb-engine.cc                    |   88 +++++++++++++--
 src/dbgengine/nmv-gdb-engine.h                     |    4 +
 src/dbgengine/nmv-i-debugger.h                     |  120 +++++++++++++++++--
 src/dbgengine/nmv-varobj-walker.cc                 |   25 ----
 .../dbgperspective/nmv-local-vars-inspector.cc     |   15 ---
 src/persp/dbgperspective/nmv-var-inspector.cc      |   18 ---
 tests/fooprog.cc                                   |    3 +-
 tests/test-var-path-expr.cc                        |   46 +++++++-
 tests/test-var-walker.cc                           |   35 ++++--
 tests/test-vars.cc                                 |   57 ++++++---
 10 files changed, 295 insertions(+), 116 deletions(-)

diff --git a/src/dbgengine/nmv-gdb-engine.cc b/src/dbgengine/nmv-gdb-engine.cc
index b9b7436..3d44c07 100644
--- a/src/dbgengine/nmv-gdb-engine.cc
+++ b/src/dbgengine/nmv-gdb-engine.cc
@@ -2735,6 +2735,8 @@ struct OnCreateVariableHandler : public OutputHandler
     void do_handle (CommandAndOutput &a_in)
     {
         VariableSafePtr var = a_in.output ().result_record ().variable ();
+        if (!var->internal_name ().empty ())
+        var->debugger (m_engine);
 
         // Set the name of the variable to the name that got stored
         // in the tag0 member of the command.
@@ -2785,20 +2787,30 @@ struct OnDeleteVariableHandler : public OutputHandler {
 
     void do_handle (CommandAndOutput &a_in)
     {
-        THROW_IF_FAIL (a_in.command ().variable ());
+        IDebugger::VariableSafePtr var;
         THROW_IF_FAIL (m_engine);
 
-        // Call the slot associated to IDebugger::delete_variable (), if
-        // Any.
+        // Call the slot associated to IDebugger::delete_variable (),
+        // if Any.
         if (a_in.command ().has_slot ()) {
-            typedef sigc::slot<void, const IDebugger::VariableSafePtr> SlotType;
-            SlotType slot = a_in.command ().get_slot<SlotType> ();
-            slot (a_in.command ().variable ());
+            // The resulting command can either have an associated
+            // instance of IDebugger::Variable attached to it or not,
+            // depending on the flavor of IDebugger::delete_variable
+            // that was called.  Make sure to handle both cases.
+            if (a_in.command ().variable ()) {
+                typedef sigc::slot<void, const IDebugger::VariableSafePtr> SlotType;
+                SlotType slot = a_in.command ().get_slot<SlotType> ();
+                var = a_in.command ().variable ();
+                slot (var);
+            } else {
+                typedef IDebugger::DefaultSlot DefaultSlot;
+                IDebugger::DefaultSlot slot = a_in.command ().get_slot<DefaultSlot> ();
+                slot ();
+            }
         }
         // Emit the general IDebugger::variable_deleted_signal ().
-        m_engine->variable_deleted_signal ().emit
-                (a_in.command ().variable (),
-                 a_in.command ().cookie ());
+        m_engine->variable_deleted_signal ().emit (var,
+                                                   a_in.command ().cookie ());
     }
 }; // end OnDeleteVariableHandler
 
@@ -5755,6 +5767,19 @@ GDBEngine::create_variable (const UString &a_name,
     queue_command (command);
 }
 
+/// If a variable has a GDB variable object then this method deletes
+/// the backend.  You should not use this method because the life
+/// cycle of variables backend counter parts is automatically tied to
+/// the life cycle of instances of IDebugger::Variable, unless you
+/// know what you are doing.
+///
+/// Note that when the varobj is deleted, the
+/// IDebugger::variable_deleted signal is invoked.
+///
+/// \param a_var the variable which backend counter to delete.
+///
+/// \param a_cookie a string cookie passed to the
+/// IDebugger::variable_deleted_signal.
 void
 GDBEngine::delete_variable (const VariableSafePtr a_var,
                             const UString &a_cookie)
@@ -5765,6 +5790,22 @@ GDBEngine::delete_variable (const VariableSafePtr a_var,
                      a_cookie);
 }
 
+/// If a variable has a GDB variable object, then this method deletes
+/// the varobj.  You should not use this method because the life cycle
+/// of variables backend counter parts is automatically tied to the
+/// life cycle of instances of IDebugger::Variable, unless you know
+/// what you are doing.
+///
+/// Note that when the varobj is deleted, the
+/// IDebugger::variable_deleted signal is invoked.
+///
+/// \param a_var the variable which backend counter to delete.
+///
+/// \param a_slot a slot asynchronously called when the backend
+/// variable oject is deleted.
+///
+/// \param a_cookie a string cookie passed to the
+/// IDebugger::variable_deleted_signal.
 void
 GDBEngine::delete_variable (const VariableSafePtr a_var,
                             const ConstVariableSlot &a_slot,
@@ -5783,6 +5824,35 @@ GDBEngine::delete_variable (const VariableSafePtr a_var,
     queue_command (command);
 }
 
+/// Deletes a variable object named by a given string.  You should not
+/// use this method because the life cycle of variables backend
+/// counter parts is automatically tied to the life cycle of instances
+/// of IDebugger::Variable, unless you know what you are doing.
+///
+/// Note that when the backend counter part is deleted, the
+/// IDebugger::variable_deleted signal is invoked.
+///
+/// \param a_internal_name the name of the backend variable object we
+/// want to delete.
+///
+/// \param a_slot a slot that is going to be called asynchronuously
+/// when the backend object is deleted.
+void
+GDBEngine::delete_variable (const UString &a_internal_name,
+                            const DefaultSlot &a_slot,
+                            const UString &a_cookie)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_DD;
+
+    THROW_IF_FAIL (!a_internal_name.empty ());
+
+    Command command ("delete-variable",
+                     "-var-delete " + a_internal_name,
+                     a_cookie);
+    command.set_slot (a_slot);
+    queue_command (command);
+}
+
 /// Unfold a given variable.
 ///
 /// Query the backend for the member variables of the given variable.
diff --git a/src/dbgengine/nmv-gdb-engine.h b/src/dbgengine/nmv-gdb-engine.h
index 11b1668..cea76ed 100644
--- a/src/dbgengine/nmv-gdb-engine.h
+++ b/src/dbgengine/nmv-gdb-engine.h
@@ -584,6 +584,10 @@ public:
                           const ConstVariableSlot &a_s,
                           const UString &a_cookie);
 
+    void delete_variable (const UString &a_internal_name,
+			  const DefaultSlot &a_slot,
+			  const UString &a_cookie);
+
     void unfold_variable (VariableSafePtr a_var,
                           const UString &a_cookie);
 
diff --git a/src/dbgengine/nmv-i-debugger.h b/src/dbgengine/nmv-i-debugger.h
index 6fb5fff..56bea4a 100644
--- a/src/dbgengine/nmv-i-debugger.h
+++ b/src/dbgengine/nmv-i-debugger.h
@@ -345,6 +345,8 @@ public:
             m_args.clear ();
         }
     };//end class Frame
+
+    typedef sigc::slot<void> DefaultSlot;
     typedef sigc::slot<void, const vector<IDebugger::Frame>&>
         FrameVectorSlot;
     typedef sigc::slot<void, const map<int, IDebugger::VariableList>& >
@@ -378,6 +380,14 @@ public:
         // (e.g: backend side variable objects in GDB), then this
         // is the name of the backend side counterpart of this variable.
         UString m_internal_name;
+        // If the variable was created with a backend counterpart
+        // (e.g, GDB Variable objects), then this client-side
+        // variable instance needs to have a hold on the instance of
+        // IDebugger that was used to create the variable.  This is
+        // needed so that that instance of IDebugger can be used to
+        // tie the life cycle of the remote variable object peer with
+        // the life cycle of this instance.
+        mutable IDebugger *m_debugger;
         UString m_name;
         UString m_name_caption;
         UString m_value;
@@ -412,8 +422,10 @@ public:
                   const UString &a_name,
                   const UString &a_value,
                   const UString &a_type,
-                  bool a_in_scope = true)
+                  bool a_in_scope = true,
+                  IDebugger *a_dbg = 0)
             : m_internal_name (a_internal_name),
+            m_debugger (a_dbg),
             m_name (a_name),
             m_value (a_value),
             m_type (a_type),
@@ -428,8 +440,10 @@ public:
         Variable (const UString &a_name,
                   const UString &a_value,
                   const UString &a_type,
-                  bool a_in_scope = true)
-            : m_name (a_name),
+                  bool a_in_scope = true,
+                  IDebugger *a_dbg = 0)
+            : m_debugger (a_dbg),
+            m_name (a_name),
             m_value (a_value),
             m_type (a_type),
             m_parent (0),
@@ -441,24 +455,41 @@ public:
         {
         }
 
-        Variable (const UString &a_name)
-            : m_name (a_name),
+        Variable (const UString &a_name,
+                  IDebugger *a_dbg = 0)
+            : m_debugger (a_dbg),
+            m_name (a_name),
             m_parent (0),
             m_num_expected_children (0),
             m_in_scope (true),
             m_format (UNDEFINED_FORMAT),
             m_needs_revisualizing (false)
-                
-        {}
+        {
+        }
 
-        Variable ()
-            : m_parent (0),
+        Variable (IDebugger *a_dbg = 0)
+            : m_debugger (a_dbg),
+            m_parent (0),
             m_num_expected_children (0),
             m_in_scope (true),
             m_format (UNDEFINED_FORMAT),
             m_needs_revisualizing (false)
-                
-        {}
+        {
+        }
+
+        ~Variable ()
+        {
+            // If this variable is peered with an engine-side variable
+            // object then ask the debugging engine to delete the peer
+            // variable object now.
+            if (m_debugger
+                && !internal_name ().empty ()
+                && m_debugger->is_attached_to_target ()) {
+                IDebugger::DefaultSlot empty_slot;
+                m_debugger->delete_variable (internal_name (),
+                                             empty_slot);
+            }
+        }
 
         const VariableList& members () const {return m_members;}
 
@@ -552,6 +583,10 @@ public:
         /// \param a_in the new name of backend side counterpart variable object.
         void internal_name (const UString &a_in) {m_internal_name = a_in;}
 
+
+        IDebugger* debugger () const {return m_debugger;}
+        void debugger (IDebugger *a_dbg) {m_debugger = a_dbg;}
+
         const UString& name () const {return m_name;}
         void name (const UString &a_name)
         {
@@ -871,8 +906,6 @@ public:
         return false;
     }
 
-    typedef sigc::slot<void> DefaultSlot;
-
     typedef sigc::slot<void,
                        const std::pair<int, const IDebugger::Breakpoint&>&>
         BreakpointSlot;
@@ -1086,6 +1119,12 @@ public:
     virtual sigc::signal<void, const VariableSafePtr, const UString&>&
                                  variable_created_signal () const = 0;
 
+    /// This signal is emitted after IDebugger::delete_variable is
+    /// called and the underlying backend-side variable object has
+    /// been effectively deleted.  Note that when no instance of
+    /// VariableSafePtr has been passed to the
+    /// IDebugger::delete_variable method, the first argument of this
+    /// signal slot is a null pointer to VariableSafePtr.
     virtual sigc::signal<void, const VariableSafePtr, const UString&>&
                                  variable_deleted_signal () const = 0;
 
@@ -1386,13 +1425,68 @@ public:
                                   const ConstVariableSlot &a_slot,
                                   const UString &a_cookie = "") = 0;
 
+    /// If a variable has a backend counterpart (e.g, a variable object
+    /// when using the GDB backend), then this method deletes the
+    /// backend.  You should not use this method because the life cycle
+    /// of variables backend counter parts is automatically
+    /// tied to the life cycle of instances of IDebugger::Variable,
+    /// unless you know what you are doing.
+    ///
+    /// Note that when the backend counter part is deleted, the
+    /// IDebugger::variable_deleted_signal is invoked with the a_var
+    /// variable in argument.
+    ///
+    /// \param a_var the variable which backend counter to delete.
+    ///
+    /// \param a_cookie a string cookie passed to the
+    /// IDebugger::variable_deleted_signal.
     virtual void delete_variable (const VariableSafePtr a_var,
                                   const UString &a_cookie = "") = 0;
 
+    /// If a variable has a backend counterpart (e.g, a variable object
+    /// when using the GDB backend), then this method deletes the
+    /// backend.  You should not use this method because the life cycle
+    /// of variables backend counter parts is automatically
+    /// tied to the life cycle of instances of IDebugger::Variable,
+    /// unless you know what you are doing.
+    ///
+    /// Note that when the backend counter part is deleted, the
+    /// IDebugger::variable_deleted_signal is invoked with the a_var
+    /// variable in argument.
+    ///
+    /// \param a_var the variable which backend counter to delete.
+    ///
+    /// \param a_slot a slot asynchronously called when the backend
+    /// variable oject is deleted.
+    ///
+    /// \param a_cookie a string cookie passed to the
+    /// IDebugger::variable_deleted_signal.
     virtual void delete_variable (const VariableSafePtr a_var,
                                   const ConstVariableSlot&,
                                   const UString &a_cookie = "") = 0;
 
+    /// Deletes a backend variable object (e.g, for GDB, a so called
+    /// variable object) named by a given string.
+    /// You should not use this method because the life cycle
+    /// of variables backend counter parts is automatically
+    /// tied to the life cycle of instances of IDebugger::Variable,
+    /// unless you know what you are doing.
+    ///
+    /// Note that when the backend counter part is deleted, the
+    /// IDebugger::variable_deleted_signal is invoked, with null pointer
+    /// to IDebugger::Variable.
+    ///
+    /// \param a_internal_name the name of the backend variable object
+    /// we want to delete.
+    ///
+    /// \param a_slot a slot that is going to be called
+    /// asynchronuously when the backend object is deleted.
+    ///
+    /// \param a_cookie
+    virtual void delete_variable (const UString &a_internal_name,
+                                  const DefaultSlot &a_slot,
+                                  const UString &a_cookie = "") = 0;
+
     virtual void unfold_variable (VariableSafePtr a_var,
                                   const UString &a_cookie = "") = 0;
     virtual void unfold_variable
diff --git a/src/dbgengine/nmv-varobj-walker.cc b/src/dbgengine/nmv-varobj-walker.cc
index 462b3b5..9fab8f1 100644
--- a/src/dbgengine/nmv-varobj-walker.cc
+++ b/src/dbgengine/nmv-varobj-walker.cc
@@ -73,11 +73,6 @@ public:
     {
     }
 
-    ~VarobjWalker ()
-    {
-        delete_varobj_if_necessary ();
-    }
-
     sigc::signal<void,
                  const IDebugger::VariableSafePtr>
                                     visited_variable_node_signal () const;
@@ -101,8 +96,6 @@ public:
 
     unsigned get_maximum_member_depth () const;
 
-    void delete_varobj_if_necessary ();
-
     void do_walk_variable_real (const IDebugger::VariableSafePtr,
                                 unsigned a_max_depth);
 
@@ -135,8 +128,6 @@ VarobjWalker::connect (IDebuggerSafePtr a_debugger,
     THROW_IF_FAIL (a_debugger);
     THROW_IF_FAIL (!a_var_name.empty ());
 
-    delete_varobj_if_necessary ();
-
     m_debugger = a_debugger;
     m_var_name = a_var_name;
     m_debugger->create_variable
@@ -156,8 +147,6 @@ VarobjWalker::connect (IDebuggerSafePtr a_debugger,
     // The variable must be backed by variable objects.
     THROW_IF_FAIL (!a_var->internal_name ().empty ());
 
-    delete_varobj_if_necessary ();
-
     m_debugger = a_debugger;
     m_variable = a_var;
 }
@@ -213,20 +202,6 @@ VarobjWalker::get_maximum_member_depth () const
 }
 
 void
-VarobjWalker::delete_varobj_if_necessary ()
-{
-    LOG_FUNCTION_SCOPE_NORMAL_DD;
-
-    if (!m_var_name.empty ()
-        && m_variable
-        && m_debugger
-        && m_debugger->is_attached_to_target ()) {
-        m_debugger->delete_variable (m_variable);
-    }
-
-}
-
-void
 VarobjWalker::do_walk_variable_real (const IDebugger::VariableSafePtr a_var,
                                      unsigned a_max_depth)
 {
diff --git a/src/persp/dbgperspective/nmv-local-vars-inspector.cc b/src/persp/dbgperspective/nmv-local-vars-inspector.cc
index 05659c3..e1c222c 100644
--- a/src/persp/dbgperspective/nmv-local-vars-inspector.cc
+++ b/src/persp/dbgperspective/nmv-local-vars-inspector.cc
@@ -329,7 +329,6 @@ public:
                 row_it = tree_store->erase (row_it);
             }
         }
-        delete_vars_backend_peers (local_vars);
         local_vars.clear ();
         local_vars_changed_at_prev_stop.clear ();
     }
@@ -347,7 +346,6 @@ public:
                 row_it = tree_store->erase (*row_it);
             }
         }
-        delete_vars_backend_peers (function_arguments);
         function_arguments.clear ();
         func_args_changed_at_prev_stop.clear ();
     }
@@ -591,19 +589,6 @@ public:
         }
     }
 
-    void
-    delete_vars_backend_peers (IDebugger::VariableList &a_vars)
-    {
-        for (IDebugger::VariableList::const_iterator it = a_vars.begin ();
-             it != a_vars.end ();
-             ++it) {
-            if (!(*it) || (*it)->internal_name ().empty ()) {
-                continue;
-            }
-            debugger->delete_variable (*it);
-        }
-    }
-
     Glib::RefPtr<Gtk::UIManager>
     get_ui_manager ()
     {
diff --git a/src/persp/dbgperspective/nmv-var-inspector.cc b/src/persp/dbgperspective/nmv-var-inspector.cc
index c95ad97..d5f109f 100644
--- a/src/persp/dbgperspective/nmv-var-inspector.cc
+++ b/src/persp/dbgperspective/nmv-var-inspector.cc
@@ -163,20 +163,6 @@ class VarInspector::Priv : public sigc::trackable {
         get_ui_manager ()->insert_action_group (var_inspector_action_group);
     }
 
-    // If the variable we are inspected was created
-    // with a backend counterpart (variable objects for GDB),
-    // instruct the backend to delete its variable counterpart.
-    void
-    delete_variable_if_needed ()
-    {
-        LOG_FUNCTION_SCOPE_NORMAL_DD;
-        if (variable
-            && !variable->internal_name ().empty ()
-            && debugger) {
-            debugger->delete_variable (variable);
-        }
-    }
-
     void
     graphically_set_variable (const IDebugger::VariableSafePtr a_variable,
                               bool a_expand)
@@ -213,7 +199,6 @@ class VarInspector::Priv : public sigc::trackable {
         re_visualize = a_re_visualize;
 
         re_init_tree_view ();
-        delete_variable_if_needed ();
         variable = a_variable;
         if (a_re_visualize) {
             debugger->revisualize_variable (a_variable,
@@ -629,7 +614,6 @@ public:
 
     ~Priv ()
     {
-        delete_variable_if_needed ();
     }
 };//end class VarInspector::Priv
 
@@ -670,7 +654,6 @@ VarInspector::inspect_variable (const UString &a_variable_name,
     if (a_variable_name == "") {return;}
     THROW_IF_FAIL (m_priv);
     m_priv->re_init_tree_view ();
-    m_priv->delete_variable_if_needed ();
     m_priv->create_variable (a_variable_name, a_expand);
 }
 
@@ -701,7 +684,6 @@ VarInspector::clear ()
 {
     THROW_IF_FAIL (m_priv);
     m_priv->re_init_tree_view ();
-    m_priv->delete_variable_if_needed ();
 }
 
 NEMIVER_END_NAMESPACE (nemiver)
diff --git a/tests/fooprog.cc b/tests/fooprog.cc
index 61bdb96..b2a8900 100644
--- a/tests/fooprog.cc
+++ b/tests/fooprog.cc
@@ -92,8 +92,7 @@ func4 (Person &a_person)
 }
 
 int
-main (int a_argc __attribute__((unused)),
-      char *a_argv[] __attribute__((unused)))
+main (int, char **)
 {
     Person person ("Bob", "Barton", 15);
     func1 ();
diff --git a/tests/test-var-path-expr.cc b/tests/test-var-path-expr.cc
index f215c36..34063cf 100644
--- a/tests/test-var-path-expr.cc
+++ b/tests/test-var-path-expr.cc
@@ -11,6 +11,15 @@
 using namespace nemiver;
 using namespace nemiver::common;
 
+typedef std::list<IDebugger::VariableSafePtr> VariablesList;
+
+static int num_variables_created;
+
+// This container holds variables backed up by backend-side
+// variable objects created during this test, so that they stay
+// alive during the life of this test.
+VariablesList variables;
+
 Glib::RefPtr<Glib::MainLoop> loop =
     Glib::MainLoop::create (Glib::MainContext::get_default ());
 
@@ -28,6 +37,23 @@ on_program_finished_signal ()
     loop->quit ();
 }
 
+/// Counts the number of deleted variables.  If it equals the number
+/// of created variables, then exit the event loop, effectively
+/// allowing the test to exit.
+static void
+on_variable_deleted_signal (const IDebugger::VariableSafePtr a_var,
+                            const UString&)
+{
+    BOOST_REQUIRE (!a_var);
+    MESSAGE ("a backend-side variable object got deleted!");
+
+    static int num_variables_deleted;
+    num_variables_deleted++;
+
+    if (num_variables_deleted == num_variables_created)
+        loop->quit ();
+}
+
 static void
 on_breakpoints_set_signal (const std::map<int, IDebugger::Breakpoint> &a_breaks,
                            const UString &a_cookie)
@@ -49,7 +75,11 @@ on_variable_expr_path (const IDebugger::VariableSafePtr a_var)
     MESSAGE ("var expr path: " << a_var->path_expression ());
     BOOST_REQUIRE (a_var->path_expression ()
                    == "((((person).m_first_name)).npos)");
-    loop->quit ();
+
+    // This should help delete all the variables (and their
+    // backend-side variable objects) created during this test, along
+    // with a_var, after this function returns.
+    variables.clear ();
 }
 
 static void
@@ -68,6 +98,8 @@ on_variable_unfolded (const IDebugger::VariableSafePtr a_var,
         a_debugger->query_variable_path_expr (a_var->members ().front (),
                                               &on_variable_expr_path);
     }
+    // Ensure that a_var lives throughout the test.
+    variables.push_back (a_var);
 }
 
 static void
@@ -76,6 +108,9 @@ on_variable_created (const IDebugger::VariableSafePtr a_var,
 {
     MESSAGE ("variable created: " << a_var->name ());
 
+    // Add a_var to the list of live variables, so that it remains
+    // alive during the whole life of the main function.
+    variables.push_back (a_var);
     if (a_var->needs_unfolding ()) {
         MESSAGE ("unfolding variable " << a_var->name ());
         a_debugger->unfold_variable (a_var,
@@ -91,7 +126,7 @@ on_stopped_signal (IDebugger::StopReason a_reason,
                    int /*a_thread_id*/,
                    int /*a_bp_num*/,
                    const UString &/*a_cookie*/,
-                   IDebuggerSafePtr &a_debugger)
+                   IDebuggerSafePtr a_debugger)
 {
     MESSAGE ("stopped at: "
              << a_frame.function_name ()
@@ -104,16 +139,15 @@ on_stopped_signal (IDebugger::StopReason a_reason,
         a_debugger->create_variable ("person",
                                      sigc::bind (&on_variable_created,
                                                  a_debugger));
+        ++num_variables_created;
     } else {
         a_debugger->do_continue ();
     }
 }
 
 NEMIVER_API int
-test_main (int argc, char *argv[])
+test_main (int, char **)
 {
-    if (argc || argv) {/*keep compiler happy*/}
-
     NEMIVER_TRY
 
     Initializer::do_init ();
@@ -133,6 +167,8 @@ test_main (int argc, char *argv[])
     debugger->program_finished_signal ().connect
                                             (&on_program_finished_signal);
 
+    debugger->variable_deleted_signal ().connect (&on_variable_deleted_signal);
+
     debugger->breakpoints_list_signal ().connect
                                             (&on_breakpoints_set_signal);
 
diff --git a/tests/test-var-walker.cc b/tests/test-var-walker.cc
index 11654de..768f3c8 100644
--- a/tests/test-var-walker.cc
+++ b/tests/test-var-walker.cc
@@ -12,14 +12,19 @@
 using namespace nemiver;
 using namespace nemiver::common;
 
+typedef std::list<IDebugger::VariableSafePtr> Variables;
+typedef std::map<std::string, IVarListWalkerSafePtr> VarListWalkerMap;
+typedef std::map<std::string, string> StringMap;
+
+// This is to hold variables which should live throughout the life
+// time of this test.
+Variables variables;
+
 Glib::RefPtr<Glib::MainLoop> s_loop =
     Glib::MainLoop::create (Glib::MainContext::get_default ());
 
 IDebugger::Frame s_current_frame;
 
-typedef std::list<IDebugger::VariableSafePtr> DebuggerVariableList;
-typedef std::map<std::string, IVarListWalkerSafePtr> VarListWalkerMap;
-typedef std::map<std::string, string> StringMap;
 
 VarListWalkerMap&
 var_list_walker ()
@@ -123,15 +128,19 @@ on_stopped_signal (IDebugger::StopReason a_reason,
 void
 on_frames_arguments_listed_signal
         (const map<int, list<IDebugger::VariableSafePtr> > &a_frames_params,
-         const UString &a_cookie)
+         const UString &)
 {
-    if (a_cookie.empty ()) {/*keep compiler happy*/}
     map<int, list<IDebugger::VariableSafePtr> >::const_iterator it;
     it = a_frames_params.find (0);
     if (it == a_frames_params.end ()) {
         LOG_ERROR ("Could not find current frame");
         return;
     }
+    Variables::const_iterator i = it->second.begin ();
+    for (; i != it->second.end (); ++i) {
+        variables.push_back (*i);
+    }
+
     BOOST_REQUIRE (var_list_walker ().find (s_current_frame.function_name ())
                    != var_list_walker ().end ());
     IVarListWalkerSafePtr walker = var_list_walker ()[s_current_frame.function_name ()];
@@ -142,13 +151,17 @@ on_frames_arguments_listed_signal
 }
 
 void
-on_local_variables_listed_signal (const DebuggerVariableList &a_variables,
-                                  const UString &a_cookie)
+on_local_variables_listed_signal (const Variables &a_variables,
+                                  const UString &)
 {
-    if (a_variables.empty () || a_cookie.empty ()) {
-    }
     BOOST_REQUIRE (var_list_walker ().find (s_current_frame.function_name ())
                    != var_list_walker ().end ());
+
+    Variables::const_iterator i = a_variables.begin ();
+    for (; i != a_variables.end (); ++i) {
+        variables.push_back (*i);
+    }
+
     IVarListWalkerSafePtr walker = var_list_walker ()[s_current_frame.function_name ()];
     BOOST_REQUIRE (walker);
     walker->remove_variables ();
@@ -218,7 +231,7 @@ test_main (int argc, char **argv)
     debugger->set_breakpoint ("func3");
 
     var_list_walker ()["main"] = create_var_list_walker (debugger);
-    expected_variables ()["main"] = " person a_argc a_argv";
+    expected_variables ()["main"] = " person";
     var_list_walker ()["func1"] = create_var_list_walker (debugger);
     expected_variables ()["func1"] = " i";
     var_list_walker ()["func2"] = create_var_list_walker (debugger);
@@ -232,6 +245,8 @@ test_main (int argc, char **argv)
     //****************************************
     s_loop->run ();
 
+    variables.clear ();
+
     BOOST_REQUIRE (actual_variables ().find ("main")  != actual_variables ().end ());
     BOOST_REQUIRE (actual_variables ().find ("func1") != actual_variables ().end ());
     BOOST_REQUIRE (actual_variables ().find ("func2") != actual_variables ().end ());
diff --git a/tests/test-vars.cc b/tests/test-vars.cc
index b3f438f..bb1a017 100644
--- a/tests/test-vars.cc
+++ b/tests/test-vars.cc
@@ -12,6 +12,13 @@
 using namespace nemiver;
 using namespace nemiver::common;
 
+typedef  std::list<IDebugger::VariableSafePtr> Variables;
+
+// This is to hold instances of IDebugger::Variable which backend-side
+// variable object has to live throughout the life time of this test.
+static Variables variables;
+static IDebugger::VariableSafePtr person_var;
+
 Glib::RefPtr<Glib::MainLoop> s_loop =
     Glib::MainLoop::create (Glib::MainContext::get_default ());
 static int nb_vars_created;
@@ -19,7 +26,7 @@ static int nb_vars_created2;
 static int nb_vars_deleted;
 static int nb_vars_deleted2;
 static int unfold_requests;
-static IDebugger::VariableSafePtr var_to_delete;
+
 
 void on_variable_deleted_signal (const IDebugger::VariableSafePtr a_var);
 void on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_var,
@@ -61,11 +68,12 @@ on_variable_created_signal (IDebugger::VariableSafePtr a_var)
     a_var->to_string (var_str, true /*show var name*/);
     MESSAGE ("variable created " << var_str);
 
+    variables.push_back (a_var);
     nb_vars_created++;
     if (a_var->name () == "person") {
         // put the variable aside so that we
         // can delete it later
-        var_to_delete = a_var;
+        person_var = a_var;
     }
 }
 
@@ -77,6 +85,7 @@ on_variable_created_signal2 (IDebugger::VariableSafePtr a_var,
     UString var_str;
     a_var->to_string (var_str, true /*show var name*/);
     MESSAGE ("variable created (second report): " << var_str);
+    variables.push_back (a_var);
     nb_vars_created2++;
 
     unfold_requests++;
@@ -166,16 +175,21 @@ on_changed_variables_listed_signal
         MESSAGE ("name: "  + (*it)->internal_name ());
         MESSAGE ("value: " + (*it)->value ());
     }
-    a_debugger->delete_variable (root, &on_variable_deleted_signal);
+    a_debugger->step_over ();
+    person_var.reset ();
 }
 
 void
 on_variable_deleted_signal (const IDebugger::VariableSafePtr a_var)
 {
-    MESSAGE ("variable deleted. Name: "
-             << a_var->name ()
-             << ", Internal Name: "
-             << a_var->internal_name ());
+    if (a_var) {
+        MESSAGE ("variable deleted. Name: "
+                 << a_var->name ()
+                 << ", Internal Name: "
+                 << a_var->internal_name ());
+    } else {
+        MESSAGE ("backend-side variable object deleted");
+    }
     nb_vars_deleted++;
 }
 
@@ -184,10 +198,14 @@ on_variable_deleted_signal2 (const IDebugger::VariableSafePtr a_var,
                              const UString&,
                              IDebuggerSafePtr a_debugger)
 {
-    MESSAGE ("variable deleted. Name: "
-             << a_var->name ()
-             << ", Internal Name: "
-             << a_var->internal_name ());
+    if (a_var) {
+        MESSAGE ("variable deleted. Name: "
+                 << a_var->name ()
+                 << ", Internal Name: "
+                 << a_var->internal_name ());
+    } else {
+        MESSAGE ("backend-side variable object deleted");
+    }
     nb_vars_deleted2++;
     a_debugger->step_over ();
 }
@@ -219,13 +237,13 @@ on_stopped_signal (IDebugger::StopReason /*a_reason*/,
                  sigc::bind (&on_variable_created_in_func4_signal,
                              a_debugger));
         } else if (nb_stops_in_func4 < 5 ) {
-            a_debugger->step_over ("in-func4");
+            a_debugger->step_over ();
         } else {
             a_debugger->do_continue ();
         }
     } else if (nb_stops == 2) {
-        // Okay we stepped once, now we can now create the variable
-        // for the person variable.
+        // Okay we stepped once, we can now create the variable for
+        // the person variable.
         a_debugger->create_variable ("person",
                                      &on_variable_created_signal);
         MESSAGE ("Requested creation of variable 'person'");
@@ -235,7 +253,7 @@ on_stopped_signal (IDebugger::StopReason /*a_reason*/,
         // let's now ask the debugger to tell us which descendant variable
         // was changed exactly.
         a_debugger->list_changed_variables
-                (var_to_delete,
+                (person_var,
                  sigc::bind (&on_changed_variables_listed_signal, a_debugger));
     } else {
         a_debugger->step_over ();
@@ -288,12 +306,13 @@ test_main (int, char **)
     //****************************************
     s_loop->run ();
 
-    NEMIVER_CATCH_AND_RETURN_NOX (-1)
-
+    NEMIVER_CATCH_AND_RETURN_NOX (-1);
+    
     BOOST_REQUIRE (nb_vars_created == nb_vars_created2);
     BOOST_REQUIRE (nb_vars_created > 0);
-    BOOST_REQUIRE (nb_vars_deleted == nb_vars_deleted2);
-    BOOST_REQUIRE (nb_vars_deleted > 0);
+    BOOST_REQUIRE (nb_vars_deleted == 0);
+
+    variables.clear ();
 
     return 0;
 }
-- 
1.7.6.4


-- 
		Dodji


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]