blob: 4e64283eb0f0fab4e582792f23b0b90fc46b060b [file] [log] [blame]
/*
* (c) Copyright IBM Corp. 2002.
* All Rights Reserved.
*/
package org.eclipse.jdt.internal.debug.eval.ast.engine;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Message;
import org.eclipse.jdt.debug.core.IEvaluationRunnable;
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.IJavaValue;
import org.eclipse.jdt.debug.core.IJavaVariable;
import org.eclipse.jdt.debug.eval.IAstEvaluationEngine;
import org.eclipse.jdt.debug.eval.ICompiledExpression;
import org.eclipse.jdt.debug.eval.IEvaluationListener;
import org.eclipse.jdt.internal.debug.eval.EvaluationResult;
import org.eclipse.jdt.internal.debug.eval.ast.instructions.InstructionSequence;
public class ASTEvaluationEngine implements IAstEvaluationEngine {
private IJavaProject fProject;
private IJavaDebugTarget fDebugTarget;
private List fEvaluationThreads= new ArrayList();
public ASTEvaluationEngine(IJavaProject project, IJavaDebugTarget debugTarget) {
setJavaProject(project);
setDebugTarget(debugTarget);
}
public void setJavaProject(IJavaProject project) {
fProject = project;
}
public void setDebugTarget(IJavaDebugTarget debugTarget) {
fDebugTarget = debugTarget;
}
/**
* @see IEvaluationEngine#evaluate(String, IJavaStackFrame, IEvaluationListener)
*/
public void evaluate(String snippet, IJavaStackFrame frame, IEvaluationListener listener, int evaluationDetail) throws DebugException {
ICompiledExpression expression= getCompiledExpression(snippet, frame);
evaluateExpression(expression, frame, listener, evaluationDetail);
}
/**
* @see IEvaluationEngine#evaluate(String, IJavaObject, IJavaThread, IEvaluationListener)
*/
public void evaluate(String snippet, IJavaObject thisContext, IJavaThread thread, IEvaluationListener listener, int evaluationDetail) throws DebugException {
ICompiledExpression expression= getCompiledExpression(snippet, thisContext);
evaluateExpression(expression, thisContext, thread, listener, evaluationDetail);
}
/**
* @see IEvaluationEngine#evaluate(ICompiledExpression, IJavaStackFrame, IEvaluationListener)
*/
public void evaluateExpression(ICompiledExpression expression, IJavaStackFrame frame, IEvaluationListener listener, int evaluationDetail) throws DebugException {
RuntimeContext context = new RuntimeContext(getJavaProject(), frame);
doEvaluation(expression, context, (IJavaThread)frame.getThread(), listener, evaluationDetail);
}
/**
* @see IEvaluationEngine#evaluate(ICompiledExpression, IJavaObject, IJavaThread, IEvaluationListener)
*/
public void evaluateExpression(ICompiledExpression expression, IJavaObject thisContext, IJavaThread thread, IEvaluationListener listener, int evaluationDetail) throws DebugException {
IRuntimeContext context = new JavaObjectRuntimeContext(thisContext, getJavaProject(), thread);
doEvaluation(expression, context, thread, listener, evaluationDetail);
}
/**
* Evaluates the given expression in the given thread and the given runtime context.
*/
private void doEvaluation(ICompiledExpression expression, IRuntimeContext context, IJavaThread thread, IEvaluationListener listener, int evaluationDetail) throws DebugException {
getEvaluationThread().evaluate(expression, context, thread, listener, evaluationDetail);
}
private EvaluationThread getEvaluationThread() {
Iterator iter= fEvaluationThreads.iterator();
EvaluationThread thread= null;
while (iter.hasNext()) {
thread= (EvaluationThread)iter.next();
if (!thread.isEvaluating()) {
return thread;
}
}
thread= new EvaluationThread();
fEvaluationThreads.add(thread);
return thread;
}
/**
* Notifies this evaluation engine that the given evaluation thread
* has completed an evaluation. If there are any threads available
* (not currently evaluating), the given thread is stopped. Otherwise
* the thread is allowed to keep running - it will be reused for the
* next evaluation.
*/
private void evaluationThreadFinished(EvaluationThread thread) {
if (fEvaluationThreads.size() == 1) {
// Always leave at least one thread running
return;
}
boolean allBusy= true;
Iterator iter= fEvaluationThreads.iterator();
EvaluationThread tempThread= null;
while (iter.hasNext()) {
tempThread= (EvaluationThread)iter.next();
if (!tempThread.isEvaluating()) {
// Another thread is available. The given thread
// can be stopped
allBusy= false;
break;
}
}
if (!allBusy) {
thread.stop();
fEvaluationThreads.remove(thread);
}
}
class EvaluationThread {
private ICompiledExpression fExpression;
private IRuntimeContext fContext;
private IJavaThread fThread;
private IEvaluationListener fListener;
private int fEvaluationDetail;
private boolean fEvaluating= false;
private Thread fEvaluationThread;
private boolean fStopped= false;
private Object fLock= new Object();
public boolean isEvaluating() {
return fEvaluating;
}
public void stop() {
fStopped= true;
synchronized (fLock) {
fLock.notify();
}
}
public void evaluate(ICompiledExpression expression, IRuntimeContext context, IJavaThread thread, IEvaluationListener listener, int evaluationDetail) {
fExpression= expression;
fContext= context;
fThread= thread;
fListener= listener;
fEvaluationDetail= evaluationDetail;
if (fEvaluationThread == null) {
// Create a new thread
fEvaluationThread= new Thread(new Runnable() {
public void run() {
while (!fStopped) {
synchronized (fLock) {
doEvaluation();
try {
// Sleep until the next evaluation
fLock.wait();
} catch (InterruptedException exception) {
}
}
}
}
}, "Evaluation thread"); //$NON-NLS-1$
fEvaluationThread.start();
} else {
// Use the existing thread
synchronized (fLock) {
fLock.notify();
}
}
}
public synchronized void doEvaluation() {
fEvaluating= true;
EvaluationResult result = new EvaluationResult(ASTEvaluationEngine.this, fExpression.getSnippet(), fThread);
if (fExpression.hasErrors()) {
Message[] errors= fExpression.getErrors();
for (int i= 0, numErrors= errors.length; i < numErrors; i++) {
result.addError(errors[i]);
}
fListener.evaluationComplete(result);
return;
}
final IJavaValue[] valuez = new IJavaValue[1];
final InstructionSequence instructionSet = (InstructionSequence)fExpression;
IEvaluationRunnable er = new IEvaluationRunnable() {
public void run(IJavaThread jt, IProgressMonitor pm) {
valuez[0] = instructionSet.evaluate(fContext);
}
};
CoreException exception = null;
try {
fThread.runEvaluation(er, null, fEvaluationDetail);
} catch (DebugException e) {
exception = e;
}
IJavaValue value = valuez[0];
if (exception == null) {
exception= instructionSet.getException();
}
if (value != null) {
result.setValue(value);
}
if (exception != null) {
result.setException(new DebugException(exception.getStatus()));
}
fEvaluating= false;
evaluationThreadFinished(this);
fListener.evaluationComplete(result);
}
}
/**
* @see IEvaluationEngine#getCompiledExpression(String, IJavaStackFrame)
*/
public ICompiledExpression getCompiledExpression(String snippet, IJavaStackFrame frame) {
IJavaProject javaProject = getJavaProject();
RuntimeContext context = new RuntimeContext(javaProject, frame);
EvaluationSourceGenerator mapper = null;
CompilationUnit unit = null;
try {
IJavaVariable[] localsVar = context.getLocals();
int numLocalsVar= localsVar.length;
// ******
// to hide problems with local variable declare as instance of Local Types
IJavaVariable[] locals= new IJavaVariable[numLocalsVar];
int numLocals= 0;
for (int i = 0; i < numLocalsVar; i++) {
if (!isLocalType(localsVar[i].getReferenceTypeName())) {
locals[numLocals++]= localsVar[i];
}
}
// to solve and remove
// ******
int[] localModifiers = new int[numLocals];
String[] localTypesNames= new String[numLocals];
String[] localVariables= new String[numLocals];
for (int i = 0; i < numLocals; i++) {
localVariables[i] = locals[i].getName();
localTypesNames[i] = locals[i].getReferenceTypeName();
localModifiers[i]= 0;
}
mapper = new EvaluationSourceGenerator(new String[0], localModifiers, localTypesNames, localVariables, snippet);
unit = AST.parseCompilationUnit(mapper.getSource(frame).toCharArray(), mapper.getCompilationUnitName(), javaProject);
} catch (CoreException e) {
InstructionSequence expression= new InstructionSequence(snippet);
expression.addError(new Message(e.getStatus().getMessage(), 1));
return expression;
}
return createExpressionFromAST(snippet, mapper, unit);
}
// ******
// for hide problems with local variable declare as instance of Local Types
private boolean isLocalType(String typeName) {
StringTokenizer strTok= new StringTokenizer(typeName,"$"); //$NON-NLS-1$
strTok.nextToken();
while (strTok.hasMoreTokens()) {
char char0= strTok.nextToken().charAt(0);
if ('0' <= char0 && char0 <= '9') {
return true;
}
}
return false;
}
// ******
/**
* @see IEvaluationEngine#getCompiledExpression(String, IJavaObject, IJavaThread)
*/
public ICompiledExpression getCompiledExpression(String snippet, IJavaObject thisContext) {
IJavaProject javaProject = getJavaProject();
EvaluationSourceGenerator mapper = null;
CompilationUnit unit = null;
mapper = new EvaluationSourceGenerator(new String[0], new int[0], new String[0], new String[0], snippet);
try {
unit = AST.parseCompilationUnit(mapper.getSource(thisContext, javaProject).toCharArray(), mapper.getCompilationUnitName(), javaProject);
} catch (CoreException e) {
InstructionSequence expression= new InstructionSequence(snippet);
expression.addError(new Message(e.getStatus().getMessage(), 1));
return expression;
}
return createExpressionFromAST(snippet, mapper, unit);
}
/**
* Creates a compiled expression for the given snippet using the given mapper and
* compiliation unit (AST).
* @param snippet the code snippet to be compiled
* @param mapper the object which will be used to create the expression
* @param unit the compilation unit (AST) generated for the snippet
*/
private ICompiledExpression createExpressionFromAST(String snippet, EvaluationSourceGenerator mapper, CompilationUnit unit) {
Message[] messages= unit.getMessages();
if (messages.length != 0) {
boolean error= false;
InstructionSequence errorSequence= new InstructionSequence(snippet);
int codeSnippetStartOffset= mapper.getStartPosition();
int codeSnippetEndOffset= codeSnippetStartOffset + snippet.length();
for (int i = 0; i < messages.length; i++) {
Message message= messages[i];
int errorOffset= message.getSourcePosition();
// TO DO: Internationalize "void method..." error message check
if (codeSnippetStartOffset <= errorOffset && errorOffset <= codeSnippetEndOffset && !EngineEvaluationMessages.getString("ASTEvaluationEngine.Void_methods_cannot_return_a_value_1").equals(message.getMessage())) { //$NON-NLS-1$
errorSequence.addError(message);
error = true;
}
}
if (error) {
return errorSequence;
}
}
ASTInstructionCompiler visitor = new ASTInstructionCompiler(mapper.getStartPosition(), snippet);
unit.accept(visitor);
return visitor.getInstructions();
}
/*
* @see IEvaluationEngine#getJavaProject()
*/
public IJavaProject getJavaProject() {
return fProject;
}
/*
* @see IEvaluationEngine#getDebugTarget()
*/
public IJavaDebugTarget getDebugTarget() {
return fDebugTarget;
}
/*
* @see IEvaluationEngine#dispose()
*/
public void dispose() {
// Stop all evaluation threads.
Iterator iter= fEvaluationThreads.iterator();
while (iter.hasNext()) {
((EvaluationThread)iter.next()).stop();
}
}
protected void finalize() throws Throwable {
dispose();
super.finalize();
}
}