1 module d.rt.eh;
2 
3 import d.rt.dwarf;
4 import d.rt.unwind;
5 
6 private enum ExceptionRegno = 0;
7 private enum SelectorRegno = 1;
8 
9 private auto getExceptionClass() {
10 	ulong ec;
11 
12 	auto s = "SDC\0D2\0\0";
13 	for (uint i = 0; i < s.length; i++) {
14 		ec = ec << 8 | s[i];
15 	}
16 
17 	return ec;
18 }
19 
20 enum ExceptionClass = getExceptionClass();
21 
22 private Throwable inFlight;
23 private _Unwind_Exception ue;
24 
25 extern(C) void __sd_eh_delete(_Unwind_Reason_Code reason,
26                               _Unwind_Exception* exceptionObject) {
27 	inFlight = null;
28 }
29 
30 /**
31  * Throws a exception.
32  */
33 extern(C) void __sd_eh_throw(Throwable t) {
34 	if (inFlight !is null) {
35 		// TODO: chain
36 	}
37 
38 	inFlight = t;
39 
40 	ue.exception_class = ExceptionClass;
41 	ue.exception_cleanup = &__sd_eh_delete;
42 
43 	auto f = _Unwind_RaiseException(&ue);
44 
45 	import core.stdc.stdlib, core.stdc.stdio;
46 	printf("FAILED TO RAISE EXCEPTION %i\n".ptr, f);
47 	exit(-1);
48 }
49 
50 extern(C) _Unwind_Reason_Code __sd_eh_personality(
51 	int ver,
52 	_Unwind_Action actions,
53 	ulong exceptionClass,
54 	_Unwind_Exception* exceptionObject,
55 	_Unwind_Context* ctx
56 ) {
57 	if (ver != 1) {
58 		return _Unwind_Reason_Code.FATAL_PHASE1_ERROR;
59 	}
60 
61 	auto p = cast(const(ubyte)*) _Unwind_GetLanguageSpecificData(ctx);
62 	if (p is null) {
63 		return _Unwind_Reason_Code.CONTINUE_UNWIND;
64 	}
65 
66 	// XXX: GCC mention:
67 	// Shortcut for phase 2 found handler for domestic exception.
68 	// Consider adding this when it is understood :D
69 
70 	// get the instruction pointer
71 	// will be used to find the right entry in the callsite_table
72 	// -1 because it will point past the last instruction
73 	auto ip = _Unwind_GetIP(ctx) - 1;
74 
75 	// printf("ip:%p\n".ptr, ip);
76 
77 	auto headers = parseLsdHeader(p, ctx);
78 	/+
79 	printf("start:\t%p\n".ptr, headers.start);
80 	printf("lpStart:\t%p\n".ptr, headers.lpStart);
81 	printf("typeTable:\t%p\n".ptr, headers.typeTable);
82 	printf("actionTable:\t%p\n".ptr, headers.actionTable);
83 	+/
84 	_Unwind_Ptr landingPad = null;
85 	const(ubyte)* actionPtr = null;
86 
87 	while (p < headers.actionTable) {
88 		auto start = read_encoded(p, ctx, headers.callSiteEncoding);
89 		auto len = read_encoded(p, ctx, headers.callSiteEncoding);
90 		auto lp = read_encoded(p, ctx, headers.callSiteEncoding);
91 		auto action = read_uleb128(p);
92 
93 		// printf("start: %ld\tlen: %ld\tlp: %ld\n".ptr, start, len, lp);
94 
95 		// The table is sorted, so if we've passed the ip, stop.
96 		if (ip < headers.start + start) {
97 			return _Unwind_Reason_Code.CONTINUE_UNWIND;
98 		}
99 
100 		// We found something !
101 		if (ip < headers.start + start + len) {
102 			if (lp) {
103 				landingPad = headers.lpStart + lp;
104 			}
105 
106 			if (action) {
107 				actionPtr = headers.actionTable + action - 1;
108 			}
109 
110 			break;
111 		}
112 	}
113 
114 	if (landingPad is null) {
115 		return _Unwind_Reason_Code.CONTINUE_UNWIND;
116 	}
117 
118 	// We have landing pad, but no action. This is a cleanup.
119 	if (actionPtr is null) {
120 		return setupCleanup(ctx, actions, landingPad, exceptionObject);
121 	}
122 
123 	// We do not catch foreign exceptions and if we have to force unwind.
124 	bool doCatch = (exceptionClass == ExceptionClass)
125 		&& !(actions & _Unwind_Action.FORCE_UNWIND);
126 
127 	ptrdiff_t nextOffset = -1;
128 	while (nextOffset) {
129 		auto switchval = read_sleb128(actionPtr);
130 		auto prev = actionPtr;
131 		nextOffset = read_sleb128(actionPtr);
132 
133 		if (switchval < 0) {
134 			import core.stdc.stdlib, core.stdc.stdio;
135 			printf("FILTER NOT SUPPORTED\n".ptr);
136 			exit(-1);
137 		}
138 
139 		if (switchval == 0) {
140 			// XXX: Ensure that nextOffset is 0 as cleanup must come last.
141 			return setupCleanup(ctx, actions, landingPad, exceptionObject);
142 		}
143 
144 		p = headers.typeTable - switchval * headers.typeEncoding.getSize();
145 		auto tmp = read_encoded(p, ctx, headers.typeEncoding);
146 		auto candidate = *(cast(ClassInfo*) &tmp);
147 
148 		// Null is a special case that always catches.
149 		if (candidate !is null && !doCatch) {
150 			continue;
151 		}
152 
153 		// null is a wildcard for catch all.
154 		// XXX: We don't need to recompute all downcast every time.
155 		if (candidate is null
156 			    || __sd_class_downcast(inFlight, candidate) !is null) {
157 			return setupCatch(ctx, actions, switchval, landingPad,
158 			                  exceptionObject);
159 		}
160 
161 		actionPtr = prev + nextOffset;
162 	}
163 
164 	// No action found.
165 	return _Unwind_Reason_Code.CONTINUE_UNWIND;
166 }
167 
168 private _Unwind_Reason_Code setupCatch(
169 	_Unwind_Context* ctx,
170 	_Unwind_Action actions,
171 	ptrdiff_t switchval,
172 	_Unwind_Ptr landingPad,
173 	_Unwind_Exception* exceptionObject
174 ) {
175 	if (actions & _Unwind_Action.SEARCH_PHASE) {
176 		return _Unwind_Reason_Code.HANDLER_FOUND;
177 	}
178 
179 	if (actions & _Unwind_Action.CLEANUP_PHASE) {
180 		_Unwind_SetGR(ctx, ExceptionRegno,
181 		              *(cast(_Unwind_Word*) &exceptionObject));
182 		_Unwind_SetGR(ctx, SelectorRegno, switchval);
183 		_Unwind_SetIP(ctx, landingPad);
184 
185 		return _Unwind_Reason_Code.INSTALL_CONTEXT;
186 	}
187 
188 	return _Unwind_Reason_Code.FATAL_PHASE2_ERROR;
189 }
190 
191 _Unwind_Reason_Code setupCleanup(
192 	_Unwind_Context* ctx,
193 	_Unwind_Action actions,
194 	_Unwind_Ptr landingPad,
195 	_Unwind_Exception* exceptionObject
196 ) {
197 	// If we're merely in search phase, continue
198 	if (actions & _Unwind_Action.SEARCH_PHASE) {
199 		return _Unwind_Reason_Code.CONTINUE_UNWIND;
200 	}
201 
202 	_Unwind_SetGR(ctx, ExceptionRegno, *(cast(_Unwind_Word*) &exceptionObject));
203 	_Unwind_SetGR(ctx, SelectorRegno, 0);
204 	_Unwind_SetIP(ctx, landingPad);
205 
206 	return _Unwind_Reason_Code.INSTALL_CONTEXT;
207 }
208 
209 // This is specific from SDC's runtime, so DMD doesn't know about it.
210 private extern(C) Object __sd_class_downcast(Object o, ClassInfo c);