// Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // (C) Copyright 2007 Anthony Williams #ifndef EXCEPTION_PTR_HPP #define EXCEPTION_PTR_HPP #include #include "thread_heap_alloc.hpp" #include #include "thread_primitives.hpp" #include namespace jss { namespace detail { namespace win32 { unsigned const exception_maximum_parameters=15; unsigned const exception_noncontinuable=1; struct exception_record { unsigned long ExceptionCode; unsigned long ExceptionFlags; exception_record *ExceptionRecord; void* ExceptionAddress; unsigned long NumberParameters; ulong_ptr ExceptionInformation[exception_maximum_parameters]; }; struct exception_pointers { exception_record* ExceptionRecord; void* ContextRecord; }; extern "C" { __declspec(dllimport) void __stdcall RaiseException(unsigned long,unsigned long,unsigned long,ulong_ptr*); } } unsigned const cpp_exception_code=0xE06D7363; unsigned const cpp_exception_magic_flag=0x19930520; unsigned const cpp_exception_parameter_count=3; struct dummy_exception_type {}; typedef int(dummy_exception_type::*normal_copy_constructor_ptr)(void* src); typedef int(dummy_exception_type::*copy_constructor_with_virtual_base_ptr)(void* src,void* dest); typedef void (dummy_exception_type::*destructor_ptr)(); union cpp_copy_constructor { normal_copy_constructor_ptr normal_copy_constructor; copy_constructor_with_virtual_base_ptr copy_constructor_with_virtual_base; }; enum cpp_type_flags{ class_is_simple_type=1, class_has_virtual_base=4 }; struct cpp_type_info { unsigned flags; std::type_info* type_info; int this_offset; int vbase_descr; int vbase_offset; unsigned long size; cpp_copy_constructor copy_constructor; }; struct cpp_type_info_table { unsigned count; const cpp_type_info* info[1]; }; struct cpp_exception_type { unsigned flags; destructor_ptr destructor; void(*custom_handler)(); const cpp_type_info_table* type_info_table; }; struct cloned_exception { cpp_exception_type const* exception_type; void* exception_object; bool run_destructor; long count; cloned_exception(cloned_exception const& other): exception_type(other.exception_type), exception_object(0), count(0), run_destructor(false) { clone(other.exception_object); } void clone(void* source_object) { exception_object=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,exception_type->type_info_table->info[0]->size); const cpp_type_info *type = exception_type->type_info_table->info[0]; if(!(type->flags & class_is_simple_type) && type->copy_constructor.normal_copy_constructor) { dummy_exception_type* dummy_exception_ptr=reinterpret_cast(exception_object); if(type->flags & class_has_virtual_base) { (dummy_exception_ptr->*(type->copy_constructor.copy_constructor_with_virtual_base))(source_object,exception_object); } else { (dummy_exception_ptr->*(type->copy_constructor.normal_copy_constructor))(source_object); } } else { memmove(exception_object,source_object,type->size); } run_destructor=true; } cloned_exception(void* source_object,cpp_exception_type const* exception_type_): exception_type(exception_type_), exception_object(0), count(0), run_destructor(false) { clone(source_object); } ~cloned_exception() { if(exception_object) { if(run_destructor) { dummy_exception_type* dummy_exception_ptr=reinterpret_cast(exception_object); (dummy_exception_ptr->*(exception_type->destructor))(); } detail::win32::HeapFree(win32::GetProcessHeap(),0,exception_object); } } void rethrow() { cloned_exception temp(*this); detail::win32::ulong_ptr args[detail::cpp_exception_parameter_count]; args[0]=detail::cpp_exception_magic_flag; args[1]=reinterpret_cast(temp.exception_object); args[2]=reinterpret_cast(temp.exception_type); temp.run_destructor=false; detail::win32::RaiseException(detail::cpp_exception_code,detail::win32::exception_noncontinuable,detail::cpp_exception_parameter_count,args); } friend void intrusive_ptr_add_ref(cloned_exception * p) { JSS_INTERLOCKED_INCREMENT(&p->count); } friend void intrusive_ptr_release(cloned_exception * p) { if(!JSS_INTERLOCKED_DECREMENT(&p->count)) { detail::heap_delete(p); } } }; } typedef boost::intrusive_ptr exception_ptr; namespace detail { extern "C" int* _errno(); bool is_cpp_exception(win32::exception_record* record) { return record && (record->ExceptionCode==cpp_exception_code) && (record->NumberParameters==cpp_exception_parameter_count) && (record->ExceptionInformation[0]==cpp_exception_magic_flag); } unsigned long exception_cloning_filter(exception_ptr* ptr,void* info_) { win32::exception_pointers* info=reinterpret_cast(info_); win32::exception_record* record=info->ExceptionRecord; if(is_cpp_exception(record)) { if(!record->ExceptionInformation[2]) { #if _MSC_VER==1310 unsigned const exception_info_offset=0x74; #elif _MSC_VER==1400 unsigned const exception_info_offset=0x80; #endif record=*reinterpret_cast(reinterpret_cast(_errno())+exception_info_offset); } if(is_cpp_exception(record) && record->ExceptionInformation[2]) { *ptr=detail::heap_new(reinterpret_cast(record->ExceptionInformation[1]), reinterpret_cast(record->ExceptionInformation[2])); } } return EXCEPTION_EXECUTE_HANDLER; } void clone_current_exception(exception_ptr* res) { __try { throw; } __except(exception_cloning_filter(res,GetExceptionInformation())) { } } } inline exception_ptr current_exception() { exception_ptr res; clone_current_exception(&res); return res; } inline void rethrow_exception(exception_ptr p) { if(p) { p->rethrow(); } else { throw "no exception stored"; } } template exception_ptr copy_exception(E e) { try { throw e; } catch( ... ) { return current_exception(); } } } #endif