aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog22
-rw-r--r--gdb/NEWS2
-rw-r--r--gdb/breakpoint.c1
-rw-r--r--gdb/c-exp.y13
-rw-r--r--gdb/cp-abi.c20
-rw-r--r--gdb/cp-abi.h12
-rw-r--r--gdb/eval.c17
-rw-r--r--gdb/expprint.c5
-rw-r--r--gdb/gnu-v3-abi.c158
-rw-r--r--gdb/parse.c1
-rw-r--r--gdb/std-operator.def3
-rw-r--r--gdb/testsuite/ChangeLog5
-rw-r--r--gdb/testsuite/gdb.cp/typeid.cc60
-rw-r--r--gdb/testsuite/gdb.cp/typeid.exp67
14 files changed, 385 insertions, 1 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index d96ab8b..8730b7d 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,27 @@
2013-04-15 Tom Tromey <tromey@redhat.com>
+ PR c++/9065:
+ * NEWS: Update.
+ * breakpoint.c (watchpoint_exp_is_const): Add OP_TYPEID.
+ * c-exp.y (TYPEID): New token.
+ (exp): Add new TYPEID productions.
+ (ident_tokens): Add "typeid".
+ * cp-abi.c (cplus_typeid, cplus_typeid_type): New functions.
+ * cp-abi.h (cplus_typeid, cplus_typeid_type): Declare.
+ (struct cp_abi_ops) <get_typeid, get_typeid_type>: New fields.
+ * eval.c (evaluate_subexp_standard) <OP_TYPEID>: New case.
+ * expprint.c (dump_subexp_body_standard) <OP_TYPEID>: New
+ case.
+ * gnu-v3-abi.c (std_type_info_gdbarch_data): New global.
+ (build_std_type_info_type, gnuv3_get_typeid_type)
+ (gnuv3_get_typeid): New functions.
+ (init_gnuv3_ops): Initialize std_type_info_gdbarch_data. Set
+ new fields on ABI object.
+ * parse.c (operator_length_standard) <OP_TYPEID>: New case.
+ * std-operator.def (OP_TYPEID): New.
+
+2013-04-15 Tom Tromey <tromey@redhat.com>
+
* elfread.c (elf_symtab_read): Install versioned symbol under
unversioned name as well.
diff --git a/gdb/NEWS b/gdb/NEWS
index 55f25d8..c78a4fb 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -29,6 +29,8 @@ show remote trace-status-packet
* Newly installed $prefix/bin/gcore acts as a shell interface for the
GDB command gcore.
+* GDB now implements the the C++ 'typeid' operator.
+
* MI changes
** The -trace-save MI command can optionally save trace buffer in Common
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index bd1ca5d..b0dd9b4 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -10317,6 +10317,7 @@ watchpoint_exp_is_const (const struct expression *exp)
case OP_TYPE:
case OP_TYPEOF:
case OP_DECLTYPE:
+ case OP_TYPEID:
case OP_NAME:
case OP_OBJC_NSSTRING:
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index c3c7f16..dd032d2 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -234,6 +234,7 @@ static void c_print_token (FILE *file, int type, YYSTYPE value);
%token ENTRY
%token TYPEOF
%token DECLTYPE
+%token TYPEID
/* Special type cases, put in to allow the parser to distinguish different
legal basetypes. */
@@ -346,6 +347,14 @@ exp : exp DECREMENT %prec UNARY
{ write_exp_elt_opcode (UNOP_POSTDECREMENT); }
;
+exp : TYPEID '(' exp ')' %prec UNARY
+ { write_exp_elt_opcode (OP_TYPEID); }
+ ;
+
+exp : TYPEID '(' type_exp ')' %prec UNARY
+ { write_exp_elt_opcode (OP_TYPEID); }
+ ;
+
exp : SIZEOF exp %prec UNARY
{ write_exp_elt_opcode (UNOP_SIZEOF); }
;
@@ -2290,7 +2299,9 @@ static const struct token ident_tokens[] =
{"__typeof", TYPEOF, OP_TYPEOF, 0 },
{"typeof", TYPEOF, OP_TYPEOF, FLAG_SHADOW },
{"__decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX },
- {"decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX | FLAG_SHADOW }
+ {"decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX | FLAG_SHADOW },
+
+ {"typeid", TYPEID, OP_TYPEID, FLAG_CXX}
};
/* When we find that lexptr (the global var defined in parse.c) is
diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c
index 8e9d545..7ac2a20 100644
--- a/gdb/cp-abi.c
+++ b/gdb/cp-abi.c
@@ -179,6 +179,26 @@ cplus_print_vtable (struct value *value)
(*current_cp_abi.print_vtable) (value);
}
+/* See cp-abi.h. */
+
+struct value *
+cplus_typeid (struct value *value)
+{
+ if (current_cp_abi.get_typeid == NULL)
+ error (_("GDB cannot find the typeid on this target"));
+ return (*current_cp_abi.get_typeid) (value);
+}
+
+/* See cp-abi.h. */
+
+struct type *
+cplus_typeid_type (struct gdbarch *gdbarch)
+{
+ if (current_cp_abi.get_typeid_type == NULL)
+ error (_("GDB cannot find the type for 'typeid' on this target"));
+ return (*current_cp_abi.get_typeid_type) (gdbarch);
+}
+
int
cp_pass_by_reference (struct type *type)
{
diff --git a/gdb/cp-abi.h b/gdb/cp-abi.h
index 96e9aea..d68e2ec 100644
--- a/gdb/cp-abi.h
+++ b/gdb/cp-abi.h
@@ -178,6 +178,16 @@ void cplus_make_method_ptr (struct type *type, gdb_byte *CONTENTS,
void cplus_print_vtable (struct value *value);
+/* Implement 'typeid': find the type info for VALUE, if possible. If
+ the type info cannot be found, throw an exception. */
+
+extern struct value *cplus_typeid (struct value *value);
+
+/* Return the type of 'typeid' for the current C++ ABI on the given
+ architecture. */
+
+extern struct type *cplus_typeid_type (struct gdbarch *gdbarch);
+
/* Determine if we are currently in a C++ thunk. If so, get the
address of the routine we are thunking to and continue to there
instead. */
@@ -219,6 +229,8 @@ struct cp_abi_ops
struct value * (*method_ptr_to_value) (struct value **,
struct value *);
void (*print_vtable) (struct value *);
+ struct value *(*get_typeid) (struct value *value);
+ struct type *(*get_typeid_type) (struct gdbarch *gdbarch);
CORE_ADDR (*skip_trampoline) (struct frame_info *, CORE_ADDR);
int (*pass_by_reference) (struct type *type);
};
diff --git a/gdb/eval.c b/gdb/eval.c
index a91ba22..f04baee 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2798,6 +2798,23 @@ evaluate_subexp_standard (struct type *expect_type,
else
error (_("Attempt to use a type as an expression"));
+ case OP_TYPEID:
+ {
+ struct value *result;
+ enum exp_opcode sub_op = exp->elts[*pos].opcode;
+
+ if (sub_op == OP_TYPE || sub_op == OP_DECLTYPE || sub_op == OP_TYPEOF)
+ result = evaluate_subexp (NULL_TYPE, exp, pos,
+ EVAL_AVOID_SIDE_EFFECTS);
+ else
+ result = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+ if (noside != EVAL_NORMAL)
+ return allocate_value (cplus_typeid_type (exp->gdbarch));
+
+ return cplus_typeid (result);
+ }
+
default:
/* Removing this case and compiling with gcc -Wall reveals that
a lot of cases are hitting this case. Some of these should
diff --git a/gdb/expprint.c b/gdb/expprint.c
index 6905534..ea9b560 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -953,6 +953,11 @@ dump_subexp_body_standard (struct expression *exp,
elt = dump_subexp (exp, stream, elt);
fprintf_filtered (stream, ")");
break;
+ case OP_TYPEID:
+ fprintf_filtered (stream, "typeid (");
+ elt = dump_subexp (exp, stream, elt);
+ fprintf_filtered (stream, ")");
+ break;
case STRUCTOP_STRUCT:
case STRUCTOP_PTR:
{
diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c
index b3585a6..7649a48 100644
--- a/gdb/gnu-v3-abi.c
+++ b/gdb/gnu-v3-abi.c
@@ -34,6 +34,12 @@
static struct cp_abi_ops gnu_v3_abi_ops;
+/* A gdbarch key for std::type_info, in the event that it can't be
+ found in the debug info. */
+
+static struct gdbarch_data *std_type_info_gdbarch_data;
+
+
static int
gnuv3_is_vtable_name (const char *name)
{
@@ -984,6 +990,154 @@ gnuv3_print_vtable (struct value *value)
do_cleanups (cleanup);
}
+/* Return a GDB type representing `struct std::type_info', laid out
+ appropriately for ARCH.
+
+ We use this function as the gdbarch per-architecture data
+ initialization function. */
+
+static void *
+build_std_type_info_type (struct gdbarch *arch)
+{
+ struct type *t;
+ struct field *field_list, *field;
+ int offset;
+ struct type *void_ptr_type
+ = builtin_type (arch)->builtin_data_ptr;
+ struct type *char_type
+ = builtin_type (arch)->builtin_char;
+ struct type *char_ptr_type
+ = make_pointer_type (make_cv_type (1, 0, char_type, NULL), NULL);
+
+ field_list = xmalloc (sizeof (struct field [2]));
+ memset (field_list, 0, sizeof (struct field [2]));
+ field = &field_list[0];
+ offset = 0;
+
+ /* The vtable. */
+ FIELD_NAME (*field) = "_vptr.type_info";
+ FIELD_TYPE (*field) = void_ptr_type;
+ SET_FIELD_BITPOS (*field, offset * TARGET_CHAR_BIT);
+ offset += TYPE_LENGTH (FIELD_TYPE (*field));
+ field++;
+
+ /* The name. */
+ FIELD_NAME (*field) = "__name";
+ FIELD_TYPE (*field) = char_ptr_type;
+ SET_FIELD_BITPOS (*field, offset * TARGET_CHAR_BIT);
+ offset += TYPE_LENGTH (FIELD_TYPE (*field));
+ field++;
+
+ gdb_assert (field == (field_list + 2));
+
+ t = arch_type (arch, TYPE_CODE_STRUCT, offset, NULL);
+ TYPE_NFIELDS (t) = field - field_list;
+ TYPE_FIELDS (t) = field_list;
+ TYPE_TAG_NAME (t) = "gdb_gnu_v3_type_info";
+ INIT_CPLUS_SPECIFIC (t);
+
+ return t;
+}
+
+/* Implement the 'get_typeid_type' method. */
+
+static struct type *
+gnuv3_get_typeid_type (struct gdbarch *gdbarch)
+{
+ struct symbol *typeinfo;
+ struct type *typeinfo_type;
+
+ typeinfo = lookup_symbol ("std::type_info", NULL, STRUCT_DOMAIN, NULL);
+ if (typeinfo == NULL)
+ typeinfo_type = gdbarch_data (gdbarch, std_type_info_gdbarch_data);
+ else
+ typeinfo_type = SYMBOL_TYPE (typeinfo);
+
+ return typeinfo_type;
+}
+
+/* Implement the 'get_typeid' method. */
+
+static struct value *
+gnuv3_get_typeid (struct value *value)
+{
+ struct type *typeinfo_type;
+ struct type *type;
+ struct gdbarch *gdbarch;
+ struct cleanup *cleanup;
+ struct value *result;
+ char *typename, *canonical;
+
+ /* We have to handle values a bit trickily here, to allow this code
+ to work properly with non_lvalue values that are really just
+ disguised types. */
+ if (value_lval_const (value) == lval_memory)
+ value = coerce_ref (value);
+
+ type = check_typedef (value_type (value));
+
+ /* In the non_lvalue case, a reference might have slipped through
+ here. */
+ if (TYPE_CODE (type) == TYPE_CODE_REF)
+ type = check_typedef (TYPE_TARGET_TYPE (type));
+
+ /* Ignore top-level cv-qualifiers. */
+ type = make_cv_type (0, 0, type, NULL);
+ gdbarch = get_type_arch (type);
+
+ typename = type_to_string (type);
+ if (typename == NULL)
+ error (_("cannot find typeinfo for unnamed type"));
+ cleanup = make_cleanup (xfree, typename);
+
+ /* We need to canonicalize the type name here, because we do lookups
+ using the demangled name, and so we must match the format it
+ uses. E.g., GDB tends to use "const char *" as a type name, but
+ the demangler uses "char const *". */
+ canonical = cp_canonicalize_string (typename);
+ if (canonical != NULL)
+ {
+ make_cleanup (xfree, canonical);
+ typename = canonical;
+ }
+
+ typeinfo_type = gnuv3_get_typeid_type (gdbarch);
+
+ /* We check for lval_memory because in the "typeid (type-id)" case,
+ the type is passed via a not_lval value object. */
+ if (TYPE_CODE (type) == TYPE_CODE_CLASS
+ && value_lval_const (value) == lval_memory
+ && gnuv3_dynamic_class (type))
+ {
+ struct value *vtable, *typeinfo_value;
+ CORE_ADDR address = value_address (value) + value_embedded_offset (value);
+
+ vtable = gnuv3_get_vtable (gdbarch, type, address);
+ if (vtable == NULL)
+ error (_("cannot find typeinfo for object of type '%s'"), typename);
+ typeinfo_value = value_field (vtable, vtable_field_type_info);
+ result = value_ind (value_cast (make_pointer_type (typeinfo_type, NULL),
+ typeinfo_value));
+ }
+ else
+ {
+ char *sym_name;
+ struct minimal_symbol *minsym;
+
+ sym_name = concat ("typeinfo for ", typename, (char *) NULL);
+ make_cleanup (xfree, sym_name);
+ minsym = lookup_minimal_symbol (sym_name, NULL, NULL);
+
+ if (minsym == NULL)
+ error (_("could not find typeinfo symbol for '%s'"), typename);
+
+ result = value_at_lazy (typeinfo_type, SYMBOL_VALUE_ADDRESS (minsym));
+ }
+
+ do_cleanups (cleanup);
+ return result;
+}
+
/* Determine if we are currently in a C++ thunk. If so, get the address
of the routine we are thunking to and continue to there instead. */
@@ -1116,6 +1270,8 @@ init_gnuv3_ops (void)
{
vtable_type_gdbarch_data
= gdbarch_data_register_post_init (build_gdb_vtable_type);
+ std_type_info_gdbarch_data
+ = gdbarch_data_register_post_init (build_std_type_info_type);
gnu_v3_abi_ops.shortname = "gnu-v3";
gnu_v3_abi_ops.longname = "GNU G++ Version 3 ABI";
@@ -1134,6 +1290,8 @@ init_gnuv3_ops (void)
gnu_v3_abi_ops.make_method_ptr = gnuv3_make_method_ptr;
gnu_v3_abi_ops.method_ptr_to_value = gnuv3_method_ptr_to_value;
gnu_v3_abi_ops.print_vtable = gnuv3_print_vtable;
+ gnu_v3_abi_ops.get_typeid = gnuv3_get_typeid;
+ gnu_v3_abi_ops.get_typeid_type = gnuv3_get_typeid_type;
gnu_v3_abi_ops.skip_trampoline = gnuv3_skip_trampoline;
gnu_v3_abi_ops.pass_by_reference = gnuv3_pass_by_reference;
}
diff --git a/gdb/parse.c b/gdb/parse.c
index aa8f09c..13699a8 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -972,6 +972,7 @@ operator_length_standard (const struct expression *expr, int endpos,
case UNOP_TRUNC:
case OP_TYPEOF:
case OP_DECLTYPE:
+ case OP_TYPEID:
oplen = 1;
args = 1;
break;
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 467c141..c4f33d9 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -316,3 +316,6 @@ OP (OP_TYPEOF)
evaluated solely for its type. This is similar to typeof, but has
slight different semantics. */
OP (OP_DECLTYPE)
+
+/* The typeid operator. This has one expression argument. */
+OP (OP_TYPEID)
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 6526980..7da3ff0 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,10 @@
2013-04-15 Tom Tromey <tromey@redhat.com>
+ * gdb.cp/typeid.cc: New file.
+ * gdb.cp/typeid.exp: New file.
+
+2013-04-15 Tom Tromey <tromey@redhat.com>
+
* gdb.cp/exception.exp: Add "catch rethrow" tests.
2013-04-13 Yao Qi <yao@codesourcery.com>
diff --git a/gdb/testsuite/gdb.cp/typeid.cc b/gdb/testsuite/gdb.cp/typeid.cc
new file mode 100644
index 0000000..7120031
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/typeid.cc
@@ -0,0 +1,60 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013 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/>. */
+
+#include <typeinfo>
+
+int i;
+char *cp;
+const char *ccp;
+char ca[5];
+
+struct Base
+{
+ virtual ~Base() { }
+};
+
+struct VB1 : public virtual Base
+{
+};
+
+struct VB2 : public virtual Base
+{
+};
+
+struct Derived : public VB1, VB2
+{
+};
+
+Derived d;
+
+Base *b = &d;
+VB1 *vb1 = &d;
+VB1 *vb2 = &d;
+
+const Base *bv = &d;
+
+int main ()
+{
+ const std::type_info &xi = typeid(i);
+ const std::type_info &xcp = typeid(cp);
+ const std::type_info &xccp = typeid(ccp);
+ const std::type_info &xca = typeid(ca);
+ const std::type_info &xd = typeid(d);
+ const std::type_info &xb = typeid(b);
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/typeid.exp b/gdb/testsuite/gdb.cp/typeid.exp
new file mode 100644
index 0000000..2096ad9
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/typeid.exp
@@ -0,0 +1,67 @@
+# Copyright 2013 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/>.
+
+standard_testfile .cc
+
+if {[skip_cplus_tests]} {
+ return -1
+}
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+ return -1
+}
+
+proc do_typeid_tests {started} {
+ global hex
+
+ # We might see the standard type or gdb's internal type.
+ set type_re "(std::type_info|struct gdb_gnu_v3_type_info)"
+
+
+ foreach simple_var {i cp ccp ca b} {
+ gdb_test "print &typeid($simple_var)" \
+ " = \\($type_re \\*\\) $hex.*"
+
+ # Note that we test pointer equality rather than object
+ # equality here. That is because std::type_info's operator==
+ # is not present in the libstdc++ .so.
+ gdb_test "print &typeid($simple_var) == &typeid(typeof($simple_var))" \
+ " = true"
+ }
+
+ # typeid for these is Derived. Don't try these tests until the
+ # inferior has started.
+ if {$started} {
+ foreach der_var {*b *vb1 *vb2 *bv d {const Derived} {const Derived &}} {
+ gdb_test "print &typeid($der_var)" \
+ " = \\($type_re \\*\\) $hex.*"
+ gdb_test "print &typeid($der_var) == &typeid(d)" \
+ " = true"
+ }
+ }
+}
+
+with_test_prefix "before starting" {
+ do_typeid_tests 0
+}
+
+if ![runto_main] {
+ untested typeid
+ return -1
+}
+
+with_test_prefix "after starting" {
+ do_typeid_tests 1
+}