Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {}
}
11 changes: 11 additions & 0 deletions src/org/rascalmpl/debug/AbstractInterpreterEventTrigger.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.util.Collection;
import java.util.EventObject;

import org.rascalmpl.exceptions.Throw;

/**
* Interpreter event trigger template that does not
* specify yet how an event is fired.
Expand Down Expand Up @@ -143,6 +145,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
59 changes: 57 additions & 2 deletions src/org/rascalmpl/debug/DebugHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,19 @@
import org.rascalmpl.values.functions.IFunction;
import java.util.function.Function;
import org.rascalmpl.exceptions.RascalStackOverflowError;
import org.rascalmpl.exceptions.RuntimeExceptionFactory;
import org.rascalmpl.interpreter.staticErrors.StaticError;
import org.rascalmpl.exceptions.Throw;
import org.rascalmpl.parser.gtd.exception.ParseError;
import org.rascalmpl.interpreter.utils.ReadEvalPrintDialogMessages;
import io.usethesource.vallang.io.StandardTextWriter;
import io.usethesource.vallang.type.Type;
import java.io.StringWriter;
import java.net.URISyntaxException;
import java.io.PrintWriter;
import org.rascalmpl.repl.output.impl.PrinterErrorCommandOutput;

import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.ISourceLocation;
import org.rascalmpl.uri.URIUtil;
import io.usethesource.vallang.IValue;
Expand All @@ -60,6 +64,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 +176,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 +269,7 @@ public void suspended(Object runtime, IntSupplier getCallStackSize, AbstractAST
break;
}
}

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

private boolean handleExceptionSuspension(Evaluator eval, Exception e) {
AbstractAST referenceAST = eval.getCurrentAST();
if(e instanceof Throw){
Throw thr = (Throw) e;
IValue excValue = thr.getException();
if(excValue.getType().isAbstractData() && excValue instanceof IConstructor){
IConstructor cons = (IConstructor) excValue;
Type constructorType = cons.getConstructorType();

// Match on different constructors
if(constructorType == RuntimeExceptionFactory.ParseError){
try{
return !referenceAST.getLocation().top().equals(
URIUtil.createFromURI("std:///ParseTree.rsc"));
}catch(URISyntaxException use){
return true; // if std not found, suspend
}
}
// ... TODO : other cases
}
// TODO : other kinds of exceptions
}
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
21 changes: 21 additions & 0 deletions src/org/rascalmpl/interpreter/Evaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.rascalmpl.debug.IRascalSuspendTriggerListener;
import org.rascalmpl.exceptions.ImplementationError;
import org.rascalmpl.exceptions.StackTrace;
import org.rascalmpl.exceptions.Throw;
import org.rascalmpl.ideservices.IDEServices;
import org.rascalmpl.interpreter.asserts.NotYetImplemented;
import org.rascalmpl.interpreter.callbacks.IConstructorDeclared;
Expand Down Expand Up @@ -167,6 +168,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 +786,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 +1599,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
2 changes: 2 additions & 0 deletions src/org/rascalmpl/interpreter/IEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.rascalmpl.ast.Statement;
import org.rascalmpl.debug.IRascalMonitor;
import org.rascalmpl.debug.IRascalSuspendTriggerListener;
import org.rascalmpl.exceptions.Throw;
import org.rascalmpl.interpreter.callbacks.IConstructorDeclared;
import org.rascalmpl.interpreter.env.Environment;
import org.rascalmpl.interpreter.env.GlobalEnvironment;
Expand Down Expand Up @@ -60,6 +61,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
4 changes: 4 additions & 0 deletions src/org/rascalmpl/interpreter/result/RascalFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.rascalmpl.ast.Statement;
import org.rascalmpl.ast.Type.Structured;
import org.rascalmpl.exceptions.ImplementationError;
import org.rascalmpl.exceptions.Throw;
import org.rascalmpl.interpreter.Accumulator;
import org.rascalmpl.interpreter.IEvaluator;
import org.rascalmpl.interpreter.IEvaluatorContext;
Expand Down Expand Up @@ -369,6 +370,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