[nemiver/varobjs-support] Add support to list/assign/evaluate/query variable objects



commit 29991a6629cdf2e552cdae9f6f93b3135b1c6403
Author: Dodji Seketeli <dodji redhat com>
Date:   Thu Mar 26 16:37:32 2009 +0100

    Add support to list/assign/evaluate/query variable objects
    
    	* src/dbgengine/nmv-dbg-common.h:
    	(Output::ResultRecord::has_changed_var_list,
    	 Output::ResultRecord::changed_var_list): New entry points.
    	* src/dbgengine/nmv-i-debugger.h:
    	(IDebugger::Variable::parent):  Make this return a proper SafePtr
    	instead of a bare pointer.
    	(IDebugger::Variable::has_parent,
    	IDebugger::Variable::root,
    	IDebugger::Variable::get_descendant,
    	IDebugger::variable_expression_evaluated_signal,
    	IDebugger::changed_variable_signals,
    	IDebugger::assigned_variable_signal,
    	IDebugger::assign_variable,
    	IDebugger::evaluate_variable_expr,
    	IDebugger::list_changed_variables): New entry points.
    	* src/dbgengine/nmv-gdb-engine.h:
    	(GDBEngine::variable_expression_evaluated_signal,
    	GDBEngine::changed_variables_signal,
    	GDBEngine::assigned_variable_signal): Declare new signal getters.
    	(GDBEngine::assign_variable, GDBEngine::assign_variable,
    	GDBEngine::list_changed_variables): Declare new entry points.
    	* src/dbgengine/nmv-gdb-engine.cc:
    	(OnStoppedHandler::do_handle): Add more logging.
    	(OnResultRecordHandler::can_handle,
    	OnResultRecordHandler::do_handle): Handle replies of assign-variable
    	and evaluate-expression commands.
    	(OnListChangedVariableHandler): New output handler for the
    	list-changed-variables command.
    	(GDBEngine::init_output_handlers): Setup the
    	new OnListChangedVariableHandler.
    	(GDBEngine::variable_expression_evaluated_signal,
    	GDBEngine::changed_variables_signal,
    	GDBEngine::assigned_variable_signal): Define new signal getters.
    	(GDBEngine::unfold_variable): Send the proper command to GDB to make
    	it list all the values of each variable node.
    	(GDBEngine::assign_variable,
    	GDBEngine::evaluate_variable_expr,
    	GDBEngine::list_changed_variables): Define new entry points.
    	* src/dbgengine/nmv-gdbmi-parser.h :
    	(GDBMIParser::parse_var_changed_list): Declare new entry point.
    	* src/dbgengine/nmv-gdbmi-parser.cc:
    	(GDBMIParser::parse_var_list_children): Add a comment to describe the
    	function.
    	(GDBMIParser::parse_var_changed_list): Define new entry point to parse
    	the result of GDBEngine::list_changed_variables.
    	(GDBMIParser::parse_result_record): Use the new
    	GDBMIParser::parse_var_changed_list to parse the result record of the
    	call GDBEngine::list_changed_variables.
    	* tests/test-gdbmi.cc: Add tests for parsing the result of
    	IDebugger::list_changed_list.
    	* tests/test-vars.cc: Updated this varobjs test to be able to assign
    	values to a variable, list/update changed children of a given variable
    	object, get the value of a variable object.
    	This entry adds Basically adds support to assign a value to a variable
    	object, list/update changed children of a given variable object and
    	get the value of a variable object.
---
 src/dbgengine/nmv-dbg-common.h    |   24 ++++
 src/dbgengine/nmv-gdb-engine.cc   |  240 ++++++++++++++++++++++++++++++++++++-
 src/dbgengine/nmv-gdb-engine.h    |   32 +++++
 src/dbgengine/nmv-gdbmi-parser.cc |  123 ++++++++++++++++++-
 src/dbgengine/nmv-gdbmi-parser.h  |    5 +-
 src/dbgengine/nmv-i-debugger.h    |   91 ++++++++++++++-
 tests/test-gdbmi.cc               |   11 ++-
 tests/test-vars.cc                |  130 ++++++++++++++++++---
 8 files changed, 629 insertions(+), 27 deletions(-)

diff --git a/src/dbgengine/nmv-dbg-common.h b/src/dbgengine/nmv-dbg-common.h
index 0ae7ec4..92596a0 100644
--- a/src/dbgengine/nmv-dbg-common.h
+++ b/src/dbgengine/nmv-dbg-common.h
@@ -397,6 +397,11 @@ public:
         vector<IDebugger::VariableSafePtr> m_variable_children;
         bool m_has_variable_children;
 
+        // Updated sub variables (direct/indirect children)
+        // of a given variable
+        list<IDebugger::VariableSafePtr> m_changed_var_list;
+        bool m_has_changed_var_list;
+
     public:
         ResultRecord () {clear () ;}
 
@@ -434,6 +439,7 @@ public:
             m_has_variable = false;
             m_nb_variable_deleted = 0;
             m_has_variable_children = false;
+            m_has_changed_var_list = false;
         }
 
         /// \name accessors
@@ -645,6 +651,24 @@ public:
             has_variable_children (true);
         }
 
