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]

[5/10] RFC: implement "typeid"


This implements the C++ "typeid" operator, aka PR 9065.

Most of the implementation is straightforward.

gnu-v3-abi.c takes the liberty of supplying a phony type_info structure
in case the real thing cannot be found in the debuginfo.  I think this
is ok because the type_info layout is part of the ABI.

Built and regtested on x86-64 Fedora 16.
New tests included.

Tom

	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.cp/typeid.cc: New file.
	* gdb.cp/typeid.exp: New file.
---
 gdb/NEWS                        |    2 +
 gdb/breakpoint.c                |    1 +
 gdb/c-exp.y                     |   13 +++-
 gdb/cp-abi.c                    |   20 +++++
 gdb/cp-abi.h                    |   12 +++
 gdb/eval.c                      |   17 ++++
 gdb/expprint.c                  |    5 +
 gdb/gnu-v3-abi.c                |  158 +++++++++++++++++++++++++++++++++++++++
 gdb/parse.c                     |    1 +
 gdb/std-operator.def            |    3 +
 gdb/testsuite/gdb.cp/typeid.cc  |   60 +++++++++++++++
 gdb/testsuite/gdb.cp/typeid.exp |   67 ++++++++++++++++
 12 files changed, 358 insertions(+), 1 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/typeid.cc
 create mode 100644 gdb/testsuite/gdb.cp/typeid.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 6984390..0004966 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -36,6 +36,8 @@ Lynx 178 PowerPC		powerpc-*-lynx*178
 
 * 'info proc' now works on some core files.
 
+* GDB now implements the the C++ 'typeid' operator.
+
 * Python scripting
 
   ** Vectors can be created with gdb.Type.vector.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 6682ec2..3115f9e 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -10299,6 +10299,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 f6659d4..e5bd7bb 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -233,6 +233,7 @@ static void check_parameter_typelist (VEC (type_ptr) *);
 %token ENTRY
 %token TYPEOF
 %token DECLTYPE
+%token TYPEID
 
 /* Special type cases, put in to allow the parser to distinguish different
    legal basetypes.  */
@@ -345,6 +346,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); }
 	;
@@ -2247,7 +2256,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 366ba30..47ba121 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 d7f80e2..d1d192b 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2816,6 +2816,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 4dd2d7c..2fa44c3 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -951,6 +951,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 d02f95d..764b783 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 09c378b..e07e8c4 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -968,6 +968,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/gdb.cp/typeid.cc b/gdb/testsuite/gdb.cp/typeid.cc
new file mode 100644
index 0000000..7120031f
--- /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
+}
-- 
1.7.7.6


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