blob: 713e425ce16407af7834896290393badb2eaf6b8 [file] [log] [blame]
package org.eclipse.jdt.internal.debug.core;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import com.sun.jdi.*;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.IDebugStatusConstants;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.eval.IEvaluationContext;
import org.eclipse.jdt.debug.core.*;
import java.util.*;
/**
* Proxy to a stack frame on the target.
*/
public class JDIStackFrame extends JDIDebugElement implements IJavaStackFrame {
// Resource String keys
private static final String PREFIX= "jdi_stack_frame.";
private static final String ERROR= PREFIX + "error.";
private static final String ERROR_GET_NAME= ERROR + "get_name";
private static final String ERROR_DROP_NOT_SUPPORTED= ERROR + "drop_not_supported";
private static final String ERROR_GET_ARGUMENTS= ERROR + "get_arguments";
private static final String ERROR_GET_DECLARING_TYPE= ERROR + "get_declaring_type";
private static final String ERROR_GET_RECEIVING_TYPE= ERROR + "get_receiving_type";
private static final String ERROR_GET_LINE_NUMBER= ERROR + "get_line_number";
private static final String ERROR_GET_SIGNATURE= ERROR + "get_signature";
private static final String ERROR_GET_METHOD= ERROR + "get_method";
private static final String ERROR_GET_SOURCE_NAME= ERROR + "get_source_name";
/**
* Underlying stack frame
*/
protected StackFrame fStackFrame;
/**
* The method this stack frame is associated with. Cached
* lazily on first access.
*/
protected Method fMethod= null;
/**
* Whether the variables need refreshing
*/
protected boolean fRefreshVariables= true;
/**
* Creates a new stack frame in the given thread.
*/
public JDIStackFrame(JDIThread thread, StackFrame stackFrame) {
super(thread);
fStackFrame= stackFrame;
}
/**
* @see IDebugElement
*/
public int getElementType() {
return STACK_FRAME;
}
/**
* @see IDebugElement
*/
public IStackFrame getStackFrame() {
return this;
}
/**
* @see IDebugElement
*/
public IThread getThread() {
return (IThread)fParent;
}
/**
* @see ISuspendResume
*/
public boolean canResume() {
return getThread().canResume();
}
/**
* @see ISuspendResume
*/
public boolean canSuspend() {
return getThread().canSuspend();
}
/**
* @see IStep.
*/
public boolean canStepInto() {
try {
return exists() && isTopStackFrame() && getThread().canStepInto();
} catch (DebugException e) {
return false;
}
}
/**
* @see IStep.
*/
public boolean canStepOver() {
try {
return exists() && getThread().canStepOver();
} catch (DebugException e) {
return false;
}
}
/**
* @see IStep.
*/
public boolean canStepReturn() {
try {
List frames = ((JDIThread)getThread()).getChildren0();
if (frames != null && !frames.isEmpty()) {
Object bottomFrame = frames.get(frames.size() - 1);
return exists() && !this.equals(bottomFrame) && getThread().canStepReturn();
}
} catch (DebugException e) {
}
return false;
}
/**
* Returns the underlying method associated with this stack frame.
*/
Method getUnderlyingMethod() throws DebugException {
if (fMethod == null) {
try {
fMethod= fStackFrame.location().method();
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_METHOD, e);
}
}
return fMethod;
}
/**
* @see IDebugElement
*/
protected List getChildren0() throws DebugException {
if (fChildren == null) {
Method method= getUnderlyingMethod();
fChildren= new ArrayList();
// #isStatic() does not claim to throw any exceptions - so it is not try/catch coded
if (method.isStatic()) {
// add statics
List allFields= null;
try {
allFields= method.declaringType().allFields();
} catch (VMDisconnectedException e) {
return Collections.EMPTY_LIST;
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_CHILDREN, e);
}
if (allFields != null) {
Iterator fields= allFields.iterator();
while (fields.hasNext()) {
Field field= (Field) fields.next();
if (field.isStatic()) {
fChildren.add(new JDIFieldVariable(this, field));
}
}
Collections.sort(fChildren, new Comparator() {
public int compare(Object a, Object b) {
return sortStaticChildren(a, b);
}
});
}
} else {
// add "this"
ObjectReference t= null;
try {
t= fStackFrame.thisObject();
} catch (VMDisconnectedException e) {
return Collections.EMPTY_LIST;
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_CHILDREN, e);
}
if (t != null) {
fChildren.add(new JDIThisVariable(this, t));
}
}
// add locals
Iterator variables= getUnderlyingVisibleVariables().iterator();
while (variables.hasNext()) {
LocalVariable var= (LocalVariable) variables.next();
fChildren.add(new JDILocalVariable(this, var));
}
} else if (fRefreshVariables) {
updateVariables();
}
fRefreshVariables = false;
return fChildren;
}
/**
* Sorts the static variable children lexically
*/
protected int sortStaticChildren(Object a, Object b) {
JDIFieldVariable v1= (JDIFieldVariable)a;
JDIFieldVariable v2= (JDIFieldVariable)b;
try {
return v1.getName().compareToIgnoreCase(v2.getName());
} catch (DebugException de) {
internalError(de);
return -1;
}
}
/**
* @see IDebugElement
*/
public String getName() throws DebugException {
return getMethodName();
}
/**
* @see IJavaStackFrame
*/
public List getArgumentTypeNames() throws DebugException {
try {
return getUnderlyingMethod().argumentTypeNames();
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_ARGUMENTS, e);
}
return Collections.EMPTY_LIST;
}
/**
* @see IStackFrame
*/
public int getLineNumber() throws DebugException {
if (getThread().isSuspended()) {
try {
Location location= fStackFrame.location();
if (location != null) {
return location.lineNumber();
}
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_LINE_NUMBER, e);
}
}
return -1;
}
/**
* @see IStep
*/
public boolean isStepping() {
return getThread().isStepping();
}
/**
* @see ISuspendResume
*/
public boolean isSuspended() {
return getThread().isSuspended();
}
/**
* @see ISuspendResume
*/
public void resume() throws DebugException {
getThread().resume();
}
/**
* @see IStep.
*/
public void stepInto() throws DebugException {
if (!canStepInto()) {
return;
}
getThread().stepInto();
}
/**
* @see IStep
*/
public void stepOver() throws DebugException {
if (!canStepOver()) {
return;
}
if (isTopStackFrame()) {
getThread().stepOver();
} else {
((JDIThread)getThread()).stepToFrame(this);
}
}
/**
* @see IStep
*/
public void stepReturn() throws DebugException {
if (!canStepReturn()) {
return;
}
if (isTopStackFrame()) {
getThread().stepReturn();
} else {
List frames = ((JDIThread)getThread()).getChildren0();
int index = frames.indexOf(this);
if (index >= 0 && index < frames.size() - 1) {
IStackFrame nextFrame = (IStackFrame)frames.get(index + 1);
((JDIThread)getThread()).stepToFrame(nextFrame);
}
}
}
/**
* @see ISuspendResume
*/
public void suspend() throws DebugException {
getThread().suspend();
}
/**
* Notes that variables will need to be updated on
* the next access.
*/
protected void invalidateVariables() {
fRefreshVariables = true;
}
/**
* Update my variables incrementally.
*/
protected void updateVariables() throws DebugException {
if (fChildren == null) {
return;
}
Method method= getUnderlyingMethod();
int index= 0;
if (method.isStatic()) {
// update statics
while (index < fChildren.size() && fChildren.get(index) instanceof JDIFieldVariable) {
index++;
}
} else {
// update "this"
ObjectReference thisObject= null;
try {
thisObject= fStackFrame.thisObject();
} catch (VMDisconnectedException e) {
return;
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_CHILDREN, e);
}
JDIThisVariable oldThisObject= null;
if (!fChildren.isEmpty() && fChildren.get(0) instanceof JDIThisVariable) {
oldThisObject= (JDIThisVariable) fChildren.get(0);
}
if (thisObject == null && oldThisObject != null) {
// removal of 'this'
fChildren.remove(0);
index= 0;
} else {
if (oldThisObject == null && thisObject != null) {
// creation of 'this'
oldThisObject= new JDIThisVariable(this, thisObject);
fChildren.add(0, oldThisObject);
index= 1;
} else {
if (oldThisObject != null) {
// 'this' still exists
index= 1;
}
}
}
}
List locals= null;
try {
locals= fStackFrame.visibleVariables();
} catch (AbsentInformationException e) {
locals= new ArrayList(0);
} catch (NativeMethodException e) {
locals= new ArrayList(0);
} catch (VMDisconnectedException e) {
return;
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_CHILDREN, e);
}
int localIndex= -1;
while (index < fChildren.size()) {
JDILocalVariable local= (JDILocalVariable) fChildren.get(index);
localIndex= locals.indexOf(local.getLocal());
if (localIndex >= 0) {
// update variable with new underling JDI LocalVariable
local.setLocal((LocalVariable) locals.get(localIndex));
locals.remove(localIndex);
index++;
} else {
// remove variable
fChildren.remove(index);
}
}
// add any new locals
Iterator newOnes= locals.iterator();
while (newOnes.hasNext()) {
JDILocalVariable local= new JDILocalVariable(this, (LocalVariable) newOnes.next());
fChildren.add(local);
}
}
/**
* @see IDropToFrame
*/
public boolean supportsDropToFrame() {
try {
JDIThread thread= (JDIThread) getThread();
return !thread.isTerminated()
&& thread.isSuspended()
&& thread.getUnderlyingThread() instanceof org.eclipse.jdi.hcr.ThreadReference
&& ((org.eclipse.jdi.hcr.VirtualMachine) ((JDIDebugTarget) getDebugTarget()).getVM()).canDoReturn();
} catch (UnsupportedOperationException e) {
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
internalError(e);
}
return false;
}
/**
* @see IDropToFrame
*/
public void dropToFrame() throws DebugException {
if (supportsDropToFrame()) {
((JDIThread) getThread()).dropToFrame(this);
} else {
notSupported(ERROR_DROP_NOT_SUPPORTED);
}
}
/**
* @see IVariableLookup
*/
public IVariable findVariable(String varName) throws DebugException {
List list= getChildren0();
Iterator variables = list.iterator();
JDIThisVariable thisVariable= null;
while (variables.hasNext()) {
IVariable var= (IVariable) variables.next();
if (var.getName().equals(varName)) {
return var;
}
if (var instanceof JDIThisVariable) {
// save for later - check for instance and static vars
thisVariable= (JDIThisVariable)var;
}
}
if (thisVariable != null) {
variables= ((JDIValue)thisVariable.getValue()).getChildren0().iterator();
while (variables.hasNext()) {
IVariable var= (IVariable) variables.next();
if (var.getName().equals(varName)) {
return var;
}
}
}
return null;
}
/**
* Helper method that retrieves visible varialbes in this stack frame
* handling any exceptions. Returns an empty list if there are no
* variables.
*/
List getUnderlyingVisibleVariables() throws DebugException {
List variables= Collections.EMPTY_LIST;
try {
variables= fStackFrame.visibleVariables();
} catch (AbsentInformationException e) {
} catch (NativeMethodException e) {
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_CHILDREN, e);
}
return variables;
}
/**
* Returns the underlying JDI StackFrame
*/
StackFrame getUnderlyingStackFrame() {
return fStackFrame;
}
/**
* Sets the underlying JDI StackFrame. Called by a thread
* when incrementally updating after a step has completed.
*/
void setUnderlyingStackFrame(StackFrame frame) {
fStackFrame = frame;
}
/**
* Returns the Java stack frame adapter for this stack frame
*
* @see IAdaptable
*/
protected Object getAdpater(Class adapter) {
if (adapter == IJavaStackFrame.class || adapter == IJavaModifiers.class) {
return this;
}
return super.getAdapter(adapter);
}
/**
* Evaluates the snippet in the context of this stack frame
*
* @see IJavaStackFrame
*/
public void evaluate(String snippet, IJavaEvaluationListener listener, IJavaProject project) throws DebugException {
IEvaluationContext underlyingContext = ((JDIDebugTarget)getDebugTarget()).getEvaluationContext(project);
evaluate(snippet, listener, underlyingContext);
}
/**
* @see IJavaStackFrame
*/
public void evaluate(String snippet, IJavaEvaluationListener listener, IEvaluationContext evaluationContext) throws DebugException {
((JDIThread)getThread()).verifyEvaluation(evaluationContext);
StackFrameEvaluationContext context = new StackFrameEvaluationContext(this, evaluationContext);
context.evaluate(snippet, listener);
}
/**
* @see IJavaEvaluate
*/
public boolean canPerformEvaluation() {
return ((IJavaThread)getThread()).canPerformEvaluation();
}
/**
* @see IJavaStackFrame
*/
public String getSignature() throws DebugException {
try {
return getUnderlyingMethod().signature();
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_SIGNATURE, e);
}
return getUnknownMessage();
}
/**
* @see IJavaStackFrame
*/
public String getDeclaringTypeName() throws DebugException {
try {
return getUnderlyingMethod().declaringType().name();
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_DECLARING_TYPE, e);
}
return getUnknownMessage();
}
/**
* @see IJavaStackFrame
*/
public String getReceivingTypeName() throws DebugException {
try {
ObjectReference thisObject = fStackFrame.thisObject();
if (thisObject == null) {
return getDeclaringTypeName();
} else {
return thisObject.referenceType().name();
}
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_RECEIVING_TYPE, e);
}
return getUnknownMessage();
}
/**
* @see IJavaStackFrame
*/
public String getMethodName() throws DebugException {
try {
return getUnderlyingMethod().name();
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_NAME, e);
}
return getUnknownMessage();
}
/**
* @see IJavaStackFrame
*/
public boolean isAbstract() throws DebugException {
return getUnderlyingMethod().isAbstract();
}
/**
* @see IJavaStackFrame
*/
public boolean isNative() throws DebugException {
return getUnderlyingMethod().isNative();
}
/**
* @see IJavaStackFrame
*/
public boolean isStaticInitializer() throws DebugException {
return getUnderlyingMethod().isStaticInitializer();
}
/**
* @see IJavaStackFrame
*/
public boolean isFinal() throws DebugException {
return getUnderlyingMethod().isFinal();
}
/**
* @see IJavaStackFrame
*/
public boolean isSynchronized() throws DebugException {
return getUnderlyingMethod().isSynchronized();
}
/**
* @see IJavaStackFrame
*/
public boolean isSynthetic() throws DebugException {
return getUnderlyingMethod().isSynthetic();
}
/**
* @see IJavaStackFrame
*/
public boolean isPublic() throws DebugException {
return getUnderlyingMethod().isPublic();
}
/**
* @see IJavaStackFrame
*/
public boolean isPrivate() throws DebugException {
return getUnderlyingMethod().isPrivate();
}
/**
* @see IJavaStackFrame
*/
public boolean isProtected() throws DebugException {
return getUnderlyingMethod().isProtected();
}
/**
* @see IJavaStackFrame
*/
public boolean isPackagePrivate() throws DebugException {
return getUnderlyingMethod().isPackagePrivate();
}
/**
* @see IJavaStackFrame
*/
public boolean isStatic() throws DebugException {
return getUnderlyingMethod().isStatic();
}
/**
* @see IJavaStackFrame
*/
public String getSourceName() throws DebugException {
try {
Location l = getUnderlyingMethod().location();
if (l != null) {
return l.sourceName();
}
} catch (AbsentInformationException e) {
} catch (VMDisconnectedException e) {
} catch (RuntimeException e) {
targetRequestFailed(ERROR_GET_SOURCE_NAME, e);
}
return null;
}
protected boolean isTopStackFrame() throws DebugException {
IStackFrame tos = getThread().getTopStackFrame();
return tos != null && tos.equals(this);
}
protected boolean exists() throws DebugException {
return ((JDIThread)getThread()).getChildren0().indexOf(this) != -1;
}
}