+        bool has_changed_var_list () const
+        {
+            return m_has_changed_var_list;
+        }
+        void has_changed_var_list (bool a_in)
+        {
+            m_has_changed_var_list = a_in;
+        }
+        const list<IDebugger::VariableSafePtr>& changed_var_list () const
+        {
+            return m_changed_var_list;
+        }
+        void changed_var_list (const list<IDebugger::VariableSafePtr> &a_in)
+        {
+            m_changed_var_list = a_in;
+            has_changed_var_list (true);
+        }
+
         /// @}
 
     };//end class ResultRecord
diff --git a/src/dbgengine/nmv-gdb-engine.cc b/src/dbgengine/nmv-gdb-engine.cc
index 18d8862..76f8466 100644
--- a/src/dbgengine/nmv-gdb-engine.cc
+++ b/src/dbgengine/nmv-gdb-engine.cc
@@ -236,6 +236,15 @@ public:
     mutable sigc::signal<void, const VariableSafePtr, const UString&>
                                                         variable_unfolded_signal;
 
+    mutable sigc::signal<void, const VariableSafePtr, const UString&>
+                                        variable_expression_evaluated_signal;
+
+    mutable sigc::signal<void, const list<VariableSafePtr>&, const UString&>
+                                                changed_variables_signal;
+
+    mutable sigc::signal<void, VariableSafePtr, const UString&>
+                                                assigned_variable_signal;
+
     //***********************
     //</GDBEngine attributes>
     //************************
@@ -1106,7 +1115,9 @@ struct OnStoppedHandler: OutputHandler {
         LOG_FUNCTION_SCOPE_NORMAL_DD;
 
         THROW_IF_FAIL (m_is_stopped && m_engine);
-        if (a_in.has_command ()) {}
+        LOG_DD ("stopped. Command name was: '"
+                << a_in.command ().name () << "' "
+                << "Cookie was '" << a_in.command ().cookie () << "'");
 
         int thread_id = m_out_of_band_record.thread_id ();
         int breakpoint_number = -1;
@@ -1533,6 +1544,8 @@ struct OnResultRecordHandler : OutputHandler {
         m_engine (a_engine)
     {}
 
+    // TODO: split this OutputHandler into several different handlers.
+    // Ideally there should be one handler per command sent to GDB.
     bool can_handle (CommandAndOutput &a_in)
     {
         if ((a_in.command ().name () == "print-variable-value"
@@ -1540,7 +1553,9 @@ struct OnResultRecordHandler : OutputHandler {
              || a_in.command ().name () == "print-pointed-variable-value"
              || a_in.command ().name () == "dereference-variable"
              || a_in.command ().name () == "set-register-value"
-             || a_in.command ().name () == "set-memory")
+             || a_in.command ().name () == "set-memory"
+             || a_in.command ().name () == "assign-variable"
+             || a_in.command ().name () == "evaluate-expression")
             && a_in.output ().has_result_record ()
             && (a_in.output ().result_record ().kind ()
                 == Output::ResultRecord::DONE)
@@ -1598,6 +1613,48 @@ struct OnResultRecordHandler : OutputHandler {
             var->set (*a_in.output ().result_record ().variable_value ());
             m_engine->variable_value_set_signal ().emit
                                             (var, a_in.command ().cookie ());
+        } else if (a_in.command ().name () == "assign-variable") {
+            THROW_IF_FAIL (a_in.command ().variable ());
+            THROW_IF_FAIL (a_in.output ().result_record ().variable_value ());
+
+            // Set the result we got from gdb into the variable
+            // handed to us by the client code.
+            a_in.command ().variable ()->value
+                (a_in.output ().result_record ().variable_value ()->value ());
+
+            // Call the slot associated to IDebugger::assign_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 ());
+            }
+
+            // Tell the world that the variable got assigned
+            m_engine->assigned_variable_signal ().emit
+                (a_in.command ().variable (), a_in.command ().cookie ());
+        } else if (a_in.command ().name () == "evaluate-expression") {
+            THROW_IF_FAIL (a_in.command ().variable ());
+            THROW_IF_FAIL (a_in.output ().result_record ().variable_value ());
+
+            // Set the result we got from gdb into the variable
+            // handed to us by the client code.
+            a_in.command ().variable ()->value
+                (a_in.output ().result_record ().variable_value ()->value ());
+
+            // Call the slot associated to
+            // IDebugger::evaluate_variable_expr(), 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 ());
+            }
+
+            // Tell the world that the expression got evaluated
+            m_engine->variable_expression_evaluated_signal ().emit
+                (a_in.command ().variable (), a_in.command ().cookie ());
         } else if (a_in.command ().name () == "print-pointed-variable-value") {
             LOG_DD ("got print-pointed-variable-value");
             THROW_IF_FAIL (var_name != "");
@@ -2158,6 +2215,66 @@ struct OnUnfoldVariableHandler : public OutputHandler {
     }
 };// End struct OnUnfoldVariableHandler
 
+struct OnListChangedVariableHandler : public OutputHandler {
+    GDBEngine *m_engine;
+
+    OnListChangedVariableHandler (GDBEngine *a_engine) :
+        m_engine (a_engine)
+    {
+    }
+
+    bool can_handle (CommandAndOutput &a_in)
+    {
+        if (a_in.output ().has_result_record ()
+            && a_in.output ().result_record ().kind ()
+                == Output::ResultRecord::DONE
+            && a_in.output ().result_record ().has_changed_var_list ()
+            && a_in.command ().name () == "list-changed-variables") {
+            LOG_DD ("handler selected");
+            return true;
+        }
+        return false;
+    }
+
+    void do_handle (CommandAndOutput &a_in)
+    {
+        THROW_IF_FAIL (a_in.command ().variable ());
+        THROW_IF_FAIL (a_in.output ().result_record ().has_changed_var_list ());
+
+        // We are going to build the list of descendants of
+        // a_in.command ().variable () that got returned as being
+        // changed.
+        list<IDebugger::VariableSafePtr> vars;
+        const list<IDebugger::VariableSafePtr> &changed_vars =
+                    a_in.output ().result_record ().changed_var_list ();
+        list<IDebugger::VariableSafePtr>::const_iterator it;
+        IDebugger::VariableSafePtr v;
+        for (it = changed_vars.begin (); it != changed_vars.end (); ++it) {
+            v = a_in.command ().variable ()->get_descendant
+                                                ((*it)->internal_name ());
+            // Okay, so v now contains the a descendant variable of
+            // a_in.command ().variable () which value got modified.
+            // Let's set the value of v to the new updated value.
+            v->value ((*it)->value ());
+            if (v) {
+                vars.push_back (v);
+            }
+        }
+        // Call the slot associated to
+        // IDebugger::list_changed_variables(), if any.
+        if (a_in.command ().has_slot ()) {
+            typedef sigc::slot<void,
+                               const list<IDebugger::VariableSafePtr>&>
+                                                                SlotType;
+            SlotType slot = a_in.command ().get_slot<SlotType> ();
+            slot (vars);
+        }
+        // Tell the world about the descendant variables that changed.
+        m_engine->changed_variables_signal ().emit
+                            (vars, a_in.command ().cookie ());
+    }
+};//end OnListChangedVariableHandler
+
 //****************************
 //</GDBengine output handlers
 //***************************
@@ -2438,6 +2555,8 @@ GDBEngine::init_output_handlers ()
             (OutputHandlerSafePtr (new OnDeleteVariableHandler (this)));
     m_priv->output_handler_list.add
             (OutputHandlerSafePtr (new OnUnfoldVariableHandler (this)));
+    m_priv->output_handler_list.add
+            (OutputHandlerSafePtr (new OnListChangedVariableHandler (this)));
 }
 
 sigc::signal<void, Output&>&
@@ -2707,6 +2826,24 @@ GDBEngine::variable_unfolded_signal () const
     return m_priv->variable_unfolded_signal;
 }
 
