279 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "cxxabi.h"10 11#include <cassert>12#include <cstdio>13#include <cstdlib>14 15#include "test_macros.h"16 17// Wrapper routines18void *my_alloc2 ( size_t sz ) {19 void *p = std::malloc ( sz );20// std::printf ( "Allocated %ld bytes at %lx\n", sz, (unsigned long) p );21 return p;22}23 24void my_dealloc2 ( void *p ) {25// std::printf ( "Freeing %lx\n", (unsigned long) p );26 std::free ( p );27}28 29void my_dealloc3 ( void *p, size_t ) {30// std::printf ( "Freeing %lx (size %ld)\n", (unsigned long) p, sz );31 std::free ( p );32}33 34void my_construct ( void * ) {35// std::printf ( "Constructing %lx\n", (unsigned long) p );36}37 38void my_destruct ( void * ) {39// std::printf ( "Destructing %lx\n", (unsigned long) p );40}41 42int gCounter;43void count_construct ( void * ) { ++gCounter; }44void count_destruct ( void * ) { --gCounter; }45 46 47int gConstructorCounter;48int gConstructorThrowTarget;49int gDestructorCounter;50int gDestructorThrowTarget;51void throw_construct ( void * ) {52#ifndef TEST_HAS_NO_EXCEPTIONS53 if ( gConstructorCounter == gConstructorThrowTarget )54 throw 1;55 ++gConstructorCounter;56#endif57}58void throw_destruct ( void * ) {59#ifndef TEST_HAS_NO_EXCEPTIONS60 if ( ++gDestructorCounter == gDestructorThrowTarget )61 throw 2;62#endif63}64 65#if __cplusplus >= 201103L66# define CAN_THROW noexcept(false)67#else68# define CAN_THROW69#endif70 71struct vec_on_stack {72 void *storage;73 vec_on_stack () : storage ( __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct )) {}74 ~vec_on_stack () CAN_THROW {__cxxabiv1::__cxa_vec_delete ( storage, 40, 8, throw_destruct ); }75};76 77// Test calls with empty constructors and destructors78int test_empty ( ) {79 void *one, *two, *three;80 81// Try with no padding and no con/destructors82 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, NULL, NULL );83 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, NULL, NULL, my_alloc2, my_dealloc2 );84 three = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, NULL, NULL, my_alloc2, my_dealloc3 );85 86 __cxxabiv1::__cxa_vec_delete ( one, 40, 0, NULL );87 __cxxabiv1::__cxa_vec_delete2( two, 40, 0, NULL, my_dealloc2 );88 __cxxabiv1::__cxa_vec_delete3( three, 40, 0, NULL, my_dealloc3 );89 90// Try with no padding91 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, my_construct, my_destruct );92 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, my_construct, my_destruct, my_alloc2, my_dealloc2 );93 three = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, my_construct, my_destruct, my_alloc2, my_dealloc3 );94 95 __cxxabiv1::__cxa_vec_delete ( one, 40, 0, my_destruct );96 __cxxabiv1::__cxa_vec_delete2( two, 40, 0, my_destruct, my_dealloc2 );97 __cxxabiv1::__cxa_vec_delete3( three, 40, 0, my_destruct, my_dealloc3 );98 99// Padding and no con/destructors100 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, NULL, NULL );101 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, NULL, NULL, my_alloc2, my_dealloc2 );102 three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, NULL, NULL, my_alloc2, my_dealloc3 );103 104 __cxxabiv1::__cxa_vec_delete ( one, 40, 8, NULL );105 __cxxabiv1::__cxa_vec_delete2( two, 40, 8, NULL, my_dealloc2 );106 __cxxabiv1::__cxa_vec_delete3( three, 40, 8, NULL, my_dealloc3 );107 108// Padding with con/destructors109 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, my_construct, my_destruct );110 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, my_construct, my_destruct, my_alloc2, my_dealloc2 );111 three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, my_construct, my_destruct, my_alloc2, my_dealloc3 );112 113 __cxxabiv1::__cxa_vec_delete ( one, 40, 8, my_destruct );114 __cxxabiv1::__cxa_vec_delete2( two, 40, 8, my_destruct, my_dealloc2 );115 __cxxabiv1::__cxa_vec_delete3( three, 40, 8, my_destruct, my_dealloc3 );116 117 return 0;118}119 120// Make sure the constructors and destructors are matched121int test_counted ( ) {122 int retVal = 0;123 void *one, *two, *three;124 125// Try with no padding126 gCounter = 0;127 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, count_construct, count_destruct );128 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, count_construct, count_destruct, my_alloc2, my_dealloc2 );129 three = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, count_construct, count_destruct, my_alloc2, my_dealloc3 );130 131 __cxxabiv1::__cxa_vec_delete ( one, 40, 0, count_destruct );132 __cxxabiv1::__cxa_vec_delete2( two, 40, 0, count_destruct, my_dealloc2 );133 __cxxabiv1::__cxa_vec_delete3( three, 40, 0, count_destruct, my_dealloc3 );134 135// Since there was no padding, the # of elements in the array are not stored136// and the destructors are not called.137 if ( gCounter != 30 ) {138 std::printf("Mismatched Constructor/Destructor calls (1)\n");139 std::printf(" Expected 30, got %d\n", gCounter);140 retVal = 1;141 }142 143 gCounter = 0;144 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, count_construct, count_destruct );145 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, count_construct, count_destruct, my_alloc2, my_dealloc2 );146 three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, count_construct, count_destruct, my_alloc2, my_dealloc3 );147 148 __cxxabiv1::__cxa_vec_delete ( one, 40, 8, count_destruct );149 __cxxabiv1::__cxa_vec_delete2( two, 40, 8, count_destruct, my_dealloc2 );150 __cxxabiv1::__cxa_vec_delete3( three, 40, 8, count_destruct, my_dealloc3 );151 152 if ( gCounter != 0 ) {153 std::printf("Mismatched Constructor/Destructor calls (2)\n");154 std::printf(" Expected 0, got %d\n", gCounter);155 retVal = 1;156 }157 158 return retVal;159}160 161#ifndef TEST_HAS_NO_EXCEPTIONS162// Make sure the constructors and destructors are matched163int test_exception_in_constructor ( ) {164 int retVal = 0;165 void *one, *two, *three;166 167// Try with no padding168 gConstructorCounter = gDestructorCounter = 0;169 gConstructorThrowTarget = 15;170 gDestructorThrowTarget = -1;171 try {172 one = two = three = NULL;173 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, throw_construct, throw_destruct );174 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, throw_construct, throw_destruct, my_alloc2, my_dealloc2 );175 three = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, throw_construct, throw_destruct, my_alloc2, my_dealloc3 );176 }177 catch ( int i ) {}178 179 __cxxabiv1::__cxa_vec_delete ( one, 40, 0, throw_destruct );180 __cxxabiv1::__cxa_vec_delete2( two, 40, 0, throw_destruct, my_dealloc2 );181 __cxxabiv1::__cxa_vec_delete3( three, 40, 0, throw_destruct, my_dealloc3 );182 183// Since there was no padding, the # of elements in the array are not stored184// and the destructors are not called.185// Since we threw after 15 calls to the constructor, we should see 5 calls to186// the destructor from the partially constructed array.187 if ( gConstructorCounter - gDestructorCounter != 10 ) {188 std::printf("Mismatched Constructor/Destructor calls (1C)\n");189 std::printf("%d constructors, but %d destructors\n", gConstructorCounter, gDestructorCounter);190 retVal = 1;191 }192 193 gConstructorCounter = gDestructorCounter = 0;194 gConstructorThrowTarget = 15;195 gDestructorThrowTarget = -1;196 try {197 one = two = three = NULL;198 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct );199 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc2 );200 three = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc3 );201 }202 catch ( int i ) {}203 204 __cxxabiv1::__cxa_vec_delete ( one, 40, 8, throw_destruct );205 __cxxabiv1::__cxa_vec_delete2( two, 40, 8, throw_destruct, my_dealloc2 );206 __cxxabiv1::__cxa_vec_delete3( three, 40, 8, throw_destruct, my_dealloc3 );207 208 if ( gConstructorCounter != gDestructorCounter ) {209 std::printf("Mismatched Constructor/Destructor calls (2C)\n");210 std::printf("%d constructors, but %d destructors\n", gConstructorCounter, gDestructorCounter);211 retVal = 1;212 }213 214 return retVal;215}216#endif217 218#ifndef TEST_HAS_NO_EXCEPTIONS219// Make sure the constructors and destructors are matched220int test_exception_in_destructor ( ) {221 int retVal = 0;222 void *one, *two, *three;223 one = two = three = NULL;224 225// Throw from within a destructor226 gConstructorCounter = gDestructorCounter = 0;227 gConstructorThrowTarget = -1;228 gDestructorThrowTarget = 15;229 try {230 one = two = NULL;231 one = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct );232 two = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc2 );233 }234 catch ( int i ) {}235 236 try {237 __cxxabiv1::__cxa_vec_delete ( one, 40, 8, throw_destruct );238 __cxxabiv1::__cxa_vec_delete2( two, 40, 8, throw_destruct, my_dealloc2 );239 assert(false);240 }241 catch ( int i ) {}242 243// We should have thrown in the middle of cleaning up "two", which means that244// there should be 20 calls to the destructor and the try block should exit245// before the assertion.246 if ( gConstructorCounter != 20 || gDestructorCounter != 20 ) {247 std::printf("Unexpected Constructor/Destructor calls (1D)\n");248 std::printf("Expected (20, 20), but got (%d, %d)\n", gConstructorCounter, gDestructorCounter);249 retVal = 1;250 }251 252// Try throwing from a destructor - should be fine.253 gConstructorCounter = gDestructorCounter = 0;254 gConstructorThrowTarget = -1;255 gDestructorThrowTarget = 5;256 try { vec_on_stack v; }257 catch ( int i ) {}258 259 if ( gConstructorCounter != gDestructorCounter ) {260 std::printf("Mismatched Constructor/Destructor calls (2D)\n");261 std::printf("%d constructors, but %d destructors\n", gConstructorCounter, gDestructorCounter);262 retVal = 1;263 }264 265 return retVal;266}267#endif268 269int main(int, char**) {270 int retVal = 0;271 retVal += test_empty ();272 retVal += test_counted ();273#ifndef TEST_HAS_NO_EXCEPTIONS274 retVal += test_exception_in_constructor ();275 retVal += test_exception_in_destructor ();276#endif277 return retVal;278}279