Re: set print object on should affect MI varobjs (PR mi/13393)

xgsa
2012-04-03T13:27:25Z

[-- Attachment #1: Type: text/plain, Size: 967 bytes --]

Hi,
> Hi. ChangeLog nit:
> ...
Thanks, the similar case with update_dynamic_varobj_children is fixed too.

The same patch and updated changelog are attached.

P.S. It seems the question with regression testing is left unanswered.
Could someone clarify how it should be done or point me where I could
read about it?

The original patch description:

That patch makes "set print object" option affect MI interface. Here is
an example:

struct Base {
Base() : a(1) {}
virtual ~Base() {} // Enforce type to have RTTI
int a;
};

struct Derived : public Base {
Derived() : b(2) {}
int b;
};

int main() {
Derived b;
Base* aPtr = &b;
return 0; // [1]
}

Start gdb in MI mode and run to line [1]. Make -var-create for aPtr. If
"set print object" is "on" you will see the type Derived* for the
created varobj.

See also more details here:
http://sourceware.org/bugzilla/show_bug.cgi?id=13393

Thanks,
Anton.

[-- Attachment #2: gdb_varobj_rtti4.patch --]
[-- Type: text/x-diff, Size: 35321 bytes --]

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 56bf5d5..1b951df 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -8499,7 +8499,8 @@ When displaying a pointer to an object, identify the @emph{actual}
the virtual function table. Note that the virtual function table is
required---this feature can only work for objects that have run-time
type identification; a single virtual method in the object's declared
-type is sufficient.
+type is sufficient. Note that this setting is also taken into account when
+working with variable objects via MI (@pxref{GDB/MI}).

@item set print object off
Display only the declared type of objects, without reference to the
@@ -28861,7 +28862,10 @@ will not be interesting.

@item type
The varobj's type. This is a string representation of the type, as
-would be printed by the @value{GDBN} CLI.
+would be printed by the @value{GDBN} CLI. If @samp{print object}
+(@pxref{Print Settings, set print object}) is set to @code{on}, the
+@emph{actual} (derived) type of the object is shown rather than the
+@emph{declared} one.

@item thread-id
If a variable object is bound to a specific thread, then this is the
@@ -29032,7 +29036,10 @@ Number of children this child has. For a dynamic varobj, this will be
0.

@item type
-The type of the child.
+The type of the child. If @samp{print object}
+(@pxref{Print Settings, set print object}) is set to @code{on}, the
+@emph{actual} (derived) type of the object is shown rather than the
+@emph{declared} one.

@item value
If values were requested, this is the value.
diff --git a/gdb/testsuite/gdb.mi/mi-var-rtti.cc b/gdb/testsuite/gdb.mi/mi-var-rtti.cc
new file mode 100644
index 0000000..d05f660
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-var-rtti.cc
@@ -0,0 +1,360 @@
+/* Copyright 2012 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+struct Base {
+ Base() : A(1) {}
+ virtual ~Base() {} // Enforce type to have vtable
+ int A;
+};
+
+struct Derived : public Base {
+ Derived() : B(2), C(3) {}
+ int B;
+ int C;
+};
+
+
+void use_rtti_for_ptr_test ()
+{
+ /*: BEGIN: use_rtti_for_ptr :*/
+ Derived d;
+ Base* ptr = &d;
+ const Base* constPtr = &d;
+ Base* const ptrConst = &d;
+ Base const* const constPtrConst = &d;
+ /*:
+ set testname use_rtti_for_ptr
+ set_print_object off $testname
+ check_new_derived_without_rtti ptr {Base \*} $testname
+ check_new_derived_without_rtti constPtr {const Base \*} $testname
+ check_new_derived_without_rtti ptrConst {Base \* const} $testname
+ check_new_derived_without_rtti constPtrConst {const Base \* const} \
+ $testname
+
+ set_print_object on $testname
+ check_new_derived_with_rtti ptr {Derived \*} $testname
+ check_new_derived_with_rtti constPtr {const Derived \*} $testname
+ check_new_derived_with_rtti ptrConst {Derived \* const} $testname
+ check_new_derived_with_rtti constPtrConst {const Derived \* const} \
+ $testname
+ :*/
+ return;
+ /*: END: use_rtti_for_ptr :*/
+}
+
+
+void use_rtti_for_ref_test ()
+{
+ /*: BEGIN: use_rtti_for_ref :*/
+ Derived d;
+ Base& ref = d;
+ const Base& constRef = d;
+ /*:
+ set testname use_rtti_for_ref
+ set_print_object off $testname
+ check_new_derived_without_rtti ref {Base \&} $testname
+ check_new_derived_without_rtti constRef {const Base \&} $testname
+
+ set_print_object on $testname
+ check_new_derived_with_rtti ref {Derived \&} $testname
+ check_new_derived_with_rtti constRef {const Derived \&} $testname
+ :*/
+ return;
+ /*: END: use_rtti_for_ref :*/
+}
+
+
+void use_rtti_for_ptr_child_test ()
+{
+ /*: BEGIN: use_rtti_for_ptr_child :*/
+ Derived d;
+ struct S {
+ Base* ptr;
+ const Base* constPtr;
+ Base* const ptrConst;
+ Base const* const constPtrConst;
+ S ( Base* v ) :
+ ptr ( v ),
+ constPtr ( v ),
+ ptrConst ( v ),
+ constPtrConst ( v ) {}
+ } s ( &d );
+ /*:
+ set testname use_rtti_for_ptr_child
+
+ set_print_object off $testname
+ mi_create_varobj VAR s "create varobj for s (without RTTI) in $testname"
+ mi_list_varobj_children VAR {
+ { VAR.public public 4 }
+ } "list children of s (without RTTI) in $testname"
+ mi_list_varobj_children VAR.public {
+ { VAR.public.ptr ptr 1 {Base \*} }
+ { VAR.public.constPtr constPtr 1 {const Base \*} }
+ { VAR.public.ptrConst ptrConst 1 {Base \* const} }
+ { VAR.public.constPtrConst constPtrConst 1 {const Base \* const} }
+ } "list children of s.public (without RTTI) in $testname"
+ check_derived_without_rtti VAR.public.ptr s.ptr $testname
+ check_derived_without_rtti VAR.public.constPtr s.constPtr $testname
+ check_derived_without_rtti VAR.public.ptrConst s.ptrConst $testname
+ check_derived_without_rtti VAR.public.constPtrConst s.constPtrConst \
+ $testname
+ mi_delete_varobj VAR "delete varobj for s (without RTTI) in $testname"
+
+ set_print_object on $testname
+ mi_create_varobj VAR s "create varobj for s (with RTTI) in $testname"
+ mi_list_varobj_children VAR {
+ { VAR.public public 4 }
+ } "list children of s (with RTTI) in $testname"
+ mi_list_varobj_children VAR.public {
+ { VAR.public.ptr ptr 2 {Derived \*} }
+ { VAR.public.constPtr constPtr 2 {const Derived \*} }
+ { VAR.public.ptrConst ptrConst 2 {Derived \* const} }
+ { VAR.public.constPtrConst constPtrConst 2 {const Derived \* const}}
+ } "list children of s.public (with RTTI) in $testname"
+ check_derived_with_rtti VAR.public.ptr s.ptr $testname
+ check_derived_with_rtti VAR.public.constPtr s.constPtr $testname
+ check_derived_with_rtti VAR.public.ptrConst s.ptrConst $testname
+ check_derived_with_rtti VAR.public.constPtrConst s.constPtrConst \
+ $testname
+ mi_delete_varobj VAR "delete varobj for s (with RTTI) in $testname"
+ :*/
+ return;
+ /*: END: use_rtti_for_ptr_child :*/
+}
+
+
+void use_rtti_for_ref_child_test ()
+{
+ /*: BEGIN: use_rtti_for_ref_child :*/
+ Derived d;
+ struct S {
+ Base& ref;
+ const Base& constRef;
+ S ( Base& v ) :
+ ref ( v ),
+ constRef ( v ) {}
+ } s ( d );
+ /*:
+ set testname use_rtti_for_ref_child
+
+ set_print_object off $testname
+ mi_create_varobj VAR s "create varobj for s (without RTTI) in $testname"
+ mi_list_varobj_children VAR {
+ { VAR.public public 2 }
+ } "list children of s (without RTTI) in $testname"
+ mi_list_varobj_children VAR.public {
+ { VAR.public.ref ref 1 {Base \&} }
+ { VAR.public.constRef constRef 1 {const Base \&} }
+ } "list children of s.public (without RTTI) in $testname"
+ check_derived_without_rtti VAR.public.ref s.ref $testname
+ check_derived_without_rtti VAR.public.constRef s.constRef $testname
+ mi_delete_varobj VAR "delete varobj for s (without RTTI) in $testname"
+
+ set_print_object on $testname
+ mi_create_varobj VAR s "create varobj for s (with RTTI) in $testname"
+ mi_list_varobj_children VAR {
+ { VAR.public public 2 }
+ } "list children of s (with RTTI) in $testname"
+ mi_list_varobj_children VAR.public {
+ { VAR.public.ref ref 2 {Derived \&} }
+ { VAR.public.constRef constRef 2 {const Derived \&} }
+ } "list children of s.public (with RTTI) in $testname"
+ check_derived_with_rtti VAR.public.ref s.ref $testname
+ check_derived_with_rtti VAR.public.constRef s.constRef $testname
+ mi_delete_varobj VAR "delete varobj for s (with RTTI) in $testname"
+ :*/
+ return;
+ /*: END: use_rtti_for_ref_child :*/
+}
+
+
+struct First {
+ First() : F(-1) {}
+ int F;
+};
+
+
+struct MultipleDerived : public First, Base {
+ MultipleDerived() : B(2), C(3) {}
+ int B;
+ int C;
+};
+
+
+void use_rtti_with_multiple_inheritence_test ()
+{
+ /*: BEGIN: use_rtti_with_multiple_inheritence :*/
+ MultipleDerived d;
+ Base* ptr = &d;
+ Base& ref = d;
+ /*:
+ set testname use_rtti_with_multiple_inheritence
+ set_print_object off $testname
+ check_new_derived_without_rtti ptr {Base \*} $testname
+ check_new_derived_without_rtti ref {Base \&} $testname
+
+ set_print_object on $testname
+ mi_create_varobj_checked VAR ptr {MultipleDerived \*} \
+ "create varobj for ptr (with RTTI) in $testname"
+ mi_list_varobj_children VAR {
+ { VAR.First First 1 First }
+ { VAR.Base Base 1 Base }
+ { VAR.public public 2 }
+ } "list children of ptr (with RTTI) in $testname"
+ mi_list_varobj_children "VAR.First" {
+ { VAR.First.public public 1 }
+ } "list children of ptr.First (with RTTI) in $testname"
+ mi_list_varobj_children "VAR.First.public" {
+ { VAR.First.public.F F 0 int }
+ } "list children of ptr.Base.public (with RTTI) in $testname"
+ mi_list_varobj_children "VAR.Base" {
+ { VAR.Base.public public 1 }
+ } "list children of ptr.Base (with RTTI) in $testname"
+ mi_list_varobj_children "VAR.Base.public" {
+ { VAR.Base.public.A A 0 int }
+ } "list children of ptr.Base.public (with RTTI) in $testname"
+ mi_list_varobj_children "VAR.public" {
+ { VAR.public.B B 0 int }
+ { VAR.public.C C 0 int }
+ } "list children of ptr.public (with RTTI) in $testname"
+
+ mi_delete_varobj VAR \
+ "delete varobj for ptr (with RTTI) in $testname"
+ :*/
+ return;
+ /*: END: use_rtti_with_multiple_inheritence :*/
+}
+
+
+void type_update_when_use_rtti_test ()
+{
+ /*: BEGIN: type_update_when_use_rtti :*/
+ Derived d;
+ /*:
+ set testname type_update_when_use_rtti
+
+ set_print_object on $testname
+ mi_create_varobj_checked PTR ptr {Base \*} \
+ "create varobj for ptr in $testname"
+ check_derived_children_without_rtti PTR ptr $testname
+
+ mi_create_varobj S s "create varobj for S in $testname"
+ mi_list_varobj_children S {
+ { S.public public 1 }
+ } "list children of s in $testname"
+ mi_list_varobj_children S.public {
+ { S.public.ptr ptr 1 {Base \*} }
+ } "list children of s.public in $testname"
+ check_derived_children_without_rtti S.public.ptr s.ptr $testname
+ :*/
+
+ Base* ptr = &d;
+ struct S {
+ Base* ptr;
+ S ( Base* v ) :
+ ptr ( v ) {}
+ } s ( &d );
+ /*:
+ mi_varobj_update_with_type_change PTR {Derived \*} 2 \
+ "update ptr to derived in $testname"
+ check_derived_with_rtti PTR ptr $testname
+
+ mi_varobj_update_with_child_type_change S S.public.ptr {Derived \*} 2 \
+ "update s.ptr to derived in $testname"
+ check_derived_with_rtti S.public.ptr s.ptr $testname
+ :*/
+
+ ptr = 0;
+ s.ptr = 0;
+ /*:
+ mi_varobj_update_with_type_change PTR {Base \*} 1 \
+ "update ptr back to base type in $testname"
+ mi_delete_varobj PTR "delete varobj for ptr in $testname"
+
+ mi_varobj_update_with_child_type_change S S.public.ptr {Base \*} 1 \
+ "update s.ptr back to base type in $testname"
+ mi_delete_varobj S "delete varobj for s in $testname"
+ :*/
+ return;
+ /*: END: type_update_when_use_rtti :*/
+}
+
+
+void skip_type_update_when_not_use_rtti_test ()
+{
+ /*: BEGIN: skip_type_update_when_not_use_rtti :*/
+ Derived d;
+ /*:
+ set testname skip_type_update_when_not_use_rtti
+
+ set_print_object off $testname
+ mi_create_varobj_checked PTR ptr {Base \*} \
+ "create varobj for ptr in $testname"
+ check_derived_children_without_rtti PTR ptr $testname
+
+ mi_create_varobj S s "create varobj for S in $testname"
+ mi_list_varobj_children S {
+ { S.public public 1 }
+ } "list children of s in $testname"
+ mi_list_varobj_children S.public {
+ { S.public.ptr ptr 1 {Base \*} }
+ } "list children of s.public in $testname"
+ check_derived_children_without_rtti S.public.ptr s.ptr $testname
+ :*/
+
+ Base* ptr = &d;
+ struct S {
+ Base* ptr;
+ S ( Base* v ) :
+ ptr ( v ) {}
+ } s ( &d );
+ /*:
+ mi_varobj_update PTR {PTR PTR.public.A} \
+ "update ptr to derived type in $testname"
+ check_derived_without_rtti PTR ptr $testname
+
+ mi_varobj_update S {S.public.ptr S.public.ptr.public.A} \
+ "update s to derived type in $testname"
+ check_derived_without_rtti S.public.ptr s.ptr $testname
+ :*/
+
+ ptr = 0;
+ s.ptr = 0;
+ /*:
+ mi_varobj_update PTR {PTR PTR.public.A} \
+ "update ptr back to base type in $testname"
+ mi_delete_varobj PTR "delete varobj for ptr in $testname"
+
+ mi_varobj_update S {S.public.ptr S.public.ptr.public.A} \
+ "update s back to base type in $testname"
+ mi_delete_varobj S "delete varobj for s in $testname"
+ :*/
+ return;
+ /*: END: skip_type_update_when_not_use_rtti :*/
+}
+
+
+int main ()
+{
+ use_rtti_for_ptr_test();
+ use_rtti_for_ref_test();
+ use_rtti_for_ptr_child_test();
+ use_rtti_for_ref_child_test();
+ use_rtti_with_multiple_inheritence_test();
+ type_update_when_use_rtti_test();
+ skip_type_update_when_not_use_rtti_test();
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.mi/mi-var-rtti.exp b/gdb/testsuite/gdb.mi/mi-var-rtti.exp
new file mode 100644
index 0000000..5e43bae
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-var-rtti.exp
@@ -0,0 +1,124 @@
+# Copyright 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if { [skip_cplus_tests] } { continue }
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+ continue
+}
+
+set testfile mi-var-rtti
+set srcfile "$testfile.cc"
+set executable ${testfile}
+set binfile $objdir/$subdir/$testfile
+set opts {debug c++}
+
+if [build_executable $testfile.exp $executable $srcfile $opts] {
+ return -1;
+}
+
+mi_gdb_load ${binfile}
+
+mi_prepare_inline_tests $srcfile
+
+# Enable using RTTI to determine real types of the objects
+proc set_print_object {state testname} {
+ mi_gdb_test "-interpreter-exec console \"set print object ${state}\"" \
+ {\^done} \
+ "-interpreter-exec console \"set print object ${state}\" in $testname"
+}
+
+proc check_derived_children_without_rtti {varobj_name var_name testname} {
+ mi_list_varobj_children ${varobj_name} "
+ { ${varobj_name}.public public 1 }
+ " "list children of ${var_name} (without RTTI) in $testname"
+ mi_list_varobj_children "${varobj_name}.public" "
+ { ${varobj_name}.public.A A 0 int }
+ " "list children of ${var_name}.public (without RTTI) in $testname"
+}
+
+proc check_derived_content_without_rtti {varobj_name var_name testname} {
+ mi_check_varobj_value ${varobj_name}.public.A 1 \
+ "check ${var_name}->A (without RTTI) in $testname"
+}
+
+proc check_derived_without_rtti {varobj_name var_name testname} {
+ check_derived_children_without_rtti ${varobj_name} ${var_name} ${testname}
+ check_derived_content_without_rtti ${varobj_name} ${var_name} ${testname}
+}
+
+proc check_new_derived_without_rtti {var_name var_type testname} {
+ set varobj_name VAR
+ mi_create_varobj_checked ${varobj_name} ${var_name} ${var_type} \
+ "create varobj for ${var_name} (without RTTI) in ${testname}"
+ check_derived_without_rtti ${varobj_name} ${var_name} ${testname}
+ mi_delete_varobj ${varobj_name} \
+ "delete varobj for ${var_name} (without RTTI) in ${testname}"
+}
+
+proc check_derived_children_with_rtti {varobj_name var_name testname} {
+ mi_list_varobj_children ${varobj_name} "
+ { ${varobj_name}.Base Base 1 Base }
+ { ${varobj_name}.public public 2 }
+ " "list children of ${var_name} (with RTTI) in $testname"
+ mi_list_varobj_children "${varobj_name}.Base" "
+ { ${varobj_name}.Base.public public 1 }
+ " "list children of ${var_name}.Base (with RTTI) in $testname"
+ mi_list_varobj_children "${varobj_name}.Base.public" "
+ { ${varobj_name}.Base.public.A A 0 int }
+ " "list children of ${var_name}.Base.public (with RTTI) in $testname"
+ mi_list_varobj_children "${varobj_name}.public" "
+ { ${varobj_name}.public.B B 0 int }
+ { ${varobj_name}.public.C C 0 int }
+ " "list children of ${var_name}.public (with RTTI) in $testname"
+}
+
+proc check_derived_content_with_rtti {varobj_name var_name testname} {
+ mi_check_varobj_value ${varobj_name}.Base.public.A 1 \
+ "check ${var_name}->A (with RTTI) in $testname"
+ mi_check_varobj_value ${varobj_name}.public.B 2 \
+ "check ${var_name}->B (with RTTI) in $testname"
+ mi_check_varobj_value ${varobj_name}.public.C 3 \
+ "check ${var_name}->C (with RTTI) in $testname"
+}
+
+proc check_derived_with_rtti {varobj_name var_name testname} {
+ check_derived_children_with_rtti ${varobj_name} ${var_name} $testname
+ check_derived_content_with_rtti ${varobj_name} ${var_name} $testname
+}
+
+proc check_new_derived_with_rtti {var_name var_type testname} {
+ set varobj_name VAR
+ mi_create_varobj_checked ${varobj_name} ${var_name} ${var_type} \
+ "create varobj for ${var_name} (with RTTI) in $testname"
+ check_derived_with_rtti ${varobj_name} ${var_name} $testname
+ mi_delete_varobj ${varobj_name} \
+ "delete varobj for ${var_name} (with RTTI) in $testname"
+}
+
+mi_run_inline_test use_rtti_for_ptr
+mi_run_inline_test use_rtti_for_ref
+mi_run_inline_test use_rtti_for_ptr_child
+mi_run_inline_test use_rtti_for_ref_child
+mi_run_inline_test use_rtti_with_multiple_inheritence
+mi_run_inline_test type_update_when_use_rtti
+mi_run_inline_test skip_type_update_when_not_use_rtti
+
+mi_gdb_exit
+return 0
diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp
index 7f8642f..401565d 100644
--- a/gdb/testsuite/lib/mi-support.exp
+++ b/gdb/testsuite/lib/mi-support.exp
@@ -1298,13 +1298,17 @@ proc mi_varobj_update { name expected testname } {
mi_gdb_test "-var-update $name" $er $testname
}

-proc mi_varobj_update_with_type_change { name new_type new_children testname } {
- set v "{name=\"$name\",in_scope=\"true\",type_changed=\"true\",new_type=\"$new_type\",new_num_children=\"$new_children\",has_more=\".\"}"
+proc mi_varobj_update_with_child_type_change { name child_name new_type new_children testname } {
+ set v "{name=\"$child_name\",in_scope=\"true\",type_changed=\"true\",new_type=\"$new_type\",new_num_children=\"$new_children\",has_more=\".\"}"
set er "\\^done,changelist=\\\[$v\\\]"
verbose -log "Expecting: $er"
mi_gdb_test "-var-update $name" $er $testname
}

+proc mi_varobj_update_with_type_change { name new_type new_children testname } {
+ mi_varobj_update_with_child_type_change $name $name $new_type $new_children $testname
+}
+
# A helper that turns a key/value list into a regular expression
# matching some MI output.
proc mi_varobj_update_kv_helper {list} {
diff --git a/gdb/value.c b/gdb/value.c
index c23803a..eae3e2d 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -834,6 +834,47 @@ value_enclosing_type (struct value *value)
return value->enclosing_type;
}

+/* Look at value.h for description. */
+
+struct type *
+value_actual_type (struct value *value, int resolve_simple_types,
+ int *real_type_found)
+{
+ struct value_print_options opts;
+ struct value *target;
+ struct type *result;
+
+ get_user_print_options (&opts);
+
+ if (real_type_found)
+ *real_type_found = 0;
+ result = value_type (value);
+ if (opts.objectprint)
+ {
+ if (TYPE_CODE (result) == TYPE_CODE_PTR
+ || TYPE_CODE (result) == TYPE_CODE_REF)
+ {
+ struct type *real_type;
+
+ real_type = value_rtti_indirect_type (value, NULL, NULL, NULL);
+ if (real_type)
+ {
+ if (real_type_found)
+ *real_type_found = 1;
+ result = real_type;
+ }
+ }
+ else if (resolve_simple_types)
+ {
+ if (real_type_found)
+ *real_type_found = 1;
+ result = value_enclosing_type (value);
+ }
+ }
+
+ return result;
+}
+
static void
require_not_optimized_out (const struct value *value)
{
diff --git a/gdb/value.h b/gdb/value.h
index d501b52..9de31a6 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -138,6 +138,22 @@ extern struct type *value_enclosing_type (struct value *);
extern void set_value_enclosing_type (struct value *val,
struct type *new_type);

+/* Returns value_type or value_enclosing_type depending on
+ value_print_options.objectprint.
+
+ If RESOLVE_SIMPLE_TYPES is 0 the enclosing type will be resolved
+ only for pointers and references, else it will be returned
+ for all the types (e.g. structures). This option is useful
+ to prevent retrieving enclosing type for the base classes fields.
+
+ REAL_TYPE_FOUND is used to inform whether the real type was found
+ (or just static type was used). The NULL may be passed if it is not
+ necessary. */
+
+extern struct type *value_actual_type (struct value *value,
+ int resolve_simple_types,
+ int *real_type_found);
+
extern int value_pointed_to_offset (struct value *value);
extern void set_value_pointed_to_offset (struct value *value, int val);
extern int value_embedded_offset (struct value *value);
diff --git a/gdb/varobj.c b/gdb/varobj.c
index b6e67c6..5fce5f9 100644
--- a/gdb/varobj.c
+++ b/gdb/varobj.c
@@ -268,6 +268,9 @@ static void cppush (struct cpstack **pstack, char *name);

static char *cppop (struct cpstack **pstack);

+static int update_type_if_necessary (struct varobj *var,
+ struct value *new_value);
+
static int install_new_value (struct varobj *var, struct value *value,
int initial);

@@ -672,8 +675,14 @@ varobj_create (char *objname,

var->type = value_type (type_only_value);
}
- else
- var->type = value_type (value);
+ else
+ {
+ int real_type_found = 0;
+
+ var->type = value_actual_type (value, 0, &real_type_found);
+ if (real_type_found)
+ value = value_cast (var->type, value);
+ }

install_new_value (var, value, 1 /* Initial assignment */);

@@ -961,6 +970,7 @@ restrict_range (VEC (varobj_p) *children, int *from, int *to)
static void
install_dynamic_child (struct varobj *var,
VEC (varobj_p) **changed,
+ VEC (varobj_p) **type_changed,
VEC (varobj_p) **new,
VEC (varobj_p) **unchanged,
int *cchanged,
@@ -983,12 +993,18 @@ install_dynamic_child (struct varobj *var,
{
varobj_p existing = VEC_index (varobj_p, var->children, index);

+ int type_updated = update_type_if_necessary (existing, value);
+ if (type_updated)
+ {
+ if (type_changed)
+ VEC_safe_push (varobj_p, *type_changed, existing);
+ }
if (install_new_value (existing, value, 0))
{
- if (changed)
+ if (!type_updated && changed)
VEC_safe_push (varobj_p, *changed, existing);
}
- else if (unchanged)
+ else if (!type_updated && unchanged)
VEC_safe_push (varobj_p, *unchanged, existing);
}
}
@@ -1011,6 +1027,7 @@ dynamic_varobj_has_child_method (struct varobj *var)
static int
update_dynamic_varobj_children (struct varobj *var,
VEC (varobj_p) **changed,
+ VEC (varobj_p) **type_changed,
VEC (varobj_p) **new,
VEC (varobj_p) **unchanged,
int *cchanged,
@@ -1146,6 +1163,7 @@ update_dynamic_varobj_children (struct varobj *var,
if (v == NULL)
gdbpy_print_stack ();
install_dynamic_child (var, can_mention ? changed : NULL,
+ can_mention ? type_changed : NULL,
can_mention ? new : NULL,
can_mention ? unchanged : NULL,
can_mention ? cchanged : NULL, i, name, v);
@@ -1201,7 +1219,7 @@ varobj_get_num_children (struct varobj *var)

/* If we have a dynamic varobj, don't report -1 children.
So, try to fetch some children first. */
- update_dynamic_varobj_children (var, NULL, NULL, NULL, &dummy,
+ update_dynamic_varobj_children (var, NULL, NULL, NULL, NULL, &dummy,
0, 0, 0);
}
else
@@ -1227,8 +1245,8 @@ varobj_list_children (struct varobj *var, int *from, int *to)
/* This, in theory, can result in the number of children changing without
frontend noticing. But well, calling -var-list-children on the same
varobj twice is not something a sane frontend would do. */
- update_dynamic_varobj_children (var, NULL, NULL, NULL, &children_changed,
- 0, 0, *to);
+ update_dynamic_varobj_children (var, NULL, NULL, NULL, NULL,
+ &children_changed, 0, 0, *to);
restrict_range (var->children, from, to);
return var->children;
}
@@ -1575,6 +1593,43 @@ install_new_value_visualizer (struct varobj *var)
#endif
}

+/* When using RTTI to determine variable type it may be changed in runtime when
+ the variable value is changed. This function checks whether type of varobj
+ VAR will change when a new value NEW_VALUE is assigned and if it is so
+ updates the type of VAR. */
+
+static int
+update_type_if_necessary (struct varobj *var, struct value *new_value)
+{
+ if (new_value)
+ {
+ struct value_print_options opts;
+
+ get_user_print_options (&opts);
+ if (opts.objectprint)
+ {
+ struct type *new_type;
+ char *curr_type_str, *new_type_str;
+
+ new_type = value_actual_type (new_value, 0, 0);
+ new_type_str = type_to_string (new_type);
+ curr_type_str = varobj_get_type (var);
+ if (strcmp (curr_type_str, new_type_str) != 0)
+ {
+ var->type = new_type;
+
+ /* This information may be not valid for a new type. */
+ varobj_delete (var, NULL, 1);
+ VEC_free (varobj_p, var->children);
+ var->num_children = -1;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
/* Assign a new value to a variable object. If INITIAL is non-zero,
this is the first assignement after the variable object was just
created, or changed type. In that case, just assign the value
@@ -1880,8 +1935,9 @@ varobj_update (struct varobj **varp, int explicit)
value_of_root variable dispose of the varobj if the type
has changed. */
new = value_of_root (varp, &type_changed);
+ if (update_type_if_necessary(*varp, new))
+ type_changed = 1;
r.varobj = *varp;
-
r.type_changed = type_changed;
if (install_new_value ((*varp), new, type_changed))
r.changed = 1;
@@ -1920,6 +1976,8 @@ varobj_update (struct varobj **varp, int explicit)
if (!r.value_installed)
{
new = value_of_child (v->parent, v->index);
+ if (update_type_if_necessary(v, new))
+ r.type_changed = 1;
if (install_new_value (v, new, 0 /* type not changed */))
{
r.changed = 1;
@@ -1932,7 +1990,8 @@ varobj_update (struct varobj **varp, int explicit)
invoked. */
if (v->pretty_printer)
{
- VEC (varobj_p) *changed = 0, *new = 0, *unchanged = 0;
+ VEC (varobj_p) *changed = 0, *type_changed = 0, *unchanged = 0;
+ VEC (varobj_p) *new = 0;
int i, children_changed = 0;

if (v->frozen)
@@ -1950,7 +2009,7 @@ varobj_update (struct varobj **varp, int explicit)
it. */
if (!varobj_has_more (v, 0))
{
- update_dynamic_varobj_children (v, NULL, NULL, NULL,
+ update_dynamic_varobj_children (v, NULL, NULL, NULL, NULL,
&dummy, 0, 0, 0);
if (varobj_has_more (v, 0))
r.changed = 1;
@@ -1964,8 +2023,8 @@ varobj_update (struct varobj **varp, int explicit)

/* If update_dynamic_varobj_children returns 0, then we have
a non-conforming pretty-printer, so we skip it. */
- if (update_dynamic_varobj_children (v, &changed, &new, &unchanged,
- &children_changed, 1,
+ if (update_dynamic_varobj_children (v, &changed, &type_changed, &new,
+ &unchanged, &children_changed, 1,
v->from, v->to))
{
if (children_changed || new)
@@ -1977,6 +2036,18 @@ varobj_update (struct varobj **varp, int explicit)
popped from the work stack first, and so will be
added to result first. This does not affect
correctness, just "nicer". */
+ for (i = VEC_length (varobj_p, type_changed) - 1; i >= 0; --i)
+ {
+ varobj_p tmp = VEC_index (varobj_p, type_changed, i);
+ varobj_update_result r = {0};
+
+ /* Type may change only if value was changed. */
+ r.varobj = tmp;
+ r.changed = 1;
+ r.type_changed = 1;
+ r.value_installed = 1;
+ VEC_safe_push (varobj_update_result, stack, &r);
+ }
for (i = VEC_length (varobj_p, changed) - 1; i >= 0; --i)
{
varobj_p tmp = VEC_index (varobj_p, changed, i);
@@ -2003,9 +2074,10 @@ varobj_update (struct varobj **varp, int explicit)
if (r.changed || r.children_changed)
VEC_safe_push (varobj_update_result, result, &r);

- /* Free CHANGED and UNCHANGED, but not NEW, because NEW
- has been put into the result vector. */
+ /* Free CHANGED, TYPE_CHANGED and UNCHANGED, but not NEW,
+ because NEW has been put into the result vector. */
VEC_free (varobj_p, changed);
+ VEC_free (varobj_p, type_changed);
VEC_free (varobj_p, unchanged);

continue;
@@ -2280,7 +2352,7 @@ create_child_with_value (struct varobj *parent, int index, const char *name,
if (value != NULL)
/* If the child had no evaluation errors, var->value
will be non-NULL and contain a valid type. */
- child->type = value_type (value);
+ child->type = value_actual_type (value, 0, NULL);
else
/* Otherwise, we must compute the type. */
child->type = (*child->root->lang->type_of_child) (child->parent,
@@ -2867,6 +2939,10 @@ varobj_floating_p (struct varobj *var)
to all types and dereferencing pointers to
structures.

+ If LOOKUP_ACTUAL_TYPE is set the enclosing type of the
+ value will be fetched and if it differs from static type
+ the value will be casted to it.
+
Both TYPE and *TYPE should be non-null. VALUE
can be null if we want to only translate type.
*VALUE can be null as well -- if the parent
@@ -2878,7 +2954,8 @@ varobj_floating_p (struct varobj *var)
static void
adjust_value_for_child_access (struct value **value,
struct type **type,
- int *was_ptr)
+ int *was_ptr,
+ int lookup_actual_type)
{
gdb_assert (type && *type);

@@ -2923,6 +3000,20 @@ adjust_value_for_child_access (struct value **value,
/* The 'get_target_type' function calls check_typedef on
result, so we can immediately check type code. No
need to call check_typedef here. */
+
+ /* Access a real type of the value (if necessary and possible). */
+ if (value && *value && lookup_actual_type)
+ {
+ struct type *enclosing_type;
+ int real_type_found = 0;
+
+ enclosing_type = value_actual_type (*value, 1, &real_type_found);
+ if (real_type_found)
+ {
+ *type = enclosing_type;
+ *value = value_cast (enclosing_type, *value);
+ }
+ }
}

/* C */
@@ -2933,7 +3024,7 @@ c_number_of_children (struct varobj *var)
int children = 0;
struct type *target;

- adjust_value_for_child_access (NULL, &type, NULL);
+ adjust_value_for_child_access (NULL, &type, NULL, 0);
target = get_target_type (type);

switch (TYPE_CODE (type))
@@ -3049,7 +3140,7 @@ c_describe_child (struct varobj *parent, int index,
*cfull_expression = NULL;
parent_expression = varobj_get_path_expr (get_path_expr_parent (parent));
}
- adjust_value_for_child_access (&value, &type, &was_ptr);
+ adjust_value_for_child_access (&value, &type, &was_ptr, 0);

switch (TYPE_CODE (type))
{
@@ -3345,16 +3436,29 @@ c_value_of_variable (struct varobj *var, enum varobj_display_formats format)
static int
cplus_number_of_children (struct varobj *var)
{
+ struct value *value = NULL;
struct type *type;
int children, dont_know;
+ int lookup_actual_type = 0;
+ struct value_print_options opts;

dont_know = 1;
children = 0;

+ get_user_print_options (&opts);
+
if (!CPLUS_FAKE_CHILD (var))
{
type = get_value_type (var);
- adjust_value_for_child_access (NULL, &type, NULL);
+
+ /* It is necessary to access a real type (via RTTI). */
+ if (opts.objectprint)
+ {
+ value = var->value;
+ lookup_actual_type = (TYPE_CODE (var->type) == TYPE_CODE_REF
+ || TYPE_CODE (var->type) == TYPE_CODE_PTR);
+ }
+ adjust_value_for_child_access (&value, &type, NULL, lookup_actual_type);

if (((TYPE_CODE (type)) == TYPE_CODE_STRUCT) ||
((TYPE_CODE (type)) == TYPE_CODE_UNION))
@@ -3381,7 +3485,17 @@ cplus_number_of_children (struct varobj *var)
int kids[3];

type = get_value_type (var->parent);
- adjust_value_for_child_access (NULL, &type, NULL);
+
+ /* It is necessary to access a real type (via RTTI). */
+ if (opts.objectprint)
+ {
+ struct varobj *parent = var->parent;
+
+ value = parent->value;
+ lookup_actual_type = (TYPE_CODE (parent->type) == TYPE_CODE_REF
+ || TYPE_CODE (parent->type) == TYPE_CODE_PTR);
+ }
+ adjust_value_for_child_access (&value, &type, NULL, lookup_actual_type);

cplus_class_num_children (type, kids);
if (strcmp (var->name, "public") == 0)
@@ -3463,7 +3577,10 @@ cplus_describe_child (struct varobj *parent, int index,
struct value *value;
struct type *type;
int was_ptr;
+ int lookup_actual_type = 0;
char *parent_expression = NULL;
+ struct varobj *var;
+ struct value_print_options opts;

if (cname)
*cname = NULL;
@@ -3474,24 +3591,18 @@ cplus_describe_child (struct varobj *parent, int index,
if (cfull_expression)
*cfull_expression = NULL;

- if (CPLUS_FAKE_CHILD (parent))
- {
- value = parent->parent->value;
- type = get_value_type (parent->parent);
- if (cfull_expression)
- parent_expression
- = varobj_get_path_expr (get_path_expr_parent (parent->parent));
- }
- else
- {
- value = parent->value;
- type = get_value_type (parent);
- if (cfull_expression)
- parent_expression
- = varobj_get_path_expr (get_path_expr_parent (parent));
- }
+ get_user_print_options (&opts);
+
+ var = (CPLUS_FAKE_CHILD (parent)) ? parent->parent : parent;
+ if (opts.objectprint)
+ lookup_actual_type = (TYPE_CODE (var->type) == TYPE_CODE_REF
+ || TYPE_CODE (var->type) == TYPE_CODE_PTR);
+ value = var->value;
+ type = get_value_type (var);
+ if (cfull_expression)
+ parent_expression = varobj_get_path_expr (get_path_expr_parent (var));

- adjust_value_for_child_access (&value, &type, &was_ptr);
+ adjust_value_for_child_access (&value, &type, &was_ptr, lookup_actual_type);

if (TYPE_CODE (type) == TYPE_CODE_STRUCT
|| TYPE_CODE (type) == TYPE_CODE_UNION)

[-- Attachment #3: gdb_varobj_rtti4_1.ChangeLog --]
[-- Type: text/plain, Size: 1400 bytes --]

gdb/doc/ChangeLog:

2012-03-18 Anton Gorenkov <xgsa@yandex.ru>

PR mi/13393

* gdb.texinfo (Print Settings): Extend the description for "set print
object".
(GDB/MI Variable Objects): Extend the description for -var-create and
-var-list-children.

gdb/testsuite/ChangeLog:

2012-03-18 Anton Gorenkov <xgsa@yandex.ru>

PR mi/13393

* gdb.mi/mi-var-rtti.cc: New file.
* gdb.mi/mi-var-rtti.exp: New file.
* lib/mi-support.exp (mi_varobj_update_with_child_type_change): New
function.
(mi_varobj_update_with_type_change): updated to avoid code duplication.

gdb/ChangeLog:

2012-03-18 Anton Gorenkov <xgsa@yandex.ru>

PR mi/13393

* value.c (value_actual_type): New function.
* value.h (value_actual_type): New declaration.
* varobj.c (update_type_if_necessary): New function.
(varobj_create): Call value_actual_type instead of
value_type.
(install_dynamic_child): distinct changed and type changed MI variable
objects.
(update_dynamic_varobj_children): Updated for install_dynamic_child
change. All callers updated.
(varobj_update): Support for MI variable object type change if
the value changed and RTTI is used to determine the type.
(create_child_with_value): Call value_actual_type instead of
value_type.
(adjust_value_for_child_access): Extended with a new parameter which
specify whether the given value should be casted to enclosing type.
All callers updated.