Clover coverage report - DynamicJava Test Coverage (dynamicjava-20120303-r5436)
Coverage timestamp: Sat Mar 3 2012 03:02:19 CST
file stats: LOC: 414   Methods: 75
NCLOC: 310   Classes: 4
 
 Source file Conditionals Statements Methods TOTAL
JavaClass.java 23.9% 37% 41.3% 36%
coverage coverage
 1    package edu.rice.cs.dynamicjava.symbol;
 2   
 3    import java.util.List;
 4    import java.util.ArrayList;
 5    import java.lang.reflect.*;
 6   
 7    import edu.rice.cs.dynamicjava.Options;
 8    import edu.rice.cs.dynamicjava.symbol.type.*;
 9    import edu.rice.cs.dynamicjava.symbol.type.Type; // resolves ambiguity with java.lang.reflect.Type
 10    import edu.rice.cs.dynamicjava.interpreter.RuntimeBindings;
 11    import edu.rice.cs.dynamicjava.interpreter.EvaluatorException;
 12   
 13    import edu.rice.cs.plt.reflect.ReflectUtil;
 14    import edu.rice.cs.plt.tuple.Option;
 15    import edu.rice.cs.plt.iter.IterUtil;
 16    import edu.rice.cs.plt.lambda.Lambda;
 17    import edu.rice.cs.plt.lambda.Thunk;
 18    import edu.rice.cs.plt.lambda.Box;
 19    import edu.rice.cs.plt.lambda.LazyThunk;
 20    import edu.rice.cs.plt.lambda.WrappedException;
 21   
 22    import static edu.rice.cs.plt.debug.DebugUtil.debug;
 23   
 24    /**
 25    * DJClass implementation that wraps a Java reflection Class object. The {@link Java5Class} version
 26    * should be used instead if the class object supports Java 5 methods like
 27    * {@link Class#getTypeParameters}.
 28    */
 29    public class JavaClass implements DJClass {
 30   
 31    protected Class<?> _c;
 32   
 33  33035 public JavaClass(Class<?> c) { _c = c; }
 34   
 35  0 public String packageName() {
 36  0 String name = _c.getName();
 37  0 int dot = name.lastIndexOf('.');
 38  0 if (dot == -1) { return ""; }
 39  0 else { return name.substring(0, dot); }
 40    }
 41   
 42    /** Produces the binary name for the given class (as in {@link Class#getName}) */
 43  420 public String fullName() { return _c.getName(); }
 44   
 45  242 public boolean isAnonymous() { return ReflectUtil.isAnonymousClass(_c); }
 46   
 47  0 public String declaredName() {
 48  0 if (ReflectUtil.isAnonymousClass(_c)) {
 49  0 throw new IllegalArgumentException("Anonymous class has no declared name");
 50    }
 51  0 else { return ReflectUtil.simpleName(_c); }
 52    }
 53   
 54  429 public boolean isInterface() { return Modifier.isInterface(_c.getModifiers()); }
 55  18323 public boolean isStatic() { return Modifier.isStatic(_c.getModifiers()); }
 56  429 public boolean isAbstract() { return Modifier.isAbstract(_c.getModifiers()); }
 57  0 public boolean isFinal() { return Modifier.isFinal(_c.getModifiers()); }
 58  0 public Access accessibility() { return extractAccessibility(_c.getModifiers()); }
 59  28 public boolean hasRuntimeBindingsParams() { return false; }
 60   
 61  0 public Access.Module accessModule() {
 62    // Reflection API (1.4) doesn't tell us enclosing classes, so we're limited to declaring classes
 63  0 Class<?> result = _c;
 64  0 Class<?> outer = result.getDeclaringClass();
 65  0 while (outer != null) { result = outer; outer = result.getDeclaringClass(); }
 66  0 return new JavaClass(result);
 67    }
 68   
 69  0 public DJClass declaringClass() {
 70  0 Class<?> outer = _c.getDeclaringClass();
 71  0 return (outer == null) ? null : new JavaClass(outer);
 72    }
 73   
 74    /** List all type variables declared by this class (but not by its enclosing classes) */
 75  0 public Iterable<VariableType> declaredTypeParameters() { return IterUtil.empty(); }
 76   
 77    /** List the declared supertypes of this class */
 78  0 public Iterable<Type> declaredSupertypes() {
 79  0 Type superC = immediateSuperclass();
 80  0 Iterable<Type> superIs;
 81  0 if (_c.getInterfaces() == null) { superIs = IterUtil.empty(); }
 82  0 else { superIs = IterUtil.mapSnapshot(IterUtil.asIterable(_c.getInterfaces()), CLASS_AS_TYPE); }
 83  0 return superC == null ? superIs : IterUtil.compose(superC, superIs);
 84    }
 85   
 86  0 public Iterable<DJField> declaredFields() {
 87  0 return IterUtil.mapSnapshot(IterUtil.asIterable(_c.getDeclaredFields()), CONVERT_FIELD);
 88    }
 89   
 90  0 public Iterable<DJConstructor> declaredConstructors() {
 91  0 return IterUtil.mapSnapshot(IterUtil.asIterable(_c.getDeclaredConstructors()), CONVERT_CONSTRUCTOR);
 92    }
 93   
 94  0 public Iterable<DJMethod> declaredMethods() {
 95  0 return IterUtil.mapSnapshot(IterUtil.asIterable(_c.getDeclaredMethods()), CONVERT_METHOD);
 96    }
 97   
 98  0 public Iterable<DJClass> declaredClasses() {
 99  0 return IterUtil.mapSnapshot(IterUtil.asIterable(_c.getDeclaredClasses()), CONVERT_CLASS);
 100    }
 101   
 102   
 103    /**
 104    * Return the type bound to {@code super} in the context of this class, or
 105    * {@code null} if {@code super} is not defined
 106    */
 107  0 public Type immediateSuperclass() {
 108  0 Class<?> superT = _c.getSuperclass();
 109  0 return (superT == null) ? null : classAsType(superT);
 110    }
 111   
 112  314 public Class<?> load() { return _c; }
 113   
 114  0 public String toString() { return "JavaClass(" + _c.getName() + ")"; }
 115   
 116  29473 public boolean equals(Object o) {
 117  7 if (this == o) { return true; }
 118  0 else if (!o.getClass().equals(getClass())) { return false; }
 119  29466 else { return _c.equals(((JavaClass) o)._c); }
 120    }
 121   
 122  20593 public int hashCode() { return (getClass().hashCode() << 1) ^ _c.hashCode(); }
 123   
 124    /** Convert a class object to a type */
 125  0 private static Type classAsType(Class<?> c) {
 126  0 if (c.isPrimitive()) { return SymbolUtil.typeOfPrimitiveClass(c); }
 127  0 else if (c.isArray()) { return new SimpleArrayType(classAsType(c.getComponentType())); }
 128  0 else { return new SimpleClassType(new JavaClass(c)); }
 129    }
 130   
 131    @SuppressWarnings("unchecked") // java.lang.Class methods return (raw) type Class[] in Java 5 (fixed in Java 6)
 132    private static final Lambda<Class, Type> CLASS_AS_TYPE = new Lambda<Class, Type>() {
 133  0 public Type value(Class c) {
 134  0 return classAsType(c);
 135    }
 136    };
 137   
 138    @SuppressWarnings("unchecked") // java.lang.Class methods return (raw) type Class[] in Java 5 (fixed in Java 6)
 139    private static final Lambda<Class, DJClass> CONVERT_CLASS = new Lambda<Class, DJClass>() {
 140  0 public DJClass value(Class c) { return new JavaClass(c); }
 141    };
 142   
 143    /** Non-static because JavaField is non-static. */
 144    private final Lambda<Field, DJField> CONVERT_FIELD = new Lambda<Field, DJField>() {
 145  0 public DJField value(Field f) { return new JavaField(f); }
 146    };
 147   
 148    /** Non-static because JavaConstructor is non-static. */
 149    @SuppressWarnings("unchecked") // java.lang.Class methods return (raw) type Constructor[] in Java 5 (fixed in Java 6)
 150    private final Lambda<Constructor, DJConstructor> CONVERT_CONSTRUCTOR =
 151    new Lambda<Constructor, DJConstructor>() {
 152  0 public DJConstructor value(Constructor k) { return new JavaConstructor(k); }
 153    };
 154   
 155    /** Non-static because JavaMethod is non-static. */
 156    private final Lambda<Method, DJMethod> CONVERT_METHOD = new Lambda<Method, DJMethod>() {
 157  0 public DJMethod value(Method m) { return new JavaMethod(m); }
 158    };
 159   
 160    protected class JavaField implements DJField {
 161    protected final Field _f;
 162  58 public JavaField(Field f) { _f = f; }
 163  40 public String declaredName() { return _f.getName(); }
 164  0 public DJClass declaringClass() { return JavaClass.this; }
 165  0 public Type type() { return classAsType(_f.getType()); }
 166  1 public boolean isFinal() { return Modifier.isFinal(_f.getModifiers()); }
 167  37 public boolean isStatic() { return Modifier.isStatic(_f.getModifiers()); }
 168  2 public Access accessibility() { return extractAccessibility(_f.getModifiers()); }
 169  0 public Access.Module accessModule() { return JavaClass.this.accessModule(); }
 170   
 171  1 public Option<Object> constantValue() {
 172    // Whether a field is declared as a constant is not available via the reflection API,
 173    // so we approximate by treating all static final fields with a primitive/String type as constants.
 174    // (Note that some code may execute here during the type checking phase, before "run time".
 175    // This seems to be unavoidable given the reflection-based design.)
 176  1 if (isStatic() && isFinal() && (_f.getType().isPrimitive() || _f.getType().equals(String.class))) {
 177  1 try { return Option.some(boxForReceiver(null).value()); }
 178  0 catch (WrappedException e) { return Option.none(); }
 179    }
 180  0 else { return Option.none(); }
 181    }
 182   
 183  34 public Box<Object> boxForReceiver(final Object receiver) {
 184  34 return new Box<Object>() {
 185   
 186  9 public Object value() {
 187  9 if (!isStatic() && receiver == null) {
 188  0 throw new WrappedException(new EvaluatorException(new NullPointerException()));
 189    }
 190  9 try { _f.setAccessible(true); }
 191  0 catch (SecurityException e) { debug.log(e); /* ignore -- we can't relax accessibility */ }
 192  9 try { return _f.get(receiver); }
 193    catch (IllegalAccessException e) {
 194    // should have been caught by static analysis
 195  0 throw new RuntimeException(e);
 196    }
 197    catch (LinkageError e) {
 198    // may be ExceptionInInitializerError, NoClassDefFoundError, etc.
 199  0 throw new WrappedException(new EvaluatorException(e, FIELD_GET_EXTRA_STACK));
 200    }
 201    catch (Throwable t) {
 202    // Errors, etc., can be thrown and not caught during a class's static initialization
 203  0 throw new WrappedException(new EvaluatorException(t, FIELD_GET_EXTRA_STACK));
 204    }
 205    }
 206   
 207  25 public void set(Object o) {
 208  25 if (!isStatic() && receiver == null) {
 209  0 throw new WrappedException(new EvaluatorException(new NullPointerException()));
 210    }
 211  25 try { _f.setAccessible(true); }
 212  0 catch (SecurityException e) { debug.log(e); /* ignore -- we can't relax accessibility */ }
 213  25 try { _f.set(receiver, o); }
 214    catch (IllegalAccessException e) {
 215    // should have been caught by static analysis
 216  0 throw new RuntimeException(e);
 217    }
 218    catch (LinkageError e) {
 219    // may be ExceptionInInitializerError, NoClassDefFoundError, etc.
 220  0 throw new WrappedException(new EvaluatorException(e, FIELD_SET_EXTRA_STACK));
 221    }
 222    catch (Throwable t) {
 223    // Errors, etc., can be thrown and not caught during a class's static initialization
 224  0 throw new WrappedException(new EvaluatorException(t, FIELD_SET_EXTRA_STACK));
 225    }
 226    }
 227   
 228    };
 229    }
 230   
 231  0 public String toString() { return "JavaField(" + declaredName() + ")"; }
 232    }
 233   
 234    private static final String[] FIELD_GET_EXTRA_STACK =
 235    new String[]{ "java.lang.reflect.Field.get",
 236    "java.lang.reflect.Field.getFieldAccessor",
 237    "java.lang.reflect.Field.acquireFieldAccessor",
 238    "sun.reflect.ReflectionFactory.newFieldAccessor",
 239    "sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor",
 240    "sun.misc.Unsafe.ensureClassInitialized" };
 241   
 242    private static final String[] FIELD_SET_EXTRA_STACK =
 243    new String[]{ "java.lang.reflect.Field.set",
 244    "java.lang.reflect.Field.getFieldAccessor",
 245    "java.lang.reflect.Field.acquireFieldAccessor",
 246    "sun.reflect.ReflectionFactory.newFieldAccessor",
 247    "sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor",
 248    "sun.misc.Unsafe.ensureClassInitialized" };
 249   
 250   
 251    /** Non-static in order to determine the outer type. */
 252    protected class JavaConstructor implements DJConstructor {
 253    protected final Constructor<?> _k;
 254    protected final Type _outerType;
 255    private final Thunk<Iterable<LocalVariable>> _params;
 256   
 257  1148 public JavaConstructor(Constructor<?> k) {
 258  1148 _k = k;
 259  1148 DJClass outer = SymbolUtil.dynamicOuterClass(JavaClass.this);
 260  1148 _outerType = (outer == null) ? null : SymbolUtil.thisType(outer);
 261  1148 _params = makeParamThunk(); /* allows overriding */
 262    }
 263   
 264  0 public String declaredName() { return isAnonymous() ? "<anonymous>" : JavaClass.this.declaredName(); }
 265  0 public DJClass declaringClass() { return JavaClass.this; }
 266  1139 public Access accessibility() { return extractAccessibility(_k.getModifiers()); }
 267  0 public Access.Module accessModule() { return JavaClass.this.accessModule(); }
 268  0 protected Thunk<Iterable<LocalVariable>> makeParamThunk() { return paramFactory(_k.getParameterTypes()); }
 269  0 public Iterable<VariableType> typeParameters() { return IterUtil.empty(); }
 270  0 public DJConstructor declaredSignature() { return this; }
 271   
 272  1293 public Iterable<LocalVariable> parameters() {
 273  1293 Iterable<LocalVariable> result = _params.value();
 274  0 if (_outerType != null) { result = IterUtil.skipFirst(result); }
 275  1293 return result;
 276    }
 277   
 278  1116 public Type returnType() { return SymbolUtil.thisType(JavaClass.this); }
 279   
 280  0 public Iterable<Type> thrownTypes() {
 281  0 return IterUtil.mapSnapshot(IterUtil.asIterable(_k.getExceptionTypes()), CLASS_AS_TYPE);
 282    }
 283   
 284  141 public Object evaluate(Object outer, Iterable<Object> args, RuntimeBindings bindings, Options options)
 285    throws EvaluatorException {
 286  141 if (_outerType != null) {
 287  0 if (outer == null) {
 288  0 throw new WrappedException(new EvaluatorException(new NullPointerException()));
 289    }
 290  0 args = IterUtil.compose(outer, args);
 291    }
 292   
 293  141 try { _k.setAccessible(true); }
 294  0 catch (SecurityException e) { debug.log(e); /* ignore -- we can't relax accessibility */ }
 295   
 296  141 Object[] argsArray = IterUtil.toArray(args, Object.class);
 297  141 try {
 298  141 return _k.newInstance(argsArray);
 299    }
 300    catch (InvocationTargetException e) {
 301  0 throw new EvaluatorException(e.getCause(), CONSTRUCTOR_EXTRA_STACK);
 302    }
 303    catch (LinkageError e) {
 304    // may be ExceptionInInitializerError, NoClassDefFoundError, etc.
 305  0 throw new EvaluatorException(e, CONSTRUCTOR_EXTRA_STACK);
 306    }
 307    catch (IllegalAccessException e) {
 308    // This should have been caught by static analysis
 309  0 throw new RuntimeException(e);
 310    }
 311    catch (InstantiationException e) {
 312    // This should have been caught by static analysis
 313  0 throw new RuntimeException(e);
 314    }
 315    catch (Throwable t) {
 316    // Errors, etc., can be thrown and not caught during a class's static initialization
 317  0 throw new EvaluatorException(t, METHOD_EXTRA_STACK);
 318    }
 319    }
 320   
 321  0 public String toString() { return "JavaConstructor(" + declaredName() + ")"; }
 322    }
 323   
 324    private static final String[] CONSTRUCTOR_EXTRA_STACK =
 325    new String[]{ "java.lang.reflect.Constructor.newInstance",
 326    "sun.reflect.DelegatingConstructorAccessorImpl.newInstance",
 327    "sun.reflect.NativeConstructorAccessorImpl.newInstance",
 328    "sun.reflect.NativeConstructorAccessorImpl.newInstance0" };
 329   
 330   
 331    protected class JavaMethod implements DJMethod {
 332    protected final Method _m;
 333    private final Thunk<Iterable<LocalVariable>> _params;
 334  9076 public JavaMethod(Method m) { _m = m; _params = makeParamThunk(); /* allows overriding */ }
 335  0 protected Thunk<Iterable<LocalVariable>> makeParamThunk() { return paramFactory(_m.getParameterTypes()); }
 336  9473 public String declaredName() { return _m.getName(); }
 337  0 public DJClass declaringClass() { return JavaClass.this; }
 338  539 public boolean isStatic() { return Modifier.isStatic(_m.getModifiers()); }
 339  0 public boolean isAbstract() { return Modifier.isAbstract(_m.getModifiers()); }
 340  0 public boolean isFinal() { return Modifier.isFinal(_m.getModifiers()); }
 341  1043 public Access accessibility() { return extractAccessibility(_m.getModifiers()); }
 342  0 public Access.Module accessModule() { return JavaClass.this.accessModule(); }
 343  0 public Type returnType() { return classAsType(_m.getReturnType()); }
 344  0 public Iterable<VariableType> typeParameters() { return IterUtil.empty(); }
 345  776 public Iterable<LocalVariable> parameters() { return _params.value(); }
 346  0 public Iterable<Type> thrownTypes() {
 347  0 return IterUtil.mapSnapshot(IterUtil.asIterable(_m.getExceptionTypes()), CLASS_AS_TYPE);
 348    }
 349   
 350  0 public DJMethod declaredSignature() { return this; }
 351  137 public Object evaluate(Object receiver, Iterable<Object> args, RuntimeBindings bindings,
 352    Options options) throws EvaluatorException {
 353  137 if (!isStatic() && receiver == null) {
 354  0 throw new WrappedException(new EvaluatorException(new NullPointerException()));
 355    }
 356   
 357  137 try { _m.setAccessible(true); }
 358  0 catch (SecurityException e) { debug.log(e); /* ignore -- we can't relax accessibility */ }
 359   
 360  137 Object[] argsArray = IterUtil.toArray(args, Object.class);
 361  137 try {
 362  137 return _m.invoke(receiver, argsArray);
 363    }
 364    catch (InvocationTargetException e) {
 365  0 throw new EvaluatorException(e.getCause(), METHOD_EXTRA_STACK);
 366    }
 367    catch (LinkageError e) {
 368    // may be ExceptionInInitializerError, NoClassDefFoundError, etc.
 369  0 throw new EvaluatorException(e, METHOD_EXTRA_STACK);
 370    }
 371    catch (IllegalAccessException e) {
 372    // This should have been caught by static analysis
 373  0 throw new RuntimeException(e);
 374    }
 375    catch (Throwable t) {
 376    // Errors, etc., can be thrown and not caught during a class's static initialization
 377  0 throw new EvaluatorException(t, METHOD_EXTRA_STACK);
 378    }
 379    }
 380   
 381  0 public String toString() { return "JavaMethod(" + declaredName() + ")"; }
 382    }
 383   
 384    private static final String[] METHOD_EXTRA_STACK =
 385    new String[] { "java.lang.reflect.Method.invoke",
 386    "sun.reflect.DelegatingMethodAccessorImpl.invoke",
 387    "sun.reflect.NativeMethodAccessorImpl.invoke",
 388    "sun.reflect.NativeMethodAccessorImpl.invoke0" };
 389   
 390  0 private static Thunk<Iterable<LocalVariable>> paramFactory(final Class<?>[] cs) {
 391    // Caches LocalVariables so we don't create duplicates
 392  0 return LazyThunk.make(new Thunk<Iterable<LocalVariable>>() {
 393  0 public Iterable<LocalVariable> value() {
 394  0 List<LocalVariable> result = new ArrayList<LocalVariable>(cs.length);
 395    // TODO: can we access better information about the parameters -- names, final declarations?
 396  0 int argNum = 1;
 397  0 for (Class<?> c : cs) { result.add(new LocalVariable("a" + argNum++, classAsType(c), false)); }
 398    // Must wrap result in something that implements Iterable for Retroweaver compatibility:
 399    // normally, it's not a problem; but erasure inserts casts to the thunk's parameter type,
 400    // is translated to the Retroweaver Iterable type.
 401  0 return IterUtil.asSizedIterable(result);
 402    }
 403    });
 404    }
 405   
 406    /** Convert a reflection modifier int to an appropriate Access object */
 407  2184 private static Access extractAccessibility(int mods) {
 408  2184 if (Modifier.isPublic(mods)) { return Access.PUBLIC; }
 409  0 else if (Modifier.isProtected(mods)) { return Access.PROTECTED; }
 410  0 else if (Modifier.isPrivate(mods)) { return Access.PRIVATE; }
 411  0 else { return Access.PACKAGE; }
 412    }
 413   
 414    }