+sigc::signal<void, const IDebugger::VariableSafePtr, const UString&>&
+GDBEngine::variable_expression_evaluated_signal () const
+{
+    return m_priv->variable_expression_evaluated_signal;
+}
+
+sigc::signal<void, const list<IDebugger::VariableSafePtr>&, const UString&>&
+GDBEngine::changed_variables_signal () const
+{
+    return m_priv->changed_variables_signal;
+}
+
+sigc::signal<void, IDebugger::VariableSafePtr, const UString&>&
+GDBEngine::assigned_variable_signal () const
+{
+    return m_priv->assigned_variable_signal;
+}
+
 //******************
 //<signal handlers>
 //******************
@@ -3882,7 +4019,8 @@ GDBEngine::unfold_variable (const VariableSafePtr a_var,
     THROW_IF_FAIL (!a_var->internal_name ().empty ());
 
     Command command ("unfold-variable",
-                     "-var-list-children" + a_var->internal_name (),
+                     "-var-list-children --all-values "
+                         + a_var->internal_name (),
                      a_cookie);
     command.variable (a_var);
 
@@ -3911,6 +4049,102 @@ GDBEngine::unfold_variable (const VariableSafePtr a_var,
     queue_command (command);
 }
 
+void
+GDBEngine::assign_variable (const VariableSafePtr a_var,
+                            const UString &a_expression,
+                            const UString &a_cookie)
+{
+    THROW_IF_FAIL (a_var);
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+    THROW_IF_FAIL (!a_expression.empty ());
+
+    Command command ("assign-variable",
+                     "-var-assign "
+                      + a_var->internal_name () + " " + a_expression,
+                     a_cookie);
+    command.variable (a_var);
+    queue_command (command);
+
+}
+
+void
+GDBEngine::assign_variable
+                    (const VariableSafePtr a_var,
+                     const UString &a_expression,
+                     const sigc::slot<void, const VariableSafePtr>& a_slot)
+{
+    THROW_IF_FAIL (a_var);
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+    THROW_IF_FAIL (!a_expression.empty ());
+
+    Command command ("assign-variable",
+                     "-var-assign "
+                         + a_var->internal_name () + " " + a_expression);
+    command.variable (a_var);
+    command.set_slot (a_slot);
+    queue_command (command);
+}
+
+void
+GDBEngine::evaluate_variable_expr (const VariableSafePtr a_var,
+                                   const UString &a_cookie)
+{
+    THROW_IF_FAIL (a_var);
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+    Command command ("evaluate-expression",
+                     "-var-evaluate-expression " + a_var->internal_name (),
+                     a_cookie);
+    command.variable (a_var);
+    queue_command (command);
+}
+
+void
+GDBEngine::evaluate_variable_expr
+                        (const VariableSafePtr a_var,
+                         const sigc::slot<void, const VariableSafePtr> &a_slot)
+{
+    THROW_IF_FAIL (a_var);
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+    Command command ("evaluate-expression",
+                     "-var-evaluate-expression " + a_var->internal_name ());
+    command.variable (a_var);
+    command.set_slot (a_slot);
+    queue_command (command);
+}
+
+void
+GDBEngine::list_changed_variables (VariableSafePtr a_var,
+                                   const UString &a_cookie)
+{
+    THROW_IF_FAIL (a_var);
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+    Command command ("list-changed-variables",
+                     "-var-update --all-values "
+                         + a_var->internal_name (),
+                     a_cookie);
+    command.variable (a_var);
+    queue_command (command);
+}
+
+void
+GDBEngine::list_changed_variables
+                (VariableSafePtr a_var,
+                 const sigc::slot<void, const list<VariableSafePtr> > &a_slot)
+{
+    THROW_IF_FAIL (a_var);
+    THROW_IF_FAIL (!a_var->internal_name ().empty ());
+
+    Command command ("list-changed-variables",
+                     "-var-update --all-values "
+                         + a_var->internal_name ());
+    command.variable (a_var);
+    command.set_slot (a_slot);
+    queue_command (command);
+}
+
 //****************************
 //</GDBEngine methods>
 //****************************
diff --git a/src/dbgengine/nmv-gdb-engine.h b/src/dbgengine/nmv-gdb-engine.h
index aaeda62..9c2dd46 100644
--- a/src/dbgengine/nmv-gdb-engine.h
+++ b/src/dbgengine/nmv-gdb-engine.h
@@ -182,6 +182,16 @@ public:
 
     sigc::signal<void, const VariableSafePtr, const UString&>&
                                                 variable_unfolded_signal () const;
+
+    sigc::signal<void, const VariableSafePtr, const UString&>&
+                variable_expression_evaluated_signal () const;
+
+    sigc::signal<void, const list<VariableSafePtr>&, const UString&>&
+                changed_variables_signal () const;
+
+    sigc::signal<void, VariableSafePtr, const UString&>&
+                assigned_variable_signal () const;
+
     //*************
     //</signals>
     //*************
@@ -408,6 +418,28 @@ public:
                           const UString &a_cookie);
     void unfold_variable (VariableSafePtr a_var,
                           const sigc::slot<void, const VariableSafePtr> &);
