diff options
-rw-r--r-- | gdb/ChangeLog | 20 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 13 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 9 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-var-rtti.cc | 360 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-var-rtti.exp | 124 | ||||
-rw-r--r-- | gdb/testsuite/lib/mi-support.exp | 8 | ||||
-rw-r--r-- | gdb/value.c | 41 | ||||
-rw-r--r-- | gdb/value.h | 16 | ||||
-rw-r--r-- | gdb/varobj.c | 185 |
10 files changed, 742 insertions, 42 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 98e5ef6..5ff4c97 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,23 @@ +2012-04-14 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. + 2012-04-14 Yao Qi <yao@codesourcery.com> Import gnulib module inttypes from git diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index ecb3409..e288b9e 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,11 @@ +2012-04-14 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. + 2012-04-11 Siva Chandra Reddy <sivachandra@google.com> * gdb.texinfo (Examining Data): Document the 'explore' command. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 37ffbb1..613782e 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -8666,7 +8666,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 @@ -29103,7 +29104,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 @@ -29274,7 +29278,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/ChangeLog b/gdb/testsuite/ChangeLog index bfcc42d..92ddbe8 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2012-04-14 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. + 2012-04-11 Siva Chandra Reddy <sivachandra@google.com> * gdb.python/Makefile.in: Add py-explore and py-explore-cc to 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 4d04a20..03aa5fd 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 2e4dabf..3086499 100644 --- a/gdb/varobj.c +++ b/gdb/varobj.c @@ -270,6 +270,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); @@ -716,8 +719,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); + } /* Set language info */ lang = variable_language (var); @@ -1005,6 +1014,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, @@ -1027,12 +1037,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); } } @@ -1055,6 +1071,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, @@ -1190,6 +1207,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); @@ -1245,7 +1263,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 @@ -1271,8 +1289,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; } @@ -1619,6 +1637,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 @@ -1948,8 +2003,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; @@ -1990,6 +2046,8 @@ varobj_update (struct varobj **varp, int explicit) struct type *new_type; new = value_of_child (v->parent, v->index); + if (update_type_if_necessary(v, new)) + r.type_changed = 1; if (new) new_type = value_type (new); else @@ -2019,7 +2077,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) @@ -2037,7 +2096,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; @@ -2051,8 +2110,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) @@ -2064,6 +2123,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); @@ -2090,9 +2161,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; @@ -2367,7 +2439,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, @@ -2948,6 +3020,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 @@ -2959,7 +3035,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); @@ -3004,6 +3081,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); + } + } } /* Implement the "value_is_changeable_p" varobj callback for most @@ -3044,7 +3135,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)) @@ -3160,7 +3251,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)) { @@ -3456,16 +3547,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)) @@ -3492,7 +3596,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) @@ -3574,7 +3688,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; @@ -3585,24 +3702,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) |