Clover coverage report - DrJava Test Coverage (drjava-20110828-r5448)
Coverage timestamp: Sun Aug 28 2011 03:13:33 CDT
file stats: LOC: 610   Methods: 63
NCLOC: 347   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
InterpreterJVM.java 18.3% 42.2% 52.4% 39.9%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.model.repl.newjvm;
 38   
 39    import java.lang.reflect.*;
 40    import java.util.*;
 41    import java.io.*;
 42   
 43    import java.rmi.*;
 44   
 45    // NOTE: Do NOT import/use the config framework in this class!
 46    // (This class runs in a different JVM, and will not share the config object)
 47   
 48   
 49    import edu.rice.cs.util.OutputStreamRedirector;
 50    import edu.rice.cs.util.InputStreamRedirector;
 51    import edu.rice.cs.util.UnexpectedException;
 52    import edu.rice.cs.util.classloader.ClassFileError;
 53    import edu.rice.cs.util.newjvm.*;
 54    import edu.rice.cs.plt.collect.CollectUtil;
 55    import edu.rice.cs.plt.iter.IterUtil;
 56    import edu.rice.cs.plt.reflect.ReflectUtil;
 57    import edu.rice.cs.plt.tuple.Option;
 58    import edu.rice.cs.plt.tuple.OptionVisitor;
 59    import edu.rice.cs.plt.tuple.Pair;
 60    import edu.rice.cs.plt.text.TextUtil;
 61   
 62    import edu.rice.cs.drjava.platform.PlatformFactory;
 63    import edu.rice.cs.drjava.model.junit.JUnitModelCallback;
 64    import edu.rice.cs.drjava.model.junit.JUnitTestManager;
 65    import edu.rice.cs.drjava.model.junit.JUnitError;
 66    import edu.rice.cs.drjava.model.repl.InteractionsPaneOptions;
 67   
 68    import edu.rice.cs.dynamicjava.Options;
 69    import edu.rice.cs.dynamicjava.interpreter.*;
 70    import edu.rice.cs.dynamicjava.symbol.*;
 71    import edu.rice.cs.dynamicjava.symbol.type.Type;
 72   
 73    // For Windows focus fix
 74    import javax.swing.JDialog;
 75   
 76    import static edu.rice.cs.plt.debug.DebugUtil.debug;
 77    import static edu.rice.cs.plt.debug.DebugUtil.error;
 78   
 79    /** This is the main class for the interpreter JVM. All public methods except those involving remote calls (callbacks)
 80    * use synchronizazion on _stateLock (unless synchronization has no effect). The class is not ready for remote
 81    * calls until handleStart has been executed. This class is loaded in the Interpreter JVM, not the Main JVM.
 82    * (Do not use DrJava's config framework here.)
 83    * <p>
 84    * Note that this class is specific to DynamicJava. It must be refactored to accommodate other interpreters.
 85    * @version $Id: InterpreterJVM.java 5445 2011-08-17 20:32:57Z rcartwright $
 86    */
 87    public class InterpreterJVM extends AbstractSlaveJVM implements InterpreterJVMRemoteI, JUnitModelCallback {
 88   
 89    /** Singleton instance of this class. */
 90    public static final InterpreterJVM ONLY = new InterpreterJVM();
 91   
 92    // As RMI can lead to parallel threads, all fields must be thread-safe. Collections are wrapped
 93    // in synchronized versions.
 94   
 95    private final InteractionsPaneOptions _interpreterOptions;
 96    private volatile Pair<String, Interpreter> _activeInterpreter;
 97    private final Interpreter _defaultInterpreter;
 98    private final Map<String, Interpreter> _interpreters;
 99    private final Set<Interpreter> _busyInterpreters;
 100    // The following variable appears to be useless.
 101    // private final Map<String, Pair<TypeContext, RuntimeBindings>> _environments;
 102   
 103    private final ClassPathManager _classPathManager;
 104    private final ClassLoader _interpreterLoader;
 105   
 106    // Lock object for ensuring mutual exclusion on updates and compound accesses
 107    private final Object _stateLock = new Object();
 108   
 109    /** Responsible for running JUnit tests in this JVM. */
 110    private final JUnitTestManager _junitTestManager;
 111   
 112    /** Remote reference to the MainJVM class in DrJava's primary JVM. Assigned ONLY once. */
 113    private volatile MainJVMRemoteI _mainJVM;
 114   
 115    /** Private constructor; use the singleton ONLY instance. */
 116  180 private InterpreterJVM() {
 117  180 super("Reset Interactions Thread", "Poll DrJava Thread");
 118   
 119  180 _classPathManager = new ClassPathManager(ReflectUtil.SYSTEM_CLASS_PATH);
 120  180 _interpreterLoader = _classPathManager.makeClassLoader(null);
 121  180 _junitTestManager = new JUnitTestManager(this, _classPathManager);
 122   
 123    // set the thread context class loader, this way NextGen and Mint can use the interpreter's class loader
 124  180 Thread.currentThread().setContextClassLoader(_interpreterLoader);
 125   
 126    // _interpreterOptions = Options.DEFAULT;
 127  180 _interpreterOptions = new InteractionsPaneOptions();
 128  180 _defaultInterpreter = new Interpreter(_interpreterOptions, _interpreterLoader);
 129  180 _interpreters = new HashMap<String,Interpreter>();
 130  180 _busyInterpreters = new HashSet<Interpreter>();
 131    // _environments = new HashMap<String, Pair<TypeContext, RuntimeBindings>>();
 132  180 _activeInterpreter = Pair.make("", _defaultInterpreter);
 133    }
 134   
 135    /** Actions to perform when this JVM is started (through its superclass, AbstractSlaveJVM). Not synchronized
 136    * because "this" is not initialized for general access until this method has run. */
 137  180 protected void handleStart(MasterRemote mainJVM) {
 138    //_dialog("handleStart");
 139  180 _mainJVM = (MainJVMRemoteI) mainJVM;
 140   
 141    // redirect stdin
 142  180 System.setIn(new InputStreamRedirector() {
 143  1 protected String _getInput() {
 144  1 try { return _mainJVM.getConsoleInput(); }
 145    catch(RemoteException re) {
 146  0 error.log(re);
 147  0 throw new UnexpectedException("Main JVM can't be reached for input.\n" + re);
 148    }
 149    }
 150    });
 151   
 152    // redirect stdout
 153  180 System.setOut(new PrintStream(new OutputStreamRedirector() {
 154  9 public void print(String s) {
 155  9 try { _mainJVM.systemOutPrint(s); }
 156    catch (RemoteException re) {
 157  0 error.log(re);
 158  0 throw new UnexpectedException("Main JVM can't be reached for output.\n" + re);
 159    }
 160    }
 161    }));
 162   
 163    // redirect stderr
 164  180 System.setErr(new PrintStream(new OutputStreamRedirector() {
 165  2 public void print(String s) {
 166  2 try { _mainJVM.systemErrPrint(s); }
 167    catch (RemoteException re) {
 168  0 error.log(re);
 169  0 throw new UnexpectedException("Main JVM can't be reached for output.\n" + re);
 170    }
 171    }
 172    }));
 173   
 174    /* On Windows, any frame or dialog opened from Interactions pane will appear *behind* DrJava's frame, unless a
 175    * previous frame or dialog is shown here. Not sure what the difference is, but this hack seems to work. (I'd
 176    * be happy to find a better solution, though.) Only necessary on Windows, since frames and dialogs on other
 177    * platforms appear correctly in front of DrJava. */
 178  180 if (PlatformFactory.ONLY.isWindowsPlatform()) {
 179  0 JDialog d = new JDialog();
 180  0 d.setSize(0,0);
 181  0 d.setVisible(true);
 182  0 d.setVisible(false);
 183    }
 184    //_dialog("interpreter JVM started");
 185    }
 186   
 187    /* Concurrent operations on _interpreters. */
 188  3 private Interpreter getInterpreter(String name) {
 189  3 synchronized(_interpreters) {return _interpreters.get(name); }
 190    }
 191  2 private boolean isInterpreterName(String name) {
 192  2 synchronized(_interpreters) {return _interpreters.containsKey(name); }
 193    }
 194  2 private Interpreter putInterpreter(String name, Interpreter i) {
 195  2 synchronized(_interpreters) { return _interpreters.put(name, i); }
 196    }
 197    // This method must be public because it is part of a declared interface
 198  0 public void removeInterpreter(String name) {
 199  0 synchronized(_interpreters) { _interpreters.remove(name); }
 200    }
 201   
 202    /* Concurrent operations on _busyInterpreters. */
 203  53 private boolean addBusyInterpreter(Interpreter i) {
 204  53 synchronized(_busyInterpreters) { return _busyInterpreters.add(i); }
 205    }
 206   
 207  52 private boolean removeBusyInterpreter(Interpreter i) {
 208  52 synchronized(_busyInterpreters) { return _busyInterpreters.remove(i); }
 209    }
 210   
 211  28 private boolean isBusyInterpreter(Interpreter i) {
 212  28 synchronized(_busyInterpreters) { return _busyInterpreters.contains(i); }
 213    }
 214   
 215    /** Interprets the given string of source code in the active interpreter. The result is returned to MainJVM via
 216    * the interpretResult method. No synchronization necessary; the code only contains a single read of
 217    * local state.
 218    * @param s Source code to interpret.
 219    */
 220  54 public InterpretResult interpret(String s) { return interpret(s, _activeInterpreter.second()); }
 221   
 222    /** Interprets the given string of source code with the given interpreter. The result is returned to
 223    * MainJVM via the interpretResult method.
 224    * @param s Source code to interpret.
 225    * @param interpreterName Name of the interpreter to use
 226    * @throws IllegalArgumentException if the named interpreter does not exist
 227    */
 228  0 public InterpretResult interpret(String s, String name) {
 229  0 Interpreter i = getInterpreter(name);
 230  0 if (i == null) {
 231  0 throw new IllegalArgumentException("Interpreter '" + name + "' does not exist.");
 232    }
 233  0 return interpret(s, i);
 234    }
 235   
 236  54 private InterpretResult interpret(String input, Interpreter interpreter) {
 237  54 debug.logStart("Interpret " + input);
 238   
 239  53 boolean available = addBusyInterpreter(interpreter);
 240  0 if (! available) { debug.logEnd(); return InterpretResult.busy(); }
 241   
 242    // set the thread context class loader, this way NextGen and Mint can use the interpreter's class loader
 243  53 Thread.currentThread().setContextClassLoader(_interpreterLoader); // _interpreterLoader is final
 244   
 245  53 Option<Object> result = null;
 246  53 try { result = interpreter.interpret(input); }
 247  9 catch (InterpreterException e) { debug.logEnd(); return InterpretResult.exception(e); }
 248  0 catch (Throwable e) { debug.logEnd(); return InterpretResult.unexpectedException(e); }
 249  52 finally { removeBusyInterpreter(interpreter); }
 250   
 251  43 return result.apply(new OptionVisitor<Object, InterpretResult>() {
 252  14 public InterpretResult forNone() { return InterpretResult.noValue(); }
 253  29 public InterpretResult forSome(Object obj) {
 254  5 if (obj instanceof String) { debug.logEnd(); return InterpretResult.stringValue((String) obj); }
 255  0 else if (obj instanceof Character) { debug.logEnd(); return InterpretResult.charValue((Character) obj); }
 256  23 else if (obj instanceof Number) { debug.logEnd(); return InterpretResult.numberValue((Number) obj); }
 257  0 else if (obj instanceof Boolean) { debug.logEnd(); return InterpretResult.booleanValue((Boolean) obj); }
 258    else {
 259  1 try {
 260  1 String resultString = TextUtil.toString(obj);
 261  0 String resultTypeStr = null;
 262  0 if (obj!=null) {
 263  0 Class<?> c = obj.getClass();
 264  0 resultTypeStr = getClassName(c);
 265    }
 266  0 debug.logEnd();
 267  0 return InterpretResult.objectValue(resultString,resultTypeStr);
 268    }
 269    catch (Throwable t) {
 270    // an exception occurred during toString
 271  1 debug.logEnd();
 272  1 return InterpretResult.exception(new EvaluatorException(t));
 273    }
 274    }
 275    }
 276    });
 277    }
 278   
 279    /** Gets the value of the variable with the given name in the current interpreter.
 280    * Invoked reflectively by the debugger. To simplify the inter-process exchange,
 281    * an array here is used as the return type rather than an {@code Option<Object>} --
 282    * an empty array corresponds to "none," and a singleton array corresponds to a "some."
 283    * @param var name of the variable to look up
 284    * @return empty array for "none", singleton array for "some" value
 285    * @see edu.rice.cs.drjava.model.debug.jpda.JPDADebugger#GET_VARIABLE_VALUE_SIG
 286    * @see edu.rice.cs.drjava.model.debug.jpda.JPDADebugger#_copyVariablesFromInterpreter()
 287    */
 288  0 public Object[] getVariableValue(String var) {
 289  0 Pair<Object,String>[] arr = getVariable(var);
 290  0 if (arr.length == 0) return new Object[0];
 291  0 else return new Object[] { arr[0].first() };
 292    }
 293   
 294    /** Gets the value and type string of the variable with the given name in the current interpreter.
 295    * Invoked reflectively by the debugger. To simplify the inter-process exchange,
 296    * an array here is used as the return type rather than an {@code Option<Object>} --
 297    * an empty array corresponds to "none," and a singleton array corresponds to a "some."
 298    */
 299  0 @SuppressWarnings("unchecked")
 300    public Pair<Object,String>[] getVariable(String var) {
 301  0 synchronized(_stateLock) {
 302  0 InterpretResult ir = interpret(var);
 303  0 return ir.apply(new InterpretResult.Visitor<Pair<Object,String>[]>() {
 304  0 public Pair<Object,String>[] fail() { return new Pair[0]; }
 305  0 public Pair<Object,String>[] value(Object val) {
 306  0 return new Pair[] { new Pair<Object,String>(val, getClassName(val.getClass())) };
 307    }
 308  0 public Pair<Object,String>[] forNoValue() { return fail(); }
 309  0 public Pair<Object,String>[] forStringValue(String val) { return value(val); }
 310  0 public Pair<Object,String>[] forCharValue(Character val) { return value(val); }
 311  0 public Pair<Object,String>[] forNumberValue(Number val) { return value(val); }
 312  0 public Pair<Object,String>[] forBooleanValue(Boolean val) { return value(val); }
 313  0 public Pair<Object,String>[] forObjectValue(String valString, String objTypeString) {
 314  0 return new Pair[] { new Pair<Object,String>(valString, objTypeString) }; }
 315  0 public Pair<Object,String>[] forException(String message) { return fail(); }
 316  0 public Pair<Object,String>[] forEvalException(String message, StackTraceElement[] stackTrace) { return fail(); }
 317  0 public Pair<Object,String>[] forUnexpectedException(Throwable t) { return fail(); }
 318  0 public Pair<Object,String>[] forBusy() { return fail(); }
 319    });
 320    }
 321    }
 322   
 323    /** Gets the string representation of the value of a variable in the current interpreter.
 324    * @param var the name of the variable
 325    * @return null if the variable is not defined; the first part of the pair is "null" if the value is null,
 326    * otherwise its string representation; the second part is the string representation of the variable's type
 327    */
 328  0 public Pair<String,String> getVariableToString(String var) {
 329  0 synchronized(_stateLock) {
 330    // if (!isValidFieldName(var)) { return "<error in watch name>"; }
 331  0 Pair<Object,String>[] val = getVariable(var); // recursive locking
 332  0 if (val.length == 0) { return new Pair<String,String>(null,null); }
 333    else {
 334  0 Object o = val[0].first();
 335  0 try { return new Pair<String,String>(TextUtil.toString(o),val[0].second()); }
 336  0 catch (Throwable t) { return new Pair<String,String>("<error in toString()>",""); }
 337    }
 338    }
 339    }
 340   
 341    /** @return the name of the class, with the right number of array suffixes "[]" and while being ambiguous
 342    * about boxed and primitive types. */
 343  0 public static String getClassName(Class<?> c) {
 344  0 StringBuilder sb = new StringBuilder();
 345  0 boolean isArray = c.isArray();
 346  0 while(c.isArray()) {
 347  0 sb.append("[]");
 348  0 c = c.getComponentType();
 349    }
 350   
 351  0 if (!isArray) {
 352    // we can't distinguish primitive types from their boxed types right now
 353  0 if (c.equals(Byte.class)) { return "byte"+sb.toString()+" or "+c.getSimpleName()+sb.toString(); }
 354  0 if (c.equals(Short.class)) { return "short"+sb.toString()+" or "+c.getSimpleName()+sb.toString(); }
 355  0 if (c.equals(Integer.class)) { return "int"+sb.toString()+" or "+c.getSimpleName()+sb.toString(); }
 356  0 if (c.equals(Long.class)) { return "long"+sb.toString()+" or "+c.getSimpleName()+sb.toString(); }
 357  0 if (c.equals(Float.class)) { return "float"+sb.toString()+" or "+c.getSimpleName()+sb.toString(); }
 358  0 if (c.equals(Double.class)) { return "double"+sb.toString()+" or "+c.getSimpleName()+sb.toString(); }
 359  0 if (c.equals(Boolean.class)) { return "boolean"+sb.toString()+" or "+c.getSimpleName()+sb.toString(); }
 360  0 if (c.equals(Character.class)) { return "char"+sb.toString()+" or "+c.getSimpleName()+sb.toString(); }
 361  0 else return c.getName()+sb.toString();
 362    }
 363    else {
 364    // if it's an array, we can distinguish boxed types and primitive types
 365  0 return c.getName()+sb.toString();
 366    }
 367    }
 368   
 369    /** Adds a named Interpreter to the list.
 370    * @param name the unique name for the interpreter
 371    * @throws IllegalArgumentException if the name is not unique
 372    */
 373  2 public void addInterpreter(String name) {
 374  2 synchronized(_stateLock) {
 375  2 if (isInterpreterName(name)) {
 376  0 throw new IllegalArgumentException("'" + name + "' is not a unique interpreter name");
 377    }
 378  2 Interpreter i = new Interpreter(_interpreterOptions, _interpreterLoader);
 379  2 putInterpreter(name, i);
 380    }
 381    }
 382   
 383    /** Adds a named Interpreter in the given environment to the list. Invoked reflectively by
 384    * the debugger.
 385    * @param name The unique name for the interpreter
 386    * @param thisVal The value of {@code this} (may be null, implying this is a static context)
 387    * @param thisClass The class in whose context the interpreter is to be created
 388    * @param localVars Values of local variables
 389    * @param localVarNames Names of the local variables
 390    * @param localVarClasses Classes of the local variables. To simplify the work callers must
 391    * do, a value with a primitive type may have a {@code null} entry here.
 392    * @throws IllegalArgumentException if the name is not unique, or if the local var arrays
 393    * are not all of the same length
 394    * @see edu.rice.cs.drjava.model.debug.jpda.JPDADebugger#ADD_INTERPRETER_SIG
 395    * @see edu.rice.cs.drjava.model.debug.jpda.JPDADebugger#_dumpVariablesIntoInterpreterAndSwitch
 396    */
 397  0 public void addInterpreter(String name, Object thisVal, Class<?> thisClass, Object[] localVars,
 398    String[] localVarNames, Class<?>[] localVarClasses) {
 399  0 synchronized(_stateLock) {
 400  0 debug.logValues(new String[]{ "name", "thisVal", "thisClass", "localVars", "localVarNames",
 401    "localVarClasses" }, name, thisVal, thisClass, localVars, localVarNames, localVarClasses);
 402  0 if (isInterpreterName(name)) {
 403  0 throw new IllegalArgumentException("'" + name + "' is not a unique interpreter name");
 404    }
 405  0 if (localVars.length != localVarNames.length || localVars.length != localVarClasses.length) {
 406  0 throw new IllegalArgumentException("Local variable arrays are inconsistent");
 407    }
 408   
 409    // TODO: handle inner classes
 410    // TODO: enforce final vars?
 411  0 Package pkg = thisClass.getPackage();
 412  0 DJClass c = SymbolUtil.wrapClass(thisClass);
 413  0 List<LocalVariable> vars = new LinkedList<LocalVariable>();
 414  0 for (int i = 0; i < localVars.length; i++) {
 415  0 if (localVarClasses[i] == null) {
 416  0 try { localVarClasses[i] = (Class<?>) localVars[i].getClass().getField("TYPE").get(null); }
 417  0 catch (IllegalAccessException e) { throw new IllegalArgumentException(e); }
 418  0 catch (NoSuchFieldException e) { throw new IllegalArgumentException(e); }
 419    }
 420  0 Type varT = SymbolUtil.typeOfGeneralClass(localVarClasses[i], _interpreterOptions.typeSystem());
 421  0 vars.add(new LocalVariable(localVarNames[i], varT, false));
 422    }
 423   
 424  0 TypeContext ctx = new ImportContext(_interpreterLoader, _interpreterOptions);
 425  0 if (pkg != null) { ctx = ctx.setPackage(pkg.getName()); }
 426  0 ctx = new ClassSignatureContext(ctx, c, _interpreterLoader);
 427  0 ctx = new ClassContext(ctx, c);
 428  0 ctx = new DebugMethodContext(ctx, thisVal == null);
 429  0 ctx = new LocalContext(ctx, vars);
 430   
 431  0 RuntimeBindings bindings = RuntimeBindings.EMPTY;
 432  0 if (thisVal != null) { bindings = new RuntimeBindings(bindings, c, thisVal); }
 433  0 bindings = new RuntimeBindings(bindings, vars, IterUtil.asIterable(localVars));
 434   
 435  0 Interpreter i = new Interpreter(_interpreterOptions, ctx, bindings);
 436    // _environments.put(name, Pair.make(ctx, bindings));
 437  0 putInterpreter(name, i);
 438    }
 439    }
 440   
 441    /** A custom context for interpreting within the body of a defined method. */
 442    private static class DebugMethodContext extends DelegatingContext {
 443    private final boolean _isStatic;
 444  0 public DebugMethodContext(TypeContext next, boolean isStatic) { super(next); _isStatic = isStatic; }
 445  0 protected TypeContext duplicate(TypeContext next) { return new DebugMethodContext(next, _isStatic); }
 446  0 @Override public DJClass getThis() { return _isStatic ? null : super.getThis(); }
 447  0 @Override public DJClass getThis(String className) { return _isStatic ? null : super.getThis(className); }
 448  0 @Override public Type getReturnType() { return null; }
 449  0 @Override public Iterable<Type> getDeclaredThrownTypes() { return IterUtil.empty(); }
 450    }
 451   
 452   
 453    // /** Removes the interpreter with the given name, if it exists. */
 454    // public void removeInterpreter(String name) {
 455    // synchronized(_stateLock) {
 456    // _interpreters.remove(name)
 457    //// _environments.remove(name);
 458    // }
 459    // }
 460   
 461    /** Sets the current interpreter to be the one specified by the given name
 462    * @param name the unique name of the interpreter to set active
 463    * @return Status flags: whether the current interpreter changed, and whether it is busy
 464    */
 465  3 public Pair<Boolean, Boolean> setActiveInterpreter(String name) {
 466  3 synchronized(_stateLock) {
 467  3 Interpreter i = getInterpreter(name);
 468  1 if (i == null) { throw new IllegalArgumentException("Interpreter '" + name + "' does not exist."); }
 469  2 boolean changed = (i != _activeInterpreter.second());
 470  2 _activeInterpreter = Pair.make(name, i);
 471  2 return Pair.make(changed, isBusyInterpreter(i));
 472    }
 473    }
 474   
 475    /** Sets the default interpreter to be active.
 476    * @return Status flags: whether the current interpreter changed, and whether it is busy
 477    */
 478  26 public Pair<Boolean, Boolean> setToDefaultInterpreter() {
 479  26 synchronized(_stateLock) {
 480  26 boolean changed = (_defaultInterpreter != _activeInterpreter.second());
 481  26 _activeInterpreter = Pair.make("", _defaultInterpreter);
 482  26 return Pair.make(changed, isBusyInterpreter(_defaultInterpreter));
 483    }
 484    }
 485   
 486    /** Check that all access of class members is permitted by accessibility controls. */
 487  180 public void setEnforceAllAccess(boolean enforce) {
 488  180 synchronized(_stateLock) {
 489  180 _interpreterOptions.setEnforceAllAccess(enforce);
 490    }
 491    }
 492   
 493    /** Check that access of private class members is permitted (irrelevant if setEnforceAllAccess() is set to true). */
 494  180 public void setEnforcePrivateAccess(boolean enforce) {
 495  180 synchronized(_stateLock) {
 496  180 _interpreterOptions.setEnforcePrivateAccess(enforce);
 497    }
 498    }
 499   
 500    /** Require a semicolon at the end of statements. */
 501  180 public void setRequireSemicolon(boolean require) {
 502  180 synchronized(_stateLock) {
 503  180 _interpreterOptions.setRequireSemicolon(require);
 504    }
 505    }
 506   
 507    /** Require variable declarations to include an explicit type. */
 508  180 public void setRequireVariableType(boolean require) {
 509  180 synchronized(_stateLock) {
 510  180 _interpreterOptions.setRequireVariableType(require);
 511    }
 512    }
 513   
 514    // ---------- JUnit methods ----------
 515    /** Sets up a JUnit test suite in the Interpreter JVM and finds which classes are really TestCases classes (by
 516    * loading them). Unsynchronized because it contains a remote call and does not involve mutable local state.
 517    * @param classNames the class names to run in a test
 518    * @param files the associated file
 519    * @return the class names that are actually test cases
 520    */
 521  18 public List<String> findTestClasses(List<String> classNames, List<File> files) throws RemoteException {
 522  18 return _junitTestManager.findTestClasses(classNames, files);
 523    }
 524   
 525    /** Runs JUnit test suite already cached in the Interpreter JVM. Unsynchronized because it contains a remote call
 526    * and does not involve mutable local state.
 527    * @return false if no test suite is cached; true otherwise
 528    */
 529  16 public boolean runTestSuite() throws RemoteException { return _junitTestManager.runTestSuite(); }
 530   
 531    /** Notifies Main JVM that JUnit has been invoked on a non TestCase class. Unsynchronized because it contains a
 532    * remote call and does not involve mutable local state.
 533    * @param isTestAll whether or not it was a use of the test all button
 534    * @param didCompileFail whether or not a compile before this JUnit attempt failed
 535    */
 536  0 public void nonTestCase(boolean isTestAll, boolean didCompileFail) {
 537  0 try { _mainJVM.nonTestCase(isTestAll, didCompileFail); }
 538  0 catch (RemoteException re) { error.log(re); }
 539    }
 540   
 541    /** Notifies the main JVM that JUnitTestManager has encountered an illegal class file. Unsynchronized because it
 542    * contains a remote call and does not involve mutable local state.
 543    * @param e the ClassFileError object describing the error on loading the file
 544    */
 545  0 public void classFileError(ClassFileError e) {
 546  0 try { _mainJVM.classFileError(e); }
 547  0 catch (RemoteException re) { error.log(re); }
 548    }
 549   
 550    /** Notifies that a suite of tests has started running. Unsynchronized because it contains a remote call and does
 551    * not involve mutable local state.
 552    * @param numTests The number of tests in the suite to be run.
 553    */
 554  16 public void testSuiteStarted(int numTests) {
 555  16 try { _mainJVM.testSuiteStarted(numTests); }
 556  0 catch (RemoteException re) { error.log(re); }
 557    }
 558   
 559    /** Notifies that a particular test has started. Unsynchronized because it contains a remote call and does not
 560    * involve mutable local state.
 561    * @param testName The name of the test being started.
 562    */
 563  25 public void testStarted(String testName) {
 564  25 try { _mainJVM.testStarted(testName); }
 565  0 catch (RemoteException re) { error.log(re); }
 566    }
 567   
 568    /** Notifies that a particular test has ended. Unsynchronized because it contains a remote call.
 569    * @param testName The name of the test that has ended.
 570    * @param wasSuccessful Whether the test passed or not.
 571    * @param causedError If not successful, whether the test caused an error or simply failed.
 572    */
 573  24 public void testEnded(String testName, boolean wasSuccessful, boolean causedError) {
 574  24 try { _mainJVM.testEnded(testName, wasSuccessful, causedError); }
 575  0 catch (RemoteException re) { error.log(re); }
 576    }
 577   
 578    /** Notifies that a full suite of tests has finished running. Unsynchronized because it contains a remote call
 579    * and does not involve mutable local state.
 580    * @param errors The array of errors from all failed tests in the suite.
 581    */
 582  15 public void testSuiteEnded(JUnitError[] errors) {
 583  15 try { _mainJVM.testSuiteEnded(errors); }
 584  0 catch (RemoteException re) { error.log(re); }
 585    }
 586   
 587    /** Called when the JUnitTestManager wants to open a file that is not currently open. Unsynchronized because it
 588    * contains a remote call and does not involve mutable local state.
 589    * @param className the name of the class for which we want to find the file
 590    * @return the file associated with the given class
 591    */
 592  4 public File getFileForClassName(String className) {
 593  4 try { return _mainJVM.getFileForClassName(className); }
 594  0 catch (RemoteException re) { error.log(re); return null; }
 595    }
 596   
 597  0 public void junitJVMReady() { }
 598   
 599    // --------- Class path methods ----------
 600  2 public void addExtraClassPath(File f) { _classPathManager.addExtraCP(f); }
 601  0 public void addProjectClassPath(File f) { _classPathManager.addProjectCP(f); }
 602  25 public void addBuildDirectoryClassPath(File f) { _classPathManager.addBuildDirectoryCP(f); }
 603  182 public void addProjectFilesClassPath(File f) { _classPathManager.addProjectFilesCP(f); }
 604  223 public void addExternalFilesClassPath(File f) { _classPathManager.addExternalFilesCP(f); }
 605  0 public Iterable<File> getClassPath() {
 606    // need to make a serializable snapshot
 607  0 return IterUtil.snapshot(_classPathManager.getClassPath());
 608    }
 609   
 610    }