Clover coverage report - DynamicJava Test Coverage (dynamicjava-20120303-r5436)
Coverage timestamp: Sat Mar 3 2012 03:02:19 CST
file stats: LOC: 1,442   Methods: 115
NCLOC: 1,098   Classes: 4
 
 Source file Conditionals Statements Methods TOTAL
TreeCompiler.java 36% 58.1% 56.5% 54%
coverage coverage
 1    package edu.rice.cs.dynamicjava.interpreter;
 2   
 3    import java.lang.reflect.Modifier;
 4    import java.util.*;
 5    import edu.rice.cs.plt.iter.IterUtil;
 6    import edu.rice.cs.plt.lambda.Lambda;
 7    import edu.rice.cs.plt.lambda.Runnable2;
 8    import edu.rice.cs.plt.lambda.WrappedException;
 9    import edu.rice.cs.plt.reflect.JavaVersion;
 10    import edu.rice.cs.plt.tuple.Pair;
 11    import edu.rice.cs.plt.collect.CollectUtil;
 12   
 13    import org.objectweb.asm.MethodVisitor;
 14    import org.objectweb.asm.ClassWriter;
 15   
 16    import edu.rice.cs.dynamicjava.Options;
 17    import edu.rice.cs.dynamicjava.symbol.*;
 18    import edu.rice.cs.dynamicjava.symbol.type.*;
 19    import koala.dynamicjava.tree.*;
 20    import koala.dynamicjava.tree.tiger.*;
 21    import koala.dynamicjava.tree.visitor.*;
 22    import koala.dynamicjava.interpreter.NodeProperties;
 23   
 24    import static org.objectweb.asm.Opcodes.*;
 25    import static koala.dynamicjava.tree.ModifierSet.Modifier.*;
 26   
 27    /**
 28    * Compiles an AST class declaration by producing a stub class, where each method consists of
 29    * forwarding calls to the interpreter. To preserve enclosing context (since all DynamicJava class
 30    * declarations appear in a local context), all constructors and static methods are parameterized
 31    * by an additional {@code RuntimeBindings} argument (the exception is inner classes, where constructors
 32    * are instead parameterized, as are standard javac compiled inner classes, by an enclosing object).
 33    * A compiled class looks something like the following:
 34    * <pre>
 35    * class CompiledClass extends SuperClass {
 36    * private static final TreeCompiler.EvaluationAdapter $adapter;
 37    * final TreeCompiler.BindingsFactory $bindingsFactory;
 38    *
 39    * static String STATIC_FIELD;
 40    * Number instanceField;
 41    *
 42    * static {
 43    * $adapter = ((TreeClassLoader) CompiledClass.class.getClassLoader()).getAdapter("CompiledClass");
 44    * STATIC_FIELD = $adapter.evaluateExpression("STATIC_FIELD", RuntimeBindings.EMPTY);
 45    * }
 46    *
 47    * public CompiledClass(RuntimeBindings $bindings, int x, int y) {
 48    * $bindingsFactory = $adapter.makeBindingsFactory($bindings);
 49    * Object[] $args = new Object[]{ x, y };
 50    * super($adapter.evaluateConstructorCallArg("(II)V", 0, $bindings, $args),
 51    * $adapter.evaluateConstructorCallArg("(II)V", 1, $bindings, $args));
 52    * instanceField = $adapter.evaluateExpression("instanceField", $bindingsFactory.value(this));
 53    * $adapter.evaluateConstructorBody("(II)V", $bindingsFactory.value(this), $args);
 54    * }
 55    *
 56    * public Number getInstanceField() {
 57    * Object[] $args = new Object[]{};
 58    * return $adapter.evaluateMethodBody("getInstance()Ljava/lang/Number;",
 59    * $bindingsFactory.value(this), $args);
 60    * }
 61    *
 62    * public static String staticMethod(RuntimeBindings $bindings, String arg) {
 63    * Object[] $args = new Object[]{ arg };
 64    * return $adapter.evaluateMethod("staticMethod(Ljava/lang/String;)Ljava/lang/String;",
 65    * $bindings, $args);
 66    * }
 67    *
 68    * }
 69    * </pre>
 70    */
 71    public class TreeCompiler {
 72   
 73    private static final String ADAPTER_FIELD = "$adapter";
 74    private static final String BINDINGS_FACTORY_FIELD = "$bindingsFactory";
 75   
 76    private static final String RUNTIME_BINDINGS_NAME =
 77    org.objectweb.asm.Type.getInternalName(RuntimeBindings.class);
 78    private static final String EVALUATION_ADAPTER_NAME =
 79    org.objectweb.asm.Type.getInternalName(EvaluationAdapter.class);
 80    private static final String BINDINGS_FACTORY_NAME =
 81    org.objectweb.asm.Type.getInternalName(BindingsFactory.class);
 82    private static final String TREE_CLASS_LOADER_NAME =
 83    org.objectweb.asm.Type.getInternalName(TreeClassLoader.class);
 84   
 85    private static final String OBJECT_DESCRIPTOR =
 86    org.objectweb.asm.Type.getDescriptor(Object.class);
 87    private static final String CLASS_DESCRIPTOR =
 88    org.objectweb.asm.Type.getDescriptor(Class.class);
 89    private static final String CLASS_LOADER_DESCRIPTOR =
 90    org.objectweb.asm.Type.getDescriptor(ClassLoader.class);
 91    private static final String STRING_DESCRIPTOR =
 92    org.objectweb.asm.Type.getDescriptor(String.class);
 93    private static final String BOOLEAN_DESCRIPTOR =
 94    org.objectweb.asm.Type.getDescriptor(Boolean.class);
 95    private static final String CHARACTER_DESCRIPTOR =
 96    org.objectweb.asm.Type.getDescriptor(Character.class);
 97    private static final String BYTE_DESCRIPTOR =
 98    org.objectweb.asm.Type.getDescriptor(Byte.class);
 99    private static final String SHORT_DESCRIPTOR =
 100    org.objectweb.asm.Type.getDescriptor(Short.class);
 101    private static final String INTEGER_DESCRIPTOR =
 102    org.objectweb.asm.Type.getDescriptor(Integer.class);
 103    private static final String LONG_DESCRIPTOR =
 104    org.objectweb.asm.Type.getDescriptor(Long.class);
 105    private static final String FLOAT_DESCRIPTOR =
 106    org.objectweb.asm.Type.getDescriptor(Float.class);
 107    private static final String DOUBLE_DESCRIPTOR =
 108    org.objectweb.asm.Type.getDescriptor(Double.class);
 109    private static final String RUNTIME_BINDINGS_DESCRIPTOR =
 110    org.objectweb.asm.Type.getDescriptor(RuntimeBindings.class);
 111    private static final String EVALUATION_ADAPTER_DESCRIPTOR =
 112    org.objectweb.asm.Type.getDescriptor(EvaluationAdapter.class);
 113    private static final String BINDINGS_FACTORY_DESCRIPTOR =
 114    org.objectweb.asm.Type.getDescriptor(BindingsFactory.class);
 115   
 116   
 117    private final TreeClass _treeClass;
 118    private final Options _opt;
 119    private ClassWriter _classWriter;
 120    private final EvaluationAdapter _adapter;
 121    private final String _name;
 122    private final boolean _java5;
 123    private final Map<String, MethodDeclaration> _methods; // keys: name + descriptor
 124    private final Map<String, ConstructorDeclaration> _constructors; // keys: descriptor
 125    private final Map<String, Initializer> _initializers; // keys: "class init x" or "instance init x"
 126    private final Map<String, Expression> _expressions; // keys: field names or "anon super arg x"
 127    private final List<Runnable2<MethodVisitor, StackSizeTracker>> _staticInits;
 128    private final List<Runnable2<MethodVisitor, StackSizeTracker>> _instanceInits;
 129   
 130  35 public TreeCompiler(TreeClass treeClass, Options opt) {
 131  35 _treeClass = treeClass;
 132  35 _opt = opt;
 133  35 _classWriter = null;
 134  35 _adapter = new EvaluationAdapter();
 135  35 _name = className(_treeClass);
 136  35 _java5 = JavaVersion.CURRENT.supports(JavaVersion.JAVA_5);
 137  35 _methods = new HashMap<String, MethodDeclaration>();
 138  35 _constructors = new HashMap<String, ConstructorDeclaration>();
 139  35 _initializers = new HashMap<String, Initializer>();
 140  35 _expressions = new HashMap<String, Expression>();
 141  35 _staticInits = new LinkedList<Runnable2<MethodVisitor, StackSizeTracker>>();
 142  35 _instanceInits = new LinkedList<Runnable2<MethodVisitor, StackSizeTracker>>();
 143    }
 144   
 145    // Use this to perform verification with the CheckClassAdapter:
 146    //private ClassVisitor _classWriter;
 147    //private ClassWriter _realWriter;
 148  35 public byte[] bytecode() {
 149  35 if (_classWriter == null) {
 150  35 _classWriter = new ClassWriter(0);
 151    //_realWriter = new ClassWriter(0);
 152    //_classWriter = new org.objectweb.asm.util.CheckClassAdapter(_realWriter);
 153  35 compileClass(_treeClass.declaration());
 154    //dumpClassFile();
 155    }
 156  35 return _classWriter.toByteArray();
 157    //return _realWriter.toByteArray();
 158    }
 159   
 160    /** Dump the compiled bytes to a file for use in debugging. */
 161  0 @SuppressWarnings("unused") private void dumpClassFile() {
 162  0 try {
 163  0 String name = _treeClass.fullName();
 164  0 int dot = name.lastIndexOf('.');
 165  0 if (dot >= 0) { name = name.substring(dot); }
 166  0 java.io.OutputStream out = new java.io.FileOutputStream(name + ".class");
 167  0 out.write(_classWriter.toByteArray());
 168    //out.write(_realWriter.toByteArray());
 169  0 out.close();
 170    }
 171  0 catch (java.io.IOException e) { System.out.println("Can't write bytes"); }
 172    }
 173   
 174   
 175  35 public EvaluationAdapter evaluationAdapter() { return _adapter; }
 176   
 177   
 178  35 private void compileClass(Node ast) {
 179  35 Type extendsT = TypeSystem.OBJECT;
 180  35 List<? extends ReferenceTypeName> implementsTs = CollectUtil.emptyList();
 181  35 List<Node> members = CollectUtil.emptyList();
 182  35 int accessFlags = 0;
 183  35 final boolean isInterface;
 184  35 if (ast instanceof ClassDeclaration) {
 185  35 ClassDeclaration cd = (ClassDeclaration) ast;
 186  35 extendsT = NodeProperties.getType(cd.getSuperclass());
 187  1 if (cd.getInterfaces() != null) { implementsTs = cd.getInterfaces(); }
 188  35 members = cd.getMembers();
 189  35 accessFlags = cd.getModifiers().getBitVector();
 190  35 isInterface = false;
 191    }
 192  0 else if (ast instanceof InterfaceDeclaration) {
 193  0 InterfaceDeclaration id = (InterfaceDeclaration) ast;
 194  0 if (id.getInterfaces() != null) { implementsTs = id.getInterfaces(); }
 195  0 members = id.getMembers();
 196  0 accessFlags = id.getModifiers().getBitVector(INTERFACE);
 197  0 isInterface = true;
 198    }
 199  0 else if (ast instanceof AnonymousAllocation) {
 200  0 AnonymousAllocation aa = (AnonymousAllocation) ast;
 201  0 ReferenceTypeName parent = aa.getCreationType();
 202  0 Type parentT = NodeProperties.getType(parent);
 203  0 if (extractClass(parentT).isInterface()) { implementsTs = Collections.singletonList(parent); }
 204  0 else { extendsT = parentT; }
 205  0 members = aa.getMembers();
 206  0 isInterface = false;
 207    }
 208  0 else if (ast instanceof AnonymousInnerAllocation) {
 209  0 extendsT = NodeProperties.getSuperType(ast);
 210  0 members = ((AnonymousInnerAllocation) ast).getMembers();
 211  0 isInterface = false;
 212    }
 213  0 else { throw new RuntimeException("Unexpected class AST node type: " + ast); }
 214   
 215    // Promote default access to public -- a reference may logically appear in the same
 216    // package but, due to implementation constraints, be loaded by a different class loader.
 217    // In that situation, default access isn't permitted at run time.
 218  35 accessFlags = defaultToPublicAccess(accessFlags);
 219   
 220  35 String classSig = null;
 221  35 if (_java5) {
 222  35 List<TypeParameter> paramAsts = Collections.emptyList();
 223  35 if (ast instanceof TypeDeclaration) {
 224  35 paramAsts = ((TypeDeclaration) ast).getTypeParams().unwrap(paramAsts);
 225    }
 226   
 227  35 StringBuilder sigBuilder = new StringBuilder();
 228  19 if (!paramAsts.isEmpty()) { sigBuilder.append(typeParamListSignature(paramAsts)); }
 229  35 sigBuilder.append(typeSignature(extendsT));
 230  35 for (ReferenceTypeName implementsT : implementsTs) {
 231  1 sigBuilder.append(typeSignature(NodeProperties.getType(implementsT)));
 232    }
 233  35 classSig = sigBuilder.toString();
 234    }
 235   
 236  35 _classWriter.visit(_java5 ? V1_5 : V1_4, accessFlags, _name, classSig,
 237    className(extractClass(extendsT)), extractClassNames(implementsTs));
 238  35 DJClass declaring = _treeClass.declaringClass();
 239  35 if (declaring != null) {
 240    // visitOuter corresponds to Class.getEnclosingClass(), visitInner to Class.getDeclaringClass()
 241  0 _classWriter.visitOuterClass(className(declaring), null, null);
 242  0 _classWriter.visitInnerClass(_name, className(declaring), _treeClass.declaredName(), accessFlags);
 243    }
 244   
 245  35 if (isInterface) {
 246    // interface fields must be public (adapter is necessary to interpret declared field initializers)
 247  0 _classWriter.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, ADAPTER_FIELD,
 248    EVALUATION_ADAPTER_DESCRIPTOR, null, null).visitEnd();
 249    }
 250    else {
 251  35 _classWriter.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, ADAPTER_FIELD,
 252    EVALUATION_ADAPTER_DESCRIPTOR, null, null).visitEnd();
 253    // to be accessible to inner classes, must not be private
 254  35 _classWriter.visitField(ACC_FINAL | ACC_SYNTHETIC, BINDINGS_FACTORY_FIELD,
 255    BINDINGS_FACTORY_DESCRIPTOR, null, null).visitEnd();
 256    }
 257   
 258  35 final List<ConstructorDeclaration> constructors = new LinkedList<ConstructorDeclaration>();
 259  35 for (Node member : members) {
 260  143 member.acceptVisitor(new AbstractVisitor<Void>() {
 261  0 @Override public Void defaultCase(Node member) {
 262    // Ignore any declarations we can't handle (if this declaration is malformed,
 263    // that should have already been caught)
 264  0 return null;
 265    }
 266  0 @Override public Void visit(ClassDeclaration member) {
 267  0 recordInnerClass(member, isInterface); return null;
 268    }
 269  0 @Override public Void visit(InterfaceDeclaration member) {
 270  0 recordInnerClass(member, isInterface); return null;
 271    }
 272  19 @Override public Void visit(ConstructorDeclaration member) {
 273    // constructor compilation must be deferred until all initialization code has been found
 274  19 constructors.add(member); return null;
 275    }
 276  112 @Override public Void visit(MethodDeclaration member) {
 277  112 compileMethod(member, isInterface); return null;
 278    }
 279  12 @Override public Void visit(FieldDeclaration member) {
 280  12 compileField(member, isInterface); return null;
 281    }
 282  0 @Override public Void visit(ClassInitializer member) {
 283  0 compileInitializerBlock(member, true); return null;
 284    }
 285  0 @Override public Void visit(InstanceInitializer member) {
 286  0 compileInitializerBlock(member, false); return null;
 287    }
 288    });
 289    }
 290   
 291  35 compileClassInitializer();
 292  35 if (ast instanceof AnonymousAllocation) {
 293  0 compileAnonymousConstructor((AnonymousAllocation) ast);
 294    }
 295  35 else if (ast instanceof AnonymousInnerAllocation) {
 296  0 compileAnonymousInnerConstructor((AnonymousInnerAllocation) ast);
 297    }
 298  35 else if (ast instanceof ClassDeclaration) {
 299  16 if (constructors.isEmpty()) { compileDefaultConstructor(extendsT); }
 300    else {
 301  19 for (ConstructorDeclaration k : constructors) { compileConstructor(k, extendsT); }
 302    }
 303    }
 304   
 305  35 _classWriter.visitEnd();
 306    }
 307   
 308  0 private void recordInnerClass(TypeDeclaration ast, boolean isInterface) {
 309  0 int access = isInterface ? ast.getModifiers().getBitVector(STATIC, FINAL) : ast.getModifiers().getBitVector();
 310  0 _classWriter.visitInnerClass(className(NodeProperties.getDJClass(ast)), _name, ast.getName(), access);
 311    }
 312   
 313  16 private void compileDefaultConstructor(Type extendsT) {
 314  16 DJClass outerC = SymbolUtil.dynamicOuterClass(_treeClass);
 315  16 String methodDescriptor;
 316  16 String methodSig;
 317  16 if (outerC == null) {
 318  16 methodDescriptor = "(" + RUNTIME_BINDINGS_DESCRIPTOR + ")V";
 319  16 methodSig = null;
 320    }
 321    else {
 322  0 Type outerT = SymbolUtil.thisType(outerC);
 323  0 methodDescriptor = "(" + typeDescriptor(outerT) + ")V";
 324  0 methodSig = _java5 ? ("(" + typeSignature(outerT) + ")V") : null;
 325    }
 326  16 MethodVisitor mv = _classWriter.visitMethod(ACC_PUBLIC, "<init>", methodDescriptor, methodSig, null);
 327  16 StackSizeTracker stack = new StackSizeTracker(2);
 328  16 mv.visitCode();
 329   
 330  16 int bindingsVar = emitPartialBindingsVar(mv, outerC, stack);
 331  16 emitBindingsFactoryAssignment(mv, bindingsVar, stack);
 332  16 emitDefaultSuperCall(mv, extendsT, bindingsVar, stack);
 333  16 for (Runnable2<MethodVisitor, StackSizeTracker> initCode : _instanceInits) {
 334  0 initCode.run(mv, stack);
 335    }
 336   
 337  16 mv.visitInsn(RETURN);
 338  16 mv.visitMaxs(stack.maxStack(), stack.maxLocals());
 339  16 mv.visitEnd();
 340    }
 341   
 342  0 private void compileAnonymousConstructor(AnonymousAllocation ast) {
 343  0 MethodVisitor mv = _classWriter.visitMethod(ACC_PUBLIC, "<init>",
 344    "(" + RUNTIME_BINDINGS_DESCRIPTOR + ")V", null, null);
 345  0 StackSizeTracker stack = new StackSizeTracker(2);
 346  0 mv.visitCode();
 347   
 348  0 emitBindingsFactoryAssignment(mv, 1, stack);
 349   
 350    // super call: this.<init>(args) or this.<init>($bindings, args)
 351  0 Type superT = NodeProperties.getType(ast.getCreationType());
 352  0 DJClass superC = extractClass(superT);
 353  0 if (superC.isInterface()) { emitDefaultSuperCall(mv, TypeSystem.OBJECT, 1, stack); }
 354    else {
 355  0 stack.mark();
 356  0 mv.visitVarInsn(ALOAD, 0);
 357  0 stack.adjust(1);
 358  0 boolean includeBindings = superC.hasRuntimeBindingsParams();
 359  0 String extraArg = "";
 360  0 if (includeBindings) {
 361  0 extraArg = RUNTIME_BINDINGS_DESCRIPTOR;
 362  0 mv.visitVarInsn(ALOAD, 1);
 363  0 stack.adjust(1);
 364    }
 365  0 DJConstructor superTarget = NodeProperties.getConstructor(ast).declaredSignature();
 366  0 List<Expression> superArgs = ast.getArguments();
 367  0 if (superArgs != null) { emitAnonSuperArgs(mv, superTarget, superArgs, stack); }
 368  0 mv.visitMethodInsn(INVOKESPECIAL, className(superC), "<init>",
 369    paramListDescriptor(extraArg, superTarget.parameters()) + "V");
 370  0 stack.reset();
 371    }
 372   
 373  0 for (Runnable2<MethodVisitor, StackSizeTracker> initCode : _instanceInits) {
 374  0 initCode.run(mv, stack);
 375    }
 376  0 mv.visitInsn(RETURN);
 377  0 mv.visitMaxs(stack.maxStack(), stack.maxLocals());
 378  0 mv.visitEnd();
 379    }
 380   
 381  0 private void compileAnonymousInnerConstructor(AnonymousInnerAllocation ast) {
 382  0 MethodVisitor mv = _classWriter.visitMethod(ACC_PUBLIC, "<init>",
 383    "(" + RUNTIME_BINDINGS_DESCRIPTOR + ")V", null, null);
 384  0 StackSizeTracker stack = new StackSizeTracker(2);
 385  0 mv.visitCode();
 386   
 387  0 emitBindingsFactoryAssignment(mv, 1, stack);
 388   
 389    // super call: this.<init>(outer, args)
 390  0 Type superT = NodeProperties.getSuperType(ast);
 391  0 DJClass superC = extractClass(superT);
 392  0 DJClass superOuterC = SymbolUtil.dynamicOuterClass(superC);
 393   
 394  0 stack.mark();
 395  0 mv.visitVarInsn(ALOAD, 0);
 396  0 stack.adjust(1);
 397   
 398  0 String outerExpKey = "anon super arg outer";
 399  0 _expressions.put(outerExpKey, ast.getExpression());
 400  0 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 401  0 mv.visitLdcInsn(outerExpKey);
 402  0 mv.visitVarInsn(ALOAD, 1);
 403  0 stack.adjust(3);
 404  0 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "evaluateExpression",
 405    EVALUATE_EXPRESSION_DESCRIPTOR);
 406  0 stack.adjust(-2);
 407   
 408  0 DJConstructor superTarget = NodeProperties.getConstructor(ast).declaredSignature();
 409  0 List<Expression> superArgs = ast.getArguments();
 410  0 if (superArgs != null) { emitAnonSuperArgs(mv, superTarget, superArgs, stack); }
 411  0 String callDescriptor = paramListDescriptor(typeDescriptor(SymbolUtil.thisType(superOuterC)),
 412    superTarget.parameters()) + "V";
 413  0 mv.visitMethodInsn(INVOKESPECIAL, className(superC), "<init>", callDescriptor);
 414  0 stack.reset();
 415   
 416  0 for (Runnable2<MethodVisitor, StackSizeTracker> initCode : _instanceInits) {
 417  0 initCode.run(mv, stack);
 418    }
 419   
 420  0 mv.visitInsn(RETURN);
 421  0 mv.visitMaxs(stack.maxStack(), stack.maxLocals());
 422  0 mv.visitEnd();
 423    }
 424   
 425  19 private void compileConstructor(ConstructorDeclaration ast, Type extendsT) {
 426  19 DJClass outerC = SymbolUtil.dynamicOuterClass(_treeClass);
 427  19 Type outerT = (outerC == null) ? null : SymbolUtil.thisType(outerC);
 428  19 List<FormalParameter> params = ast.getParameters();
 429  19 List<? extends ReferenceTypeName> exceptions = ast.getExceptions();
 430  19 String firstArgDescriptor = (outerT == null) ? RUNTIME_BINDINGS_DESCRIPTOR : typeDescriptor(outerT);
 431  19 String methodDescriptor = paramListDescriptor(firstArgDescriptor, extractVars(params)) + "V";
 432   
 433  19 String methodSig = null;
 434  19 if (_java5) {
 435  19 List<TypeParameter> typeParamAsts = ast.getTypeParams().unwrap(Collections.<TypeParameter>emptyList());
 436  19 StringBuilder sigBuilder = new StringBuilder();
 437  0 if (!typeParamAsts.isEmpty()) { sigBuilder.append(typeParamListSignature(typeParamAsts)); }
 438  19 String firstArgSig = (outerT == null) ? RUNTIME_BINDINGS_DESCRIPTOR : typeSignature(outerT);
 439  19 sigBuilder.append(paramListSignature(firstArgSig, extractVars(params)));
 440  19 sigBuilder.append("V");
 441  19 for (ReferenceTypeName tn : exceptions) {
 442  0 sigBuilder.append('^').append(typeSignature(NodeProperties.getType(tn)));
 443    }
 444  19 methodSig = sigBuilder.toString();
 445    }
 446   
 447    // Promote default access to protected -- a subclass may logically appear in the same
 448    // package but, due to implementation constraints, be loaded by a different class loader.
 449    // In that situation, default access isn't permitted at run time.
 450  19 int access = defaultToProtectedAccess(ast.getModifiers().getBitVector());
 451  19 MethodVisitor mv = _classWriter.visitMethod(access, "<init>", methodDescriptor,
 452    methodSig, extractClassNames(exceptions));
 453   
 454  19 String key = methodDescriptor;
 455  19 _constructors.put(key, ast);
 456   
 457  19 int[] paramLocations = computeParamLocations(params, 2);
 458  19 StackSizeTracker stack = new StackSizeTracker(paramLocations[params.size()]);
 459  19 mv.visitCode();
 460   
 461  19 int bindingsVar = emitPartialBindingsVar(mv, outerC, stack);
 462  19 int boxedParamsVar = emitBoxParams(mv, params, paramLocations, stack);
 463   
 464    // super/this call: this.<init>(args) or this.<init>(outer, args) or this.<init>($bindings, args)
 465  19 boolean callsSuper;
 466  19 ConstructorCall call = ast.getConstructorCall();
 467  19 if (call == null) {
 468  12 callsSuper = true;
 469  12 emitBindingsFactoryAssignment(mv, bindingsVar, stack);
 470  12 emitDefaultSuperCall(mv, extendsT, bindingsVar, stack);
 471    }
 472    else {
 473  7 callsSuper = call.isSuper();
 474  7 if (callsSuper) { emitBindingsFactoryAssignment(mv, bindingsVar, stack); }
 475  7 DJConstructor callTarget = NodeProperties.getConstructor(call).declaredSignature();
 476  7 DJClass extendsC = extractClass(extendsT);
 477  7 stack.mark();
 478  7 mv.visitVarInsn(ALOAD, 0);
 479  7 stack.adjust(1);
 480   
 481    // Additional super/this call arg: may be a RuntimeBindings, an enclosing object, or nothing
 482  7 String extraArg = "";
 483  7 if (call.getExpression() == null) {
 484  7 if (callsSuper) {
 485  7 if (extendsC.hasRuntimeBindingsParams()) {
 486  7 extraArg = RUNTIME_BINDINGS_DESCRIPTOR;
 487  7 mv.visitVarInsn(ALOAD, bindingsVar);
 488  7 stack.adjust(1);
 489    }
 490    }
 491    else { // this call
 492  0 if (outerC == null) {
 493  0 extraArg = RUNTIME_BINDINGS_DESCRIPTOR;
 494  0 mv.visitVarInsn(ALOAD, bindingsVar);
 495  0 stack.adjust(1);
 496    }
 497    else { // outerC is defined
 498  0 extraArg = typeDescriptor(outerT);
 499  0 mv.visitVarInsn(ALOAD, 1);
 500  0 stack.adjust(1);
 501    }
 502    }
 503    }
 504    else { // call.getExpression() is defined
 505  0 if (callsSuper) {
 506  0 extraArg = typeDescriptor(SymbolUtil.thisType(SymbolUtil.dynamicOuterClass(extendsC)));
 507    }
 508    else {
 509  0 extraArg = typeDescriptor(outerT);
 510    }
 511  0 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 512  0 mv.visitLdcInsn(key);
 513  0 stack.adjust(2);
 514  0 emitIntConstant(mv, -1, stack);
 515  0 mv.visitVarInsn(ALOAD, bindingsVar); // can't use $bindingsFactory until after the call
 516  0 mv.visitVarInsn(ALOAD, boxedParamsVar);
 517  0 stack.adjust(2);
 518  0 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "evaluateConstructorCallArg",
 519    EVALUATE_CONSTRUCTOR_CALL_ARG_DESCRIPTOR);
 520  0 stack.adjust(-4);
 521    }
 522   
 523    // evaluate super/this call args
 524  7 int i = 0;
 525  7 for (Pair<LocalVariable, Expression> arg :
 526    IterUtil.zip(callTarget.parameters(), call.getArguments())) {
 527  7 Type paramT = arg.first().type();
 528  7 Object val = expressionConstantVal(arg.second());
 529  7 if (val == null) {
 530  7 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 531  7 mv.visitLdcInsn(key);
 532  7 stack.adjust(2);
 533  7 emitIntConstant(mv, i, stack);
 534  7 mv.visitVarInsn(ALOAD, bindingsVar); // can't use $bindingsFactory until after the call
 535  7 mv.visitVarInsn(ALOAD, boxedParamsVar);
 536  7 stack.adjust(2);
 537  7 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "evaluateConstructorCallArg",
 538    EVALUATE_CONSTRUCTOR_CALL_ARG_DESCRIPTOR);
 539  7 stack.adjust(-4);
 540  7 emitConvert(mv, paramT, stack);
 541    }
 542  0 else { emitConstant(mv, val, stack); }
 543  7 i++;
 544    }
 545   
 546  7 mv.visitMethodInsn(INVOKESPECIAL, callsSuper ? className(extendsC) : _name, "<init>",
 547    paramListDescriptor(extraArg, callTarget.parameters()) + "V");
 548  7 stack.reset();
 549    }
 550   
 551  19 if (callsSuper) {
 552  19 for (Runnable2<MethodVisitor, StackSizeTracker> initCode : _instanceInits) {
 553  0 initCode.run(mv, stack);
 554    }
 555    }
 556   
 557  19 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 558  19 mv.visitLdcInsn(key);
 559  19 stack.adjust(2);
 560  19 emitLoadBindings(mv, 0, _name, stack);
 561  19 mv.visitVarInsn(ALOAD, boxedParamsVar);
 562  19 stack.adjust(1);
 563  19 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "evaluateConstructorBody",
 564    EVALUATE_CONSTRUCTOR_BODY_DESCRIPTOR);
 565  19 stack.adjust(-4);
 566   
 567  19 mv.visitInsn(RETURN);
 568  19 mv.visitMaxs(stack.maxStack(), stack.maxLocals());
 569  19 mv.visitEnd();
 570    }
 571   
 572  35 private void compileClassInitializer() {
 573  35 MethodVisitor mv = _classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
 574  35 StackSizeTracker stack = new StackSizeTracker(0);
 575  35 mv.visitCode();
 576   
 577    // $adapter = ((TreeClassLoader) Class.forName("Name").getClassLoader()).getAdapter("Name");
 578  35 mv.visitLdcInsn(_treeClass.fullName()); // don't use _name -- it's an internal name
 579  35 stack.adjust(1);
 580  35 mv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName",
 581    "(" + STRING_DESCRIPTOR + ")" + CLASS_DESCRIPTOR);
 582  35 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader",
 583    "()" + CLASS_LOADER_DESCRIPTOR);
 584  35 mv.visitTypeInsn(CHECKCAST, TREE_CLASS_LOADER_NAME);
 585  35 mv.visitLdcInsn(_treeClass.fullName()); // don't use _name -- it's an internal name
 586  35 stack.adjust(1);
 587  35 mv.visitMethodInsn(INVOKEVIRTUAL, TREE_CLASS_LOADER_NAME, "getAdapter",
 588    "(" + STRING_DESCRIPTOR + ")" + EVALUATION_ADAPTER_DESCRIPTOR);
 589  35 stack.adjust(-1);
 590  35 mv.visitFieldInsn(PUTSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 591  35 stack.adjust(-1);
 592   
 593  35 for (Runnable2<MethodVisitor, StackSizeTracker> initCode : _staticInits) {
 594  0 initCode.run(mv, stack);
 595    }
 596   
 597  35 mv.visitInsn(RETURN);
 598  35 mv.visitMaxs(stack.maxStack(), stack.maxLocals());
 599  35 mv.visitEnd();
 600    }
 601   
 602  112 private void compileMethod(MethodDeclaration ast, boolean isInterface) {
 603  112 int access = isInterface ? ast.getModifiers().getBitVector(ABSTRACT) : ast.getModifiers().getBitVector();
 604  0 if (isInterface) { access = defaultToPublicAccess(access); }
 605  112 List<FormalParameter> params = ast.getParameters();
 606  112 Type returnT = NodeProperties.getType(ast.getReturnType());
 607  112 List<? extends ReferenceTypeName> exceptions = ast.getExceptions();
 608  112 boolean isStatic = Modifier.isStatic(access);
 609  112 String extraArg = isStatic ? RUNTIME_BINDINGS_DESCRIPTOR : "";
 610  112 String methodDescriptor = paramListDescriptor(extraArg, extractVars(params)) + typeDescriptor(returnT);
 611   
 612  112 String methodSig = null;
 613  112 if (_java5) {
 614  112 List<TypeParameter> typeParamAsts = ast.getTypeParams().unwrap(Collections.<TypeParameter>emptyList());
 615   
 616  112 StringBuilder sigBuilder = new StringBuilder();
 617  0 if (!typeParamAsts.isEmpty()) { sigBuilder.append(typeParamListSignature(typeParamAsts)); }
 618  112 sigBuilder.append(paramListSignature(extraArg, extractVars(params)));
 619  112 sigBuilder.append(typeSignature(returnT));
 620  112 for (ReferenceTypeName tn : exceptions) {
 621  0 sigBuilder.append('^').append(typeSignature(NodeProperties.getType(tn)));
 622    }
 623  112 methodSig = sigBuilder.toString();
 624    }
 625   
 626  112 final MethodVisitor mv = _classWriter.visitMethod(access, ast.getName(), methodDescriptor,
 627    methodSig, extractClassNames(exceptions));
 628   
 629  112 if (!Modifier.isAbstract(access) && !Modifier.isNative(access)) {
 630  112 String key = ast.getName() + methodDescriptor;
 631  112 _methods.put(key, ast);
 632   
 633  112 int[] paramLocations = computeParamLocations(params, 1);
 634  112 StackSizeTracker stack = new StackSizeTracker(paramLocations[params.size()]);
 635  112 mv.visitCode();
 636   
 637  112 int boxedParamsVar = emitBoxParams(mv, params, paramLocations, stack);
 638   
 639  112 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 640  112 mv.visitLdcInsn(key);
 641  112 stack.adjust(2);
 642  0 if (isStatic) { mv.visitVarInsn(ALOAD, 0); stack.adjust(1); }
 643  112 else { emitLoadBindings(mv, 0, _name, stack); }
 644  112 mv.visitVarInsn(ALOAD, boxedParamsVar);
 645  112 stack.adjust(1);
 646  112 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "evaluateMethod",
 647    EVALUATE_METHOD_DESCRIPTOR);
 648  112 stack.adjust(-3);
 649   
 650  112 stack.mark();
 651  112 emitConvert(mv, returnT, stack);
 652  112 _opt.typeSystem().erase(returnT).apply(new TypeAbstractVisitor_void() {
 653  58 @Override public void forReferenceType(ReferenceType t) { mv.visitInsn(ARETURN); }
 654  42 @Override public void forPrimitiveType(PrimitiveType t) { mv.visitInsn(IRETURN); }
 655  0 @Override public void forLongType(LongType t) { mv.visitInsn(LRETURN); }
 656  0 @Override public void forFloatType(FloatType t) { mv.visitInsn(FRETURN); }
 657  0 @Override public void forDoubleType(DoubleType t) { mv.visitInsn(DRETURN); }
 658  12 @Override public void forVoidType(VoidType t) { mv.visitInsn(RETURN); }
 659    });
 660  112 stack.reset();
 661   
 662  112 mv.visitMaxs(stack.maxStack(), stack.maxLocals());
 663    }
 664  112 mv.visitEnd();
 665    }
 666   
 667  12 private void compileField(final FieldDeclaration ast, boolean isInterface) {
 668  12 int access = isInterface ? ast.getModifiers().getBitVector(STATIC, FINAL) : ast.getModifiers().getBitVector();
 669  0 if (isInterface) { access = defaultToPublicAccess(access); }
 670  12 final boolean isStatic = Modifier.isStatic(access);
 671  12 final Type t = NodeProperties.getType(ast.getType());
 672  12 Expression init = ast.getInitializer();
 673  12 Object val = null;
 674  0 if (isStatic && init != null) { val = expressionConstantVal(init); }
 675  12 _classWriter.visitField(access, ast.getName(), typeDescriptor(t), typeSignature(t),
 676    val).visitEnd();
 677   
 678  12 if (init != null && val == null) {
 679  0 final String key = ast.getName();
 680  0 _expressions.put(key, init);
 681  0 (isStatic ? _staticInits : _instanceInits).add(new Runnable2<MethodVisitor, StackSizeTracker>() {
 682  0 public void run(MethodVisitor mv, StackSizeTracker stack) {
 683  0 stack.mark();
 684  0 if (!isStatic) { mv.visitVarInsn(ALOAD, 0); stack.adjust(1); }
 685  0 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 686  0 mv.visitLdcInsn(key);
 687  0 stack.adjust(2);
 688  0 if (isStatic) {
 689  0 mv.visitFieldInsn(GETSTATIC, RUNTIME_BINDINGS_NAME, "EMPTY", RUNTIME_BINDINGS_DESCRIPTOR);
 690  0 stack.adjust(1);
 691    }
 692    else {
 693  0 emitLoadBindings(mv, 0, _name, stack);
 694    }
 695  0 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "evaluateExpression",
 696    EVALUATE_EXPRESSION_DESCRIPTOR);
 697  0 stack.adjust(-2);
 698   
 699  0 emitConvert(mv, t, stack);
 700  0 if (isStatic) {
 701  0 mv.visitFieldInsn(PUTSTATIC, _name, ast.getName(), typeDescriptor(t));
 702    }
 703    else {
 704  0 mv.visitFieldInsn(PUTFIELD, _name, ast.getName(), typeDescriptor(t));
 705    }
 706  0 stack.reset();
 707    }
 708    });
 709    }
 710   
 711    }
 712   
 713  0 private void compileInitializerBlock(Initializer ast, final boolean isStatic) {
 714  0 final String key = (isStatic ? "class init " : "instance init ") + _initializers.size();
 715  0 _initializers.put(key, ast);
 716  0 (isStatic ? _staticInits : _instanceInits).add(new Runnable2<MethodVisitor, StackSizeTracker>() {
 717  0 public void run(MethodVisitor mv, StackSizeTracker stack) {
 718  0 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 719  0 mv.visitLdcInsn(key);
 720  0 stack.adjust(2);
 721  0 if (isStatic) {
 722  0 mv.visitFieldInsn(GETSTATIC, RUNTIME_BINDINGS_NAME, "EMPTY", RUNTIME_BINDINGS_DESCRIPTOR);
 723  0 stack.adjust(1);
 724    }
 725    else {
 726  0 emitLoadBindings(mv, 0, _name, stack);
 727    }
 728  0 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "evaluateInitializer",
 729    EVALUATE_INITIALIZER_DESCRIPTOR);
 730  0 stack.adjust(-3);
 731    }
 732    });
 733    }
 734   
 735   
 736    /* Compilation helper methods */
 737   
 738    /** Push a constant of unknown size onto the stack. Uses emitIntConstant for Integers. */
 739  0 private void emitConstant(MethodVisitor mv, Object val, StackSizeTracker stack) {
 740  0 if (val instanceof Integer) { emitIntConstant(mv, (Integer) val, stack); }
 741    else {
 742  0 mv.visitLdcInsn(val);
 743  0 if (val instanceof Long || val instanceof Double) { stack.adjust(2); }
 744  0 else { stack.adjust(1); }
 745    }
 746    }
 747   
 748    /**
 749    * Push an int onto the stack. Optimized to use simpler instructions than LDC (such as ICONST_0)
 750    * where possible.
 751    */
 752  281 private void emitIntConstant(MethodVisitor mv, int val, StackSizeTracker stack) {
 753  281 switch (val) {
 754  0 case -1: mv.visitInsn(ICONST_M1); break;
 755  138 case 0: mv.visitInsn(ICONST_0); break;
 756  115 case 1: mv.visitInsn(ICONST_1); break;
 757  28 case 2: mv.visitInsn(ICONST_2); break;
 758  0 case 3: mv.visitInsn(ICONST_3); break;
 759  0 case 4: mv.visitInsn(ICONST_4); break;
 760  0 case 5: mv.visitInsn(ICONST_5); break;
 761  0 default:
 762  0 if (val >= Byte.MIN_VALUE && val <= Byte.MAX_VALUE) { mv.visitIntInsn(BIPUSH, val); }
 763  0 else if (val >= Short.MIN_VALUE && val <= Short.MAX_VALUE) { mv.visitIntInsn(SIPUSH, val); }
 764  0 else { mv.visitLdcInsn(val); }
 765  0 break;
 766    }
 767  281 stack.adjust(1);
 768    }
 769   
 770    /**
 771    * Determine the location of a constructor's RuntimeBindings before "this" has been bound.
 772    * If this is one of the parameters of the constructor, no code is generated and the value 1 is
 773    * returned. Otherwise, the constructor must have an outer object parameter, and that object
 774    * is used to compute the RuntimeBindings, which is then stored as a new local variable.
 775    */
 776  35 private int emitPartialBindingsVar(MethodVisitor mv, DJClass outerC, StackSizeTracker stack) {
 777  35 if (outerC == null) { return 1; }
 778    else {
 779  0 int result = stack.newVariable();
 780  0 emitLoadBindings(mv, 1, className(outerC), stack);
 781  0 mv.visitVarInsn(ASTORE, result);
 782  0 stack.adjust(-1);
 783  0 return result;
 784    }
 785    }
 786   
 787    /**
 788    * Set the "$bindingsFactory" field in a constructor. Callers must provide a variable index
 789    * at which the partial bindings are stored.
 790    */
 791  35 private void emitBindingsFactoryAssignment(MethodVisitor mv, int bindingsVar, StackSizeTracker stack) {
 792  35 mv.visitVarInsn(ALOAD, 0);
 793  35 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 794  35 mv.visitVarInsn(ALOAD, bindingsVar);
 795  35 stack.adjust(3);
 796  35 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "makeBindingsFactory",
 797    MAKE_BINDINGS_FACTORY_DESCRIPTOR);
 798  35 stack.adjust(-1);
 799  35 mv.visitFieldInsn(PUTFIELD, _name, BINDINGS_FACTORY_FIELD, BINDINGS_FACTORY_DESCRIPTOR);
 800  35 stack.adjust(-2);
 801    }
 802   
 803    /** Super constructor call -- one of "super()" or "super($bindings)" */
 804  28 private void emitDefaultSuperCall(MethodVisitor mv, Type extendsT, int bindingsVar,
 805    StackSizeTracker stack) {
 806  28 DJClass extendsC = extractClass(extendsT);
 807  28 mv.visitVarInsn(ALOAD, 0);
 808  28 stack.adjust(1);
 809  28 if (extendsC.hasRuntimeBindingsParams()) {
 810  0 mv.visitVarInsn(ALOAD, bindingsVar);
 811  0 stack.adjust(1);
 812  0 mv.visitMethodInsn(INVOKESPECIAL, className(extendsC), "<init>",
 813    "(" + RUNTIME_BINDINGS_DESCRIPTOR + ")V");
 814  0 stack.adjust(-2);
 815    }
 816    else {
 817  28 mv.visitMethodInsn(INVOKESPECIAL, className(extendsC), "<init>", "()V");
 818  28 stack.adjust(-1);
 819    }
 820    }
 821   
 822    /** Invoke "var.$bindingsFactory.value(var)", for some local variable with the given class name. */
 823  131 private void emitLoadBindings(MethodVisitor mv, int thisVar, String thisClassName,
 824    StackSizeTracker stack) {
 825  131 mv.visitVarInsn(ALOAD, thisVar);
 826  131 stack.adjust(1);
 827  131 mv.visitFieldInsn(GETFIELD, thisClassName, BINDINGS_FACTORY_FIELD, BINDINGS_FACTORY_DESCRIPTOR);
 828  131 mv.visitVarInsn(ALOAD, thisVar);
 829  131 stack.adjust(1);
 830  131 mv.visitMethodInsn(INVOKEVIRTUAL, BINDINGS_FACTORY_NAME, "value", BINDINGS_FACTORY_VALUE_DESCRIPTOR);
 831  131 stack.adjust(-1);
 832    }
 833   
 834   
 835    /** Evaluate all arguments appearing in an anonymous class's super constructor invocation. */
 836  0 private void emitAnonSuperArgs(MethodVisitor mv, DJConstructor k, List<Expression> args,
 837    StackSizeTracker stack) {
 838  0 int i = 0;
 839  0 for (Pair<LocalVariable, Expression> arg : IterUtil.zip(k.parameters(), args)) {
 840  0 String key = "anon super arg " + i;
 841  0 _expressions.put(key, arg.second());
 842  0 Type paramT = arg.first().type();
 843  0 Object val = expressionConstantVal(arg.second());
 844  0 if (val == null) {
 845  0 mv.visitFieldInsn(GETSTATIC, _name, ADAPTER_FIELD, EVALUATION_ADAPTER_DESCRIPTOR);
 846  0 mv.visitLdcInsn(key);
 847  0 mv.visitVarInsn(ALOAD, 1);
 848  0 stack.adjust(3);
 849  0 mv.visitMethodInsn(INVOKEVIRTUAL, EVALUATION_ADAPTER_NAME, "evaluateExpression",
 850    EVALUATE_EXPRESSION_DESCRIPTOR);
 851  0 stack.adjust(-2);
 852  0 emitConvert(mv, paramT, stack);
 853    }
 854  0 else { emitConstant(mv, val, stack); }
 855  0 i++;
 856    }
 857    }
 858   
 859    /**
 860    * Copy the arguments to a constructor or method into an Object[]; box as necessary.
 861    * @return The location of the variable storing the Object[]
 862    */
 863  131 private int emitBoxParams(MethodVisitor mv, List<FormalParameter> params, int[] paramLocations,
 864    StackSizeTracker stack) {
 865  131 emitIntConstant(mv, params.size(), stack);
 866  131 mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
 867  131 int i = 0;
 868  131 for (FormalParameter param : params) {
 869  143 Type t = NodeProperties.getVariable(param).type();
 870  143 mv.visitInsn(DUP);
 871  143 stack.adjust(1);
 872  143 emitIntConstant(mv, i, stack);
 873  143 emitLoadAndBox(mv, t, paramLocations[i], stack);
 874  143 mv.visitInsn(AASTORE);
 875  143 stack.adjust(-3);
 876  143 i++;
 877    }
 878  131 int result = stack.newVariable();
 879  131 mv.visitVarInsn(ASTORE, result);
 880  131 stack.adjust(-1);
 881  131 return result;
 882    }
 883   
 884    /** Load a single variable onto the stack and box it if it is a primitive. */
 885  143 private void emitLoadAndBox(final MethodVisitor mv, Type t, final int var,
 886    final StackSizeTracker stack) {
 887  143 t.apply(new TypeAbstractVisitor_void() {
 888  129 @Override public void defaultCase(Type t) {
 889  129 mv.visitVarInsn(ALOAD, var); stack.adjust(1);
 890    }
 891  0 @Override public void forBooleanType(BooleanType t) {
 892  0 mv.visitVarInsn(ILOAD, var);
 893  0 stack.adjust(1);
 894  0 mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)" + BOOLEAN_DESCRIPTOR);
 895    }
 896  0 @Override public void forCharType(CharType t) {
 897  0 generateBoxingCode(ILOAD, 1, "java/lang/Character", CHARACTER_DESCRIPTOR, "C");
 898    }
 899  0 @Override public void forByteType(ByteType t) {
 900  0 generateBoxingCode(ILOAD, 1, "java/lang/Byte", BYTE_DESCRIPTOR, "B");
 901    }
 902  0 @Override public void forShortType(ShortType t) {
 903  0 generateBoxingCode(ILOAD, 1, "java/lang/Short", SHORT_DESCRIPTOR, "S");
 904    }
 905  14 @Override public void forIntType(IntType t) {
 906  14 generateBoxingCode(ILOAD, 1, "java/lang/Integer", INTEGER_DESCRIPTOR, "I");
 907    }
 908  0 @Override public void forLongType(LongType t) {
 909  0 generateBoxingCode(LLOAD, 2, "java/lang/Long", LONG_DESCRIPTOR, "J");
 910    }
 911  0 @Override public void forFloatType(FloatType t) {
 912  0 generateBoxingCode(FLOAD, 1, "java/lang/Float", FLOAT_DESCRIPTOR, "F");
 913    }
 914  0 @Override public void forDoubleType(DoubleType t) {
 915  0 generateBoxingCode(DLOAD, 2, "java/lang/Double", DOUBLE_DESCRIPTOR, "D");
 916    }
 917   
 918  14 private void generateBoxingCode(int loadOp, int size, String className, String boxedDescriptor,
 919    String primDescriptor) {
 920  14 if (_java5) {
 921  14 mv.visitVarInsn(loadOp, var);
 922  14 stack.adjust(size);
 923  14 mv.visitMethodInsn(INVOKESTATIC, className, "valueOf", "(" + primDescriptor + ")" +
 924    boxedDescriptor);
 925  14 stack.adjust(-size+1);
 926    }
 927    else {
 928  0 mv.visitTypeInsn(NEW, className);
 929  0 mv.visitInsn(DUP);
 930  0 stack.adjust(2);
 931  0 mv.visitVarInsn(loadOp, var);
 932  0 stack.adjust(size);
 933  0 mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(" + primDescriptor + ")V");
 934  0 stack.adjust(-size-1);
 935    }
 936    }
 937   
 938    });
 939    }
 940   
 941    /** Convert the value on the stack to the given type, casting or unboxing if necessary. */
 942  119 private void emitConvert(final MethodVisitor mv, Type expectedT, final StackSizeTracker stack) {
 943  119 _opt.typeSystem().erase(expectedT).apply(new TypeAbstractVisitor_void() {
 944    // do nothing for NullType -- it should be erased to Object, so no cast is needed
 945  0 @Override public void forArrayType(ArrayType t) {
 946  0 mv.visitTypeInsn(CHECKCAST, typeDescriptor(t));
 947    }
 948  65 @Override public void forClassType(ClassType t) {
 949  65 if (!t.equals(TypeSystem.OBJECT)) {
 950  46 mv.visitTypeInsn(CHECKCAST, className(t.ofClass()));
 951    }
 952    }
 953  0 @Override public void forBooleanType(BooleanType t) {
 954  0 generateUnboxingCode(1, "java/lang/Boolean", "booleanValue", "Z");
 955    }
 956  0 @Override public void forCharType(CharType t) {
 957  0 generateUnboxingCode(1, "java/lang/Character", "charValue", "C");
 958    }
 959  0 @Override public void forByteType(ByteType t) {
 960  0 generateUnboxingCode(1, "java/lang/Byte", "byteValue", "B");
 961    }
 962  0 @Override public void forShortType(ShortType t) {
 963  0 generateUnboxingCode(1, "java/lang/Short", "shortValue", "S");
 964    }
 965  42 @Override public void forIntType(IntType t) {
 966  42 generateUnboxingCode(1, "java/lang/Integer", "intValue", "I");
 967    }
 968  0 @Override public void forLongType(LongType t) {
 969  0 generateUnboxingCode(2, "java/lang/Long", "longValue", "J");
 970    }
 971  0 @Override public void forFloatType(FloatType t) {
 972  0 generateUnboxingCode(1, "java/lang/Float", "floatValue", "F");
 973    }
 974  0 @Override public void forDoubleType(DoubleType t) {
 975  0 generateUnboxingCode(2, "java/lang/Double", "doubleValue", "D");
 976    }
 977  42 private void generateUnboxingCode(int size, String className, String methodName,
 978    String primDescriptor) {
 979    // The bytecode verifier requires this cast
 980  42 mv.visitTypeInsn(CHECKCAST, className);
 981  42 mv.visitMethodInsn(INVOKEVIRTUAL, className, methodName, "()" + primDescriptor);
 982  42 stack.adjust(-1+size);
 983    }
 984    });
 985    }
 986   
 987    /** Helper method for printing debug messages from generated code. */
 988  0 @SuppressWarnings("unused") private void emitDebug(MethodVisitor mv, String message, StackSizeTracker stack) {
 989  0 mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
 990  0 mv.visitLdcInsn(message);
 991  0 stack.adjust(2);
 992  0 mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
 993  0 stack.adjust(-2);
 994    }
 995   
 996    /** Helper method for printing a toString of the current object. The object is not consumed. */
 997  0 @SuppressWarnings("unused") private void emitPrintToString(MethodVisitor mv, StackSizeTracker stack) {
 998  0 mv.visitInsn(DUP);
 999  0 mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
 1000  0 stack.adjust(2);
 1001  0 mv.visitInsn(SWAP);
 1002  0 mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V");
 1003  0 stack.adjust(-2);
 1004    }
 1005   
 1006    /* AUXILIARY STATIC METHODS */
 1007   
 1008  322 private static String typeSignature(Type t) { return encodeType(t); }
 1009   
 1010    /** Nonstatic because it depends on field _opt. */
 1011  274 private String typeDescriptor(Type t) { return encodeType(_opt.typeSystem().erase(t)); }
 1012   
 1013  488 private static String className(DJClass c) { return c.fullName().replace('.', '/'); }
 1014   
 1015  71 private static DJClass extractClass(Type t) {
 1016  71 if (t instanceof ClassType) { return ((ClassType) t).ofClass(); }
 1017    else {
 1018  0 throw new IllegalArgumentException("Expected ClassType, but given " + t.getClass().getName());
 1019    }
 1020    }
 1021   
 1022  166 private static String[] extractClassNames(List<? extends ReferenceTypeName> types) {
 1023  166 String[] result = new String[types.size()];
 1024  166 int i = 0;
 1025  166 for (ReferenceTypeName tn : types) {
 1026  1 result[i++] = className(extractClass(NodeProperties.getType(tn)));
 1027    }
 1028  166 return result;
 1029    }
 1030   
 1031  19 private static String typeParamListSignature(Iterable<? extends TypeParameter> params) {
 1032  19 StringBuilder result = new StringBuilder();
 1033  19 result.append('<');
 1034  19 for (TypeParameter p : params) {
 1035  19 VariableType t = NodeProperties.getTypeVariable(p);
 1036  19 result.append(t.symbol().name());
 1037  19 Type upper = t.symbol().upperBound();
 1038  19 boolean validIntersection = false;
 1039  19 if (upper instanceof IntersectionType) {
 1040  0 for (Type intersectElt : ((IntersectionType) upper).ofTypes()) {
 1041  0 validIntersection = true;
 1042  0 result.append(':').append(typeSignature(intersectElt));
 1043    }
 1044    }
 1045  19 if (!validIntersection) { result.append(':').append(typeSignature(upper)); }
 1046    }
 1047  19 result.append('>');
 1048  19 return result.toString();
 1049    }
 1050   
 1051  333 private static Iterable<LocalVariable> extractVars(Iterable<? extends FormalParameter> params) {
 1052  333 return IterUtil.map(params, NodeProperties.NODE_VARIABLE);
 1053    }
 1054   
 1055  131 private static String paramListSignature(String prefix, Iterable<LocalVariable> params) {
 1056  131 StringBuilder result = new StringBuilder();
 1057  131 result.append('(');
 1058  131 result.append(prefix);
 1059  143 for (LocalVariable var : params) { result.append(typeSignature(var.type())); }
 1060  131 result.append(')');
 1061  131 return result.toString();
 1062    }
 1063   
 1064    /** Nonstatic because it depends on field _opt. */
 1065  138 private String paramListDescriptor(String prefix, Iterable<LocalVariable> params) {
 1066  138 StringBuilder result = new StringBuilder();
 1067  138 result.append('(');
 1068  138 result.append(prefix);
 1069  150 for (LocalVariable var : params) { result.append(typeDescriptor(var.type())); }
 1070  138 result.append(')');
 1071  138 return result.toString();
 1072    }
 1073   
 1074    /** Abstraction of typeDescriptor and typeSignature. */
 1075  596 private static String encodeType(Type t) {
 1076  596 final StringBuilder result = new StringBuilder();
 1077  596 t.apply(new TypeAbstractVisitor_void() {
 1078  0 @Override public void forBooleanType(BooleanType t) { result.append('Z'); }
 1079  0 @Override public void forCharType(CharType t) { result.append('C'); }
 1080  0 @Override public void forByteType(ByteType t) { result.append('B'); }
 1081  0 @Override public void forShortType(ShortType t) { result.append('S'); }
 1082  196 @Override public void forIntType(IntType t) { result.append('I'); }
 1083  0 @Override public void forLongType(LongType t) { result.append('J'); }
 1084  0 @Override public void forFloatType(FloatType t) { result.append('F'); }
 1085  0 @Override public void forDoubleType(DoubleType t) { result.append('D'); }
 1086  0 @Override public void forNullType(NullType t) { result.append(OBJECT_DESCRIPTOR); }
 1087  168 @Override public void forArrayType(ArrayType t) { result.append('['); t.ofType().apply(this); }
 1088   
 1089  314 @Override public void forClassType(ClassType t) {
 1090  314 result.append('L');
 1091  314 boolean first = true;
 1092  314 for (DJClass c : SymbolUtil.outerClassChain(t.ofClass())) {
 1093  314 if (first) { result.append(className(c)); first = false; }
 1094  0 else { result.append('$').append(c.declaredName()); }
 1095    }
 1096  314 result.append(';');
 1097    }
 1098   
 1099  22 @Override public void forParameterizedClassType(ParameterizedClassType t) {
 1100  22 result.append('L');
 1101  22 Character classDelim = null;
 1102  22 Iterator<? extends Type> args = t.typeArguments().iterator();
 1103  22 DJClass tClass = t.ofClass();
 1104  22 for (DJClass c : SymbolUtil.outerClassChain(tClass)) {
 1105  22 if (classDelim == null) { result.append(className(c)); }
 1106  0 else { result.append(classDelim).append(c.declaredName()); }
 1107  22 classDelim = '$';
 1108  22 if (SymbolUtil.dynamicallyEncloses(c, tClass)) {
 1109  22 Iterable<VariableType> params = c.declaredTypeParameters();
 1110  22 if (!IterUtil.isEmpty(params)) {
 1111  22 result.append('<');
 1112  22 for (VariableType param : params) { args.next().apply(this); }
 1113  22 result.append('>');
 1114  22 classDelim = '.';
 1115    }
 1116    }
 1117    }
 1118  22 result.append(';');
 1119    }
 1120   
 1121  62 @Override public void forVariableType(VariableType t) {
 1122  62 result.append('T');
 1123  62 result.append(t.symbol().name());
 1124  62 result.append(';');
 1125    }
 1126   
 1127  0 @Override public void forIntersectionType(IntersectionType t) {
 1128  0 if (IterUtil.isEmpty(t.ofTypes())) { result.append(OBJECT_DESCRIPTOR); }
 1129  0 else { IterUtil.first(t.ofTypes()).apply(this); }
 1130    }
 1131   
 1132  0 @Override public void forUnionType(UnionType t) {
 1133  0 result.append("Ljava/lang/Object;");
 1134    }
 1135   
 1136  0 @Override public void forTopType(TopType t) { result.append(OBJECT_DESCRIPTOR); }
 1137  0 @Override public void forBottomType(BottomType t) { result.append(OBJECT_DESCRIPTOR); }
 1138  24 @Override public void forVoidType(VoidType v) { result.append("V"); }
 1139   
 1140  0 @Override public void forWildcard(Wildcard w) {
 1141  0 if (w.symbol().upperBound().equals(TypeSystem.OBJECT)) {
 1142  0 if (w.symbol().lowerBound().equals(TypeSystem.NULL)) { result.append('*'); }
 1143  0 else { result.append('-'); w.symbol().lowerBound().apply(this); }
 1144    }
 1145  0 else { result.append('+'); w.symbol().upperBound().apply(this); }
 1146    }
 1147   
 1148    });
 1149  596 return result.toString();
 1150    }
 1151   
 1152    /**
 1153    * Create an array mapping param indices to their corresponding locations in the local variable
 1154    * stack. The first entry has index {@code reservedSize} (representing the space reserved for
 1155    * implicit parameters like "this" and "bindings"). The last entry (at index {@code params.size() + 1})
 1156    * is the size of the local variable stack occupied by parameters. This mapping is nontrivial,
 1157    * because while most values have a 1-unit size, {@code double}s and {@code long}s have size 2.
 1158    */
 1159  131 private static int[] computeParamLocations(List<FormalParameter> params, int reservedSize) {
 1160  131 int[] result = new int[params.size() + 1];
 1161  131 int index = reservedSize;
 1162  131 int paramI = 0;
 1163  131 for (FormalParameter p : params) {
 1164  143 result[paramI] = index;
 1165  143 Type t = NodeProperties.getVariable(p).type();
 1166  0 if (t instanceof LongType || t instanceof DoubleType) { index += 2; }
 1167  143 else { index += 1; }
 1168  143 paramI++;
 1169    }
 1170  131 result[paramI] = index;
 1171  131 return result;
 1172    }
 1173   
 1174    /**
 1175    * Get the constant value of the given expression, if it exists and is of a supported type.
 1176    * Otherwise, return {@code null}.
 1177    */
 1178  7 private static Object expressionConstantVal(Expression exp) {
 1179  7 Object result = null;
 1180  7 if (NodeProperties.hasValue(exp)) {
 1181  0 result = NodeProperties.getValue(exp);
 1182  0 if (!(result instanceof Integer || result instanceof Float || result instanceof Long ||
 1183    result instanceof Double || result instanceof String)) {
 1184  0 result = null;
 1185    }
 1186    }
 1187  7 return result;
 1188    }
 1189   
 1190    /** If the given access flags has default access, set it to public. */
 1191  35 private static int defaultToPublicAccess(int accessFlags) {
 1192  35 if (!Modifier.isPublic(accessFlags) && !Modifier.isProtected(accessFlags) &&
 1193    !Modifier.isPrivate(accessFlags)) {
 1194  35 accessFlags |= PUBLIC.getBits();
 1195    }
 1196  35 return accessFlags;
 1197    }
 1198   
 1199    /** If the given access flags has default access, set it to protected. */
 1200  19 private static int defaultToProtectedAccess(int accessFlags) {
 1201  19 if (!Modifier.isPublic(accessFlags) && !Modifier.isProtected(accessFlags) &&
 1202    !Modifier.isPrivate(accessFlags)) {
 1203  0 accessFlags |= PROTECTED.getBits();
 1204    }
 1205  19 return accessFlags;
 1206    }
 1207   
 1208   
 1209    /* Helper classes */
 1210   
 1211    /**
 1212    * Simplifies calculating the stack size that must be allocated for a method. Keeps track
 1213    * of both local variable allocations and stack growth as code is generated.
 1214    */
 1215    private static class StackSizeTracker {
 1216    private int _maxStack;
 1217    private int _maxLocals;
 1218    private int _currentStack;
 1219    private int _markedStack;
 1220   
 1221    /**
 1222    * @param reservedParamsSize The space reserved for special parameters like {@code this}.
 1223    */
 1224  182 public StackSizeTracker(int reservedParamsSize) {
 1225  182 _maxStack = 0;
 1226  182 _maxLocals = reservedParamsSize;
 1227  182 _currentStack = 0;
 1228  182 _markedStack = 0;
 1229    }
 1230   
 1231    /**
 1232    * Adjust the current stack size. Positive values cause it to grow; negative values cause it
 1233    * to shrink.
 1234    */
 1235  2019 public void adjust(int delta) {
 1236  2019 _currentStack += delta;
 1237  624 if (_currentStack > _maxStack) { _maxStack = _currentStack; }
 1238  0 if (_currentStack < 0) { throw new IllegalStateException("Stack has negative size"); }
 1239    }
 1240   
 1241    /**
 1242    * Mark the current stack size for later resetting. Note that nested marks (two marks followed by
 1243    * two resets) are not supported (both resets go to the most recent mark).
 1244    */
 1245  119 public void mark() { _markedStack = _currentStack; }
 1246   
 1247    /** Reset stack size to the previously-marked location. */
 1248  119 public void reset() { _currentStack = _markedStack; }
 1249   
 1250    /**
 1251    * Allocate space for a variable of any type besides {@code double} or {@code long}.
 1252    * @return The index at which the variable should be stored.
 1253    */
 1254  131 public int newVariable() {
 1255  131 return _maxLocals++; // result is the pre-increment value
 1256    }
 1257   
 1258    /**
 1259    * Allocate a variable holding a {@code double} or {@code long}.
 1260    * @return The index at which the variable should be stored.
 1261    */
 1262  0 public int newBigVariable() {
 1263  0 int result = _maxLocals;
 1264  0 _maxLocals += 2;
 1265  0 return result;
 1266    }
 1267   
 1268  182 public int maxStack() { return _maxStack; }
 1269   
 1270  182 public int maxLocals() { return _maxLocals; }
 1271   
 1272    }
 1273   
 1274   
 1275    /**
 1276    * Provides an interface through which compiled classes can invoke the interpreter. Each instance
 1277    * corresponds to a single compiled class; within the class, each relevant piece of syntax has
 1278    * a key (a string) generated at compile-time, which the adapter maps to the AST. Compiled
 1279    * classes must only provide that key and an environment in which to perform evaluation,
 1280    * and the adapter calls the appropriate interpreter methods.
 1281    */
 1282    public class EvaluationAdapter {
 1283   
 1284    /**
 1285    * Evaluate a method body in the given environment, extended with the method parameters bound
 1286    * to the given arguments. If the method is non-static, {@code this} should be defined in
 1287    * {@code bindings}.
 1288    * @return The value returned by the method body (via a return statement); {@code null}
 1289    * if the return statement is void, or if no return statement is evaluated.
 1290    * @throws Throwable Any exceptions (or errors) that occur during evaluation, without any wrapping.
 1291    */
 1292  30 public Object evaluateMethod(String key, RuntimeBindings bindings, Object[] args) throws Throwable {
 1293  30 MethodDeclaration decl = _methods.get(key);
 1294  30 RuntimeBindings methodBindings = bindArgs(bindings, decl.getParameters(), args);
 1295  30 return evaluateBlock(decl.getBody(), NodeProperties.getErasedType(decl).value(), methodBindings);
 1296    }
 1297   
 1298    /**
 1299    * Evaluate an expression in the given environment.
 1300    * @return The value of the interpreted expression.
 1301    * @throws Throwable Any exceptions (or errors) that occur during evaluation, without any wrapping.
 1302    */
 1303  0 public Object evaluateExpression(String key, RuntimeBindings bindings) throws Throwable {
 1304  0 Expression exp = _expressions.get(key);
 1305  0 return evaluateExpression(exp, bindings);
 1306    }
 1307   
 1308    /**
 1309    * Evaluate the argument to a {@code this} or {@code super} constructor call at the given index.
 1310    * The given environment is extended with the constructor parameters bound to the given arguments;
 1311    * {@code this} should be defined in {@code bindings}.
 1312    * @param index 0-based index into the call's arguments; -1 for the call's outer (prefix) expression
 1313    * @throws Throwable Any exceptions (or errors) that occur during evaluation, without any wrapping.
 1314    */
 1315  10 public Object evaluateConstructorCallArg(String key, int index, RuntimeBindings bindings,
 1316    Object[] args) throws Throwable {
 1317  10 ConstructorDeclaration decl = _constructors.get(key);
 1318  10 Expression exp;
 1319  0 if (index == -1) { exp = decl.getConstructorCall().getExpression(); }
 1320  10 else { exp = decl.getConstructorCall().getArguments().get(index); }
 1321  10 RuntimeBindings constructorBindings = bindArgs(bindings, decl.getParameters(), args);
 1322  10 return evaluateExpression(exp, constructorBindings);
 1323    }
 1324   
 1325    /**
 1326    * Evaluate a constructor body in the given environment, extended with the constructor parameters
 1327    * bound to the given arguments. {@code this} should be defined in {@code bindings}.
 1328    * @throws Throwable Any exceptions (or errors) that occur during evaluation, without any wrapping.
 1329    */
 1330  31 public void evaluateConstructorBody(String key, RuntimeBindings bindings, Object[] args)
 1331    throws Throwable {
 1332  31 ConstructorDeclaration decl = _constructors.get(key);
 1333  31 RuntimeBindings constructorBindings = bindArgs(bindings, decl.getParameters(), args);
 1334  31 evaluateBlock(new BlockStatement(decl.getStatements()), void.class, constructorBindings);
 1335    }
 1336   
 1337    /**
 1338    * Evaluate an initializer in the given environment. If the initializer is non-static,
 1339    * {@code this} should be defined in {@code bindings}.
 1340    * @throws Throwable Any exceptions (or errors) that occur during evaluation, without any wrapping.
 1341    */
 1342  0 public void evaluateInitializer(String key, RuntimeBindings bindings) throws Throwable {
 1343  0 Initializer decl = _initializers.get(key);
 1344  0 evaluateBlock(decl.getBlock(), void.class, bindings);
 1345    }
 1346   
 1347    /**
 1348    * Make a factory object for binding a value to {@code this}. This level of indirection is
 1349    * necessary because constructors cannot pass "this" to a method until after the super constructor
 1350    * has run; in the mean time, other methods of this class or constructors of its inner classes may
 1351    * have been invoked, requiring some way to produce the complete bindings.
 1352    * @param bindings A set of bindings to extend.
 1353    */
 1354  49 public BindingsFactory makeBindingsFactory(RuntimeBindings bindings) {
 1355  49 return new BindingsFactory(bindings, _treeClass);
 1356    }
 1357   
 1358  71 private RuntimeBindings bindArgs(RuntimeBindings parent, List<FormalParameter> params,
 1359    Object[] args) {
 1360  71 return new RuntimeBindings(parent, extractVars(params), IterUtil.asIterable(args));
 1361    }
 1362   
 1363  10 private Object evaluateExpression(Expression exp, RuntimeBindings bindings) throws Throwable {
 1364  10 try { return new ExpressionEvaluator(bindings, _opt).value(exp); }
 1365    catch (WrappedException e) {
 1366  0 if (e.getCause() instanceof EvaluatorException) {
 1367  0 throw ((EvaluatorException) e.getCause()).getCause();
 1368    }
 1369  0 else { throw e; }
 1370    }
 1371    }
 1372   
 1373  61 private Object evaluateBlock(BlockStatement block, Class<?> returnType,
 1374    RuntimeBindings bindings) throws Throwable {
 1375  61 try {
 1376  61 block.acceptVisitor(new StatementEvaluator(bindings, _opt));
 1377    // if we didn't return, produce null or a zero primitive
 1378  35 return SymbolUtil.initialValue(returnType);
 1379    }
 1380    catch (StatementEvaluator.ReturnException e) {
 1381  26 return e.value().unwrap(null);
 1382    }
 1383    catch (WrappedException e) {
 1384  0 if (e.getCause() instanceof EvaluatorException) {
 1385  0 throw ((EvaluatorException) e.getCause()).getCause();
 1386    }
 1387  0 else { throw e; }
 1388    }
 1389    }
 1390   
 1391    }
 1392   
 1393    /**
 1394    * A simple factory mapping an object to a RuntimeBindings in which that object is defined as "this".
 1395    * See {@link EvaluationAdapter#makeBindingsFactory}. By informal contract, all invocations of
 1396    * {@link #value} should use the same {@code thisVal} parameter.
 1397    */
 1398    public static class BindingsFactory implements Lambda<Object, RuntimeBindings> {
 1399    private final RuntimeBindings _bindings;
 1400    private final DJClass _thisClass;
 1401    private RuntimeBindings _cachedResult;
 1402  49 public BindingsFactory(RuntimeBindings bindings, DJClass thisClass) {
 1403  49 _bindings = bindings;
 1404  49 _thisClass = thisClass;
 1405  49 _cachedResult = null;
 1406    }
 1407  61 public RuntimeBindings value(Object thisVal) {
 1408    // Since a BindingsFactory is stored with every instance of every class compiled by TreeCompiler,
 1409    // and run with every method invocation, the time/space tradeoff of caching here is worth examining
 1410    // more closely.
 1411  49 if (_cachedResult == null) { _cachedResult = new RuntimeBindings(_bindings, _thisClass, thisVal); }
 1412  61 return _cachedResult;
 1413    }
 1414    }
 1415   
 1416   
 1417    /* Descriptors of EvaluationAdapter methods. Declared here to be easy to check. */
 1418   
 1419    private static final String EVALUATE_METHOD_DESCRIPTOR =
 1420    "(" + STRING_DESCRIPTOR + RUNTIME_BINDINGS_DESCRIPTOR + "[" + OBJECT_DESCRIPTOR +
 1421    ")" + OBJECT_DESCRIPTOR;
 1422   
 1423    private static final String EVALUATE_EXPRESSION_DESCRIPTOR =
 1424    "(" + STRING_DESCRIPTOR + RUNTIME_BINDINGS_DESCRIPTOR + ")" + OBJECT_DESCRIPTOR;
 1425   
 1426    private static final String EVALUATE_CONSTRUCTOR_CALL_ARG_DESCRIPTOR =
 1427    "(" + STRING_DESCRIPTOR + "I" + RUNTIME_BINDINGS_DESCRIPTOR + "[" + OBJECT_DESCRIPTOR +
 1428    ")" + OBJECT_DESCRIPTOR;
 1429   
 1430    private static final String EVALUATE_CONSTRUCTOR_BODY_DESCRIPTOR =
 1431    "(" + STRING_DESCRIPTOR + RUNTIME_BINDINGS_DESCRIPTOR + "[" + OBJECT_DESCRIPTOR + ")V";
 1432   
 1433    private static final String EVALUATE_INITIALIZER_DESCRIPTOR =
 1434    "(" + STRING_DESCRIPTOR + RUNTIME_BINDINGS_DESCRIPTOR + ")V";
 1435   
 1436    private static final String MAKE_BINDINGS_FACTORY_DESCRIPTOR =
 1437    "(" + RUNTIME_BINDINGS_DESCRIPTOR + ")" + BINDINGS_FACTORY_DESCRIPTOR;
 1438   
 1439    private static final String BINDINGS_FACTORY_VALUE_DESCRIPTOR =
 1440    "(" + OBJECT_DESCRIPTOR + ")" + RUNTIME_BINDINGS_DESCRIPTOR;
 1441   
 1442    }