Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.

Commit 31b30cf

Browse files
committed
win32 EH: add fiber support, check catchType to allow converting exception to Error
1 parent 5e0cc5d commit 31b30cf

File tree

2 files changed

+161
-64
lines changed

2 files changed

+161
-64
lines changed

src/ldc/eh/common.d

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ extern(C) void fatalerror(in char* format, ...)
3636
abort();
3737
}
3838

39+
version(Win32) {} else version = notMSVC;
40+
version(notMSVC):
41+
3942
// ------------------------
4043
// Reading DWARF data
4144
// ------------------------

src/ldc/eh/win32.d

Lines changed: 158 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import core.sys.windows.windows;
1212
import core.exception : onOutOfMemoryError, OutOfMemoryError;
1313
import core.stdc.stdlib : malloc, free;
1414
import core.stdc.string : memcpy;
15+
import rt.util.container.common : xmalloc;
1516

1617
// pointers are image relative for Win64 versions
1718
version(Win64)
@@ -173,43 +174,52 @@ CatchableType* getCatchableType(TypeInfo_Class ti)
173174
}
174175

175176
///////////////////////////////////////////////////////////////
176-
extern(C) Object _d_eh_enter_catch(void* ptr)
177+
extern(C) Object _d_eh_enter_catch(void* ptr, ClassInfo catchType)
177178
{
178-
if (!ptr)
179-
return null; // null for "catch all" in scope(failure), will rethrow
180-
Throwable e = *(cast(Throwable*) ptr);
181-
182-
while(exceptionStack.length > 0)
179+
assert(ptr);
180+
181+
// is this a thrown D exception?
182+
auto e = *(cast(Throwable*) ptr);
183+
size_t pos = exceptionStack.find(e);
184+
if (pos >= exceptionStack.length())
185+
return null;
186+
187+
auto caught = e;
188+
// append inner unhandled thrown exceptions
189+
for (size_t p = pos + 1; p < exceptionStack.length(); p++)
190+
e = chainExceptions(e, exceptionStack[p]);
191+
exceptionStack.shrink(pos);
192+
193+
// given the bad semantics of Errors, we are fine with passing
194+
// the test suite with slightly inaccurate behaviour by just
195+
// rethrowing a collateral Error here, though it might need to
196+
// be caught by a catch handler in an inner scope
197+
if (e !is caught)
183198
{
184-
Throwable t = exceptionStack.pop();
185-
if (t is e)
186-
break;
199+
if (_d_isbaseof(typeid(e), catchType))
200+
*cast(Throwable*) ptr = e; // the current catch can also catch this Error
201+
else
202+
_d_throw_exception(e);
203+
}
204+
return e;
205+
}
187206

188-
auto err = cast(Error) t;
189-
if (err && !cast(Error)e)
207+
Throwable chainExceptions(Throwable e, Throwable t)
208+
{
209+
if (!cast(Error) e)
210+
if (auto err = cast(Error) t)
190211
{
191-
// there is an Error in flight, but we caught an Exception
192-
// so we convert it and rethrow the Error
193212
err.bypassedException = e;
194-
throw err;
213+
return err;
195214
}
196-
t.next = e.next;
197-
e.next = t;
198-
}
199215

216+
auto pChain = &e.next;
217+
while (*pChain)
218+
pChain = &(pChain.next);
219+
*pChain = t;
200220
return e;
201221
}
202222

203-
alias terminate_handler = void function();
204-
205-
extern(C) void** __current_exception();
206-
extern(C) void** __current_exception_context();
207-
extern(C) int* __processing_throw();
208-
209-
extern(C) terminate_handler set_terminate(terminate_handler new_handler);
210-
211-
terminate_handler old_terminate_handler; // explicitely per thread
212-
213223
ExceptionStack exceptionStack;
214224

215225
struct ExceptionStack
@@ -233,14 +243,36 @@ nothrow:
233243
return _p[--_length];
234244
}
235245

246+
void shrink(size_t sz)
247+
{
248+
while (_length > sz)
249+
_p[--_length] = null;
250+
}
251+
236252
ref inout(Throwable) opIndex(size_t idx) inout
237253
{
238254
return _p[idx];
239255
}
240256

257+
size_t find(Throwable e)
258+
{
259+
for (size_t i = _length; i > 0; )
260+
if (exceptionStack[--i] is e)
261+
return i;
262+
return ~0;
263+
}
264+
241265
@property size_t length() const { return _length; }
242266
@property bool empty() const { return !length; }
243267

268+
void swap(ref ExceptionStack other)
269+
{
270+
static void swapField(T)(ref T a, ref T b) { T o = b; b = a; a = o; }
271+
swapField(_length, other._length);
272+
swapField(_p, other._p);
273+
swapField(_cap, other._cap);
274+
}
275+
244276
private:
245277
void grow()
246278
{
@@ -260,8 +292,13 @@ private:
260292
size_t _cap;
261293
}
262294

295+
///////////////////////////////////////////////////////////////
296+
alias terminate_handler = void function();
297+
extern(C) terminate_handler set_terminate(terminate_handler new_handler);
298+
terminate_handler old_terminate_handler; // explicitely per thread
299+
263300
// helper to access TLS from naked asm
264-
int tlsUncaughtExceptions() nothrow
301+
size_t tlsUncaughtExceptions() nothrow
265302
{
266303
return exceptionStack.length;
267304
}
@@ -273,41 +310,98 @@ auto tlsOldTerminateHandler() nothrow
273310

