/******************************************************************************* * Copyright (c) 2009-2012 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI * * Emilie Balland - (CWI) * * Arnold Lankamp - Arnold.Lankamp@cwi.nl * * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI * * Bert Lisser - Bert.Lisser@cwi.nl - CWI *******************************************************************************/ package org.rascalmpl.eclipse.debug.core.model; import static org.rascalmpl.debug.DebugMessageFactory.requestResumption; import static org.rascalmpl.debug.DebugMessageFactory.requestStepInto; import static org.rascalmpl.debug.DebugMessageFactory.requestStepOver; import static org.rascalmpl.debug.DebugMessageFactory.requestSuspension; import static org.rascalmpl.debug.DebugMessageFactory.requestTermination; import java.util.Set; import java.util.Stack; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; import org.rascalmpl.debug.IRascalEventListener; import org.rascalmpl.debug.IRascalFrame; import org.rascalmpl.debug.IRascalRuntimeInspection; import org.rascalmpl.debug.RascalEvent; import org.rascalmpl.uri.URIUtil; import io.usethesource.vallang.ISourceLocation; /** * A Rascal thread. Rascal programs are currently modelled single threaded. */ public class RascalThread extends RascalDebugElement implements IThread, IRascalEventListener { private IBreakpoint fBreakpoint = null; private boolean fStepping = false; private boolean fSuspended = false; public RascalThread(IDebugTarget target) { super(target); getRascalDebugTarget().addEventListener(this); } @Override public String getName() throws DebugException { return "Rascal Thread"; } @Override public int getPriority() throws DebugException { return 0; } @Override public IBreakpoint[] getBreakpoints() { if (fBreakpoint == null) { return new IBreakpoint[0]; } return new IBreakpoint[]{fBreakpoint}; } @Override public IStackFrame[] getStackFrames() throws DebugException { if (isSuspended()) { IRascalRuntimeInspection eval = getRascalDebugTarget().getEvaluator(); Stack<IRascalFrame> callStack = eval.getCurrentStack(); int size = callStack.size(); Set<String> imports = callStack.get(size-1).getImports(); IStackFrame[] theFrames = new IStackFrame[callStack.size()+imports.size()]; // for the top, use the current AST location ISourceLocation currentLoc = eval.getCurrentPointOfExecution() != null ? eval.getCurrentPointOfExecution() : URIUtil.rootLocation("stdin"); theFrames[0] = new RascalStackFrame(this, callStack.get(size-1), currentLoc, null); for (int i = 1; i < size; i++) { theFrames[i] = new RascalStackFrame(this, callStack.get(size-i-1), callStack.get(size-i).getCallerLocation(), theFrames[i-1]); } return theFrames; } return new IStackFrame[0]; } @Override public IStackFrame getTopStackFrame() throws DebugException { IStackFrame[] frames = getStackFrames(); if (frames.length > 0) { return frames[0]; } return null; } @Override public boolean hasStackFrames() throws DebugException { IRascalRuntimeInspection eval = getRascalDebugTarget().getEvaluator(); return isSuspended() || eval.getTopFrame().getCallerLocation() != null; } @Override public boolean canResume() { return isSuspended(); } @Override public boolean canSuspend() { return !isSuspended() && !isTerminated(); } @Override public boolean isSuspended() { return fSuspended && !isTerminated(); } @Override public void resume() throws DebugException { sendRequest(requestResumption()); } @Override public void suspend() throws DebugException { sendRequest(requestSuspension()); } @Override public boolean canStepInto() { return !isTerminated() && isSuspended(); } @Override public boolean canStepOver() { return !isTerminated() && isSuspended(); } @Override public boolean canStepReturn() { return false; } @Override public boolean isStepping() { return fStepping; } @Override public void stepInto() throws DebugException { sendRequest(requestStepInto()); } @Override public void stepOver() throws DebugException { sendRequest(requestStepOver()); } @Override public void stepReturn() throws DebugException { /** * not used, see {@link #canStepReturn()} * */ } @Override public boolean canTerminate(){ return !isTerminated(); } @Override public boolean isTerminated(){ return getDebugTarget().isTerminated(); } @Override public synchronized void terminate() throws DebugException{ sendRequest(requestTermination()); } /** * Sets whether this thread is stepping * * @param stepping whether stepping */ private void setStepping(boolean stepping) { fStepping = stepping; } /** * Sets whether this thread is suspended * * @param suspended whether suspended */ private void setSuspended(boolean suspended) { fSuspended = suspended; } /** * Notifies this thread it has been suspended by the given breakpoint. * * @param breakpoint breakpoint */ public void suspendedBy(IBreakpoint breakpoint) { fBreakpoint = breakpoint; suspended(DebugEvent.BREAKPOINT); } /** * Notification the target has suspended for the given reason * * @param detail reason for the suspend */ private void suspended(int detail) { fireSuspendEvent(detail); } /** * Indicates if the reason for suspending this thread * is a breakpoint hit. * * @return suspension caused by breakpoint */ public boolean isSuspendedByBreakpoint() { return fBreakpoint != null && isSuspended(); } /** * Notification the target has resumed for the given reason. * Clears any error condition that was last encountered and * fires a resume event. * * @param detail reason for the resume */ public synchronized void resumed(int detail) { fireResumeEvent(detail); } @Override public void handleRascalEvent(RascalEvent event) { // clear previous state fBreakpoint = null; setStepping(false); switch (event.getKind()) { case SUSPEND: setSuspended(true); switch (event.getDetail()) { case BREAKPOINT: fireSuspendEvent(DebugEvent.BREAKPOINT); break; case CLIENT_REQUEST: fireSuspendEvent(DebugEvent.CLIENT_REQUEST); break; case STEP_END: fireSuspendEvent(DebugEvent.STEP_END); break; default: new UnsupportedOperationException("Suspension mode not supported."); } break; case RESUME: setSuspended(false); switch (event.getDetail()) { case STEP_INTO: setStepping(true); resumed(DebugEvent.STEP_INTO); break; case STEP_OVER: setStepping(true); resumed(DebugEvent.STEP_OVER); break; case CLIENT_REQUEST: setStepping(false); resumed(DebugEvent.CLIENT_REQUEST); break; default: new UnsupportedOperationException("Continuation mode not supported."); } break; case CREATE: case IDLE: /* * sending a request of resumption to the runtime cleans the state, * if it the last operation before IDLE was a step over or step * into. (JV: but I commented this out to get a variables view for the console frame.) */ // sendRequest(requestResumption()); setSuspended(true); fireSuspendEvent(DebugEvent.BREAKPOINT | DebugEvent.STEP_END); // BREAKPOINT is essential to trigger viewer updates break; case TERMINATE: setSuspended(true); fireSuspendEvent(DebugEvent.TERMINATE); } } }