Skip to content
20 changes: 19 additions & 1 deletion src/org/rascalmpl/dap/RascalDebugAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -154,14 +155,21 @@ public CompletableFuture<Capabilities> initialize(InitializeRequestArguments arg
Capabilities capabilities = new Capabilities();

capabilities.setSupportsConfigurationDoneRequest(true);
capabilities.setExceptionBreakpointFilters(new ExceptionBreakpointsFilter[]{});
capabilities.setSupportsStepBack(false);
capabilities.setSupportsRestartFrame(false);
capabilities.setSupportsSetVariable(false);
capabilities.setSupportsRestartRequest(false);
capabilities.setSupportsCompletionsRequest(true);
capabilities.setSupportsConditionalBreakpoints(true);

ExceptionBreakpointsFilter[] exceptionFilters = new ExceptionBreakpointsFilter[1];
ExceptionBreakpointsFilter exFilter = new ExceptionBreakpointsFilter();
exFilter.setFilter("rascalExceptions");
exFilter.setLabel("Rascal Exceptions");
exFilter.setDescription("Break when a Rascal exception is thrown");
exceptionFilters[0] = exFilter;
capabilities.setExceptionBreakpointFilters(exceptionFilters);

return capabilities;
}, ownExecutor);
}
Expand Down Expand Up @@ -298,6 +306,16 @@ private static ITree locateBreakableTree(ITree tree, int line) {
return null;
}

@Override
public CompletableFuture<SetExceptionBreakpointsResponse> setExceptionBreakpoints(SetExceptionBreakpointsArguments args) {
return CompletableFuture.supplyAsync(() -> {
SetExceptionBreakpointsResponse response = new SetExceptionBreakpointsResponse();
debugHandler.setSuspendOnException(Arrays.stream(args.getFilters()).anyMatch("rascalExceptions"::equals));
response.setBreakpoints(new Breakpoint[0]);
return response;
}, ownExecutor);
}

@Override
public CompletableFuture<Void> attach(Map<String, Object> args) {
client.initialized();
Expand Down
13 changes: 13 additions & 0 deletions src/org/rascalmpl/dap/RascalDebugEventTrigger.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
package org.rascalmpl.dap;

import io.usethesource.vallang.ISourceLocation;

import org.eclipse.lsp4j.debug.StoppedEventArguments;
import org.eclipse.lsp4j.debug.services.IDebugProtocolClient;
import org.rascalmpl.dap.breakpoint.BreakpointsCollection;
Expand Down Expand Up @@ -132,6 +133,18 @@ public void fireSuspendByClientRequestEvent() {
client.stopped(stoppedEventArguments);
}

@Override
public void fireSuspendByExceptionEvent(Exception exception) {
suspendedState.suspended();

StoppedEventArguments stoppedEventArguments = new StoppedEventArguments();
stoppedEventArguments.setThreadId(RascalDebugAdapter.mainThreadID);
stoppedEventArguments.setDescription("Paused on exception.");
stoppedEventArguments.setReason("exception");
stoppedEventArguments.setText(exception.getMessage());
client.stopped(stoppedEventArguments);
}

@Override
public void fireSuspendEvent(RascalEvent.Detail detail) {}
}
9 changes: 9 additions & 0 deletions src/org/rascalmpl/debug/AbstractInterpreterEventTrigger.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ public void fireSuspendByBreakpointEvent(Object data) {
fireEvent(event);
}

public void fireSuspendByExceptionEvent(Exception exception) {
RascalEvent event = new RascalEvent(source,
RascalEvent.Kind.SUSPEND,
RascalEvent.Detail.EXCEPTION);
event.setData(exception);

fireEvent(event);
}

/**
* Fires a idle event for this interpreter. E.g. this happens when the REPL
* is waiting for another command input.
Expand Down
45 changes: 43 additions & 2 deletions src/org/rascalmpl/debug/DebugHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ public final class DebugHandler implements IDebugHandler, IRascalRuntimeEvaluati
*/
private boolean suspendRequested;

private boolean suspendOnException = false;

private Exception lastExceptionHandled = null;

public boolean getSuspendOnException() {
return suspendOnException;
}

public void setSuspendOnException(boolean suspendOnException) {
this.suspendOnException = suspendOnException;
}