+
+    void assign_variable (const VariableSafePtr a_var,
+                          const UString &a_expression,
+                          const UString &a_cookie);
+
+    void assign_variable
+                        (const VariableSafePtr a_var,
+                         const UString &a_expression,
+                         const sigc::slot<void, const VariableSafePtr>& a_slot);
+
+    void evaluate_variable_expr (const VariableSafePtr a_var,
+                                 const UString &a_cookie);
+    void evaluate_variable_expr
+            (const VariableSafePtr a_var,
+             const sigc::slot<void, const VariableSafePtr> &a_slot);
+
+    void list_changed_variables (VariableSafePtr a_root,
+                                 const UString &a_cookie);
+
+    void list_changed_variables
+                (VariableSafePtr a_root,
+                 const sigc::slot<void, const list<VariableSafePtr> > &a_slot);
 };//end class GDBEngine
 
 NEMIVER_END_NAMESPACE (nemiver)
diff --git a/src/dbgengine/nmv-gdbmi-parser.cc b/src/dbgengine/nmv-gdbmi-parser.cc
index 83404dc..704e76a 100644
--- a/src/dbgengine/nmv-gdbmi-parser.cc
+++ b/src/dbgengine/nmv-gdbmi-parser.cc
@@ -144,8 +144,10 @@ const char* PREFIX_STOPPED_ASYNC_OUTPUT = "*stopped,";
 const char* PREFIX_NAME = "name=\"";
 const char* PREFIX_VARIABLE_DELETED = "ndeleted=\"";
 const char* NDELETED = "ndeleted";
-const char* PREFIX_NUMCHILD= "numchild=\"";
-const char* NUMCHILD= "numchild";
+const char* PREFIX_NUMCHILD = "numchild=\"";
+const char* NUMCHILD = "numchild";
+const char* PREFIX_VARIABLES_CHANGED_LIST = "changelist=[";
+const char* CHANGELIST = "changelist";
 
 bool parse_c_string_body (const UString &a_input,
                           UString::size_type a_from,
@@ -4837,6 +4839,15 @@ fetch_gdbmi_result:
                 } else {
                     LOG_PARSING_ERROR2 (cur);
                 }
