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. gdb/testsuite * gdb.cp/typeid.cc: New file. * gdb.cp/typeid.exp: New file.
This commit is contained in:
parent
715c6909e2
commit
6e72ca205c
@ -1,3 +1,25 @@
|
||||
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
|
||||
|
2
gdb/NEWS
2
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
|
||||
|
@ -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:
|
||||
|
||||
|
13
gdb/c-exp.y
13
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
|
||||
|
20
gdb/cp-abi.c
20
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)
|
||||
{
|
||||
|
12
gdb/cp-abi.h
12
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);
|
||||
};
|
||||
|
17
gdb/eval.c
17
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
|
||||
|
@ -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:
|
||||
{
|
||||
|
158
gdb/gnu-v3-abi.c
158
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -1,3 +1,8 @@
|
||||
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.
|
||||
|
60
gdb/testsuite/gdb.cp/typeid.cc
Normal file
60
gdb/testsuite/gdb.cp/typeid.cc
Normal file
@ -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;
|
||||
}
|
67
gdb/testsuite/gdb.cp/typeid.exp
Normal file
67
gdb/testsuite/gdb.cp/typeid.exp
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user