From cddcf8734ed06ada9384a461bc21d58b44f6eba1 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Tue, 20 Mar 2012 16:56:14 +0000 Subject: Rework exception matching. - Remove typeinfo since thing break if the compiler decides to use the system one and merge its contents into typeinfo.h - Make each type_info object have a vtable with the same layout as the public vtable in libstdc++'s . This fixes code (e.g. libobjc2's Objective-C++ exception handling) which rely on being able to add new types. - Add some extra tests I suspect exceptions catching pointer-to-member types will not work correctly, but I've never seen anyone do this and don't have any tests for it. --- src/dynamic_cast.cc | 107 ++++++++++++++++++++++++++++++++++++++-------------- src/exception.cc | 47 +++-------------------- src/typeinfo | 26 ------------- src/typeinfo.h | 107 +++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 182 insertions(+), 105 deletions(-) delete mode 100644 src/typeinfo (limited to 'src') diff --git a/src/dynamic_cast.cc b/src/dynamic_cast.cc index c81ad01..7a07e4c 100644 --- a/src/dynamic_cast.cc +++ b/src/dynamic_cast.cc @@ -46,9 +46,65 @@ struct vtable_header */ #define ADD_TO_PTR(x, off) (__typeof__(x))(((char*)x) + off) -bool __class_type_info::can_cast_to(const struct __class_type_info *other) const +bool std::type_info::__do_catch(std::type_info const *ex_type, + void **exception_object, + unsigned int outer) const { - return this == other; + const type_info *type = this; + + if (type == ex_type) + { + return true; + } + if (const __class_type_info *cti = dynamic_cast(type)) + { + return ex_type->__do_upcast(cti, exception_object); + } + return false; +} + +bool __pbase_type_info::__do_catch(std::type_info const *ex_type, + void **exception_object, + unsigned int outer) const +{ + if (ex_type == this) + { + return true; + } + if (!ex_type->__is_pointer_p()) + { + // Can't catch a non-pointer type in a pointer catch + return false; + } + + if (!(outer & 1)) + { + // If the low bit is cleared on this means that we've gone + // through a pointer that is not const qualified. + return false; + } + // Clear the low bit on outer if we're not const qualified. + if (!(__flags & __const_mask)) + { + outer &= ~1; + } + + const __pbase_type_info *ptr_type = + static_cast(ex_type); + + if (ptr_type->__flags & ~__flags) + { + // Handler pointer is less qualified + return false; + } + + // Special case for void* handler. + if(*__pointee == typeid(void)) + { + return true; + } + + return __pointee->__do_catch(ptr_type->__pointee, exception_object, outer); } void *__class_type_info::cast_to(void *obj, const struct __class_type_info *other) const @@ -60,12 +116,6 @@ void *__class_type_info::cast_to(void *obj, const struct __class_type_info *othe return 0; } - -bool __si_class_type_info::can_cast_to(const struct __class_type_info *other) const -{ - return this == other || __base_type->can_cast_to(other); -} - void *__si_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const { if (this == other) @@ -74,31 +124,32 @@ void *__si_class_type_info::cast_to(void *obj, const struct __class_type_info *o } return __base_type->cast_to(obj, other); } - - -bool __vmi_class_type_info::can_cast_to(const struct __class_type_info *other) const +bool __si_class_type_info::__do_upcast(const __class_type_info *target, + void **thrown_object) const { - if (this == other) + if (this == target) { return true; } - for (unsigned int i=0 ; i<__base_count ; i++) - { - const __base_class_type_info *info = &__base_info[i]; - if(info->isPublic() && info->__base_type->can_cast_to(other)) - { - return true; - } - } - return false; + return __base_type->__do_upcast(target, thrown_object); } void *__vmi_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const { - if (this == other) + if (__do_upcast(other, &obj)) { return obj; } + return 0; +} + +bool __vmi_class_type_info::__do_upcast(const __class_type_info *target, + void **thrown_object) const +{ + if (this == target) + { + return true; + } for (unsigned int i=0 ; i<__base_count ; i++) { const __base_class_type_info *info = &__base_info[i]; @@ -111,6 +162,7 @@ void *__vmi_class_type_info::cast_to(void *obj, const struct __class_type_info * // virtual table of the virtual base offset for the virtual base // referenced (negative).' + void *obj = *thrown_object; if (info->isVirtual()) { // Object's vtable @@ -121,18 +173,17 @@ void *__vmi_class_type_info::cast_to(void *obj, const struct __class_type_info * } void *cast = ADD_TO_PTR(obj, offset); - if (info->__base_type == other) + if (info->__base_type == target || + (info->__base_type->__do_upcast(target, &cast))) { - return cast; - } - if ((cast = info->__base_type->cast_to(cast, other))) - { - return cast; + *thrown_object = cast; + return true; } } return 0; } + /** * ABI function used to implement the dynamic_cast<> operator. Some cases of * this operator are implemented entirely in the compiler (e.g. to void*). diff --git a/src/exception.cc b/src/exception.cc index 8cbcb97..fe56297 100644 --- a/src/exception.cc +++ b/src/exception.cc @@ -847,14 +847,11 @@ static bool check_type_signature(__cxa_exception *ex, const std::type_info *type, void *&adjustedPtr) { - // TODO: For compatibility with the GNU implementation, we should move this - // out into a __do_catch() virtual function in std::type_info void *exception_ptr = (void*)(ex+1); - const std::type_info *ex_type = ex->exceptionType; + const std::type_info *ex_type = ex->exceptionType; - const __pointer_type_info *ptr_type = - dynamic_cast(ex_type); - if (0 != ptr_type) + bool is_ptr = ex_type->__is_pointer_p(); + if (is_ptr) { exception_ptr = *(void**)exception_ptr; } @@ -862,11 +859,6 @@ static bool check_type_signature(__cxa_exception *ex, // // Note: A 0 here is a catchall, not a cleanup, so we return true to // indicate that we found a catch. - // - // TODO: Provide a class for matching against foreign exceptions. This is - // already done in libobjc2, allowing C++ exceptions to be boxed as - // Objective-C objects. We should do something similar, allowing foreign - // exceptions to be wrapped in a C++ exception and delivered. if (0 == type) { if (ex) @@ -878,28 +870,6 @@ static bool check_type_signature(__cxa_exception *ex, if (0 == ex) { return false; } - const __pointer_type_info *target_ptr_type = - dynamic_cast(type); - - if (0 != ptr_type && 0 != target_ptr_type) - { - if (ptr_type->__flags & ~target_ptr_type->__flags) - { - // Handler pointer is less qualified - return false; - } - - // Special case for void* handler. - if(*target_ptr_type->__pointee == typeid(void)) - { - adjustedPtr = exception_ptr; - return true; - } - - ex_type = ptr_type->__pointee; - type = target_ptr_type->__pointee; - } - // If the types are the same, no casting is needed. if (*type == *ex_type) { @@ -907,18 +877,13 @@ static bool check_type_signature(__cxa_exception *ex, return true; } - const __class_type_info *cls_type = - dynamic_cast(ex_type); - const __class_type_info *target_cls_type = - dynamic_cast(type); - if (0 != cls_type && - 0 != target_cls_type && - cls_type->can_cast_to(target_cls_type)) + if (type->__do_catch(ex_type, &exception_ptr, 1)) { - adjustedPtr = cls_type->cast_to(exception_ptr, target_cls_type); + adjustedPtr = exception_ptr; return true; } + return false; } /** diff --git a/src/typeinfo b/src/typeinfo deleted file mode 100644 index 74e77ae..0000000 --- a/src/typeinfo +++ /dev/null @@ -1,26 +0,0 @@ -namespace std -{ - /** - * Standard type info class. The layout of this class is specified by the - * ABI. - */ - class type_info - { - public: - /** - * Virtual destructor. This class must have one virtual function to - * ensure that it has a vtable. - */ - virtual ~type_info(); - bool operator==(const type_info &) const; - bool operator!=(const type_info &) const; - bool before(const type_info &) const; - const char* name() const; - type_info(); - private: - type_info(const type_info& rhs); - type_info& operator= (const type_info& rhs); - const char *__type_name; - }; -} - diff --git a/src/typeinfo.h b/src/typeinfo.h index e35f6c1..8223977 100644 --- a/src/typeinfo.h +++ b/src/typeinfo.h @@ -26,7 +26,86 @@ #include #include "abi_namespace.h" -#include "typeinfo" + +namespace ABI_NAMESPACE +{ + struct __class_type_info; +} +namespace std +{ + /** + * Standard type info class. The layout of this class is specified by the + * ABI. The layout of the vtable is not, but is intended to be + * compatible with the GNU ABI. + * + * Unlike the GNU version, the vtable layout is considered semi-private. + */ + class type_info + { + public: + /** + * Virtual destructor. This class must have one virtual function to + * ensure that it has a vtable. + */ + virtual ~type_info(); + bool operator==(const type_info &) const; + bool operator!=(const type_info &) const; + bool before(const type_info &) const; + const char* name() const; + type_info(); + private: + type_info(const type_info& rhs); + type_info& operator= (const type_info& rhs); + const char *__type_name; + /* + * The following functions are in this order to match the + * vtable layout of libsupc++. This allows libcxxrt to be used + * with libraries that depend on this. + * + * These functions are in the public headers for libstdc++, so + * we have to assume that someone will probably call them and + * expect them to work. Their names must also match the names used in + * libsupc++, so that code linking against this library can subclass + * type_info and correctly fill in the values in the vtables. + */ + public: + /** + * Catch function. Allows external libraries to implement + * their own basic types. This is used, for example, in the + * GNUstep Objective-C runtime to allow Objective-C types to be + * caught in G++ catch blocks. + * + * The outer parameter indicates the number of outer pointers + * in the high bits. The low bit indicates whether the + * pointers are const qualified. + */ + virtual bool __do_catch(const type_info *thrown_type, + void **thrown_object, + unsigned outer) const; + /** + * Performs an upcast. This is used in exception handling to + * cast from subclasses to superclasses. If the upcast is + * possible, it returns true and adjusts the pointer. If the + * upcast is not possible, it returns false and does not adjust + * the pointer. + */ + virtual bool __do_upcast( + const ABI_NAMESPACE::__class_type_info *target, + void **thrown_object) const + { + return false; + } + /** + * Returns true if this is some pointer type, false otherwise. + */ + virtual bool __is_pointer_p() const { return false; } + /** + * Returns true if this is some function type, false otherwise. + */ + virtual bool __is_function_p() const { return false; } + }; +} + namespace ABI_NAMESPACE { @@ -50,6 +129,7 @@ namespace ABI_NAMESPACE struct __function_type_info : public std::type_info { virtual ~__function_type_info(); + virtual bool __is_function_p() const { return true; } }; /** * Type info for enums. @@ -68,13 +148,12 @@ namespace ABI_NAMESPACE /** * Function implementing dynamic casts. */ - virtual void *cast_to(void *obj, - const struct __class_type_info *other) const; - /** - * Function returning whether a cast from this type to another type is - * possible. - */ - virtual bool can_cast_to(const struct __class_type_info *other) const; + virtual void *cast_to(void *obj, const struct __class_type_info *other) const; + virtual bool __do_upcast(const __class_type_info *target, + void **thrown_object) const + { + return this == target; + } }; /** @@ -85,8 +164,10 @@ namespace ABI_NAMESPACE { virtual ~__si_class_type_info(); const __class_type_info *__base_type; + virtual bool __do_upcast( + const ABI_NAMESPACE::__class_type_info *target, + void **thrown_object) const; virtual void *cast_to(void *obj, const struct __class_type_info *other) const; - virtual bool can_cast_to(const struct __class_type_info *other) const; }; /** @@ -166,8 +247,10 @@ namespace ABI_NAMESPACE /** The class is diamond shaped. */ __diamond_shaped_mask = 0x2 }; + virtual bool __do_upcast( + const ABI_NAMESPACE::__class_type_info *target, + void **thrown_object) const; virtual void *cast_to(void *obj, const struct __class_type_info *other) const; - virtual bool can_cast_to(const struct __class_type_info *other) const; }; /** @@ -201,6 +284,10 @@ namespace ABI_NAMESPACE /** Pointer is a pointer to a member of an incomplete class. */ __incomplete_class_mask = 0x10 }; + virtual bool __is_pointer_p() const { return true; } + virtual bool __do_catch(const type_info *thrown_type, + void **thrown_object, + unsigned outer) const; }; /** -- cgit v1.2.3