blob: d94119c9efd8efb15c96939cd0d65bea13d176ed [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.debug.ui.actions;
import com.ibm.icu.text.MessageFormat;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventFilter;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jface.dialogs.MessageDialog;
/**
* Handles stepping into a selected method, for a specific thread.
*/
public class StepIntoSelectionHandler implements IDebugEventFilter {
/**
* The method to step into
*/
private IMethod fMethod;
/**
* Resolved signature of the method to step into
*/
private String fResolvedSignature;
/**
* The thread in which to step
*/
private IJavaThread fThread;
/**
* The initial stack frame
*/
private String fOriginalName;
private String fOriginalSignature;
private String fOriginalTypeName;
private int fOriginalStackDepth;
/**
* Whether this is the first step into.
*/
private boolean fFirstStep = true;
/**
* The state of step filters before the step.
*/
private boolean fStepFilterEnabledState;
/**
* Expected event kind
*/
private int fExpectedKind = -1;
/**
* Expected event detail
*/
private int fExpectedDetail = -1;
/**
* Constructs a step handler to step into the given method in the given thread
* starting from the given stack frame.
*/
public StepIntoSelectionHandler(IJavaThread thread, IJavaStackFrame frame, IMethod method) {
fMethod = method;
fThread = thread;
try {
fOriginalName = frame.getName();
fOriginalSignature = frame.getSignature();
fOriginalTypeName = frame.getDeclaringTypeName();
if (method.isBinary()) {
fResolvedSignature = method.getSignature();
} else {
fResolvedSignature = ToggleBreakpointAdapter.resolveMethodSignature(method.getDeclaringType(), method.getSignature());
}
} catch (CoreException e) {
JDIDebugUIPlugin.log(e);
}
}
/**
* Returns the target thread for the step.
*
* @return the target thread for the step
*/
protected IJavaThread getThread() {
return fThread;
}
protected IJavaDebugTarget getDebugTarget() {
return (IJavaDebugTarget)getThread().getDebugTarget();
}
/**
* Returns the method to step into
*
* @return the method to step into
*/
protected IMethod getMethod() {
return fMethod;
}
/**
* Returns the resolved signature of the method to step into
*
* @return the resolved signature of the method to step into
*/
protected String getSignature() {
return fResolvedSignature;
}
/**
* @see org.eclipse.debug.core.IDebugEventFilter#filterDebugEvents(org.eclipse.debug.core.DebugEvent)
*/
public DebugEvent[] filterDebugEvents(DebugEvent[] events) {
// we only expect one event from our thread - find the event
DebugEvent event = null;
int index = -1;
int threadEvents = 0;
for (int i = 0; i < events.length; i++) {
DebugEvent e = events[i];
if (isExpectedEvent(e)) {
event = e;
index = i;
threadEvents++;
} else if (e.getSource() == getThread()) {
threadEvents++;
}
}
if (event == null) {
// nothing to process in this event set
return events;
}
// create filtered event set
DebugEvent[] filtered = new DebugEvent[events.length - 1];
if (filtered.length > 0) {
int j = 0;
for (int i = 0; i < events.length; i++) {
if (i != index) {
filtered[j] = events[i];
j++;
}
}
}
// if more than one event in our thread, abort (filtering our event)
if (threadEvents > 1) {
cleanup();
return filtered;
}
// we have the one expected event - process it
switch (event.getKind()) {
case DebugEvent.RESUME:
// next, we expect a step end
setExpectedEvent(DebugEvent.SUSPEND, DebugEvent.STEP_END);
if (fFirstStep) {
fFirstStep = false;
return events; // include the first resume event
}
// secondary step - filter the event
return filtered;
case DebugEvent.SUSPEND:
// compare location to desired location
try {
final IJavaStackFrame frame = (IJavaStackFrame)getThread().getTopStackFrame();
int stackDepth = frame.getThread().getStackFrames().length;
String name = null;
if (frame.isConstructor()) {
name = frame.getDeclaringTypeName();
index = name.lastIndexOf('.');
if (index >= 0) {
name = name.substring(index + 1);
}
} else {
name = frame.getName();
}
if (name.equals(getMethod().getElementName()) && frame.getSignature().equals(getSignature())) {
// hit
cleanup();
return events;
}
// step again
Runnable r = null;
if (stackDepth > fOriginalStackDepth) {
if (frame.isSynthetic()) {
// step thru synthetic methods
r = new Runnable() {
public void run() {
try {
setExpectedEvent(DebugEvent.RESUME, DebugEvent.STEP_INTO);
frame.stepInto();
} catch (DebugException e) {
JDIDebugUIPlugin.log(e);
cleanup();
DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[]{new DebugEvent(getDebugTarget(), DebugEvent.CHANGE)});
}
}
};
} else {
r = new Runnable() {
public void run() {
try {
setExpectedEvent(DebugEvent.RESUME, DebugEvent.STEP_RETURN);
frame.stepReturn();
} catch (DebugException e) {
JDIDebugUIPlugin.log(e);
cleanup();
DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[]{new DebugEvent(getDebugTarget(), DebugEvent.CHANGE)});
}
}
};
}
} else if (stackDepth == fOriginalStackDepth){
// we should be back in the original stack frame - if not, abort
if (!(frame.getSignature().equals(fOriginalSignature) && frame.getName().equals(fOriginalName) && frame.getDeclaringTypeName().equals(fOriginalTypeName))) {
missed();
return events;
}
r = new Runnable() {
public void run() {
try {
setExpectedEvent(DebugEvent.RESUME, DebugEvent.STEP_INTO);
frame.stepInto();
} catch (DebugException e) {
JDIDebugUIPlugin.log(e);
cleanup();
DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[]{new DebugEvent(getDebugTarget(), DebugEvent.CHANGE)});
}
}
};
} else {
// we returned from the original frame - never hit the desired method
missed();
return events;
}
DebugPlugin.getDefault().asyncExec(r);
// filter the events
return filtered;
} catch (CoreException e) {
// abort
JDIDebugUIPlugin.log(e);
cleanup();
return events;
}
}
// execution should not reach here
return events;
}
/**
* Called when stepping returned from the original frame without entering the desired method.
*/
protected void missed() {
cleanup();
Runnable r = new Runnable() {
public void run() {
String methodName = null;
try {
methodName = Signature.toString(getMethod().getSignature(), getMethod().getElementName(), getMethod().getParameterNames(), false, false);
} catch (JavaModelException e) {
methodName = getMethod().getElementName();
}
new MessageDialog(JDIDebugUIPlugin.getActiveWorkbenchShell(), ActionMessages.StepIntoSelectionHandler_1, null, MessageFormat.format(ActionMessages.StepIntoSelectionHandler_Execution_did_not_enter____0____before_the_current_method_returned__1, new String[]{methodName}), MessageDialog.INFORMATION, new String[] {ActionMessages.StepIntoSelectionHandler_2}, 0).open();
}
};
JDIDebugUIPlugin.getStandardDisplay().asyncExec(r);
}
/**
* Performs the step.
*/
public void step() {
// add event filter and turn off step filters
DebugPlugin.getDefault().addDebugEventFilter(this);
fStepFilterEnabledState = getDebugTarget().isStepFiltersEnabled();
getDebugTarget().setStepFiltersEnabled(false);
try {
fOriginalStackDepth = getThread().getStackFrames().length;
setExpectedEvent(DebugEvent.RESUME, DebugEvent.STEP_INTO);
getThread().stepInto();
} catch (DebugException e) {
JDIDebugUIPlugin.log(e);
cleanup();
DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[]{new DebugEvent(getDebugTarget(), DebugEvent.CHANGE)});
}
}
/**
* Cleans up when the step is complete/aborted.
*/
protected void cleanup() {
DebugPlugin.getDefault().removeDebugEventFilter(this);
// restore step filter state
getDebugTarget().setStepFiltersEnabled(fStepFilterEnabledState);
}
/**
* Sets the expected debug event kind and detail we are waiting for next.
*
* @param kind event kind
* @param detail event detail
*/
private void setExpectedEvent(int kind, int detail) {
fExpectedKind = kind;
fExpectedDetail = detail;
}
/**
* Returns whether the given event is what we expected.
*
* @param event fire event
* @return whether the event is what we expected
*/
protected boolean isExpectedEvent(DebugEvent event) {
return event.getSource().equals(getThread()) &&
event.getKind() == fExpectedKind &&
event.getDetail() == fExpectedDetail;
}
}