+            } else if (!RAW_INPUT.compare (cur,
+                                           strlen (PREFIX_VARIABLES_CHANGED_LIST),
+                                           PREFIX_VARIABLES_CHANGED_LIST)) {
+                list<IDebugger::VariableSafePtr> vars;
+                if (parse_var_changed_list (cur, cur, vars)) {
+                    result_record.changed_var_list (vars);
+                } else {
+                    LOG_PARSING_ERROR2 (cur);
+                }
             } else {
                 GDBMIResultSafePtr result;
                 if (!parse_gdbmi_result (cur, cur, result)
@@ -6074,6 +6085,10 @@ GDBMIParser::parse_variables_deleted (UString::size_type a_from,
     return true;
 }
 
+// We want to parse something like:
+// 'numchild=N,children=[{name=NAME,numchild=N,type=TYPE}]'
+// It's actually two RESULTs, separated by a comma. The second
+// result is a LIST of TUPLEs. Each TUPLE represents a children variable.
 bool
 GDBMIParser::parse_var_list_children
                             (UString::size_type a_from,
@@ -6084,7 +6099,6 @@ GDBMIParser::parse_var_list_children
     UString::size_type cur = a_from;
     CHECK_END2 (cur);
 
-
     if (RAW_INPUT.compare (cur, strlen (PREFIX_NUMCHILD),
                            PREFIX_NUMCHILD)) {
         LOG_PARSING_ERROR2 (cur);
@@ -6247,6 +6261,109 @@ GDBMIParser::parse_var_list_children
     return true;
 }
 
+// We want to parse something that likes:
+// 'changelist=[{name="var1",value="3",in_scope="true",type_changed="false"}]'
+// It's a RESULT, which value is a LIST of TUPLEs.
+// Each TUPLE represents a child variable.
+bool
+GDBMIParser::parse_var_changed_list (UString::size_type a_from,
+                                     UString::size_type &a_to,
+                                     list<IDebugger::VariableSafePtr> &a_vars)
+{
+    LOG_FUNCTION_SCOPE_NORMAL_D (GDBMI_PARSING_DOMAIN);
+    UString::size_type cur = a_from;
+    CHECK_END2 (cur);
+
+    if (RAW_INPUT.compare (cur, strlen (PREFIX_VARIABLES_CHANGED_LIST),
+                           PREFIX_VARIABLES_CHANGED_LIST)) {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+    GDBMIResultSafePtr result;
+    if (!parse_gdbmi_result (cur, cur, result) || !result) {
+        LOG_PARSING_ERROR2 (cur);
+        return false;
+    }
+    // The name of RESULT must be CHANGELIST
+    if (result->variable () != CHANGELIST) {
+        LOG_ERROR ("expected gdbmi variable " << CHANGELIST << ", got: "
+                   << result->variable () << "\'");
+        return false;
+    }
+    // The value of the RESULT must be a LIST
+    if (!result->value ()
+        || result->value ()->content_type () != GDBMIValue::LIST_TYPE
+        || !result->value ()->get_list_content ()) {
+        LOG_ERROR ("expected a LIST value for the GDBMI variable "
+                   << CHANGELIST);
+        return false;
+    }
+
+    if (result->value ()->get_list_content ()->content_type ()
+            != GDBMIList::VALUE_TYPE) {
+        LOG_ERROR ("expected a TUPLE content in the LIST value of "
+                   << CHANGELIST);
+        return false;
+    }
+    list<GDBMIValueSafePtr> values;
+    result->value ()->get_list_content ()->get_value_content (values);
+    if (values.empty ()) {
+        LOG_ERROR ("expected a non empty TUPLE content in the LIST value of "
+                   << CHANGELIST);
+        return false;
+    }
+    for (list<GDBMIValueSafePtr>::const_iterator value_it = values.begin ();
+         value_it != values.end ();
+         ++value_it) {
+        if (!(*value_it)) {
+            continue;
+        }
+        if ((*value_it)->content_type () != GDBMIValue::TUPLE_TYPE
+            || !(*value_it)->get_tuple_content ()) {
+            LOG_ERROR ("expected a non empty TUPLE content in the LIST value of "
+                       << CHANGELIST);
+            return false;
+        }
+        // the components of a given child variable
+        list<GDBMIResultSafePtr> comps =
+                (*value_it)->get_tuple_content ()->content ();
+        UString n, v, internal_name, value;
+        bool in_scope = false, type_changed = false;
+        IDebugger::VariableSafePtr var;
+        // Walk the list of components of the child variable and really
+        // build the damn variable
+        for (list<GDBMIResultSafePtr>::const_iterator it = comps.begin ();
+             it != comps.end ();
+             ++it) {
+            if (!(*it)
+                || (*it)->value ()->content_type () != GDBMIValue::STRING_TYPE) {
+                continue;
+            }
+            n = (*it)->variable ();
+            v = (*it)->value ()->get_string_content ();
+            if (n == "name") {
+                internal_name = v;
+            } else if (n == "value") {
+                value = v;
+            } else if (n == "in_scope") {
+                in_scope = (v == "true");
+            } else if (n == "type_changed") {
+                type_changed = (v == "true");
+            }
+        }
+        if (!internal_name.empty ()) {
+            var = IDebugger::VariableSafePtr
+                    (new IDebugger::Variable (internal_name,
+                                              "" /* name */,
+                                              value,
+                                              "" /* type */));
+            a_vars.push_back (var);
+        }
+    }
+    a_to = cur;
+    return true;
+}
+
 bool
 GDBMIParser::parse_register_names (UString::size_type a_from,
                                    UString::size_type &a_to,
diff --git a/src/dbgengine/nmv-gdbmi-parser.h b/src/dbgengine/nmv-gdbmi-parser.h
index 01afbe6..e8c817f 100644
--- a/src/dbgengine/nmv-gdbmi-parser.h
+++ b/src/dbgengine/nmv-gdbmi-parser.h
@@ -795,11 +795,14 @@ public:
                                   UString::size_type &a_to,
                                   vector<IDebugger::VariableSafePtr> &a_vars);
 
+    bool parse_var_changed_list (UString::size_type a_from,
+                                 UString::size_type &a_to,
+                                 list<IDebugger::VariableSafePtr> &a_vars);
+
     bool parse_result_record (UString::size_type a_from,
                               UString::size_type &a_to,
                               Output::ResultRecord &a_record);
 
-
     bool parse_output_record (UString::size_type a_from,
                               UString::size_type &a_to,
                               Output &a_output);
diff --git a/src/dbgengine/nmv-i-debugger.h b/src/dbgengine/nmv-i-debugger.h
index ff56f93..a3bd89c 100644
--- a/src/dbgengine/nmv-i-debugger.h
+++ b/src/dbgengine/nmv-i-debugger.h
@@ -357,12 +357,41 @@ public:
         void type (const UString &a_type) {m_type = a_type;}
         void type (const string &a_type) {m_type = a_type;}
 
-        Variable* parent () const {return m_parent;}
+        /// Return true if this instance of Variable has a parent variable,
+        /// false otherwise.
+        bool has_parent () const
+        {
+            return m_parent != 0;
+        }
+
+        /// A getter of the parent Variable of the current instance.
+        const VariableSafePtr parent () const
+        {
+            VariableSafePtr parent (m_parent, true/*add a reference*/);
+            return parent;
+        }
+
+        /// Parent variable setter.
+        /// We don't increase the ref count of the
+        /// parent variable to avoid memory leaks due to refcounting cyles.
+        /// The parent already holds a reference on this instance of
+        /// Variable so we really should not hold a reference on it as
+        /// well.
         void parent (Variable *a_parent)
         {
             m_parent = a_parent;
         }
 
+        /// \return the root parent of this variable.
+        /// Beware what is returned here is a bare pointer.
+        const VariableSafePtr root () const
+        {
+            if (!has_parent ()) {
+                return VariableSafePtr (this, true /*increase refcount*/);
+            }
+            return parent ()->root ();
+        }
+
         void to_string (UString &a_str,
                         bool a_show_var_name = false,
                         const UString &a_indent_str="") const
@@ -522,6 +551,31 @@ public:
         {
             return m_num_expected_children != 0;
         }
+
+        /// Return the descendant of the current instance of Variable.
+        /// \param a_internal_path the internal fully qualified path of the
+        ///        descendant variable.
+        const VariableSafePtr get_descendant
+                                (const UString &a_internal_path) const
+        {
+            VariableSafePtr result;
+            if (internal_name () == a_internal_path) {
+                result.reset (this, true /*take refcount*/);
+                return result;
+            }
+            for (list<VariableSafePtr>::const_iterator it = m_members.begin ();
+                 it != m_members.end ();
+                 ++it) {
+                if (*it && (*it)->internal_name () == a_internal_path) {
+                    return *it;
+                }
+                result = (*it)->get_descendant (a_internal_path);
+                if (result) {
+                    return result;
+                }
+            }
+            return result;
+        }
     };//end class Variable
 
     enum State {
@@ -744,6 +798,15 @@ public:
 
     virtual sigc::signal<void, const VariableSafePtr, const UString&>&
                                         variable_unfolded_signal () const = 0;
+
+    virtual sigc::signal<void, const VariableSafePtr, const UString&>&
+                variable_expression_evaluated_signal () const = 0;
+
+    virtual sigc::signal<void, const list<VariableSafePtr>&, const UString&>&
+                changed_variables_signal () const  = 0;
+
+    virtual sigc::signal<void, VariableSafePtr, const UString&>&
+                assigned_variable_signal () const = 0;
     /// @}
 
     virtual void do_init (IConfMgrSafePtr &a_conf_mgr) = 0;
@@ -920,8 +983,30 @@ public:
                                                    const VariableSafePtr>&) = 0;
     virtual void unfold_variable (VariableSafePtr a_var,
                                   const UString &a_cookie) = 0;
-    virtual void unfold_variable (VariableSafePtr a_var,
-                                  const sigc::slot<void, const VariableSafePtr> &) = 0;
+    virtual void unfold_variable
+                (VariableSafePtr a_var,
+                 const sigc::slot<void, const VariableSafePtr> &) = 0;
+
+    virtual void assign_variable (const VariableSafePtr a_var,
+                                  const UString &a_expression,
+                                  const UString &a_cookie) = 0;
+
+    virtual void assign_variable
+                    (const VariableSafePtr a_var,
+                     const UString &a_expression,
+                     const sigc::slot<void, const VariableSafePtr>& a_slot) = 0;
+
+    virtual void evaluate_variable_expr (const VariableSafePtr a_var,
+                                         const UString &a_cookie) = 0;
+    virtual void evaluate_variable_expr
+            (const VariableSafePtr a_var,
+             const sigc::slot<void, const VariableSafePtr> &a_slot)= 0;
+
+    virtual void list_changed_variables (VariableSafePtr a_root,
+                                         const UString &a_cookie) = 0;
+    virtual void list_changed_variables
+            (VariableSafePtr a_root,
+             const sigc::slot<void, const list<VariableSafePtr> > &a_slot) = 0;
 };//end IDebugger
 
 NEMIVER_END_NAMESPACE (nemiver)
diff --git a/tests/test-gdbmi.cc b/tests/test-gdbmi.cc
index ce2c3cb..e0ca024 100644
--- a/tests/test-gdbmi.cc
+++ b/tests/test-gdbmi.cc
@@ -54,6 +54,8 @@ static const char *gv_output_record5="^done,ndeleted=\"2\"\n";
 
 static const char *gv_output_record6="^done,numchild=\"3\",children=[child={name=\"var2.public.m_first_name\",exp=\"m_first_name\",numchild=\"2\",type=\"string\"},child={name=\"var2.public.m_family_name\",exp=\"m_family_name\",numchild=\"2\",type=\"string\"},child={name=\"var2.public.m_age\",exp=\"m_age\",numchild=\"0\",type=\"unsigned int\"}]\n";
 
+static const char *gv_output_record7="^done,changelist=[{name=\"var1.public.m_first_name.public.npos\",value=\"1\",in_scope=\"true\",type_changed=\"false\"}]\n";
+
 //the partial result of a gdbmi command: -stack-list-argument 1 command
 //this command is used to implement IDebugger::list_frames_arguments()
 static const char* gv_stack_arguments0 =
@@ -291,13 +293,13 @@ test_output_record ()
                    == "var1");
     BOOST_REQUIRE (output.result_record ().variable ()->type () == "Person");
 
-    // gv_output_record4 should result in 2 deleted variables.
+    // gv_output_record5 should result in 2 deleted variables.
     parser.push_input (gv_output_record5);
     is_ok = parser.parse_output_record (0, to, output);
     BOOST_REQUIRE (is_ok);
     BOOST_REQUIRE (output.result_record ().number_of_variables_deleted () == 2);
 
-    // gv_output_record5 should result in 3 children variables.
+    // gv_output_record6 should result in 3 children variables.
     parser.push_input (gv_output_record6);
     is_ok = parser.parse_output_record (0, to, output);
     BOOST_REQUIRE (is_ok);
@@ -323,6 +325,11 @@ test_output_record ()
                    && v[i]->name () == "m_age"
                    && v[i]->type () == "unsigned int"
                    && v[i]->internal_name () == "var2.public.m_age");
