1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for
3 // full license information.
4 
5 /*
6      Tests some throw and rethrow situations (mostly CRT a test)
7 */
8 
9 
10 #include <stdlib.h>
11 #include <stdio.h>
12 
13 
14 #define FALSE 0
15 #define TRUE 1
16 #define NO_CTOR_THROW 1
17 #define NO_DTOR_THROW 2
18 
19 
20 int Object[100];
21 int CurrentObjectNumber, Test;
22 int MaxTest = 10;
23 int MaxObjectCount = 1;
24 int Fail;
25 
26 
27 void FAIL(int i)
28 {
29     printf("FAILED on %d\n", i);
30     Fail++;
31 }
32 
33 void dealloc(int i, int no_throw)
34 {
35     /* Make sure i is valid, and object exists */
36     if(i<0 || i>=MaxObjectCount || !Object[i])
37         FAIL(i);
38 
39     Object[i] = 0;
40 }
41 
42 void alloc(int i, int no_throw)
43 {
44     if(CurrentObjectNumber > MaxObjectCount)
45         MaxObjectCount = CurrentObjectNumber;
46 
47     /* Object already exists? */
48     if(Object[i]) FAIL(i);
49 
50     Object[i] = 1;
51 }
52 
53 class B
54 {
55 public:
56     int i;
57     int flag;
58     B();
59     B(int);
60     B(const B &b);
61     ~B();
62 };
63 
64 B::B()
65 {
66     i = CurrentObjectNumber++;
67     printf("B ctor.  i = %d\n", i);
68     alloc(i, FALSE);
69 }
70 
71 B::B(int f)
72 {
73     i = CurrentObjectNumber++;
74     flag = f;
75     printf("B ctor.  i = %d\n", i);
76     alloc(i, flag==NO_CTOR_THROW);
77 }
78 
79 B::B(const B &b)
80 {
81     i = CurrentObjectNumber++;
82     printf("B copy ctor.  i = %d\n", i);
83     alloc(i, FALSE);
84 }
85 
86 B::~B()
87 {
88     printf("B dtor.  i = %d\n", i);
89     dealloc(i, flag==NO_DTOR_THROW);
90 }
91 
92 class A
93 {
94 public:
95     int i;
96     A();
97     A(int)
98     {
99         i = CurrentObjectNumber++;
100         printf("A(int) ctor.  i = %d\n", i);
101         alloc(i, FALSE);
102     }
103     A operator+(A a);
104     A(const A &a)
105     {
106         /* Try objects in ctor */
107         B b1 = NO_DTOR_THROW, b2 = NO_DTOR_THROW;
108 
109         i = CurrentObjectNumber++;
110         printf("A copy ctor.  i = %d\n", i);
111         alloc(i, FALSE);
112     }
113 
114     ~A(){
115         /* Try objects in dtor */
116         B b1 = NO_CTOR_THROW, b2 = NO_CTOR_THROW;
117 
118         printf("A dtor.  i = %d\n", i);
119         dealloc(i, FALSE);
120     };
121 };
122 
123 A::A()
124 {
125     i=CurrentObjectNumber++;
126     printf("A ctor.  i = %d\n", i);
127     alloc(i, FALSE);
128 }
129 
130 A A::operator+(A a)
131 {
132     printf("A%d + A%d\n", i, a.i);
133     return A();
134 }
135 
136 void Throwa(A a)
137 {
138     printf("Throwing\n");
139     throw a;
140 }
141 
142 void bar()
143 {
144     A a;
145 
146     Throwa(a);
147 }
148 
149 void foobar()
150 {
151     B b;
152     bar();
153 }
154 
155 // Somehow, inlining this causes different unwinding order..
156 
157 __declspec(noinline) void Rethrow2()
158 {
159     A a;
160     printf("Rethrowing\n");
161     throw;
162 }
163 
164 #pragma inline_depth(0)
165 void Rethrow()
166 {
167     Rethrow2();
168 }
169 #pragma inline_depth()
170 
171 void foobar2()
172 {
173     B b;
174 
175     try{
176         A a;
177         bar();
178     }catch(A a){
179         printf("In catch;\n");
180         Rethrow();
181     }
182 }
183 
184 void foobar3()
185 {
186     B b;
187 
188     try{
189         A a;
190         bar();
191     }catch(A a){
192         printf("In catch\n");
193         A a2;
194 
195         printf("Throwing new a\n");
196         throw a2;
197     }
198 }
199 
200 void foobar4()
201 {
202     B b;
203 
204     try{
205         B b;
206         try{
207             A a1, a2;
208             try {
209                 A a1, a2;
210                 foobar2();
211             }catch(A a){
212                 printf("In catch #1\n");
213                 B b;
214                 printf("Rethrowing\n");
215                 throw;
216             }
217         }catch(A &a){
218             printf("In catch #2\n");
219             A a2;
220 
221             printf("Throwing new a\n");
222             throw a;
223         }
224     }catch(A a){
225         printf("In catch #3\n");
226         B b;
227         printf("Rethrowing\n");
228         throw;
229     }
230 }
231 
232 __declspec(noinline) void throw_B_2()
233 {
234     B b;
235     printf("Throwing a new b\n");
236     throw b;
237 }
238 
239 #pragma inline_depth(0)
240 void throw_B()
241 {
242     throw_B_2();
243 }
244 #pragma inline_depth()
245 
246 
247 void foobar5()
248 {
249     try {
250         B b1;
251         try {
252             B b2;
253             try {
254                 B b3;
255                 foobar();
256             }catch(B b){
257                 printf("In catch #1\n");
258                 FAIL(-1);
259             }
260             FAIL(-1);
261         }catch(A a){
262             A a2;
263             printf("In catch #2\n");
264             throw_B();
265         }
266         FAIL(-1);
267     }catch(B b){
268         printf("In catch #3\n");
269         printf("Throwing a new a\n");
270         throw A();
271     }
272     FAIL(-1);
273 }
274 
275 
276 /* Simple throw with unwinds */
277 void test1()
278 {
279     A a;
280     foobar();
281 }
282 
283 /* Throw followed by a rethrow */
284 void test2()
285 {
286     A a;
287     foobar2();
288 }
289 
290 /* Throw followed by a new throw */
291 void test3()
292 {
293     A a;
294     foobar3();
295 }
296 
297 /* Nested trys with rethrow/throw/rethrow */
298 void test4()
299 {
300     A a;
301     foobar4();
302 }
303 
304 /* Makes sure a new throw skips appropriate unwound frames. */
305 void test5()
306 {
307     A a;
308     foobar5();
309 }
310 
311 // Tests 3 level of new throw
312 void test6()
313 {
314     try{
315         B b1;
316         try{
317             B b2;
318             try{
319                 B b3;
320                 printf("Throwing a b\n");
321                 throw(b3);
322             }catch(B b){
323                 B b4;
324                 printf("In catch #1\n");
325                 printf("Throwing a new b\n");
326                 throw(b4);
327             }
328             FAIL(-1);
329         }catch(B b){
330             B b5;
331             printf("In catch #2\n");
332             printf("Throwing a new b\n");
333             throw(b5);
334         }
335         FAIL(-1);
336     }catch(B b){
337         A a1;
338         printf("In catch #3\n");
339         printf("Throwing a new a\n");
340         throw(a1);
341     }
342     FAIL(-1);
343 }
344 
345 // Testing try/catch inside a catch
346 void test7()
347 {
348     B b1;
349     try{
350         B b2;
351         try{
352             B b3;
353 
354             printf("Throwing a b\n");
355             throw(B());
356         }catch(B b){
357             B b4;
358             printf("In catch #1\n");
359             try{
360                 B b5;
361                 printf("Rethrowing b\n");
362                 throw;
363             }catch(B b){
364                 B b5;
365                 printf("In catch #1 of catch#1\n");
366                 printf("Rethrowing b\n");
367                 throw;
368             }
369         }
370     }catch(B b){
371         B b6;
372         printf("In catch #2\n");
373         printf("Throwing a new A\n");
374         throw(A());
375     }
376 }
377 
378 void ThrowB()
379 {
380     B b;
381 
382     throw(B());
383 }
384 
385 void bar8()
386 {
387     try{
388         B b5;
389         printf("Rethrowing b\n");
390         Rethrow();
391     }catch(B b){
392         B b5;
393         printf("In catch #1 of catch#1\n");
394         printf("Rethrowing b\n");
395         Rethrow();
396     }
397 }
398 
399 void foo8()
400 {
401     B b;
402     try{
403         B b3;
404 
405         printf("Throwing a b\n");
406         ThrowB();
407     }catch(B b){
408         B b4;
409         printf("In catch #1\n");
410         bar8();
411     }
412 }
413 
414 // Testing call to try/catch function inside a catch
415 void test8()
416 {
417     B b1;
418     try{
419         B b2;
420         foo8();
421     }catch(B b){
422         B b6;
423         printf("In catch #2\n");
424         printf("Throwing a new A\n");
425         throw(A());
426     }
427 }
428 
429 void foo9()
430 {
431     try {
432         puts("Rethrow");
433         throw;
434     }catch(...){
435         puts("In catch #2");
436     }
437 }
438 
439 void test9()
440 {
441     try{
442         B b;
443         puts("Throwing B");
444         throw b;
445     }catch(...){
446         puts("In catch #1");
447         foo9();
448     }
449     puts("End of test9, throwing a A");
450     throw A();
451 }
452 
453 void foo10()
454 {
455     try {
456         puts("Throwing a new B()");
457         throw B();
458     }catch(...){
459         puts("In catch #2");
460     }
461 }
462 
463 void test10()
464 {
465     try{
466         B b;
467         puts("Throwing B");
468         throw b;
469     }catch(...){
470         puts("In catch #1");
471         foo10();
472     }
473     puts("End of test10, throwing a A");
474     throw A();
475 }
476 
477 int main()
478 {
479     int i;
480 
481     /* Call test(), with a different ctor/dtor throwing each time */
482     for(Test = 1; Test <= MaxTest; Test++)  {
483 
484         CurrentObjectNumber = 0;
485 
486         printf("\nTest #%d\n", Test);
487 
488         try {
489             switch(Test){
490             case 1:
491                 test1();
492                 break;
493             case 2:
494                 test2();
495                 break;
496             case 3:
497                 test3();
498                 break;
499             case 4:
500                 test4();
501                 break;
502             case 5:
503                 test5();
504                 break;
505             case 6:
506                 test6();
507                 break;
508             case 7:
509                 test7();
510                 break;
511             case 8:
512                 test8();
513                 break;
514             case 9:
515                 test9();
516                 break;
517             case 10:
518                 test10();
519                 break;
520             }
521 
522             FAIL(-1);
523 
524         }catch(A a){
525             printf("In main's catch\n");
526         }catch(...){
527             FAIL(-1);
528         }
529 
530         /* Any objects which didn't get dtor'd? */
531         for(i = 0; i < MaxObjectCount; i++) {
532             if(Object[i]) {
533                 FAIL(i);
534                 Object[i] = 0;
535             }
536         }
537 
538         printf("\n");
539     }
540 
541     printf("\n");
542     if(Fail)
543         printf("FAILED %d tests\n", Fail);
544     else
545         printf("Passed\n");
546 
547 }
548