Skip to content

Commit

Permalink
add interactive debugging to default condition handler
Browse files Browse the repository at this point in the history
  • Loading branch information
arvyy committed Dec 25, 2024
1 parent 2d4ca7a commit 1c6d541
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ public ISLISPTruffleLanguageView(ISLISPContext context, Object object) {

@ExportMessage
Object toDisplayString(boolean ignored) {
return ISLISPFormatObject.format(object);
return ISLISPFormatObject.format(object, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
import com.oracle.truffle.api.exception.AbstractTruffleException;

/**
* Control flow exception that unwinds stack from point of signal
* to the handler for non-continuable conditions.
*
* See
* @{@link com.github.arvyy.islisp.functions.ISLISPSignalCondition},
* @{@link com.github.arvyy.islisp.nodes.ISLISPWithHandlerNode}.
* Exception thrown in case a condition is signalled, but no
* signal handler is present (can happen if an islisp function is stored and reinvoked from non-islisp context).
*/
public class ISLISPNonContinuableCondition extends AbstractTruffleException {
public class ISLISPUncaughtConditionException extends AbstractTruffleException {

private final Object condition;

Expand All @@ -19,7 +15,7 @@ public class ISLISPNonContinuableCondition extends AbstractTruffleException {
*
* @param condition condition value
*/
public ISLISPNonContinuableCondition(Object condition) {
public ISLISPUncaughtConditionException(Object condition) {
this.condition = condition;
}

Expand All @@ -29,4 +25,5 @@ public ISLISPNonContinuableCondition(Object condition) {
public Object getCondition() {
return condition;
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.github.arvyy.islisp.functions;

import com.github.arvyy.islisp.ISLISPContext;
import com.github.arvyy.islisp.exceptions.ISLISPInteractiveExitException;
import com.github.arvyy.islisp.exceptions.ISLISPError;
import com.github.arvyy.islisp.nodes.ISLISPFunctionDispatchNode;
import com.github.arvyy.islisp.nodes.ISLISPFunctionDispatchNodeGen;
import com.github.arvyy.islisp.nodes.ISLISPInteractiveDebugger;
import com.github.arvyy.islisp.runtime.LispFunction;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.VirtualFrame;
Expand All @@ -20,12 +21,19 @@ public class ISLISPDefaultHandler extends RootNode {
@Child
private ISLISPFunctionDispatchNode dispatchNode;

@Child
private ISLISPInteractiveDebugger interactiveDebugger;

private final boolean isInteractive;

ISLISPDefaultHandler(TruffleLanguage<?> language, boolean isInteractive) {
ISLISPDefaultHandler(
TruffleLanguage<?> language,
boolean isInteractive
) {
super(language);
this.isInteractive = isInteractive;
dispatchNode = ISLISPFunctionDispatchNodeGen.create();
interactiveDebugger = new ISLISPInteractiveDebugger();
}

@Override
Expand All @@ -41,7 +49,8 @@ public Object execute(VirtualFrame frame) {
var condition = frame.getArguments()[1];
dispatchNode.executeDispatch(reportConditionFunction, new Object[]{condition, errorOutput});
if (isInteractive) {
throw new ISLISPInteractiveExitException(condition);
interactiveDebugger.startRepl(condition);
throw new ISLISPError("Interactive debugger returned normally", this);
} else {
return dispatchNode.executeDispatch(exitFunction, new Object[] {1});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,14 @@ Object doFallback(Object stream, Object obj, Object escape) {
* Util java procedure to give same string representation as what format-object produces.
*
* @param o value
* @param escape if values should be present in a readable form (eg., show double quotes for string)
* @return display string
*/
@CompilerDirectives.TruffleBoundary
public static String format(Object o) {
public static String format(Object o, boolean escape) {
try {
var stream = new LispStream(null, new ByteArrayOutputStream());
doPrint(stream, o, false);
doPrint(stream, o, escape);
return stream.getOutputString().get();
} catch (Exception e) {
return "";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.github.arvyy.islisp.functions;

import com.github.arvyy.islisp.ISLISPContext;
import com.github.arvyy.islisp.Utils;
import com.github.arvyy.islisp.exceptions.ISLISPContinueException;
import com.github.arvyy.islisp.exceptions.ISLISPError;
import com.github.arvyy.islisp.exceptions.ISLISPNonContinuableCondition;
import com.github.arvyy.islisp.exceptions.ISLISPUncaughtConditionException;
import com.github.arvyy.islisp.nodes.ISLISPErrorSignalerNode;
import com.github.arvyy.islisp.nodes.ISLISPFunctionDispatchNode;
import com.github.arvyy.islisp.nodes.ISLISPFunctionDispatchNodeGen;
Expand Down Expand Up @@ -80,28 +81,29 @@ public Object execute(VirtualFrame frame) {
}
var continuable = frame.getArguments()[2];
setContinuable.call(null, continuable, conditionValue);
if (continuable != ctx.getNil()) {
var handler = ctx.popHandler();
// it's possible no handler is active, in which case treat it same as non-continuable.
// (eg., in a case when islisp function was returned from eval, and then called
// from a different truffle language / context)
if (handler == null) {
throw new ISLISPNonContinuableCondition(conditionValue);
}
try {
dispatchNode.executeDispatch(handler, new Object[]{conditionValue});
throw new ISLISPError("Condition handler returned normally", this);
} catch (ISLISPContinueException e) {
if (e.getCondition() == conditionValue) {
return e.getValue();
} else {
throw e;
var handler = ctx.popHandler();
// it's possible no handler is active, in which case throw special exception to indicate it.
// (eg., in a case when islisp function was returned from eval, and then called
// from a different truffle language / context)
if (handler == null) {
throw new ISLISPUncaughtConditionException(conditionValue);
}
try {
dispatchNode.executeDispatch(handler, new Object[]{conditionValue});
// ISLISP spec requires handler to do non-local transfer
throw new ISLISPError("Condition handler returned normally", this);
} catch (ISLISPContinueException e) {
if (e.getCondition() == conditionValue) {
// non-continuable exception shouldn't be able to be continued >:(
if (Utils.isNil(continuable)) {
throw new ISLISPError("Condition is not continuable", this);
}
} finally {
ctx.pushHandler(handler);
return e.getValue();
} else {
throw e;
}
} else {
throw new ISLISPNonContinuableCondition(conditionValue);
} finally {
ctx.pushHandler(handler);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public Object signalWrongArgumentCount(int actual, int min, int max) {
sRequiredMin(), min,
sRequiredMax(), max
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand Down Expand Up @@ -209,7 +209,7 @@ public Object signalDomainError(String message, Object obj, LispClass expectedCl
sObject(), obj,
sExpectedClass(), expectedClass
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -226,7 +226,7 @@ public Object signalUnboundVariable(Symbol name) {
ctx.namedSymbol("name"), name,
ctx.namedSymbol("namespace"), ctx.namedSymbol("variable")
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -243,7 +243,7 @@ public Object signalUndefinedFunction(Symbol name) {
ctx.namedSymbol("name"), name,
ctx.namedSymbol("namespace"), ctx.namedSymbol("function")
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -260,7 +260,7 @@ public Object signalUndefinedClass(Symbol name) {
ctx.namedSymbol("name"), name,
ctx.namedSymbol("namespace"), ctx.namedSymbol("class")
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

@CompilerDirectives.TruffleBoundary
Expand Down Expand Up @@ -305,7 +305,7 @@ public Object signalEndOfStream() {
null,
ctx.lookupClass("ROOT", ctx.namedSymbol("<end-of-stream>"))
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -319,7 +319,7 @@ public Object signalDivisionByZero() {
null,
ctx.lookupClass("ROOT", ctx.namedSymbol("<division-by-zero>"))
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -337,7 +337,7 @@ public Object signalIndexOutOfRange(int actual, int bounds) {
sBounds(), bounds,
sActual(), actual
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -351,7 +351,7 @@ public Object signalNoNextMethod() {
null,
ctx.lookupClass("ROOT", ctx.namedSymbol("<no-next-method-error>"))
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -368,7 +368,7 @@ public Object signalTruffleInteropError(InteropException interopException) {
cTruffleInteropError(),
sMessage(), interopException.getMessage()
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}


Expand All @@ -386,7 +386,7 @@ public Object signalIOError(IOException exception) {
ctx.lookupClass("ROOT", ctx.namedSymbol("<io-error>")),
ctx.namedSymbol("message"), exception.getMessage()
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -404,7 +404,7 @@ public Object signalUnknownConversion(Object value, Object to) {
ctx.namedSymbol("value"), value,
ctx.namedSymbol("to"), to
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}

/**
Expand All @@ -420,6 +420,6 @@ public Object signalImmutableBindingError(Symbol bindingName) {
ctx.lookupClass("ROOT", ctx.namedSymbol("<immutable-binding-error>")),
ctx.namedSymbol("binding"), bindingName
);
return getSignalCallNode().call(null, condition, ctx.getNil());
return getSignalCallNode().call(null, condition, ctx.getT());
}
}
Loading

0 comments on commit 1c6d541

Please sign in to comment.