package org.eclipse.jdt.internal.debug.core; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import java.util.*; | |
import org.eclipse.core.resources.IMarker; | |
import org.eclipse.debug.core.*; | |
import org.eclipse.debug.core.model.*; | |
import org.eclipse.jdt.core.IJavaProject; | |
import org.eclipse.jdt.core.eval.IEvaluationContext; | |
import org.eclipse.jdt.debug.core.IJavaEvaluationListener; | |
import org.eclipse.jdt.debug.core.IJavaThread; | |
import com.sun.jdi.*; | |
import com.sun.jdi.event.*; | |
import com.sun.jdi.request.*; | |
/** | |
* Proxy to a thread reference on the target. | |
*/ | |
public class JDIThread extends JDIDebugElement implements IJavaThread, ITimeoutListener { | |
// Resource String keys | |
private static final String PREFIX= "jdi_thread."; | |
private static final String ERROR= PREFIX + "error."; | |
private static final String ERROR_GET_NAME= ERROR + "get_name"; | |
private static final String ERROR_GET_PRIORITY= ERROR + "get_priority"; | |
private static final String ERROR_GET_THREAD_GROUP= ERROR + "get_thread_group"; | |
private static final String ERROR_GET_THREAD_GROUP_NAME= ERROR + "get_thread_group_name"; | |
private static final String ERROR_DROPPING_FRAME= ERROR + "dropping_frame"; | |
private static final String ERROR_CREATING_STEP_REQUEST= ERROR + "creating_step_request"; | |
private static final String ERROR_INVOKING_METHOD= ERROR + "invoking_method"; | |
private static final String ERROR_RESUME= ERROR + "resume"; | |
private static final String ERROR_STEP= ERROR + "step"; | |
private static final String ERROR_SUSPEND= ERROR + "suspend"; | |
private static final String ERROR_TERMINATE= ERROR + "terminate"; | |
private static final String CANT_TERMINATE= ERROR + "cant_terminate"; | |
protected static final String IN_EVALUATION= ERROR + "in_evaluation"; | |
protected static final String NO_BUILT_STATE= ERROR + "no_built_state"; | |
protected static final String INVALID_EVALUATION_LOCATION= ERROR + "invalid_evaluation_location"; | |
protected static final String MAIN_THREAD_GROUP = "main"; | |
/** | |
* Underlying thread. | |
*/ | |
protected ThreadReference fThread; | |
/** | |
* Underlying thread group. | |
*/ | |
protected ThreadGroupReference fThreadGroup; | |
/** | |
* Whether children need to be refreshed. Set to | |
* to true after a step has started. | |
*/ | |
protected boolean fRefreshChildren = true; | |
/** | |
* Step request used to step in this thread (only one is allowed per | |
* thread). | |
*/ | |
protected StepRequest fStepRequest= null; | |
/** | |
* Whether running. | |
*/ | |
protected boolean fRunning; | |
/** | |
* Whether stepping. | |
*/ | |
protected boolean fStepping; | |
protected int fStepCount= 0; | |
/** | |
* Whether suspended by an event in the VM such as a | |
* breakpoint or step, or via an explicit user | |
* request to suspend. | |
*/ | |
protected boolean fEventSuspend = false; | |
/** | |
* The destination stack frame when stepping | |
* in the non-top stack frame, or <code>null</code> | |
* when stepping in the top stack frame. | |
*/ | |
protected IStackFrame fDestinationFrame; | |
/** | |
* Step timer. During a long running step, children are disposed. | |
*/ | |
protected Timer fTimer; | |
/** | |
* Whether terminated. | |
*/ | |
protected boolean fTerminated; | |
/** | |
* Whether this thread is a system thread. | |
*/ | |
protected boolean fIsSystemThread; | |
/** | |
* The breakpoint that caused the last suspend, or | |
* <code>null</code> if none. | |
*/ | |
protected IMarker fCurrentBreakpoint; | |
/** | |
* The cached named of the underlying thread. | |
*/ | |
protected String fName= null; | |
/** | |
* Whether this thread is doing a "drop to frame". | |
*/ | |
protected boolean fDropping= false; | |
/** | |
* Whether this thread is doing a "reenter top frame" | |
*/ | |
protected boolean fReentering= false; | |
/** | |
* A count of the number of frames remaining to drop | |
*/ | |
protected int fFramesToDrop= 0; | |
/** | |
* Whether this thread is currently performing | |
* an evaluation (invoke method). Nested method | |
* invocations cannot be performed. | |
*/ | |
protected boolean fInEvaluation = false; | |
protected boolean fEvaluationAborted = false; | |
/** | |
* Creates a new thread on the underlying thread reference. | |
*/ | |
public JDIThread(JDIDebugTarget target, ThreadReference thread) { | |
super(target); | |
fThread= thread; | |
initialize(); | |
} | |
/** | |
* Initializes this thread on creation | |
*/ | |
protected void initialize() { | |
// system thread | |
try { | |
determineIfSystemThread(); | |
} catch (DebugException e) { | |
internalError(e); | |
} | |
// state | |
fTerminated= false; | |
fStepping= false; | |
try { | |
fRunning= !getUnderlyingThread().isSuspended(); | |
} catch (VMDisconnectedException e) { | |
fTerminated = true; | |
fRunning = false; | |
} catch (RuntimeException e) { | |
internalError(e); | |
fRunning= false; | |
} | |
} | |
/** | |
* @see IDebugElement | |
*/ | |
public int getElementType() { | |
return THREAD; | |
} | |
/** | |
* @see IDebugElement | |
*/ | |
public IThread getThread() { | |
return this; | |
} | |
/** | |
* @see IJavaThread | |
*/ | |
public IMarker getBreakpoint() { | |
if (fCurrentBreakpoint != null && !fCurrentBreakpoint.exists()) { | |
fCurrentBreakpoint= null; | |
} | |
return fCurrentBreakpoint; | |
} | |
/** | |
* @see ISuspendResume | |
*/ | |
public boolean canResume() { | |
return isSuspended(); | |
} | |
/** | |
* @see ISuspendResume | |
*/ | |
public boolean canSuspend() { | |
return !isSuspended(); | |
} | |
/** | |
* @see ITerminate | |
*/ | |
public boolean canTerminate() { | |
ObjectReference threadDeath= ((JDIDebugTarget) getDebugTarget()).getThreadDeathInstance(); | |
return threadDeath != null && !isSystemThread() && !isTerminated(); | |
} | |
/** | |
* @see IStep | |
*/ | |
public boolean canStepInto() { | |
return isSuspended() && !isStepping(); | |
} | |
/** | |
* @see IStep | |
*/ | |
public boolean canStepOver() { | |
return isSuspended() && !isStepping(); | |
} | |
/** | |
* @see IStep | |
*/ | |
public boolean canStepReturn() { | |
return isSuspended() && !isStepping(); | |
} | |
/** | |
* Determines and sets whether this thread represents a system thread. | |
*/ | |
protected void determineIfSystemThread() throws DebugException { | |
fIsSystemThread= false; | |
ThreadGroupReference tgr= getUnderlyingThreadGroup(); | |
fIsSystemThread = tgr != null; | |
while (tgr != null) { | |
String tgn= null; | |
try { | |
tgn= tgr.name(); | |
tgr= tgr.parent(); | |
} catch (VMDisconnectedException e) { | |
break; | |
} catch (UnsupportedOperationException e) { | |
fIsSystemThread = false; | |
break; | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_GET_THREAD_GROUP_NAME, e); | |
} | |
if (tgn != null && tgn.equals(MAIN_THREAD_GROUP)) { | |
fIsSystemThread= false; | |
break; | |
} | |
} | |
} | |
protected void enableStepRequest(int type) throws DebugException { | |
EventRequestManager erm= getEventRequestManager(); | |
try { | |
if (fStepRequest != null) | |
erm.deleteEventRequest(fStepRequest); | |
fStepRequest= erm.createStepRequest(fThread, StepRequest.STEP_LINE, type); | |
fStepRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); | |
fStepRequest.addCountFilter(1); | |
fStepRequest.enable(); | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_CREATING_STEP_REQUEST, e); | |
} | |
} | |
/** | |
* @see IDebugElement | |
*/ | |
public boolean hasChildren() { | |
return isSuspended(); | |
} | |
/** | |
* @see IDebugElement | |
*/ | |
protected List getChildren0() throws DebugException { | |
if (isSuspended()) { | |
if (isTerminated()) { | |
fChildren = Collections.EMPTY_LIST; | |
return fChildren; | |
} | |
if (fRefreshChildren) { | |
if (fChildren == null || fChildren.isEmpty()) { | |
fChildren = createAllStackFrames(); | |
} else { | |
// compute new or removed stack frames | |
List frames= getUnderlyingFrames(); | |
int offset= 0, length= frames.size(); | |
if (length > fChildren.size()) { | |
// compute new children | |
offset= length - fChildren.size(); | |
for (int i= offset - 1; i >= 0; i--) { | |
JDIStackFrame newStackFrame= new JDIStackFrame(this, (StackFrame) frames.get(i)); | |
// addChild appends - we need a stack, so insert manually | |
fChildren.add(0, newStackFrame); | |
} | |
length= fChildren.size() - offset; | |
} else | |
if (length < fChildren.size()) { | |
// compute removed children | |
int removed= fChildren.size() - length; | |
for (int i= 0; i < removed; i++) { | |
fChildren.remove(0); | |
} | |
} else { | |
if (frames.isEmpty()) { | |
fChildren = Collections.EMPTY_LIST; | |
return fChildren; | |
} else { | |
// same number of stack frames - if the TOS is different, remove/replace all stack frames | |
Method oldMethod= ((JDIStackFrame) fChildren.get(0)).getUnderlyingMethod(); | |
if (oldMethod == null) { | |
fChildren = createAllStackFrames(); | |
return fChildren; | |
} | |
StackFrame newTOS= (StackFrame) frames.get(0); | |
Method newMethod= getUnderlyingMethod(newTOS); | |
if (newMethod == null) { | |
fChildren = createAllStackFrames(); | |
return fChildren; | |
} | |
if (!oldMethod.equals(newMethod)) { | |
// remove & replace all stack frames | |
fChildren= createAllStackFrames(); | |
// no stack frames to update | |
offset= fChildren.size(); | |
} | |
} | |
} | |
// update existing frames | |
if (offset < fChildren.size()) { | |
updateStackFrames(frames, offset, fChildren, length); | |
} | |
} | |
fRefreshChildren = false; | |
} | |
} else | |
return Collections.EMPTY_LIST; | |
return fChildren; | |
} | |
/** | |
* Helper method for #getChildren0 to create new children for all frames | |
*/ | |
protected List createAllStackFrames() throws DebugException { | |
List frames= getUnderlyingFrames(); | |
if (frames == null) { | |
return new ArrayList(0); | |
} | |
List list= new ArrayList(frames.size()); | |
Iterator iter= frames.iterator(); | |
while (iter.hasNext()) { | |
JDIStackFrame newStackFrame= new JDIStackFrame(this, (StackFrame) iter.next()); | |
list.add(newStackFrame); | |
} | |
return list; | |
} | |
/** | |
* Helper method for #getChildren to retrieve stack frames for this thread | |
* | |
* @see com.sun.jdi.ThreadReference | |
*/ | |
protected List getUnderlyingFrames() throws DebugException { | |
List frames= null; | |
try { | |
frames= fThread.frames(); | |
} catch (IncompatibleThreadStateException e) { | |
targetRequestFailed(ERROR_GET_CHILDREN, e); | |
} catch (VMDisconnectedException e) { | |
return Collections.EMPTY_LIST; | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_GET_CHILDREN, e); | |
} | |
return frames; | |
} | |
/** | |
* Helper method for #getChildren to retrieve the method for a stack frame | |
*/ | |
protected Method getUnderlyingMethod(StackFrame frame) throws DebugException { | |
Method method= null; | |
try { | |
method= frame.location().method(); | |
} catch (VMDisconnectedException e) { | |
return null; | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_GET_CHILDREN, e); | |
} | |
return method; | |
} | |
/** | |
* Invokes a method in this thread, and returns the result. Only one receiver may | |
* be specified - either a class or an object, the other must be <code>null</code>. | |
*/ | |
protected Value invokeMethod(ClassType receiverClass, ObjectReference receiverObject, Method method, List args) throws DebugException { | |
if (fInEvaluation) { | |
requestFailed(IN_EVALUATION, null); | |
} | |
Value result= null; | |
int timeout= getRequestTimeout(); | |
try { | |
// set the request timeout to be infinite | |
setRequestTimeout(Integer.MAX_VALUE); | |
setRunning(true); | |
fInEvaluation = true; | |
if (receiverClass == null) { | |
result= receiverObject.invokeMethod(fThread, method, args, ClassType.INVOKE_SINGLE_THREADED); | |
} else { | |
result= receiverClass.invokeMethod(fThread, method, args, ClassType.INVOKE_SINGLE_THREADED); | |
} | |
} catch (InvalidTypeException e) { | |
invokeFailed(e, timeout); | |
} catch (ClassNotLoadedException e) { | |
invokeFailed(e, timeout); | |
} catch (IncompatibleThreadStateException e) { | |
invokeFailed(e, timeout); | |
} catch (InvocationException e) { | |
invokeFailed(e, timeout); | |
} catch (RuntimeException e) { | |
invokeFailed(e, timeout); | |
} | |
invokeComplete(timeout); | |
if (fEvaluationAborted) { | |
fEvaluationAborted = false; | |
resume(); | |
} | |
return result; | |
} | |
/** | |
* Called by JDIValue when an evaluation of | |
* #toString times out. Causes this thread to | |
* be automatically resumed when it returns from | |
* its evaluation - see <code>invokeMethod</code>. | |
*/ | |
protected void abortEvaluation() { | |
fEvaluationAborted = true; | |
} | |
/** | |
* Invokes a method in this thread, creating a new instance of the given | |
* class using the specified constructor, and returns the result. | |
*/ | |
protected ObjectReference newInstance(ClassType receiverClass, Method constructor, List args) throws DebugException { | |
if (fInEvaluation) { | |
requestFailed(IN_EVALUATION, null); | |
} | |
ObjectReference result= null; | |
int timeout= getRequestTimeout(); | |
try { | |
// set the request timeout to be infinite | |
setRequestTimeout(Integer.MAX_VALUE); | |
setRunning(true); | |
fInEvaluation = true; | |
result= receiverClass.newInstance(fThread, constructor, args, ClassType.INVOKE_SINGLE_THREADED); | |
} catch (InvalidTypeException e) { | |
invokeFailed(e, timeout); | |
} catch (ClassNotLoadedException e) { | |
invokeFailed(e, timeout); | |
} catch (IncompatibleThreadStateException e) { | |
invokeFailed(e, timeout); | |
} catch (InvocationException e) { | |
invokeFailed(e, timeout); | |
} catch (RuntimeException e) { | |
invokeFailed(e, timeout); | |
} | |
invokeComplete(timeout); | |
return result; | |
} | |
/** | |
* An invocation failed. Restore the JDI timeout value and | |
* handle the exception. | |
*/ | |
protected void invokeFailed(Throwable e, int restoreTimeout) throws DebugException { | |
invokeComplete(restoreTimeout); | |
targetRequestFailed(ERROR_INVOKING_METHOD, e); | |
} | |
/** | |
* Update state when invocation is complete. Restore | |
* the orginal timeout value for JDI requests. | |
*/ | |
protected void invokeComplete(int restoreTimeout) { | |
setRunning(false); | |
fInEvaluation = false; | |
setRequestTimeout(restoreTimeout); | |
} | |
/** | |
* Sets the timeout interval for jdi requests in milliseconds | |
*/ | |
protected void setRequestTimeout(int timeout) { | |
VirtualMachine vm = getVM(); | |
if (vm instanceof org.eclipse.jdi.VirtualMachine) { | |
((org.eclipse.jdi.VirtualMachine) vm).setRequestTimeout(timeout); | |
} | |
} | |
/** | |
* Returns the timeout interval for jdi requests in millieseconds, | |
* or -1 if not supported | |
*/ | |
protected int getRequestTimeout() { | |
VirtualMachine vm = getVM(); | |
if (vm instanceof org.eclipse.jdi.VirtualMachine) { | |
return ((org.eclipse.jdi.VirtualMachine) vm).getRequestTimeout(); | |
} | |
return -1; | |
} | |
/** | |
* @see IDebugElement | |
*/ | |
public String getName() throws DebugException { | |
if (fName == null) { | |
try { | |
fName = getUnderlyingThread().name(); | |
} catch (VMDisconnectedException e) { | |
fName = getUnknownMessage(); | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_GET_NAME, e); | |
} | |
} | |
return fName; | |
} | |
/** | |
* @see IThread | |
*/ | |
public int getPriority() throws DebugException { | |
// to get the priority, we must get the value from the "priority" field | |
Field p= null; | |
try { | |
p= getUnderlyingThread().referenceType().fieldByName("priority"); | |
if (p == null) { | |
requestFailed(ERROR_GET_PRIORITY, null); | |
} | |
Value v= getUnderlyingThread().getValue(p); | |
if (v instanceof IntegerValue) { | |
return ((IntegerValue)v).value(); | |
} else { | |
requestFailed(ERROR_GET_PRIORITY, null); | |
} | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_GET_PRIORITY, e); | |
} | |
return -1; | |
} | |
/** | |
* @see IThread | |
*/ | |
public IStackFrame getTopStackFrame() throws DebugException { | |
if (isSuspended()) { | |
List c= getChildren0(); | |
if (c.isEmpty()) { | |
return null; | |
} else { | |
return (IStackFrame) c.get(0); | |
} | |
} else { | |
return null; | |
} | |
} | |
/** | |
* Suspend the thread based on a breakpoint or watchpoint event | |
*/ | |
protected void handleLocatableEvent(LocatableEvent event) { | |
abortDropAndStep(); | |
fCurrentBreakpoint= (IMarker) event.request().getProperty(IDebugConstants.BREAKPOINT_MARKER); | |
setRunning(false, DebugEvent.BREAKPOINT); | |
((JDIDebugTarget) getDebugTarget()).expireHitCount(event); | |
} | |
/** | |
* Suspend the thread based on an exception event | |
*/ | |
protected void handleException(ExceptionEvent event) { | |
abortDropAndStep(); | |
fCurrentBreakpoint= (IMarker) event.request().getProperty(IDebugConstants.BREAKPOINT_MARKER); | |
setRunning(false, DebugEvent.BREAKPOINT); | |
} | |
/** | |
* Suspend the thread based on the method entry event | |
*/ | |
protected void handleSuspendMethodEntry(IMarker breakpoint) { | |
abortDropAndStep(); | |
fCurrentBreakpoint= breakpoint; | |
setRunning(false, DebugEvent.BREAKPOINT); | |
} | |
protected void handleStep(StepEvent event) { | |
fRunning = false; | |
if (fDestinationFrame != null) { | |
try { | |
if (getTopStackFrame().equals(fDestinationFrame)) { | |
fDestinationFrame = null; | |
} else if (getChildren0().indexOf(fDestinationFrame) == -1) { | |
fDestinationFrame = null; | |
} else { | |
if (hasPendingEvents()) { | |
fDestinationFrame = null; | |
} else { | |
stepReturn0(); | |
fRunning = true; | |
fStepCount--; | |
return; | |
} | |
} | |
} catch (DebugException e) { | |
abortDropAndStep(); | |
internalError(e); | |
} | |
} else if (fDropping) { | |
fFramesToDrop--; | |
fDropping= fFramesToDrop > 0; | |
if (fDropping) { | |
try { | |
dropTopFrame(); | |
} catch (DebugException e) { | |
abortDropAndStep(); | |
internalError(e); | |
} | |
} else { | |
try { | |
reenterTopFrame(); | |
} catch (DebugException e) { | |
abortDropAndStep(); | |
internalError(e); | |
} | |
} | |
} else if (fReentering) { | |
fReentering= false; | |
try { | |
stepInto0(); | |
} catch (DebugException e) { | |
abortDropAndStep(); | |
internalError(e); | |
} | |
} | |
fRunning = true; | |
setRunning(false, DebugEvent.STEP_END); | |
} | |
/** | |
* @see IStep | |
*/ | |
public boolean isStepping() { | |
return fStepping; | |
} | |
/** | |
* @see IStep | |
*/ | |
public boolean isSuspended() { | |
return !fRunning && !fTerminated; | |
} | |
/** | |
* @see IJavaThread | |
*/ | |
public boolean isSystemThread() { | |
return fIsSystemThread; | |
} | |
/** | |
* @see IJavaThread | |
*/ | |
public String getThreadGroupName() throws DebugException { | |
ThreadGroupReference tgr= getUnderlyingThreadGroup(); | |
if (tgr != null) { | |
try { | |
return fThreadGroup.name(); | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_GET_THREAD_GROUP_NAME, e); | |
} | |
} | |
return getUnknownMessage(); | |
} | |
/** | |
* @see ITerminate | |
*/ | |
public boolean isTerminated() { | |
return fTerminated; | |
} | |
/** | |
* @see ISuspendResume | |
*/ | |
public void resume() throws DebugException { | |
if (!isSuspended()) { | |
return; | |
} | |
try { | |
setRunning(true); | |
fThread.resume(); | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
setRunning(false); | |
targetRequestFailed(ERROR_RESUME, e); | |
} | |
} | |
/** | |
* Sets the running state for this thread. Invalidates | |
* children on a step start, or clears them on a resume. | |
* Fires resume/suspend events. Starts/stops step timer. | |
*/ | |
void setRunning(boolean running, int detail) { | |
if (fRunning != running) { | |
fRunning= running; | |
if (fRunning) { | |
fCurrentBreakpoint= null; | |
fRefreshChildren = true; | |
if (detail == DebugEvent.STEP_START) { | |
fStepCount++; | |
fStepping = true; | |
if (fStepCount == 1) { | |
invalidateStackFrames(); | |
startStepTimer(); | |
} | |
} else { | |
fChildren = null; | |
} | |
if (!fStepping || fStepCount == 1) { | |
fireResumeEvent(detail); | |
} | |
} else { | |
if (detail == DebugEvent.STEP_END) { | |
fStepCount--; | |
} | |
if (fStepCount == 0) { | |
stopStepTimer(); | |
// update underlying stack frames | |
try { | |
getChildren0(); | |
} catch (DebugException e) { | |
internalError(e); | |
} | |
fStepping= false; | |
fireSuspendEvent(detail); | |
} | |
fEventSuspend = detail != DebugEvent.CLIENT_REQUEST; | |
} | |
} | |
} | |
void setRunning(boolean running) { | |
setRunning(running, -1); | |
} | |
protected void invalidateStackFrames() { | |
if (fChildren != null) { | |
Iterator frames = fChildren.iterator(); | |
while (frames.hasNext()) { | |
((JDIStackFrame)frames.next()).invalidateVariables(); | |
} | |
} | |
} | |
protected void step(int type) throws DebugException { | |
try { | |
setRunning(true, DebugEvent.STEP_START); | |
enableStepRequest(type); | |
fThread.resume(); | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
setRunning(false, DebugEvent.STEP_END); | |
targetRequestFailed(ERROR_STEP, e); | |
} | |
} | |
/** | |
* A step has timed out. Children are disposed. | |
*/ | |
public void timeout() { | |
fChildren = Collections.EMPTY_LIST; | |
fireChangeEvent(); | |
} | |
/** | |
* @see IStep | |
*/ | |
public void stepInto() throws DebugException { | |
if (!canStepInto()) { | |
return; | |
} | |
stepInto0(); | |
} | |
private void stepInto0() throws DebugException { | |
step(StepRequest.STEP_INTO); | |
} | |
/** | |
* @see IStep | |
*/ | |
public void stepOver() throws DebugException { | |
if (!canStepOver()) { | |
return; | |
} | |
step(StepRequest.STEP_OVER); | |
} | |
/** | |
* @see IStep | |
*/ | |
public void stepReturn() throws DebugException { | |
if (!canStepReturn()) { | |
return; | |
} | |
stepReturn0(); | |
} | |
private void stepReturn0() throws DebugException { | |
step(StepRequest.STEP_OUT); | |
} | |
/** | |
* @see ISuspendResume | |
*/ | |
public void suspend() throws DebugException { | |
try { | |
// remove any pending step request | |
if (fStepRequest != null) { | |
try { | |
getEventRequestManager().deleteEventRequest(fStepRequest); | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_SUSPEND, e); | |
} | |
} | |
fThread.suspend(); | |
abortDropAndStep(); | |
setRunning(false, DebugEvent.CLIENT_REQUEST); | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
setRunning(true); | |
targetRequestFailed(ERROR_SUSPEND, e); | |
} | |
} | |
/** | |
* @see ITerminate | |
*/ | |
public void terminate() throws DebugException { | |
ObjectReference threadDeath= ((JDIDebugTarget) getDebugTarget()).getThreadDeathInstance(); | |
if (threadDeath != null) { | |
try { | |
fThread.stop(threadDeath); | |
} catch (InvalidTypeException e) { | |
targetRequestFailed(ERROR_TERMINATE, e); | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_TERMINATE, e); | |
} | |
// Resume the thread so that stop will work | |
resume(); | |
} else { | |
requestFailed(CANT_TERMINATE, null); | |
} | |
} | |
/** | |
* Replaces the StackFrame objects in the old frames list with the objects | |
* from the new frames list. StackFrames are invalid after a resume or step | |
* and must be replaced with the new objects. | |
*/ | |
protected void updateStackFrames(List newFrames, int offset, List oldFrames, int length) throws DebugException { | |
for (int i= 0; i < length; i++) { | |
JDIStackFrame frame= (JDIStackFrame) oldFrames.get(offset); | |
frame.setUnderlyingStackFrame((StackFrame) newFrames.get(offset)); | |
offset++; | |
} | |
} | |
/** | |
* Drops to the given stack frame | |
*/ | |
protected void dropToFrame(IStackFrame frame) throws DebugException { | |
fFramesToDrop= getChildren0().indexOf(frame); | |
fDropping= fFramesToDrop > 0; | |
if (fDropping) { | |
dropTopFrame(); | |
} else { | |
reenterTopFrame(); | |
} | |
} | |
/** | |
* Drops the top frame, sending a step start event | |
*/ | |
protected void dropTopFrame() throws DebugException { | |
try { | |
enableStepRequest(StepRequest.STEP_OUT); | |
setRunning(true, DebugEvent.STEP_START); | |
// Resume with a do return | |
org.eclipse.jdi.hcr.ThreadReference hcrThread= (org.eclipse.jdi.hcr.ThreadReference) fThread; | |
hcrThread.doReturn(null, true); | |
} catch (VMDisconnectedException e) { | |
setRunning(false); | |
} catch (RuntimeException e) { | |
setRunning(false); | |
targetRequestFailed(ERROR_DROPPING_FRAME, e); | |
} | |
} | |
protected void stepToFrame(IStackFrame frame) throws DebugException { | |
if (!canStepReturn()) { | |
return; | |
} | |
fDestinationFrame = frame; | |
stepReturn(); | |
} | |
/** | |
* Reenters the top frame | |
* | |
* @exception DebugException on failure | |
*/ | |
private void reenterTopFrame() throws DebugException { | |
try { | |
fReentering= true; | |
EventRequestManager erm= getEventRequestManager(); | |
if (fStepRequest != null) { | |
erm.deleteEventRequest(fStepRequest); | |
} | |
fStepRequest= ((org.eclipse.jdi.hcr.EventRequestManager) erm).createReenterStepRequest(fThread); | |
fStepRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); | |
fStepRequest.addCountFilter(1); | |
fStepRequest.enable(); | |
// Resume with a do return | |
org.eclipse.jdi.hcr.ThreadReference hcrThread= (org.eclipse.jdi.hcr.ThreadReference) fThread; | |
hcrThread.doReturn(null, true); | |
setRunning(true, DebugEvent.STEP_START); | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
fReentering= false; | |
setRunning(false); | |
targetRequestFailed(ERROR_DROPPING_FRAME, e); | |
} | |
} | |
protected void abortDropAndStep() { | |
fStepCount = 0; | |
abortDrop(); | |
abortStep(); | |
} | |
/** | |
* Aborts the drop | |
*/ | |
protected void abortDrop() { | |
fDropping= false; | |
fFramesToDrop= 0; | |
} | |
/** | |
* Aborts the current step | |
*/ | |
protected void abortStep() { | |
fDestinationFrame = null; | |
EventRequestManager erm= getEventRequestManager(); | |
try { | |
if (fStepRequest != null) | |
erm.deleteEventRequest(fStepRequest); | |
fStepRequest = null; | |
} catch (VMDisconnectedException e) { | |
} catch (RuntimeException e) { | |
internalError(e); | |
} | |
} | |
/** | |
* @see IVariableLookup | |
*/ | |
public IVariable findVariable(String varName) throws DebugException { | |
if (isSuspended()) { | |
Iterator stackframes= getChildren0().iterator(); | |
while (stackframes.hasNext()) { | |
JDIStackFrame sf= (JDIStackFrame) stackframes.next(); | |
IVariable var= sf.findVariable(varName); | |
if (var != null) { | |
return var; | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* Evaluates the snippet using this thread (no stack frame context) | |
* | |
* @see IJavaThread | |
*/ | |
public void evaluate(String snippet, IJavaEvaluationListener listener, IJavaProject project) throws DebugException { | |
IEvaluationContext underlyingContext = ((JDIDebugTarget)getDebugTarget()).getEvaluationContext(project); | |
evaluate(snippet, listener, underlyingContext); | |
} | |
/** | |
* | |
* @see IJavaThread | |
*/ | |
public void evaluate(String snippet, IJavaEvaluationListener listener, IEvaluationContext evaluationContext) throws DebugException { | |
verifyEvaluation(evaluationContext); | |
ThreadEvaluationContext context = new ThreadEvaluationContext(this, evaluationContext); | |
context.evaluate(snippet, listener); | |
} | |
protected void verifyEvaluation(IEvaluationContext evaluationContext) throws DebugException { | |
if (fInEvaluation) { | |
requestFailed(IN_EVALUATION, null); | |
} | |
if (!evaluationContext.getProject().hasBuildState()) { | |
requestFailed(NO_BUILT_STATE, null); | |
} | |
if (!fEventSuspend) { | |
requestFailed(INVALID_EVALUATION_LOCATION, null); | |
} | |
} | |
/** | |
* @see IJavaEvaluate | |
*/ | |
public boolean canPerformEvaluation() { | |
return isSuspended() && !fInEvaluation && fEventSuspend; | |
} | |
protected void dispose() { | |
if (fTimer != null) { | |
fTimer.dispose(); | |
} | |
} | |
/** | |
* Notification this thread has terminated - update state | |
*/ | |
protected void terminated() { | |
fTerminated= true; | |
fRunning= false; | |
dispose(); | |
fireTerminateEvent(); | |
} | |
/** | |
* Returns this thread's underlying thread reference | |
*/ | |
protected ThreadReference getUnderlyingThread() { | |
return fThread; | |
} | |
/** | |
* Returns this thread's underlying thread group | |
*/ | |
protected ThreadGroupReference getUnderlyingThreadGroup() throws DebugException { | |
if (fThreadGroup == null) { | |
try { | |
fThreadGroup = getUnderlyingThread().threadGroup(); | |
} catch (VMDisconnectedException e) { | |
return null; | |
} catch (UnsupportedOperationException e) { | |
return null; | |
} catch (RuntimeException e) { | |
targetRequestFailed(ERROR_GET_THREAD_GROUP, e); | |
} | |
} | |
return fThreadGroup; | |
} | |
/** | |
* Starts the step timer. | |
*/ | |
protected void startStepTimer() { | |
if (fTimer == null) { | |
fTimer = new Timer(); | |
} | |
fTimer.start(this, 3000); | |
} | |
/** | |
* Stops the step timer. | |
*/ | |
protected void stopStepTimer() { | |
if (fTimer != null) { | |
fTimer.stop(); | |
} | |
} | |
} | |