This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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.*"
+

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]