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 <cassert>
12 #include <cstdio>
13 #include <cstdlib>
14 
15 #include "test_macros.h"
16 
17 //  Wrapper routines
my_alloc2(size_t sz)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 
my_dealloc2(void * p)24 void my_dealloc2 ( void *p ) {
25 //  std::printf ( "Freeing %lx\n", (unsigned long) p );
26     std::free ( p );
27 }
28 
my_dealloc3(void * p,size_t)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 
my_construct(void *)34 void my_construct ( void * ) {
35 //  std::printf ( "Constructing %lx\n", (unsigned long) p );
36 }
37 
my_destruct(void *)38 void my_destruct  ( void * ) {
39 //  std::printf ( "Destructing  %lx\n", (unsigned long) p );
40 }
41 
42 int gCounter;
count_construct(void *)43 void count_construct ( void * ) { ++gCounter; }
count_destruct(void *)44 void count_destruct  ( void * ) { --gCounter; }
45 
46 
47 int gConstructorCounter;
48 int gConstructorThrowTarget;
49 int gDestructorCounter;
50 int gDestructorThrowTarget;
throw_construct(void *)51 void throw_construct ( void * ) {
52 #ifndef TEST_HAS_NO_EXCEPTIONS
53     if ( gConstructorCounter   == gConstructorThrowTarget )
54         throw 1;
55     ++gConstructorCounter;
56 #endif
57 }
throw_destruct(void *)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;
vec_on_stackvec_on_stack73     vec_on_stack () : storage ( __cxxabiv1::__cxa_vec_new    (            10, 40, 8, throw_construct, throw_destruct )) {}
~vec_on_stackvec_on_stack74     ~vec_on_stack () CAN_THROW {__cxxabiv1::__cxa_vec_delete ( storage,       40, 8,                  throw_destruct );  }
75 };
76 
77 //  Test calls with empty constructors and destructors
test_empty()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
test_counted()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::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_EXCEPTIONS
162 //  Make sure the constructors and destructors are matched
test_exception_in_constructor()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::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 #endif
217 
218 #ifndef TEST_HAS_NO_EXCEPTIONS
219 //  Make sure the constructors and destructors are matched
test_exception_in_destructor()220 int test_exception_in_destructor ( ) {
221     int retVal = 0;
222     void *one, *two, *three;
223     one = two = three = NULL;
224 
225 //  Throw from within a destructor
226     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 that
244 //  there should be 20 calls to the destructor and the try block should exit
245 //  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 #endif
268 
main(int,char **)269 int main(int, char**) {
270     int retVal = 0;
271     retVal += test_empty ();
272     retVal += test_counted ();
273 #ifndef TEST_HAS_NO_EXCEPTIONS
274     retVal += test_exception_in_constructor ();
275     retVal += test_exception_in_destructor ();
276 #endif
277     return retVal;
278 }
279