This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
pr9065 patch
- From: Chris Moller <cmoller at redhat dot com>
- To: gdb-patches at sourceware dot org
- Date: Tue, 09 Mar 2010 15:38:30 -0500
- Subject: pr9065 patch
This is the typeid thing.
This patch implements typeid in gdb, letting users do things like
p typeid(<expression>)
p typeid(<expression>).name()
p typeid(<expression>).__name
p typeid(<expression>).__is_pointer_()
and the same thing for types (i.e., p typeid(<type>).name())
FWIW, you can even do stuff like
set tp = typeid(<expression>).name()
(assuming there's a "char *tp;" in your source...)
There are some differences, though, between the gdb typeid and the c++
typeid, the biggest is that the gdb version only partially implements
the type_info class: it doesn't actually implement the methods, it just
fakes them, and even though you can assign things like "set tp =
typeid(<expression>).name()" you can't assign "set ti =
typeid(<expression>)" where "type_info *ti;"
Another difference is that the real typeid returns mangled names,
leaving it to the user to demangle. This strikes me as a pain, so I
just provide the demangled names.
The attached patch works as advertised--see pr9065.exp for more
details--but I haven't done a full regression yet.
Feedback welcome,
Chris
? testsuite/gdb.cp/pr9065
Index: c-exp.y
===================================================================
RCS file: /cvs/src/src/gdb/c-exp.y,v
retrieving revision 1.70
diff -u -r1.70 c-exp.y
--- c-exp.y 10 Feb 2010 18:57:21 -0000 1.70
+++ c-exp.y 9 Mar 2010 20:08:47 -0000
@@ -161,6 +161,7 @@
%}
%type <voidval> exp exp1 type_exp start variable qualified_name lcurly
+%type <lval> opt_args
%type <lval> rcurly
%type <tval> type typebase qualified_type
%type <tvec> nonempty_typelist
@@ -201,7 +202,7 @@
%token <ssym> NAME_OR_INT
%token OPERATOR
-%token STRUCT CLASS UNION ENUM SIZEOF UNSIGNED COLONCOLON
+%token STRUCT CLASS UNION ENUM SIZEOF UNSIGNED COLONCOLON TYPEID
%token TEMPLATE
%token ERROR
%token NEW DELETE
@@ -307,6 +308,41 @@
{ write_exp_elt_opcode (UNOP_SIZEOF); }
;
+exp : TYPEID exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_TYPEID_VAR); }
+ ;
+
+exp : TYPEID '(' type ')' %prec UNARY
+ { write_exp_elt_opcode (OP_TYPE);
+ CHECK_TYPEDEF ($3);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (OP_TYPE);
+ write_exp_elt_opcode (UNOP_TYPEID_TYPE);
+ write_exp_elt_longcst (0);
+ write_exp_elt_longcst ((LONGEST) 1);
+ write_exp_elt_opcode (UNOP_TYPEID_TYPE);}
+ ;
+
+exp : TYPEID '(' type ')' '.' name opt_args %prec UNARY
+ { write_exp_elt_opcode (OP_STRING);
+ write_exp_string ($6);
+ write_exp_elt_opcode (OP_STRING);
+ write_exp_elt_opcode (OP_TYPE);
+ CHECK_TYPEDEF ($3);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (OP_TYPE);
+ write_exp_elt_opcode (UNOP_TYPEID_TYPE);
+ write_exp_elt_longcst ($7);
+ write_exp_elt_longcst ((LONGEST) 2);
+ write_exp_elt_opcode (UNOP_TYPEID_TYPE);}
+ ;
+
+opt_args : /* nothing */
+ { $$ = 0; }
+ | '(' ')'
+ { $$ = 1; }
+ ;
+
exp : exp ARROW name
{ write_exp_elt_opcode (STRUCTOP_PTR);
write_exp_string ($3);
@@ -1897,6 +1933,7 @@
{"struct", STRUCT, OP_NULL, 0},
{"signed", SIGNED_KEYWORD, OP_NULL, 0},
{"sizeof", SIZEOF, OP_NULL, 0},
+ {"typeid", TYPEID, OP_NULL, 0},
{"double", DOUBLE_KEYWORD, OP_NULL, 0},
{"false", FALSEKEYWORD, OP_NULL, 1},
{"class", CLASS, OP_NULL, 1},
Index: eval.c
===================================================================
RCS file: /cvs/src/src/gdb/eval.c,v
retrieving revision 1.127
diff -u -r1.127 eval.c
--- eval.c 8 Feb 2010 20:55:42 -0000 1.127
+++ eval.c 9 Mar 2010 20:08:50 -0000
@@ -43,6 +43,7 @@
#include "gdb_obstack.h"
#include "objfiles.h"
#include "python/python.h"
+#include "gdbcore.h"
#include "gdb_assert.h"
@@ -676,6 +677,314 @@
return type;
}
+static struct value *
+create_type_info_class(char *type_name)
+{
+ int i, nfields;
+ int bitpos = -1;
+ struct type *typeid_type;
+
+ typeid_type = lookup_struct ("std::type_info", NULL);
+
+ for (i = 0; i < TYPE_NFIELDS (typeid_type); i++)
+ {
+ if (!strcmp ("__name", TYPE_FIELD_NAME (typeid_type, i)))
+ {
+ bitpos = TYPE_FIELD_BITPOS(typeid_type, i);
+ break;
+ }
+ }
+ if (bitpos != -1)
+ {
+ struct value *inferior_val;
+ CORE_ADDR inferior_addr;
+ struct value *rv;
+ struct value *val;
+
+ val = allocate_value (typeid_type);
+
+ inferior_val = value_allocate_space_in_inferior (1 + strlen (type_name));
+ inferior_addr = value_as_address (inferior_val);
+ write_memory (inferior_addr,
+ type_name, strlen (type_name));
+
+ memcpy (value_contents_raw (val) + bitpos/8,
+ &inferior_addr, sizeof(inferior_addr));
+ rv = value_coerce_to_target (val);
+
+ return rv;
+ }
+ else error (_ ("Malformed type_info class"));
+}
+
+static struct value *
+fake_type_info_class(char *type_name, char *field_name, struct expression *exp,
+ int is_pointer, int is_function, int field_has_args)
+{
+ int i, nelms;
+ struct type *typeid_type;
+
+ /*
+ We can't really evaluate typeid(<var>).<member> because it doesn't
+ really exist and creating it would be a pain. So we're just going
+ to assume the structure of class type_info is invariant and fake
+ the bits it that need faking.
+ */
+
+ typeid_type = lookup_struct ("std::type_info", NULL);
+
+ nelms = TYPE_NFIELDS (typeid_type);
+
+ for (i = 0; i < nelms; i++)
+ {
+ struct type *ft = TYPE_FIELD_TYPE(typeid_type, i);
+ int is_func = (TYPE_CODE (ft) == TYPE_CODE_METHOD
+ || TYPE_CODE (ft) == TYPE_CODE_FUNC);
+ if (!strcmp (field_name, TYPE_FIELD_NAME(typeid_type, i))
+ && is_func == field_has_args)
+ {
+ typeid_type = language_string_char_type (exp->language_defn,
+ exp->gdbarch);
+ return value_cstring (type_name, 1+strlen (type_name),
+ typeid_type);
+ }
+ }
+
+ nelms = TYPE_NFN_FIELDS_TOTAL (typeid_type);
+
+ for (i = 0; i < nelms; i++)
+ {
+ struct fn_field *fn_fields;
+ struct type *ft;
+ int is_func;
+
+ fn_fields = TYPE_FN_FIELDLIST1(typeid_type, i);
+ ft = TYPE_FN_FIELD_TYPE(fn_fields, 0);
+ is_func = (TYPE_CODE (ft) == TYPE_CODE_METHOD
+ || TYPE_CODE (ft) == TYPE_CODE_FUNC);
+
+ if (!strcmp (field_name, TYPE_FN_FIELDLIST_NAME(typeid_type, i))
+ && is_func == field_has_args)
+ {
+ /*
+ We're going to make the assumption that class typeinfo
+ isn't going to change. Given that, we need to handle:
+
+ const char* name()
+ char *__name;
+ ==> return type string
+
+ bool before(const type_info& __arg)
+ ==> warn not supported
+
+ bool __is_pointer_p()
+ bool __is_function_p()
+ ==> return a bool
+
+ The == and != operators won't appear here--maybe do
+ something about them somewhere else.
+ */
+
+ if (!strcmp (field_name, "name"))
+ {
+ typeid_type = language_string_char_type (exp->language_defn,
+ exp->gdbarch);
+ return value_cstring (type_name, 1+strlen (type_name),
+ typeid_type);
+ }
+ else if (!strcmp (field_name, "__is_pointer_p"))
+ {
+ return
+ value_from_longest (builtin_type (exp->gdbarch)->builtin_bool,
+ (LONGEST)is_pointer);
+ }
+ else if (!strcmp (field_name, "__is_function_p"))
+ {
+ return
+ value_from_longest (builtin_type (exp->gdbarch)->builtin_bool,
+ (LONGEST)is_function);
+ }
+ break;
+ }
+ }
+ error (_("%s is not a member of class type_info"), field_name);
+}
+
+static struct value *
+evaluate_subexp_for_typeid_type (struct expression *exp, int *pos)
+ __attribute__ ((noinline));
+
+static struct value *
+evaluate_subexp_for_typeid_type (struct expression *exp, int *pos)
+{
+ char *type_name;
+ struct type *type;
+ int pc;
+ int nargs;
+ struct type *typeid_type;
+ char *field_name;
+ int field_has_args;
+ int is_pointer = 0, is_function = 0;
+
+ pc = (*pos);
+ (*pos) += 7;
+
+ nargs = longest_to_int (exp->elts[pc + 1].longconst);
+ field_has_args = longest_to_int (exp->elts[pc].longconst);
+
+ if (nargs == 1)
+ {
+ type = exp->elts[pc + 4].type;
+ field_name = NULL;
+ }
+ else
+ {
+ int tpos = pc + 3;
+ type = exp->elts[pc + 9].type;
+ exp->elts[tpos].opcode = exp->elts[tpos+7].opcode = STRUCTOP_STRUCT;
+ field_name = extract_field_op (exp, &tpos);
+ }
+
+ if (TYPE_NAME (type))
+ type_name = TYPE_NAME (type);
+ else if (TYPE_TAG_NAME (type))
+ type_name = TYPE_TAG_NAME (type);
+ else
+ type_name = "<anonymous>";
+
+ if (nargs == 1)
+ return create_type_info_class(type_name);
+ else
+ return fake_type_info_class(type_name, field_name, exp,
+ is_pointer, is_function, field_has_args);
+}
+
+static struct value *
+evaluate_subexp_for_typeid_var (struct expression *exp, int *pos)
+{
+ enum exp_opcode op;
+ char *type_name = NULL;
+ struct type *typeid_type;
+ struct type *type = NULL;
+ struct value *val;
+ int show_deref = 0;
+ int is_pointer = 0;
+ int is_function = 0;
+ char *field_name = NULL;
+ struct type *symbol_type = NULL;
+ int has_args = 0;
+
+ if (exp->elts[*pos].opcode == OP_FUNCALL)
+ {
+
+ gdb_assert (longest_to_int (exp->elts[*pos + 1].longconst == 0));
+ *pos += 3; /* Skip past the OP_FUNCALL. */
+
+ gdb_assert (exp->elts[*pos].opcode == STRUCTOP_STRUCT);
+
+ field_name = extract_field_op (exp, pos);
+ has_args = 1;
+ }
+ else if (exp->elts[*pos].opcode == STRUCTOP_STRUCT)
+ field_name = extract_field_op (exp, pos);
+
+ if (exp->elts[*pos].opcode == OP_VAR_VALUE)
+ {
+ struct symbol * sym = exp->elts[*pos + 2].symbol;
+ symbol_type = SYMBOL_TYPE (sym);
+ }
+
+ if (exp->elts[*pos].opcode == OP_TYPE)
+ {
+ type = exp->elts[*pos + 1].type;
+ *pos += 3;
+ }
+ else
+ {
+ val = evaluate_subexp (NULL_TYPE, exp, pos,
+ EVAL_AVOID_SIDE_EFFECTS);
+
+ type = check_typedef (value_type (val));
+ }
+
+ if (TYPE_CODE_REF == TYPE_CODE (type)
+ || TYPE_CODE_PTR == TYPE_CODE (type)
+ || TYPE_CODE_ARRAY == TYPE_CODE (type))
+ {
+ struct type *target_type = TYPE_TARGET_TYPE(type);
+ type_name = TYPE_NAME (target_type);
+ is_pointer = 1;
+ show_deref = 1;
+ }
+ else if (TYPE_CODE_FUNC == TYPE_CODE (type))
+ {
+ struct type *target_type = TYPE_TARGET_TYPE(type);
+
+ if (TYPE_NAME (target_type))
+ type_name = TYPE_NAME (target_type);
+ else if (TYPE_TAG_NAME (target_type))
+ type_name = TYPE_TAG_NAME (target_type);
+ else
+ {
+ if (symbol_type)
+ {
+ target_type = TYPE_TARGET_TYPE(symbol_type);
+ if (target_type && TYPE_CODE_PTR == TYPE_CODE (target_type))
+ {
+ target_type = TYPE_TARGET_TYPE(target_type);
+ if (TYPE_NAME (target_type))
+ type_name = TYPE_NAME (target_type);
+ else if (TYPE_TAG_NAME (target_type))
+ type_name = TYPE_TAG_NAME (target_type);
+ is_pointer = 1;
+ show_deref = 1;
+ }
+ else
+ {
+ if (TYPE_NAME (target_type))
+ type_name = TYPE_NAME (target_type);
+ else if (TYPE_TAG_NAME (target_type))
+ type_name = TYPE_TAG_NAME (target_type);
+ }
+ }
+ }
+ if (!type_name) type_name = "<anonymous>";
+ is_function = 1;
+ }
+ else
+ {
+ if (TYPE_NAME (type))
+ type_name = TYPE_NAME (type);
+ else if (TYPE_TAG_NAME (type))
+ type_name = TYPE_TAG_NAME (type);
+ else
+ type_name = "<anonymous>";
+ }
+
+ if (TYPE_CODE_STRUCT == TYPE_CODE (type))
+ {
+#define STRUCT_LBL "struct "
+ char * tmp = alloca (1 + strlen (STRUCT_LBL) + strlen (type_name));
+ strcpy (tmp, STRUCT_LBL);
+ strcat (tmp, type_name);
+ type_name = tmp;
+ }
+
+ if (show_deref) {
+#define DEREF_STAR " *"
+ char * tmp = alloca (1 + strlen (DEREF_STAR) + strlen (type_name));
+ strcpy (tmp, type_name);
+ strcat (tmp, DEREF_STAR);
+ type_name = tmp;
+ }
+
+ if (field_name == NULL)
+ return create_type_info_class(type_name);
+ else
+ return fake_type_info_class(type_name, field_name, exp,
+ is_pointer, is_function, has_args);
+}
+
struct value *
evaluate_subexp_standard (struct type *expect_type,
struct expression *exp, int *pos,
@@ -2468,6 +2777,23 @@
}
return evaluate_subexp_for_sizeof (exp, pos);
+ case UNOP_TYPEID_TYPE:
+ if (noside == EVAL_SKIP)
+ {
+ evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
+ goto nosideret;
+ }
+ return evaluate_subexp_for_typeid_type (exp, pos);
+
+ case UNOP_TYPEID_VAR:
+ if (noside == EVAL_SKIP)
+ {
+ evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
+ goto nosideret;
+ }
+ return evaluate_subexp_for_typeid_var (exp, pos);
+ break;
+
case UNOP_CAST:
(*pos) += 2;
type = exp->elts[pc + 1].type;
Index: expression.h
===================================================================
RCS file: /cvs/src/src/gdb/expression.h,v
retrieving revision 1.34
diff -u -r1.34 expression.h
--- expression.h 18 Jan 2010 20:54:33 -0000 1.34
+++ expression.h 9 Mar 2010 20:08:50 -0000
@@ -265,6 +265,8 @@
UNOP_PREDECREMENT, /* -- before an expression */
UNOP_POSTDECREMENT, /* -- after an expression */
UNOP_SIZEOF, /* Unary sizeof (followed by expression) */
+ UNOP_TYPEID_TYPE, /* Unary typeid (followed by expression) */
+ UNOP_TYPEID_VAR, /* Unary typeid (followed by expression) */
UNOP_PLUS, /* Unary plus */
Index: parse.c
===================================================================
RCS file: /cvs/src/src/gdb/parse.c,v
retrieving revision 1.95
diff -u -r1.95 parse.c
--- parse.c 10 Feb 2010 18:57:21 -0000 1.95
+++ parse.c 9 Mar 2010 20:08:51 -0000
@@ -863,6 +863,16 @@
oplen = 3;
break;
+ case UNOP_TYPEID_TYPE:
+ oplen = 4;
+ args = longest_to_int (expr->elts[endpos - 2].longconst);
+ break;
+
+ case UNOP_TYPEID_VAR:
+ oplen = 1;
+ args = 1;
+ break;
+
case BINOP_VAL:
case UNOP_CAST:
case UNOP_DYNAMIC_CAST:
Index: testsuite/gdb.cp/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.cp/Makefile.in,v
retrieving revision 1.9
diff -u -r1.9 Makefile.in
--- testsuite/gdb.cp/Makefile.in 8 Feb 2010 18:27:53 -0000 1.9
+++ testsuite/gdb.cp/Makefile.in 9 Mar 2010 20:08:53 -0000
@@ -5,7 +5,7 @@
derivation inherit local member-ptr method misc \
overload ovldbreak ref-typ ref-typ2 templates userdef virtfunc namespace \
ref-types ref-params method2 pr9594 gdb2495 virtfunc2 pr9067 \
- pr1072
+ pr1072 pr9065
all info install-info dvi install uninstall installcheck check:
@echo "Nothing to be done for $@..."
Index: testsuite/gdb.cp/pr9065.cc
===================================================================
RCS file: testsuite/gdb.cp/pr9065.cc
diff -N testsuite/gdb.cp/pr9065.cc
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.cp/pr9065.cc 9 Mar 2010 20:08:54 -0000
@@ -0,0 +1,55 @@
+using namespace std;
+#include <stdio.h>
+#include <iostream>
+#include <typeinfo>
+
+struct vs {
+ int i;
+ const char * c;
+};
+
+class cs {
+private:
+ double ff;
+public:
+ double getval() { return ff; }
+};
+
+typedef int * iptr;
+typedef double dbl;
+typedef struct vs vvs;
+
+char *
+dummy_fcn()
+{
+ return NULL;
+}
+
+double
+dummy_dbl()
+{
+ return 8.5;
+}
+
+int
+main()
+{
+ char tpp[] = { "hi there" };
+ const char * tp;
+ int gh;
+ int myArray[10];
+ struct vs vsi;
+
+ iptr iptri = NULL;
+ dbl dbli;
+ vvs vvsi;
+
+ cs csi;
+
+ tp = "bye bye";
+
+ vsi.i = 8888;
+ vsi.c = "garbage";
+
+ return 0; // bp marker
+}
Index: testsuite/gdb.cp/pr9065.exp
===================================================================
RCS file: testsuite/gdb.cp/pr9065.exp
diff -N testsuite/gdb.cp/pr9065.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.cp/pr9065.exp 9 Mar 2010 20:08:54 -0000
@@ -0,0 +1,73 @@
+# Copyright 2010 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/>.
+
+set testfile pr9065
+set srcfile ${testfile}.cc
+if [prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}] {
+ return -1
+}
+
+if ![runto_main] {
+ untested pr9065
+ return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "bp marker"]
+gdb_continue_to_breakpoint "bp marker"
+
+gdb_test "p typeid(tp).name()" "char \\*.*"
+gdb_test "p typeid(*tp).name()" "char.*"
+gdb_test "p typeid(tp\[0\]).name()" "char.*"
+gdb_test "p typeid(tp\[0\]).__is_pointer_p()" "false.*"
+gdb_test "p typeid(tp).__is_pointer_p()" "true.*"
+gdb_test "p typeid(tp).__is_function_p()" "false.*"
+
+gdb_test "p typeid(tpp).name()" "char \\*.*"
+gdb_test "p typeid(*tpp).name()" "char.*"
+gdb_test "p typeid(tpp\[0\]).__name" "char.*"
+
+gdb_test "p typeid(gh).name()" "int.*"
+
+gdb_test "p typeid(myArray).name()" "int \\*.*"
+gdb_test "p typeid(*myArray).name()" "int.*"
+gdb_test "p typeid(myArray\[0\]).name()" "int.*"
+
+gdb_test "p typeid(vsi).name()" "struct vs.*"
+gdb_test "p typeid(vsi.i).__name" "int.*"
+gdb_test "p typeid(vsi.c).name()" "char \\*.*"
+
+gdb_test "p typeid(main).name()" "int.*"
+gdb_test "p typeid(main).__is_pointer_p()" "false.*"
+gdb_test "p typeid(main).__is_function_p()" "true.*"
+
+gdb_test "p typeid(vvsi).name()" "struct vs.*"
+
+gdb_test "p typeid(dbli).name()" "double.*"
+gdb_test "p typeid(dbli).__is_pointer_p()" "false.*"
+gdb_test "p typeid(dbli).__is_function_p()" "false.*"
+
+gdb_test "p typeid(iptri).__name" "int \\*.*"
+gdb_test "p typeid(iptri).__is_pointer_p()" "true.*"
+gdb_test "p typeid(iptri).__is_function_p()" "false.*"
+gdb_test "p typeid(iptri)" "_vptr.type_info = 0x0, __name.*int \\*.*"
+
+gdb_test "p typeid(dummy_fcn).__name" "char \\*.*"
+gdb_test "p typeid(dummy_fcn).__is_function_p()" "true.*"
+gdb_test "p typeid(dummy_fcn).__is_pointer_p()" "true.*"
+
+gdb_test "p typeid(dummy_dbl).__name" "double.*"
+gdb_test "p typeid(dummy_dbl).__is_function_p()" "true.*"
+gdb_test "p typeid(dummy_dbl).__is_pointer_p()" "false.*"
+