1 //===---------------------------- test_vector.cpp -------------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "cxxabi.h"
10 
11 #include <iostream>
12 #include <cstdlib>
13 #include <cassert>
14 
15 #include "test_macros.h"
16 
17 //  Wrapper routines
18 void *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 
24 void my_dealloc2 ( void *p ) {
25 //  std::printf ( "Freeing %lx\n", (unsigned long) p );
26     std::free ( p );
27     }
28 
29 void my_dealloc3 ( void *p, size_t ) {
30 //  std::printf ( "Freeing %lx (size %ld)\n", (unsigned long) p, sz );
31     std::free ( p );
32     }
33 
34 void my_construct ( void * ) {
35 //  std::printf ( "Constructing %lx\n", (unsigned long) p );
36     }
37 
38 void my_destruct  ( void * ) {
39 //  std::printf ( "Destructing  %lx\n", (unsigned long) p );
40     }
41 
42 int gCounter;
43 void count_construct ( void * ) { ++gCounter; }
44 void count_destruct  ( void * ) { --gCounter; }
45 
46 
47 int gConstructorCounter;
48 int gConstructorThrowTarget;
49 int gDestructorCounter;
50 int gDestructorThrowTarget;
51 void throw_construct ( void * ) {
52 #ifndef TEST_HAS_NO_EXCEPTIONS
53     if ( gConstructorCounter   == gConstructorThrowTarget )
54         throw 1;
55     ++gConstructorCounter;
56 #endif
57 }
58 void throw_destruct  ( void * ) {
59 #ifndef TEST_HAS_NO_EXCEPTIONS
60     if ( ++gDestructorCounter  == gDestructorThrowTarget  )
61         throw 2;
62 #endif
63 }
64 
65 #if __cplusplus >= 201103L
66 #   define CAN_THROW noexcept(false)
67 #else
68 #   define CAN_THROW
69 #endif
70 
71 struct 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 destructors
78 int test_empty ( ) {
79     void *one, *two, *three;
80 
81 //  Try with no padding and no con/destructors
82     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 padding
91     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/destructors
100     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/destructors
109     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 matched
121 int test_counted ( ) {
122     int retVal = 0;
123     void *one, *two, *three;
124 
125 //  Try with no padding
126     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 stored
136 //  and the destructors are not called.
137     if ( gCounter != 30 ) {
138         std::cerr << "Mismatched Constructor/Destructor calls (1)" << std::endl;
139         std::cerr << "  Expected 30, got " << gCounter << std::endl;
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::cerr << "Mismatched Constructor/Destructor calls (2)" << std::endl;
154         std::cerr << "  Expected 0, got " << gCounter << std::endl;
155         retVal = 1;
156         }
157 
158     return retVal;
159     }
160 
161 #ifndef TEST_HAS_NO_EXCEPTIONS
162 //  Make sure the constructors and destructors are matched
163 int test_exception_in_constructor ( ) {
164     int retVal = 0;
165     void *one, *two, *three;
166 
167 //  Try with no padding
168     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 stored
184 //  and the destructors are not called.
185 //  Since we threw after 15 calls to the constructor, we should see 5 calls to
186 //      the destructor from the partially constructed array.
187     if ( gConstructorCounter - gDestructorCounter != 10 ) {
188         std::cerr << "Mismatched Constructor/Destructor calls (1C)" << std::endl;
189         std::cerr << gConstructorCounter << " constructors, but " <<
190                 gDestructorCounter << " destructors" << std::endl;
191         retVal = 1;
192         }
193 
194     gConstructorCounter = gDestructorCounter = 0;
195     gConstructorThrowTarget = 15;
196     gDestructorThrowTarget  = -1;
197     try {
198         one = two = three = NULL;
199         one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct );
200         two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc2 );
201         three   = __cxxabiv1::__cxa_vec_new3( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc3 );
202         }
203     catch ( int i ) {}
204 
205     __cxxabiv1::__cxa_vec_delete ( one,       40, 8, throw_destruct );
206     __cxxabiv1::__cxa_vec_delete2( two,       40, 8, throw_destruct, my_dealloc2 );
207     __cxxabiv1::__cxa_vec_delete3( three,     40, 8, throw_destruct, my_dealloc3 );
208 
209     if ( gConstructorCounter != gDestructorCounter ) {
210         std::cerr << "Mismatched Constructor/Destructor calls (2C)" << std::endl;
211         std::cerr << gConstructorCounter << " constructors, but " <<
212                 gDestructorCounter << " destructors" << std::endl;
213         retVal = 1;
214         }
215 
216     return retVal;
217     }
218 #endif
219 
220 #ifndef TEST_HAS_NO_EXCEPTIONS
221 //  Make sure the constructors and destructors are matched
222 int test_exception_in_destructor ( ) {
223     int retVal = 0;
224     void *one, *two, *three;
225     one = two = three = NULL;
226 
227 //  Throw from within a destructor
228     gConstructorCounter = gDestructorCounter = 0;
229     gConstructorThrowTarget = -1;
230     gDestructorThrowTarget  = 15;
231     try {
232         one = two = NULL;
233         one     = __cxxabiv1::__cxa_vec_new ( 10, 40, 8, throw_construct, throw_destruct );
234         two     = __cxxabiv1::__cxa_vec_new2( 10, 40, 8, throw_construct, throw_destruct, my_alloc2, my_dealloc2 );
235         }
236     catch ( int i ) {}
237 
238     try {
239         __cxxabiv1::__cxa_vec_delete ( one,       40, 8, throw_destruct );
240         __cxxabiv1::__cxa_vec_delete2( two,       40, 8, throw_destruct, my_dealloc2 );
241         assert(false);
242         }
243     catch ( int i ) {}
244 
245 //  We should have thrown in the middle of cleaning up "two", which means that
246 //  there should be 20 calls to the destructor and the try block should exit
247 //  before the assertion.
248     if ( gConstructorCounter != 20 || gDestructorCounter != 20 ) {
249         std::cerr << "Unexpected Constructor/Destructor calls (1D)" << std::endl;
250         std::cerr << "Expected (20, 20), but got (" << gConstructorCounter << ", " <<
251                 gDestructorCounter << ")" << std::endl;
252         retVal = 1;
253         }
254 
255 //  Try throwing from a destructor - should be fine.
256     gConstructorCounter = gDestructorCounter = 0;
257     gConstructorThrowTarget = -1;
258     gDestructorThrowTarget  = 5;
259     try { vec_on_stack v; }
260     catch ( int i ) {}
261 
262     if ( gConstructorCounter != gDestructorCounter ) {
263         std::cerr << "Mismatched Constructor/Destructor calls (2D)" << std::endl;
264         std::cerr << gConstructorCounter << " constructors, but " <<
265                 gDestructorCounter << " destructors" << std::endl;
266         retVal = 1;
267         }
268 
269     return retVal;
270     }
271 #endif
272 
273 int main () {
274     int retVal = 0;
275     retVal += test_empty ();
276     retVal += test_counted ();
277 #ifndef TEST_HAS_NO_EXCEPTIONS
278     retVal += test_exception_in_constructor ();
279     retVal += test_exception_in_destructor ();
280 #endif
281     return retVal;
282     }
283