4.3.1.7.8.2.2. 构建type_info的定义
下面就是构建这个type_info类型系列。首先是基类。
1251 static void
1252 create_tinfo_types (void) in rtti.c
1253 {
1254 my_friendly_assert (!ti_desc_type_node, 20020609);
1255
1256 push_nested_namespace (abi_node);
如果我们需要将不是被当前名字空间所包含的名字空间,作为当前名字空间(例如,当前是A::B,现在我们需要A::D),那么我们应该使用push_nested_namespace,而不是push_namespace。这个函数通过递归,首先回溯到全局名字空间,由push_to_top_level将当前名字空间到全局名字空间之间的内容缓存,然后由push_namespace逐个加入从全局名字空间到所希望名字空间之间的内容(参考进入全局名字空间)。
3143 void
3144 push_nested_namespace (tree ns) in name-lookup.c
3145 {
3146 if (ns == global_namespace)
3147 push_to_top_level ();
3148 else
3149 {
3150 push_nested_namespace (CP_DECL_CONTEXT (ns));
3151 push_namespace (DECL_NAME (ns));
3152 }
3153 }
push_nested_namespace的反向操作是pop_nested_namespace。它恢复所有被缓存的内容(参考从全局名字空间返回)。
3158 void
3159 pop_nested_namespace (tree ns) in name-lookup.c
3160 {
3161 timevar_push (TV_NAME_LOOKUP);
3162 while (ns != global_namespace)
3163 {
3164 pop_namespace ();
3165 ns = CP_DECL_CONTEXT (ns);
3166 }
3167
3168 pop_from_top_level ();
3169 timevar_pop (TV_NAME_LOOKUP);
3170 }
现在我们在名字空间__cxxabiv1里了。在“gcc-3.4.6/libstdc++-v3/libsupc++”目录下,GCC有文件cxxabi.h。这个文件就是Linux系统include目录中的“c++/`version`/cxxabi.h”,而其代码生成在libstdc++.so.`ver`库文件里。编译器将按这个文件的内容来生成类定义。
__cxxabiv1名字空间里的类定义都是std::type_info的派生类。因此,首先应该构建这个基类。但是注意下面,这个基类构建在__cxxabiv1名字空间里,且名字是“type_info_pseduo”。事实上,在GCC内部并没有生成std::type_info的定义。在下面std::type_info的定义中,我们可以看到这个类不能单独使用,只能作为基类,而且它具有平凡析构函数(虽然是虚函数)。真正被使用的是__cxxabiv1中的type_info派生类。那么我们偷梁换柱,用type_info_pseduo来替换std::type_info亦无不可,只要我们用到方法、成员都一样的声明,并且不使用基类指针(编译器保证这一点)。下面可以看到type_info_pseduo类是被大大简化了的。
create_tinfo_types (continue)
1258 /* Create the internal type_info structure. This is used as a base for
1259 the other structures. */
1260 {
1261 tree field, fields;
1262
1263 ti_desc_type_node = make_aggr_type (RECORD_TYPE);
1264 field = build_decl (FIELD_DECL, NULL_TREE, const_ptr_type_node);
1265 fields = field;
1266
1267 field = build_decl (FIELD_DECL, NULL_TREE, const_string_type_node);
1268 TREE_CHAIN (field) = fields;
1269 fields = field;
1270
1271 finish_builtin_struct (ti_desc_type_node, "__type_info_pseudo",
1272 fields, NULL_TREE);
1273 TYPE_HAS_CONSTRUCTOR (ti_desc_type_node) = 1;
1274 }
类type_info_pseduo具有2个匿名成员,一个具有类型“const void*”,另一个是类型“const char*”。这是为了让type_info_pseduo与std::type_info具有相同的大小(注意std::type_info有虚函数表,其大小就是const void*)。函数finish_builtin_struct完成这个结构体。
1476 void
1477 finish_builtin_struct (tree type, const char *name, tree fields, in stor-layout.c
1478 tree align_type)
1479 {
1480 tree tail, next;
1481
1482 for (tail = NULL_TREE; fields; tail = fields, fields = next)
1483 {
1484 DECL_FIELD_CONTEXT (fields) = type;
1485 next = TREE_CHAIN (fields);
1486 TREE_CHAIN (fields) = tail;
1487 }
1488 TYPE_FIELDS (type) = tail;
1489
1490 if (align_type)
1491 {
1492 TYPE_ALIGN (type) = TYPE_ALIGN (align_type);
1493 TYPE_USER_ALIGN (type) = TYPE_USER_ALIGN (align_type);
1494 }
1495
1496 layout_type (type);
1497 #if 0 /* not yet, should get fixed properly later */
1498 TYPE_NAME (type) = make_type_decl (get_identifier (name), type);
1499 #else
1500 TYPE_NAME (type) = build_decl (TYPE_DECL, get_identifier (name), type);
1501 #endif
1502 TYPE_STUB_DECL (type) = TYPE_NAME (type);
1503 layout_decl (TYPE_NAME (type), 0);
1504 }
下面将std::type_info的定义列出。作为对比,type_info_pseduo没有定义任何方法,而且只有不可用的匿名成员。为什么会这样呢?
63 class type_info in typeinfo
64 {
65 public:
66 /** Destructor. Being the first non-inline virtual function, this
67 * controls in which translation unit the vtable is emitted. The
68 * compiler makes use of that information to know where to emit
69 * the runtime-mandated type_info structures in the new-abi. */
70 virtual ~type_info();
71
72 private:
73 /// Assigning type_info is not supported. Made private.
74 type_info& operator=(const type_info&);
75 type_info(const type_info&);
76
77 protected:
78 const char *__name;
79
80 protected:
81 explicit type_info(const char *__n): __name(__n) { }
82
83 public:
84 // the public interface
85 /** Returns an @e implementation-defined byte string; this is not
86 * portable between compilers! */
87 const char* name() const
88 { return __name; }
89
90 #if !__GXX_MERGED_TYPEINFO_NAMES
91 bool before(const type_info& __arg) const;
92 // In old abi, or when weak symbols are not supported, there can
93 // be multiple instances of a type_info object for one
94 // type. Uniqueness must use the _name value, not object address.
95 bool operator==(const type_info& __arg) const;
96 #else
97 /** Returns true if @c *this precedes @c __arg in the implementation's
98 * collation order. */
99 // In new abi we can rely on type_info's NTBS being unique,
100 // and therefore address comparisons are sufficient.
101 bool before(const type_info& __arg) const
102 { return __name < __arg.__name; }
103 bool operator==(const type_info& __arg) const
104 { return __name == __arg.__name; }
105 #endif
106 bool operator!=(const type_info& __arg) const
107 { return !operator==(__arg); }
108
109 // the internal interface
110 public:
111 // return true if this is a pointer type of some kind
112 virtual bool __is_pointer_p() const;
113 // return true if this is a function type
114 virtual bool __is_function_p() const;
115
116 // Try and catch a thrown type. Store an adjusted pointer to the
117 // caught type in THR_OBJ. If THR_TYPE is not a pointer type, then
118 // THR_OBJ points to the thrown object. If THR_TYPE is a pointer
119 // type, then THR_OBJ is the pointer itself. OUTER indicates the
120 // number of outer pointers, and whether they were const
121 // qualified.
122 virtual bool __do_catch(const type_info *__thr_type, void **__thr_obj,
123 unsigned __outer) const;
124
125 // internally used during catch matching
126 virtual bool __do_upcast(const __cxxabiv1::__class_type_info *__target,
127 void **__obj_ptr) const;
128 };
首先,派生类指针向基类指针的向下类型转换(downcast),是不需要运行时类型信息的。派生类本身就有足够的信息,这一点我们后面会看到。而对于基类指针向派生类指针的类型提升(upcast),在C++中,有2种方法,一是通过C形式的强制转换,这一过程不会有任何类型检查;二是通过dynamic_cast操作符,这是库函数,它接受type_info对象。另外,C++会执行类型提升的地方,是catch表达式中的捕捉对象,编译器会自动调用dynamic_cast。那么,前端只要保证在交给后端生成汇编代码之前,dynamic_cast函数的实参是真实的type_info对象,而不是这里的伪type_info对象,就没有问题(因为真正的type_info方法也在库里)。后面可以看到,编译器就是这样做的,在前端编译的最后阶段,它将伪type_info对象替换为真实对象(因此,2者大小相同非常重要)。
接下来,继续定义对应于__cxxabiv1名字空间中的伪type_info派生类及辅助类。同样,这里的唯一准则就是,必须和真正的类型大小一样。
create_tinfo_types (continue)
1276 /* Fundamental type_info */
1277 bltn_desc_type_node = create_pseudo_type_info
1278 ("__fundamental_type_info", 0,
1279 NULL);
1280
1281 /* Array, function and enum type_info. No additional fields. */
1282 ary_desc_type_node = create_pseudo_type_info
1283 ("__array_type_info", 0,
1284 NULL);
1285 func_desc_type_node = create_pseudo_type_info
1286 ("__function_type_info", 0,
1287 NULL);
1288 enum_desc_type_node = create_pseudo_type_info
1289 ("__enum_type_info", 0,
1290 NULL);
1291
1292 /* Class type_info. Add a flags field. */
1293 class_desc_type_node = create_pseudo_type_info
1294 ("__class_type_info", 0,
1295 NULL);
1296
1297 /* Single public non-virtual base class. Add pointer to base class.
1298 This is really a descendant of __class_type_info. */
1299 si_class_desc_type_node = create_pseudo_type_info
1300 ("__si_class_type_info", 0,
1301 build_decl (FIELD_DECL, NULL_TREE, type_info_ptr_type),
1302 NULL);
1303
1304 /* Base class internal helper. Pointer to base type, offset to base,
1305 flags. */
1306 {
1307 tree field, fields;
1308
1309 field = build_decl (FIELD_DECL, NULL_TREE, type_info_ptr_type);
1310 fields = field;
1311
1312 field = build_decl (FIELD_DECL, NULL_TREE, integer_types[itk_long]);
1313 TREE_CHAIN (field) = fields;
1314 fields = field;
1315
1316 base_desc_type_node = make_aggr_type (RECORD_TYPE);
1317 finish_builtin_struct (base_desc_type_node, "__base_class_type_info_pseudo",
1318 fields, NULL_TREE);
1319 TYPE_HAS_CONSTRUCTOR (base_desc_type_node) = 1;
1320 }
1321
1322 /* General hierarchy is created as necessary in this vector. */
1323 vmi_class_desc_type_node = make_tree_vec (10);
1324
1325 /* Pointer type_info. Adds two fields, qualification mask
1326 and pointer to the pointed to type. This is really a descendant of
1327 __pbase_type_info. */
1328 ptr_desc_type_node = create_pseudo_type_info
1329 ("__pointer_type_info", 0,
1330 build_decl (FIELD_DECL, NULL_TREE, integer_type_node),
1331 build_decl (FIELD_DECL, NULL_TREE, type_info_ptr_type),
1332 NULL);
1333
1334 /* Pointer to member data type_info. Add qualifications flags,
1335 pointer to the member's type info and pointer to the class.
1336 This is really a descendant of __pbase_type_info. */
1337 ptm_desc_type_node = create_pseudo_type_info
1338 ("__pointer_to_member_type_info", 0,
1339 build_decl (FIELD_DECL, NULL_TREE, integer_type_node),
1340 build_decl (FIELD_DECL, NULL_TREE, type_info_ptr_type),
1341 build_decl (FIELD_DECL, NULL_TREE, type_info_ptr_type),
1342 NULL);
1343
1344 pop_nested_namespace (abi_node);
1345 }
这些派生类的构建由下面的create_pseduo_type_info完成。在C++中,非虚基类于派生类,就如同一个数据成员。在编译器的实现中亦是这样处理。下面的1132行,伪type_info类型被加入派生类中,作为基类。
1112 static tree
1113 create_pseudo_type_info (const char *real_name, int ident, ...) in rtti.c
1114 {
1115 tree pseudo_type;
1116 char *pseudo_name;
1117 tree fields;
1118 tree field_decl;
1119 tree result;
1120 va_list ap;
1121
1122 va_start (ap, ident);
1123
1124 /* Generate the pseudo type name. */
1125 pseudo_name = alloca (strlen (real_name) + 30);
1126 strcpy (pseudo_name, real_name);
1127 strcat (pseudo_name, "_pseudo");
1128 if (ident)
1129 sprintf (pseudo_name + strlen (pseudo_name), "%d", ident);
1130
1131 /* First field is the pseudo type_info base class. */
1132 fields = build_decl (FIELD_DECL, NULL_TREE, ti_desc_type_node);
1133
1134 /* Now add the derived fields. */
1135 while ((field_decl = va_arg (ap, tree)))
1136 {
1137 TREE_CHAIN (field_decl) = fields;
1138 fields = field_decl;
1139 }
1140
1141 /* Create the pseudo type. */
1142 pseudo_type = make_aggr_type (RECORD_TYPE);
1143 finish_builtin_struct (pseudo_type, pseudo_name, fields, NULL_TREE);
1144 CLASSTYPE_AS_BASE (pseudo_type) = pseudo_type;
1145
1146 result = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE);
1147 TINFO_REAL_NAME (result) = get_identifier (real_name);
1148 TINFO_PSEUDO_TYPE (result) =
1149 cp_build_qualified_type (pseudo_type, TYPE_QUAL_CONST);
1150
1151 va_end (ap);
1152 return result;
1153 }
注意,在这里传入create_pseduo_type_info的参数real_name是真实type_info派生类型的名字。这个名字,在1147行,亦被设置在伪type_info派生类的TINFO_REAL_NAME域。它将指引编译器产生dynamic_cast函数所需要的对象。
4.3.1.7.8.2.3. RTTI的使用
4.3.1.7.8.2.3.1. 为类型生成RTTI
只有用户定义了包含虚函数的类,编译器才为之创建type_info对象。或者,编译器在看到dynamic_cast时,编译器也会为dynamic_cast两边的类型创建type_info对象。这创建过程由函数get_tinfo_decl完成。这里的参数type是需要type_info对象的类(type_info与下面tinfo互用)。
317 tree
318 get_tinfo_decl (tree type) in class.c
319 {
320 tree name;
321 tree d;
322
323 if (COMPLETE_TYPE_P (type)
324 && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
325 {
326 error ("cannot create type information for type `%T' because its size is variable",
327 type);
328 return error_mark_node;
329 }
330
331 if (TREE_CODE (type) == METHOD_TYPE)
332 type = build_function_type (TREE_TYPE (type),
333 TREE_CHAIN (TYPE_ARG_TYPES (type)));
334
335 /* For a class type, the variable is cached in the type node
336 itself. */
337 if (CLASS_TYPE_P (type))
338 {
339 d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
340 if (d)
341 return d;
342 }
343
344 name = mangle_typeinfo_for_type (type);
345
346 d = IDENTIFIER_GLOBAL_VALUE (name);
347 if (!d)
348 {
349 tree var_desc = get_pseudo_ti_desc (type);
350
351 d = build_lang_decl (VAR_DECL, name, TINFO_PSEUDO_TYPE (var_desc));
352
353 DECL_ARTIFICIAL (d) = 1;
354 TREE_READONLY (d) = 1;
355 TREE_STATIC (d) = 1;
356 DECL_EXTERNAL (d) = 1;
357 DECL_COMDAT (d) = 1;
358 TREE_PUBLIC (d) = 1;
359 SET_DECL_ASSEMBLER_NAME (d, name);
360
361 pushdecl_top_level_and_finish (d, NULL_TREE);
362
363 if (CLASS_TYPE_P (type))
364 CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
365
366 /* Remember the type it is for. */
367 TREE_TYPE (name) = type;
368
369 /* Add decl to the global array of tinfo decls. */
370 my_friendly_assert (unemitted_tinfo_decls != 0, 20030312);
371 VARRAY_PUSH_TREE (unemitted_tinfo_decls, d);
372 }
373
374 return d;
375 }
在344行,mangle_typeinfo_for_type返回类的,具有修饰名的,代表tinfo对象的,唯一的标识符(tinfo的修饰名和普通项没有不同,除了有一个领头的“TI”字串)。Tinfo对象是类的公有静态成员,而且使用的是修饰名。因此,它一旦构建了,将在361行加入到全局名字空间,而不会有名字冲突的问题。这样大大简化了对tinfo对象的查找,只需346行的代码就够了;否则要一个个类那样查找,效率低得多。
如果类还没有tinfo对象,首先需要get_pseudo_ti_desc找出其对应的类型。
1159 static tree
1160 get_pseudo_ti_desc (tree type) in rtti.c
1161 {
1162 switch (TREE_CODE (type))
1163 {
1164 case OFFSET_TYPE:
1165 return ptm_desc_type_node;
1166 case POINTER_TYPE:
1167 return ptr_desc_type_node;
1168 case ENUMERAL_TYPE:
1169 return enum_desc_type_node;
1170 case FUNCTION_TYPE:
1171 return func_desc_type_node;
1172 case ARRAY_TYPE:
1173 return ary_desc_type_node;
1174 case UNION_TYPE:
1175 case RECORD_TYPE:
1176 if (TYPE_PTRMEMFUNC_P (type))
1177 return ptm_desc_type_node;
1178 else if (!COMPLETE_TYPE_P (type))
1179 {
1180 if (!at_eof)
1181 cxx_incomplete_type_error (NULL_TREE, type);
1182 return class_desc_type_node;
1183 }
1184 else if (!CLASSTYPE_N_BASECLASSES (type))
1185 return class_desc_type_node;
1186 else
1187 {
1188 tree binfo = TYPE_BINFO (type);
1189 tree base_binfos = BINFO_BASETYPES (binfo);
1190 tree base_accesses = BINFO_BASEACCESSES (binfo);
1191 tree base_binfo = TREE_VEC_ELT (base_binfos, 0);
1192 int num_bases = TREE_VEC_LENGTH (base_binfos);
1193
1194 if (num_bases == 1
1195 && TREE_VEC_ELT (base_accesses, 0) == access_public_node
1196 && !TREE_VIA_VIRTUAL (base_binfo)
1197 && integer_zerop (BINFO_OFFSET (base_binfo)))
1198 /* single non-virtual public. */
1199 return si_class_desc_type_node;
1200 else
1201 {
1202 tree var_desc;
1203 tree array_domain, base_array;
1204
1205 if (TREE_VEC_LENGTH (vmi_class_desc_type_node) <= num_bases)
1206 {
1207 int ix;
1208 tree extend = make_tree_vec (num_bases + 5);
1209
1210 for (ix = TREE_VEC_LENGTH (vmi_class_desc_type_node); ix--;)
1211 TREE_VEC_ELT (extend, ix)
1212 = TREE_VEC_ELT (vmi_class_desc_type_node, ix);
1213 vmi_class_desc_type_node = extend;
1214 }
1215 var_desc = TREE_VEC_ELT (vmi_class_desc_type_node, num_bases);
1216 if (var_desc)
1217 return var_desc;
1218
1219 /* Create the array of __base_class_type_info entries.
1220 G++ 3.2 allocated an array that had one too many
1221 entries, and then filled that extra entries with
1222 zeros. */
1223 if (abi_version_at_least (2))
1224 array_domain = build_index_type (size_int (num_bases - 1));
1225 else
1226 array_domain = build_index_type (size_int (num_bases));
1227 base_array =
1228 build_array_type (base_desc_type_node, array_domain);
1229
1230 push_nested_namespace (abi_node);
1231 var_desc = create_pseudo_type_info
1232 ("__vmi_class_type_info", num_bases,
1233 build_decl (FIELD_DECL, NULL_TREE, integer_type_node),
1234 build_decl (FIELD_DECL, NULL_TREE, integer_type_node),
1235 build_decl (FIELD_DECL, NULL_TREE, base_array),
1236 NULL);
1237 pop_nested_namespace (abi_node);
1238
1239 TREE_VEC_ELT (vmi_class_desc_type_node, num_bases) = var_desc;
1240 return var_desc;
1241 }
1242 }
1243 default:
1244 return bltn_desc_type_node;
1245 }
1246 }
vmi_class_desc_type_node最初定义为大小为10的空数组,它用于多继承的类。实际上这是数组中,第一项用于虚继承,剩下的则分别设置着__vmi_class_type_info2,__vmi_class_type_info3等,用于有多个基类的派生类。
VAR_DECL节点,在RECORD_TYPE节点中,代表类的静态成员。一旦它被创建出来,tinfo对象就算准备好了,并由pushdecl_top_level_and_finish 加到全局名字空间。不过我们已经知道,这里构建的tinfo是伪对象,它还需要编译器的后处理,为此它被缓存入unemitted_tinfo_decls。