| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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; |
| } |
| } |