0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.debug.eval;
0011:
0012: import java.io.File;
0013: import java.io.FileOutputStream;
0014: import java.io.IOException;
0015: import com.ibm.icu.text.MessageFormat;
0016: import java.util.ArrayList;
0017: import java.util.Arrays;
0018: import java.util.Collections;
0019: import java.util.Iterator;
0020: import java.util.List;
0021:
0022: import org.eclipse.core.resources.IMarker;
0023: import org.eclipse.core.runtime.CoreException;
0024: import org.eclipse.core.runtime.IProgressMonitor;
0025: import org.eclipse.core.runtime.IStatus;
0026: import org.eclipse.core.runtime.Status;
0027: import org.eclipse.debug.core.DebugEvent;
0028: import org.eclipse.debug.core.DebugException;
0029: import org.eclipse.debug.core.model.IVariable;
0030: import org.eclipse.jdt.core.IJavaProject;
0031: import org.eclipse.jdt.core.IType;
0032: import org.eclipse.jdt.core.JavaModelException;
0033: import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
0034: import org.eclipse.jdt.core.eval.IEvaluationContext;
0035: import org.eclipse.jdt.debug.core.IEvaluationRunnable;
0036: import org.eclipse.jdt.debug.core.IJavaClassObject;
0037: import org.eclipse.jdt.debug.core.IJavaClassType;
0038: import org.eclipse.jdt.debug.core.IJavaDebugTarget;
0039: import org.eclipse.jdt.debug.core.IJavaObject;
0040: import org.eclipse.jdt.debug.core.IJavaStackFrame;
0041: import org.eclipse.jdt.debug.core.IJavaThread;
0042: import org.eclipse.jdt.debug.core.IJavaType;
0043: import org.eclipse.jdt.debug.core.IJavaValue;
0044: import org.eclipse.jdt.debug.core.IJavaVariable;
0045: import org.eclipse.jdt.debug.core.JDIDebugModel;
0046: import org.eclipse.jdt.debug.eval.IClassFileEvaluationEngine;
0047: import org.eclipse.jdt.debug.eval.IEvaluationListener;
0048: import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
0049: import org.eclipse.jdt.internal.debug.core.JavaDebugUtils;
0050: import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
0051: import org.eclipse.jdt.internal.debug.core.model.JDIValue;
0052:
0053: import com.sun.jdi.InvocationException;
0054: import com.sun.jdi.ObjectReference;
0055:
0056: /**
0057: * An evaluation engine that deploys class files locally
0058: */
0059:
0060: public class LocalEvaluationEngine implements
0061: IClassFileEvaluationEngine, ICodeSnippetRequestor,
0062: IEvaluationRunnable {
0063:
0064: private static final String CODE_SNIPPET_NAME = "CodeSnippet.class"; //$NON-NLS-1$
0065:
0066: /**
0067: * A count of the number of engines created.
0068: * Count is incremented on instantiation and decremented on
0069: * dispose. When the count == 0, the special CodeSnippet.class
0070: * is deleted as this class file is shared by all.
0071: */
0072: private static int ENGINE_COUNT = 0;
0073:
0074: /**
0075: * The Java project context in which to compile snippets.
0076: */
0077: private IJavaProject fJavaProject;
0078:
0079: /**
0080: * The debug target on which to execute snippets
0081: */
0082: private IJavaDebugTarget fDebugTarget;
0083:
0084: /**
0085: * The location in which to deploy snippet class files
0086: */
0087: private File fOutputDirectory;
0088:
0089: /**
0090: * The listener to notify when the current evaluation
0091: * is complete.
0092: */
0093: private IEvaluationListener fListener;
0094:
0095: /**
0096: * The stack frame context for the current evaluation
0097: * or <code>null</code> if there is no stack frame
0098: * context.
0099: */
0100: private IJavaStackFrame fStackFrame;
0101:
0102: /**
0103: * The result of this evaluation
0104: */
0105: private EvaluationResult fResult;
0106:
0107: /**
0108: * Collection of deployed snippet class files
0109: */
0110: private List fSnippetFiles;
0111:
0112: /**
0113: * Collection of directories created by this evaluation
0114: * engine.
0115: */
0116: private List fDirectories;
0117:
0118: /**
0119: * Evaluation context for the Java project associated
0120: * with this evaluation engine.
0121: */
0122: private IEvaluationContext fEvaluationContext;
0123:
0124: /**
0125: * Array of modifier constants for visible local variables
0126: * in the current evaluation.
0127: *
0128: * XXX: constants should be 'default' or 'final'. Where
0129: * are these constants defined.
0130: */
0131: private int[] fLocalVariableModifiers;
0132:
0133: /**
0134: * Array of names of visible local variables
0135: * in the current evaluation.
0136: */
0137: private String[] fLocalVariableNames;
0138:
0139: /**
0140: * Array of type names of visible local variables
0141: * in the current evaluation.
0142: */
0143: private String[] fLocalVariableTypeNames;
0144:
0145: /**
0146: * The 'this' object for the current evaluation
0147: * or <code>null</code> if there is no 'this'
0148: * context (static method, or not context)
0149: */
0150: private IJavaObject fThis;
0151:
0152: /**
0153: * Whether this engine has been disposed.
0154: */
0155: private boolean fDisposed = false;
0156:
0157: /**
0158: * The number of evaluations currently being
0159: * performed.
0160: */
0161: private int fEvaluationCount = 0;
0162:
0163: /**
0164: * The name of the code snippet class to instantiate
0165: */
0166: private String fCodeSnippetClassName = null;
0167:
0168: /**
0169: * Whether to hit breakpoints in the evaluation thread
0170: */
0171: private boolean fHitBreakpoints = false;
0172:
0173: /**
0174: * Constant for empty array of <code>java.lang.String</code>
0175: */
0176: private static final String[] EMPTY_STRING_ARRAY = new String[0];
0177:
0178: /**
0179: * Constant for empty array of <code>int</code>
0180: */
0181: private static final int[] EMPTY_INT_ARRAY = new int[0];
0182:
0183: /**
0184: * Cosntructs a new evaluation engine for the given VM in the context
0185: * of the specified project. Class files required for the evaluation will
0186: * be deployed to the specified directory (which must be on the class
0187: * path of the VM in order for evaluation to work).
0188: *
0189: * @param project context in which to compile snippets
0190: * @param vm debug target in which to evaluate snippets
0191: * @param directory location where snippet class files will
0192: * be deployed for execution. The directory must exist
0193: */
0194: public LocalEvaluationEngine(IJavaProject project,
0195: IJavaDebugTarget vm, File directory) {
0196: setJavaProject(project);
0197: setDebugTarget(vm);
0198: setOutputDirectory(directory);
0199: ENGINE_COUNT++;
0200: }
0201:
0202: /**
0203: * @see ICodeSnippetRequestor#acceptClassFiles(byte[][], String[][], String)
0204: */
0205: public boolean acceptClassFiles(byte[][] classFileBytes,
0206: String[][] classFileCompoundNames,
0207: String codeSnippetClassName) {
0208: try {
0209: deploy(classFileBytes, classFileCompoundNames);
0210: } catch (DebugException e) {
0211: getResult().setException(e);
0212: return false;
0213: }
0214: if (codeSnippetClassName != null) {
0215: setCodeSnippetClassName(codeSnippetClassName);
0216: try {
0217: getThread().runEvaluation(this , null,
0218: DebugEvent.EVALUATION, getHitBreakpoints());
0219: } catch (DebugException e) {
0220: // exception handling is in evaluation runnable
0221: }
0222: }
0223: return true;
0224: }
0225:
0226: public void run(IJavaThread thread, IProgressMonitor monitor) {
0227: IJavaObject codeSnippetInstance = null;
0228: try {
0229: codeSnippetInstance = newInstance(getCodeSnippetClassName());
0230: initializeLocals(codeSnippetInstance);
0231: codeSnippetInstance.sendMessage(RUN_METHOD,
0232: "()V", null, getThread(), false); //$NON-NLS-1$
0233: restoreLocals(codeSnippetInstance);
0234:
0235: // now retrieve the description of the result
0236: IVariable[] fields = codeSnippetInstance.getVariables();
0237: IJavaVariable resultValue = null;
0238: IJavaVariable resultType = null;
0239: for (int i = 0; i < fields.length; i++) {
0240: if (fields[i].getName().equals(RESULT_TYPE_FIELD)) {
0241: resultType = (IJavaVariable) fields[i];
0242: }
0243: if (fields[i].getName().equals(RESULT_VALUE_FIELD)) {
0244: resultValue = (IJavaVariable) fields[i];
0245: }
0246: }
0247: IJavaValue result = convertResult(
0248: (IJavaClassObject) resultType.getValue(),
0249: (IJavaValue) resultValue.getValue());
0250: getResult().setValue(result);
0251: } catch (DebugException e) {
0252: getResult().setException(e);
0253:
0254: Throwable underlyingException = e.getStatus()
0255: .getException();
0256: if (underlyingException instanceof InvocationException) {
0257: ObjectReference theException = ((InvocationException) underlyingException)
0258: .exception();
0259: if (theException != null) {
0260: try {
0261: try {
0262: IJavaObject v = (IJavaObject) JDIValue
0263: .createValue(
0264: (JDIDebugTarget) getDebugTarget(),
0265: theException);
0266: v
0267: .sendMessage(
0268: "printStackTrace", "()V", null, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
0269: } catch (DebugException de) {
0270: JDIDebugPlugin.log(de);
0271: }
0272: } catch (RuntimeException re) {
0273: JDIDebugPlugin.log(re);
0274: }
0275: }
0276: }
0277: }
0278:
0279: }
0280:
0281: /**
0282: * Initializes the value of instance variables in the
0283: * 'code snippet object' that are used as placeholders
0284: * for locals and 'this' in the current stack frame.
0285: *
0286: * @param object instance of code snippet class that will
0287: * be run
0288: * @exception DebugException if an exception is thrown
0289: * accessing the given object
0290: */
0291: protected void initializeLocals(IJavaObject object)
0292: throws DebugException {
0293: IJavaVariable[] locals = null;
0294: IJavaObject this Object = getThis();
0295: if (getStackFrame() != null) {
0296: locals = getStackFrame().getLocalVariables();
0297: }
0298: if (locals != null) {
0299: for (int i = 0; i < locals.length; i++) {
0300: IJavaVariable local = locals[i];
0301: IJavaVariable field = object.getField(LOCAL_VAR_PREFIX
0302: + local.getName(), false);
0303: // internal error if field is not found
0304: if (field == null) {
0305: throw new DebugException(
0306: new Status(
0307: IStatus.ERROR,
0308: JDIDebugModel.getPluginIdentifier(),
0309: DebugException.REQUEST_FAILED,
0310: EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__4,
0311: null));
0312: }
0313: field.setValue(local.getValue());
0314: }
0315: }
0316: if (this Object != null) {
0317: IJavaVariable field = object.getField(DELEGATE_THIS, false);
0318: // internal error if field is not found
0319: if (field == null) {
0320: throw new DebugException(
0321: new Status(
0322: IStatus.ERROR,
0323: JDIDebugModel.getPluginIdentifier(),
0324: DebugException.REQUEST_FAILED,
0325: EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_initialize___this ___context__5,
0326: null));
0327: }
0328: field.setValue(this Object);
0329: }
0330: }
0331:
0332: /**
0333: * Restores the value local variables from the instance
0334: * variables in the 'code snippet object' that are used
0335: * as placeholders for locals in the current stack frame.
0336: *
0337: * @param object instance of code snippet class that was
0338: * run
0339: * @exception DebugException if an exception is thrown
0340: * accessing the given object
0341: */
0342: protected void restoreLocals(IJavaObject object)
0343: throws DebugException {
0344: IJavaVariable[] locals = null;
0345: if (getStackFrame() != null) {
0346: locals = getStackFrame().getLocalVariables();
0347: }
0348: if (locals != null) {
0349: for (int i = 0; i < locals.length; i++) {
0350: IJavaVariable local = locals[i];
0351: IJavaVariable field = object.getField(LOCAL_VAR_PREFIX
0352: + local.getName(), false);
0353: // internal error if field is not found
0354: if (field == null) {
0355: throw new DebugException(
0356: new Status(
0357: IStatus.ERROR,
0358: JDIDebugModel.getPluginIdentifier(),
0359: DebugException.REQUEST_FAILED,
0360: EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__6,
0361: null));
0362: }
0363: local.setValue(field.getValue());
0364: }
0365: }
0366: }
0367:
0368: /**
0369: * @see ICodeSnippetRequestor#acceptProblem(IMarker, String, int)
0370: */
0371: public void acceptProblem(IMarker problemMarker,
0372: String fragmentSource, int fragmentKind) {
0373: if (problemMarker.getAttribute(IMarker.SEVERITY, -1) != IMarker.SEVERITY_ERROR) {
0374: return;
0375: }
0376: getResult().addError(
0377: problemMarker.getAttribute(IMarker.MESSAGE, "")); //$NON-NLS-1$
0378: }
0379:
0380: /**
0381: * @see IEvaluationEngine#getDebugTarget()
0382: */
0383: public IJavaDebugTarget getDebugTarget() {
0384: return fDebugTarget;
0385: }
0386:
0387: /**
0388: * Sets the debug target in which snippets are executed.
0389: *
0390: * @param debugTarget the debug target in which snippets are executed
0391: */
0392: private void setDebugTarget(IJavaDebugTarget debugTarget) {
0393: fDebugTarget = debugTarget;
0394: }
0395:
0396: /**
0397: * @see IEvaluationEngine#getJavaProject()
0398: */
0399: public IJavaProject getJavaProject() {
0400: return fJavaProject;
0401: }
0402:
0403: /**
0404: * Sets the Java project in which snippets are compiled.
0405: *
0406: * @param javaProject the Java project in which snippets are compiled
0407: */
0408: private void setJavaProject(IJavaProject javaProject) {
0409: fJavaProject = javaProject;
0410: }
0411:
0412: /**
0413: * Returns the directory in which snippet class files are deployed.
0414: *
0415: * @return the directory in which snippet class files are deployed.
0416: */
0417: public File getOutputDirectory() {
0418: return fOutputDirectory;
0419: }
0420:
0421: /**
0422: * Sets the directory in which snippet class files are
0423: * deployed.
0424: *
0425: * @param outputDirectory location to deploy snippet class files
0426: */
0427: private void setOutputDirectory(File outputDirectory) {
0428: fOutputDirectory = outputDirectory;
0429: }
0430:
0431: /**
0432: * @see IClassFileEvaluationEngine#evaluate(String, IJavaThread, IEvaluationListener)
0433: */
0434: public void evaluate(String snippet, IJavaThread thread,
0435: IEvaluationListener listener, boolean hitBreakpoints)
0436: throws DebugException {
0437: checkDisposed();
0438: checkEvaluating();
0439: try {
0440: evaluationStarted();
0441: setListener(listener);
0442: setHitBreakpoints(hitBreakpoints);
0443: setResult(new EvaluationResult(this , snippet, thread));
0444: checkThread();
0445: // no receiver/stack frame context
0446: setThis(null);
0447: setLocalVariableNames(EMPTY_STRING_ARRAY);
0448: setLocalVariableTypeNames(EMPTY_STRING_ARRAY);
0449: setLocalVariableModifiers(EMPTY_INT_ARRAY);
0450:
0451: // do the evaluation in a different thread
0452: Runnable r = new Runnable() {
0453: public void run() {
0454: try {
0455: LocalEvaluationEngine.this
0456: .getEvaluationContext()
0457: .evaluateCodeSnippet(
0458: LocalEvaluationEngine.this
0459: .getSnippet(),
0460: LocalEvaluationEngine.this ,
0461: null);
0462: } catch (JavaModelException e) {
0463: LocalEvaluationEngine.this .getResult()
0464: .setException(
0465: new DebugException(e
0466: .getStatus()));
0467: } finally {
0468: LocalEvaluationEngine.this .evaluationComplete();
0469: }
0470: }
0471: };
0472:
0473: Thread t = new Thread(r);
0474: t.setDaemon(true);
0475: t.start();
0476: } catch (DebugException d) {
0477: evaluationAborted();
0478: throw d;
0479: }
0480:
0481: }
0482:
0483: /**
0484: * @see IEvaluationEngine#evaluate(String, IJavaStackFrame, IEvaluationListener, int)
0485: */
0486: public void evaluate(String snippet, IJavaStackFrame frame,
0487: IEvaluationListener listener, int evaluationDetail,
0488: boolean hitBreakpoints) throws DebugException {
0489: checkDisposed();
0490: checkEvaluating();
0491: try {
0492: evaluationStarted();
0493: setListener(listener);
0494: setStackFrame(frame);
0495: setHitBreakpoints(hitBreakpoints);
0496: setResult(new EvaluationResult(this , snippet,
0497: (IJavaThread) frame.getThread()));
0498: checkThread();
0499:
0500: // set up local variables and 'this' context for evaluation
0501: IJavaVariable[] locals = frame.getLocalVariables();
0502:
0503: List typeNames = new ArrayList(locals.length);
0504: List varNames = new ArrayList(locals.length);
0505:
0506: for (int i = 0; i < locals.length; i++) {
0507: IJavaVariable var = locals[i];
0508: String typeName = getTranslatedTypeName(var
0509: .getReferenceTypeName());
0510: if (typeName != null) {
0511: typeNames.add(typeName);
0512: varNames.add(var.getName());
0513: }
0514: }
0515:
0516: setLocalVariableTypeNames((String[]) typeNames
0517: .toArray(new String[typeNames.size()]));
0518: setLocalVariableNames((String[]) varNames
0519: .toArray(new String[varNames.size()]));
0520: int[] modifiers = new int[typeNames.size()];
0521: // cannot determine if local is final, so specify as default
0522: Arrays.fill(modifiers, 0);
0523: setLocalVariableModifiers(modifiers);
0524: setThis(frame.getThis());
0525:
0526: final boolean isStatic = frame.isStatic();
0527: final boolean isConstructor = frame.isConstructor();
0528: final IType receivingType = JavaDebugUtils
0529: .resolveDeclaringType(frame);
0530: validateReceivingType(receivingType);
0531:
0532: // do the evaluation in a different thread
0533: Runnable r = new Runnable() {
0534: public void run() {
0535: try {
0536: LocalEvaluationEngine.this
0537: .getEvaluationContext()
0538: .evaluateCodeSnippet(
0539: LocalEvaluationEngine.this
0540: .getSnippet(),
0541: LocalEvaluationEngine.this
0542: .getLocalVariableTypeNames(),
0543: LocalEvaluationEngine.this
0544: .getLocalVariableNames(),
0545: LocalEvaluationEngine.this
0546: .getLocalVariableModifiers(),
0547: receivingType, isStatic,
0548: isConstructor,
0549: LocalEvaluationEngine.this ,
0550: null);
0551: } catch (JavaModelException e) {
0552: LocalEvaluationEngine.this .getResult()
0553: .setException(
0554: new DebugException(e
0555: .getStatus()));
0556: } finally {
0557: LocalEvaluationEngine.this .evaluationComplete();
0558: }
0559: }
0560: };
0561:
0562: Thread t = new Thread(r);
0563: t.setDaemon(true);
0564: t.start();
0565: } catch (DebugException d) {
0566: evaluationAborted();
0567: throw d;
0568: } catch (CoreException e) {
0569: evaluationAborted();
0570: throw new DebugException(e.getStatus());
0571: }
0572: }
0573:
0574: /**
0575: * Verifies the receiving type was resovled and is not an inner type.
0576: *
0577: * @param receivingType
0578: * @throws DebugException
0579: */
0580: private void validateReceivingType(final IType receivingType)
0581: throws DebugException {
0582: if (receivingType == null) {
0583: throw new DebugException(
0584: new Status(
0585: IStatus.ERROR,
0586: JDIDebugModel.getPluginIdentifier(),
0587: DebugException.REQUEST_FAILED,
0588: EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_determine_receiving_type_context__18,
0589: null));
0590: }
0591:
0592: if (receivingType.getDeclaringType() != null) {
0593: throw new DebugException(
0594: new Status(
0595: IStatus.ERROR,
0596: JDIDebugModel.getPluginIdentifier(),
0597: DebugException.REQUEST_FAILED,
0598: EvaluationMessages.LocalEvaluationEngine_Evaluation_in_context_of_inner_type_not_supported__19,
0599: null));
0600: }
0601: }
0602:
0603: /**
0604: * @see IEvaluationEngine#evaluate(String, IJavaObject, IJavaThread, IEvaluationListener, int)
0605: */
0606: public void evaluate(String snippet, IJavaObject this Context,
0607: IJavaThread thread, IEvaluationListener listener,
0608: int evaluationDetail, boolean hitBreakpoints)
0609: throws DebugException {
0610: checkDisposed();
0611: checkEvaluating();
0612: try {
0613: evaluationStarted();
0614: setListener(listener);
0615: setHitBreakpoints(hitBreakpoints);
0616: setResult(new EvaluationResult(this , snippet, thread));
0617: checkThread();
0618:
0619: // no locals
0620: setLocalVariableTypeNames(new String[0]);
0621: setLocalVariableNames(new String[0]);
0622: setLocalVariableModifiers(new int[0]);
0623:
0624: setThis(this Context);
0625:
0626: final boolean isStatic = false;
0627: final boolean isConstructor = false;
0628: final IType receivingType = JavaDebugUtils
0629: .resolveType(this Context.getJavaType());
0630: validateReceivingType(receivingType);
0631:
0632: // do the evaluation in a different thread
0633: Runnable r = new Runnable() {
0634: public void run() {
0635: try {
0636: LocalEvaluationEngine.this
0637: .getEvaluationContext()
0638: .evaluateCodeSnippet(
0639: LocalEvaluationEngine.this
0640: .getSnippet(),
0641: LocalEvaluationEngine.this
0642: .getLocalVariableTypeNames(),
0643: LocalEvaluationEngine.this
0644: .getLocalVariableNames(),
0645: LocalEvaluationEngine.this
0646: .getLocalVariableModifiers(),
0647: receivingType, isStatic,
0648: isConstructor,
0649: LocalEvaluationEngine.this ,
0650: null);
0651: } catch (JavaModelException e) {
0652: LocalEvaluationEngine.this .getResult()
0653: .setException(
0654: new DebugException(e
0655: .getStatus()));
0656: } finally {
0657: LocalEvaluationEngine.this .evaluationComplete();
0658: }
0659: }
0660: };
0661:
0662: Thread t = new Thread(r);
0663: t.setDaemon(true);
0664: t.start();
0665: } catch (DebugException d) {
0666: evaluationAborted();
0667: throw d;
0668: } catch (CoreException e) {
0669: evaluationAborted();
0670: throw new DebugException(e.getStatus());
0671: }
0672: }
0673:
0674: /**
0675: * Throws an exception if this engine has already been
0676: * disposed.
0677: *
0678: * @exception DebugException if this engine has been disposed
0679: */
0680: protected void checkDisposed() throws DebugException {
0681: if (isDisposed()) {
0682: throw new DebugException(
0683: new Status(
0684: IStatus.ERROR,
0685: JDIDebugModel.getPluginIdentifier(),
0686: DebugException.REQUEST_FAILED,
0687: EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___evaluation_context_has_been_disposed__7,
0688: null));
0689: }
0690: }
0691:
0692: /**
0693: * Throws an exception if this engine is already in an
0694: * evaluation.
0695: *
0696: * @exception DebugException if this engine is currently
0697: * performing an evaluation
0698: */
0699: protected void checkEvaluating() throws DebugException {
0700: if (isEvaluating()) {
0701: throw new DebugException(new Status(IStatus.ERROR,
0702: JDIDebugModel.getPluginIdentifier(),
0703: DebugException.REQUEST_FAILED,
0704: "Cannot perform nested evaluations.", null) //$NON-NLS-1$
0705: );
0706: }
0707: }
0708:
0709: /**
0710: * Throws an exception if this engine's current evaluation
0711: * thread is not suspended.
0712: *
0713: * @exception DebugException if this engine's current evaluation
0714: * thread is not suspended
0715: */
0716: protected void checkThread() throws DebugException {
0717: if (!getThread().isSuspended()) {
0718: throw new DebugException(
0719: new Status(
0720: IStatus.ERROR,
0721: JDIDebugModel.getPluginIdentifier(),
0722: DebugException.REQUEST_FAILED,
0723: EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___evaluation_thread_must_be_suspended__8,
0724: null));
0725: }
0726: }
0727:
0728: /**
0729: * Deletes deployed class files, and clears state.
0730: *
0731: * @see IEvaluationEngine#dispose()
0732: */
0733: public void dispose() {
0734: fDisposed = true;
0735: ENGINE_COUNT--;
0736: if (isEvaluating()) {
0737: // cannot dispose if in an evaluation, must
0738: // wait for evaluation to complete
0739: return;
0740: }
0741: List snippetFiles = getSnippetFiles();
0742: Iterator iter = snippetFiles.iterator();
0743: while (iter.hasNext()) {
0744: File file = (File) iter.next();
0745: if (file.exists()) {
0746: if (CODE_SNIPPET_NAME.equals(file.getName())
0747: && ENGINE_COUNT > 0) {
0748: continue; //do not delete the common file for other engines
0749: }
0750: if (!file.delete()) {
0751: JDIDebugPlugin
0752: .log(new Status(
0753: IStatus.ERROR,
0754: JDIDebugModel.getPluginIdentifier(),
0755: DebugException.REQUEST_FAILED,
0756: MessageFormat
0757: .format(
0758: "Unable to delete temporary evaluation class file {0}.", new String[] { file.getAbsolutePath() }), null) //$NON-NLS-1$
0759: );
0760: }
0761: }
0762: }
0763: List directories = getDirectories();
0764: // remove directories in bottom up order
0765: int i = directories.size() - 1;
0766: while (i >= 0) {
0767: File dir = (File) directories.get(i);
0768: String[] listing = dir.list();
0769: if (dir.exists() && listing != null && listing.length == 0
0770: && !dir.delete()) {
0771: JDIDebugPlugin
0772: .log(new Status(
0773: IStatus.ERROR,
0774: JDIDebugModel.getPluginIdentifier(),
0775: DebugException.REQUEST_FAILED,
0776: MessageFormat
0777: .format(
0778: "Unable to delete temporary evaluation directory {0}.", new String[] { dir.getAbsolutePath() }), null) //$NON-NLS-1$
0779: );
0780: }
0781: i--;
0782: }
0783: reset();
0784: setJavaProject(null);
0785: setDebugTarget(null);
0786: setOutputDirectory(null);
0787: setResult(null);
0788: setEvaluationContext(null);
0789: }
0790:
0791: /**
0792: * Resets this engine for another evaluation.
0793: */
0794: private void reset() {
0795: setThis(null);
0796: setStackFrame(null);
0797: setListener(null);
0798: }
0799:
0800: /**
0801: * Returns the listener to notify when the current
0802: * evaluation is complete.
0803: *
0804: * @return the listener to notify when the current
0805: * evaluation is complete
0806: */
0807: protected IEvaluationListener getListener() {
0808: return fListener;
0809: }
0810:
0811: /**
0812: * Sets the listener to notify when the current
0813: * evaluation is complete.
0814: *
0815: * @param listener the listener to notify when the current
0816: * evaluation is complete
0817: */
0818: private void setListener(IEvaluationListener listener) {
0819: fListener = listener;
0820: }
0821:
0822: /**
0823: * Returns the stack frame context for the current
0824: * evaluation, or <code>null</code> if none.
0825: *
0826: * @return the stack frame context for the current
0827: * evaluation, or <code>null</code> if none
0828: */
0829: protected IJavaStackFrame getStackFrame() {
0830: return fStackFrame;
0831: }
0832:
0833: /**
0834: * Sets the stack frame context for the current evaluation.
0835: *
0836: * @param stackFrame stack frame context or <code>null</code>
0837: * if none
0838: */
0839: private void setStackFrame(IJavaStackFrame stackFrame) {
0840: fStackFrame = stackFrame;
0841: }
0842:
0843: /**
0844: * Returns the thread in which the current evaluation is
0845: * to be executed.
0846: *
0847: * @return the thread in which the current evaluation is
0848: * to be executed
0849: */
0850: protected IJavaThread getThread() {
0851: return getResult().getThread();
0852: }
0853:
0854: /**
0855: * Returns the code snippet being evaluated.
0856: *
0857: * @return the code snippet being evaluated.
0858: */
0859: protected String getSnippet() {
0860: return getResult().getSnippet();
0861: }
0862:
0863: /**
0864: * Returns the current evaluation result.
0865: *
0866: * @return the current evaluation result
0867: */
0868: protected EvaluationResult getResult() {
0869: return fResult;
0870: }
0871:
0872: /**
0873: * Sets the current evaluation result.
0874: *
0875: * @param result the current evaluation result
0876: */
0877: private void setResult(EvaluationResult result) {
0878: fResult = result;
0879: }
0880:
0881: /**
0882: * Deploys the given class files to this engine's
0883: * output location, and adds the files to this
0884: * engines list of temporary files to be deleted
0885: * when disposed.
0886: *
0887: * @exception DebugException if this fails due to a
0888: * lower level exception.
0889: */
0890: protected void deploy(byte[][] classFiles, String[][] classFileNames)
0891: throws DebugException {
0892: for (int i = 0; i < classFiles.length; i++) {
0893: String[] compoundName = classFileNames[i];
0894: //create required folders
0895: File dir = LocalEvaluationEngine.this .getOutputDirectory();
0896: try {
0897: String pkgDirName = dir.getCanonicalPath();
0898: for (int j = 0; j < (compoundName.length - 1); j++) {
0899: pkgDirName += File.separator + compoundName[j];
0900: File pkgDir = new File(pkgDirName);
0901: if (!pkgDir.exists()) {
0902: pkgDir.mkdir();
0903: addDirectory(pkgDir);
0904: }
0905: }
0906: String name = compoundName[compoundName.length - 1]
0907: + ".class"; //$NON-NLS-1$
0908: File classFile = new File(pkgDirName + File.separator
0909: + name);
0910: if (!classFile.exists()) {
0911: classFile.createNewFile();
0912: }
0913: FileOutputStream stream = new FileOutputStream(
0914: classFile);
0915: stream.write(classFiles[i]);
0916: stream.close();
0917: LocalEvaluationEngine.this .addSnippetFile(classFile);
0918: } catch (IOException e) {
0919: throw new DebugException(
0920: new Status(
0921: IStatus.ERROR,
0922: JDIDebugModel.getPluginIdentifier(),
0923: DebugException.REQUEST_FAILED,
0924: MessageFormat
0925: .format(
0926: EvaluationMessages.LocalEvaluationEngine__0__occurred_deploying_class_file_for_evaluation_9,
0927: new String[] { e
0928: .toString() }),
0929: e));
0930: }
0931: }
0932: }
0933:
0934: /**
0935: * Adds the given file to this engine's collection
0936: * of deployed snippet class files, which are to
0937: * be deleted when this engine is diposed.
0938: *
0939: * @param File snippet class file
0940: */
0941: private void addSnippetFile(File file) {
0942: if (fSnippetFiles == null) {
0943: fSnippetFiles = new ArrayList();
0944: }
0945: fSnippetFiles.add(file);
0946: }
0947:
0948: /**
0949: * Adds the given file to this engine's collection
0950: * of cerated directories, which are to
0951: * be deleted when this engine is diposed.
0952: *
0953: * @param file directory created for class file deployment
0954: */
0955: private void addDirectory(File file) {
0956: if (fDirectories == null) {
0957: fDirectories = new ArrayList();
0958: }
0959: fDirectories.add(file);
0960: }
0961:
0962: /**
0963: * Returns an evaluation context for this evaluation
0964: * engine. An evaluation context is associted with a
0965: * specific Java project. The evaluation context is
0966: * created lazily on the first access.
0967: *
0968: * @return evaluation context
0969: */
0970: protected IEvaluationContext getEvaluationContext() {
0971: if (fEvaluationContext == null) {
0972: fEvaluationContext = getJavaProject()
0973: .newEvaluationContext();
0974: }
0975: return fEvaluationContext;
0976: }
0977:
0978: /**
0979: * Sets the evaluation context for this evaluation
0980: * engine.
0981: *
0982: * @param context evaluation context
0983: */
0984: private void setEvaluationContext(IEvaluationContext context) {
0985: fEvaluationContext = context;
0986: }
0987:
0988: /**
0989: * Returns a collection of snippet class file deployed by
0990: * this evaluation engine, possibly empty.
0991: *
0992: * @return deployed class files
0993: */
0994: protected List getSnippetFiles() {
0995: if (fSnippetFiles == null) {
0996: return Collections.EMPTY_LIST;
0997: }
0998: return fSnippetFiles;
0999: }
1000:
1001: /**
1002: * Returns a collection of directories created by
1003: * this evaluation engine, possibly empty.
1004: *
1005: * @return directories created when deploying class files
1006: */
1007: protected List getDirectories() {
1008: if (fDirectories == null) {
1009: return Collections.EMPTY_LIST;
1010: }
1011: return fDirectories;
1012: }
1013:
1014: /**
1015: * Returns whether this evaluation engine has been
1016: * disposed.
1017: *
1018: * @return whether this evaluation engine has been
1019: * disposed
1020: */
1021: protected boolean isDisposed() {
1022: return fDisposed;
1023: }
1024:
1025: /**
1026: * The evaluation is complete. Notify the current listener
1027: * and reset for the next evaluation.
1028: */
1029: protected void evaluationComplete() {
1030: // only notify if plug-in not yet shutdown (bug# 8693)
1031: if (JDIDebugPlugin.getDefault() != null) {
1032: getListener().evaluationComplete(getResult());
1033: }
1034: evaluationEnded();
1035: reset();
1036: if (isDisposed()) {
1037: // if the engine was disposed during an evaluation
1038: // do the cleanup now
1039: dispose();
1040: }
1041: }
1042:
1043: /**
1044: * Increments the evaluation counter.
1045: */
1046: private void evaluationStarted() {
1047: fEvaluationCount++;
1048: }
1049:
1050: /**
1051: * Decrements the evaluation counter.
1052: */
1053: private void evaluationEnded() {
1054: if (fEvaluationCount > 0) {
1055: fEvaluationCount--;
1056: }
1057: }
1058:
1059: /**
1060: * Returns whether this engine is currently in the
1061: * midst of an evaluation.
1062: */
1063: protected boolean isEvaluating() {
1064: return fEvaluationCount > 0;
1065: }
1066:
1067: /**
1068: * Called when an evaluation is aborted due to an
1069: * exception. Decrements the evalution count, and
1070: * disposes this engine if the target VM disconnected
1071: * or terminated during the evaluation attempt.
1072: */
1073: private void evaluationAborted() {
1074: evaluationEnded();
1075: if (isDisposed()) {
1076: // if the engine was disposed during an evaluation
1077: // do the cleanup now
1078: dispose();
1079: }
1080: }
1081:
1082: /**
1083: * Constructs and returns a new instance of the specified
1084: * class on the target VM.
1085: *
1086: * @param className fully qualified class name
1087: * @return a new instance on the target, as an <code>IJavaValue</code>
1088: * @exception DebugException if creation fails
1089: */
1090: protected IJavaObject newInstance(String className)
1091: throws DebugException {
1092: IJavaObject object = null;
1093: IJavaClassType clazz = null;
1094: IJavaType[] types = getDebugTarget().getJavaTypes(className);
1095: if (types != null && types.length > 0) {
1096: clazz = (IJavaClassType) types[0];
1097: }
1098: if (clazz == null) {
1099: // The class is not loaded on the target VM.
1100: // Force the load of the class.
1101: types = getDebugTarget().getJavaTypes("java.lang.Class"); //$NON-NLS-1$
1102: IJavaClassType classClass = null;
1103: if (types != null && types.length > 0) {
1104: classClass = (IJavaClassType) types[0];
1105: }
1106: if (classClass == null) {
1107: // unable to load the class
1108: throw new DebugException(
1109: new Status(
1110: IStatus.ERROR,
1111: JDIDebugModel.getPluginIdentifier(),
1112: DebugException.REQUEST_FAILED,
1113: EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_instantiate_code_snippet_class__11,
1114: null));
1115: }
1116: IJavaValue[] args = new IJavaValue[] { getDebugTarget()
1117: .newValue(className) };
1118: IJavaObject classObject = (IJavaObject) classClass
1119: .sendMessage(
1120: "forName", "(Ljava/lang/String;)Ljava/lang/Class;", args, getThread()); //$NON-NLS-2$ //$NON-NLS-1$
1121: object = (IJavaObject) classObject
1122: .sendMessage(
1123: "newInstance", "()Ljava/lang/Object;", null, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
1124: } else {
1125: object = clazz.newInstance("<init>", null, getThread()); //$NON-NLS-1$
1126: }
1127: return object;
1128: }
1129:
1130: /**
1131: * Interpretts and returns the result of the running the snippet
1132: * class file. The type of the result is described by an instance of
1133: * <code>java.lang.Class</code>. The value is interpretted based
1134: * on the result type.
1135: * <p>
1136: * Objects as well as primitve data types (boolean, int, etc.),
1137: * have class objects, which are created by the VM. If the class object
1138: * represents a primitive data type, then the associated value
1139: * is stored in an instance of its "object" class. For example, when
1140: * the result type is the class object for <code>int</code>, the result
1141: * object is an instance of <code>java.lang.Integer</code>, and the
1142: * actual <code>int</code> is stored in the </code>intValue()</code>.
1143: * When the result type is the class object for <code>java.lang.Integer</code>
1144: * the result object is an instance of <code>java.lang.Integer</code>,
1145: * to be interpretted as a <ocde>java.lang.Integer</code>.
1146: * </p>
1147: *
1148: * @param resultType the class of the result
1149: * @param resultValue the value of ther result, to be interpretted
1150: * based on resultType
1151: * @return the result of running the code snipped class file
1152: */
1153: protected IJavaValue convertResult(IJavaClassObject resultType,
1154: IJavaValue result) throws DebugException {
1155: if (resultType == null) {
1156: // there was an exception or compilation problem - no result
1157: return null;
1158: }
1159:
1160: // check the type of the result - if a primitive type, convert it
1161: String sig = resultType.getInstanceType().getSignature();
1162: if (sig.equals("V") || sig.equals("Lvoid;")) { //$NON-NLS-2$ //$NON-NLS-1$
1163: // void
1164: return getDebugTarget().voidValue();
1165: }
1166:
1167: if (result.getJavaType() == null) {
1168: // null result
1169: return result;
1170: }
1171:
1172: if (sig.length() == 1) {
1173: // primitive type - find the instance variable with the
1174: // signature of the result type we are looking for
1175: IVariable[] vars = result.getVariables();
1176: IJavaVariable var = null;
1177: for (int i = 0; i < vars.length; i++) {
1178: IJavaVariable jv = (IJavaVariable) vars[i];
1179: if (!jv.isStatic() && jv.getSignature().equals(sig)) {
1180: var = jv;
1181: break;
1182: }
1183: }
1184: if (var != null) {
1185: return (IJavaValue) var.getValue();
1186: }
1187: } else {
1188: // an object
1189: return result;
1190: }
1191: throw new DebugException(
1192: new Status(
1193: IStatus.ERROR,
1194: JDIDebugModel.getPluginIdentifier(),
1195: DebugException.REQUEST_FAILED,
1196: EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___internal_error_retreiving_result__17,
1197: null));
1198: }
1199:
1200: /**
1201: * Returns the modifiers of the local variables
1202: * visible in this evaluation, possibly empty.
1203: *
1204: * @return array of modifiers
1205: */
1206: private int[] getLocalVariableModifiers() {
1207: return fLocalVariableModifiers;
1208: }
1209:
1210: /**
1211: * Sets the modifiers of the local variables
1212: * visible in this evaluation, possibly empty.
1213: *
1214: * @param localVariableModifiers array of modifiers
1215: */
1216: private void setLocalVariableModifiers(int[] localVariableModifiers) {
1217: fLocalVariableModifiers = localVariableModifiers;
1218: }
1219:
1220: /**
1221: * Returns the names of the local variables
1222: * visible in this evaluation, possibly empty.
1223: *
1224: * @param array of names
1225: */
1226: private String[] getLocalVariableNames() {
1227: return fLocalVariableNames;
1228: }
1229:
1230: /**
1231: * Sets the names of the local variables
1232: * visible in this evaluation, possibly empty.
1233: *
1234: * @param localVariableNames array of names
1235: */
1236: private void setLocalVariableNames(String[] localVariableNames) {
1237: fLocalVariableNames = localVariableNames;
1238: }
1239:
1240: /**
1241: * Returns the type names of the local variables
1242: * visible in this evaluation, possibly empty.
1243: *
1244: * @param array of type names
1245: */
1246: private String[] getLocalVariableTypeNames() {
1247: return fLocalVariableTypeNames;
1248: }
1249:
1250: /**
1251: * Sets the type names of the local variables
1252: * visible in this evaluation, possibly empty.
1253: *
1254: * @param localVariableTypeNames array of type names
1255: */
1256: private void setLocalVariableTypeNames(
1257: String[] localVariableTypeNames) {
1258: fLocalVariableTypeNames = localVariableTypeNames;
1259: }
1260:
1261: /**
1262: * Sets the receiver context for the associated evaluation,
1263: * possibly <code>null</code> if the evaluation is
1264: * in the context of a static method or there is
1265: * no object context.
1266: *
1267: * @param thisObject the receiver content of the
1268: * associated evaluation, or <code>null</code>
1269: */
1270: private void setThis(IJavaObject this Object) {
1271: fThis = this Object;
1272: }
1273:
1274: /**
1275: * Returns the receiver context for the associated evaluation,
1276: * or <code>null</code> if the evaluation is
1277: * in the context of a static method or there is
1278: * no object context.
1279: *
1280: * @return the receiver context of the associated
1281: * evaluation or <code>null</code>
1282: */
1283: private IJavaObject getThis() {
1284: return fThis;
1285: }
1286:
1287: /**
1288: * Returns a copy of the type name with '$' replaced by
1289: * '.', or returns <code>null</code> if the given type
1290: * name refers to an anonymous inner class.
1291: *
1292: * @param typeName a fully qualified type name
1293: * @return a copy of the type name with '$' replaced by
1294: * '.', or returns <code>null</code> if the given type
1295: * name refers to an anonymous inner class.
1296: */
1297: protected String getTranslatedTypeName(String typeName) {
1298: int index = typeName.lastIndexOf('$');
1299: if (index == -1) {
1300: return typeName;
1301: }
1302: if (index + 1 > typeName.length()) {
1303: // invalid name
1304: return typeName;
1305: }
1306: String last = typeName.substring(index + 1);
1307: try {
1308: Integer.parseInt(last);
1309: return null;
1310: } catch (NumberFormatException e) {
1311: return typeName.replace('$', '.');
1312: }
1313: }
1314:
1315: /**
1316: * Returns an array of simple type names that are
1317: * part of the given type's qualified name. For
1318: * example, if the given name is <code>x.y.A$B</code>,
1319: * an array with <code>["A", "B"]</code> is returned.
1320: *
1321: * @param typeName fully qualified type name
1322: * @return array of nested type names
1323: */
1324: protected String[] getNestedTypeNames(String typeName) {
1325: int index = typeName.lastIndexOf('.');
1326: if (index >= 0) {
1327: typeName = typeName.substring(index + 1);
1328: }
1329: index = typeName.indexOf('$');
1330: ArrayList list = new ArrayList(1);
1331: while (index >= 0) {
1332: list.add(typeName.substring(0, index));
1333: typeName = typeName.substring(index + 1);
1334: index = typeName.indexOf('$');
1335: }
1336: list.add(typeName);
1337: return (String[]) list.toArray(new String[list.size()]);
1338: }
1339:
1340: /**
1341: * @see IClassFileEvaluationEngine#getImports()
1342: */
1343: public String[] getImports() {
1344: return getEvaluationContext().getImports();
1345: }
1346:
1347: /**
1348: * @see IClassFileEvaluationEngine#setImports(String[])
1349: */
1350: public void setImports(String[] imports) {
1351: getEvaluationContext().setImports(imports);
1352: }
1353:
1354: /**
1355: * Sets the name of the code snippet to instantiate
1356: * to run the current evaluation.
1357: *
1358: * @param name the name of the deployed code snippet
1359: * to instantiate and run
1360: */
1361: private void setCodeSnippetClassName(String name) {
1362: fCodeSnippetClassName = name;
1363: }
1364:
1365: /**
1366: * Returns the name of the code snippet to instantiate
1367: * to run the current evaluation.
1368: *
1369: * @return the name of the deployed code snippet
1370: * to instantiate and run
1371: */
1372: protected String getCodeSnippetClassName() {
1373: return fCodeSnippetClassName;
1374: }
1375:
1376: /**
1377: * @see ICodeSnippetRequestor#isRequestingClassFiles()
1378: */
1379: public boolean isRequestingClassFiles() {
1380: return true;
1381: }
1382:
1383: /**
1384: * Returns whether to hit breakpoints in the evaluation thread.
1385: *
1386: * @return whether to hit breakpoints in the evaluation thread
1387: */
1388: protected boolean getHitBreakpoints() {
1389: return fHitBreakpoints;
1390: }
1391:
1392: /**
1393: * Sets whether to hit breakpoints in the evaluation thread.
1394: * @param hit whether to hit breakpoints in the evaluation thread
1395: */
1396: private void setHitBreakpoints(boolean hit) {
1397: fHitBreakpoints = hit;
1398: }
1399:
1400: }
|