Skip to content
Open
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
21 changes: 19 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.setExceptionBreakpointFilters(new ExceptionBreakpointsFilter[]{});
capabilities.setSupportsStepBack(false);
capabilities.setSupportsRestartFrame(false);
capabilities.setSupportsRestartFrame(true);
capabilities.setSupportsSetVariable(false);
capabilities.setSupportsRestartRequest(false);
capabilities.setSupportsCompletionsRequest(true);
Expand Down Expand Up @@ -748,6 +748,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 @@ -132,6 +132,17 @@ public void fireSuspendByClientRequestEvent() {
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 @@ -95,6 +98,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 @@ -250,6 +259,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);
}
}

protected AbstractAST getReferenceAST() {
Expand Down Expand Up @@ -446,6 +461,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
UNSPECIFIED, CLIENT_REQUEST, STEP_INTO, STEP_OVER, STEP_END, BREAKPOINT, STEP_OUT, RESTART_FRAME_END
}

private final Kind kind;
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