diff options
author | Marshall Clow <mclow@qualcomm.com> | 2011-06-10 03:40:19 +0000 |
---|---|---|
committer | Marshall Clow <mclow@qualcomm.com> | 2011-06-10 03:40:19 +0000 |
commit | 280ddee8bded79832e101e6874743a487f00bde7 (patch) | |
tree | c7131850a1eb360ba39f46bf95b776615a321cf2 /libcxxabi | |
parent | caa33d36fb3f126d9cbe854793ca617d05170255 (diff) | |
download | bcm5719-llvm-280ddee8bded79832e101e6874743a487f00bde7.tar.gz bcm5719-llvm-280ddee8bded79832e101e6874743a487f00bde7.zip |
Implement vector new and delete functionality
llvm-svn: 132832
Diffstat (limited to 'libcxxabi')
-rw-r--r-- | libcxxabi/src/cxa_vector.cpp | 367 | ||||
-rw-r--r-- | libcxxabi/test/test_vector1.cpp | 207 | ||||
-rw-r--r-- | libcxxabi/test/test_vector2.cpp | 125 | ||||
-rw-r--r-- | libcxxabi/test/test_vector3.cpp | 48 |
4 files changed, 747 insertions, 0 deletions
diff --git a/libcxxabi/src/cxa_vector.cpp b/libcxxabi/src/cxa_vector.cpp new file mode 100644 index 00000000000..67ecc243142 --- /dev/null +++ b/libcxxabi/src/cxa_vector.cpp @@ -0,0 +1,367 @@ +//===-------------------------- cxa_vector.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the "Array Construction and Destruction APIs" +// http://www.codesourcery.com/public/cxx-abi/abi.html#array-ctor +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" + +#include <exception> // for std::terminate + +namespace __cxxabiv1 { + +#pragma mark --Helper routines and classes -- + +namespace { + inline static size_t __get_element_count ( void *p ) { + return static_cast <size_t *> (p)[-1]; + } + + inline static void __set_element_count ( void *p, size_t element_count ) { + static_cast <size_t *> (p)[-1] = element_count; + } + + +// A pair of classes to simplify exception handling and control flow. +// They get passed a block of memory in the constructor, and unless the +// 'release' method is called, they deallocate the memory in the destructor. +// Prefered usage is to allocate some memory, attach it to one of these objects, +// and then, when all the operations to set up the memory block have succeeded, +// call 'release'. If any of the setup operations fail, or an exception is +// thrown, then the block is automatically deallocated. +// +// The only difference between these two classes is the signature for the +// deallocation function (to match new2/new3 and delete2/delete3. + class st_heap_block2 { + public: + typedef void (*dealloc_f)(void *); + + st_heap_block2 ( dealloc_f dealloc, void *ptr ) + : dealloc_ ( dealloc ), ptr_ ( ptr ), enabled_ ( true ) {} + ~st_heap_block2 () { if ( enabled_ ) dealloc_ ( ptr_ ) ; } + void release () { enabled_ = false; } + + private: + dealloc_f dealloc_; + void *ptr_; + bool enabled_; + }; + + class st_heap_block3 { + public: + typedef void (*dealloc_f)(void *, size_t); + + st_heap_block3 ( dealloc_f dealloc, void *ptr, size_t size ) + : dealloc_ ( dealloc ), ptr_ ( ptr ), size_ ( size ), enabled_ ( true ) {} + ~st_heap_block3 () { if ( enabled_ ) dealloc_ ( ptr_, size_ ) ; } + void release () { enabled_ = false; } + + private: + dealloc_f dealloc_; + void *ptr_; + size_t size_; + bool enabled_; + }; + + class st_cxa_cleanup { + public: + typedef void (*destruct_f)(void *); + + st_cxa_cleanup ( void *ptr, size_t &idx, size_t element_size, destruct_f destructor ) + : ptr_ ( ptr ), idx_ ( idx ), element_size_ ( element_size ), + destructor_ ( destructor ), enabled_ ( true ) {} + ~st_cxa_cleanup () { + if ( enabled_ ) + __cxa_vec_cleanup ( ptr_, idx_, element_size_, destructor_ ); + } + + void release () { enabled_ = false; } + + private: + void *ptr_; + size_t &idx_; + size_t element_size_; + destruct_f destructor_; + bool enabled_; + }; + + class st_terminate { + public: + st_terminate ( bool enabled = true ) : enabled_ ( enabled ) {} + ~st_terminate () { if ( enabled_ ) std::terminate (); } + void release () { enabled_ = false; } + private: + bool enabled_ ; + }; +} + +#pragma mark --Externally visible routines-- + +extern "C" { + +// Equivalent to +// +// __cxa_vec_new2(element_count, element_size, padding_size, constructor, +// destructor, &::operator new[], &::operator delete[]) +void* __cxa_vec_new( + size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void*), void (*destructor)(void*) ) { + + return __cxa_vec_new2 ( element_count, element_size, padding_size, + constructor, destructor, &::operator new [], &::operator delete [] ); +} + + + +// Given the number and size of elements for an array and the non-negative +// size of prefix padding for a cookie, allocate space (using alloc) for +// the array preceded by the specified padding, initialize the cookie if +// the padding is non-zero, and call the given constructor on each element. +// Return the address of the array proper, after the padding. +// +// If alloc throws an exception, rethrow the exception. If alloc returns +// NULL, return NULL. If the constructor throws an exception, call +// destructor for any already constructed elements, and rethrow the +// exception. If the destructor throws an exception, call std::terminate. +// +// The constructor may be NULL, in which case it must not be called. If the +// padding_size is zero, the destructor may be NULL; in that case it must +// not be called. +// +// Neither alloc nor dealloc may be NULL. +void* __cxa_vec_new2( + size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void*), void (*destructor)(void*), + void* (*alloc)(size_t), void (*dealloc)(void*) ) { + + const size_t heap_size = element_count * element_size + padding_size; + char * const heap_block = static_cast<char *> ( alloc ( heap_size )); + char *vec_base = heap_block; + + if ( NULL != vec_base ) { + st_heap_block2 heap ( dealloc, heap_block ); + + // put the padding before the array elements + if ( 0 != padding_size ) { + vec_base += padding_size; + __set_element_count ( vec_base, element_count ); + } + + // Construct the elements + __cxa_vec_ctor ( vec_base, element_count, element_size, constructor, destructor ); + heap.release (); // We're good! + } + + return vec_base; +} + + +// Same as __cxa_vec_new2 except that the deallocation function takes both +// the object address and its size. +void* __cxa_vec_new3( + size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void*), void (*destructor)(void*), + void* (*alloc)(size_t), void (*dealloc)(void*, size_t) ) { + + const size_t heap_size = element_count * element_size + padding_size; + char * const heap_block = static_cast<char *> ( alloc ( heap_size )); + char *vec_base = heap_block; + + if ( NULL != vec_base ) { + st_heap_block3 heap ( dealloc, heap_block, heap_size ); + + // put the padding before the array elements + if ( 0 != padding_size ) { + vec_base += padding_size; + __set_element_count ( vec_base, element_count ); + } + + // Construct the elements + __cxa_vec_ctor ( vec_base, element_count, element_size, constructor, destructor ); + heap.release (); // We're good! + } + + return vec_base; +} + + +// Given the (data) addresses of a destination and a source array, an +// element count and an element size, call the given copy constructor to +// copy each element from the source array to the destination array. The +// copy constructor's arguments are the destination address and source +// address, respectively. If an exception occurs, call the given destructor +// (if non-NULL) on each copied element and rethrow. If the destructor +// throws an exception, call terminate(). The constructor and or destructor +// pointers may be NULL. If either is NULL, no action is taken when it +// would have been called. + +void __cxa_vec_cctor( void* dest_array, void* src_array, + size_t element_count, size_t element_size, + void (*constructor) (void*, void*), void (*destructor)(void*) ) { + + if ( NULL != constructor ) { + size_t idx = 0; + char *src_ptr = static_cast<char *>(src_array); + char *dest_ptr = static_cast<char *>(dest_array); + st_cxa_cleanup cleanup ( dest_array, idx, element_size, destructor ); + + for ( idx = 0; idx < element_count; + ++idx, src_ptr += element_size, dest_ptr += element_size ) + constructor ( dest_ptr, src_ptr ); + cleanup.release (); // We're good! + } +} + + +// Given the (data) address of an array, not including any cookie padding, +// and the number and size of its elements, call the given constructor on +// each element. If the constructor throws an exception, call the given +// destructor for any already-constructed elements, and rethrow the +// exception. If the destructor throws an exception, call terminate(). The +// constructor and/or destructor pointers may be NULL. If either is NULL, +// no action is taken when it would have been called. +void __cxa_vec_ctor( + void* array_address, size_t element_count, size_t element_size, + void (*constructor)(void*), void (*destructor)(void*) ) { + + if ( NULL != constructor ) { + size_t idx; + char *ptr = static_cast <char *> ( array_address ); + st_cxa_cleanup cleanup ( array_address, idx, element_size, destructor ); + + // Construct the elements + for ( idx = 0; idx < element_count; ++idx, ptr += element_size ) + constructor ( ptr ); + cleanup.release (); // We're good! + } +} + +// Given the (data) address of an array, the number of elements, and the +// size of its elements, call the given destructor on each element. If the +// destructor throws an exception, rethrow after destroying the remaining +// elements if possible. If the destructor throws a second exception, call +// terminate(). The destructor pointer may be NULL, in which case this +// routine does nothing. +void __cxa_vec_dtor( + void* array_address, size_t element_count, size_t element_size, + void (*destructor)(void*) ) { + + if ( NULL != destructor ) { + char *ptr = static_cast <char *> (array_address); + size_t idx = element_count; + st_cxa_cleanup cleanup ( array_address, idx, element_size, destructor ); + { + st_terminate exception_guard (std::uncaught_exception ()); + ptr += element_count * element_size; // one past the last element + + while ( idx-- > 0 ) { + ptr -= element_size; + destructor ( ptr ); + } + exception_guard.release (); // We're good ! + } + cleanup.release (); // We're still good! + } +} + +// Given the (data) address of an array, the number of elements, and the +// size of its elements, call the given destructor on each element. If the +// destructor throws an exception, call terminate(). The destructor pointer +// may be NULL, in which case this routine does nothing. +void __cxa_vec_cleanup( void* array_address, size_t element_count, + size_t element_size, void (*destructor)(void*) ) { + + if ( NULL != destructor ) { + char *ptr = static_cast <char *> (array_address); + size_t idx = element_count; + st_terminate exception_guard; + + ptr += element_count * element_size; // one past the last element + while ( idx-- > 0 ) { + ptr -= element_size; + destructor ( ptr ); + } + exception_guard.release (); // We're done! + } +} + + +// If the array_address is NULL, return immediately. Otherwise, given the +// (data) address of an array, the non-negative size of prefix padding for +// the cookie, and the size of its elements, call the given destructor on +// each element, using the cookie to determine the number of elements, and +// then delete the space by calling ::operator delete[](void *). If the +// destructor throws an exception, rethrow after (a) destroying the +// remaining elements, and (b) deallocating the storage. If the destructor +// throws a second exception, call terminate(). If padding_size is 0, the +// destructor pointer must be NULL. If the destructor pointer is NULL, no +// destructor call is to be made. +// +// The intent of this function is to permit an implementation to call this +// function when confronted with an expression of the form delete[] p in +// the source code, provided that the default deallocation function can be +// used. Therefore, the semantics of this function are consistent with +// those required by the standard. The requirement that the deallocation +// function be called even if the destructor throws an exception derives +// from the resolution to DR 353 to the C++ standard, which was adopted in +// April, 2003. +void __cxa_vec_delete( void* array_address, + size_t element_size, size_t padding_size, void (*destructor)(void*) ) { + + __cxa_vec_delete2 ( array_address, element_size, padding_size, + destructor, &::operator delete [] ); +} + + +// Same as __cxa_vec_delete, except that the given function is used for +// deallocation instead of the default delete function. If dealloc throws +// an exception, the result is undefined. The dealloc pointer may not be +// NULL. +void __cxa_vec_delete2( void* array_address, + size_t element_size, size_t padding_size, + void (*destructor)(void*), void (*dealloc)(void*) ) { + + if ( NULL != array_address ) { + char *vec_base = static_cast <char *> (array_address); + char *heap_block = vec_base - padding_size; + st_heap_block2 heap ( dealloc, heap_block ); + + if ( 0 != padding_size && NULL != destructor ) // call the destructors + __cxa_vec_dtor ( array_address, __get_element_count ( vec_base ), + element_size, destructor ); + } +} + + +// Same as __cxa_vec_delete, except that the given function is used for +// deallocation instead of the default delete function. The deallocation +// function takes both the object address and its size. If dealloc throws +// an exception, the result is undefined. The dealloc pointer may not be +// NULL. +void __cxa_vec_delete3( void* array_address, + size_t element_size, size_t padding_size, + void (*destructor)(void*), void (*dealloc) (void*, size_t)) { + + if ( NULL != array_address ) { + char *vec_base = static_cast <char *> (array_address); + char *heap_block = vec_base - padding_size; + const size_t element_count = padding_size ? __get_element_count ( vec_base ) : 0; + const size_t heap_block_size = element_size * element_count + padding_size; + st_heap_block3 heap ( dealloc, heap_block, heap_block_size ); + + if ( 0 != padding_size && NULL != destructor ) // call the destructors + __cxa_vec_dtor ( array_address, element_count, element_size, destructor ); + } +} + + +} // extern "C" + +} // abi diff --git a/libcxxabi/test/test_vector1.cpp b/libcxxabi/test/test_vector1.cpp new file mode 100644 index 00000000000..2a539d4d4a3 --- /dev/null +++ b/libcxxabi/test/test_vector1.cpp @@ -0,0 +1,207 @@ +//===---------------------------- test_vector.cpp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" + +#include <iostream> +#include <cstdlib> + +// Wrapper routines +void *my_alloc2 ( size_t sz ) { + void *p = std::malloc ( sz ); +// std::printf ( "Allocated %ld bytes at %lx\n", sz, (unsigned long) p ); + return p; + } + +void my_dealloc2 ( void *p ) { +// std::printf ( "Freeing %lx\n", (unsigned long) p ); + std::free ( p ); + } + +void my_dealloc3 ( void *p, size_t sz ) { +// std::printf ( "Freeing %lx (size %ld)\n", (unsigned long) p, sz ); + std::free ( p ); + } + +void my_construct ( void *p ) { +// std::printf ( "Constructing %lx\n", (unsigned long) p ); + } + +void my_destruct ( void *p ) { +// std::printf ( "Destructing %lx\n", (unsigned long) p ); + } + +int gCounter; +void count_construct ( void *p ) { ++gCounter; } +void count_destruct ( void *p ) { --gCounter; } + + +int gConstructorCounter; +int gConstructorThrowTarget; +int gDestructorCounter; +int gDestructorThrowTarget; +void throw_construct ( void *p ) { if ( gConstructorCounter == gConstructorThrowTarget ) throw 1; ++gConstructorCounter; } +void throw_destruct ( void *p ) { if ( ++gDestructorCounter == gDestructorThrowTarget ) throw 2; } + +struct vec_on_stack { + void *storage; + vec_on_stack () : storage ( __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct )) {} + ~vec_on_stack () { __cxxabiv1::__cxa_vec_delete ( storage, 40, 8, throw_destruct ); } + }; + +// Test calls with empty constructors and destructors +int test_empty ( ) { + void *one, *two, *three; + +// Try with no padding and no con/destructors + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, NULL, NULL ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, NULL, NULL, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, NULL, NULL, my_alloc2, my_dealloc3 ); + + __cxxabiv1::__cxa_vec_delete ( one, 40, 0, NULL ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 0, NULL, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 0, NULL, my_dealloc3 ); + +// Try with no padding + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, my_construct, my_destruct ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, my_construct, my_destruct, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, my_construct, my_destruct, my_alloc2, my_dealloc3 ); + + __cxxabiv1::__cxa_vec_delete ( one, 40, 0, my_destruct ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 0, my_destruct, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 0, my_destruct, my_dealloc3 ); + +// Padding and no con/destructors + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, NULL, NULL ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, NULL, NULL, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, NULL, NULL, my_alloc2, my_dealloc3 ); + + __cxxabiv1::__cxa_vec_delete ( one, 40, 8, NULL ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 8, NULL, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 8, NULL, my_dealloc3 ); + +// Padding with con/destructors + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, my_construct, my_destruct ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, my_construct, my_destruct, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, my_construct, my_destruct, my_alloc2, my_dealloc3 ); + + __cxxabiv1::__cxa_vec_delete ( one, 40, 8, my_destruct ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 8, my_destruct, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 8, my_destruct, my_dealloc3 ); + + return 0; + } + +// Make sure the constructors and destructors are matched +int test_counted ( ) { + int retVal = 0; + void *one, *two, *three; + +// Try with no padding + gCounter = 0; + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, count_construct, count_destruct ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, count_construct, count_destruct, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, count_construct, count_destruct, my_alloc2, my_dealloc3 ); + + __cxxabiv1::__cxa_vec_delete ( one, 40, 0, count_destruct ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 0, count_destruct, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 0, count_destruct, my_dealloc3 ); + +// Since there was no padding, the # of elements in the array are not stored +// and the destructors are not called. + if ( gCounter != 30 ) { + std::cerr << "Mismatched Constructor/Destructor calls (1)" << std::endl; + std::cerr << " Expected 30, got " << gCounter << std::endl; + retVal = 1; + } + + gCounter = 0; + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, count_construct, count_destruct ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, count_construct, count_destruct, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, count_construct, count_destruct, my_alloc2, my_dealloc3 ); + + __cxxabiv1::__cxa_vec_delete ( one, 40, 8, count_destruct ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 8, count_destruct, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 8, count_destruct, my_dealloc3 ); + + if ( gCounter != 0 ) { + std::cerr << "Mismatched Constructor/Destructor calls (2)" << std::endl; + std::cerr << " Expected 0, got " << gCounter << std::endl; + retVal = 1; + } + + return retVal; + } + +// Make sure the constructors and destructors are matched +int test_exception_in_constructor ( ) { + int retVal = 0; + void *one, *two, *three; + +// Try with no padding + gConstructorCounter = gDestructorCounter = 0; + gConstructorThrowTarget = 15; + gDestructorThrowTarget = -1; + try { + one = two = three = NULL; + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, throw_construct, throw_destruct ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, throw_construct, throw_destruct, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, throw_construct, throw_destruct, my_alloc2, my_dealloc3 ); + } + catch ( int i ) {} + + __cxxabiv1::__cxa_vec_delete ( one, 40, 0, throw_destruct ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 0, throw_destruct, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 0, throw_destruct, my_dealloc3 ); + +// Since there was no padding, the # of elements in the array are not stored +// and the destructors are not called. +// Since we threw after 15 calls to the constructor, we should see 5 calls to +// the destructor from the partially constructed array. + if ( gConstructorCounter - gDestructorCounter != 10 ) { + std::cerr << "Mismatched Constructor/Destructor calls (1C)" << std::endl; + std::cerr << gConstructorCounter << " constructors, but " << + gDestructorCounter << " destructors" << std::endl; + retVal = 1; + } + + gConstructorCounter = gDestructorCounter = 0; + gConstructorThrowTarget = 15; + gDestructorThrowTarget = -1; + try { + one = two = three = NULL; + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc3 ); + } + catch ( int i ) {} + + __cxxabiv1::__cxa_vec_delete ( one, 40, 8, throw_destruct ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 8, throw_destruct, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 8, throw_destruct, my_dealloc3 ); + + if ( gConstructorCounter != gDestructorCounter ) { + std::cerr << "Mismatched Constructor/Destructor calls (2C)" << std::endl; + std::cerr << gConstructorCounter << " constructors, but " << + gDestructorCounter << " destructors" << std::endl; + retVal = 1; + } + + return retVal; + } + +void my_terminate () { exit ( 0 ); } + +int main ( int argc, char *argv [] ) { + int retVal = 0; + retVal += test_empty (); + retVal += test_counted (); + retVal += test_exception_in_constructor (); + return retVal; + } diff --git a/libcxxabi/test/test_vector2.cpp b/libcxxabi/test/test_vector2.cpp new file mode 100644 index 00000000000..85c2b0b4d8e --- /dev/null +++ b/libcxxabi/test/test_vector2.cpp @@ -0,0 +1,125 @@ +//===--------------------------- test_vector2.cpp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" + +#include <iostream> +#include <cstdlib> + +void my_terminate () { exit ( 0 ); } + +// Wrapper routines +void *my_alloc2 ( size_t sz ) { + void *p = std::malloc ( sz ); +// std::printf ( "Allocated %ld bytes at %lx\n", sz, (unsigned long) p ); + return p; + } + +void my_dealloc2 ( void *p ) { +// std::printf ( "Freeing %lx\n", (unsigned long) p ); + std::free ( p ); + } + +void my_dealloc3 ( void *p, size_t sz ) { +// std::printf ( "Freeing %lx (size %ld)\n", (unsigned long) p, sz ); + std::free ( p ); + } + +void my_construct ( void *p ) { +// std::printf ( "Constructing %lx\n", (unsigned long) p ); + } + +void my_destruct ( void *p ) { +// std::printf ( "Destructing %lx\n", (unsigned long) p ); + } + +int gCounter; +void count_construct ( void *p ) { ++gCounter; } +void count_destruct ( void *p ) { --gCounter; } + + +int gConstructorCounter; +int gConstructorThrowTarget; +int gDestructorCounter; +int gDestructorThrowTarget; +void throw_construct ( void *p ) { if ( gConstructorCounter == gConstructorThrowTarget ) throw 1; ++gConstructorCounter; } +void throw_destruct ( void *p ) { if ( ++gDestructorCounter == gDestructorThrowTarget ) throw 2; } + +struct vec_on_stack { + void *storage; + vec_on_stack () : storage ( __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct )) {} + ~vec_on_stack () { __cxxabiv1::__cxa_vec_delete ( storage, 40, 8, throw_destruct ); } + }; + + +// Make sure the constructors and destructors are matched +void test_exception_in_destructor ( ) { + void *one, *two, *three; + +// Throw from within a destructor + gConstructorCounter = gDestructorCounter = 0; + gConstructorThrowTarget = -1; + gDestructorThrowTarget = 15; + try { + one = two = three = NULL; + one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct ); + two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc2 ); + three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc3 ); + } + catch ( int i ) {} + + try { + __cxxabiv1::__cxa_vec_delete ( one, 40, 8, throw_destruct ); + __cxxabiv1::__cxa_vec_delete2( two, 40, 8, throw_destruct, my_dealloc2 ); + __cxxabiv1::__cxa_vec_delete3( three, 40, 8, throw_destruct, my_dealloc3 ); + } + catch ( int i ) {} + +// We should have thrown in the middle of cleaning up "two", which means that +// there should be 20 calls to the destructor, and "three" was not cleaned up. + if ( gConstructorCounter != 30 || gDestructorCounter != 20 ) { + std::cerr << "Unexpected Constructor/Destructor calls (1D)" << std::endl; + std::cerr << "Expected (30, 20), but got (" << gConstructorCounter << ", " << + gDestructorCounter << ")" << std::endl; + } + +// Try throwing from a destructor - should be fine. + gConstructorCounter = gDestructorCounter = 0; + gConstructorThrowTarget = -1; + gDestructorThrowTarget = 5; + try { vec_on_stack v; } + catch ( int i ) {} + +// there should be 20 calls to the destructor, and "three" was not cleaned up. + if ( gConstructorCounter != gDestructorCounter ) { + std::cerr << "Mismatched Constructor/Destructor calls (2C)" << std::endl; + std::cerr << gConstructorCounter << " constructors, but " << + gDestructorCounter << " destructors" << std::endl; + } + +// Try throwing from a destructor while unwinding the stack -- should abort + gConstructorCounter = gDestructorCounter = 0; + gConstructorThrowTarget = -1; + gDestructorThrowTarget = 5; + try { + vec_on_stack v; + throw 3; + } + catch ( int i ) {} + + std::cerr << "should never get here" << std::endl; + } + + + +int main ( int argc, char *argv [] ) { + std::set_terminate ( my_terminate ); + test_exception_in_destructor (); + return 1; // we failed if we get here + } diff --git a/libcxxabi/test/test_vector3.cpp b/libcxxabi/test/test_vector3.cpp new file mode 100644 index 00000000000..8143b95c192 --- /dev/null +++ b/libcxxabi/test/test_vector3.cpp @@ -0,0 +1,48 @@ +#include "cxxabi.h" + +#include <stdio.h> +#include <assert.h> +#include <exception> + +#include <memory> + +// use dtors instead of try/catch +namespace test1 { + struct B { + ~B() { + printf("should not be run\n"); + exit(10); + } +}; + +struct A { + ~A() +#if __has_feature(cxx_noexcept) + noexcept(false) +#endif + { + B b; + throw 0; + } +}; +} // test1 + +void my_terminate() { exit(0); } + +template <class T> +void destroy(void* v) +{ + T* t = static_cast<T*>(v); + t->~T(); +} + +int main( int argc, char *argv []) +{ + std::set_terminate(my_terminate); + { + typedef test1::A Array[10]; + Array a[10]; // calls _cxa_vec_dtor + __cxxabiv1::__cxa_vec_dtor(a, 10, sizeof(test1::A), destroy<test1::A>); + assert(false); + } +}
\ No newline at end of file |