blob: 99e1feb96675216127c2624189b8e5c9c0416eb2 [file] [log] [blame]
package org.eclipse.jdt.internal.debug.core;
import com.sun.jdi.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.IDebugStatusConstants;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
import org.eclipse.jdt.core.eval.IEvaluationContext;
import org.eclipse.jdt.debug.core.IJavaEvaluationListener;
import org.eclipse.jdt.debug.core.IJavaEvaluationResult;
import org.eclipse.jdt.debug.core.IJavaValue;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* An evaluation context for a stack frame.
*/
public class ThreadEvaluationContext implements ICodeSnippetRequestor, Runnable, IJavaEvaluationResult {
//NLS
protected static final String PREFIX = "jdi_evaluation.";
protected static final String ERROR = PREFIX + "error.";
protected static final String ERROR_EVALUATION = ERROR+ "evaluation";
protected static final String ERROR_THREAD_NOT_SUSPENDED = ERROR+ "thread_not_suspended";
/**
* The evaluation context for the associated java project
*/
protected IEvaluationContext fContext;
/**
* The thread to perform the evaluation in
*/
protected JDIThread fModelThread;
/**
* Problems, source fragments, and kinds
*/
protected List fProblems, fSourceFragments, fKinds;
/**
* The actual code snippet
*/
protected String fSnippet;
/**
* The debug exception that describes the evaluation failure, if any
*/
protected DebugException fDebugException = null;
/**
* The listener that we report back to
*/
protected IJavaEvaluationListener fListener;
/**
* The resulting value
*/
protected IJavaValue fValue;
/**
* The (JDI) result
*/
protected ObjectReference fResult;
/**
* The (JDI) type of the result value
*/
protected ClassObjectReference fResultType;
/**
* Constructs a context for a thread and IEvaluationContext
*/
public ThreadEvaluationContext(JDIThread modelThread, IEvaluationContext context) {
fContext = context;
fModelThread = modelThread;
fProblems = new ArrayList();
fSourceFragments = new ArrayList();
fKinds = new ArrayList();
}
/**
* @see IJavaEvaluationAdapter
*/
public void evaluate(String expression, IJavaEvaluationListener listener) throws DebugException {
if (getModelThread().isSuspended()) {
fListener = listener;
fSnippet = expression;
Thread t = new Thread(this);
t.start();
} else {
getModelThread().requestFailed(ERROR_THREAD_NOT_SUSPENDED, null);
}
}
/**
* Runs the evaluation
*/
public void run() {
try {
doEvaluation();
convertResult();
} catch (DebugException e) {
fDebugException = e;
}
fListener.evaluationComplete(this);
}
/**
* Do thread specific evaluation
*/
protected void doEvaluation() throws DebugException {
try {
getEvaluationContext().evaluateCodeSnippet(fSnippet, this, null);
} catch (JavaModelException e) {
throw new DebugException(e.getStatus());
}
}
/**
* Makes an IValue from the result of the evaluation
*/
protected void convertResult() throws DebugException {
if (fResult != null) {
// check the type of the result - if a primitive type, convert it
String sig = fResultType.reflectedType().signature();
if (sig.length() == 1) {
// primitive type
Field valueField = getValueField(sig);
if (valueField != null) {
try {
Value v= fResult.getValue(valueField);
fValue = new JDIValue(v, getModelThread());
return;
} catch (RuntimeException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
}
}
}
// not a primite type
fValue = new JDIValue(fResult, getModelThread());
} else {
if (fResultType != null) {
try {
ReferenceType ref= fResultType.reflectedType();
String sig = ref.signature();
if (sig.equals("V") || sig.equals("Lvoid;")) {
// void
fValue = new JDIVoidValue(getModelThread());
return;
}
} catch (RuntimeException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
}
}
// null
fValue = new JDIValue(null, getModelThread());
}
}
/**
* Returns the first non static field in the result type with the given signature (we
* assume it contains the primitive value we are looking for)
*/
protected Field getValueField(String signature) throws DebugException {
try {
Iterator iter= fResult.referenceType().fields().iterator();
while (iter.hasNext()) {
Field f = (Field)iter.next();
if (!f.isStatic() && f.signature().equals(signature)) {
return f;
}
}
} catch (RuntimeException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
}
return null;
}
/**
* @see ICodeSnippetRuntime
*/
public boolean acceptClassFiles(byte[][] classFiles, String[][] classFileNames, String codeSnippetClassName) {
// deploy the class files
try {
deploy(classFiles, classFileNames);
} catch (DebugException e) {
fDebugException = e;
return false;
}
// evaluate the snippet, if given a class name
if (codeSnippetClassName != null) {
try {
runSnippet(codeSnippetClassName);
} catch (DebugException e) {
fDebugException = e;
return false;
}
}
return true;
}
/**
* Deploy the class files.
*
* <p>Currently, this involves writing them to the output folder of the
* associated Java Project.
*
* @exception DebugException if this fails due to a lower level exception.
*/
protected void deploy(byte[][] classFiles, String[][] classFileNames) throws DebugException {
getDebugTarget().deploy(classFiles, classFileNames, getEvaluationContext());
}
/**
* Evaluate the code snippet
*/
protected void runSnippet(String codeSnippetClassName) throws DebugException {
ClassType codeSnippetClass;
ObjectReference codeSnippet;
Method method;
List arguments;
ObjectReference codeSnippetRunner;
VirtualMachine jdiVM = getVM();
ThreadReference jdiThread = getUnderlyingThread();
try {
// Get the code snippet class
List classes = jdiVM.classesByName(codeSnippetClassName);
if (classes.size() == 0) {
// Load the class
codeSnippetClass = classForName(codeSnippetClassName);
if (codeSnippetClass == null) {
getModelThread().requestFailed(DebugJavaUtils.getResourceString("jdi_evaluation.error.evaluation"), null);
return;
}
} else {
codeSnippetClass = (ClassType)classes.get(0);
}
// Create a new code snippet
Method constructor = (Method)codeSnippetClass.methodsByName("<init>").get(0);
codeSnippet = codeSnippetClass.newInstance(jdiThread, constructor, new ArrayList(), ClassType.INVOKE_SINGLE_THREADED);
// Get the method 'runCodeSnippet' and its arguments
method = (Method)codeSnippetClass.methodsByName(RUN_METHOD).get(0);
arguments = new ArrayList();
// Invoke runCodeSnippet(CodeSnippet)
getModelThread().invokeMethod(null, codeSnippet, method, arguments);
// Retrieve the result
Field resultField = codeSnippetClass.fieldByName(RESULT_VALUE_FIELD);
fResult = (ObjectReference)codeSnippet.getValue(resultField);
Field resultTypeField = codeSnippetClass.fieldByName(RESULT_TYPE_FIELD);
fResultType = (ClassObjectReference)codeSnippet.getValue(resultTypeField);
} catch (ClassNotLoadedException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
} catch (IncompatibleThreadStateException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
} catch (InvalidTypeException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
} catch (InvocationException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
} catch (DebugException e) {
throw e;
} catch (RuntimeException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
}
}
/**
* @see ICodeSnippetRequestor
*/
public void acceptProblem(IMarker problem, String sourceFragment, int kind) {
fProblems.add(problem);
fSourceFragments.add(sourceFragment);
fKinds.add(new Integer(kind));
}
/**
* Force the load of the code snippet class in the target VM by
* emulating a Class.forName(String).
*
* @exception DebugException on failure
*/
protected ClassType classForName(String className) throws DebugException {
try {
// get java.lang.Class
VirtualMachine jdiVM = getVM();
List classes = jdiVM.classesByName("java.lang.Class");
if (classes.size() == 0) {
return null;
}
ClassType classClass= (ClassType)classes.get(0);
List methods= classClass.methodsByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;");
if (methods.isEmpty()) {
return null;
}
Method forName = (Method)methods.get(0);
ThreadReference jdiThread = getUnderlyingThread();
StringReference nameArg = jdiVM.mirrorOf(className);
List args = new ArrayList();
args.add(nameArg);
ClassObjectReference classObject = (ClassObjectReference)getModelThread().invokeMethod(classClass, null, forName, args);
// translate the ClassObjectReference to the ClassType
ClassType loadedClass = null;
classes = jdiVM.classesByName(className);
if (classes.size() > 0) {
loadedClass = (ClassType)classes.get(0);
}
return loadedClass;
} catch (RuntimeException e) {
getModelThread().targetRequestFailed(ERROR_EVALUATION, e);
}
return null;
}
/**
* Helper method - returns the jdi thread reference on the target,
* based on the "model" thread.
*/
protected ThreadReference getUnderlyingThread() {
return getModelThread().fThread;
}
/**
* Helper method - returns the model thread
*/
protected JDIThread getModelThread() {
return fModelThread;
}
/**
* Helper method - returns the jdi VM
*/
protected VirtualMachine getVM() {
return fModelThread.getVM();
}
/**
* Returns the debug target in which the evaluation is being performed.
*/
protected JDIDebugTarget getDebugTarget() {
return (JDIDebugTarget)fModelThread.getDebugTarget();
}
/**
* Returns the underlying evaluation context for this evaluation.
*/
protected IEvaluationContext getEvaluationContext() {
return fContext;
}
/**
* @see IJavaEvaluationResult
*/
public IJavaValue getValue() {
return fValue;
}
/**
* @see IJavaEvaluationResult
*/
public boolean hasProblems() {
return !fProblems.isEmpty() || fDebugException != null;
}
/**
* @see IJavaEvaluationResult
*/
public IMarker[] getProblems() {
return (IMarker[])fProblems.toArray(new IMarker[fProblems.size()]);
}
/**
* @see IJavaEvaluationResult
*/
public String getSourceFragment(IMarker problem) {
int index = fProblems.indexOf(problem);
if (index >=0) {
return (String)fSourceFragments.get(index);
} else {
return null;
}
}
/**
* @see IJavaEvaluationResult
*/
public int getKind(IMarker problem) {
int index = fProblems.indexOf(problem);
if (index >=0) {
return ((Integer)fKinds.get(index)).intValue();
} else {
return -1;
}
}
/**
* @see IJavaEvaluationResult
*/
public String getSnippet() {
return fSnippet;
}
/**
* @see IJavaEvaluationResult
*/
public Throwable getException() {
return fDebugException;
}
}