Skip to content
23 changes: 21 additions & 2 deletions src/org/rascalmpl/dap/RascalDebugAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public CompletableFuture<Capabilities> initialize(InitializeRequestArguments arg

capabilities.setSupportsConfigurationDoneRequest(true);
capabilities.setSupportsStepBack(false);
capabilities.setSupportsRestartFrame(false);
capabilities.setSupportsRestartFrame(true);
capabilities.setSupportsSetVariable(false);
capabilities.setSupportsRestartRequest(false);
capabilities.setSupportsCompletionsRequest(true);
Expand Down Expand Up @@ -395,13 +395,15 @@ private StackFrame createStackFrame(int id, ISourceLocation loc, String name){
StackFrame frame = new StackFrame();
frame.setId(id);
frame.setName(name);
frame.setCanRestart(false);
if(loc != null && !loc.getScheme().equals(promptLocation.getScheme())) {
var offsets = columns.get(loc);
var line = shiftLine(loc.getBeginLine());
var column = shiftColumn(offsets.translateColumn(loc.getBeginLine(), loc.getBeginColumn(), false));
frame.setLine(line);
frame.setColumn(column);
frame.setSource(getSourceFromISourceLocation(loc));
frame.setCanRestart(true);
}
return frame;
}
Expand Down Expand Up @@ -766,6 +768,23 @@ public CompletableFuture<CompletionsResponse> completions(CompletionsArguments a
return response;
}, ownExecutor);
}


@Override
public CompletableFuture<Void> restartFrame(RestartFrameArguments args) {
return CompletableFuture.supplyAsync(() -> {
if(args.getFrameId() == 0){
// From DAP spec, we must send a stopped event. Here we use this to indicate that the REPL frame cannot be restarted
StoppedEventArguments stoppedEventArguments = new StoppedEventArguments();
stoppedEventArguments.setThreadId(RascalDebugAdapter.mainThreadID);
stoppedEventArguments.setDescription("Cannot restart the REPL frame");
stoppedEventArguments.setReason("restart");
client.stopped(stoppedEventArguments);
} else{
debugHandler.processMessage(DebugMessageFactory.requestRestartFrame(args.getFrameId()));
}
return null;

}, ownExecutor);
}
}

11 changes: 11 additions & 0 deletions src/org/rascalmpl/dap/RascalDebugEventTrigger.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,17 @@ public void fireSuspendByExceptionEvent(Exception exception) {
client.stopped(stoppedEventArguments);
}

@Override
public void fireSuspendByRestartFrameEndEvent() {
suspendedState.suspended();

StoppedEventArguments stoppedEventArguments = new StoppedEventArguments();
stoppedEventArguments.setThreadId(RascalDebugAdapter.mainThreadID);
stoppedEventArguments.setDescription("Paused after restarting frame.");
stoppedEventArguments.setReason("restart");
client.stopped(stoppedEventArguments);
}

@Override
public void fireSuspendEvent(RascalEvent.Detail detail) {}
}
8 changes: 8 additions & 0 deletions src/org/rascalmpl/debug/AbstractInterpreterEventTrigger.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ public void fireSuspendByStepEndEvent() {
fireSuspendEvent(RascalEvent.Detail.STEP_END);
}

/**
* Fires a resume event for this debug element with detail
* <code>RascalEvent.Detail.CLIENT_REQUEST</code>.
*/
public void fireSuspendByRestartFrameEndEvent() {
fireSuspendEvent(RascalEvent.Detail.RESTART_FRAME_END);
}

/**
* Fires a suspend event for this debug element with detail
* <code>RascalEvent.Detail.STEP_END</code>.
Expand Down
28 changes: 28 additions & 0 deletions src/org/rascalmpl/debug/DebugHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import static org.rascalmpl.debug.AbstractInterpreterEventTrigger.newNullEventTrigger;

import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Map;
import java.util.function.IntSupplier;

Expand All @@ -26,6 +28,7 @@
import org.rascalmpl.interpreter.Evaluator;
import org.rascalmpl.interpreter.control_exceptions.InterruptException;
import org.rascalmpl.interpreter.control_exceptions.QuitException;
import org.rascalmpl.interpreter.control_exceptions.RestartFrameException;
import org.rascalmpl.interpreter.env.Environment;
import org.rascalmpl.interpreter.env.Pair;
import org.rascalmpl.interpreter.result.Result;
Expand Down Expand Up @@ -107,6 +110,12 @@ private enum DebugStepMode {
*/
private Evaluator evaluator = null;

