package st.gravel.support.jvm.runtime; import st.gravel.support.jvm.NonLocalReturn; public class ExceptionStack { public static class ExceptionHandler { private final Object exceptionSelector; private final Object exBlock; private final ExceptionHandler previous; private final Object marker; private final Object protectedBlock; public ExceptionHandler(Object protectedBlock, Object exceptionSelector, Object exBlock, ExceptionHandler previous, Object marker) { this.protectedBlock = protectedBlock; this.exceptionSelector = exceptionSelector; this.exBlock = exBlock; this.previous = previous; this.marker = marker; } public void handle(Object exception) throws Throwable { setCurrentHandler(exception, this); Object value = MethodTools.perform(exBlock, "value:", exception); throw new NonLocalReturn(value, marker); } public boolean handles(Object exception) throws Throwable { Object res = MethodTools.perform(exceptionSelector, "handles:", exception); return res == Boolean.TRUE; } public Object marker() { return marker; } public ExceptionHandler previous() { return previous; } } private static final ThreadLocal<ExceptionStack> currentStack = new ThreadLocal<ExceptionStack>() { @Override protected ExceptionStack initialValue() { return new ExceptionStack(); } }; public static ExceptionHandler addHandler(Object protectedBlock, Object exceptionSelector, Object exBlock, Object marker) { return currentStack.get().pvtAddHandler(protectedBlock, exceptionSelector, exBlock, marker); } private static ExceptionHandler currentHandler(Object exception) { return (ExceptionHandler) MethodTools.safePerform(exception, "currentHandler"); } public static void handle(Object exception, Object resumeMarker) throws Throwable { currentStack.get().pvtHandle(exception, resumeMarker); } public static void handlePass(Object exception) throws Throwable { currentStack.get().pvtHandlePass(exception); } public static void handleResume_(Object exception, Object returnValue) { currentStack.get().pvtHandleResume(exception, returnValue); } public static Object handleRetry(Object exception) throws Throwable { return currentStack.get().pvtHandleRetry(exception); } public static Object handleRetryUsing_(Object exception, Object value) throws Throwable { return currentStack.get().pvtHandleRetryUsing_(exception, value); } public static Object handleReturn(Object exception, Object value) { return currentStack.get().pvtHandleReturn(exception, value); } private static ExceptionHandler initialHandler(Object exception) { return (ExceptionHandler) MethodTools.safePerform(exception, "initialHandler"); } public static boolean isNested(Object exception) throws Throwable { return currentStack.get().pvtIsNested(exception); } public static void removeHandler(ExceptionHandler handler) { currentStack.get().pvtRemoveHandler(handler); } private static void setCurrentHandler(Object exception, ExceptionHandler value) { MethodTools.safePerform(exception, "currentHandler:", value); } private static void setInitialHandler(Object exception, ExceptionHandler value) { MethodTools.safePerform(exception, "initialHandler:", value); } private ExceptionHandler current; private void executeDefaultAction(Object exception) { MethodTools.safePerform(exception, "defaultAction"); } public void handleFrom(Object exception, ExceptionHandler handler) throws Throwable { while (handler != null) { if (handler.handles(exception)) { handler.handle(exception); return; } handler = handler.previous(); } executeDefaultAction(exception); } private ExceptionHandler pvtAddHandler(Object protectedBlock, Object exceptionSelector, Object exBlock, Object marker) { current = new ExceptionHandler(protectedBlock, exceptionSelector, exBlock, current, marker); return current; } private void pvtHandle(Object exception, Object resumeMarker) throws Throwable { setResumeMarker(exception, resumeMarker); ExceptionHandler handler = initialHandler(exception); if (handler == null) handler = current; if (handler == null) { executeDefaultAction(exception); return; } setInitialHandler(exception, handler); current = handler.previous(); handleFrom(exception, handler); current = handler; } private void pvtHandlePass(Object exception) throws Throwable { handleFrom(exception, currentHandler(exception).previous()); } private void pvtHandleResume(Object exception, Object returnValue) { Object resumeMarker = resumeMarker(exception); throw new NonLocalReturn(returnValue, resumeMarker); } private Object pvtHandleRetry(Object exception) throws Throwable { ExceptionHandler handler = currentHandler(exception); return pvtHandleRetryUsing_(exception, handler.protectedBlock); } private Object pvtHandleRetryUsing_(Object exception, Object value) throws Throwable { Object result = MethodTools.perform(value, "value"); return pvtHandleReturn(exception, result); } private Object pvtHandleReturn(Object exception, Object value) { throw new NonLocalReturn(value, currentHandler(exception).marker()); } private boolean pvtIsNested(Object exception) throws Throwable { ExceptionHandler handler = initialHandler(exception); if (handler == null) return false; while (handler != null) { if (handler.handles(exception)) { return true; } handler = handler.previous(); } return false; } private void pvtRemoveHandler(ExceptionHandler handler) { if (current == handler) { current = handler.previous(); } } private Object resumeMarker(Object exception) { return MethodTools.safePerform(exception, "resumeMarker"); } private void setResumeMarker(Object exception, Object resumeMarker) { MethodTools.safePerform(exception, "resumeMarker:", resumeMarker); } }