+    // gv_output_record7 should result in 1 variable.
+    parser.push_input (gv_output_record7);
+    is_ok = parser.parse_output_record (0, to, output);
+    BOOST_REQUIRE (is_ok);
+    BOOST_REQUIRE (output.result_record ().has_changed_var_list ());
 }
 
 void
diff --git a/tests/test-vars.cc b/tests/test-vars.cc
index c431d71..e71cb3b 100644
--- a/tests/test-vars.cc
+++ b/tests/test-vars.cc
@@ -22,6 +22,13 @@ 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,
                                   IDebuggerSafePtr a_debugger);
+void on_changed_variables_listed_signal
+                        (const list<IDebugger::VariableSafePtr> &a_vars,
+                         IDebuggerSafePtr a_debugger);
+void on_assign_variable_signal (const IDebugger::VariableSafePtr a_var,
+                                IDebuggerSafePtr a_debugger);
+void on_evaluate_variable_expr_signal (const IDebugger::VariableSafePtr a_var,
+                                       IDebuggerSafePtr a_debugger);
 
 void
 on_command_done_signal (const UString&, const UString&)
@@ -48,7 +55,10 @@ on_running_signal ()
 void
 on_variable_created_signal (IDebugger::VariableSafePtr a_var)
 {
-    MESSAGE ("variable created: " << a_var->name ());
+    UString var_str;
+    a_var->to_string (var_str, true /*show var name*/);
+    MESSAGE ("variable created " << var_str);
+
     nb_vars_created++;
     if (a_var->name () == "person") {
         // put the variable aside so that we
@@ -64,7 +74,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: " << var_str);
+    MESSAGE ("variable created (second report): " << var_str);
     nb_vars_created2++;
 
     unfold_requests++;
@@ -74,6 +84,45 @@ on_variable_created_signal2 (IDebugger::VariableSafePtr a_var,
 }
 
 void
+on_variable_created_in_func4_signal (IDebugger::VariableSafePtr a_var,
+                                     IDebuggerSafePtr a_debugger)
+{
+    BOOST_REQUIRE (a_var->name () == "i");
+    MESSAGE ("variable " << a_var->name () << " created");
+    nb_vars_created++;
+    a_debugger->assign_variable (a_var, "3456",
+                                 sigc::bind (&on_assign_variable_signal,
+                                             a_debugger));
+}
+
+void
+on_assign_variable_signal (const IDebugger::VariableSafePtr a_var,
+                           IDebuggerSafePtr a_debugger)
+{
+    BOOST_REQUIRE (a_var);
+    BOOST_REQUIRE (a_var->name () == "i");
+    BOOST_REQUIRE (a_var->value () == "3456");
+    MESSAGE (a_var->name () << " assigned to: " + a_var->value ());
+
+    a_debugger->evaluate_variable_expr
+                            (a_var,
+                             sigc::bind (&on_evaluate_variable_expr_signal,
+                                         a_debugger));
+}
+
+void
+on_evaluate_variable_expr_signal (const IDebugger::VariableSafePtr a_var,
+                                  IDebuggerSafePtr a_debugger)
+{
+    BOOST_REQUIRE (a_var);
+    BOOST_REQUIRE (a_var->name () == "i");
+    BOOST_REQUIRE (a_var->value () == "3456");
+    MESSAGE (a_var->name () <<  " evaluated to: " + a_var->value ());
+
+    a_debugger->do_continue ();
+}
+
+void
 on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_var,
                              IDebuggerSafePtr a_debugger)
 {
@@ -92,9 +141,27 @@ on_variable_unfolded_signal (const IDebugger::VariableSafePtr a_var,
                                                      a_debugger));
         }
     }
