1 /*******************************************************************************
2  * Copyright (c) 2000, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.jdt.internal.debug.eval;
15 
16 import java.io.File;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.text.MessageFormat;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.Iterator;
24 import java.util.List;
25 
26 import org.eclipse.core.resources.IMarker;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.runtime.IProgressMonitor;
29 import org.eclipse.core.runtime.IStatus;
30 import org.eclipse.core.runtime.Status;
31 import org.eclipse.debug.core.DebugEvent;
32 import org.eclipse.debug.core.DebugException;
33 import org.eclipse.debug.core.model.IVariable;
34 import org.eclipse.jdt.core.IJavaProject;
35 import org.eclipse.jdt.core.IType;
36 import org.eclipse.jdt.core.JavaModelException;
37 import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
38 import org.eclipse.jdt.core.eval.IEvaluationContext;
39 import org.eclipse.jdt.debug.core.IEvaluationRunnable;
40 import org.eclipse.jdt.debug.core.IJavaClassObject;
41 import org.eclipse.jdt.debug.core.IJavaClassType;
42 import org.eclipse.jdt.debug.core.IJavaDebugTarget;
43 import org.eclipse.jdt.debug.core.IJavaObject;
44 import org.eclipse.jdt.debug.core.IJavaStackFrame;
45 import org.eclipse.jdt.debug.core.IJavaThread;
46 import org.eclipse.jdt.debug.core.IJavaType;
47 import org.eclipse.jdt.debug.core.IJavaValue;
48 import org.eclipse.jdt.debug.core.IJavaVariable;
49 import org.eclipse.jdt.debug.core.JDIDebugModel;
50 import org.eclipse.jdt.debug.eval.IClassFileEvaluationEngine;
51 import org.eclipse.jdt.debug.eval.IEvaluationEngine;
52 import org.eclipse.jdt.debug.eval.IEvaluationListener;
53 import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
54 import org.eclipse.jdt.internal.debug.core.JavaDebugUtils;
55 import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
56 import org.eclipse.jdt.internal.debug.core.model.JDIValue;
57 
58 import com.sun.jdi.InvocationException;
59 import com.sun.jdi.ObjectReference;
60 
61 /**
62  * An evaluation engine that deploys class files locally
63  */
64 
65 public class LocalEvaluationEngine implements IClassFileEvaluationEngine,
66 		ICodeSnippetRequestor, IEvaluationRunnable {
67 
68 	private static final String CODE_SNIPPET_NAME = "CodeSnippet.class"; //$NON-NLS-1$
69 
70 	/**
71 	 * A count of the number of engines created. Count is incremented on
72 	 * instantiation and decremented on dispose. When the count == 0, the
73 	 * special CodeSnippet.class is deleted as this class file is shared by all.
74 	 */
75 	private static int ENGINE_COUNT = 0;
76 
77 	/**
78 	 * The Java project context in which to compile snippets.
79 	 */
80 	private IJavaProject fJavaProject;
81 
82 	/**
83 	 * The debug target on which to execute snippets
84 	 */
85 	private IJavaDebugTarget fDebugTarget;
86 
87 	/**
88 	 * The location in which to deploy snippet class files
89 	 */
90 	private File fOutputDirectory;
91 
92 	/**
93 	 * The listener to notify when the current evaluation is complete.
94 	 */
95 	private IEvaluationListener fListener;
96 
97 	/**
98 	 * The stack frame context for the current evaluation or <code>null</code>
99 	 * if there is no stack frame context.
100 	 */
101 	private IJavaStackFrame fStackFrame;
102 
103 	/**
104 	 * The result of this evaluation
105 	 */
106 	private EvaluationResult fResult;
107 
108 	/**
109 	 * Collection of deployed snippet class files
110 	 */
111 	private List<File> fSnippetFiles;
112 
113 	/**
114 	 * Collection of directories created by this evaluation engine.
115 	 */
116 	private List<File> fDirectories;
117 
118 	/**
119 	 * Evaluation context for the Java project associated with this evaluation
120 	 * engine.
121 	 */
122 	private IEvaluationContext fEvaluationContext;
123 
124 	/**
125 	 * Array of modifier constants for visible local variables in the current
126 	 * evaluation.
127 	 *
128 	 * XXX: constants should be 'default' or 'final'. Where are these constants
129 	 * defined.
130 	 */
131 	private int[] fLocalVariableModifiers;
132 
133 	/**
134 	 * Array of names of visible local variables in the current evaluation.
135 	 */
136 	private String[] fLocalVariableNames;
137 
138 	/**
139 	 * Array of type names of visible local variables in the current evaluation.
140 	 */
141 	private String[] fLocalVariableTypeNames;
142 
143 	/**
144 	 * The 'this' object for the current evaluation or <code>null</code> if
145 	 * there is no 'this' context (static method, or not context)
146 	 */
147 	private IJavaObject fThis;
148 
149 	/**
150 	 * Whether this engine has been disposed.
151 	 */
152 	private boolean fDisposed = false;
153 
154 	/**
155 	 * The number of evaluations currently being performed.
156 	 */
157 	private int fEvaluationCount = 0;
158 
159 	/**
160 	 * The name of the code snippet class to instantiate
161 	 */
162 	private String fCodeSnippetClassName = null;
163 
164 	/**
165 	 * Whether to hit breakpoints in the evaluation thread
166 	 */
167 	private boolean fHitBreakpoints = false;
168 
169 	/**
170 	 * Constant for empty array of <code>java.lang.String</code>
171 	 */
172 	private static final String[] EMPTY_STRING_ARRAY = new String[0];
173 
174 	/**
175 	 * Constant for empty array of <code>int</code>
176 	 */
177 	private static final int[] EMPTY_INT_ARRAY = new int[0];
178 
179 	/**
180 	 * Constructs a new evaluation engine for the given VM in the context of the
181 	 * specified project. Class files required for the evaluation will be
182 	 * deployed to the specified directory (which must be on the class path of
183 	 * the VM in order for evaluation to work).
184 	 *
185 	 * @param project
186 	 *            context in which to compile snippets
187 	 * @param vm
188 	 *            debug target in which to evaluate snippets
189 	 * @param directory
190 	 *            location where snippet class files will be deployed for
191 	 *            execution. The directory must exist
192 	 */
LocalEvaluationEngine(IJavaProject project, IJavaDebugTarget vm, File directory)193 	public LocalEvaluationEngine(IJavaProject project, IJavaDebugTarget vm,
194 			File directory) {
195 		setJavaProject(project);
196 		setDebugTarget(vm);
197 		setOutputDirectory(directory);
198 		ENGINE_COUNT++;
199 	}
200 
201 	/**
202 	 * @see ICodeSnippetRequestor#acceptClassFiles(byte[][], String[][], String)
203 	 */
204 	@Override
acceptClassFiles(byte[][] classFileBytes, String[][] classFileCompoundNames, String codeSnippetClassName)205 	public boolean acceptClassFiles(byte[][] classFileBytes,
206 			String[][] classFileCompoundNames, String codeSnippetClassName) {
207 		try {
208 			deploy(classFileBytes, classFileCompoundNames);
209 		} catch (DebugException e) {
210 			getResult().setException(e);
211 			return false;
212 		}
213 		if (codeSnippetClassName != null) {
214 			setCodeSnippetClassName(codeSnippetClassName);
215 			try {
216 				getThread().runEvaluation(this, null, DebugEvent.EVALUATION,
217 						getHitBreakpoints());
218 			} catch (DebugException e) {
219 				// exception handling is in evaluation runnable
220 			}
221 		}
222 		return true;
223 	}
224 
225 	@Override
run(IJavaThread thread, IProgressMonitor monitor)226 	public void run(IJavaThread thread, IProgressMonitor monitor) {
227 		IJavaObject codeSnippetInstance = null;
228 		try {
229 			codeSnippetInstance = newInstance(getCodeSnippetClassName());
230 			initializeLocals(codeSnippetInstance);
231 			codeSnippetInstance.sendMessage(RUN_METHOD,	"()V", null, getThread(), false); //$NON-NLS-1$
232 			restoreLocals(codeSnippetInstance);
233 
234 			// now retrieve the description of the result
235 			IVariable[] fields = codeSnippetInstance.getVariables();
236 			IJavaVariable resultValue = null;
237 			IJavaVariable resultType = null;
238 			for (IVariable field : fields) {
239 				if (field.getName().equals(RESULT_TYPE_FIELD)) {
240 					resultType = (IJavaVariable) field;
241 				}
242 				if (field.getName().equals(RESULT_VALUE_FIELD)) {
243 					resultValue = (IJavaVariable) field;
244 				}
245 			}
246 			IJavaValue result = convertResult((IJavaClassObject) resultType.getValue(), (IJavaValue) resultValue.getValue());
247 			getResult().setValue(result);
248 		} catch (DebugException e) {
249 			getResult().setException(e);
250 
251 			Throwable underlyingException = e.getStatus().getException();
252 			if (underlyingException instanceof InvocationException) {
253 				ObjectReference theException = ((InvocationException) underlyingException)
254 						.exception();
255 				if (theException != null) {
256 					try {
257 						try {
258 							IJavaObject v = (IJavaObject) JDIValue.createValue(
259 									(JDIDebugTarget) getDebugTarget(),
260 									theException);
261 							v.sendMessage(
262 									"printStackTrace", "()V", null, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
263 						} catch (DebugException de) {
264 							JDIDebugPlugin.log(de);
265 						}
266 					} catch (RuntimeException re) {
267 						JDIDebugPlugin.log(re);
268 					}
269 				}
270 			}
271 		}
272 
273 	}
274 
275 	/**
276 	 * Initializes the value of instance variables in the 'code snippet object'
277 	 * that are used as place-holders for locals and 'this' in the current stack
278 	 * frame.
279 	 *
280 	 * @param object
281 	 *            instance of code snippet class that will be run
282 	 * @exception DebugException
283 	 *                if an exception is thrown accessing the given object
284 	 */
initializeLocals(IJavaObject object)285 	protected void initializeLocals(IJavaObject object) throws DebugException {
286 		IJavaVariable[] locals = null;
287 		IJavaObject thisObject = getThis();
288 		if (getStackFrame() != null) {
289 			locals = getStackFrame().getLocalVariables();
290 		}
291 		if (locals != null) {
292 			for (IJavaVariable local : locals) {
293 				IJavaVariable field = object.getField(
294 						LOCAL_VAR_PREFIX + local.getName(), false);
295 				// internal error if field is not found
296 				if (field == null) {
297 					throw new DebugException(
298 							new Status(
299 									IStatus.ERROR,
300 									JDIDebugModel.getPluginIdentifier(),
301 									DebugException.REQUEST_FAILED,
302 									EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__4,
303 									null));
304 				}
305 				field.setValue(local.getValue());
306 			}
307 		}
308 		if (thisObject != null) {
309 			IJavaVariable field = object.getField(DELEGATE_THIS, false);
310 			// internal error if field is not found
311 			if (field == null) {
312 				throw new DebugException(
313 						new Status(
314 								IStatus.ERROR,
315 								JDIDebugModel.getPluginIdentifier(),
316 								DebugException.REQUEST_FAILED,
317 								EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_initialize___this___context__5,
318 								null));
319 			}
320 			field.setValue(thisObject);
321 		}
322 	}
323 
324 	/**
325 	 * Restores the value local variables from the instance variables in the
326 	 * 'code snippet object' that are used as place-holders for locals in the
327 	 * current stack frame.
328 	 *
329 	 * @param object
330 	 *            instance of code snippet class that was run
331 	 * @exception DebugException
332 	 *                if an exception is thrown accessing the given object
333 	 */
restoreLocals(IJavaObject object)334 	protected void restoreLocals(IJavaObject object) throws DebugException {
335 		IJavaVariable[] locals = null;
336 		if (getStackFrame() != null) {
337 			locals = getStackFrame().getLocalVariables();
338 		}
339 		if (locals != null) {
340 			for (IJavaVariable local : locals) {
341 				IJavaVariable field = object.getField(
342 						LOCAL_VAR_PREFIX + local.getName(), false);
343 				// internal error if field is not found
344 				if (field == null) {
345 					throw new DebugException(
346 							new Status(
347 									IStatus.ERROR,
348 									JDIDebugModel.getPluginIdentifier(),
349 									DebugException.REQUEST_FAILED,
350 									EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__6,
351 									null));
352 				}
353 				local.setValue(field.getValue());
354 			}
355 		}
356 	}
357 
358 	/**
359 	 * @see ICodeSnippetRequestor#acceptProblem(IMarker, String, int)
360 	 */
361 	@Override
acceptProblem(IMarker problemMarker, String fragmentSource, int fragmentKind)362 	public void acceptProblem(IMarker problemMarker, String fragmentSource,
363 			int fragmentKind) {
364 		if (problemMarker.getAttribute(IMarker.SEVERITY, -1) != IMarker.SEVERITY_ERROR) {
365 			return;
366 		}
367 		getResult().addError(problemMarker.getAttribute(IMarker.MESSAGE, "")); //$NON-NLS-1$
368 	}
369 
370 	/**
371 	 * @see IEvaluationEngine#getDebugTarget()
372 	 */
373 	@Override
getDebugTarget()374 	public IJavaDebugTarget getDebugTarget() {
375 		return fDebugTarget;
376 	}
377 
378 	/**
379 	 * Sets the debug target in which snippets are executed.
380 	 *
381 	 * @param debugTarget
382 	 *            the debug target in which snippets are executed
383 	 */
setDebugTarget(IJavaDebugTarget debugTarget)384 	private void setDebugTarget(IJavaDebugTarget debugTarget) {
385 		fDebugTarget = debugTarget;
386 	}
387 
388 	/**
389 	 * @see IEvaluationEngine#getJavaProject()
390 	 */
391 	@Override
getJavaProject()392 	public IJavaProject getJavaProject() {
393 		return fJavaProject;
394 	}
395 
396 	/**
397 	 * Sets the Java project in which snippets are compiled.
398 	 *
399 	 * @param javaProject
400 	 *            the Java project in which snippets are compiled
401 	 */
setJavaProject(IJavaProject javaProject)402 	private void setJavaProject(IJavaProject javaProject) {
403 		fJavaProject = javaProject;
404 	}
405 
406 	/**
407 	 * Returns the directory in which snippet class files are deployed.
408 	 *
409 	 * @return the directory in which snippet class files are deployed.
410 	 */
getOutputDirectory()411 	public File getOutputDirectory() {
412 		return fOutputDirectory;
413 	}
414 
415 	/**
416 	 * Sets the directory in which snippet class files are deployed.
417 	 *
418 	 * @param outputDirectory
419 	 *            location to deploy snippet class files
420 	 */
setOutputDirectory(File outputDirectory)421 	private void setOutputDirectory(File outputDirectory) {
422 		fOutputDirectory = outputDirectory;
423 	}
424 
425 	/**
426 	 * @see IClassFileEvaluationEngine#evaluate(String, IJavaThread,
427 	 *      IEvaluationListener)
428 	 */
429 	@Override
evaluate(String snippet, IJavaThread thread, IEvaluationListener listener, boolean hitBreakpoints)430 	public void evaluate(String snippet, IJavaThread thread,
431 			IEvaluationListener listener, boolean hitBreakpoints)
432 			throws DebugException {
433 		checkDisposed();
434 		checkEvaluating();
435 		try {
436 			evaluationStarted();
437 			setListener(listener);
438 			setHitBreakpoints(hitBreakpoints);
439 			setResult(new EvaluationResult(this, snippet, thread));
440 			checkThread();
441 			// no receiver/stack frame context
442 			setThis(null);
443 			setLocalVariableNames(EMPTY_STRING_ARRAY);
444 			setLocalVariableTypeNames(EMPTY_STRING_ARRAY);
445 			setLocalVariableModifiers(EMPTY_INT_ARRAY);
446 
447 			// do the evaluation in a different thread
448 			Runnable r = new Runnable() {
449 				@Override
450 				public void run() {
451 					try {
452 						LocalEvaluationEngine.this
453 								.getEvaluationContext()
454 								.evaluateCodeSnippet(
455 										LocalEvaluationEngine.this.getSnippet(),
456 										LocalEvaluationEngine.this, null);
457 					} catch (JavaModelException e) {
458 						LocalEvaluationEngine.this.getResult().setException(
459 								new DebugException(e.getStatus()));
460 					} finally {
461 						LocalEvaluationEngine.this.evaluationComplete();
462 					}
463 				}
464 			};
465 
466 			Thread t = new Thread(r);
467 			t.setDaemon(true);
468 			t.start();
469 		} catch (DebugException d) {
470 			evaluationAborted();
471 			throw d;
472 		}
473 
474 	}
475 
476 	/**
477 	 * @see IEvaluationEngine#evaluate(String, IJavaStackFrame,
478 	 *      IEvaluationListener, int)
479 	 */
480 	@Override
evaluate(String snippet, IJavaStackFrame frame, IEvaluationListener listener, int evaluationDetail, boolean hitBreakpoints)481 	public void evaluate(String snippet, IJavaStackFrame frame,
482 			IEvaluationListener listener, int evaluationDetail,
483 			boolean hitBreakpoints) throws DebugException {
484 		checkDisposed();
485 		checkEvaluating();
486 		try {
487 			evaluationStarted();
488 			setListener(listener);
489 			setStackFrame(frame);
490 			setHitBreakpoints(hitBreakpoints);
491 			setResult(new EvaluationResult(this, snippet,
492 					(IJavaThread) frame.getThread()));
493 			checkThread();
494 
495 			// set up local variables and 'this' context for evaluation
496 			IJavaVariable[] locals = frame.getLocalVariables();
497 
498 			List<String> typeNames = new ArrayList<>(locals.length);
499 			List<String> varNames = new ArrayList<>(locals.length);
500 
501 			for (IJavaVariable var : locals) {
502 				String typeName = getTranslatedTypeName(var
503 						.getReferenceTypeName());
504 				if (typeName != null) {
505 					typeNames.add(typeName);
506 					varNames.add(var.getName());
507 				}
508 			}
509 
510 			setLocalVariableTypeNames(typeNames
511 					.toArray(new String[typeNames.size()]));
512 			setLocalVariableNames(varNames
513 					.toArray(new String[varNames.size()]));
514 			int[] modifiers = new int[typeNames.size()];
515 			// cannot determine if local is final, so specify as default
516 			Arrays.fill(modifiers, 0);
517 			setLocalVariableModifiers(modifiers);
518 			setThis(frame.getThis());
519 
520 			final boolean isStatic = frame.isStatic();
521 			final boolean isConstructor = frame.isConstructor();
522 			final IType receivingType = JavaDebugUtils
523 					.resolveDeclaringType(frame);
524 			validateReceivingType(receivingType);
525 
526 			// do the evaluation in a different thread
527 			Runnable r = new Runnable() {
528 				@Override
529 				public void run() {
530 					try {
531 						LocalEvaluationEngine.this
532 								.getEvaluationContext()
533 								.evaluateCodeSnippet(
534 										LocalEvaluationEngine.this.getSnippet(),
535 										LocalEvaluationEngine.this
536 												.getLocalVariableTypeNames(),
537 										LocalEvaluationEngine.this
538 												.getLocalVariableNames(),
539 										LocalEvaluationEngine.this
540 												.getLocalVariableModifiers(),
541 										receivingType, isStatic, isConstructor,
542 										LocalEvaluationEngine.this, null);
543 					} catch (JavaModelException e) {
544 						LocalEvaluationEngine.this.getResult().setException(
545 								new DebugException(e.getStatus()));
546 					} finally {
547 						LocalEvaluationEngine.this.evaluationComplete();
548 					}
549 				}
550 			};
551 
552 			Thread t = new Thread(r);
553 			t.setDaemon(true);
554 			t.start();
555 		} catch (DebugException d) {
556 			evaluationAborted();
557 			throw d;
558 		} catch (CoreException e) {
559 			evaluationAborted();
560 			throw new DebugException(e.getStatus());
561 		}
562 	}
563 
564 	/**
565 	 * Verifies the receiving type was resolved and is not an inner type.
566 	 *
567 	 * @param receivingType
568 	 * @throws DebugException
569 	 */
validateReceivingType(final IType receivingType)570 	private void validateReceivingType(final IType receivingType)
571 			throws DebugException {
572 		if (receivingType == null) {
573 			throw new DebugException(
574 					new Status(
575 							IStatus.ERROR,
576 							JDIDebugModel.getPluginIdentifier(),
577 							DebugException.REQUEST_FAILED,
578 							EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_determine_receiving_type_context__18,
579 							null));
580 		}
581 
582 		if (receivingType.getDeclaringType() != null) {
583 			throw new DebugException(
584 					new Status(
585 							IStatus.ERROR,
586 							JDIDebugModel.getPluginIdentifier(),
587 							DebugException.REQUEST_FAILED,
588 							EvaluationMessages.LocalEvaluationEngine_Evaluation_in_context_of_inner_type_not_supported__19,
589 							null));
590 		}
591 	}
592 
593 	/**
594 	 * @see IEvaluationEngine#evaluate(String, IJavaObject, IJavaThread,
595 	 *      IEvaluationListener, int)
596 	 */
597 	@Override
evaluate(String snippet, IJavaObject thisContext, IJavaThread thread, IEvaluationListener listener, int evaluationDetail, boolean hitBreakpoints)598 	public void evaluate(String snippet, IJavaObject thisContext,
599 			IJavaThread thread, IEvaluationListener listener,
600 			int evaluationDetail, boolean hitBreakpoints) throws DebugException {
601 		checkDisposed();
602 		checkEvaluating();
603 		try {
604 			evaluationStarted();
605 			setListener(listener);
606 			setHitBreakpoints(hitBreakpoints);
607 			setResult(new EvaluationResult(this, snippet, thread));
608 			checkThread();
609 
610 			// no locals
611 			setLocalVariableTypeNames(new String[0]);
612 			setLocalVariableNames(new String[0]);
613 			setLocalVariableModifiers(new int[0]);
614 
615 			setThis(thisContext);
616 
617 			final boolean isStatic = false;
618 			final boolean isConstructor = false;
619 			final IType receivingType = JavaDebugUtils.resolveType(thisContext
620 					.getJavaType());
621 			validateReceivingType(receivingType);
622 
623 			// do the evaluation in a different thread
624 			Runnable r = new Runnable() {
625 				@Override
626 				public void run() {
627 					try {
628 						LocalEvaluationEngine.this
629 								.getEvaluationContext()
630 								.evaluateCodeSnippet(
631 										LocalEvaluationEngine.this.getSnippet(),
632 										LocalEvaluationEngine.this
633 												.getLocalVariableTypeNames(),
634 										LocalEvaluationEngine.this
635 												.getLocalVariableNames(),
636 										LocalEvaluationEngine.this
637 												.getLocalVariableModifiers(),
638 										receivingType, isStatic, isConstructor,
639 										LocalEvaluationEngine.this, null);
640 					} catch (JavaModelException e) {
641 						LocalEvaluationEngine.this.getResult().setException(
642 								new DebugException(e.getStatus()));
643 					} finally {
644 						LocalEvaluationEngine.this.evaluationComplete();
645 					}
646 				}
647 			};
648 
649 			Thread t = new Thread(r);
650 			t.setDaemon(true);
651 			t.start();
652 		} catch (DebugException d) {
653 			evaluationAborted();
654 			throw d;
655 		} catch (CoreException e) {
656 			evaluationAborted();
657 			throw new DebugException(e.getStatus());
658 		}
659 	}
660 
661 	/**
662 	 * Throws an exception if this engine has already been disposed.
663 	 *
664 	 * @exception DebugException
665 	 *                if this engine has been disposed
666 	 */
checkDisposed()667 	protected void checkDisposed() throws DebugException {
668 		if (isDisposed()) {
669 			throw new DebugException(
670 					new Status(
671 							IStatus.ERROR,
672 							JDIDebugModel.getPluginIdentifier(),
673 							DebugException.REQUEST_FAILED,
674 							EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___evaluation_context_has_been_disposed__7,
675 							null));
676 		}
677 	}
678 
679 	/**
680 	 * Throws an exception if this engine is already in an evaluation.
681 	 *
682 	 * @exception DebugException
683 	 *                if this engine is currently performing an evaluation
684 	 */
checkEvaluating()685 	protected void checkEvaluating() throws DebugException {
686 		if (isEvaluating()) {
687 			throw new DebugException(new Status(IStatus.ERROR,
688 					JDIDebugModel.getPluginIdentifier(),
689 					DebugException.REQUEST_FAILED,
690 					"Cannot perform nested evaluations.", null) //$NON-NLS-1$
691 			);
692 		}
693 	}
694 
695 	/**
696 	 * Throws an exception if this engine's current evaluation thread is not
697 	 * suspended.
698 	 *
699 	 * @exception DebugException
700 	 *                if this engine's current evaluation thread is not
701 	 *                suspended
702 	 */
checkThread()703 	protected void checkThread() throws DebugException {
704 		if (!getThread().isSuspended()) {
705 			throw new DebugException(
706 					new Status(
707 							IStatus.ERROR,
708 							JDIDebugModel.getPluginIdentifier(),
709 							DebugException.REQUEST_FAILED,
710 							EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___evaluation_thread_must_be_suspended__8,
711 							null));
712 		}
713 	}
714 
715 	/**
716 	 * Deletes deployed class files, and clears state.
717 	 *
718 	 * @see IEvaluationEngine#dispose()
719 	 */
720 	@Override
dispose()721 	public void dispose() {
722 		fDisposed = true;
723 		ENGINE_COUNT--;
724 		if (isEvaluating()) {
725 			// cannot dispose if in an evaluation, must
726 			// wait for evaluation to complete
727 			return;
728 		}
729 		List<File> snippetFiles = getSnippetFiles();
730 		Iterator<File> iter = snippetFiles.iterator();
731 		while (iter.hasNext()) {
732 			File file = iter.next();
733 			if (file.exists()) {
734 				if (CODE_SNIPPET_NAME.equals(file.getName())
735 						&& ENGINE_COUNT > 0) {
736 					continue; // do not delete the common file for other engines
737 				}
738 				if (!file.delete()) {
739 					JDIDebugPlugin
740 							.log(new Status(
741 									IStatus.ERROR,
742 									JDIDebugModel.getPluginIdentifier(),
743 									DebugException.REQUEST_FAILED,
744 									MessageFormat
745 											.format("Unable to delete temporary evaluation class file {0}.", new Object[] { file.getAbsolutePath() }), null) //$NON-NLS-1$
746 							);
747 				}
748 			}
749 		}
750 		List<File> directories = getDirectories();
751 		// remove directories in bottom up order
752 		int i = directories.size() - 1;
753 		while (i >= 0) {
754 			File dir = directories.get(i);
755 			String[] listing = dir.list();
756 			if (dir.exists() && listing != null && listing.length == 0
757 					&& !dir.delete()) {
758 				JDIDebugPlugin
759 						.log(new Status(
760 								IStatus.ERROR,
761 								JDIDebugModel.getPluginIdentifier(),
762 								DebugException.REQUEST_FAILED,
763 								MessageFormat
764 										.format("Unable to delete temporary evaluation directory {0}.", new Object[] { dir.getAbsolutePath() }), null) //$NON-NLS-1$
765 						);
766 			}
767 			i--;
768 		}
769 		reset();
770 		setJavaProject(null);
771 		setDebugTarget(null);
772 		setOutputDirectory(null);
773 		setResult(null);
774 		setEvaluationContext(null);
775 	}
776 
777 	/**
778 	 * Resets this engine for another evaluation.
779 	 */
reset()780 	private void reset() {
781 		setThis(null);
782 		setStackFrame(null);
783 		setListener(null);
784 	}
785 
786 	/**
787 	 * Returns the listener to notify when the current evaluation is complete.
788 	 *
789 	 * @return the listener to notify when the current evaluation is complete
790 	 */
getListener()791 	protected IEvaluationListener getListener() {
792 		return fListener;
793 	}
794 
795 	/**
796 	 * Sets the listener to notify when the current evaluation is complete.
797 	 *
798 	 * @param listener
799 	 *            the listener to notify when the current evaluation is complete
800 	 */
setListener(IEvaluationListener listener)801 	private void setListener(IEvaluationListener listener) {
802 		fListener = listener;
803 	}
804 
805 	/**
806 	 * Returns the stack frame context for the current evaluation, or
807 	 * <code>null</code> if none.
808 	 *
809 	 * @return the stack frame context for the current evaluation, or
810 	 *         <code>null</code> if none
811 	 */
getStackFrame()812 	protected IJavaStackFrame getStackFrame() {
813 		return fStackFrame;
814 	}
815 
816 	/**
817 	 * Sets the stack frame context for the current evaluation.
818 	 *
819 	 * @param stackFrame
820 	 *            stack frame context or <code>null</code> if none
821 	 */
setStackFrame(IJavaStackFrame stackFrame)822 	private void setStackFrame(IJavaStackFrame stackFrame) {
823 		fStackFrame = stackFrame;
824 	}
825 
826 	/**
827 	 * Returns the thread in which the current evaluation is to be executed.
828 	 *
829 	 * @return the thread in which the current evaluation is to be executed
830 	 */
getThread()831 	protected IJavaThread getThread() {
832 		return getResult().getThread();
833 	}
834 
835 	/**
836 	 * Returns the code snippet being evaluated.
837 	 *
838 	 * @return the code snippet being evaluated.
839 	 */
getSnippet()840 	protected String getSnippet() {
841 		return getResult().getSnippet();
842 	}
843 
844 	/**
845 	 * Returns the current evaluation result.
846 	 *
847 	 * @return the current evaluation result
848 	 */
getResult()849 	protected EvaluationResult getResult() {
850 		return fResult;
851 	}
852 
853 	/**
854 	 * Sets the current evaluation result.
855 	 *
856 	 * @param result
857 	 *            the current evaluation result
858 	 */
setResult(EvaluationResult result)859 	private void setResult(EvaluationResult result) {
860 		fResult = result;
861 	}
862 
863 	/**
864 	 * Deploys the given class files to this engine's output location, and adds
865 	 * the files to this engines list of temporary files to be deleted when
866 	 * disposed.
867 	 *
868 	 * @exception DebugException
869 	 *                if this fails due to a lower level exception.
870 	 */
deploy(byte[][] classFiles, String[][] classFileNames)871 	protected void deploy(byte[][] classFiles, String[][] classFileNames)
872 			throws DebugException {
873 		for (int i = 0; i < classFiles.length; i++) {
874 			String[] compoundName = classFileNames[i];
875 			// create required folders
876 			File dir = LocalEvaluationEngine.this.getOutputDirectory();
877 			try {
878 				String pkgDirName = dir.getCanonicalPath();
879 				for (int j = 0; j < (compoundName.length - 1); j++) {
880 					pkgDirName += File.separator + compoundName[j];
881 					File pkgDir = new File(pkgDirName);
882 					if (!pkgDir.exists()) {
883 						pkgDir.mkdir();
884 						addDirectory(pkgDir);
885 					}
886 				}
887 				String name = compoundName[compoundName.length - 1] + ".class"; //$NON-NLS-1$
888 				File classFile = new File(pkgDirName + File.separator + name);
889 				if (!classFile.exists()) {
890 					classFile.createNewFile();
891 				}
892 				try (FileOutputStream stream = new FileOutputStream(classFile)) {
893 					stream.write(classFiles[i]);
894 				}
895 				LocalEvaluationEngine.this.addSnippetFile(classFile);
896 			} catch (IOException e) {
897 				throw new DebugException(
898 						new Status(
899 								IStatus.ERROR,
900 								JDIDebugModel.getPluginIdentifier(),
901 								DebugException.REQUEST_FAILED,
902 								MessageFormat
903 										.format(EvaluationMessages.LocalEvaluationEngine__0__occurred_deploying_class_file_for_evaluation_9,
904 												new Object[] { e.toString() }),
905 								e));
906 			}
907 		}
908 	}
909 
910 	/**
911 	 * Adds the given file to this engine's collection of deployed snippet class
912 	 * files, which are to be deleted when this engine is disposed.
913 	 *
914 	 * @param File
915 	 *            snippet class file
916 	 */
addSnippetFile(File file)917 	private void addSnippetFile(File file) {
918 		if (fSnippetFiles == null) {
919 			fSnippetFiles = new ArrayList<>();
920 		}
921 		fSnippetFiles.add(file);
922 	}
923 
924 	/**
925 	 * Adds the given file to this engine's collection of created directories,
926 	 * which are to be deleted when this engine is disposed.
927 	 *
928 	 * @param file
929 	 *            directory created for class file deployment
930 	 */
addDirectory(File file)931 	private void addDirectory(File file) {
932 		if (fDirectories == null) {
933 			fDirectories = new ArrayList<>();
934 		}
935 		fDirectories.add(file);
936 	}
937 
938 	/**
939 	 * Returns an evaluation context for this evaluation engine. An evaluation
940 	 * context is associated with a specific Java project. The evaluation context
941 	 * is created lazily on the first access.
942 	 *
943 	 * @return evaluation context
944 	 */
getEvaluationContext()945 	protected IEvaluationContext getEvaluationContext() {
946 		if (fEvaluationContext == null) {
947 			fEvaluationContext = getJavaProject().newEvaluationContext();
948 		}
949 		return fEvaluationContext;
950 	}
951 
952 	/**
953 	 * Sets the evaluation context for this evaluation engine.
954 	 *
955 	 * @param context
956 	 *            evaluation context
957 	 */
setEvaluationContext(IEvaluationContext context)958 	private void setEvaluationContext(IEvaluationContext context) {
959 		fEvaluationContext = context;
960 	}
961 
962 	/**
963 	 * Returns a collection of snippet class file deployed by this evaluation
964 	 * engine, possibly empty.
965 	 *
966 	 * @return deployed class files
967 	 */
getSnippetFiles()968 	protected List<File> getSnippetFiles() {
969 		if (fSnippetFiles == null) {
970 			return Collections.EMPTY_LIST;
971 		}
972 		return fSnippetFiles;
973 	}
974 
975 	/**
976 	 * Returns a collection of directories created by this evaluation engine,
977 	 * possibly empty.
978 	 *
979 	 * @return directories created when deploying class files
980 	 */
getDirectories()981 	protected List<File> getDirectories() {
982 		if (fDirectories == null) {
983 			return Collections.EMPTY_LIST;
984 		}
985 		return fDirectories;
986 	}
987 
988 	/**
989 	 * Returns whether this evaluation engine has been disposed.
990 	 *
991 	 * @return whether this evaluation engine has been disposed
992 	 */
isDisposed()993 	protected boolean isDisposed() {
994 		return fDisposed;
995 	}
996 
997 	/**
998 	 * The evaluation is complete. Notify the current listener and reset for the
999 	 * next evaluation.
1000 	 */
evaluationComplete()1001 	protected void evaluationComplete() {
1002 		// only notify if plug-in not yet shutdown (bug# 8693)
1003 		if (JDIDebugPlugin.getDefault() != null) {
1004 			getListener().evaluationComplete(getResult());
1005 		}
1006 		evaluationEnded();
1007 		reset();
1008 		if (isDisposed()) {
1009 			// if the engine was disposed during an evaluation
1010 			// do the cleanup now
1011 			dispose();
1012 		}
1013 	}
1014 
1015 	/**
1016 	 * Increments the evaluation counter.
1017 	 */
evaluationStarted()1018 	private void evaluationStarted() {
1019 		fEvaluationCount++;
1020 	}
1021 
1022 	/**
1023 	 * Decrements the evaluation counter.
1024 	 */
evaluationEnded()1025 	private void evaluationEnded() {
1026 		if (fEvaluationCount > 0) {
1027 			fEvaluationCount--;
1028 		}
1029 	}
1030 
1031 	/**
1032 	 * Returns whether this engine is currently in the midst of an evaluation.
1033 	 */
isEvaluating()1034 	protected boolean isEvaluating() {
1035 		return fEvaluationCount > 0;
1036 	}
1037 
1038 	/**
1039 	 * Called when an evaluation is aborted due to an exception. Decrements the
1040 	 * evaluation count, and disposes this engine if the target VM disconnected
1041 	 * or terminated during the evaluation attempt.
1042 	 */
evaluationAborted()1043 	private void evaluationAborted() {
1044 		evaluationEnded();
1045 		if (isDisposed()) {
1046 			// if the engine was disposed during an evaluation
1047 			// do the cleanup now
1048 			dispose();
1049 		}
1050 	}
1051 
1052 	/**
1053 	 * Constructs and returns a new instance of the specified class on the
1054 	 * target VM.
1055 	 *
1056 	 * @param className
1057 	 *            fully qualified class name
1058 	 * @return a new instance on the target, as an <code>IJavaValue</code>
1059 	 * @exception DebugException
1060 	 *                if creation fails
1061 	 */
newInstance(String className)1062 	protected IJavaObject newInstance(String className) throws DebugException {
1063 		IJavaObject object = null;
1064 		IJavaClassType clazz = null;
1065 		IJavaType[] types = getDebugTarget().getJavaTypes(className);
1066 		if (types != null && types.length > 0) {
1067 			clazz = (IJavaClassType) types[0];
1068 		}
1069 		if (clazz == null) {
1070 			// The class is not loaded on the target VM.
1071 			// Force the load of the class.
1072 			types = getDebugTarget().getJavaTypes("java.lang.Class"); //$NON-NLS-1$
1073 			IJavaClassType classClass = null;
1074 			if (types != null && types.length > 0) {
1075 				classClass = (IJavaClassType) types[0];
1076 			}
1077 			if (classClass == null) {
1078 				// unable to load the class
1079 				throw new DebugException(
1080 						new Status(
1081 								IStatus.ERROR,
1082 								JDIDebugModel.getPluginIdentifier(),
1083 								DebugException.REQUEST_FAILED,
1084 								EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_instantiate_code_snippet_class__11,
1085 								null));
1086 			}
1087 			IJavaValue[] args = new IJavaValue[] { getDebugTarget().newValue(
1088 					className) };
1089 			IJavaObject classObject = (IJavaObject) classClass
1090 					.sendMessage(
1091 							"forName", "(Ljava/lang/String;)Ljava/lang/Class;", args, getThread()); //$NON-NLS-2$ //$NON-NLS-1$
1092 			object = (IJavaObject) classObject
1093 					.sendMessage(
1094 							"newInstance", "()Ljava/lang/Object;", null, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
1095 		} else {
1096 			object = clazz.newInstance("<init>", null, getThread()); //$NON-NLS-1$
1097 		}
1098 		return object;
1099 	}
1100 
1101 	/**
1102 	 * Interprets and returns the result of the running the snippet class file.
1103 	 * The type of the result is described by an instance of
1104 	 * <code>java.lang.Class</code>. The value is interpreted based on the
1105 	 * result type.
1106 	 * <p>
1107 	 * Objects as well as primitive data types (boolean, int, etc.), have class
1108 	 * objects, which are created by the VM. If the class object represents a
1109 	 * primitive data type, then the associated value is stored in an instance
1110 	 * of its "object" class. For example, when the result type is the class
1111 	 * object for <code>int</code>, the result object is an instance of
1112 	 * <code>java.lang.Integer</code>, and the actual <code>int</code> is stored
1113 	 * in the </code>intValue()</code>. When the result type is the class object
1114 	 * for <code>java.lang.Integer</code> the result object is an instance of
1115 	 * <code>java.lang.Integer</code>, to be interpreted as a
1116 	 * <code>java.lang.Integer</code>.
1117 	 * </p>
1118 	 *
1119 	 * @param resultType
1120 	 *            the class of the result
1121 	 * @param resultValue
1122 	 *            the value of the result, to be interpreted based on
1123 	 *            resultType
1124 	 * @return the result of running the code snippet class file
1125 	 */
convertResult(IJavaClassObject resultType, IJavaValue result)1126 	protected IJavaValue convertResult(IJavaClassObject resultType,
1127 			IJavaValue result) throws DebugException {
1128 		if (resultType == null) {
1129 			// there was an exception or compilation problem - no result
1130 			return null;
1131 		}
1132 
1133 		// check the type of the result - if a primitive type, convert it
1134 		String sig = resultType.getInstanceType().getSignature();
1135 		if (sig.equals("V") || sig.equals("Lvoid;")) { //$NON-NLS-2$ //$NON-NLS-1$
1136 			// void
1137 			return getDebugTarget().voidValue();
1138 		}
1139 
1140 		if (result.getJavaType() == null) {
1141 			// null result
1142 			return result;
1143 		}
1144 
1145 		if (sig.length() == 1) {
1146 			// primitive type - find the instance variable with the
1147 			// signature of the result type we are looking for
1148 			IVariable[] vars = result.getVariables();
1149 			IJavaVariable var = null;
1150 			for (IVariable var2 : vars) {
1151 				IJavaVariable jv = (IJavaVariable) var2;
1152 				if (!jv.isStatic() && jv.getSignature().equals(sig)) {
1153 					var = jv;
1154 					break;
1155 				}
1156 			}
1157 			if (var != null) {
1158 				return (IJavaValue) var.getValue();
1159 			}
1160 		} else {
1161 			// an object
1162 			return result;
1163 		}
1164 		throw new DebugException(
1165 				new Status(
1166 						IStatus.ERROR,
1167 						JDIDebugModel.getPluginIdentifier(),
1168 						DebugException.REQUEST_FAILED,
1169 						EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___internal_error_retreiving_result__17,
1170 						null));
1171 	}
1172 
1173 	/**
1174 	 * Returns the modifiers of the local variables visible in this evaluation,
1175 	 * possibly empty.
1176 	 *
1177 	 * @return array of modifiers
1178 	 */
getLocalVariableModifiers()1179 	private int[] getLocalVariableModifiers() {
1180 		return fLocalVariableModifiers;
1181 	}
1182 
1183 	/**
1184 	 * Sets the modifiers of the local variables visible in this evaluation,
1185 	 * possibly empty.
1186 	 *
1187 	 * @param localVariableModifiers
1188 	 *            array of modifiers
1189 	 */
setLocalVariableModifiers(int[] localVariableModifiers)1190 	private void setLocalVariableModifiers(int[] localVariableModifiers) {
1191 		fLocalVariableModifiers = localVariableModifiers;
1192 	}
1193 
1194 	/**
1195 	 * Returns the names of the local variables visible in this evaluation,
1196 	 * possibly empty.
1197 	 *
1198 	 * @param array
1199 	 *            of names
1200 	 */
getLocalVariableNames()1201 	private String[] getLocalVariableNames() {
1202 		return fLocalVariableNames;
1203 	}
1204 
1205 	/**
1206 	 * Sets the names of the local variables visible in this evaluation,
1207 	 * possibly empty.
1208 	 *
1209 	 * @param localVariableNames
1210 	 *            array of names
1211 	 */
setLocalVariableNames(String[] localVariableNames)1212 	private void setLocalVariableNames(String[] localVariableNames) {
1213 		fLocalVariableNames = localVariableNames;
1214 	}
1215 
1216 	/**
1217 	 * Returns the type names of the local variables visible in this evaluation,
1218 	 * possibly empty.
1219 	 *
1220 	 * @param array
1221 	 *            of type names
1222 	 */
getLocalVariableTypeNames()1223 	private String[] getLocalVariableTypeNames() {
1224 		return fLocalVariableTypeNames;
1225 	}
1226 
1227 	/**
1228 	 * Sets the type names of the local variables visible in this evaluation,
1229 	 * possibly empty.
1230 	 *
1231 	 * @param localVariableTypeNames
1232 	 *            array of type names
1233 	 */
setLocalVariableTypeNames(String[] localVariableTypeNames)1234 	private void setLocalVariableTypeNames(String[] localVariableTypeNames) {
1235 		fLocalVariableTypeNames = localVariableTypeNames;
1236 	}
1237 
1238 	/**
1239 	 * Sets the receiver context for the associated evaluation, possibly
1240 	 * <code>null</code> if the evaluation is in the context of a static method
1241 	 * or there is no object context.
1242 	 *
1243 	 * @param thisObject
1244 	 *            the receiver content of the associated evaluation, or
1245 	 *            <code>null</code>
1246 	 */
setThis(IJavaObject thisObject)1247 	private void setThis(IJavaObject thisObject) {
1248 		fThis = thisObject;
1249 	}
1250 
1251 	/**
1252 	 * Returns the receiver context for the associated evaluation, or
1253 	 * <code>null</code> if the evaluation is in the context of a static method
1254 	 * or there is no object context.
1255 	 *
1256 	 * @return the receiver context of the associated evaluation or
1257 	 *         <code>null</code>
1258 	 */
getThis()1259 	private IJavaObject getThis() {
1260 		return fThis;
1261 	}
1262 
1263 	/**
1264 	 * Returns a copy of the type name with '$' replaced by '.', or returns
1265 	 * <code>null</code> if the given type name refers to an anonymous inner
1266 	 * class.
1267 	 *
1268 	 * @param typeName
1269 	 *            a fully qualified type name
1270 	 * @return a copy of the type name with '$' replaced by '.', or returns
1271 	 *         <code>null</code> if the given type name refers to an anonymous
1272 	 *         inner class.
1273 	 */
getTranslatedTypeName(String typeName)1274 	protected String getTranslatedTypeName(String typeName) {
1275 		int index = typeName.lastIndexOf('$');
1276 		if (index == -1) {
1277 			return typeName;
1278 		}
1279 		if (index + 1 > typeName.length()) {
1280 			// invalid name
1281 			return typeName;
1282 		}
1283 		String last = typeName.substring(index + 1);
1284 		try {
1285 			Integer.parseInt(last);
1286 			return null;
1287 		} catch (NumberFormatException e) {
1288 			return typeName.replace('$', '.');
1289 		}
1290 	}
1291 
1292 	/**
1293 	 * Returns an array of simple type names that are part of the given type's
1294 	 * qualified name. For example, if the given name is <code>x.y.A$B</code>,
1295 	 * an array with <code>["A", "B"]</code> is returned.
1296 	 *
1297 	 * @param typeName
1298 	 *            fully qualified type name
1299 	 * @return array of nested type names
1300 	 */
getNestedTypeNames(String typeName)1301 	protected String[] getNestedTypeNames(String typeName) {
1302 		int index = typeName.lastIndexOf('.');
1303 		if (index >= 0) {
1304 			typeName = typeName.substring(index + 1);
1305 		}
1306 		index = typeName.indexOf('$');
1307 		ArrayList<String> list = new ArrayList<>(1);
1308 		while (index >= 0) {
1309 			list.add(typeName.substring(0, index));
1310 			typeName = typeName.substring(index + 1);
1311 			index = typeName.indexOf('$');
1312 		}
1313 		list.add(typeName);
1314 		return list.toArray(new String[list.size()]);
1315 	}
1316 
1317 	/**
1318 	 * @see IClassFileEvaluationEngine#getImports()
1319 	 */
1320 	@Override
getImports()1321 	public String[] getImports() {
1322 		return getEvaluationContext().getImports();
1323 	}
1324 
1325 	/**
1326 	 * @see IClassFileEvaluationEngine#setImports(String[])
1327 	 */
1328 	@Override
setImports(String[] imports)1329 	public void setImports(String[] imports) {
1330 		getEvaluationContext().setImports(imports);
1331 	}
1332 
1333 	/**
1334 	 * Sets the name of the code snippet to instantiate to run the current
1335 	 * evaluation.
1336 	 *
1337 	 * @param name
1338 	 *            the name of the deployed code snippet to instantiate and run
1339 	 */
setCodeSnippetClassName(String name)1340 	private void setCodeSnippetClassName(String name) {
1341 		fCodeSnippetClassName = name;
1342 	}
1343 
1344 	/**
1345 	 * Returns the name of the code snippet to instantiate to run the current
1346 	 * evaluation.
1347 	 *
1348 	 * @return the name of the deployed code snippet to instantiate and run
1349 	 */
getCodeSnippetClassName()1350 	protected String getCodeSnippetClassName() {
1351 		return fCodeSnippetClassName;
1352 	}
1353 
1354 	/**
1355 	 * @see ICodeSnippetRequestor#isRequestingClassFiles()
1356 	 */
isRequestingClassFiles()1357 	public boolean isRequestingClassFiles() {
1358 		return true;
1359 	}
1360 
1361 	/**
1362 	 * Returns whether to hit breakpoints in the evaluation thread.
1363 	 *
1364 	 * @return whether to hit breakpoints in the evaluation thread
1365 	 */
getHitBreakpoints()1366 	protected boolean getHitBreakpoints() {
1367 		return fHitBreakpoints;
1368 	}
1369 
1370 	/**
1371 	 * Sets whether to hit breakpoints in the evaluation thread.
1372 	 *
1373 	 * @param hit
1374 	 *            whether to hit breakpoints in the evaluation thread
1375 	 */
setHitBreakpoints(boolean hit)1376 	private void setHitBreakpoints(boolean hit) {
1377 		fHitBreakpoints = hit;
1378 	}
1379 
1380 }
1381