| # Copyright 1992-2021 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/>. | |
| # This file was written by Fred Fish. (fnf@cygnus.com) | |
| # And rewritten by Michael Chastain <mec.gnu@mindspring.com>. | |
| set nl "\[\r\n\]+" | |
| if { [skip_cplus_tests] } { continue } | |
| load_lib "cp-support.exp" | |
| standard_testfile .cc | |
| if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { | |
| return -1 | |
| } | |
| # Test ptype of class objects. | |
| proc test_ptype_class_objects {} { | |
| # Simple type. | |
| cp_test_ptype_class \ | |
| "struct default_public_struct" "" "struct" "default_public_struct" \ | |
| { | |
| { field public "int a;" } | |
| { field public "int b;" } | |
| } | |
| # Another simple type. | |
| cp_test_ptype_class \ | |
| "struct explicit_public_struct" "" "struct" "explicit_public_struct" \ | |
| { | |
| { field public "int a;" } | |
| { field public "int b;" } | |
| } | |
| # Another simple type. | |
| cp_test_ptype_class \ | |
| "struct protected_struct" "" "struct" "protected_struct" \ | |
| { | |
| { field protected "int a;" } | |
| { field protected "int b;" } | |
| } | |
| # Another simple type. | |
| cp_test_ptype_class \ | |
| "struct private_struct" "" "struct" "private_struct" \ | |
| { | |
| { field private "int a;" } | |
| { field private "int b;" } | |
| } | |
| # A bigger type. | |
| cp_test_ptype_class \ | |
| "struct mixed_protection_struct" "" "struct" "mixed_protection_struct" \ | |
| { | |
| { field public "int a;" } | |
| { field public "int b;" } | |
| { field private "int c;" } | |
| { field private "int d;" } | |
| { field protected "int e;" } | |
| { field protected "int f;" } | |
| { field public "int g;" } | |
| { field private "int h;" } | |
| { field protected "int i;" } | |
| } | |
| # All that again with "class" instead of "struct". | |
| # gdb does not care about the difference anyways. | |
| cp_test_ptype_class \ | |
| "class public_class" "" "class" "public_class" \ | |
| { | |
| { field public "int a;" } | |
| { field public "int b;" } | |
| } | |
| # Another simple type. | |
| cp_test_ptype_class \ | |
| "class protected_class" "" "class" "protected_class" \ | |
| { | |
| { field protected "int a;" } | |
| { field protected "int b;" } | |
| } | |
| # Another simple type. | |
| cp_test_ptype_class \ | |
| "class default_private_class" "" "class" "default_private_class" \ | |
| { | |
| { field private "int a;" } | |
| { field private "int b;" } | |
| } | |
| # Another simple type. | |
| cp_test_ptype_class \ | |
| "class explicit_private_class" "" "class" "explicit_private_class" \ | |
| { | |
| { field private "int a;" } | |
| { field private "int b;" } | |
| } | |
| # A bigger type. | |
| cp_test_ptype_class \ | |
| "class mixed_protection_class" "" "class" "mixed_protection_class" \ | |
| { | |
| { field public "int a;" } | |
| { field public "int b;" } | |
| { field private "int c;" } | |
| { field private "int d;" } | |
| { field protected "int e;" } | |
| { field protected "int f;" } | |
| { field public "int g;" } | |
| { field private "int h;" } | |
| { field protected "int i;" } | |
| } | |
| # Here are some classes with inheritance. | |
| # Base class. | |
| cp_test_ptype_class \ | |
| "class A" "" "class" "A" \ | |
| { | |
| { field public "int a;" } | |
| { field public "int x;" } | |
| } | |
| # Derived class. | |
| cp_test_ptype_class \ | |
| "class B" "" "class" "B" \ | |
| { | |
| { base "public A" } | |
| { field public "int b;" } | |
| { field public "int x;" } | |
| } | |
| # Derived class. | |
| cp_test_ptype_class \ | |
| "class C" "" "class" "C" \ | |
| { | |
| { base "public A" } | |
| { field public "int c;" } | |
| { field public "int x;" } | |
| } | |
| # Derived class, multiple inheritance. | |
| cp_test_ptype_class \ | |
| "class D" "" "class" "D" \ | |
| { | |
| { base "public B" } | |
| { base "public C" } | |
| { field public "int d;" } | |
| { field public "int x;" } | |
| } | |
| # Derived class. | |
| cp_test_ptype_class \ | |
| "class E" "" "class" "E" \ | |
| { | |
| { base "public D" } | |
| { field public "int e;" } | |
| { field public "int x;" } | |
| } | |
| # This is a break from inheritance tests. | |
| # | |
| # gcc 2.X with stabs (stabs or stabs+?) used to have a problem with | |
| # static methods whose name is the same as their argument mangling. | |
| cp_test_ptype_class \ | |
| "class Static" "" "class" "Static" \ | |
| { | |
| { method public "static void ii(int, int);" } | |
| } | |
| # Here are some virtual inheritance tests. | |
| # A virtual base class. | |
| cp_test_ptype_class \ | |
| "class vA" "" "class" "vA" \ | |
| { | |
| { field public "int va;" } | |
| { field public "int vx;" } | |
| } | |
| # A derived class with a virtual base. | |
| cp_test_ptype_class \ | |
| "class vB" "" "class" "vB" \ | |
| { | |
| { base "public virtual vA" } | |
| { vbase "vA" } | |
| { field public "int vb;" } | |
| { field public "int vx;" } | |
| } | |
| # Another derived class with a virtual base. | |
| cp_test_ptype_class \ | |
| "class vC" "" "class" "vC" \ | |
| { | |
| { base "public virtual vA" } | |
| { vbase "vA" } | |
| { field public "int vc;" } | |
| { field public "int vx;" } | |
| } | |
| # A classic diamond class. | |
| cp_test_ptype_class \ | |
| "class vD" "" "class" "vD" \ | |
| { | |
| { base "public virtual vB" } | |
| { base "public virtual vC" } | |
| { vbase "vC" } | |
| { vbase "vB" } | |
| { field public "int vd;" } | |
| { field public "int vx;" } | |
| } | |
| # A class derived from a diamond class. | |
| cp_test_ptype_class \ | |
| "class vE" "" "class" "vE" \ | |
| { | |
| { base "public virtual vD" } | |
| { vbase "vD" } | |
| { field public "int ve;" } | |
| { field public "int vx;" } | |
| } | |
| # Another inheritance series. | |
| # A base class. | |
| cp_test_ptype_class \ | |
| "class Base1" "" "class" "Base1" \ | |
| { | |
| { field public "int x;" } | |
| { method public "Base1(int);" } | |
| } | |
| # Another base class. | |
| cp_test_ptype_class \ | |
| "class Foo" "" "class" "Foo" \ | |
| { | |
| { field public "int x;" } | |
| { field public "int y;" } | |
| { field public "static int st;" } | |
| { method public "Foo(int, int);" } | |
| { method public "int operator!();" } | |
| { method public "operator int();" } | |
| { method public "int times(int);" } | |
| } \ | |
| "" \ | |
| { | |
| { | |
| "operator int();" | |
| "int operator int();" | |
| { setup_kfail "gdb/1497" "*-*-*" } | |
| } | |
| { | |
| "operator int();" | |
| "int operator int(void);" | |
| { setup_kfail "gdb/1497" "*-*-*" } | |
| } | |
| } | |
| # A multiple inheritance derived class. | |
| cp_test_ptype_class \ | |
| "class Bar" "" "class" "Bar" \ | |
| { | |
| { base "public Base1" } | |
| { base "public Foo" } | |
| { field public "int z;" } | |
| { method public "Bar(int, int, int);" } | |
| } | |
| # Derived class with typedef'd baseclass with virtual methods. | |
| cp_test_ptype_class \ | |
| "class DynamicBar" "" "class" "DynamicBar" \ | |
| { | |
| { base "public DynamicBase2" } | |
| { field public "int y;" } | |
| { method public "DynamicBar(int, int);" } | |
| } | |
| # Classes with typedefs of different access. | |
| cp_test_ptype_class \ | |
| "class class_with_typedefs" "" "class" "class_with_typedefs" \ | |
| { | |
| { field protected \ | |
| "class_with_typedefs::public_int public_int_;" } | |
| { field protected \ | |
| "class_with_typedefs::protected_int protected_int_;" } | |
| { field protected \ | |
| "class_with_typedefs::private_int private_int_;" } | |
| { method public "class_with_typedefs(void);" } | |
| { method public "class_with_typedefs::public_int add_public(class_with_typedefs::public_int);" } | |
| { method public \ | |
| "class_with_typedefs::public_int add_all(int);" } | |
| { method protected "class_with_typedefs::protected_int add_protected(class_with_typedefs::protected_int);" } | |
| { method private "class_with_typedefs::private_int add_private(class_with_typedefs::private_int);" } | |
| { typedef public "typedef int public_int;" } | |
| { typedef protected "typedef int protected_int;" } | |
| { typedef private "typedef int private_int;" } | |
| } | |
| cp_test_ptype_class \ | |
| "class class_with_public_typedef" "" "class" \ | |
| "class_with_public_typedef" { | |
| { field private "int a;" } | |
| { field public "class_with_public_typedef::INT b;" } | |
| { typedef public "typedef int INT;" } | |
| } | |
| cp_test_ptype_class \ | |
| "class class_with_protected_typedef" "" "class" \ | |
| "class_with_protected_typedef" { | |
| { field private "int a;" } | |
| { field protected "class_with_protected_typedef::INT b;" } | |
| { typedef protected "typedef int INT;" } | |
| } | |
| cp_test_ptype_class \ | |
| "struct struct_with_protected_typedef" "" "struct" \ | |
| "struct_with_protected_typedef" { | |
| { field public "int a;" } | |
| { field protected "struct_with_protected_typedef::INT b;" } | |
| { typedef protected "typedef int INT;" } | |
| } | |
| cp_test_ptype_class \ | |
| "struct struct_with_private_typedef" "" "struct" \ | |
| "struct_with_private_typedef" { | |
| { field public "int a;" } | |
| { field private "struct_with_private_typedef::INT b;" } | |
| { typedef private "typedef int INT;" } | |
| } | |
| # For the following two cases, we cannot use cp_test_ptype_class. | |
| # We need to explicitly check whether the access label was suppressed. | |
| set ws {[ \t\r\n]*} | |
| foreach {tag lbl} {"class" "private" "struct" "public"} { | |
| gdb_test "ptype/r ${tag}_with_${lbl}_typedef" "type = $tag ${tag}_with_${lbl}_typedef \{${ws}int a;${ws}${tag}_with_${lbl}_typedef::INT b;${ws}typedef int INT;${ws}\}" | |
| } | |
| } | |
| # Test simple access to class members. | |
| proc test_non_inherited_member_access {} { | |
| # Print non-inherited members of g_A. | |
| gdb_test "print g_A.a" ".* = 1" | |
| gdb_test "print g_A.x" ".* = 2" | |
| # Print non-inherited members of g_B. | |
| gdb_test "print g_B.b" ".* = 5" | |
| gdb_test "print g_B.x" ".* = 6" | |
| # Print non-inherited members of g_C. | |
| gdb_test "print g_C.c" ".* = 9" | |
| gdb_test "print g_C.x" ".* = 10" | |
| # Print non-inherited members of g_D. | |
| gdb_test "print g_D.d" ".* = 19" | |
| gdb_test "print g_D.x" ".* = 20" | |
| # Print non-inherited members of g_E. | |
| gdb_test "print g_E.e" ".* = 31" | |
| gdb_test "print g_E.x" ".* = 32" | |
| } | |
| # Test access to members of other classes. | |
| # gdb should refuse to print them. | |
| # (I feel old -- I remember when this was legal in C -- chastain). | |
| proc test_wrong_class_members {} { | |
| gdb_test "print g_A.b" "There is no member( or method|) named b." | |
| gdb_test "print g_B.c" "There is no member( or method|) named c." | |
| gdb_test "print g_B.d" "There is no member( or method|) named d." | |
| gdb_test "print g_C.b" "There is no member( or method|) named b." | |
| gdb_test "print g_C.d" "There is no member( or method|) named d." | |
| gdb_test "print g_D.e" "There is no member( or method|) named e." | |
| } | |
| # Test access to names that are not members of any class. | |
| proc test_nonexistent_members {} { | |
| gdb_test "print g_A.y" "There is no member( or method|) named y." | |
| gdb_test "print g_B.z" "There is no member( or method|) named z." | |
| gdb_test "print g_C.q" "There is no member( or method|) named q." | |
| gdb_test "print g_D.p" "There is no member( or method|) named p." | |
| } | |
| # Call a method that expects a base class parameter with base, inherited, | |
| # and unrelated class arguments. | |
| proc test_method_param_class {} { | |
| gdb_test "call class_param.Aptr_a (&g_A)" ".* = 1" | |
| gdb_test "call class_param.Aptr_x (&g_A)" ".* = 2" | |
| gdb_test "call class_param.Aptr_a (&g_B)" ".* = 3" | |
| gdb_test "call class_param.Aptr_x (&g_B)" ".* = 4" | |
| gdb_test "call class_param.Aref_a (g_A)" ".* = 1" | |
| gdb_test "call class_param.Aref_x (g_A)" ".* = 2" | |
| gdb_test "call class_param.Aref_a (g_B)" ".* = 3" | |
| gdb_test "call class_param.Aref_x (g_B)" ".* = 4" | |
| gdb_test "call class_param.Aval_a (g_A)" ".* = 1" | |
| gdb_test "call class_param.Aval_x (g_A)" ".* = 2" | |
| gdb_test "call class_param.Aval_a (g_B)" ".* = 3" | |
| gdb_test "call class_param.Aval_x (g_B)" ".* = 4" | |
| gdb_test "call class_param.Aptr_a (&foo)" "Cannot resolve .*" "unrelated class *param" | |
| gdb_test "call class_param.Aref_a (foo)" "Cannot resolve .*" "unrelated class ¶m" | |
| gdb_test "call class_param.Aval_a (foo)" "Cannot resolve .*" "unrelated class param" | |
| } | |
| # Examine a class with an enum field. | |
| proc test_enums {} { | |
| global gdb_prompt | |
| global nl | |
| # print the object | |
| # We match the enum values with and without qualifiers. As of | |
| # 2008-08-21 we can output the qualifiers for DWARF-2. | |
| gdb_test "print obj_with_enum" \ | |
| "\\$\[0-9\]+ = \{priv_enum = (ClassWithEnum::)?red, x = 0\}" \ | |
| "print obj_with_enum (1)" | |
| # advance one line | |
| gdb_test "next" ".*" | |
| # print the object again | |
| gdb_test "print obj_with_enum" \ | |
| "\\$\[0-9\]+ = \{priv_enum = (ClassWithEnum::)?green, x = 0\}" \ | |
| "print obj_with_enum (2)" | |
| # print the enum member | |
| gdb_test "print obj_with_enum.priv_enum" "\\$\[0-9\]+ = (ClassWithEnum::)?green" | |
| # ptype on the enum member | |
| gdb_test_multiple "ptype obj_with_enum.priv_enum" "ptype obj_with_enum.priv_enum" { | |
| -re "type = enum ClassWithEnum::PrivEnum (: unsigned (int|short|char) )?\{ ?(ClassWithEnum::)?red, (ClassWithEnum::)?green, (ClassWithEnum::)?blue, (ClassWithEnum::)?yellow = 42 ?\}$nl$gdb_prompt $" { | |
| pass "ptype obj_with_enum.priv_enum" | |
| } | |
| -re "type = enum PrivEnum \{ ?(ClassWithEnum::)?red, (ClassWithEnum::)?green, (ClassWithEnum::)?blue, (ClassWithEnum::)?yellow = 42 ?\}$nl$gdb_prompt $" { | |
| # gcc 2.95.3 -gdwarf-2 | |
| # gcc 3.3.2 -gdwarf-2 | |
| pass "ptype obj_with_enum.priv_enum" | |
| } | |
| -re "type = enum \{ ?red, green, blue, yellow = 42 ?\}$nl$gdb_prompt $" { | |
| # This case case is a little dubious, but it's not clear what | |
| # ought to be required of a ptype on a private enum... | |
| # -sts 19990324 | |
| # | |
| # It bugs me that this happens with gcc 3. | |
| # -- chastain 2003-12-30 | |
| # | |
| # gcc 2.95.3 -gstabs+ | |
| # gcc 3.3.2 -gstabs+ | |
| # gcc HEAD 2003-12-28 21:08:30 UTC -gstabs+ | |
| pass "ptype obj_with_enum.priv_enum" | |
| } | |
| } | |
| # ptype on the object | |
| # NOTE: carlton/2003-02-28: One could certainly argue that plain | |
| # "PrivEnum" | |
| # is acceptable: PrivEnum is a member of ClassWithEnum, so | |
| # there's no need to explicitly qualify its name with | |
| # "ClassWithEnum::". The truth, though, is that GDB is simply | |
| # forgetting that PrivEnum is a member of ClassWithEnum, so we do | |
| # that output for a bad reason instead of a good reason. Under | |
| # stabs, we probably can't get this right; under DWARF-2, we can. | |
| cp_test_ptype_class \ | |
| "obj_with_enum" "" "class" "ClassWithEnum" \ | |
| { | |
| { field public "ClassWithEnum::PrivEnum priv_enum;" } | |
| { field public "int x;" } | |
| } \ | |
| "" \ | |
| { | |
| { | |
| "ClassWithEnum::PrivEnum priv_enum;" | |
| "PrivEnum priv_enum;" | |
| { setup_kfail "gdb/57" "*-*-*" } | |
| } | |
| } | |
| # I'll do this test two different ways, because of a parser bug. | |
| # See PR gdb/1588. | |
| gdb_test_multiple "print (ClassWithEnum::PrivEnum) 42" "print (ClassWithEnum::PrivEnum) 42" { | |
| -re "\\$\[0-9\]+ = (ClassWithEnum::)?yellow$nl$gdb_prompt $" { | |
| pass "print (ClassWithEnum::PrivEnum) 42" | |
| } | |
| -re "A (parse|syntax) error in expression, near `42'.$nl$gdb_prompt $" { | |
| # "parse error" is bison 1.35. | |
| # "syntax error" is bison 1.875. | |
| kfail "gdb/1588" "print (ClassWithEnum::PrivEnum) 42" | |
| } | |
| } | |
| gdb_test_multiple "print ('ClassWithEnum::PrivEnum') 42" "print ('ClassWithEnum::PrivEnum') 42" { | |
| -re "\\$\[0-9\]+ = (ClassWithEnum::)?yellow$nl$gdb_prompt $" { | |
| # gcc 3.3.2 -gstabs+ | |
| # gcc HEAD 2003-12-28 21:08:30 UTC -gstabs+ | |
| pass "print ('ClassWithEnum::PrivEnum') 42" | |
| } | |
| -re "No symbol \"ClassWithEnum::PrivEnum\" in current context.$nl$gdb_prompt $" { | |
| # gcc 2.95.3 -gdwarf-2 | |
| # gcc 3.3.2 -gdwarf-2 | |
| # gcc HEAD 2003-12-28 21:08:30 UTC -gdwarf-2 | |
| # gcc 2.95.3 -gstabs+ | |
| kfail "gdb/57" "print ('ClassWithEnum::PrivEnum') 42" | |
| } | |
| } | |
| } | |
| # Pointers to class members | |
| proc test_pointers_to_class_members {} { | |
| gdb_test "print Bar::z" "Cannot reference non-static field \"z\"" | |
| gdb_test "print &Foo::x" "\\$\[0-9\]+ = &Foo::x" | |
| gdb_test "print (int)&Foo::x" "\\$\[0-9\]+ = 0" | |
| gdb_test "print (int)&Bar::y == 2*sizeof(int)" "\\$\[0-9\]+ = true" | |
| gdb_test "ptype Bar::z" "type = int" | |
| gdb_test "ptype &Bar::z" "type = int Bar::\\*" | |
| # TODO: this is a bogus test. It's looking at a variable that | |
| # has not even been declared yet, so it's accessing random junk | |
| # on the stack and comparing that it's NOT equal to a specific | |
| # value. It's been like this since gdb 4.10 in 1993! | |
| # -- chastain 2004-01-01 | |
| gdb_test "print (int)pmi == sizeof(int)" ".* = false" | |
| } | |
| # Test static members. | |
| proc test_static_members {} { | |
| global hex | |
| gdb_test "print Foo::st" "\\$\[0-9\]+ = 100" | |
| gdb_test_no_output "set foo.st = 200" "" | |
| gdb_test "print bar.st" "\\$\[0-9\]+ = 200" | |
| gdb_test "print &foo.st" "\\$\[0-9\]+ = \\(int ?\\*\\) $hex <Foo::st>" | |
| gdb_test "print &Bar::st" "\\$\[0-9\]+ = \\(int ?\\*\\) $hex <Foo::st>" | |
| gdb_test "print *\$" "\\$\[0-9\]+ = 200" | |
| gdb_test_no_output "set print static-members off" | |
| gdb_test "print csi" \ | |
| "{x = 10, y = 20}" \ | |
| "print csi without static members" | |
| gdb_test "print cnsi" \ | |
| "{x = 30, y = 40}" \ | |
| "print cnsi without static members" | |
| gdb_test_no_output "set print static-members on" | |
| gdb_test "print csi" \ | |
| "{x = 10, y = 20, static null = {x = 0, y = 0, static null = <same as static member of an already seen type>}}" \ | |
| "print csi with static members" | |
| gdb_test "print cnsi" \ | |
| "{x = 30, y = 40, static null = {x = 0, y = 0, static null = <same as static member of an already seen type>, static yy = {z = 5, static xx = {x = 1, y = 2, static null = <same as static member of an already seen type>, static yy = <same as static member of an already seen type>}}}, static yy = <same as static member of an already seen type>}" \ | |
| "print cnsi with static members" | |
| # Another case of infinite recursion. | |
| gdb_test "print Outer::instance" \ | |
| "{inner = {static instance = {static instance = <same as static member of an already seen type>}}, static instance = {inner = {static instance = {static instance = <same as static member of an already seen type>}}, static instance = <same as static member of an already seen type>}}" \ | |
| "print recursive static member" | |
| } | |
| proc do_tests {} { | |
| global gdb_prompt | |
| global nl | |
| gdb_test_no_output "set language c++" "" | |
| gdb_test_no_output "set width 0" "" | |
| if ![runto_main ] then { | |
| perror "couldn't run to breakpoint" | |
| return | |
| } | |
| gdb_breakpoint inheritance2 | |
| gdb_test "continue" ".*Breakpoint .* inheritance2.*" "" | |
| test_ptype_class_objects | |
| test_non_inherited_member_access | |
| test_wrong_class_members | |
| test_nonexistent_members | |
| test_method_param_class | |
| gdb_breakpoint enums2 | |
| gdb_test "continue" ".*Breakpoint .* enums2.*" "continue to enums2(\\(\\)|)" | |
| # Leave enums2. Make sure we reach the next line, in case there | |
| # are any more instructions to finish the function call. | |
| gdb_test_multiple "finish" "" { | |
| -re "enums2 \\(\\);.*$gdb_prompt $" { | |
| gdb_test "next" ".*" "" | |
| } | |
| -re "$gdb_prompt $" { } | |
| } | |
| test_enums | |
| gdb_test "finish" ".*" "" | |
| test_pointers_to_class_members | |
| test_static_members | |
| # Now some random tests that were just thrown in here. | |
| gdb_test "print base1::Base1" "<.*Base1.*>" "print ctor of typedef class" | |
| gdb_test "print base1::~Base1" "<.*~Base1(\\(\\))?>" \ | |
| "print dtor of typedef class" | |
| gdb_test "list ByAnyOtherName::times" ".*int Foo::times.*" | |
| } | |
| do_tests |