xref: /netbsd-src/external/bsd/libc++/dist/libcxxrt/test/test_foreign_exceptions.cc (revision ef049e9fc7d6ec4aea42661cf846dcc0a5d0c1fe)
1 #include <cstdio>
2 #include <cstdlib>
3 #include "unwind.h"
4 
5 #define EXCEPTION_CLASS(a,b,c,d,e,f,g,h) \
6 	((static_cast<uint64_t>(a) << 56) +\
7 	 (static_cast<uint64_t>(b) << 48) +\
8 	 (static_cast<uint64_t>(c) << 40) +\
9 	 (static_cast<uint64_t>(d) << 32) +\
10 	 (static_cast<uint64_t>(e) << 24) +\
11 	 (static_cast<uint64_t>(f) << 16) +\
12 	 (static_cast<uint64_t>(g) << 8) +\
13 	 (static_cast<uint64_t>(h)))
14 
15 // using ld --wrap=_Unwind_RaiseException hook feature
16 extern "C" _Unwind_Reason_Code __real__Unwind_RaiseException (_Unwind_Exception *e);
17 extern "C" _Unwind_Reason_Code __wrap__Unwind_RaiseException (_Unwind_Exception *e);
18 
__wrap__Unwind_RaiseException(_Unwind_Exception * e)19 extern "C" _Unwind_Reason_Code __wrap__Unwind_RaiseException (_Unwind_Exception *e)
20 {
21 	// clobber exception class forcing libcxx own exceptions to be treated
22 	// as foreign exception within libcxx itself
23 	e->exception_class = EXCEPTION_CLASS('F','O','R','E','I','G','N','\0');
24 	__real__Unwind_RaiseException(e);
25 }
26 
27 _Unwind_Exception global_e;
28 
29 enum test_status {
30 	PENDING, PASSED, FAILED
31 };
32 
33 const char test_status_str[][8] = {
34 	"PENDING", "PASSED", "FAILED"
35 };
36 
37 test_status test1_status = PENDING;
38 test_status test2_status = PENDING;
39 test_status test3_status = PENDING;
40 
test2_exception_cleanup(_Unwind_Reason_Code code,_Unwind_Exception * e)41 void test2_exception_cleanup(_Unwind_Reason_Code code, _Unwind_Exception *e)
42 {
43 	fputs("(2) exception_cleanup called\n", stderr);
44 	if (e != &global_e) {
45 		fprintf(stderr, "(2) ERROR: unexpected ptr: expecting %p, got %p\n", &global_e, e);
46 		test2_status = FAILED;
47 	}
48 	if (test2_status == PENDING)
49 		test2_status = PASSED;
50 }
51 
52 struct test3_exception
53 {
54 	static int counter;
~test3_exceptiontest3_exception55 	~test3_exception()
56 	{
57 		counter++;
58 		fputs("(3) exception dtor\n", stderr);
59 	}
60 };
61 int test3_exception::counter = 0;
62 
main()63 int main()
64 {
65 	///////////////////////////////////////////////////////////////
66 	fputs("(1) foreign exception, exception_cleanup=nullptr\n", stderr);
67 	try
68 	{
69 		global_e.exception_class = 0;
70 		global_e.exception_cleanup = 0;
71 		__real__Unwind_RaiseException(&global_e);
72 	}
73 	catch (...)
74 	{
75 	}
76 	test1_status = PASSED;
77 	fputs("(1) PASS\n", stderr);
78 
79 	///////////////////////////////////////////////////////////////
80 	fputs("(2) foreign exception, exception_cleanup present\n", stderr);
81 	try
82 	{
83 		global_e.exception_class = 0;
84 		global_e.exception_cleanup = test2_exception_cleanup;
85 		__real__Unwind_RaiseException(&global_e);
86 	}
87 	catch (...)
88 	{
89 	}
90 	fprintf(stderr, "(2) %s\n", test_status_str[test2_status]);
91 
92 	///////////////////////////////////////////////////////////////
93 	fputs("(3) C++ exception in foreign environment\n", stderr);
94 	int counter_expected;
95 	try
96 	{
97 		// throw was rigged such that the runtime treats C++ exceptions
98 		// as foreign ones
99 		throw test3_exception();
100 	}
101 	catch (test3_exception&)
102 	{
103 		fputs("(3) ERROR: wrong catch\n", stderr);
104 		test3_status = FAILED;
105 	}
106 	catch (...)
107 	{
108 		fputs("(3) catch(...)\n", stderr);
109 		counter_expected = test3_exception::counter + 1;
110 		// one more dtor immediately after we leave catch
111 	}
112 	if (test3_status == PENDING && test3_exception::counter != counter_expected) {
113 		fputs("(3) ERROR: exception dtor didn't run\n", stderr);
114 		test3_status = FAILED;
115 	}
116 	if (test3_status == PENDING)
117 		test3_status = PASSED;
118 	fprintf(stderr, "(3) %s\n", test_status_str[test3_status]);
119 
120 	///////////////////////////////////////////////////////////////
121 	if (test1_status == PASSED && test2_status == PASSED && test3_status == PASSED)
122 		return EXIT_SUCCESS;
123 	else
124 		return EXIT_FAILURE;
125 }
126