1#include "test.h"
2#include "unwind.h"
3#include <stdio.h>
4#include <stdlib.h>
5#include <stdint.h>
6
7#include <exception>
8
9#define fprintf(...)
10
11void log_cleanup(void* ignored)
12{
13	//printf("Cleanup called on %s\n", *(char**)ignored);
14}
15#define CLEANUP\
16	__attribute__((cleanup(log_cleanup))) __attribute__((unused))\
17		const char *f = __func__;
18
19/**
20 * Simple struct to test throwing.
21 */
22struct foo
23{
24	int i;
25};
26
27struct bar : foo
28{
29	float bar;
30};
31
32
33/**
34 * Non-pod type to test throwing
35 */
36class non_pod {
37public:
38    non_pod(int i): x(i) {}
39    int x;
40};
41
42
43static int cleanup_count;
44/**
45 * Simple structure declared with a destructor.  Destroying this object will
46 * increment cleanup count.  The destructor should be called automatically if
47 * an instance of cl is allocated with automatic storage.
48 */
49struct cl
50{
51	int i;
52	~cl() { fprintf(stderr, "cl destroyed: %d\n", i); cleanup_count++; }
53};
54/**
55 * Test that one cl was destroyed when running the argument.
56 */
57#define TEST_CLEANUP(x) do {\
58		int cleanups = cleanup_count;\
59		{ x; }\
60		TEST(cleanup_count == cleanups+1, "Cleanup ran correctly");\
61	} while(0)
62
63int inner(int i)
64{
65	CLEANUP
66	switch (i)
67	{
68		case 0: throw (int)1.0;
69		case 1: throw (float)1.0;
70		case 2: fprintf(stderr, "Throwing int64_t\n");throw (int64_t)1;
71		case 3: { foo f = {2} ; throw f; }
72		case 4: { bar f; f.i = 2 ; f.bar=1 ; throw f; }
73        case 5: throw non_pod(3);
74	}
75	return -1;
76}
77
78int outer(int i) throw(float, int, foo, non_pod)
79{
80	//CLEANUP
81	inner(i);
82	return 1;
83}
84
85static void test_const(void)
86{
87	int a = 1;
88	try
89	{
90		throw a;
91	}
92	catch (const int b)
93	{
94		TEST(a == b, "Caught int as const int");
95	}
96	catch(...)
97	{
98		TEST(0, "Failed to catch int as const int");
99	}
100	try
101	{
102		throw &a;
103	}
104	catch (const int *b)
105	{
106		TEST(&a == b, "Caught int* as const int*");
107	}
108	catch(...)
109	{
110		TEST(0, "Failed to catch int* as const int*");
111	}
112}
113
114static void test_catch(int s)
115{
116	cl c;
117	c.i = 12;
118	fprintf(stderr, "Entering try\n");
119	try
120	{
121		outer(s);
122	}
123	catch(int i)
124	{
125		fprintf(stderr, "Caught int %d in test %d\n", i, s);
126		TEST((s == 0 && i == 1) || (s == 2 && i == 0), "Caught int");
127		return;
128	}
129	catch (float f)
130	{
131		fprintf(stderr, "Caught float %f!\n", f);
132		TEST(s == 1 && f == 1, "Caught float");
133		return;
134	}
135	catch (foo f)
136	{
137		fprintf(stderr, "Caught struct {%d}!\n", f.i);
138		TEST((s == 3 || s == 4) && f.i == 2, "Caught struct");
139		return;
140	}
141    catch (non_pod np) {
142        fprintf(stderr, "Caught non_pod {%d}!\n", np.x);
143        TEST(s == 5 && np.x == 3, "Caught non_pod");
144        return;
145    }
146	//abort();
147	TEST(0, "Unreachable line reached");
148}
149
150void test_nested1(void)
151{
152	CLEANUP;
153	cl c;
154	c.i = 123;
155	try
156	{
157		outer(0);
158	}
159	catch (int a)
160	{
161		try
162		{
163			TEST(a == 1, "Caught int");
164			outer(1);
165		}
166		catch (float f)
167		{
168			TEST(f == 1, "Caught float inside outer catch block");
169			throw;
170		}
171	}
172}
173
174void test_nested()
175{
176	try
177	{
178		test_nested1();
179	}
180	catch (float f)
181	{
182		fprintf(stderr, "Caught re-thrown float\n");
183		TEST(f == 1, "Caught re-thrown float");
184	}
185}
186
187static int violations = 0;
188static void throw_zero()
189{
190	violations++;
191	fprintf(stderr, "Throwing 0\n");
192	throw 0;
193}
194
195struct uncaught_exception_checker
196{
197	uncaught_exception_checker(bool uncaught) : m_uncaught(uncaught) {}
198	~uncaught_exception_checker() {
199		if (std::uncaught_exception())
200			TEST(m_uncaught, "At least one uncaught exception is in flight");
201		else
202			TEST(!m_uncaught, "No uncaught exceptions are in flight");
203	}
204	bool m_uncaught;
205};
206
207void test_rethrown_uncaught_exception()
208{
209	uncaught_exception_checker outer(false);
210	try
211	{
212		try
213		{
214			throw 42;
215		}
216		catch (int)
217		{
218			uncaught_exception_checker inner(true);
219			throw;
220		}
221	}
222	catch (...) {}
223}
224
225static void exception_cleanup(_Unwind_Reason_Code, struct _Unwind_Exception *ex)
226{
227	delete ex;
228}
229
230void test_rethrown_uncaught_foreign_exception()
231{
232	uncaught_exception_checker outer(false);
233	try
234	{
235		try
236		{
237			// Throw a foreign exception.
238			_Unwind_Exception *ex = new _Unwind_Exception;
239			ex->exception_class = 1234;
240			ex->exception_cleanup = exception_cleanup;
241			_Unwind_RaiseException(ex);
242		}
243		catch (...)
244		{
245			// Note: Uncaught exceptions doesn't report foreign exceptions,
246			// because we have no way of receiving a report that the other
247			// language has caught it.
248			uncaught_exception_checker inner(false);
249			throw;
250		}
251	}
252	catch (...) {}
253}
254
255
256void test_uncaught_exception()
257{
258	uncaught_exception_checker outer(false);
259	try {
260		uncaught_exception_checker inner(true);
261		throw 42;
262	}
263	catch (...) {}
264}
265
266struct uncaught_exceptions_checker
267{
268	uncaught_exceptions_checker(int uncaught) : m_uncaught(uncaught) {}
269	~uncaught_exceptions_checker() {
270		char msg[128];
271		int uncaught = std::uncaught_exceptions();
272		snprintf(msg, sizeof msg, "%d uncaught exception%s in flight",
273		    uncaught, uncaught == 1 ? " is" : "s are");
274		TEST(uncaught == m_uncaught, msg);
275	}
276	int m_uncaught;
277};
278
279class top {
280public:
281	~top() {
282		try {
283			uncaught_exceptions_checker uec(4);
284			throw "top";
285		}
286		catch (...) {}
287	}
288};
289
290class middle {
291public:
292	~middle() {
293		try {
294			top f;
295			uncaught_exceptions_checker uec(3);
296			throw "middle";
297		}
298		catch (...) {}
299	}
300};
301
302class bottom {
303public:
304	~bottom() {
305		try {
306			middle f;
307			uncaught_exceptions_checker uec(2);
308			throw "bottom";
309		}
310		catch (...) {}
311	}
312};
313
314void test_uncaught_exceptions()
315{
316	uncaught_exceptions_checker outer(0);
317	try {
318		bottom b;
319		uncaught_exceptions_checker inner(1);
320		throw "test";
321	}
322	catch (...) {}
323}
324
325extern "C" void __cxa_bad_cast();
326
327void test_exceptions(void)
328{
329	std::set_unexpected(throw_zero);
330	TEST_CLEANUP(test_catch(0));
331	TEST_CLEANUP(test_catch(1));
332	TEST_CLEANUP(test_catch(3));
333	TEST_CLEANUP(test_catch(4));
334	TEST_CLEANUP(test_catch(5));
335	TEST_CLEANUP(test_nested());
336	try{
337		test_catch(2);
338		TEST(violations == 1, "Exactly one exception spec violation");
339	}
340	catch (int64_t i) {
341		TEST(0, "Caught int64_t, but that violates an exception spec");
342	}
343	int a;
344	try {
345		throw &a;
346	}
347	catch (const int *b)
348	{
349		TEST(&a==b, "Caught const int from thrown int");
350	}
351	try {
352		throw &a;
353	}
354	catch (int *b)
355	{
356		TEST(&a==b, "Caught int from thrown int");
357	}
358	try
359	{
360		__cxa_bad_cast();
361	}
362	catch (std::exception b)
363	{
364		TEST(1, "Caught bad cast");
365	}
366	catch (...)
367	{
368		TEST(0, "Bad cast was not caught correctly");
369	}
370	test_const();
371	test_uncaught_exception();
372	test_rethrown_uncaught_exception();
373	test_rethrown_uncaught_foreign_exception();
374	test_uncaught_exceptions();
375
376
377	//printf("Test: %s\n",
378}
379