Clover coverage report - DynamicJava Test Coverage (dynamicjava-20120303-r5436)
Coverage timestamp: Sat Mar 3 2012 03:02:19 CST
file stats: LOC: 398   Methods: 44
NCLOC: 317   Classes: 7
 
 Source file Conditionals Statements Methods TOTAL
StatementEvaluator.java 29.7% 45.7% 52.3% 42.9%
coverage coverage
 1    package edu.rice.cs.dynamicjava.interpreter;
 2   
 3    import java.util.Iterator;
 4    import java.lang.reflect.Array;
 5    import java.lang.reflect.Method;
 6    import java.lang.reflect.InvocationTargetException;
 7    import edu.rice.cs.plt.iter.IterUtil;
 8    import edu.rice.cs.plt.iter.ReadOnlyIterator;
 9    import edu.rice.cs.plt.lambda.WrappedException;
 10    import edu.rice.cs.plt.tuple.Option;
 11   
 12    import koala.dynamicjava.tree.*;
 13    import koala.dynamicjava.tree.visitor.*;
 14   
 15    import edu.rice.cs.dynamicjava.Options;
 16    import edu.rice.cs.dynamicjava.symbol.LocalVariable;
 17    import edu.rice.cs.dynamicjava.symbol.TypeSystem;
 18    import edu.rice.cs.dynamicjava.symbol.SymbolUtil;
 19   
 20    import static koala.dynamicjava.interpreter.NodeProperties.*;
 21   
 22    /**
 23    * Evaluates the given statement, assumed to have been processed by the {@link StatementChecker}
 24    * without any errors. Exceptions that occur during interpretation are wrapped in
 25    * {@link EvaluatorException}s; these, in turn, are wrapped in WrappedExceptions so that
 26    * they can be thrown within visitor methods. Changes in control flow are signalled by throwing
 27    * {@link ControlFlowException}s.
 28    */
 29    public class StatementEvaluator extends AbstractVisitor<StatementEvaluator.Result> {
 30   
 31    /**
 32    * Wraps an RuntimeBindings and an optional value. Strictly speaking, statements can sometimes produce
 33    * new bindings but should never have values. In practice, however, it's convenient to occasionally allow
 34    * a statement to produce a value in order to have a result to show the user -- the value of a variable
 35    * declaration, for example, may be that variable's value (depending on the Options used).
 36    */
 37    public static class Result {
 38    private Option<Object> _val;
 39    private RuntimeBindings _bindings;
 40   
 41  0 public Result(Object val, RuntimeBindings b) {
 42  0 _val = Option.some(val);
 43  0 _bindings = b;
 44    }
 45  1919 public Result(RuntimeBindings b) {
 46  1919 _val = Option.none();
 47  1919 _bindings = b;
 48    }
 49  502 public Option<Object> value() { return _val; }
 50  1609 public RuntimeBindings bindings() { return _bindings; }
 51    }
 52   
 53   
 54    private final RuntimeBindings _bindings;
 55    private final Options _opt;
 56   
 57  1787 public StatementEvaluator(RuntimeBindings bindings, Options opt) {
 58  1787 _bindings = bindings;
 59  1787 _opt = opt;
 60    }
 61   
 62   
 63  822 public Result evaluateSequence(Iterable<? extends Node> nodes) {
 64  822 Result result = new Result(_bindings);
 65  822 for (Node n : nodes) {
 66  1090 result = n.acceptVisitor(new StatementEvaluator(result.bindings(), _opt));
 67    }
 68  796 return result;
 69    }
 70   
 71   
 72    /* * * * * * * * * *
 73    * DECLARATIONS
 74    * * * * * * * * * */
 75   
 76  0 @Override public Result visit(PackageDeclaration node) { return new Result(_bindings); }
 77  9 @Override public Result visit(ImportDeclaration node) { return new Result(_bindings); }
 78  52 @Override public Result visit(ClassDeclaration node) { return new Result(_bindings); }
 79  0 @Override public Result visit(InterfaceDeclaration node) { return new Result(_bindings); }
 80  0 @Override public Result visit(ConstructorDeclaration node) { return new Result(_bindings); }
 81  183 @Override public Result visit(MethodDeclaration node) { return new Result(_bindings); }
 82   
 83  326 @Override public Result visit(VariableDeclaration node) {
 84    // even when an initializer is present, there may be a reference to the uninitialized
 85    // variable in the initializer
 86  326 Object init = SymbolUtil.initialValue(getErasedType(node).value());
 87  326 RuntimeBindings newB = new RuntimeBindings(_bindings, getVariable(node), init);
 88  326 if (node.getInitializer() != null) {
 89  316 newB.set(getVariable(node), new ExpressionEvaluator(newB, _opt).value(node.getInitializer()));
 90    }
 91  326 return new Result(newB);
 92    }
 93   
 94   
 95    /* * * * * * * * * *
 96    * STATEMENTS
 97    * * * * * * * * * */
 98   
 99  1 @Override public Result visit(EmptyStatement node) { return new Result(_bindings); }
 100   
 101  374 @Override public Result visit(ExpressionStatement node) {
 102  374 if (hasStatementTranslation(node)) {
 103  0 return getStatementTranslation(node).acceptVisitor(this);
 104    }
 105    else {
 106  374 Object val = new ExpressionEvaluator(_bindings, _opt).value(node.getExpression());
 107  374 if (node.getHasSemicolon() || getType(node.getExpression()).equals(TypeSystem.VOID)) {
 108  374 return new Result(_bindings);
 109    }
 110  0 else { return new Result(val, _bindings); }
 111    }
 112    }
 113   
 114  3 @Override public Result visit(WhileStatement node) {
 115  3 ExpressionEvaluator eval = new ExpressionEvaluator(_bindings, _opt);
 116  3 try {
 117  3 while ((Boolean) eval.value(node.getCondition())) {
 118  20 try { node.getBody().acceptVisitor(this); }
 119    catch (ContinueException e) {
 120  0 if (e.hasLabel() && !node.hasLabel(e.label())) { throw e; }
 121    }
 122    }
 123    }
 124    catch (BreakException e) {
 125  0 if (e.hasLabel() && !node.hasLabel(e.label())) { throw e; }
 126    }
 127  3 return new Result(_bindings);
 128    }
 129   
 130  9 @Override public Result visit(final ForEachStatement node) {
 131    // We *could* create an equivalent expression and then evaluate that
 132    // expression (that was done in a previous implementation), but it is
 133    // easier to just evaluate this directly
 134  9 LocalVariable param = getVariable(node.getParameter());
 135  9 RuntimeBindings newB = new RuntimeBindings(_bindings, param, null);
 136  9 final Object iterable = new ExpressionEvaluator(newB, _opt).value(node.getCollection());
 137  0 if (iterable == null) { throw new WrappedException(new EvaluatorException(new NullPointerException())); }
 138  9 Iterator<?> iter;
 139  9 if (iterable.getClass().isArray()) {
 140  6 final int length = Array.getLength(iterable);
 141  6 iter = new ReadOnlyIterator<Object>() {
 142    int i = 0;
 143  25 public boolean hasNext() { return i < length; }
 144  19 public Object next() {
 145  19 try { return Array.get(iterable, i++); }
 146  0 catch (ArrayIndexOutOfBoundsException e) { throw new WrappedException(new EvaluatorException(e)); }
 147    }
 148    };
 149    }
 150    else {
 151  3 try {
 152  3 Method getIterator = iterable.getClass().getMethod("iterator");
 153  3 try { getIterator.setAccessible(true); }
 154    catch (SecurityException e) { /* made a best attempt */ }
 155  3 iter = (Iterator<?>) getIterator.invoke(iterable);
 156    }
 157  0 catch (NoSuchMethodException e) { throw new RuntimeException(e); }
 158  0 catch (IllegalAccessException e) { throw new RuntimeException(e); }
 159  0 catch (InvocationTargetException e) { throw new WrappedException(new EvaluatorException(e.getCause())); }
 160    }
 161   
 162  9 StatementEvaluator seval = new StatementEvaluator(newB, _opt);
 163  9 try {
 164  9 while (true) {
 165  38 Object elt;
 166  38 try { // must catch any exceptions thrown by execution of the iterator's methods
 167  9 if (!iter.hasNext()) { break; }
 168  29 elt = iter.next();
 169    }
 170  0 catch (Throwable t) { throw new WrappedException(new EvaluatorException(t)); }
 171   
 172  29 try {
 173  29 newB.set(param, elt);
 174  29 node.getBody().acceptVisitor(seval);
 175    }
 176    catch (ContinueException e) {
 177  0 if (e.hasLabel() && !node.hasLabel(e.label())) { throw e; }
 178    }
 179    }
 180    }
 181    catch (BreakException e) {
 182  0 if (e.hasLabel() && !node.hasLabel(e.label())) { throw e; }
 183    }
 184  9 return new Result(_bindings);
 185    }
 186   
 187  17 @Override public Result visit(ForStatement node) {
 188  17 RuntimeBindings newB = _bindings;
 189  17 if (node.getInitialization() != null) {
 190  17 newB = evaluateSequence(node.getInitialization()).bindings();
 191    }
 192  17 Expression cond = node.getCondition();
 193  17 Iterable<Node> update = node.getUpdate();
 194  17 ExpressionEvaluator eval = new ExpressionEvaluator(newB, _opt);
 195  17 StatementEvaluator seval = new StatementEvaluator(newB, _opt);
 196  17 try {
 197  17 while (cond == null || (Boolean) eval.value(cond)) {
 198  44 try { node.getBody().acceptVisitor(seval); }
 199    catch (ContinueException e) {
 200  0 if (e.hasLabel() && !node.hasLabel(e.label())) { throw e; }
 201    }
 202  44 if (update != null) { seval.evaluateSequence(update); }
 203    }
 204    }
 205    catch (BreakException e) {
 206  0 if (e.hasLabel() && !node.hasLabel(e.label())) { throw e; }
 207    }
 208  17 return new Result(_bindings);
 209    }
 210   
 211  3 @Override public Result visit(DoStatement node) {
 212  3 ExpressionEvaluator eval = new ExpressionEvaluator(_bindings, _opt);
 213  3 try {
 214  3 do {
 215  21 try { node.getBody().acceptVisitor(this); }
 216    catch (ContinueException e) {
 217  0 if (e.hasLabel() && !node.hasLabel(e.label())) { throw e; }
 218    }
 219  21 } while ((Boolean) eval.value(node.getCondition()));
 220    } catch (BreakException e) {
 221  0 if (e.hasLabel() && !node.hasLabel(e.label())) { throw e; }
 222    }
 223  3 return new Result(_bindings);
 224    }
 225   
 226  0 @Override public Result visit(SwitchStatement node) {
 227  0 ExpressionEvaluator eval = new ExpressionEvaluator(_bindings, _opt);
 228  0 Object sel = eval.value(node.getSelector());
 229  0 Iterator<SwitchBlock> body = node.getBindings().iterator();
 230   
 231    // Try to match a block
 232  0 for (SwitchBlock block : node.getBindings()) {
 233  0 if (block.getExpression() != null && eval.value(block.getExpression()).equals(sel)) {
 234  0 break;
 235    }
 236  0 else { body.next(); /* remove this block from the body */ }
 237    }
 238   
 239    // Try to match a default block
 240  0 if (!body.hasNext()) {
 241  0 body = node.getBindings().iterator();
 242  0 for (SwitchBlock block : node.getBindings()) {
 243  0 if (block.getExpression() == null) { break; }
 244  0 else { body.next(); /* remove this block from the body */ }
 245    }
 246    }
 247   
 248  0 Iterable<Node> toEvaluate = IterUtil.empty();
 249  0 while (body.hasNext()) { toEvaluate = IterUtil.compose(toEvaluate, body.next().getStatements()); }
 250  0 try { evaluateSequence(toEvaluate); }
 251    catch (BreakException e) {
 252  0 if (e.hasLabel()) { throw e; }
 253    }
 254  0 return new Result(_bindings);
 255    }
 256   
 257  0 @Override public Result visit(LabeledStatement node) {
 258  0 try { node.getStatement().acceptVisitor(this); }
 259    catch (BreakException e) {
 260  0 if (!e.hasLabel() || !e.label().equals(node.getLabel())) { throw e; }
 261    }
 262  0 return new Result(_bindings);
 263    }
 264   
 265  0 @Override public Result visit(SynchronizedStatement node) {
 266  0 synchronized (new ExpressionEvaluator(_bindings, _opt).value(node.getLock())) {
 267  0 node.getBody().acceptVisitor(this);
 268    }
 269  0 return new Result(_bindings);
 270    }
 271   
 272  0 @Override public Result visit(BreakStatement node) {
 273  0 if (node.getLabel() == null) { throw new BreakException(); }
 274  0 else { throw new BreakException(node.getLabel()); }
 275    }
 276   
 277  0 @Override public Result visit(ContinueStatement node) {
 278  0 if (node.getLabel() == null) { throw new ContinueException(); }
 279  0 else { throw new ContinueException(node.getLabel()); }
 280    }
 281   
 282  0 @Override public Result visit(TryStatement node) {
 283  0 try { node.getTryBlock().acceptVisitor(this); }
 284    catch (WrappedException e) {
 285  0 if (e.getCause() instanceof EvaluatorException) {
 286  0 Throwable t = e.getCause().getCause();
 287  0 boolean handled = false;
 288  0 for (CatchStatement cs : node.getCatchStatements()) {
 289  0 if (getErasedType(cs).value().isInstance(t)) {
 290  0 handled = true;
 291  0 RuntimeBindings newB = new RuntimeBindings(_bindings, getVariable(cs.getException()), t);
 292  0 cs.getBlock().acceptVisitor(new StatementEvaluator(newB, _opt));
 293  0 break;
 294    }
 295    }
 296  0 if (!handled) { throw e; }
 297    }
 298  0 else { throw e; }
 299    }
 300    finally {
 301  0 if (node.getFinallyBlock() != null) { node.getFinallyBlock().acceptVisitor(this); }
 302    }
 303  0 return new Result(_bindings);
 304    }
 305   
 306  0 @Override public Result visit(ThrowStatement node) {
 307  0 Throwable t = (Throwable) new ExpressionEvaluator(_bindings, _opt).value(node.getExpression());
 308    // bug fix for DrJava bug 3008828
 309  0 if (t == null) {
 310    // as per Java Language Specification (JLS):
 311    // http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.18
 312    // "If evaluation of the Expression completes normally, producing a null value,
 313    // then an instance V' of class NullPointerException is created and thrown instead of null."
 314  0 t = new NullPointerException();
 315  0 t.setStackTrace(new StackTraceElement[0]);
 316    }
 317  0 throw new WrappedException(new EvaluatorException(t));
 318    }
 319   
 320  26 @Override public Result visit(ReturnStatement node) {
 321  0 if (node.getExpression() == null) { throw new ReturnException(); }
 322    else {
 323  26 Object result = new ExpressionEvaluator(_bindings, _opt).value(node.getExpression());
 324  26 throw new ReturnException(result);
 325    }
 326    }
 327   
 328  114 @Override public Result visit(IfThenStatement node) {
 329  114 if ((Boolean) new ExpressionEvaluator(_bindings, _opt).value(node.getCondition())) {
 330  3 node.getThenStatement().acceptVisitor(this);
 331    }
 332  114 return new Result(_bindings);
 333    }
 334   
 335  6 @Override public Result visit(IfThenElseStatement node) {
 336  6 if ((Boolean) new ExpressionEvaluator(_bindings, _opt).value(node.getCondition())) {
 337  3 node.getThenStatement().acceptVisitor(this);
 338    }
 339    else {
 340  3 node.getElseStatement().acceptVisitor(this);
 341    }
 342  6 return new Result(_bindings);
 343    }
 344   
 345  0 @Override public Result visit(AssertStatement node) {
 346    // TODO: detect whether assertions are turned on in this context
 347  0 ExpressionEvaluator eval = new ExpressionEvaluator(_bindings, _opt);
 348  0 if ((Boolean) eval.value(node.getCondition())) {
 349  0 return new Result(_bindings);
 350    }
 351    else {
 352  0 if (node.getFailString() == null) {
 353  0 throw new WrappedException(new EvaluatorException(new AssertionError()));
 354    }
 355    else {
 356  0 Object messageObj = eval.value(node.getFailString());
 357  0 String message;
 358  0 try { message = messageObj.toString(); }
 359  0 catch (Throwable t) { throw new WrappedException(new EvaluatorException(t)); }
 360  0 throw new WrappedException(new EvaluatorException(new AssertionError(message)));
 361    }
 362    }
 363    }
 364   
 365  259 @Override public Result visit(BlockStatement node) {
 366  259 return evaluateSequence(node.getStatements());
 367    }
 368   
 369   
 370    public static class ControlFlowException extends RuntimeException {}
 371   
 372    public static class LabelControlException extends ControlFlowException {
 373    private final String _label;
 374  0 public LabelControlException() { _label = null; }
 375  0 public LabelControlException(String label) { _label = label; }
 376  0 public boolean hasLabel() { return _label != null; }
 377  0 public String label() { return _label; }
 378    }
 379   
 380    public static class ContinueException extends LabelControlException {
 381  0 public ContinueException() { super(); }
 382  0 public ContinueException(String label) { super(label); }
 383    }
 384   
 385    public static class BreakException extends LabelControlException {
 386  0 public BreakException() { super(); }
 387  0 public BreakException(String label) { super(label); }
 388    }
 389   
 390    public static class ReturnException extends ControlFlowException {
 391    private final Option<Object> _value;
 392  0 public ReturnException() { _value = Option.none(); }
 393  26 public ReturnException(Object value) { _value = Option.some(value); }
 394  26 public Option<Object> value() { return _value; }
 395    }
 396   
 397   
 398    }