summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/private_typeinfo.cpp331
-rw-r--r--src/private_typeinfo.h52
2 files changed, 376 insertions, 7 deletions
diff --git a/src/private_typeinfo.cpp b/src/private_typeinfo.cpp
index c426c29..04dc3ac 100644
--- a/src/private_typeinfo.cpp
+++ b/src/private_typeinfo.cpp
@@ -9,7 +9,9 @@
#include "private_typeinfo.h"
+// temporary headers
#include <iostream>
+#include <cassert>
namespace std
{
@@ -57,6 +59,95 @@ __class_type_info::~__class_type_info()
{
}
+// __dynamic_cast notes:
+// Up or above refers to base classes and base objects.
+// Down or below refers to derived classes/objects.
+
+// search1 is a search algorithm used by __dynamic_cast.
+// If a static_type is found
+// If dynamic_ptr == static_ptr
+// Record the path to get here.
+// if the path to get here is public
+// stop search
+// Otherwise continue search only below this node
+// Else
+// Continue search above and below this node.
+
+// __class_type_info::search1
+// There are no nodes to search above this node
+// Returns: 1 if search should be continued, otherwise 0
+int
+__class_type_info::search1(__dynamic_cast_info* info, const void* dynamic_ptr,
+ int path_below) const
+{
+ if (this == info->static_type)
+ {
+ if (dynamic_ptr == info->static_ptr)
+ {
+ if (path_below == public_path)
+ {
+ info->path_dynamic_ptr_to_static_ptr = public_path;
+ return 0;
+ }
+ info->path_dynamic_ptr_to_static_ptr = not_public_path;
+ }
+ }
+ return 1;
+}
+
+// search2 is a search algorithm used by __dynamic_cast.
+// if this is a dst_type
+// if this has already been classified then
+// If the path to get here is public, overwrite existing path_dynamic_ptr_to_dst_ptr.
+// else we haven't been to this dst_type before.
+// Record the path to get here in path_dynamic_ptr_to_dst_ptr.
+// Is (static_ptr, static_type) above this dst_type?
+// Yes:
+// Record it as dst_ptr_leading_to_static_ptr and increment the
+// number of such recordings.
+// If this is not the first of such recordings, then stop searching.
+// Otherwise continue searching both above and below this node.
+// No:
+// record it as dst_ptr_not_leading_to_static_ptr and increment
+// the number of such recordings.
+// else if this is a static_type
+// if this is *our* static_type
+// if we found it above a dst_type, record the path from the dst_type
+// else record the path from the dynamic_type being careful not to overwrite a
+// previous public path in this latter case.
+// Record that we found our static_type.
+// Continue searching only below this node
+// else
+// Continue searching above and below this node.
+int
+__class_type_info::search2(__dynamic_cast_info* info, const void* dynamic_ptr,
+ int path_below) const
+{
+ if (this == info->dst_type)
+ {
+ if (dynamic_ptr == info->dst_ptr_not_leading_to_static_ptr)
+ {
+ if (path_below == public_path)
+ info->path_dynamic_ptr_to_dst_ptr = public_path;
+ }
+ else
+ {
+ info->path_dynamic_ptr_to_dst_ptr = path_below;
+ info->dst_ptr_not_leading_to_static_ptr = dynamic_ptr;
+ info->number_to_dst_ptr += 1;
+ }
+ }
+ else if (this == info->static_type && dynamic_ptr == info->static_ptr)
+ {
+ if (info->above_dst_ptr)
+ info->path_dst_ptr_to_static_ptr = path_below;
+ else if (info->path_dynamic_ptr_to_static_ptr != public_path)
+ info->path_dynamic_ptr_to_static_ptr = path_below;
+ info->found_static_ptr = true;
+ }
+ return 1;
+}
+
void
__class_type_info::display(const void* obj) const
{
@@ -69,6 +160,70 @@ __si_class_type_info::~__si_class_type_info()
{
}
+int
+__si_class_type_info::search1(__dynamic_cast_info* info, const void* dynamic_ptr,
+ int path_below) const
+{
+ if (this == info->static_type)
+ {
+ if (dynamic_ptr == info->static_ptr)
+ {
+ if (path_below == public_path)
+ {
+ info->path_dynamic_ptr_to_static_ptr = public_path;
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return __base_type->search1(info, dynamic_ptr, path_below);
+}
+
+int
+__si_class_type_info::search2(__dynamic_cast_info* info, const void* dynamic_ptr,
+ int path_below) const
+{
+ if (this == info->dst_type)
+ {
+ if (dynamic_ptr == info->dst_ptr_leading_to_static_ptr ||
+ dynamic_ptr == info->dst_ptr_not_leading_to_static_ptr)
+ {
+ if (path_below == public_path)
+ info->path_dynamic_ptr_to_dst_ptr = public_path;
+ }
+ else
+ {
+ info->path_dynamic_ptr_to_dst_ptr = path_below;
+ info->above_dst_ptr = true;
+ (void)__base_type->search2(info, dynamic_ptr, public_path);
+ info->above_dst_ptr = false;
+ if (info->found_static_ptr)
+ {
+ info->found_static_ptr = false;
+ info->dst_ptr_leading_to_static_ptr = dynamic_ptr;
+ info->number_to_static_ptr += 1;
+ return info->number_to_static_ptr == 1;
+ }
+ info->dst_ptr_not_leading_to_static_ptr = dynamic_ptr;
+ info->number_to_dst_ptr += 1;
+ }
+ return 1;
+ }
+ else if (this == info->static_type)
+ {
+ if (dynamic_ptr == info->static_ptr)
+ {
+ if (info->above_dst_ptr)
+ info->path_dst_ptr_to_static_ptr = path_below;
+ else if (info->path_dynamic_ptr_to_static_ptr != public_path)
+ info->path_dynamic_ptr_to_static_ptr = path_below;
+ info->found_static_ptr = true;
+ }
+ return 1;
+ }
+ return __base_type->search2(info, dynamic_ptr, path_below);
+}
+
void
__si_class_type_info::display(const void* obj) const
{
@@ -82,6 +237,123 @@ __vmi_class_type_info::~__vmi_class_type_info()
{
}
+int
+__vmi_class_type_info::search1(__dynamic_cast_info* info, const void* dynamic_ptr,
+ int path_below) const
+{
+ typedef const __base_class_type_info* Iter;
+ if (this == info->static_type)
+ {
+ if (dynamic_ptr == info->static_ptr)
+ {
+ if (path_below == public_path)
+ {
+ info->path_dynamic_ptr_to_static_ptr = public_path;
+ return 0;
+ }
+ }
+ return 1;
+ }
+ for (Iter p = __base_info, e = __base_info + __base_count; p < e; ++p)
+ {
+ int r = p->search1(info, dynamic_ptr, path_below);
+ if (r == 0)
+ return 0;
+ }
+ return 1;
+}
+
+int
+__base_class_type_info::search1(__dynamic_cast_info* info, const void* dynamic_ptr,
+ int path_below) const
+{
+ ptrdiff_t offset_to_base = __offset_flags >> __offset_shift;
+ if (__offset_flags & __virtual_mask)
+ {
+ char* vtable = *(char**)dynamic_ptr;
+ offset_to_base = (ptrdiff_t)vtable[offset_to_base];
+ }
+ return __base_type->search1(info, (char*)dynamic_ptr + offset_to_base,
+ (__offset_flags & __public_mask) ? path_below :
+ not_public_path);
+}
+
+int
+__vmi_class_type_info::search2(__dynamic_cast_info* info, const void* dynamic_ptr,
+ int path_below) const
+{
+ typedef const __base_class_type_info* Iter;
+ if (this == info->dst_type)
+ {
+ if (dynamic_ptr == info->dst_ptr_leading_to_static_ptr ||
+ dynamic_ptr == info->dst_ptr_not_leading_to_static_ptr)
+ {
+ if (path_below == public_path)
+ info->path_dynamic_ptr_to_dst_ptr = public_path;
+ }
+ else
+ {
+ info->path_dynamic_ptr_to_dst_ptr = path_below;
+ for (Iter p = __base_info, e = __base_info + __base_count; p < e; ++p)
+ {
+ info->above_dst_ptr = true;
+ int r = p->search2(info, dynamic_ptr, public_path);
+ info->above_dst_ptr = false;
+ if (r == 0)
+ return 0;
+ if (info->found_static_ptr)
+ {
+ info->found_static_ptr = false;
+ info->dst_ptr_leading_to_static_ptr = dynamic_ptr;
+ info->number_to_static_ptr += 1;
+ if (info->number_to_static_ptr != 1)
+ return 0;
+ }
+ else
+ {
+ info->dst_ptr_not_leading_to_static_ptr = dynamic_ptr;
+ info->number_to_dst_ptr += 1;
+ }
+ }
+ }
+ return 1;
+ }
+ else if (this == info->static_type)
+ {
+ if (dynamic_ptr == info->static_ptr)
+ {
+ if (info->above_dst_ptr)
+ info->path_dst_ptr_to_static_ptr = path_below;
+ else if (info->path_dynamic_ptr_to_static_ptr != public_path)
+ info->path_dynamic_ptr_to_static_ptr = path_below;
+ info->found_static_ptr = true;
+ }
+ return 1;
+ }
+ for (Iter p = __base_info, e = __base_info + __base_count; p < e; ++p)
+ {
+ int r = p->search2(info, dynamic_ptr, path_below);
+ if (r == 0)
+ return 0;
+ }
+ return 1;
+}
+
+int
+__base_class_type_info::search2(__dynamic_cast_info* info, const void* dynamic_ptr,
+ int path_below) const
+{
+ ptrdiff_t offset_to_base = __offset_flags >> __offset_shift;
+ if (__offset_flags & __virtual_mask)
+ {
+ char* vtable = *(char**)dynamic_ptr;
+ offset_to_base = (ptrdiff_t)vtable[offset_to_base];
+ }
+ return __base_type->search2(info, (char*)dynamic_ptr + offset_to_base,
+ (__offset_flags & __public_mask) ? path_below :
+ not_public_path);
+}
+
void
__vmi_class_type_info::display(const void* obj) const
{
@@ -151,12 +423,31 @@ __pointer_to_member_type_info::~__pointer_to_member_type_info()
// 2. dynamic_ptr adjusted from static_ptr given a public path from
// (static_ptr, static_type) to (dynamic_ptr, dynamic_type) and also
// a public path from (dynamic_ptr, dynamic_type) to dst_type without
-// ambiguity.
-// Things I think I know:
-// This is a DAG rooted at dynamic_type and going up.
-// Don't care about anything above static_type.
-// There can be only one dynamic_type and it is at the root.
-// A dst_type can never appear above another dst_type.
+// ambiguity, or
+// 3. nullptr
+// Knowns:
+// (dynamic_ptr, dynamic_type) can be derived from static_ptr.
+// dynamic_ptr is a pointer to the complete run time object.
+// dynamic_type is the type of the complete run time object.
+// The type hierarchy is a DAG rooted at (dynamic_ptr, dynamic_type) and
+// traveling up. Each node represents a distinct sub-object and is
+// uniquely identified by a (const void*, const __class_type_info*) pair.
+// __dynamic_cast is not called if dst_type == static_type, or if dst_type
+// appears as a node above (static_ptr, static_type), or if static_ptr is
+// nullptr. In these cases the compiler already knows the answer.
+// So if dst_type appears in the DAG, it appears at the root
+// (dst_type == dynamic_type) or between the root and any static_types.
+// No type can appear above a node of the same type in the DAG. Thus
+// there is only one node with type dynamic_type.
+// A node can appear above more than one node, as well as below more than
+// one node.
+// If dst_type == dynamic_type then there is only one dst_type in the
+// DAG and cases 1 and 2 collapse to the same degenerate case. And
+// this degenerate case is easier/faster to search:
+// Returns dynamic_ptr if there exists a public path from
+// (dynamic_ptr, dynamic_type) to (static_ptr, static_type),
+// else returns nullptr.
+// This check is purely an optimization and does not impact correctness.
extern "C"
void*
__dynamic_cast(const void* static_ptr,
@@ -175,7 +466,33 @@ std::cout << "src2dst_offset = " << src2dst_offset << '\n';
std::cout << "dynamic_ptr = " << dynamic_ptr << '\n';
std::cout << "dynamic_type = " << dynamic_type << '\n';
dynamic_type->display(dynamic_ptr);
- return 0;
+
+ const void* dst_ptr = 0;
+ __dynamic_cast_info info = {dst_type, static_ptr, static_type, src2dst_offset, 0};
+ if (dynamic_type == dst_type)
+ {
+ (void)dynamic_type->search1(&info, dynamic_ptr, public_path);
+ if (info.path_dynamic_ptr_to_static_ptr == public_path)
+ dst_ptr = dynamic_ptr;
+ }
+ else
+ {
+ (void)dynamic_type->search2(&info, dynamic_ptr, public_path);
+ switch (info.number_to_static_ptr)
+ {
+ case 0:
+ if (info.number_to_dst_ptr == 1 &&
+ info.path_dynamic_ptr_to_static_ptr == public_path &&
+ info.path_dynamic_ptr_to_dst_ptr == public_path)
+ dst_ptr = info.dst_ptr_not_leading_to_static_ptr;
+ break;
+ case 1:
+ if (info.path_dst_ptr_to_static_ptr == public_path)
+ dst_ptr = info.dst_ptr_leading_to_static_ptr;
+ break;
+ }
+ }
+ return const_cast<void*>(dst_ptr);
}
} // __cxxabiv1
diff --git a/src/private_typeinfo.h b/src/private_typeinfo.h
index 766febe..0035836 100644
--- a/src/private_typeinfo.h
+++ b/src/private_typeinfo.h
@@ -11,6 +11,7 @@
#define __PRIVATE_TYPEINFO_H_
#include <typeinfo>
+#include <cstddef>
namespace __cxxabiv1
{
@@ -43,6 +44,49 @@ public:
virtual ~__enum_type_info();
};
+enum
+{
+ unknown = 0,
+ public_path,
+ not_public_path
+};
+
+class __class_type_info;
+
+struct __dynamic_cast_info
+{
+ // const data supplied to the search
+
+ const __class_type_info* const dst_type;
+ const void* const static_ptr;
+ const __class_type_info* const static_type;
+ const std::ptrdiff_t src2dst_offset;
+
+ // non-const data learned during the search
+
+ // pointer to a dst_type which has (static_ptr, static_type) above it
+ const void* dst_ptr_leading_to_static_ptr;
+ // pointer to a dst_type which does not have (static_ptr, static_type) above it
+ const void* dst_ptr_not_leading_to_static_ptr;
+ // access of path from dst_ptr_leading_to_static_ptr to (static_ptr, static_type)
+ int path_dst_ptr_to_static_ptr;
+ // access of path from (dynamic_ptr, dynamic_type) to (static_ptr, static_type)
+ // when there is no dst_type along the path
+ int path_dynamic_ptr_to_static_ptr;
+ // access of path from (dynamic_ptr, dynamic_type) to dst_type
+ // (not used if there is a (static_ptr, static_type) above a dst_type).
+ int path_dynamic_ptr_to_dst_ptr;
+ // Number of dst_types below (static_ptr, static_type)
+ int number_to_static_ptr;
+ // Number of dst_types not below (static_ptr, static_type)
+ int number_to_dst_ptr;
+ // true when the search is above a dst_type, else false
+ bool above_dst_ptr;
+ // communicates to a dst_type node that (static_ptr, static_type) was found
+ // above it.
+ bool found_static_ptr;
+};
+
// Has no base class
class __class_type_info
: public std::type_info
@@ -50,6 +94,8 @@ class __class_type_info
public:
virtual ~__class_type_info();
+ virtual int search1(__dynamic_cast_info*, const void*, int) const;
+ virtual int search2(__dynamic_cast_info*, const void*, int) const;
virtual void display(const void* obj) const;
};
@@ -62,6 +108,8 @@ public:
virtual ~__si_class_type_info();
+ virtual int search1(__dynamic_cast_info*, const void*, int) const;
+ virtual int search2(__dynamic_cast_info*, const void*, int) const;
virtual void display(const void* obj) const;
};
@@ -78,6 +126,8 @@ public:
__offset_shift = 8
};
+ int search1(__dynamic_cast_info*, const void*, int) const;
+ int search2(__dynamic_cast_info*, const void*, int) const;
void display(const void* obj) const;
};
@@ -100,6 +150,8 @@ public:
virtual ~__vmi_class_type_info();
+ virtual int search1(__dynamic_cast_info*, const void*, int) const;
+ virtual int search2(__dynamic_cast_info*, const void*, int) const;
virtual void display(const void* obj) const;
};