blob: 2dacac85d144102ebff88d4d1ff15c3b039bae90 [file] [log] [blame]
// Copyright (C) 2011 The Android Open Source Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// dynamic_cast.cc: RTTI support.
//
// References:
// Itanium C++ ABI at http://www.codesourcery.com/public/cxx-abi/abi.html
// IHI0041A C++ Application Binary Interface for the ARM architecture.
//
#include <cxxabi.h>
#include <cstddef>
#include <cassert>
namespace
{
// Adjust a pointer by an offset.
const void*
adjust_pointer(const void* p, std::ptrdiff_t off)
{
// FIXME: should we align pointer after adjustment?
const char *cp = reinterpret_cast<const char*>(p) + off;
return reinterpret_cast<const void*>(cp);
}
// Return the vtable pointer of a polymorphic object pointed by p.
inline const void*
get_vtable(const void* p)
{
return *reinterpret_cast<void*const*>(p);
}
// Return a pointer to a __class_type_info in a vtable.
inline const abi::__class_type_info*
get_class_type_info(const void* vtable)
{
const void* type_info_ptr = adjust_pointer(vtable, -sizeof(void*));
return *reinterpret_cast<abi::__class_type_info*const*>(type_info_ptr);
}
// Return offset to object in a vtable.
inline std::ptrdiff_t
get_offset_to_top(const void* vtable)
{
const void* type_info_ptr_address = adjust_pointer(vtable, -sizeof(void*));
const void* offset_to_top_address =
adjust_pointer(type_info_ptr_address, -sizeof(std::ptrdiff_t));
return *reinterpret_cast<const std::ptrdiff_t*>(offset_to_top_address);
}
// Return the virtual pointer to the most derived object of referred by a
// pointer p.
const void*
get_most_derived_object(const void* p)
{
const void* vtable = get_vtable(p);
std::ptrdiff_t offset_to_top = get_offset_to_top(vtable);
return adjust_pointer(p, offset_to_top);
}
// We assume that -1 cannot be a valid pointer to object.
const void * const ambiguous_object =
reinterpret_cast<const void*>(-1);
// Return a pointer to the subobject described by base_info.
const void*
get_subobject(const void* object,
const void* vtable,
const abi::__base_class_type_info* base_info)
{
long offset = base_info->offset();
if (base_info->is_virtual())
{
const std::ptrdiff_t* virtual_base_offset_address =
static_cast<const std::ptrdiff_t*> (adjust_pointer(vtable, offset));
offset = *virtual_base_offset_address;
}
return adjust_pointer(object, offset);
}
// Helper of __dyanmic_cast to walk the type tree of an object.
const void *
walk_object(const void *object,
const abi::__class_type_info *type,
const void *match_object,
const abi::__class_type_info *match_type)
{
if (*type == *match_type)
return (match_object == NULL || object == match_object) ? object : NULL;
switch(type->code())
{
case abi::__class_type_info::CLASS_TYPE_INFO_CODE:
// This isn't not the class you're looking for.
return NULL;
case abi::__class_type_info::SI_CLASS_TYPE_INFO_CODE:
// derived type has a single public base at offset 0.
{
const abi::__si_class_type_info* ti =
static_cast<const abi::__si_class_type_info*>(type);
return walk_object(object, ti->__base_type, match_object,
match_type);
}
case abi::__class_type_info::VMI_CLASS_TYPE_INFO_CODE:
{
const void* vtable = get_vtable(object);
const abi::__vmi_class_type_info* ti =
static_cast<const abi::__vmi_class_type_info*>(type);
// Look at all direct bases.
const void* result = NULL;
for (unsigned i = 0; i < ti->__base_count; ++i)
{
if (!ti->__base_info[i].is_public())
continue;
const void *subobject =
get_subobject(object, vtable, &ti->__base_info[i]);
const void* walk_subobject_result =
walk_object(subobject, ti->__base_info[i].__base_type,
match_object, match_type);
if (walk_subobject_result == ambiguous_object)
return ambiguous_object;
else if (walk_subobject_result != NULL)
{
if (result == NULL)
{
result = walk_subobject_result;
}
else if (result != walk_subobject_result)
return ambiguous_object;
}
}
return result;
}
default:
assert(0);
}
return NULL;
}
// Bookkeeping structure for derived-to-base cast in the general case.
struct cast_context
{
public:
const void* object;
const abi::__class_type_info *src_type;
const abi::__class_type_info *dst_type;
std::ptrdiff_t src2dst_offset;
const void* dst_object;
const void* result;
cast_context(const void* obj, const abi::__class_type_info *src,
const abi::__class_type_info *dst, std::ptrdiff_t offset)
: object(obj), src_type(src), dst_type(dst), src2dst_offset(offset),
dst_object(NULL), result(NULL)
{ }
};
// based-to-derive cast in the general case.
void
base_to_derived_cast(const void *object,
const abi::__class_type_info *type,
cast_context* context)
{
const void* saved_dst_object = context->dst_object;
bool is_dst_type = *type == *context->dst_type;
if (is_dst_type)
context->dst_object = object;
if (object == context->object
&& context->dst_object != NULL
&& *type == *context->src_type)
{
if (context->result == NULL)
context->result = context->dst_object;
else if (context->result != context->dst_object)
context->result = ambiguous_object;
context->dst_object = saved_dst_object;
return;
}
switch(type->code())
{
case abi::__class_type_info::CLASS_TYPE_INFO_CODE:
// This isn't not the class you're looking for.
break;
case abi::__class_type_info::SI_CLASS_TYPE_INFO_CODE:
// derived type has a single public base at offset 0.
{
const abi::__si_class_type_info* ti =
static_cast<const abi::__si_class_type_info*>(type);
base_to_derived_cast(object, ti->__base_type, context);
break;
}
case abi::__class_type_info::VMI_CLASS_TYPE_INFO_CODE:
{
const void* vtable = get_vtable(object);
const abi::__vmi_class_type_info* ti =
static_cast<const abi::__vmi_class_type_info*>(type);
// Look at all direct bases.
for (unsigned i = 0; i < ti->__base_count; ++i)
{
if (!ti->__base_info[i].is_public())
continue;
const void *subobject =
get_subobject(object, vtable, &ti->__base_info[i]);
base_to_derived_cast(subobject, ti->__base_info[i].__base_type,
context);
// FIXME: Use flags in base_info to optimize search.
if (context->result == ambiguous_object)
break;
}
break;
}
default:
assert(0);
}
context->dst_object = saved_dst_object;
}
} // namespace
namespace __cxxabiv1
{
#define DYNAMIC_CAST_NO_HINT -1
#define DYNAMIC_CAST_NOT_PUBLIC_BASE -2
#define DYNAMIC_CAST_MULTIPLE_PUBLIC_NONVIRTUAL_BASE -3
/* v: source address to be adjusted; nonnull, and since the
* source object is polymorphic, *(void**)v is a virtual pointer.
* src: static type of the source object.
* dst: destination type (the "T" in "dynamic_cast<T>(v)").
* src2dst_offset: a static hint about the location of the
* source subobject with respect to the complete object;
* special negative values are:
* -1: no hint
* -2: src is not a public base of dst
* -3: src is a multiple public base type but never a
* virtual base type
* otherwise, the src type is a unique public nonvirtual
* base type of dst at offset src2dst_offset from the
* origin of dst.
*/
extern "C" void*
__dynamic_cast (const void *v,
const abi::__class_type_info *src,
const abi::__class_type_info *dst,
std::ptrdiff_t src2dst_offset)
{
const void* most_derived_object = get_most_derived_object(v);
const void* vtable = get_vtable(most_derived_object);
const abi::__class_type_info* most_derived_class_type_info =
get_class_type_info(vtable);
// If T is not a public base type of the most derived class referred
// by v, the cast always fails.
void* t_object =
const_cast<void*>(walk_object(most_derived_object,
most_derived_class_type_info, NULL, dst));
if (t_object == NULL)
return NULL;
// C++ ABI 2.9.7 The dynamic_cast Algorithm:
//
// If, in the most derived object pointed (referred) to by v, v points
// (refers) to a public base class subobject of a T object [note: this can
// be checked at compile time], and if only one object of type T is derived
// from the subobject pointed (referred) to by v, the result is a pointer
// (an lvalue referring) to that T object.
// We knew that src is not a public base, so base-to-derived cast
// is not possible. This works even if there are multiple subobjects
// of type T in the most derived object.
if (src2dst_offset != DYNAMIC_CAST_NOT_PUBLIC_BASE)
{
// If it is known that v points to a public base class subobject
// of a T object, simply adjust the pointer by the offset.
if (t_object != ambiguous_object && src2dst_offset >= 0)
return const_cast<void*>(adjust_pointer(v, -src2dst_offset));
// If there is only one T type subobject, we only need to look at
// there. Otherwise, look for the subobject referred by v in the
// most derived object.
cast_context context(v, src, dst, src2dst_offset);
if (t_object != ambiguous_object)
base_to_derived_cast(t_object, dst, &context);
else
base_to_derived_cast(most_derived_object,
most_derived_class_type_info, &context);
if (context.result != NULL && context.result != ambiguous_object)
return const_cast<void*>(context.result);
}
// C++ ABI 2.9.7 The dynamic_cast Algorithm:
//
// Otherwise, if v points (refers) to a public base class subobject of the
// most derived object, and the type of the most derived object has an
// unambiguous public base class of type T, the result is a pointer (an
// lvalue referring) to the T subobject of the most derived object.
// Otherwise, the run-time check fails.
// Check to see if T is a unambiguous public base class.
if (t_object == ambiguous_object)
return NULL;
// See if v refers to a public base class subobject.
const void* v_object =
walk_object(most_derived_object, most_derived_class_type_info, v, src);
return v_object == v ? t_object : NULL;
}
} // namespace __cxxabiv1