-    if (unfold_requests == 0 && var_to_delete)
-        a_debugger->delete_variable (var_to_delete,
-                                     &on_variable_deleted_signal);
+    if (unfold_requests == 0)
+        a_debugger->step_over ();
+}
+
+void
+on_changed_variables_listed_signal
+                        (const list<IDebugger::VariableSafePtr> &a_vars,
+                         IDebuggerSafePtr a_debugger)
+{
+    IDebugger::VariableSafePtr root = a_vars.front ()->root ();
+    THROW_IF_FAIL (root);
+    int nb_elems = a_vars.size ();
+    BOOST_REQUIRE (nb_elems == 3);
+    MESSAGE ("The changed members are: ");
+    for (list<IDebugger::VariableSafePtr>::const_iterator it = a_vars.begin ();
+         it != a_vars.end ();
+         ++it) {
+        MESSAGE ("name: "  + (*it)->internal_name ());
+        MESSAGE ("value: " + (*it)->value ());
+    }
+    a_debugger->delete_variable (root, &on_variable_deleted_signal);
 }
 
 void
@@ -109,37 +176,65 @@ on_variable_deleted_signal (const IDebugger::VariableSafePtr a_var)
 
 void
 on_variable_deleted_signal2 (const IDebugger::VariableSafePtr a_var,
-                             const UString&)
+                             const UString&,
+                             IDebuggerSafePtr a_debugger)
 {
     MESSAGE ("variable deleted. Name: "
              << a_var->name ()
              << ", Internal Name: "
              << a_var->internal_name ());
     nb_vars_deleted2++;
+    a_debugger->step_over ();
 }
 
 void
 on_stopped_signal (IDebugger::StopReason /*a_reason*/,
-                   bool,
-                   const IDebugger::Frame&,
+                   bool a_has_frame,
+                   const IDebugger::Frame &a_frame,
                    int,
                    int,
-                   const UString&,
-                   IDebuggerSafePtr &debugger)
+                   const UString &a_cookie,
+                   IDebuggerSafePtr &a_debugger)
 {
     static int nb_stops = 0;
 
     nb_stops++;
     MESSAGE ("stopped " << nb_stops << " times");
+    if (a_has_frame) {
+        MESSAGE ("frame name: " << a_frame.function_name ());
+    }
 
-    if (nb_stops == 2) {
+    if (a_has_frame && a_frame.function_name () == "func4") {
+        static int nb_stops_in_func4;
+        nb_stops_in_func4++;
+        MESSAGE ("stopped in func4 " << nb_stops_in_func4 << " times");
+        if (nb_stops_in_func4 == 3) {
+            a_debugger->create_variable
+                ("i",
+                 sigc::bind (&on_variable_created_in_func4_signal,
+                             a_debugger));
+        } else if (nb_stops_in_func4 < 5 ) {
+            a_debugger->step_over ("in-func4");
+        } 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.
-        debugger->create_variable ("person",
-                                   &on_variable_created_signal);
+        a_debugger->create_variable ("person",
+                                     &on_variable_created_signal);
         MESSAGE ("Requested creation of variable 'person'");
+    } else if (nb_stops == 6) {
+        // We passed the point where we changed the value of the members
+        // of the 'person' variale, in fooprog.
+        // let's now ask the debugger to tell us which descendant variable
+        // was changed exactly.
+        a_debugger->list_changed_variables
+                (var_to_delete,
+                 sigc::bind (&on_changed_variables_listed_signal, a_debugger));
+    } else {
+        a_debugger->step_over ();
     }
-    debugger->step_over ();
 }
 
 NEMIVER_API int
@@ -170,12 +265,17 @@ test_main (int argc __attribute__((unused)),
     debugger->variable_created_signal ().connect (sigc::bind
                                               (&on_variable_created_signal2,
                                                debugger));
-    debugger->variable_deleted_signal ().connect (&on_variable_deleted_signal2);
+    debugger->variable_deleted_signal ().connect
+                (sigc::bind (&on_variable_deleted_signal2, debugger));
     //*******************************
     //</connect to IDebugger events>
     //******************************
     debugger->load_program ("fooprog", ".");
     debugger->set_breakpoint ("main");
+    debugger->set_breakpoint ("func4");
+    // Set a breakpoint right after the person variable members get
+    // modified
+    debugger->set_breakpoint ("fooprog.cc", 98);
     debugger->run ();
 
     //****************************************



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