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-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 |
18 | void *my_alloc2 ( size_t sz ) { |
19 | void *p = std::malloc ( size: 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 ( ptr: 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 ( ptr: 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 ( element_count: 10, element_size: 40, padding_size: 8, constructor: throw_construct, destructor: throw_destruct )) {} |
74 | ~vec_on_stack () CAN_THROW {__cxxabiv1::__cxa_vec_delete ( array_address: storage, element_size: 40, padding_size: 8, destructor: 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 ( element_count: 10, element_size: 40, padding_size: 0, NULL, NULL ); |
83 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 0, NULL, NULL, alloc: my_alloc2, dealloc: my_dealloc2 ); |
84 | three = __cxxabiv1::__cxa_vec_new3( element_count: 10, element_size: 40, padding_size: 0, NULL, NULL, alloc: my_alloc2, dealloc: my_dealloc3 ); |
85 | |
86 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 0, NULL ); |
87 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 0, NULL, dealloc: my_dealloc2 ); |
88 | __cxxabiv1::__cxa_vec_delete3( array_address: three, element_size: 40, padding_size: 0, NULL, dealloc: my_dealloc3 ); |
89 | |
90 | // Try with no padding |
91 | one = __cxxabiv1::__cxa_vec_new ( element_count: 10, element_size: 40, padding_size: 0, constructor: my_construct, destructor: my_destruct ); |
92 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 0, constructor: my_construct, destructor: my_destruct, alloc: my_alloc2, dealloc: my_dealloc2 ); |
93 | three = __cxxabiv1::__cxa_vec_new3( element_count: 10, element_size: 40, padding_size: 0, constructor: my_construct, destructor: my_destruct, alloc: my_alloc2, dealloc: my_dealloc3 ); |
94 | |
95 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 0, destructor: my_destruct ); |
96 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 0, destructor: my_destruct, dealloc: my_dealloc2 ); |
97 | __cxxabiv1::__cxa_vec_delete3( array_address: three, element_size: 40, padding_size: 0, destructor: my_destruct, dealloc: my_dealloc3 ); |
98 | |
99 | // Padding and no con/destructors |
100 | one = __cxxabiv1::__cxa_vec_new ( element_count: 10, element_size: 40, padding_size: 8, NULL, NULL ); |
101 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 8, NULL, NULL, alloc: my_alloc2, dealloc: my_dealloc2 ); |
102 | three = __cxxabiv1::__cxa_vec_new3( element_count: 10, element_size: 40, padding_size: 8, NULL, NULL, alloc: my_alloc2, dealloc: my_dealloc3 ); |
103 | |
104 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 8, NULL ); |
105 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 8, NULL, dealloc: my_dealloc2 ); |
106 | __cxxabiv1::__cxa_vec_delete3( array_address: three, element_size: 40, padding_size: 8, NULL, dealloc: my_dealloc3 ); |
107 | |
108 | // Padding with con/destructors |
109 | one = __cxxabiv1::__cxa_vec_new ( element_count: 10, element_size: 40, padding_size: 8, constructor: my_construct, destructor: my_destruct ); |
110 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 8, constructor: my_construct, destructor: my_destruct, alloc: my_alloc2, dealloc: my_dealloc2 ); |
111 | three = __cxxabiv1::__cxa_vec_new3( element_count: 10, element_size: 40, padding_size: 8, constructor: my_construct, destructor: my_destruct, alloc: my_alloc2, dealloc: my_dealloc3 ); |
112 | |
113 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 8, destructor: my_destruct ); |
114 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 8, destructor: my_destruct, dealloc: my_dealloc2 ); |
115 | __cxxabiv1::__cxa_vec_delete3( array_address: three, element_size: 40, padding_size: 8, destructor: my_destruct, dealloc: 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 ( element_count: 10, element_size: 40, padding_size: 0, constructor: count_construct, destructor: count_destruct ); |
128 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 0, constructor: count_construct, destructor: count_destruct, alloc: my_alloc2, dealloc: my_dealloc2 ); |
129 | three = __cxxabiv1::__cxa_vec_new3( element_count: 10, element_size: 40, padding_size: 0, constructor: count_construct, destructor: count_destruct, alloc: my_alloc2, dealloc: my_dealloc3 ); |
130 | |
131 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 0, destructor: count_destruct ); |
132 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 0, destructor: count_destruct, dealloc: my_dealloc2 ); |
133 | __cxxabiv1::__cxa_vec_delete3( array_address: three, element_size: 40, padding_size: 0, destructor: count_destruct, dealloc: 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(format: "Mismatched Constructor/Destructor calls (1)\n" ); |
139 | std::printf(format: " Expected 30, got %d\n" , gCounter); |
140 | retVal = 1; |
141 | } |
142 | |
143 | gCounter = 0; |
144 | one = __cxxabiv1::__cxa_vec_new ( element_count: 10, element_size: 40, padding_size: 8, constructor: count_construct, destructor: count_destruct ); |
145 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 8, constructor: count_construct, destructor: count_destruct, alloc: my_alloc2, dealloc: my_dealloc2 ); |
146 | three = __cxxabiv1::__cxa_vec_new3( element_count: 10, element_size: 40, padding_size: 8, constructor: count_construct, destructor: count_destruct, alloc: my_alloc2, dealloc: my_dealloc3 ); |
147 | |
148 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 8, destructor: count_destruct ); |
149 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 8, destructor: count_destruct, dealloc: my_dealloc2 ); |
150 | __cxxabiv1::__cxa_vec_delete3( array_address: three, element_size: 40, padding_size: 8, destructor: count_destruct, dealloc: my_dealloc3 ); |
151 | |
152 | if ( gCounter != 0 ) { |
153 | std::printf(format: "Mismatched Constructor/Destructor calls (2)\n" ); |
154 | std::printf(format: " 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 |
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 ( element_count: 10, element_size: 40, padding_size: 0, constructor: throw_construct, destructor: throw_destruct ); |
174 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 0, constructor: throw_construct, destructor: throw_destruct, alloc: my_alloc2, dealloc: my_dealloc2 ); |
175 | three = __cxxabiv1::__cxa_vec_new3( element_count: 10, element_size: 40, padding_size: 0, constructor: throw_construct, destructor: throw_destruct, alloc: my_alloc2, dealloc: my_dealloc3 ); |
176 | } |
177 | catch ( int i ) {} |
178 | |
179 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 0, destructor: throw_destruct ); |
180 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 0, destructor: throw_destruct, dealloc: my_dealloc2 ); |
181 | __cxxabiv1::__cxa_vec_delete3( array_address: three, element_size: 40, padding_size: 0, destructor: throw_destruct, dealloc: 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(format: "Mismatched Constructor/Destructor calls (1C)\n" ); |
189 | std::printf(format: "%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 ( element_count: 10, element_size: 40, padding_size: 8, constructor: throw_construct, destructor: throw_destruct ); |
199 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 8, constructor: throw_construct, destructor: throw_destruct, alloc: my_alloc2, dealloc: my_dealloc2 ); |
200 | three = __cxxabiv1::__cxa_vec_new3( element_count: 10, element_size: 40, padding_size: 8, constructor: throw_construct, destructor: throw_destruct, alloc: my_alloc2, dealloc: my_dealloc3 ); |
201 | } |
202 | catch ( int i ) {} |
203 | |
204 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 8, destructor: throw_destruct ); |
205 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 8, destructor: throw_destruct, dealloc: my_dealloc2 ); |
206 | __cxxabiv1::__cxa_vec_delete3( array_address: three, element_size: 40, padding_size: 8, destructor: throw_destruct, dealloc: my_dealloc3 ); |
207 | |
208 | if ( gConstructorCounter != gDestructorCounter ) { |
209 | std::printf(format: "Mismatched Constructor/Destructor calls (2C)\n" ); |
210 | std::printf(format: "%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 |
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 ( element_count: 10, element_size: 40, padding_size: 8, constructor: throw_construct, destructor: throw_destruct ); |
232 | two = __cxxabiv1::__cxa_vec_new2( element_count: 10, element_size: 40, padding_size: 8, constructor: throw_construct, destructor: throw_destruct, alloc: my_alloc2, dealloc: my_dealloc2 ); |
233 | } |
234 | catch ( int i ) {} |
235 | |
236 | try { |
237 | __cxxabiv1::__cxa_vec_delete ( array_address: one, element_size: 40, padding_size: 8, destructor: throw_destruct ); |
238 | __cxxabiv1::__cxa_vec_delete2( array_address: two, element_size: 40, padding_size: 8, destructor: throw_destruct, dealloc: 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(format: "Unexpected Constructor/Destructor calls (1D)\n" ); |
248 | std::printf(format: "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(format: "Mismatched Constructor/Destructor calls (2D)\n" ); |
261 | std::printf(format: "%d constructors, but %d destructors\n" , gConstructorCounter, gDestructorCounter); |
262 | retVal = 1; |
263 | } |
264 | |
265 | return retVal; |
266 | } |
267 | #endif |
268 | |
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 | |