274311
void msvc_eh_terminate() nothrow
275312
{
276-
asm nothrow {
277-
naked;
278-
call tlsUncaughtExceptions;
279-
cmp EAX, 0;
280-
je L_term;
281-
282-
// hacking into the call chain to return EXCEPTION_EXECUTE_HANDLER
283-
// as the return value of __FrameUnwindFilter so that
284-
// __FrameUnwindToState continues with the next unwind block
285-
286-
// restore ptd->__ProcessingThrow
287-
push EAX;
288-
call __processing_throw;
289-
pop [EAX];
290-
291-
// undo one level of exception frames from terminate()
292-
mov EAX,FS:[0];
293-
mov EAX,[EAX];
294-
mov FS:[0], EAX;
295-
296-
// assume standard stack frames for callers
297-
mov EAX,EBP; // frame pointer of terminate()
298-
mov EAX,[EAX]; // frame pointer of __FrameUnwindFilter
299-
mov ESP,EAX; // restore stack
300-
pop EBP; // and frame pointer
301-
mov EAX, 1; // return EXCEPTION_EXECUTE_HANDLER
302-
ret;
303-
304-
L_term:
305-
call tlsOldTerminateHandler;
306-
cmp EAX, 0;
307-
je L_ret;
308-
jmp EAX;
309-
L_ret:
310-
ret;
313+
version(Win32)
314+
{
315+
asm nothrow
316+
{
317+
naked;
318+
call tlsUncaughtExceptions;
319+
cmp EAX, 1;
320+
jle L_term;
321+
322+
// hacking into the call chain to return EXCEPTION_EXECUTE_HANDLER
323+
// as the return value of __FrameUnwindFilter so that
324+
// __FrameUnwindToState continues with the next unwind block
325+
326+
// undo one level of exception frames from terminate()
327+
mov EAX,FS:[0];
328+
mov EAX,[EAX];
329+
mov FS:[0], EAX;
330+
331+
// assume standard stack frames for callers
332+
mov EAX,EBP; // frame pointer of terminate()
333+
mov EAX,[EAX]; // frame pointer of __FrameUnwindFilter
334+
mov ESP,EAX; // restore stack
335+
pop EBP; // and frame pointer
336+
mov EAX, 1; // return EXCEPTION_EXECUTE_HANDLER
337+
ret;
338+
339+
L_term:
340+
call tlsOldTerminateHandler;
341+
cmp EAX, 0;
342+
je L_ret;
343+
jmp EAX;
344+
L_ret:
345+
ret;
346+
}
347+
}
348+
}
349+
350+
///////////////////////////////////////////////////////////////
351+
extern(C) void** __current_exception() nothrow;
352+
extern(C) void** __current_exception_context() nothrow;
353+
extern(C) int* __processing_throw() nothrow;
354+
355+
struct FiberContext
356+
{
357+
ExceptionStack exceptionStack;
358+
void* currentException;
359+
void* currentExceptionContext;
360+
int processingContext;
361+
}
362+
363+
FiberContext* fiberContext;
364+
365+
extern(C) void* _d_eh_swapContext(FiberContext* newContext) nothrow
366+
{
367+
import core.stdc.string : memset;
368+
if (!fiberContext)
369+
{
370+
fiberContext = cast(FiberContext*) xmalloc(FiberContext.sizeof);
371+
memset(fiberContext, 0, FiberContext.sizeof);
372+
}
373+
fiberContext.exceptionStack.swap(exceptionStack);
374+
fiberContext.currentException = *__current_exception();
375+
fiberContext.currentExceptionContext = *__current_exception_context();
376+
fiberContext.processingContext = *__processing_throw();
377+
378+
if (newContext)
379+
{
380+
exceptionStack.swap(newContext.exceptionStack);
381+
*__current_exception() = newContext.currentException;
382+
*__current_exception_context() = newContext.currentExceptionContext;
383+
*__processing_throw() = newContext.processingContext;
384+
}
385+
else
386+
{
387+
exceptionStack = ExceptionStack();
388+
*__current_exception() = null;
389+
*__current_exception_context() = null;
390+
*__processing_throw() = 0;
391+
}
392+
393+
FiberContext* old = fiberContext;
394+
fiberContext = newContext;
395+
return old;
396+
}
397+
398+
static ~this()
399+
{
400+
import core.stdc.stdlib : free;
401+
if (fiberContext)
402+
{
403+
destroy(*fiberContext);
404+
free(fiberContext);
311405
}
312406
}
313407

@@ -318,7 +412,7 @@ extern(C) bool _d_enter_cleanup(void* ptr)
318412
// be inferred to not return, is removed by the LLVM optimizer
319413
//
320414
// TODO: setup an exception handler here (ptr passes the address
321-
// of a 16 byte stack area in a parent fuction scope) to deal with
415+
// of a 40 byte stack area in a parent fuction scope) to deal with
322416
// unhandled exceptions during unwinding.
323417
return true;
324418
}

0 commit comments

Comments
 (0)