xref: /freebsd/contrib/libcxxrt/dynamic_cast.cc (revision 675be9115aae86ad6b3d877155d4fd7822892105)
1 #include "typeinfo.h"
2 #include <stdio.h>
3 
4 using namespace ABI_NAMESPACE;
5 
6 /**
7  * Vtable header.
8  */
9 struct vtable_header
10 {
11 	/** Offset of the leaf object. */
12 	ptrdiff_t leaf_offset;
13 	/** Type of the object. */
14 	const __class_type_info *type;
15 };
16 
17 /**
18  * Simple macro that does pointer arithmetic in bytes but returns a value of
19  * the same type as the original.
20  */
21 #define ADD_TO_PTR(x, off) (__typeof__(x))(((char*)x) + off)
22 
23 bool __class_type_info::can_cast_to(const struct __class_type_info *other) const
24 {
25     return this == other;
26 }
27 
28 void *__class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
29 {
30 	if (this == other)
31 	{
32 		return obj;
33 	}
34 	return 0;
35 }
36 
37 
38 bool __si_class_type_info::can_cast_to(const struct __class_type_info *other) const
39 {
40     return this == other || __base_type->can_cast_to(other);
41 }
42 
43 void *__si_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
44 {
45 	if (this == other)
46 	{
47 		return obj;
48 	}
49 	return __base_type->cast_to(obj, other);
50 }
51 
52 
53 bool __vmi_class_type_info::can_cast_to(const struct __class_type_info *other) const
54 {
55 	if (this == other)
56 	{
57 		return true;
58 	}
59 	for (unsigned int i=0 ; i<__base_count ; i++)
60 	{
61 		const __base_class_type_info *info = &__base_info[i];
62         if(info->isPublic() && info->__base_type->can_cast_to(other))
63         {
64             return true;
65         }
66 	}
67 	return false;
68 }
69 
70 void *__vmi_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
71 {
72 	if (this == other)
73 	{
74 		return obj;
75 	}
76 	for (unsigned int i=0 ; i<__base_count ; i++)
77 	{
78 		const __base_class_type_info *info = &__base_info[i];
79 		ptrdiff_t offset = info->offset();
80 		// If this is a virtual superclass, the offset is stored in the
81 		// object's vtable at the offset requested; 2.9.5.6.c:
82 		//
83 		// 'For a non-virtual base, this is the offset in the object of the
84 		// base subobject. For a virtual base, this is the offset in the
85 		// virtual table of the virtual base offset for the virtual base
86 		// referenced (negative).'
87 
88 		if (info->isVirtual())
89 		{
90 			// Object's vtable
91 			ptrdiff_t *off = *(ptrdiff_t**)obj;
92 			// Offset location in vtable
93 			off = ADD_TO_PTR(off, offset);
94 			offset = *off;
95 		}
96 		void *cast = ADD_TO_PTR(obj, offset);
97 
98 		if (info->__base_type == other)
99 		{
100 			return cast;
101 		}
102 		if ((cast = info->__base_type->cast_to(cast, other)))
103 		{
104 			return cast;
105 		}
106 	}
107 	return 0;
108 }
109 
110 /**
111  * ABI function used to implement the dynamic_cast<> operator.  Some cases of
112  * this operator are implemented entirely in the compiler (e.g. to void*).
113  * This function implements the dynamic casts of the form dynamic_cast<T>(v).
114  * This will be translated to a call to this function with the value v as the
115  * first argument.  The type id of the static type of v is the second argument
116  * and the type id of the destination type (T) is the third argument.
117  *
118  * The third argument is a hint about the compiler's guess at the correct
119  * pointer offset.  If this value is negative, then -1 indicates no hint, -2
120  * that src is not a public base of dst, and -3 that src is a multiple public
121  * base type but never a virtual base type
122  */
123 extern "C" void* __dynamic_cast(const void *sub,
124                                 const __class_type_info *src,
125                                 const __class_type_info *dst,
126                                 ptrdiff_t src2dst_offset)
127 {
128 	char *vtable_location = *(char**)sub;
129 	const vtable_header *header =
130 		(const vtable_header*)(vtable_location - sizeof(vtable_header));
131 	void *leaf = ADD_TO_PTR((void*)sub, header->leaf_offset);
132 	return header->type->cast_to(leaf, dst);
133 }
134