| /******************************************************************************* |
| * Copyright (c) 2000, 2017 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 |
| * Samrat Dhillon samrat.dhillon@gmail.com - Bug 384458 - debug shows value of variable in another scope |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.debug.core.model; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.model.IRegisterGroup; |
| import org.eclipse.debug.core.model.IStackFrame; |
| import org.eclipse.debug.core.model.IStep; |
| import org.eclipse.debug.core.model.ISuspendResume; |
| import org.eclipse.debug.core.model.ITerminate; |
| import org.eclipse.debug.core.model.IThread; |
| import org.eclipse.debug.core.model.IVariable; |
| import org.eclipse.jdi.internal.ValueImpl; |
| import org.eclipse.jdi.internal.VirtualMachineImpl; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.debug.core.IJavaClassType; |
| import org.eclipse.jdt.debug.core.IJavaModifiers; |
| import org.eclipse.jdt.debug.core.IJavaObject; |
| import org.eclipse.jdt.debug.core.IJavaReferenceType; |
| import org.eclipse.jdt.debug.core.IJavaStackFrame; |
| import org.eclipse.jdt.debug.core.IJavaThread; |
| import org.eclipse.jdt.debug.core.IJavaValue; |
| import org.eclipse.jdt.debug.core.IJavaVariable; |
| import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin; |
| import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIReturnValueVariable; |
| import org.eclipse.jdt.internal.debug.core.model.MethodResult.ResultType; |
| |
| import com.ibm.icu.text.MessageFormat; |
| import com.sun.jdi.AbsentInformationException; |
| import com.sun.jdi.ClassNotLoadedException; |
| import com.sun.jdi.ClassType; |
| import com.sun.jdi.Field; |
| import com.sun.jdi.IncompatibleThreadStateException; |
| import com.sun.jdi.InvalidTypeException; |
| import com.sun.jdi.LocalVariable; |
| import com.sun.jdi.Location; |
| import com.sun.jdi.Method; |
| import com.sun.jdi.NativeMethodException; |
| import com.sun.jdi.ObjectReference; |
| import com.sun.jdi.ReferenceType; |
| import com.sun.jdi.StackFrame; |
| import com.sun.jdi.Type; |
| import com.sun.jdi.VirtualMachine; |
| |
| /** |
| * Proxy to a stack frame on the target. |
| */ |
| |
| public class JDIStackFrame extends JDIDebugElement implements IJavaStackFrame { |
| |
| /** |
| * This frame's depth in the call stack (0 == bottom of stack). A new frame |
| * is indicated by -2. An invalid frame is indicated by -1. |
| */ |
| private int fDepth = -2; |
| |
| /** |
| * Underlying JDI stack frame. |
| */ |
| private StackFrame fStackFrame; |
| |
| /** |
| * Containing thread. |
| */ |
| private JDIThread fThread; |
| /** |
| * Visible variables. |
| */ |
| private List<IJavaVariable> fVariables; |
| |
| /** |
| * The underlying Object associated with this stack frame. Cached lazily on |
| * first access. |
| */ |
| private ObjectReference fThisObject; |
| |
| /** |
| * The name of the type of the object that received the method call |
| * associated with this stack frame. Cached lazily on first access. |
| */ |
| private String fReceivingTypeName; |
| /** |
| * Whether the variables need refreshing |
| */ |
| private boolean fRefreshVariables = true; |
| /** |
| * Whether this stack frame has been marked as out of synch. If set to |
| * <code>true</code> this stack frame will stop dynamically calculating its |
| * out of synch state. |
| */ |
| private boolean fIsOutOfSynch = false; |
| |
| /** |
| * Whether local variable information was available |
| */ |
| private boolean fLocalsAvailable = true; |
| |
| /** |
| * Location of this stack frame |
| */ |
| private Location fLocation; |
| |
| /** |
| * Whether the current stack frame is the top of the stack |
| */ |
| private boolean fIsTop; |
| |
| /** |
| * Creates a new stack frame in the given thread. |
| * |
| * @param thread |
| * The parent JDI thread |
| * @param frame |
| * underlying frame |
| * @param depth |
| * depth on the stack (0 is bottom) |
| */ |
| public JDIStackFrame(JDIThread thread, StackFrame frame, int depth) { |
| super((JDIDebugTarget) thread.getDebugTarget()); |
| setThread(thread); |
| bind(frame, depth); |
| } |
| |
| /** |
| * Binds this frame to the given underlying frame on the target VM or |
| * returns a new frame representing the given frame. A frame can only be |
| * re-bound to an underlying frame if it refers to the same depth on the |
| * stack in the same method. |
| * |
| * @param frame |
| * underlying frame, or <code>null</code> |
| * @param depth |
| * depth in the call stack, or -1 to indicate the frame should |
| * become invalid |
| * @param return a frame to refer to the given frame or <code>null</code> |
| */ |
| protected JDIStackFrame bind(StackFrame frame, int depth) { |
| synchronized (fThread) { |
| if (fDepth == -2) { |
| // first initialization |
| fStackFrame = frame; |
| fDepth = depth; |
| fLocation = frame.location(); |
| return this; |
| } else if (depth == -1) { |
| // mark as invalid |
| fDepth = -1; |
| fStackFrame = null; |
| fIsTop = false; |
| return null; |
| } else if (fDepth == depth) { |
| Location location = frame.location(); |
| Method method = location.method(); |
| if (method.equals(fLocation.method())) { |
| try { |
| if (method.declaringType().defaultStratum() |
| .equals("Java") || //$NON-NLS-1$ |
| equals(getSourceName(location), |
| getSourceName(fLocation))) { |
| // TODO: what about receiving type being the same? |
| fStackFrame = frame; |
| fLocation = location; |
| clearCachedData(); |
| return this; |
| } |
| } catch (DebugException e) { |
| } |
| } |
| } |
| // invalidate this frame |
| bind(null, -1); |
| // return a new frame |
| return new JDIStackFrame(fThread, frame, depth); |
| } |
| |
| } |
| |
| /** |
| * @see IStackFrame#getThread() |
| */ |
| @Override |
| public IThread getThread() { |
| return fThread; |
| } |
| |
| /** |
| * @see ISuspendResume#canResume() |
| */ |
| @Override |
| public boolean canResume() { |
| return getThread().canResume(); |
| } |
| |
| /** |
| * @see ISuspendResume#canSuspend() |
| */ |
| @Override |
| public boolean canSuspend() { |
| return getThread().canSuspend(); |
| } |
| |
| /** |
| * @see IStep#canStepInto() |
| */ |
| @Override |
| public boolean canStepInto() { |
| try { |
| return exists() && isTopStackFrame() && !isObsolete() |
| && getThread().canStepInto(); |
| } catch (DebugException e) { |
| logError(e); |
| return false; |
| } |
| } |
| |
| /** |
| * @see IStep#canStepOver() |
| */ |
| @Override |
| public boolean canStepOver() { |
| return exists() && !isObsolete() && getThread().canStepOver(); |
| } |
| |
| /** |
| * @see IStep#canStepReturn() |
| */ |
| @Override |
| public boolean canStepReturn() { |
| try { |
| if (!exists() || isObsolete() || !getThread().canStepReturn()) { |
| return false; |
| } |
| List<IJavaStackFrame> frames = ((JDIThread) getThread()) |
| .computeStackFrames(); |
| if (frames != null && !frames.isEmpty()) { |
| boolean bottomFrame = this |
| .equals(frames.get(frames.size() - 1)); |
| boolean aboveObsoleteFrame = false; |
| if (!bottomFrame) { |
| int index = frames.indexOf(this); |
| if (index < frames.size() - 1 |
| && ((JDIStackFrame) frames.get(index + 1)) |
| .isObsolete()) { |
| aboveObsoleteFrame = true; |
| } |
| } |
| return !bottomFrame && !aboveObsoleteFrame; |
| } |
| } catch (DebugException e) { |
| logError(e); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the underlying method associated with this stack frame, |
| * retrieving the method is necessary. |
| */ |
| public Method getUnderlyingMethod() { |
| synchronized (fThread) { |
| return fLocation.method(); |
| } |
| } |
| |
| /** |
| * @see IStackFrame#getVariables() |
| */ |
| @Override |
| public IVariable[] getVariables() throws DebugException { |
| List<IJavaVariable> list = getVariables0(); |
| return list.toArray(new IVariable[list.size()]); |
| } |
| |
| protected List<IJavaVariable> getVariables0() throws DebugException { |
| synchronized (fThread) { |
| if (fVariables == null) { |
| |
| // throw exception if native method, so variable view will |
| // update |
| // with information message |
| if (isNative()) { |
| requestFailed( |
| JDIDebugModelMessages.JDIStackFrame_Variable_information_unavailable_for_native_methods, |
| null); |
| } |
| |
| Method method = getUnderlyingMethod(); |
| fVariables = new ArrayList<>(); |
| // #isStatic() does not claim to throw any exceptions - so it is |
| // not try/catch coded |
| if (method.isStatic()) { |
| // add statics |
| List<Field> allFields = null; |
| ReferenceType declaringType = method.declaringType(); |
| try { |
| allFields = declaringType.allFields(); |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_fields, |
| e.toString()), e); |
| // execution will not reach this line, as |
| // #targetRequestFailed will throw an exception |
| return Collections.EMPTY_LIST; |
| } |
| if (allFields != null) { |
| Iterator<Field> fields = allFields.iterator(); |
| while (fields.hasNext()) { |
| Field field = fields.next(); |
| if (field.isStatic()) { |
| fVariables.add(new JDIFieldVariable( |
| (JDIDebugTarget) getDebugTarget(), |
| field, declaringType)); |
| } |
| } |
| Collections.sort(fVariables, |
| new Comparator<IJavaVariable>() { |
| @Override |
| public int compare(IJavaVariable a, IJavaVariable b) { |
| JDIFieldVariable v1 = (JDIFieldVariable) a; |
| JDIFieldVariable v2 = (JDIFieldVariable) b; |
| try { |
| return v1.getName() |
| .compareToIgnoreCase( |
| v2.getName()); |
| } catch (DebugException de) { |
| logError(de); |
| return -1; |
| } |
| } |
| }); |
| } |
| } else { |
| // add "this" |
| ObjectReference t = getUnderlyingThisObject(); |
| if (t != null) { |
| fVariables.add(new JDIThisVariable( |
| (JDIDebugTarget) getDebugTarget(), t)); |
| } |
| } |
| addStepReturnValue(fVariables); |
| // add locals |
| Iterator<LocalVariable> variables = getUnderlyingVisibleVariables() |
| .iterator(); |
| while (variables.hasNext()) { |
| LocalVariable var = variables.next(); |
| fVariables.add(new JDILocalVariable(this, var)); |
| } |
| } else if (fRefreshVariables) { |
| updateVariables(); |
| } |
| fRefreshVariables = false; |
| return fVariables; |
| } |
| } |
| |
| /** |
| * If there is a return value from a "step return" that belongs to this frame, insert it as first element |
| * |
| * @param variables |
| */ |
| private void addStepReturnValue(List<IJavaVariable> variables) { |
| if (fIsTop) { |
| MethodResult methodResult = fThread.getMethodResult(); |
| if (methodResult != null) { |
| if (methodResult.fResultType == ResultType.returned) { |
| if (fDepth + 1 != methodResult.fTargetFrameCount) { |
| // can happen e.g., because of checkPackageAccess/System.getSecurityManager() |
| return; |
| } |
| String name = MessageFormat.format(JDIDebugModelMessages.JDIStackFrame_ReturnValue, methodResult.fMethod.name()); |
| variables.add(0, new JDIReturnValueVariable(name, JDIValue.createValue(getJavaDebugTarget(), methodResult.fValue), true)); |
| } else if (methodResult.fResultType == ResultType.returning) { |
| String name = MessageFormat.format(JDIDebugModelMessages.JDIStackFrame_ReturningValue, methodResult.fMethod.name()); |
| variables.add(0, new JDIReturnValueVariable(name, JDIValue.createValue(getJavaDebugTarget(), methodResult.fValue), true)); |
| } else if (methodResult.fResultType == ResultType.threw) { |
| if (fDepth + 1 > methodResult.fTargetFrameCount) { |
| // don't know if this really can happen, but other jvm suprises were not expected either |
| return; |
| } |
| String name = MessageFormat.format(JDIDebugModelMessages.JDIStackFrame_ExceptionThrown, methodResult.fMethod.name()); |
| variables.add(0, new JDIReturnValueVariable(name, JDIValue.createValue(getJavaDebugTarget(), methodResult.fValue), true)); |
| } else if (methodResult.fResultType == ResultType.throwing) { |
| String name = MessageFormat.format(JDIDebugModelMessages.JDIStackFrame_ThrowingException, methodResult.fMethod.name()); |
| variables.add(0, new JDIReturnValueVariable(name, JDIValue.createValue(getJavaDebugTarget(), methodResult.fValue), true)); |
| } |
| } else if(JDIThread.showStepResultIsEnabled()) { |
| variables.add(0, new JDIReturnValueVariable(JDIDebugModelMessages.JDIStackFrame_NoMethodReturnValue, new JDIPlaceholderValue(getJavaDebugTarget(), ""), false)); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * @see IStackFrame#getName() |
| */ |
| @Override |
| public String getName() throws DebugException { |
| return getMethodName(); |
| } |
| |
| /** |
| * @see IJavaStackFrame#getArgumentTypeNames() |
| */ |
| @Override |
| public List<String> getArgumentTypeNames() throws DebugException { |
| try { |
| Method underlyingMethod = getUnderlyingMethod(); |
| String genericSignature = underlyingMethod.genericSignature(); |
| if (genericSignature == null) { |
| // no generic signature |
| return underlyingMethod.argumentTypeNames(); |
| } |
| // generic signature |
| String[] parameterTypes = Signature |
| .getParameterTypes(genericSignature); |
| List<String> argumentTypeNames = new ArrayList<>(); |
| for (String parameterType : parameterTypes) { |
| argumentTypeNames.add(Signature.toString(parameterType) |
| .replace('/', '.')); |
| } |
| return argumentTypeNames; |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_argument_type_names, |
| e.toString()), e); |
| // execution will never reach this line, as |
| // #targetRequestFailed will throw an exception |
| return null; |
| } |
| } |
| |
| /** |
| * @see IStackFrame#getLineNumber() |
| */ |
| @Override |
| public int getLineNumber() throws DebugException { |
| synchronized (fThread) { |
| try { |
| return fLocation.lineNumber(); |
| } catch (RuntimeException e) { |
| if (getThread().isSuspended()) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_line_number, |
| e.toString()), e); |
| } |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * @see IStep#isStepping() |
| */ |
| @Override |
| public boolean isStepping() { |
| return getThread().isStepping(); |
| } |
| |
| /** |
| * @see ISuspendResume#isSuspended() |
| */ |
| @Override |
| public boolean isSuspended() { |
| return getThread().isSuspended(); |
| } |
| |
| /** |
| * @see ISuspendResume#resume() |
| */ |
| @Override |
| public void resume() throws DebugException { |
| getThread().resume(); |
| } |
| |
| /** |
| * @see IStep#stepInto() |
| */ |
| @Override |
| public void stepInto() throws DebugException { |
| if (!canStepInto()) { |
| return; |
| } |
| getThread().stepInto(); |
| } |
| |
| /** |
| * @see IStep#stepOver() |
| */ |
| @Override |
| public void stepOver() throws DebugException { |
| if (!canStepOver()) { |
| return; |
| } |
| if (isTopStackFrame()) { |
| getThread().stepOver(); |
| } else { |
| ((JDIThread) getThread()).stepToFrame(this); |
| } |
| } |
| |
| /** |
| * @see IStep#stepReturn() |
| */ |
| @Override |
| public void stepReturn() throws DebugException { |
| if (!canStepReturn()) { |
| return; |
| } |
| if (isTopStackFrame()) { |
| getThread().stepReturn(); |
| } else { |
| List<IJavaStackFrame> frames = ((JDIThread) getThread()) |
| .computeStackFrames(); |
| int index = frames.indexOf(this); |
| if (index >= 0 && index < frames.size() - 1) { |
| IStackFrame nextFrame = frames.get(index + 1); |
| ((JDIThread) getThread()).stepToFrame(nextFrame); |
| } |
| } |
| } |
| |
| /** |
| * @see ISuspendResume#suspend() |
| */ |
| @Override |
| public void suspend() throws DebugException { |
| getThread().suspend(); |
| } |
| |
| /** |
| * Incrementally updates this stack frames variables. |
| * |
| * @see JDIDebugElement#targetRequestFailed(String, RuntimeException) |
| */ |
| protected void updateVariables() throws DebugException { |
| if (fVariables == null) { |
| return; |
| } |
| |
| // remove old return value first, so the "this" updating logic below works |
| if (!fVariables.isEmpty() && fVariables.get(0) instanceof JDIReturnValueVariable) { |
| fVariables.remove(0); |
| } |
| |
| Method method = getUnderlyingMethod(); |
| int index = 0; |
| if (!method.isStatic()) { |
| // update "this" |
| ObjectReference thisObject; |
| thisObject = getUnderlyingThisObject(); |
| JDIThisVariable oldThisObject = null; |
| if (!fVariables.isEmpty() |
| && fVariables.get(0) instanceof JDIThisVariable) { |
| oldThisObject = (JDIThisVariable) fVariables.get(0); |
| } |
| if (thisObject == null && oldThisObject != null) { |
| // removal of 'this' |
| fVariables.remove(0); |
| index = 0; |
| } else { |
| if (oldThisObject == null && thisObject != null) { |
| // creation of 'this' |
| oldThisObject = new JDIThisVariable( |
| (JDIDebugTarget) getDebugTarget(), thisObject); |
| fVariables.add(0, oldThisObject); |
| index = 1; |
| } else { |
| if (oldThisObject != null) { |
| // 'this' still exists, replace with new 'this' if a |
| // different receiver |
| if (!oldThisObject.retrieveValue().equals(thisObject)) { |
| fVariables.remove(0); |
| fVariables.add(0, new JDIThisVariable( |
| (JDIDebugTarget) getDebugTarget(), |
| thisObject)); |
| } |
| index = 1; |
| } |
| } |
| } |
| } |
| |
| List<LocalVariable> locals = null; |
| try { |
| locals = getUnderlyingStackFrame().visibleVariables(); |
| } catch (AbsentInformationException e) { |
| locals = Collections.EMPTY_LIST; |
| } catch (NativeMethodException e) { |
| locals = Collections.EMPTY_LIST; |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_visible_variables, |
| e.toString()), e); |
| // execution will not reach this line, as |
| // #targetRequestFailed will throw an exception |
| return; |
| } |
| int localIndex = -1; |
| while (index < fVariables.size()) { |
| Object var = fVariables.get(index); |
| if (var instanceof JDILocalVariable) { |
| JDILocalVariable local = (JDILocalVariable) fVariables |
| .get(index); |
| localIndex = locals.indexOf(local.getLocal()); |
| if (localIndex >= 0) { |
| // update variable with new underling JDI LocalVariable |
| local.setLocal(locals.get(localIndex)); |
| locals.remove(localIndex); |
| index++; |
| } else { |
| // remove variable |
| fVariables.remove(index); |
| } |
| } else { |
| // field variable of a static frame |
| index++; |
| } |
| } |
| |
| // add any new locals |
| Iterator<LocalVariable> newOnes = locals.iterator(); |
| while (newOnes.hasNext()) { |
| JDILocalVariable local = new JDILocalVariable(this, newOnes.next()); |
| fVariables.add(local); |
| } |
| |
| addStepReturnValue(fVariables); |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.IDropToFrame#canDropToFrame() |
| */ |
| @Override |
| public boolean canDropToFrame() { |
| return supportsDropToFrame(); |
| } |
| |
| /** |
| * @see IJavaStackFrame#supportsDropToFrame() |
| */ |
| @Override |
| public boolean supportsDropToFrame() { |
| JDIThread thread = (JDIThread) getThread(); |
| JDIDebugTarget target = (JDIDebugTarget) thread.getDebugTarget(); |
| try { |
| if (!target.isAvailable() || !thread.isSuspended() |
| || thread.isTerminated() || thread.isInvokingMethod()) { |
| return false; |
| } |
| boolean j9Support = false; |
| boolean jdkSupport = target.canPopFrames(); |
| VirtualMachine vm = getVM(); |
| if (vm == null) { |
| return false; |
| } |
| try { |
| j9Support = (thread.getUnderlyingThread() instanceof org.eclipse.jdi.hcr.ThreadReference) |
| && ((org.eclipse.jdi.hcr.VirtualMachine) vm) |
| .canDoReturn(); |
| } catch (UnsupportedOperationException uoe) { |
| j9Support = false; |
| } |
| |
| if (jdkSupport || j9Support) { |
| // Also ensure that this frame and no frames above this |
| // frame are native. Unable to pop native stack frames. |
| List<IJavaStackFrame> frames = thread.computeStackFrames(); |
| if (jdkSupport) { |
| // JDK 1.4 VMs are currently unable to pop the bottom |
| // stack frame. |
| if ((frames.size() > 0) |
| && frames.get(frames.size() - 1) == this) { |
| return false; |
| } |
| } |
| int index = 0; |
| JDIStackFrame frame = null; |
| while (index < frames.size()) { |
| frame = (JDIStackFrame) frames.get(index); |
| index++; |
| if (frame.isNative()) { |
| return false; |
| } |
| if (frame.equals(this)) { |
| if (jdkSupport) { |
| // JDK 1.4 VMs are currently unable to pop the |
| // frame directly above a native frame |
| if (index < frames.size() |
| && ((JDIStackFrame) frames.get(index)) |
| .isNative()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| } |
| return false; |
| } catch (DebugException e) { |
| if (e.getStatus().getException() instanceof IncompatibleThreadStateException |
| || e.getStatus().getCode() == IJavaThread.ERR_THREAD_NOT_SUSPENDED) { |
| // if the thread has since resumed, drop is not supported |
| return false; |
| } |
| logError(e); |
| } catch (UnsupportedOperationException e) { |
| // drop to frame not supported - this is an expected |
| // exception for VMs that do not support drop to frame |
| return false; |
| } catch (RuntimeException e) { |
| internalError(e); |
| } |
| return false; |
| } |
| |
| /** |
| * @see IJavaStackFrame#dropToFrame() |
| */ |
| @Override |
| public void dropToFrame() throws DebugException { |
| if (supportsDropToFrame()) { |
| ((JDIThread) getThread()).dropToFrame(this); |
| } else { |
| notSupported(JDIDebugModelMessages.JDIStackFrame_Drop_to_frame_not_supported); |
| } |
| } |
| |
| public void popFrame() throws DebugException { |
| if (supportsDropToFrame()) { |
| ((JDIThread) getThread()).popFrame(this); |
| } else { |
| notSupported(JDIDebugModelMessages.JDIStackFrame_pop_frame_not_supported); |
| } |
| } |
| |
| /** |
| * @see IJavaStackFrame#findVariable(String) |
| */ |
| @Override |
| public IJavaVariable findVariable(String varName) throws DebugException { |
| if (isNative()) { |
| return null; |
| } |
| IVariable[] variables = getVariables(); |
| List<IJavaVariable> possibleMatches = new ArrayList<>(); |
| IJavaVariable thisVariable = null; |
| for (IVariable variable : variables) { |
| IJavaVariable var = (IJavaVariable) variable; |
| if (var.getName().equals(varName)) { |
| possibleMatches.add(var); |
| } |
| if (var instanceof JDIThisVariable) { |
| // save for later - check for instance and static variables |
| thisVariable = var; |
| } |
| } |
| for(IJavaVariable variable: possibleMatches){ |
| // Local Variable has more preference than Field Variable |
| if(variable instanceof JDILocalVariable){ |
| return variable; |
| } |
| } |
| if(possibleMatches.size() > 0) { |
| return possibleMatches.get(0); |
| } |
| |
| if (thisVariable != null) { |
| IVariable[] thisChildren = thisVariable.getValue().getVariables(); |
| for (IVariable element : thisChildren) { |
| IJavaVariable var = (IJavaVariable) element; |
| if (var.getName().equals(varName)) { |
| return var; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Retrieves visible variables in this stack frame handling any exceptions. |
| * Returns an empty list if there are no variables. |
| * |
| * @see JDIDebugElement#targetRequestFailed(String, RuntimeException) |
| */ |
| protected List<LocalVariable> getUnderlyingVisibleVariables() throws DebugException { |
| synchronized (fThread) { |
| List<LocalVariable> variables = Collections.EMPTY_LIST; |
| try { |
| variables = getUnderlyingStackFrame().visibleVariables(); |
| } catch (AbsentInformationException e) { |
| setLocalsAvailable(false); |
| } catch (NativeMethodException e) { |
| setLocalsAvailable(false); |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_visible_variables_2, |
| e.toString()), e); |
| } |
| return variables; |
| } |
| } |
| |
| /** |
| * Retrieves 'this' from the underlying stack frame. Returns |
| * <code>null</code> for static stack frames. |
| * |
| * @see JDIDebugElement#targetRequestFailed(String, RuntimeException) |
| */ |
| protected ObjectReference getUnderlyingThisObject() throws DebugException { |
| synchronized (fThread) { |
| if ((fStackFrame == null || fThisObject == null) && !isStatic()) { |
| try { |
| fThisObject = getUnderlyingStackFrame().thisObject(); |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_this, |
| e.toString()), e); |
| // execution will not reach this line, as |
| // #targetRequestFailed will throw an exception |
| return null; |
| } |
| } |
| return fThisObject; |
| } |
| } |
| |
| /** |
| * @see IAdaptable#getAdapter(Class) |
| */ |
| @SuppressWarnings("unchecked") |
| @Override |
| public <T> T getAdapter(Class<T> adapter) { |
| if (adapter == IJavaStackFrame.class || adapter == IJavaModifiers.class) { |
| return (T) this; |
| } |
| return super.getAdapter(adapter); |
| } |
| |
| /** |
| * @see IJavaStackFrame#getSignature() |
| */ |
| @Override |
| public String getSignature() throws DebugException { |
| try { |
| return getUnderlyingMethod().signature(); |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_method_signature, |
| e.toString()), e); |
| // execution will not reach this line, as |
| // #targetRequestFailed will throw an exception |
| return null; |
| } |
| } |
| |
| /** |
| * @see IJavaStackFrame#getDeclaringTypeName() |
| */ |
| @Override |
| public String getDeclaringTypeName() throws DebugException { |
| synchronized (fThread) { |
| try { |
| if (isObsolete()) { |
| return JDIDebugModelMessages.JDIStackFrame__unknown_declaring_type__1; |
| } |
| return JDIReferenceType.getGenericName(getUnderlyingMethod() |
| .declaringType()); |
| } catch (RuntimeException e) { |
| if (getThread().isSuspended()) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_declaring_type, |
| e.toString()), e); |
| } |
| return JDIDebugModelMessages.JDIStackFrame__unknown_declaring_type__1; |
| } |
| } |
| } |
| |
| /** |
| * @see IJavaStackFrame#getReceivingTypeName() |
| */ |
| @Override |
| public String getReceivingTypeName() throws DebugException { |
| if (fStackFrame == null || fReceivingTypeName == null) { |
| try { |
| if (isObsolete()) { |
| fReceivingTypeName = JDIDebugModelMessages.JDIStackFrame__unknown_receiving_type__2; |
| } else { |
| ObjectReference thisObject = getUnderlyingThisObject(); |
| if (thisObject == null) { |
| fReceivingTypeName = getDeclaringTypeName(); |
| } else { |
| fReceivingTypeName = JDIReferenceType |
| .getGenericName(thisObject.referenceType()); |
| } |
| } |
| } catch (RuntimeException e) { |
| if (getThread().isSuspended()) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_receiving_type, |
| e.toString()), e); |
| } |
| return JDIDebugModelMessages.JDIStackFrame__unknown_receiving_type__2; |
| } |
| } |
| return fReceivingTypeName; |
| } |
| |
| /** |
| * @see IJavaStackFrame#getMethodName() |
| */ |
| @Override |
| public String getMethodName() throws DebugException { |
| try { |
| return getUnderlyingMethod().name(); |
| } catch (RuntimeException e) { |
| if (getThread().isSuspended()) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_method_name, |
| e.toString()), e); |
| } |
| return JDIDebugModelMessages.JDIStackFrame__unknown_method__1; |
| } |
| } |
| |
| /** |
| * @see IJavaStackFrame#isNative() |
| */ |
| @Override |
| public boolean isNative() throws DebugException { |
| return getUnderlyingMethod().isNative(); |
| } |
| |
| /** |
| * @see IJavaStackFrame#isConstructor() |
| */ |
| @Override |
| public boolean isConstructor() throws DebugException { |
| return getUnderlyingMethod().isConstructor(); |
| } |
| |
| /** |
| * @see IJavaStackFrame#isStaticInitializer() |
| */ |
| @Override |
| public boolean isStaticInitializer() throws DebugException { |
| return getUnderlyingMethod().isStaticInitializer(); |
| } |
| |
| /** |
| * @see IJavaModifiers#isFinal() |
| */ |
| @Override |
| public boolean isFinal() throws DebugException { |
| return getUnderlyingMethod().isFinal(); |
| } |
| |
| /** |
| * @see IJavaStackFrame#isSynchronized() |
| */ |
| @Override |
| public boolean isSynchronized() throws DebugException { |
| return getUnderlyingMethod().isSynchronized(); |
| } |
| |
| /** |
| * @see IJavaModifiers#isSynthetic() |
| */ |
| @Override |
| public boolean isSynthetic() throws DebugException { |
| return getUnderlyingMethod().isSynthetic(); |
| } |
| |
| /** |
| * @see IJavaModifiers#isPublic() |
| */ |
| @Override |
| public boolean isPublic() throws DebugException { |
| return getUnderlyingMethod().isPublic(); |
| } |
| |
| /** |
| * @see IJavaModifiers#isPrivate() |
| */ |
| @Override |
| public boolean isPrivate() throws DebugException { |
| return getUnderlyingMethod().isPrivate(); |
| } |
| |
| /** |
| * @see IJavaModifiers#isProtected() |
| */ |
| @Override |
| public boolean isProtected() throws DebugException { |
| return getUnderlyingMethod().isProtected(); |
| } |
| |
| /** |
| * @see IJavaModifiers#isPackagePrivate() |
| */ |
| @Override |
| public boolean isPackagePrivate() throws DebugException { |
| return getUnderlyingMethod().isPackagePrivate(); |
| } |
| |
| /** |
| * @see IJavaModifiers#isStatic() |
| */ |
| @Override |
| public boolean isStatic() throws DebugException { |
| return getUnderlyingMethod().isStatic(); |
| } |
| |
| /** |
| * @see IJavaStackFrame#getSourceName() |
| */ |
| @Override |
| public String getSourceName() throws DebugException { |
| synchronized (fThread) { |
| return getSourceName(fLocation); |
| } |
| } |
| |
| /** |
| * Returns the source from the default stratum of the given location or |
| * <code>null</code> if not available (missing attribute). |
| */ |
| private String getSourceName(Location location) throws DebugException { |
| try { |
| return location.sourceName(); |
| } catch (AbsentInformationException e) { |
| return null; |
| } catch (NativeMethodException e) { |
| return null; |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_source_name, |
| e.toString()), e); |
| } |
| return null; |
| } |
| |
| private boolean equals(Object o1, Object o2) { |
| if (o1 == null) { |
| return o2 == null; |
| } |
| return o1.equals(o2); |
| } |
| |
| protected boolean isTopStackFrame() throws DebugException { |
| IStackFrame tos = getThread().getTopStackFrame(); |
| return tos != null && tos.equals(this); |
| } |
| |
| /** |
| * Sets this stack frame to be out of synch. Note that passing |
| * <code>true</code> to this method marks this stack frame as out of synch |
| * permanently (statically). |
| */ |
| public void setOutOfSynch(boolean outOfSynch) { |
| fIsOutOfSynch = outOfSynch; |
| } |
| |
| /** |
| * @see IJavaStackFrame#isOutOfSynch() |
| */ |
| @Override |
| public boolean isOutOfSynch() throws DebugException { |
| if (fIsOutOfSynch) { |
| return true; |
| } |
| // if this frame's thread is not suspended, the out-of-synch info cannot |
| // change until it suspends again |
| if (getThread().isSuspended()) { |
| JDIDebugTarget target = (JDIDebugTarget) getDebugTarget(); |
| if (target.hasHCROccurred() |
| && target.isOutOfSynch(getUnderlyingMethod() |
| .declaringType().name())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @see IJavaStackFrame#isObsolete() |
| */ |
| @Override |
| public boolean isObsolete() { |
| if (!JDIDebugPlugin.isJdiVersionGreaterThanOrEqual(new int[] { 1, 4 }) |
| || !((JDIDebugTarget) getDebugTarget()).hasHCROccurred()) { |
| // If no hot code replace has occurred, this frame |
| // cannot be obsolete. |
| return false; |
| } |
| // if this frame's thread is not suspended, the obsolete status cannot |
| // change until it suspends again |
| synchronized (fThread) { |
| if (getThread().isSuspended()) { |
| return getUnderlyingMethod().isObsolete(); |
| } |
| return false; |
| } |
| } |
| |
| protected boolean exists() { |
| synchronized (fThread) { |
| return fDepth != -1; |
| } |
| } |
| |
| /** |
| * @see ITerminate#canTerminate() |
| */ |
| @Override |
| public boolean canTerminate() { |
| return exists() && getThread().canTerminate() |
| || getDebugTarget().canTerminate(); |
| } |
| |
| /** |
| * @see ITerminate#isTerminated() |
| */ |
| @Override |
| public boolean isTerminated() { |
| return getThread().isTerminated(); |
| } |
| |
| /** |
| * @see ITerminate#terminate() |
| */ |
| @Override |
| public void terminate() throws DebugException { |
| if (getThread().canTerminate()) { |
| getThread().terminate(); |
| } else { |
| getDebugTarget().terminate(); |
| } |
| } |
| |
| /** |
| * Returns this stack frame's underlying JDI frame. |
| * |
| * @exception DebugException |
| * if this stack frame does not currently have an underlying |
| * frame (is in an interim state where this frame's thread |
| * has been resumed, and is not yet suspended). |
| */ |
| protected StackFrame getUnderlyingStackFrame() throws DebugException { |
| synchronized (fThread) { |
| if (fStackFrame == null) { |
| if (fDepth == -1) { |
| throw new DebugException(new Status(IStatus.ERROR, |
| JDIDebugPlugin.getUniqueIdentifier(), |
| IJavaStackFrame.ERR_INVALID_STACK_FRAME, |
| JDIDebugModelMessages.JDIStackFrame_25, null)); |
| } |
| if (fThread.isSuspended()) { |
| // re-index stack frames - See Bug 47198 |
| fThread.computeStackFrames(); |
| if (fDepth == -1) { |
| // If depth is -1, then this is an invalid frame |
| throw new DebugException(new Status(IStatus.ERROR, |
| JDIDebugPlugin.getUniqueIdentifier(), |
| IJavaStackFrame.ERR_INVALID_STACK_FRAME, |
| JDIDebugModelMessages.JDIStackFrame_25, null)); |
| } |
| } else { |
| throw new DebugException(new Status(IStatus.ERROR, |
| JDIDebugPlugin.getUniqueIdentifier(), |
| IJavaThread.ERR_THREAD_NOT_SUSPENDED, |
| JDIDebugModelMessages.JDIStackFrame_25, null)); |
| } |
| } |
| return fStackFrame; |
| } |
| } |
| |
| /** |
| * Sets the underlying JDI StackFrame. Called by a thread when incrementally |
| * updating after a step has completed. |
| * |
| * @param frame |
| * The underlying stack frame |
| */ |
| protected void setUnderlyingStackFrame(StackFrame frame) { |
| synchronized (fThread) { |
| fStackFrame = frame; |
| if (frame == null) { |
| fRefreshVariables = true; |
| } |
| } |
| } |
| |
| protected void setThread(JDIThread thread) { |
| fThread = thread; |
| } |
| |
| protected void setVariables(List<IJavaVariable> variables) { |
| fVariables = variables; |
| } |
| |
| /** |
| * @see IJavaStackFrame#getLocalVariables() |
| */ |
| @Override |
| public IJavaVariable[] getLocalVariables() throws DebugException { |
| List<LocalVariable> list = getUnderlyingVisibleVariables(); |
| IJavaVariable[] locals = new IJavaVariable[list.size()]; |
| for (int i = 0; i < list.size(); i++) { |
| locals[i] = new JDILocalVariable(this, list.get(i)); |
| } |
| return locals; |
| } |
| |
| /** |
| * @see IJavaStackFrame#getThis() |
| */ |
| @Override |
| public IJavaObject getThis() throws DebugException { |
| IJavaObject receiver = null; |
| if (!isStatic() && !isNative()) { |
| ObjectReference thisObject = getUnderlyingThisObject(); |
| if (thisObject != null) { |
| receiver = (IJavaObject) JDIValue.createValue( |
| (JDIDebugTarget) getDebugTarget(), thisObject); |
| } |
| } |
| return receiver; |
| } |
| |
| /** |
| * Java stack frames do not support registers |
| * |
| * @see IStackFrame#getRegisterGroups() |
| */ |
| @Override |
| public IRegisterGroup[] getRegisterGroups() { |
| return new IRegisterGroup[0]; |
| } |
| |
| /** |
| * @see IJavaStackFrame#getDeclaringType() |
| */ |
| @Override |
| public IJavaClassType getDeclaringType() throws DebugException { |
| Method method = getUnderlyingMethod(); |
| try { |
| Type type = method.declaringType(); |
| if (type instanceof ClassType) { |
| return (IJavaClassType) JDIType.createType( |
| (JDIDebugTarget) getDebugTarget(), type); |
| } |
| targetRequestFailed(JDIDebugModelMessages.JDIStackFrame_0, null); |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retreiving_declaring_type, |
| e.toString()), e); |
| } |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.debug.core.IJavaStackFrame#getReferenceType() |
| */ |
| @Override |
| public IJavaReferenceType getReferenceType() throws DebugException { |
| Method method = getUnderlyingMethod(); |
| try { |
| Type type = method.declaringType(); |
| return (IJavaReferenceType) JDIType.createType( |
| (JDIDebugTarget) getDebugTarget(), type); |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retreiving_declaring_type, |
| e.toString()), e); |
| } |
| return null; |
| } |
| |
| /** |
| * Expression level stepping not supported. |
| * |
| * @see IStackFrame#getCharEnd() |
| */ |
| @Override |
| public int getCharEnd() { |
| return -1; |
| } |
| |
| /** |
| * Expression level stepping not supported. |
| * |
| * @see IStackFrame#getCharStart() |
| */ |
| @Override |
| public int getCharStart() { |
| return -1; |
| } |
| |
| /** |
| * Clears the cached data of this stack frame. The underlying stack frame |
| * has changed in such a way that the cached data may not be valid. |
| */ |
| private void clearCachedData() { |
| fThisObject = null; |
| fReceivingTypeName = null; |
| } |
| |
| /** |
| * @see IJavaStackFrame#wereLocalsAvailable() |
| */ |
| @Override |
| public boolean wereLocalsAvailable() { |
| return fLocalsAvailable; |
| } |
| |
| /** |
| * Sets whether locals were available. If the setting is not the same as the |
| * current value, a change event is fired such that a UI client can update. |
| * |
| * @param available |
| * whether local variable information is available for this stack |
| * frame. |
| */ |
| private void setLocalsAvailable(boolean available) { |
| if (available != fLocalsAvailable) { |
| fLocalsAvailable = available; |
| fireChangeEvent(DebugEvent.STATE); |
| } |
| } |
| |
| /** |
| * @see IStackFrame#hasRegisterGroups() |
| */ |
| @Override |
| public boolean hasRegisterGroups() { |
| return false; |
| } |
| |
| /** |
| * @see IStackFrame#hasVariables() |
| */ |
| @Override |
| public boolean hasVariables() throws DebugException { |
| return getVariables0().size() > 0; |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.IFilteredStep#canStepWithFilters() |
| */ |
| @Override |
| public boolean canStepWithFilters() { |
| if (canStepInto()) { |
| String[] filters = getJavaDebugTarget().getStepFilters(); |
| return filters != null && filters.length > 0; |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.IFilteredStep#stepWithFilters() |
| */ |
| @Override |
| public void stepWithFilters() throws DebugException { |
| ((IJavaThread) getThread()).stepWithFilters(); |
| } |
| |
| /** |
| * @see org.eclipse.jdt.debug.core.IJavaStackFrame#getSourcePath(java.lang.String) |
| */ |
| @Override |
| public String getSourcePath(String stratum) throws DebugException { |
| synchronized (fThread) { |
| try { |
| return fLocation.sourcePath(stratum); |
| } catch (AbsentInformationException e) { |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_source_path, |
| e.toString()), e); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.debug.core.IJavaStackFrame#getSourcePath() |
| */ |
| @Override |
| public String getSourcePath() throws DebugException { |
| synchronized (fThread) { |
| try { |
| return fLocation.sourcePath(); |
| } catch (AbsentInformationException e) { |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_source_path, |
| e.toString()), e); |
| } |
| } |
| return null; |
| } |
| |
| /* |
| * @see |
| * org.eclipse.jdt.debug.core.IJavaStackFrame#getLineNumber(java.lang.String |
| * ) |
| */ |
| @Override |
| public int getLineNumber(String stratum) throws DebugException { |
| synchronized (fThread) { |
| try { |
| return fLocation.lineNumber(stratum); |
| } catch (RuntimeException e) { |
| if (getThread().isSuspended()) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_line_number, |
| e.toString()), e); |
| } |
| } |
| } |
| return -1; |
| } |
| |
| /* |
| * @see |
| * org.eclipse.jdt.debug.core.IJavaStackFrame#getSourceName(java.lang.String |
| * ) |
| */ |
| @Override |
| public String getSourceName(String stratum) throws DebugException { |
| synchronized (fThread) { |
| try { |
| return fLocation.sourceName(stratum); |
| } catch (AbsentInformationException e) { |
| } catch (NativeMethodException e) { |
| } catch (RuntimeException e) { |
| targetRequestFailed( |
| MessageFormat.format( |
| JDIDebugModelMessages.JDIStackFrame_exception_retrieving_source_name, |
| e.toString()), e); |
| } |
| } |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.debug.core.IJavaStackFrame#isVarargs() |
| */ |
| @Override |
| public boolean isVarArgs() throws DebugException { |
| return getUnderlyingMethod().isVarArgs(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.debug.core.IJavaStackFrame#canForceReturn() |
| */ |
| @Override |
| public boolean canForceReturn() { |
| if (getJavaDebugTarget().supportsForceReturn() && isSuspended()) { |
| try { |
| if (!isNative()) { |
| if (isTopStackFrame()) { |
| return true; |
| } |
| List<IJavaStackFrame> frames = fThread.computeStackFrames(); |
| int index = frames.indexOf(this); |
| if (index > 0) { |
| JDIStackFrame prev = (JDIStackFrame) frames |
| .get(index - 1); |
| return prev.canDropToFrame(); |
| } |
| } |
| } catch (DebugException e) { |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.jdt.debug.core.IJavaStackFrame#forceReturn(org.eclipse.jdt |
| * .debug.core.IJavaValue) |
| */ |
| @Override |
| public void forceReturn(IJavaValue value) throws DebugException { |
| if (isTopStackFrame()) { |
| fThread.forceReturn(value); |
| } else { |
| // first check assignment compatible |
| Method method = getUnderlyingMethod(); |
| try { |
| ValueImpl.checkValue(((JDIValue) value).getUnderlyingValue(), |
| method.returnType(), |
| (VirtualMachineImpl) method.virtualMachine()); |
| } catch (InvalidTypeException e) { |
| targetRequestFailed(JDIDebugModelMessages.JDIStackFrame_26, e); |
| } catch (ClassNotLoadedException e) { |
| targetRequestFailed(JDIDebugModelMessages.JDIThread_48, e); |
| } |
| List<IJavaStackFrame> frames = fThread.computeStackFrames(); |
| int index = frames.indexOf(this); |
| if (index > 0) { |
| JDIStackFrame prev = (JDIStackFrame) frames.get(index - 1); |
| fThread.popFrame(prev); |
| fThread.forceReturn(value); |
| } |
| } |
| } |
| |
| public void setIsTop(boolean isTop) { |
| this.fIsTop = isTop; |
| } |
| } |