1//===---------------------------- test_vector.cpp -------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is dual licensed under the MIT and the University of Illinois Open
6// Source Licenses. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "cxxabi.h"
11
12#include <iostream>
13#include <cstdlib>
14
15//  Wrapper routines
16void *my_alloc2 ( size_t sz ) {
17    void *p = std::malloc ( sz );
18//  std::printf ( "Allocated %ld bytes at %lx\n", sz, (unsigned long) p );
19    return p;
20    }
21
22void my_dealloc2 ( void *p ) {
23//  std::printf ( "Freeing %lx\n", (unsigned long) p );
24    std::free ( p );
25    }
26
27void my_dealloc3 ( void *p, size_t   sz   ) {
28//  std::printf ( "Freeing %lx (size %ld)\n", (unsigned long) p, sz );
29    std::free ( p );
30    }
31
32void my_construct ( void *p ) {
33//  std::printf ( "Constructing %lx\n", (unsigned long) p );
34    }
35
36void my_destruct  ( void *p ) {
37//  std::printf ( "Destructing  %lx\n", (unsigned long) p );
38    }
39
40int gCounter;
41void count_construct ( void *p ) { ++gCounter; }
42void count_destruct  ( void *p ) { --gCounter; }
43
44
45int gConstructorCounter;
46int gConstructorThrowTarget;
47int gDestructorCounter;
48int gDestructorThrowTarget;
49void throw_construct ( void *p ) { if ( gConstructorCounter   == gConstructorThrowTarget ) throw 1; ++gConstructorCounter; }
50void throw_destruct  ( void *p ) { if ( ++gDestructorCounter  == gDestructorThrowTarget  ) throw 2; }
51
52#if __cplusplus >= 201103L
53#   define CAN_THROW noexcept(false)
54#else
55#   define CAN_THROW
56#endif
57
58struct vec_on_stack {
59    void *storage;
60    vec_on_stack () : storage ( __cxxabiv1::__cxa_vec_new    (            10, 40, 8, throw_construct, throw_destruct )) {}
61    ~vec_on_stack () CAN_THROW {__cxxabiv1::__cxa_vec_delete ( storage,       40, 8,                  throw_destruct );  }
62    };
63
64//  Test calls with empty constructors and destructors
65int test_empty ( ) {
66    void *one, *two, *three;
67
68//  Try with no padding and no con/destructors
69    one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, NULL, NULL );
70    two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, NULL, NULL, my_alloc2, my_dealloc2 );
71    three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, NULL, NULL, my_alloc2, my_dealloc3 );
72
73    __cxxabiv1::__cxa_vec_delete ( one,       40, 0, NULL );
74    __cxxabiv1::__cxa_vec_delete2( two,       40, 0, NULL, my_dealloc2 );
75    __cxxabiv1::__cxa_vec_delete3( three,     40, 0, NULL, my_dealloc3 );
76
77//  Try with no padding
78    one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, my_construct, my_destruct );
79    two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, my_construct, my_destruct, my_alloc2, my_dealloc2 );
80    three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, my_construct, my_destruct, my_alloc2, my_dealloc3 );
81
82    __cxxabiv1::__cxa_vec_delete ( one,       40, 0, my_destruct );
83    __cxxabiv1::__cxa_vec_delete2( two,       40, 0, my_destruct, my_dealloc2 );
84    __cxxabiv1::__cxa_vec_delete3( three,     40, 0, my_destruct, my_dealloc3 );
85
86//  Padding and no con/destructors
87    one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, NULL, NULL );
88    two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, NULL, NULL, my_alloc2, my_dealloc2 );
89    three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, NULL, NULL, my_alloc2, my_dealloc3 );
90
91    __cxxabiv1::__cxa_vec_delete ( one,       40, 8, NULL );
92    __cxxabiv1::__cxa_vec_delete2( two,       40, 8, NULL, my_dealloc2 );
93    __cxxabiv1::__cxa_vec_delete3( three,     40, 8, NULL, my_dealloc3 );
94
95//  Padding with con/destructors
96    one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, my_construct, my_destruct );
97    two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, my_construct, my_destruct, my_alloc2, my_dealloc2 );
98    three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, my_construct, my_destruct, my_alloc2, my_dealloc3 );
99
100    __cxxabiv1::__cxa_vec_delete ( one,       40, 8, my_destruct );
101    __cxxabiv1::__cxa_vec_delete2( two,       40, 8, my_destruct, my_dealloc2 );
102    __cxxabiv1::__cxa_vec_delete3( three,     40, 8, my_destruct, my_dealloc3 );
103
104    return 0;
105    }
106
107//  Make sure the constructors and destructors are matched
108int test_counted ( ) {
109    int retVal = 0;
110    void *one, *two, *three;
111
112//  Try with no padding
113    gCounter = 0;
114    one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, count_construct, count_destruct );
115    two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, count_construct, count_destruct, my_alloc2, my_dealloc2 );
116    three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, count_construct, count_destruct, my_alloc2, my_dealloc3 );
117
118    __cxxabiv1::__cxa_vec_delete ( one,       40, 0, count_destruct );
119    __cxxabiv1::__cxa_vec_delete2( two,       40, 0, count_destruct, my_dealloc2 );
120    __cxxabiv1::__cxa_vec_delete3( three,     40, 0, count_destruct, my_dealloc3 );
121
122//  Since there was no padding, the # of elements in the array are not stored
123//  and the destructors are not called.
124    if ( gCounter != 30 ) {
125        std::cerr << "Mismatched Constructor/Destructor calls (1)" << std::endl;
126        std::cerr << "  Expected 30, got " << gCounter << std::endl;
127        retVal = 1;
128        }
129
130    gCounter = 0;
131    one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, count_construct, count_destruct );
132    two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, count_construct, count_destruct, my_alloc2, my_dealloc2 );
133    three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, count_construct, count_destruct, my_alloc2, my_dealloc3 );
134
135    __cxxabiv1::__cxa_vec_delete ( one,       40, 8, count_destruct );
136    __cxxabiv1::__cxa_vec_delete2( two,       40, 8, count_destruct, my_dealloc2 );
137    __cxxabiv1::__cxa_vec_delete3( three,     40, 8, count_destruct, my_dealloc3 );
138
139    if ( gCounter != 0 ) {
140        std::cerr << "Mismatched Constructor/Destructor calls (2)" << std::endl;
141        std::cerr << "  Expected 0, got " << gCounter << std::endl;
142        retVal = 1;
143        }
144
145    return retVal;
146    }
147
148//  Make sure the constructors and destructors are matched
149int test_exception_in_constructor ( ) {
150    int retVal = 0;
151    void *one, *two, *three;
152
153//  Try with no padding
154    gConstructorCounter = gDestructorCounter = 0;
155    gConstructorThrowTarget = 15;
156    gDestructorThrowTarget  = -1;
157    try {
158        one = two = three = NULL;
159        one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 0, throw_construct, throw_destruct );
160        two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 0, throw_construct, throw_destruct, my_alloc2, my_dealloc2 );
161        three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 0, throw_construct, throw_destruct, my_alloc2, my_dealloc3 );
162        }
163    catch ( int i ) {}
164
165    __cxxabiv1::__cxa_vec_delete ( one,       40, 0, throw_destruct );
166    __cxxabiv1::__cxa_vec_delete2( two,       40, 0, throw_destruct, my_dealloc2 );
167    __cxxabiv1::__cxa_vec_delete3( three,     40, 0, throw_destruct, my_dealloc3 );
168
169//  Since there was no padding, the # of elements in the array are not stored
170//  and the destructors are not called.
171//  Since we threw after 15 calls to the constructor, we should see 5 calls to
172//      the destructor from the partially constructed array.
173    if ( gConstructorCounter - gDestructorCounter != 10 ) {
174        std::cerr << "Mismatched Constructor/Destructor calls (1C)" << std::endl;
175        std::cerr << gConstructorCounter << " constructors, but " <<
176                gDestructorCounter << " destructors" << std::endl;
177        retVal = 1;
178        }
179
180    gConstructorCounter = gDestructorCounter = 0;
181    gConstructorThrowTarget = 15;
182    gDestructorThrowTarget  = -1;
183    try {
184        one = two = three = NULL;
185        one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct );
186        two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc2 );
187        three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc3 );
188        }
189    catch ( int i ) {}
190
191    __cxxabiv1::__cxa_vec_delete ( one,       40, 8, throw_destruct );
192    __cxxabiv1::__cxa_vec_delete2( two,       40, 8, throw_destruct, my_dealloc2 );
193    __cxxabiv1::__cxa_vec_delete3( three,     40, 8, throw_destruct, my_dealloc3 );
194
195    if ( gConstructorCounter != gDestructorCounter ) {
196        std::cerr << "Mismatched Constructor/Destructor calls (2C)" << std::endl;
197        std::cerr << gConstructorCounter << " constructors, but " <<
198                gDestructorCounter << " destructors" << std::endl;
199        retVal = 1;
200        }
201
202    return retVal;
203    }
204
205//  Make sure the constructors and destructors are matched
206int test_exception_in_destructor ( ) {
207    int retVal = 0;
208    void *one, *two, *three;
209
210//  Throw from within a destructor
211    gConstructorCounter = gDestructorCounter = 0;
212    gConstructorThrowTarget = -1;
213    gDestructorThrowTarget  = 15;
214    try {
215        one = two = three = NULL;
216        one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct );
217        two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc2 );
218        three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc3 );
219        }
220    catch ( int i ) {}
221
222    try {
223        __cxxabiv1::__cxa_vec_delete ( one,       40, 8, throw_destruct );
224        __cxxabiv1::__cxa_vec_delete2( two,       40, 8, throw_destruct, my_dealloc2 );
225        __cxxabiv1::__cxa_vec_delete3( three,     40, 8, throw_destruct, my_dealloc3 );
226        }
227    catch ( int i ) {}
228
229//  We should have thrown in the middle of cleaning up "two", which means that
230//  there should be 20 calls to the destructor, and "three" was not cleaned up.
231    if ( gConstructorCounter != 30 || gDestructorCounter != 20 ) {
232        std::cerr << "Unexpected Constructor/Destructor calls (1D)" << std::endl;
233        std::cerr << "Expected (30, 20), but got (" << gConstructorCounter << ", " <<
234                gDestructorCounter << ")" << std::endl;
235        retVal = 1;
236        }
237
238//  Try throwing from a destructor - should be fine.
239    gConstructorCounter = gDestructorCounter = 0;
240    gConstructorThrowTarget = -1;
241    gDestructorThrowTarget  = 5;
242    try { vec_on_stack v; }
243    catch ( int i ) {}
244
245    if ( gConstructorCounter != gDestructorCounter ) {
246        std::cerr << "Mismatched Constructor/Destructor calls (2D)" << std::endl;
247        std::cerr << gConstructorCounter << " constructors, but " <<
248                gDestructorCounter << " destructors" << std::endl;
249        retVal = 1;
250        }
251
252    return retVal;
253    }
254
255int main ( int argc, char *argv [] ) {
256    int retVal = 0;
257    retVal += test_empty ();
258    retVal += test_counted ();
259    retVal += test_exception_in_constructor ();
260    retVal += test_exception_in_destructor ();
261    return retVal;
262    }
263