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

xgsa
2012-03-18T16:28:06Z

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

Hi,

While implementing the support for this patch in Eclipse I ran into a
problem with type updates for child varobjs. This patch fixes the issue.

And I still have a question about the documentation update. What part of
it should be updated (the "show/set print object" commands description
or to MI interface chapter or something else)?

Thanks,
Anton

[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 1486 bytes --]

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.
(varobj_get_num_children): Likewise.
(varobj_list_children): Likewise.
(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.
(c_number_of_children): Updated for extended
adjust_value_for_child_access.
(c_describe_child): Updated for extended
adjust_value_for_child_access.
(cplus_number_of_children): Updated for extended
adjust_value_for_child_access.
(cplus_describe_child): Updated for extended
adjust_value_for_child_access.

[-- Attachment #3: gdb_varobj_rtti2.patch --]
[-- Type: text/x-diff, Size: 33739 bytes --]

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..3611cf5 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..e16e963 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..24c0398 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)