xref: /minix3/external/bsd/libc++/dist/libcxxrt/src/dynamic_cast.cc (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
14684ddb6SLionel Sambuc /*
24684ddb6SLionel Sambuc  * Copyright 2010-2011 PathScale, Inc. All rights reserved.
34684ddb6SLionel Sambuc  *
44684ddb6SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
54684ddb6SLionel Sambuc  * modification, are permitted provided that the following conditions are met:
64684ddb6SLionel Sambuc  *
74684ddb6SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright notice,
84684ddb6SLionel Sambuc  *    this list of conditions and the following disclaimer.
94684ddb6SLionel Sambuc  *
104684ddb6SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright notice,
114684ddb6SLionel Sambuc  *    this list of conditions and the following disclaimer in the documentation
124684ddb6SLionel Sambuc  *    and/or other materials provided with the distribution.
134684ddb6SLionel Sambuc  *
144684ddb6SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
154684ddb6SLionel Sambuc  * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
164684ddb6SLionel Sambuc  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
174684ddb6SLionel Sambuc  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
184684ddb6SLionel Sambuc  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
194684ddb6SLionel Sambuc  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
204684ddb6SLionel Sambuc  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
214684ddb6SLionel Sambuc  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
224684ddb6SLionel Sambuc  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
234684ddb6SLionel Sambuc  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
244684ddb6SLionel Sambuc  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
254684ddb6SLionel Sambuc  */
264684ddb6SLionel Sambuc 
274684ddb6SLionel Sambuc #include "typeinfo.h"
284684ddb6SLionel Sambuc #include <stdio.h>
294684ddb6SLionel Sambuc 
304684ddb6SLionel Sambuc using namespace ABI_NAMESPACE;
314684ddb6SLionel Sambuc 
324684ddb6SLionel Sambuc /**
334684ddb6SLionel Sambuc  * Vtable header.
344684ddb6SLionel Sambuc  */
354684ddb6SLionel Sambuc struct vtable_header
364684ddb6SLionel Sambuc {
374684ddb6SLionel Sambuc 	/** Offset of the leaf object. */
384684ddb6SLionel Sambuc 	ptrdiff_t leaf_offset;
394684ddb6SLionel Sambuc 	/** Type of the object. */
404684ddb6SLionel Sambuc 	const __class_type_info *type;
414684ddb6SLionel Sambuc };
424684ddb6SLionel Sambuc 
434684ddb6SLionel Sambuc /**
444684ddb6SLionel Sambuc  * Simple macro that does pointer arithmetic in bytes but returns a value of
454684ddb6SLionel Sambuc  * the same type as the original.
464684ddb6SLionel Sambuc  */
47*0a6a1f1dSLionel Sambuc #define ADD_TO_PTR(x, off) reinterpret_cast<__typeof__(x)>(reinterpret_cast<char*>(x) + off)
484684ddb6SLionel Sambuc 
__do_catch(std::type_info const * ex_type,void ** exception_object,unsigned int outer) const494684ddb6SLionel Sambuc bool std::type_info::__do_catch(std::type_info const *ex_type,
504684ddb6SLionel Sambuc                                 void **exception_object,
514684ddb6SLionel Sambuc                                 unsigned int outer) const
524684ddb6SLionel Sambuc {
534684ddb6SLionel Sambuc 	const type_info *type = this;
544684ddb6SLionel Sambuc 
554684ddb6SLionel Sambuc 	if (type == ex_type)
564684ddb6SLionel Sambuc 	{
574684ddb6SLionel Sambuc 		return true;
584684ddb6SLionel Sambuc 	}
594684ddb6SLionel Sambuc 	if (const __class_type_info *cti = dynamic_cast<const __class_type_info *>(type))
604684ddb6SLionel Sambuc 	{
614684ddb6SLionel Sambuc 		return ex_type->__do_upcast(cti, exception_object);
624684ddb6SLionel Sambuc 	}
634684ddb6SLionel Sambuc 	return false;
644684ddb6SLionel Sambuc }
654684ddb6SLionel Sambuc 
__do_catch(std::type_info const * ex_type,void ** exception_object,unsigned int outer) const664684ddb6SLionel Sambuc bool __pbase_type_info::__do_catch(std::type_info const *ex_type,
674684ddb6SLionel Sambuc                                    void **exception_object,
684684ddb6SLionel Sambuc                                    unsigned int outer) const
694684ddb6SLionel Sambuc {
704684ddb6SLionel Sambuc 	if (ex_type == this)
714684ddb6SLionel Sambuc 	{
724684ddb6SLionel Sambuc 		return true;
734684ddb6SLionel Sambuc 	}
744684ddb6SLionel Sambuc 	if (!ex_type->__is_pointer_p())
754684ddb6SLionel Sambuc 	{
764684ddb6SLionel Sambuc 		// Can't catch a non-pointer type in a pointer catch
774684ddb6SLionel Sambuc 		return false;
784684ddb6SLionel Sambuc 	}
794684ddb6SLionel Sambuc 
804684ddb6SLionel Sambuc 	if (!(outer & 1))
814684ddb6SLionel Sambuc 	{
824684ddb6SLionel Sambuc 		// If the low bit is cleared on this means that we've gone
834684ddb6SLionel Sambuc 		// through a pointer that is not const qualified.
844684ddb6SLionel Sambuc 		return false;
854684ddb6SLionel Sambuc 	}
864684ddb6SLionel Sambuc 	// Clear the low bit on outer if we're not const qualified.
874684ddb6SLionel Sambuc 	if (!(__flags & __const_mask))
884684ddb6SLionel Sambuc 	{
894684ddb6SLionel Sambuc 		outer &= ~1;
904684ddb6SLionel Sambuc 	}
914684ddb6SLionel Sambuc 
924684ddb6SLionel Sambuc 	const __pbase_type_info *ptr_type =
934684ddb6SLionel Sambuc 	        static_cast<const __pbase_type_info*>(ex_type);
944684ddb6SLionel Sambuc 
954684ddb6SLionel Sambuc 	if (ptr_type->__flags & ~__flags)
964684ddb6SLionel Sambuc 	{
974684ddb6SLionel Sambuc 		// Handler pointer is less qualified
984684ddb6SLionel Sambuc 		return false;
994684ddb6SLionel Sambuc 	}
1004684ddb6SLionel Sambuc 
1014684ddb6SLionel Sambuc 	// Special case for void* handler.
1024684ddb6SLionel Sambuc 	if(*__pointee == typeid(void))
1034684ddb6SLionel Sambuc 	{
1044684ddb6SLionel Sambuc 		return true;
1054684ddb6SLionel Sambuc 	}
1064684ddb6SLionel Sambuc 
1074684ddb6SLionel Sambuc 	return __pointee->__do_catch(ptr_type->__pointee, exception_object, outer);
1084684ddb6SLionel Sambuc }
1094684ddb6SLionel Sambuc 
cast_to(void * obj,const struct __class_type_info * other) const1104684ddb6SLionel Sambuc void *__class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
1114684ddb6SLionel Sambuc {
1124684ddb6SLionel Sambuc 	if (this == other)
1134684ddb6SLionel Sambuc 	{
1144684ddb6SLionel Sambuc 		return obj;
1154684ddb6SLionel Sambuc 	}
1164684ddb6SLionel Sambuc 	return 0;
1174684ddb6SLionel Sambuc }
1184684ddb6SLionel Sambuc 
cast_to(void * obj,const struct __class_type_info * other) const1194684ddb6SLionel Sambuc void *__si_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
1204684ddb6SLionel Sambuc {
1214684ddb6SLionel Sambuc 	if (this == other)
1224684ddb6SLionel Sambuc 	{
1234684ddb6SLionel Sambuc 		return obj;
1244684ddb6SLionel Sambuc 	}
1254684ddb6SLionel Sambuc 	return __base_type->cast_to(obj, other);
1264684ddb6SLionel Sambuc }
__do_upcast(const __class_type_info * target,void ** thrown_object) const1274684ddb6SLionel Sambuc bool __si_class_type_info::__do_upcast(const __class_type_info *target,
1284684ddb6SLionel Sambuc                                      void **thrown_object) const
1294684ddb6SLionel Sambuc {
1304684ddb6SLionel Sambuc 	if (this == target)
1314684ddb6SLionel Sambuc 	{
1324684ddb6SLionel Sambuc 		return true;
1334684ddb6SLionel Sambuc 	}
1344684ddb6SLionel Sambuc 	return __base_type->__do_upcast(target, thrown_object);
1354684ddb6SLionel Sambuc }
1364684ddb6SLionel Sambuc 
cast_to(void * obj,const struct __class_type_info * other) const1374684ddb6SLionel Sambuc void *__vmi_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
1384684ddb6SLionel Sambuc {
1394684ddb6SLionel Sambuc 	if (__do_upcast(other, &obj))
1404684ddb6SLionel Sambuc 	{
1414684ddb6SLionel Sambuc 		return obj;
1424684ddb6SLionel Sambuc 	}
1434684ddb6SLionel Sambuc 	return 0;
1444684ddb6SLionel Sambuc }
1454684ddb6SLionel Sambuc 
__do_upcast(const __class_type_info * target,void ** thrown_object) const1464684ddb6SLionel Sambuc bool __vmi_class_type_info::__do_upcast(const __class_type_info *target,
1474684ddb6SLionel Sambuc 		                      void **thrown_object) const
1484684ddb6SLionel Sambuc {
1494684ddb6SLionel Sambuc 	if (this == target)
1504684ddb6SLionel Sambuc 	{
1514684ddb6SLionel Sambuc 		return true;
1524684ddb6SLionel Sambuc 	}
1534684ddb6SLionel Sambuc 	for (unsigned int i=0 ; i<__base_count ; i++)
1544684ddb6SLionel Sambuc 	{
1554684ddb6SLionel Sambuc 		const __base_class_type_info *info = &__base_info[i];
1564684ddb6SLionel Sambuc 		ptrdiff_t offset = info->offset();
1574684ddb6SLionel Sambuc 		// If this is a virtual superclass, the offset is stored in the
1584684ddb6SLionel Sambuc 		// object's vtable at the offset requested; 2.9.5.6.c:
1594684ddb6SLionel Sambuc 		//
1604684ddb6SLionel Sambuc 		// 'For a non-virtual base, this is the offset in the object of the
1614684ddb6SLionel Sambuc 		// base subobject. For a virtual base, this is the offset in the
1624684ddb6SLionel Sambuc 		// virtual table of the virtual base offset for the virtual base
1634684ddb6SLionel Sambuc 		// referenced (negative).'
1644684ddb6SLionel Sambuc 
1654684ddb6SLionel Sambuc 		void *obj = *thrown_object;
1664684ddb6SLionel Sambuc 		if (info->isVirtual())
1674684ddb6SLionel Sambuc 		{
1684684ddb6SLionel Sambuc 			// Object's vtable
169*0a6a1f1dSLionel Sambuc 			ptrdiff_t *off = *static_cast<ptrdiff_t**>(obj);
1704684ddb6SLionel Sambuc 			// Offset location in vtable
1714684ddb6SLionel Sambuc 			off = ADD_TO_PTR(off, offset);
1724684ddb6SLionel Sambuc 			offset = *off;
1734684ddb6SLionel Sambuc 		}
1744684ddb6SLionel Sambuc 		void *cast = ADD_TO_PTR(obj, offset);
1754684ddb6SLionel Sambuc 
1764684ddb6SLionel Sambuc 		if (info->__base_type == target ||
1774684ddb6SLionel Sambuc 		    (info->__base_type->__do_upcast(target, &cast)))
1784684ddb6SLionel Sambuc 		{
1794684ddb6SLionel Sambuc 			*thrown_object = cast;
1804684ddb6SLionel Sambuc 			return true;
1814684ddb6SLionel Sambuc 		}
1824684ddb6SLionel Sambuc 	}
1834684ddb6SLionel Sambuc 	return 0;
1844684ddb6SLionel Sambuc }
1854684ddb6SLionel Sambuc 
1864684ddb6SLionel Sambuc 
1874684ddb6SLionel Sambuc /**
1884684ddb6SLionel Sambuc  * ABI function used to implement the dynamic_cast<> operator.  Some cases of
1894684ddb6SLionel Sambuc  * this operator are implemented entirely in the compiler (e.g. to void*).
1904684ddb6SLionel Sambuc  * This function implements the dynamic casts of the form dynamic_cast<T>(v).
1914684ddb6SLionel Sambuc  * This will be translated to a call to this function with the value v as the
1924684ddb6SLionel Sambuc  * first argument.  The type id of the static type of v is the second argument
1934684ddb6SLionel Sambuc  * and the type id of the destination type (T) is the third argument.
1944684ddb6SLionel Sambuc  *
1954684ddb6SLionel Sambuc  * The third argument is a hint about the compiler's guess at the correct
1964684ddb6SLionel Sambuc  * pointer offset.  If this value is negative, then -1 indicates no hint, -2
1974684ddb6SLionel Sambuc  * that src is not a public base of dst, and -3 that src is a multiple public
1984684ddb6SLionel Sambuc  * base type but never a virtual base type
1994684ddb6SLionel Sambuc  */
__dynamic_cast(const void * sub,const __class_type_info * src,const __class_type_info * dst,ptrdiff_t src2dst_offset)2004684ddb6SLionel Sambuc extern "C" void* __dynamic_cast(const void *sub,
2014684ddb6SLionel Sambuc                                 const __class_type_info *src,
2024684ddb6SLionel Sambuc                                 const __class_type_info *dst,
2034684ddb6SLionel Sambuc                                 ptrdiff_t src2dst_offset)
2044684ddb6SLionel Sambuc {
205*0a6a1f1dSLionel Sambuc 	const char *vtable_location = *static_cast<const char * const *>(sub);
2064684ddb6SLionel Sambuc 	const vtable_header *header =
207*0a6a1f1dSLionel Sambuc 		reinterpret_cast<const vtable_header*>(vtable_location - sizeof(vtable_header));
208*0a6a1f1dSLionel Sambuc 	void *leaf = ADD_TO_PTR(const_cast<void *>(sub), header->leaf_offset);
2094684ddb6SLionel Sambuc 	return header->type->cast_to(leaf, dst);
2104684ddb6SLionel Sambuc }
211