/**
* Indicates that the evaluator is suspended. Also used for suspending / blocking the evaluator.
*/
Expand Down Expand Up @@ -160,7 +172,21 @@ public void suspended(Object runtime, IntSupplier getCallStackSize, AbstractAST
updateSuspensionState(getCallStackSize.getAsInt(), currentAST);
getEventTrigger().fireSuspendByClientRequestEvent();
setSuspendRequested(false);
}
} else if(getSuspendOnException() && runtime instanceof Evaluator && ((Evaluator) runtime).getCurrentException() != null ) {
// Suspension due to exception
Evaluator eval = (Evaluator) runtime;
Exception e = eval.getCurrentException();
if(lastExceptionHandled != null && e == lastExceptionHandled){
return; // already handled this exception
}
if(handleExceptionSuspension(eval, e)){
lastExceptionHandled = e;
updateSuspensionState(getCallStackSize.getAsInt(), currentAST);
getEventTrigger().fireSuspendByExceptionEvent(e);
} else {
return;
}
}
else {
AbstractAST location = currentAST;

Expand Down Expand Up @@ -239,7 +265,7 @@ public void suspended(Object runtime, IntSupplier getCallStackSize, AbstractAST
break;
}
}

/*
* Waiting until GUI triggers end of suspension.
*/
Expand All @@ -252,6 +278,21 @@ public void suspended(Object runtime, IntSupplier getCallStackSize, AbstractAST
}
}

private boolean handleExceptionSuspension(Evaluator eval, Exception e) {
if(e instanceof Throw){
Throw thr = (Throw) e;
IValue excValue = thr.getException();
if(excValue.getType().isAbstractData()){
// We ignore suspension that happens due in standard library code for RuntimeExceptions
if(excValue.getType().getName().equals("RuntimeException")){
return !eval.getCurrentAST().getLocation().getScheme().equals("std");
}
}
return true;
}
return false;
}

protected AbstractAST getReferenceAST() {
return referenceAST;
}
Expand Down
2 changes: 1 addition & 1 deletion src/org/rascalmpl/debug/RascalEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public enum Kind {
* Details additional to {@link Kind}.
*/
public enum Detail {
UNSPECIFIED, CLIENT_REQUEST, STEP_INTO, STEP_OVER, STEP_END, BREAKPOINT, STEP_OUT
UNSPECIFIED, CLIENT_REQUEST, STEP_INTO, STEP_OVER, STEP_END, BREAKPOINT, STEP_OUT, EXCEPTION
}

private final Kind kind;
Expand Down
20 changes: 20 additions & 0 deletions src/org/rascalmpl/interpreter/Evaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ public void decCallNesting() {
*/
private AbstractAST currentAST;

/**
* Used in debugger exception handling
*/
private Exception currentException;

/**
* True if we're doing profiling
*/
Expand Down Expand Up @@ -780,6 +785,10 @@ public AbstractAST getCurrentAST() {
return currentAST;
}

public Exception getCurrentException() {
return currentException;
}

public void addRascalSearchPathContributor(IRascalSearchPathContributor contrib) {
rascalPathResolver.addPathContributor(contrib);
}
Expand Down Expand Up @@ -1589,6 +1598,17 @@ public void notifyAboutSuspension(AbstractAST currentAST) {
}
}

@Override
public void notifyAboutSuspensionException(Exception t) {
currentException = t;
if (!suspendTriggerListeners.isEmpty()) { // remove the breakable condition since exception can happen anywhere
for (IRascalSuspendTriggerListener listener : suspendTriggerListeners) {
listener.suspended(this, () -> getCallStack().size(), currentAST);
}
}
currentException = null;
}

public AbstractInterpreterEventTrigger getEventTrigger() {
return eventTrigger;
}
Expand Down
1 change: 1 addition & 0 deletions src/org/rascalmpl/interpreter/IEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public interface IEvaluator<T> extends IEvaluatorContext {
* @param currentAST the AST that is causes the suspension.
*/
public void notifyAboutSuspension(AbstractAST currentAST);
public void notifyAboutSuspensionException(Exception t);

/*
* Evaluation.
Expand Down
3 changes: 3 additions & 0 deletions src/org/rascalmpl/interpreter/result/RascalFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@ public Result<IValue> call(Type[] actualStaticTypes, IValue[] actuals, Map<Strin
if (eval.getCallTracing()) {
printExcept(e);
}
if(e instanceof Exception){
eval.notifyAboutSuspensionException((Exception)e); // Force call here so we do not loose the current env
}
throw e;
}
finally {
Expand Down
Loading