summaryrefslogtreecommitdiff
path: root/src/dynamic_cast.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/dynamic_cast.cc')
-rw-r--r--src/dynamic_cast.cc87
1 files changed, 87 insertions, 0 deletions
diff --git a/src/dynamic_cast.cc b/src/dynamic_cast.cc
new file mode 100644
index 0000000..2d1f64c
--- /dev/null
+++ b/src/dynamic_cast.cc
@@ -0,0 +1,87 @@
+#include "typeinfo.h"
+#include <stdio.h>
+
+using namespace ABI_NAMESPACE;
+
+/**
+ * Vtable header.
+ */
+struct vtable_header
+{
+ /** Offset of the leaf object. */
+ ptrdiff_t leaf_offset;
+ /** Type of the object. */
+ const __class_type_info *type;
+};
+
+#define ADD_TO_PTR(x, off) (__typeof__(x))(((char*)x) + off)
+
+void *__class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
+{
+ if (this == other)
+ {
+ return obj;
+ }
+ return 0;
+}
+void *__si_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
+{
+ if (this == other)
+ {
+ return obj;
+ }
+ return __base_type->cast_to(obj, other);
+}
+void *__vmi_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const
+{
+ if (this == other)
+ {
+ return obj;
+ }
+ for (unsigned int i=0 ; i<__base_count ; i++)
+ {
+ const __base_class_type_info *info = &__base_info[i];
+ ptrdiff_t offset = info->offset();
+ // If this is a virtual superclass, the offset is stored in the
+ // object's vtable at the offset requested; 2.9.5.6.c:
+ //
+ // 'For a non-virtual base, this is the offset in the object of the
+ // base subobject. For a virtual base, this is the offset in the
+ // virtual table of the virtual base offset for the virtual base
+ // referenced (negative).'
+
+ if (info->isVirtual())
+ {
+ // Object's vtable
+ ptrdiff_t *off = *(ptrdiff_t**)obj;
+ // Offset location in vtable
+ off = ADD_TO_PTR(off, offset);
+ offset = *off;
+ }
+ void *cast = ADD_TO_PTR(obj, offset);
+
+ if (info->__base_type == other)
+ {
+ return cast;
+ }
+ if ((cast = info->__base_type->cast_to(cast, other)))
+ {
+ return cast;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ABI function used to implement the dynamic_cast<> operator.
+ */
+extern "C" void* __dynamic_cast(const void *sub,
+ const __class_type_info *src,
+ const __class_type_info *dst,
+ ptrdiff_t src2dst_offset)
+{
+ char *vtable_location = *(char**)sub;
+ const vtable_header *header = (const vtable_header*)(vtable_location - sizeof(vtable_header));
+ void *leaf = ADD_TO_PTR((void*)sub, header->leaf_offset);
+ return header->type->cast_to(leaf, dst);
+}