/**
* Flag indicating a frame restart has been requested.
* Set by the debugger thread, checked and used by the evaluated thread in suspended().
*/
private AtomicInteger restartFrameId = new AtomicInteger(-1);

/**
* Create a new debug handler with its own interpreter event trigger.
*/
Expand Down Expand Up @@ -276,6 +285,12 @@ public void suspended(Object runtime, IntSupplier getCallStackSize, AbstractAST
// Ignore
}
}

// Check if a frame restart was requested while suspended
int frameToRestart = restartFrameId.getAndSet(-1);
if (frameToRestart >= 0) {
throw new RestartFrameException(frameToRestart);
}
}

private boolean handleExceptionSuspension(Evaluator eval, Exception e) {
Expand Down Expand Up @@ -487,6 +502,19 @@ public void processMessage(IDebugMessage message) {
terminateAction.run();
}
break;

case RESTART_FRAME:
if(suspended) {
int frameId = (int) message.getPayload();
assert frameId >= 0 && frameId < evaluator.getCurrentStack().size(): "Frame id out of bounds: " + frameId;
// Set flag for the evaluated thread to handle the restart
if (restartFrameId.compareAndSet(-1, frameId)) {
// Unsuspend to let the evaluated thread continue and hit the restart exception
setSuspendRequested(true);
setSuspended(false);
}
}
break;
}
break;
}
Expand Down
4 changes: 4 additions & 0 deletions src/org/rascalmpl/debug/DebugMessageFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public static IDebugMessage requestStepOut(){
public static IDebugMessage requestTermination() {
return new DebugMessage(IDebugMessage.Action.TERMINATE, IDebugMessage.Subject.INTERPRETER, IDebugMessage.Detail.UNKNOWN);
}

public static IDebugMessage requestRestartFrame(int frameId) {
return new DebugMessage(IDebugMessage.Action.RESTART_FRAME, IDebugMessage.Subject.INTERPRETER, IDebugMessage.Detail.UNKNOWN, frameId);
}

/*
* Breakpoint requests.
Expand Down
2 changes: 1 addition & 1 deletion src/org/rascalmpl/debug/IDebugMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public interface IDebugMessage {
* <code>UNKNOWN</code> if not provided.
*/
enum Action {
UNKNOWN, SET, DELETE, TERMINATE, SUSPEND, RESUME
UNKNOWN, SET, DELETE, TERMINATE, SUSPEND, RESUME, RESTART_FRAME
}

/**
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, EXCEPTION
UNSPECIFIED, CLIENT_REQUEST, STEP_INTO, STEP_OVER, STEP_END, BREAKPOINT, STEP_OUT, EXCEPTION, RESTART_FRAME_END
}

private final Kind kind;
Expand Down
11 changes: 7 additions & 4 deletions src/org/rascalmpl/interpreter/Evaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -1608,12 +1608,15 @@ 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);
try{
if (!suspendTriggerListeners.isEmpty()) { // remove the breakable condition since exception can happen anywhere
for (IRascalSuspendTriggerListener listener : suspendTriggerListeners) {
listener.suspended(this, () -> getCallStack().size(), currentAST);
}
}
} finally { // clear the exception after notifying listeners
currentException = null;
}
currentException = null;
}

public AbstractInterpreterEventTrigger getEventTrigger() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.rascalmpl.interpreter.control_exceptions;

/**
* Exception thrown to request a frame restart in the debugger.
* This exception carries the target frame ID and propagates up the call stack
* until it reaches the appropriate frame that should be restarted.
*/
public class RestartFrameException extends ControlException {
private final static long serialVersionUID = 1L;
private final int targetFrameId;

public RestartFrameException(int targetFrameId) {
super("Restart frame: " + targetFrameId);
this.targetFrameId = targetFrameId;
}

public int getTargetFrameId() {
return targetFrameId;
}
}
Loading
Loading