blob: 76f48ef944009bce60ad6cd7b530fe04c6e708a3 [file] [log] [blame]
package org.eclipse.jdt.internal.debug.eval;
/**********************************************************************
Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
This file is made available under the terms of the Common Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/cpl-v10.html
**********************************************************************/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.Message;
import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
import org.eclipse.jdt.core.eval.IEvaluationContext;
import org.eclipse.jdt.debug.core.IEvaluationRunnable;
import org.eclipse.jdt.debug.core.IJavaClassObject;
import org.eclipse.jdt.debug.core.IJavaClassType;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.core.IJavaType;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.debug.core.IJavaVariable;
import org.eclipse.jdt.debug.core.JDIDebugModel;
import org.eclipse.jdt.debug.eval.IClassFileEvaluationEngine;
import org.eclipse.jdt.debug.eval.IEvaluationListener;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
import org.eclipse.jdt.internal.debug.core.model.JDIValue;
import com.sun.jdi.InvocationException;
import com.sun.jdi.ObjectReference;
/**
* An evaluation engine that deploys class files locally
*/
public class LocalEvaluationEngine implements IClassFileEvaluationEngine, ICodeSnippetRequestor , IEvaluationRunnable {
private static final String CODE_SNIPPET_NAME= "CodeSnippet.class"; //$NON-NLS-1$
/**
* A count of the number of engines created.
* Count is incremented on instantiation and decremented on
* dispose. When the count == 0, the special CodeSnippet.class
* is deleted as this class file is shared by all.
*/
private static int ENGINE_COUNT= 0;
/**
* The Java project context in which to compile snippets.
*/
private IJavaProject fJavaProject;
/**
* The debug target on which to execute snippets
*/
private IJavaDebugTarget fDebugTarget;
/**
* The location in which to deploy snippet class files
*/
private File fOutputDirectory;
/**
* The listener to notify when the current evaluation
* is complete.
*/
private IEvaluationListener fListener;
/**
* The stack frame context for the current evaluation
* or <code>null</code> if there is no stack frame
* context.
*/
private IJavaStackFrame fStackFrame;
/**
* The result of this evaluation
*/
private EvaluationResult fResult;
/**
* Collection of deployed snippet class files
*/
private List fSnippetFiles;
/**
* Collection of directories created by this evaluation
* engine.
*/
private List fDirectories;
/**
* Evaluation context for the Java project associated
* with this evaluation engine.
*/
private IEvaluationContext fEvaluationContext;
/**
* Array of modifier constants for visible local variables
* in the current evaluation.
*
* XXX: constants should be 'default' or 'final'. Where
* are these constants defined.
*/
private int[] fLocalVariableModifiers;
/**
* Array of names of visible local variables
* in the current evaluation.
*/
private String[] fLocalVariableNames;
/**
* Array of type names of visible local variables
* in the current evaluation.
*/
private String[] fLocalVariableTypeNames;
/**
* The 'this' object for the current evaluation
* or <code>null</code> if there is no 'this'
* context (static method, or not context)
*/
private IJavaObject fThis;
/**
* Whether this engine has been disposed.
*/
private boolean fDisposed = false;
/**
* The number of evaluations currently being
* performed.
*/
private int fEvaluationCount = 0;
/**
* The name of the code snippet class to instantiate
*/
private String fCodeSnippetClassName = null;
/**
* Whether to hit breakpoints in the evaluation thread
*/
private boolean fHitBreakpoints = false;
/**
* Constant for empty array of <code>java.lang.String</code>
*/
private static final String[] EMPTY_STRING_ARRAY = new String[0];
/**
* Constant for empty array of <code>int</code>
*/
private static final int[] EMPTY_INT_ARRAY = new int[0];
/**
* Cosntructs a new evaluation engine for the given VM in the context
* of the specified project. Class files required for the evaluation will
* be deployed to the specified directory (which must be on the class
* path of the VM in order for evaluation to work).
*
* @param project context in which to compile snippets
* @param vm debug target in which to evaluate snippets
* @param directory location where snippet class files will
* be deployed for execution. The directory must exist
*/
public LocalEvaluationEngine(IJavaProject project, IJavaDebugTarget vm, File directory) {
setJavaProject(project);
setDebugTarget(vm);
setOutputDirectory(directory);
ENGINE_COUNT++;
}
/**
* @see ICodeSnippetRequestor#acceptClassFiles(byte[][], String[][], String)
*/
public boolean acceptClassFiles(
byte[][] classFileBytes,
String[][] classFileCompoundNames,
String codeSnippetClassName) {
try {
deploy(classFileBytes, classFileCompoundNames);
} catch (DebugException e) {
getResult().setException(e);
return false;
}
if (codeSnippetClassName != null) {
setCodeSnippetClassName(codeSnippetClassName);
try {
getThread().runEvaluation(this, null, DebugEvent.EVALUATION, getHitBreakpoints());
} catch (DebugException e) {
// exception handling is in evaluation runnable
}
}
return true;
}
public void run(IJavaThread thread, IProgressMonitor monitor) {
IJavaObject codeSnippetInstance = null;
try {
codeSnippetInstance = newInstance(getCodeSnippetClassName());
initializeLocals(codeSnippetInstance);
codeSnippetInstance.sendMessage(RUN_METHOD, "()V", null, getThread(), false); //$NON-NLS-1$
restoreLocals(codeSnippetInstance);
// now retrieve the description of the result
IVariable[] fields = codeSnippetInstance.getVariables();
IJavaVariable resultValue = null;
IJavaVariable resultType = null;
for (int i = 0; i < fields.length; i++) {
if (fields[i].getName().equals(RESULT_TYPE_FIELD)) {
resultType = (IJavaVariable)fields[i];
}
if (fields[i].getName().equals(RESULT_VALUE_FIELD)) {
resultValue = (IJavaVariable)fields[i];
}
}
IJavaValue result = convertResult((IJavaClassObject)resultType.getValue(), (IJavaValue)resultValue.getValue());
getResult().setValue(result);
} catch (DebugException e) {
getResult().setException(e);
Throwable underlyingException = e.getStatus().getException();
if (underlyingException instanceof InvocationException) {
ObjectReference theException = ((InvocationException)underlyingException).exception();
if (theException != null) {
try {
try {
IJavaObject v = (IJavaObject)JDIValue.createValue((JDIDebugTarget)getDebugTarget(), theException);
v.sendMessage("printStackTrace", "()V", null, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
} catch (DebugException de) {
JDIDebugPlugin.log(de);
}
} catch (RuntimeException re) {
JDIDebugPlugin.log(re);
}
}
}
}
}
/**
* Initializes the value of instance variables in the
* 'code snippet object' that are used as placeholders
* for locals and 'this' in the current stack frame.
*
* @param object instance of code snippet class that will
* be run
* @exception DebugException if an exception is thrown
* accessing the given object
*/
protected void initializeLocals(IJavaObject object) throws DebugException {
IJavaVariable[] locals = null;
IJavaObject thisObject = getThis();
if (getStackFrame() != null) {
locals = getStackFrame().getLocalVariables();
}
if (locals != null) {
for (int i = 0; i < locals.length; i++) {
IJavaVariable local = locals[i];
IJavaVariable field = object.getField(LOCAL_VAR_PREFIX + local.getName(), false);
// internal error if field is not found
if (field == null) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_unable_to_initialize_local_variables._4"), null) //$NON-NLS-1$
);
} else {
field.setValue(local.getValue());
}
}
}
if (thisObject != null) {
IJavaVariable field = object.getField(DELEGATE_THIS, false);
// internal error if field is not found
if (field == null) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_unable_to_initialize___this___context._5"), null) //$NON-NLS-1$
);
} else {
field.setValue(thisObject);
}
}
}
/**
* Restores the value local variables from the instance
* variables in the 'code snippet object' that are used
* as placeholders for locals in the current stack frame.
*
* @param object instance of code snippet class that was
* run
* @exception DebugException if an exception is thrown
* accessing the given object
*/
protected void restoreLocals(IJavaObject object) throws DebugException {
IJavaVariable[] locals = null;
if (getStackFrame() != null) {
locals = getStackFrame().getLocalVariables();
}
if (locals != null) {
for (int i = 0; i < locals.length; i++) {
IJavaVariable local = locals[i];
IJavaVariable field = object.getField(LOCAL_VAR_PREFIX + local.getName(), false);
// internal error if field is not found
if (field == null) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_unable_to_initialize_local_variables._6"), null) //$NON-NLS-1$
);
} else {
local.setValue(field.getValue());
}
}
}
}
/**
* @see ICodeSnippetRequestor#acceptProblem(IMarker, String, int)
*/
public void acceptProblem(IMarker problemMarker, String fragmentSource, int fragmentKind) {
if (problemMarker.getAttribute(IMarker.SEVERITY, -1) != IMarker.SEVERITY_ERROR) {
return;
}
Message message= new Message(problemMarker.getAttribute(IMarker.MESSAGE, ""), problemMarker.getAttribute(IMarker.CHAR_START, 0)); //$NON-NLS-1$
getResult().addError(message);
}
/**
* @see IEvaluationEngine#getDebugTarget()
*/
public IJavaDebugTarget getDebugTarget() {
return fDebugTarget;
}
/**
* Sets the debug target in which snippets are executed.
*
* @param debugTarget the debug target in which snippets are executed
*/
private void setDebugTarget(IJavaDebugTarget debugTarget) {
fDebugTarget = debugTarget;
}
/**
* @see IEvaluationEngine#getJavaProject()
*/
public IJavaProject getJavaProject() {
return fJavaProject;
}
/**
* Sets the Java project in which snippets are compiled.
*
* @param javaProject the Java project in which snippets are compiled
*/
private void setJavaProject(IJavaProject javaProject) {
fJavaProject = javaProject;
}
/**
* Returns the directory in which snippet class files are deployed.
*
* @return the directory in which snippet class files are deployed.
*/
public File getOutputDirectory() {
return fOutputDirectory;
}
/**
* Sets the directory in which snippet class files are
* deployed.
*
* @param outputDirectory location to deploy snippet class files
*/
private void setOutputDirectory(File outputDirectory) {
fOutputDirectory = outputDirectory;
}
/**
* @see IClassFileEvaluationEngine#evaluate(String, IJavaThread, IEvaluationListener)
*/
public void evaluate(String snippet, IJavaThread thread, IEvaluationListener listener, boolean hitBreakpoints) throws DebugException {
checkDisposed();
checkEvaluating();
try {
evaluationStarted();
setListener(listener);
setHitBreakpoints(hitBreakpoints);
setResult(new EvaluationResult(this, snippet, thread));
checkThread();
// no receiver/stack frame context
setThis(null);
setLocalVariableNames(EMPTY_STRING_ARRAY);
setLocalVariableTypeNames(EMPTY_STRING_ARRAY);
setLocalVariableModifiers(EMPTY_INT_ARRAY);
// do the evaluation in a different thread
Runnable r = new Runnable() {
public void run() {
try {
LocalEvaluationEngine.this.getEvaluationContext().
evaluateCodeSnippet(LocalEvaluationEngine.this.getSnippet(),
LocalEvaluationEngine.this, null);
} catch (JavaModelException e) {
LocalEvaluationEngine.this.getResult().setException(new DebugException(e.getStatus()));
}
LocalEvaluationEngine.this.evaluationComplete();
}
};
Thread t = new Thread(r);
t.start();
} catch (DebugException d) {
evaluationAborted();
throw d;
}
}
/**
* @see IEvaluationEngine#evaluate(String, IJavaStackFrame, IEvaluationListener, int)
*/
public void evaluate(String snippet, IJavaStackFrame frame, IEvaluationListener listener, int evaluationDetail, boolean hitBreakpoints) throws DebugException {
checkDisposed();
checkEvaluating();
try {
evaluationStarted();
setListener(listener);
setStackFrame(frame);
setHitBreakpoints(hitBreakpoints);
setResult(new EvaluationResult(this, snippet, (IJavaThread)frame.getThread()));
checkThread();
// set up local variables and 'this' context for evaluation
IJavaVariable[] locals = frame.getLocalVariables();
List typeNames = new ArrayList(locals.length);
List varNames = new ArrayList(locals.length);
for (int i = 0; i < locals.length; i++) {
IJavaVariable var = locals[i];
String typeName = getTranslatedTypeName(var.getReferenceTypeName());
if (typeName != null) {
typeNames.add(typeName);
varNames.add(var.getName());
}
}
setLocalVariableTypeNames((String[])typeNames.toArray(new String[typeNames.size()]));
setLocalVariableNames((String[])varNames.toArray(new String[varNames.size()]));
int[] modifiers = new int[typeNames.size()];
// cannot determine if local is final, so specify as default
Arrays.fill(modifiers, 0);
setLocalVariableModifiers(modifiers);
setThis(frame.getThis());
final boolean isStatic = frame.isStatic();
final boolean isConstructor = frame.isConstructor();
final IType receivingType = getReceivingType(frame);
// do the evaluation in a different thread
Runnable r = new Runnable() {
public void run() {
try {
LocalEvaluationEngine.this.getEvaluationContext().
evaluateCodeSnippet(
LocalEvaluationEngine.this.getSnippet(),
LocalEvaluationEngine.this.getLocalVariableTypeNames(),
LocalEvaluationEngine.this.getLocalVariableNames(),
LocalEvaluationEngine.this.getLocalVariableModifiers(),
receivingType,
isStatic,
isConstructor,
LocalEvaluationEngine.this,
null);
} catch (JavaModelException e) {
LocalEvaluationEngine.this.getResult().setException(new DebugException(e.getStatus()));
}
LocalEvaluationEngine.this.evaluationComplete();
}
};
Thread t = new Thread(r);
t.start();
} catch (DebugException d) {
evaluationAborted();
throw d;
}
}
/**
* @see IEvaluationEngine#evaluate(String, IJavaObject, IJavaThread, IEvaluationListener, int)
*/
public void evaluate(String snippet, IJavaObject thisContext, IJavaThread thread, IEvaluationListener listener, int evaluationDetail, boolean hitBreakpoints) throws DebugException {
checkDisposed();
checkEvaluating();
try {
evaluationStarted();
setListener(listener);
setHitBreakpoints(hitBreakpoints);
setResult(new EvaluationResult(this, snippet, thread));
checkThread();
// no locals
setLocalVariableTypeNames(new String[0]);
setLocalVariableNames(new String[0]);
setLocalVariableModifiers(new int[0]);
setThis(thisContext);
final boolean isStatic = false;
final boolean isConstructor = false;
final IType receivingType = getReceivingType(thisContext);
// do the evaluation in a different thread
Runnable r = new Runnable() {
public void run() {
try {
LocalEvaluationEngine.this.getEvaluationContext().
evaluateCodeSnippet(
LocalEvaluationEngine.this.getSnippet(),
LocalEvaluationEngine.this.getLocalVariableTypeNames(),
LocalEvaluationEngine.this.getLocalVariableNames(),
LocalEvaluationEngine.this.getLocalVariableModifiers(),
receivingType,
isStatic,
isConstructor,
LocalEvaluationEngine.this,
null);
} catch (JavaModelException e) {
LocalEvaluationEngine.this.getResult().setException(new DebugException(e.getStatus()));
}
LocalEvaluationEngine.this.evaluationComplete();
}
};
Thread t = new Thread(r);
t.start();
} catch (DebugException d) {
evaluationAborted();
throw d;
}
}
/**
* Throws an exception if this engine has already been
* disposed.
*
* @exception DebugException if this engine has been disposed
*/
protected void checkDisposed() throws DebugException {
if (isDisposed()) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_evaluation_context_has_been_disposed._7"), null) //$NON-NLS-1$
);
}
}
/**
* Throws an exception if this engine is already in an
* evaluation.
*
* @exception DebugException if this engine is currently
* performing an evaluation
*/
protected void checkEvaluating() throws DebugException {
if (isEvaluating()) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, "Cannot perform nested evaluations.", null) //$NON-NLS-1$
);
}
}
/**
* Throws an exception if this engine's current evaluation
* thread is not suspended.
*
* @exception DebugException if this engine's current evaluation
* thread is not suspended
*/
protected void checkThread() throws DebugException {
if (!getThread().isSuspended()) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_evaluation_thread_must_be_suspended._8"), null) //$NON-NLS-1$
);
}
}
/**
* Deletes deployed class files, and clears state.
*
* @see IEvaluationEngine#dispose()
*/
public void dispose() {
fDisposed = true;
ENGINE_COUNT--;
if (isEvaluating()) {
// cannot dispose if in an evaluation, must
// wait for evaluation to complete
return;
}
List snippetFiles = getSnippetFiles();
Iterator iter = snippetFiles.iterator();
while (iter.hasNext()) {
File file = (File)iter.next();
if (file.exists()) {
if (CODE_SNIPPET_NAME.equals(file.getName()) && ENGINE_COUNT > 0) {
continue; //do not delete the common file for other engines
}
if (!file.delete()) {
JDIDebugPlugin.log(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(), DebugException.REQUEST_FAILED,
MessageFormat.format(EvaluationMessages.getString("LocalEvaluationEngine.Unable_to_delete_temporary_evaluation_class_file_{0}_1"), new String[] {file.getAbsolutePath()}), null) //$NON-NLS-1$
);
}
}
}
List directories = getDirectories();
// remove directories in bottom up order
int i = directories.size() - 1;
while (i >= 0) {
File dir = (File)directories.get(i);
String[] listing= dir.list();
if (dir.exists() && listing != null && listing.length == 0 && !dir.delete()) {
JDIDebugPlugin.log(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(), DebugException.REQUEST_FAILED,
MessageFormat.format(EvaluationMessages.getString("LocalEvaluationEngine.Unable_to_delete_temporary_evaluation_directory_{0}_2"), new String[] {dir.getAbsolutePath()}), null) //$NON-NLS-1$
);
}
i--;
}
reset();
setJavaProject(null);
setDebugTarget(null);
setOutputDirectory(null);
setResult(null);
setEvaluationContext(null);
}
/**
* Resets this engine for another evaluation.
*/
private void reset() {
setThis(null);
setStackFrame(null);
setListener(null);
}
/**
* Returns the listener to notify when the current
* evaluation is complete.
*
* @return the listener to notify when the current
* evaluation is complete
*/
protected IEvaluationListener getListener() {
return fListener;
}
/**
* Sets the listener to notify when the current
* evaluation is complete.
*
* @param listener the listener to notify when the current
* evaluation is complete
*/
private void setListener(IEvaluationListener listener) {
fListener = listener;
}
/**
* Returns the stack frame context for the current
* evaluation, or <code>null</code> if none.
*
* @return the stack frame context for the current
* evaluation, or <code>null</code> if none
*/
protected IJavaStackFrame getStackFrame() {
return fStackFrame;
}
/**
* Sets the stack frame context for the current evaluation.
*
* @param stackFrame stack frame context or <code>null</code>
* if none
*/
private void setStackFrame(IJavaStackFrame stackFrame) {
fStackFrame = stackFrame;
}
/**
* Returns the thread in which the current evaluation is
* to be executed.
*
* @return the thread in which the current evaluation is
* to be executed
*/
protected IJavaThread getThread() {
return getResult().getThread();
}
/**
* Returns the code snippet being evaluated.
*
* @return the code snippet being evaluated.
*/
protected String getSnippet() {
return getResult().getSnippet();
}
/**
* Returns the current evaluation result.
*
* @return the current evaluation result
*/
protected EvaluationResult getResult() {
return fResult;
}
/**
* Sets the current evaluation result.
*
* @param result the current evaluation result
*/
private void setResult(EvaluationResult result) {
fResult = result;
}
/**
* Deploys the given class files to this engine's
* output location, and adds the files to this
* engines list of temporary files to be deleted
* when disposed.
*
* @exception DebugException if this fails due to a
* lower level exception.
*/
protected void deploy(final byte[][] classFiles, final String[][] classFileNames) throws DebugException {
// create the files in a workspace runnable
IWorkspace workspace= getJavaProject().getProject().getWorkspace();
IWorkspaceRunnable runnable= new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
for (int i = 0; i < classFiles.length; i++) {
String[] compoundName = classFileNames[i];
//create required folders
File dir = LocalEvaluationEngine.this.getOutputDirectory();
try {
String pkgDirName = dir.getCanonicalPath();
for (int j = 0; j < (compoundName.length - 1); j++) {
pkgDirName += File.separator + compoundName[j];
File pkgDir = new File(pkgDirName);
if (!pkgDir.exists()) {
pkgDir.mkdir();
addDirectory(pkgDir);
}
}
String name = compoundName[compoundName.length - 1] + ".class"; //$NON-NLS-1$
File classFile = new File(pkgDirName + File.separator + name);
if (!classFile.exists()) {
classFile.createNewFile();
}
FileOutputStream stream = new FileOutputStream(classFile);
stream.write(classFiles[i]);
stream.close();
LocalEvaluationEngine.this.addSnippetFile(classFile);
} catch (IOException e) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(), DebugException.REQUEST_FAILED,
MessageFormat.format(EvaluationMessages.getString("LocalEvaluationEngine.{0}_occurred_deploying_class_file_for_evaluation_9"), new String[] {e.toString()}), e) //$NON-NLS-1$
);
}
}
}
};
try {
workspace.run(runnable, null);
} catch (CoreException e) {
throw new DebugException(e.getStatus());
}
}
/**
* Adds the given file to this engine's collection
* of deployed snippet class files, which are to
* be deleted when this engine is diposed.
*
* @param File snippet class file
*/
private void addSnippetFile(File file) {
if (fSnippetFiles == null) {
fSnippetFiles = new ArrayList();
}
fSnippetFiles.add(file);
}
/**
* Adds the given file to this engine's collection
* of cerated directories, which are to
* be deleted when this engine is diposed.
*
* @param file directory created for class file deployment
*/
private void addDirectory(File file) {
if (fDirectories == null) {
fDirectories = new ArrayList();
}
fDirectories.add(file);
}
/**
* Returns an evaluation context for this evaluation
* engine. An evaluation context is associted with a
* specific Java project. The evaluation context is
* created lazily on the first access.
*
* @return evaluation context
*/
protected IEvaluationContext getEvaluationContext() {
if (fEvaluationContext == null) {
fEvaluationContext = getJavaProject().newEvaluationContext();
}
return fEvaluationContext;
}
/**
* Sets the evaluation context for this evaluation
* engine.
*
* @param context evaluation context
*/
private void setEvaluationContext(IEvaluationContext context) {
fEvaluationContext = context;
}
/**
* Returns a collection of snippet class file deployed by
* this evaluation engine, possibly empty.
*
* @return deployed class files
*/
protected List getSnippetFiles() {
if (fSnippetFiles == null) {
return Collections.EMPTY_LIST;
} else {
return fSnippetFiles;
}
}
/**
* Returns a collection of directories created by
* this evaluation engine, possibly empty.
*
* @return directories created when deploying class files
*/
protected List getDirectories() {
if (fDirectories == null) {
return Collections.EMPTY_LIST;
} else {
return fDirectories;
}
}
/**
* Returns whether this evaluation engine has been
* disposed.
*
* @return whether this evaluation engine has been
* disposed
*/
protected boolean isDisposed() {
return fDisposed;
}
/**
* The evaluation is complete. Notify the current listener
* and reset for the next evaluation.
*/
protected void evaluationComplete() {
// only notify if plug-in not yet shutdown (bug# 8693)
if (JDIDebugPlugin.getDefault() != null) {
getListener().evaluationComplete(getResult());
}
evaluationEnded();
reset();
if (isDisposed()) {
// if the engine was disposed during an evaluation
// do the cleanup now
dispose();
}
}
/**
* Increments the evaluation counter.
*/
private void evaluationStarted() {
fEvaluationCount++;
}
/**
* Decrements the evaluation counter.
*/
private void evaluationEnded() {
if (fEvaluationCount > 0) {
fEvaluationCount--;
}
}
/**
* Returns whether this engine is currently in the
* midst of an evaluation.
*/
protected boolean isEvaluating() {
return fEvaluationCount > 0;
}
/**
* Called when an evaluation is aborted due to an
* exception. Decrements the evalution count, and
* disposes this engine if the target VM disconnected
* or terminated during the evaluation attempt.
*/
private void evaluationAborted() {
evaluationEnded();
if (isDisposed()) {
// if the engine was disposed during an evaluation
// do the cleanup now
dispose();
}
}
/**
* Constructs and returns a new instance of the specified
* class on the target VM.
*
* @param className fully qualified class name
* @return a new instance on the target, as an <code>IJavaValue</code>
* @exception DebugException if creation fails
*/
protected IJavaObject newInstance(String className) throws DebugException {
IJavaObject object = null;
IJavaClassType clazz = null;
IJavaType[] types = getDebugTarget().getJavaTypes(className);
if (types != null && types.length > 0) {
clazz = (IJavaClassType)types[0];
}
if (clazz == null) {
// The class is not loaded on the target VM.
// Force the load of the class.
types = getDebugTarget().getJavaTypes("java.lang.Class"); //$NON-NLS-1$
IJavaClassType classClass = null;
if (types != null && types.length > 0) {
classClass = (IJavaClassType)types[0];
}
if (classClass == null) {
// unable to load the class
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_unable_to_instantiate_code_snippet_class._11"), null) //$NON-NLS-1$
);
}
IJavaValue[] args = new IJavaValue[] {getDebugTarget().newValue(className)};
IJavaObject classObject = (IJavaObject)classClass.sendMessage("forName", "(Ljava/lang/String;)Ljava/lang/Class;", args, getThread()); //$NON-NLS-2$ //$NON-NLS-1$
object = (IJavaObject)classObject.sendMessage("newInstance", "()Ljava/lang/Object;", null, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
} else {
object = (IJavaObject)clazz.newInstance("<init>", null, getThread()); //$NON-NLS-1$
}
return object;
}
/**
* Interpretts and returns the result of the running the snippet
* class file. The type of the result is described by an instance of
* <code>java.lang.Class</code>. The value is interpretted based
* on the result type.
* <p>
* Objects as well as primitve data types (boolean, int, etc.),
* have class objects, which are created by the VM. If the class object
* represents a primitive data type, then the associated value
* is stored in an instance of its "object" class. For example, when
* the result type is the class object for <code>int</code>, the result
* object is an instance of <code>java.lang.Integer</code>, and the
* actual <code>int</code> is stored in the </code>intValue()</code>.
* When the result type is the class object for <code>java.lang.Integer</code>
* the result object is an instance of <code>java.lang.Integer</code>,
* to be interpretted as a <ocde>java.lang.Integer</code>.
* </p>
*
* @param resultType the class of the result
* @param resultValue the value of ther result, to be interpretted
* based on resultType
* @return the result of running the code snipped class file
*/
protected IJavaValue convertResult(IJavaClassObject resultType, IJavaValue result) throws DebugException {
if (resultType == null) {
// there was an exception or compilation problem - no result
return null;
}
// check the type of the result - if a primitive type, convert it
String sig = resultType.getInstanceType().getSignature();
if (sig.equals("V") || sig.equals("Lvoid;")) { //$NON-NLS-2$ //$NON-NLS-1$
// void
return getDebugTarget().voidValue();
}
if (result.getJavaType() == null) {
// null result
return result;
}
if (sig.length() == 1) {
// primitive type - find the instance variable with the
// signature of the result type we are looking for
IVariable[] vars = result.getVariables();
IJavaVariable var = null;
for (int i = 0; i < vars.length; i++) {
IJavaVariable jv = (IJavaVariable)vars[i];
if (!jv.isStatic() && jv.getSignature().equals(sig)) {
var = jv;
break;
}
}
if (var != null) {
return (IJavaValue)var.getValue();
}
} else {
// an object
return result;
}
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_internal_error_retreiving_result._17"), null) //$NON-NLS-1$
);
}
/**
* Returns the modifiers of the local variables
* visible in this evaluation, possibly empty.
*
* @return array of modifiers
*/
private int[] getLocalVariableModifiers() {
return fLocalVariableModifiers;
}
/**
* Sets the modifiers of the local variables
* visible in this evaluation, possibly empty.
*
* @param localVariableModifiers array of modifiers
*/
private void setLocalVariableModifiers(int[] localVariableModifiers) {
fLocalVariableModifiers = localVariableModifiers;
}
/**
* Returns the names of the local variables
* visible in this evaluation, possibly empty.
*
* @param array of names
*/
private String[] getLocalVariableNames() {
return fLocalVariableNames;
}
/**
* Sets the names of the local variables
* visible in this evaluation, possibly empty.
*
* @param localVariableNames array of names
*/
private void setLocalVariableNames(String[] localVariableNames) {
fLocalVariableNames = localVariableNames;
}
/**
* Returns the type names of the local variables
* visible in this evaluation, possibly empty.
*
* @param array of type names
*/
private String[] getLocalVariableTypeNames() {
return fLocalVariableTypeNames;
}
/**
* Sets the type names of the local variables
* visible in this evaluation, possibly empty.
*
* @param localVariableTypeNames array of type names
*/
private void setLocalVariableTypeNames(String[] localVariableTypeNames) {
fLocalVariableTypeNames = localVariableTypeNames;
}
/**
* Sets the receiver context for the associated evaluation,
* possibly <code>null</code> if the evaluation is
* in the context of a static method or there is
* no object context.
*
* @param thisObject the receiver content of the
* associated evaluation, or <code>null</code>
*/
private void setThis(IJavaObject thisObject) {
fThis = thisObject;
}
/**
* Returns the receiver context for the associated evaluation,
* or <code>null</code> if the evaluation is
* in the context of a static method or there is
* no object context.
*
* @return the receiver context of the associated
* evaluation or <code>null</code>
*/
private IJavaObject getThis() {
return fThis;
}
/**
* Returns a copy of the type name with '$' replaced by
* '.', or returns <code>null</code> if the given type
* name refers to an anonymous inner class.
*
* @param typeName a fully qualified type name
* @return a copy of the type name with '$' replaced by
* '.', or returns <code>null</code> if the given type
* name refers to an anonymous inner class.
*/
protected String getTranslatedTypeName(String typeName) {
int index = typeName.lastIndexOf('$');
if (index == -1) {
return typeName;
}
if (index + 1 > typeName.length()) {
// invalid name
return typeName;
}
String last = typeName.substring(index + 1);
try {
Integer.parseInt(last);
return null;
} catch (NumberFormatException e) {
return typeName.replace('$', '.');
}
}
/**
* Returns the receiving type of the the given stack frame.
*
* @return receiving type
* @exception DebugException if:<ul>
* <li>A failure occurs while accessing attributes of
* the stack frame</li>
* <li>the resolved type is an inner type</li>
* <li>unable to resolve a type</li>
* </ul>
*/
private IType getReceivingType(IJavaStackFrame frame) throws DebugException {
String typeName = frame.getReceivingTypeName();
String sourceName =frame.getSourceName();
if (sourceName == null || !typeName.equals(frame.getDeclaringTypeName())) {
// if there is no debug attribute or the declaring type is not the
// same as the receiving type, we must guess at the receiver's source
// file
int dollarIndex= typeName.indexOf('$');
if (dollarIndex >= 0) {
typeName= typeName.substring(0, dollarIndex);
}
typeName = typeName.replace('.', IPath.SEPARATOR);
typeName+= ".java"; //$NON-NLS-1$
} else {
int index = typeName.lastIndexOf('.');
if (index >= 0) {
typeName = typeName.substring(0, index + 1);
typeName = typeName.replace('.', IPath.SEPARATOR);
} else {
typeName = ""; //$NON-NLS-1$
}
typeName+=sourceName;
}
IPath sourcePath = new Path(typeName);
IType type = null;
try {
IJavaElement result = getJavaProject().findElement(sourcePath);
String[] typeNames = getNestedTypeNames(frame.getReceivingTypeName());
if (result != null) {
if (result instanceof IClassFile) {
type = ((IClassFile)result).getType();
} else if (result instanceof ICompilationUnit) {
type = ((ICompilationUnit)result).getType(typeNames[0]);
}
}
for (int i = 1; i < typeNames.length; i++) {
type = type.getType(typeNames[i]);
}
} catch (JavaModelException e) {
throw new DebugException(e.getStatus());
}
if (type == null) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_unable_to_determine_receiving_type_context._18"), null) //$NON-NLS-1$
);
}
if (type.getDeclaringType() != null) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_in_context_of_inner_type_not_supported._19"), null) //$NON-NLS-1$
);
}
return type;
}
/**
* Returns the type of the the given object.
*
* @return type
* @exception DebugException if:<ul>
* <li>A failure occurs while accessing attributes of
* the object</li>
* </ul>
*/
private IType getReceivingType(IJavaObject object) throws DebugException {
String typeName = object.getJavaType().getName();
// we must guess at the receiver's source file name
int dollarIndex= typeName.indexOf('$');
if (dollarIndex >= 0) {
typeName= typeName.substring(0, dollarIndex);
}
return getType(typeName);
}
/**
* Returns the type associated with the specified
* name in this evaluation engine's associated Java project.
*
* @param typeName fully qualified name of type, for
* example, <code>java.lang.String</code>
* @return main type associated with source file
* @exception DebugException if:<ul>
* <li>the resolved type is an inner type</li>
* <li>unable to resolve a type</li>
* <li>a lower level Java exception occurs</li>
* </ul>
*/
private IType getType(String typeName) throws DebugException {
String path = typeName.replace('.', IPath.SEPARATOR);
path+= ".java"; //$NON-NLS-1$
IPath sourcePath = new Path(path);
IType type = null;
try {
IJavaElement result = getJavaProject().findElement(sourcePath);
String[] typeNames = getNestedTypeNames(typeName);
if (result != null) {
if (result instanceof IClassFile) {
type = ((IClassFile)result).getType();
} else if (result instanceof ICompilationUnit) {
type = ((ICompilationUnit)result).getType(typeNames[0]);
}
}
for (int i = 1; i < typeNames.length; i++) {
type = type.getType(typeNames[i]);
}
} catch (JavaModelException e) {
throw new DebugException(e.getStatus());
}
if (type == null) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_failed_-_unable_to_determine_receiving_type_context._18"), null) //$NON-NLS-1$
);
}
if (type.getParent() instanceof IType) {
throw new DebugException(
new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED, EvaluationMessages.getString("LocalEvaluationEngine.Evaluation_in_context_of_inner_type_not_supported._19"), null) //$NON-NLS-1$
);
}
return type;
}
/**
* Returns an array of simple type names that are
* part of the given type's qualified name. For
* example, if the given name is <code>x.y.A$B</code>,
* an array with <code>["A", "B"]</code> is returned.
*
* @param typeName fully qualified type name
* @return array of nested type names
*/
protected String[] getNestedTypeNames(String typeName) {
int index = typeName.lastIndexOf('.');
if (index >= 0) {
typeName= typeName.substring(index + 1);
}
index = typeName.indexOf('$');
ArrayList list = new ArrayList(1);
while (index >= 0) {
list.add(typeName.substring(0, index));
typeName = typeName.substring(index + 1);
index = typeName.indexOf('$');
}
list.add(typeName);
return (String[])list.toArray(new String[list.size()]);
}
/**
* @see IClassFileEvaluationEngine#getImports()
*/
public String[] getImports() {
return getEvaluationContext().getImports();
}
/**
* @see IClassFileEvaluationEngine#setImports(String[])
*/
public void setImports(String[] imports) {
getEvaluationContext().setImports(imports);
}
/**
* Sets the name of the code snippet to instantiate
* to run the current evaluation.
*
* @param name the name of the deployed code snippet
* to instantiate and run
*/
private void setCodeSnippetClassName(String name) {
fCodeSnippetClassName = name;
}
/**
* Returns the name of the code snippet to instantiate
* to run the current evaluation.
*
* @return the name of the deployed code snippet
* to instantiate and run
*/
protected String getCodeSnippetClassName() {
return fCodeSnippetClassName;
}
/**
* @see ICodeSnippetRequestor#isRequestingClassFiles()
*/
public boolean isRequestingClassFiles() {
return true;
}
/**
* Returns whether to hit breakpoints in the evaluation thread.
*
* @return whether to hit breakpoints in the evaluation thread
*/
protected boolean getHitBreakpoints() {
return fHitBreakpoints;
}
/**
* Sets whether to hit breakpoints in the evaluation thread.
* @param hit whether to hit breakpoints in the evaluation thread
*/
private void setHitBreakpoints(boolean hit) {
fHitBreakpoints = hit;
}
}