Package wyjc

Source Code of wyjc.Wyil2JavaBuilder$JvmCoercion

// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//    * Neither the name of the <organization> nor the
//      names of its contributors may be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package wyjc;

import java.io.*;
import java.math.BigInteger;
import java.util.*;

import wybs.lang.Build;
import wybs.lang.Builder;
import wycc.lang.Attribute;
import wycc.lang.NameID;
import wycc.lang.SyntaxError;
import wycc.util.Logger;
import wycc.util.Pair;
import static wycc.lang.SyntaxError.*;
import wyautl.util.BigRational;
import wyfs.io.BinaryOutputStream;
import wyfs.lang.Path;
import wyil.lang.*;
import wyil.lang.Constant;
import wyjc.util.WyjcBuildTask;
import jasm.attributes.Code.Handler;
import jasm.attributes.LineNumberTable;
import jasm.attributes.SourceFile;
import jasm.lang.*;
import jasm.lang.Modifier;
//import jasm.lang.Constant;
import jasm.verifier.*;
import wyrl.io.JavaIdentifierOutputStream;
import static jasm.lang.JvmTypes.*;

/**
* Responsible for converting WYIL files into Java Classfiles. This is a
* relatively straightforward process, given the all the hard work has already
* been done by the Whiley-2-Wyil builder.
*
* @author David J. Pearce
*
*/
public class Wyil2JavaBuilder implements Builder {
  private static int CLASS_VERSION = 49;

  /**
   * The master project for identifying all resources available to the
   * builder. This includes all modules declared in the project being verified
   * and/or defined in external resources (e.g. jar files).
   */
  protected final Build.Project project;

  /**
   * For logging information.
   */
  private Logger logger = Logger.NULL;

  protected String filename;
  protected JvmType.Clazz owner;

  public Wyil2JavaBuilder(Build.Project project) {
    this.project = project;
  }

  public void setLogger(Logger logger) {
    this.logger = logger;
  }

  public Build.Project project() {
    return project;
  }

  public Set<Path.Entry<?>> build(Collection<Pair<Path.Entry<?>,Path.Root>> delta) throws IOException {

    Runtime runtime = Runtime.getRuntime();
    long start = System.currentTimeMillis();
    long memory = runtime.freeMemory();

    // ========================================================================
    // Translate files
    // ========================================================================
    HashSet<Path.Entry<?>> generatedFiles = new HashSet<Path.Entry<?>>();

    for(Pair<Path.Entry<?>,Path.Root> p : delta) {
      Path.Root dst = p.second();
      Path.Entry<WyilFile> sf = (Path.Entry<WyilFile>) p.first();
      Path.Entry<ClassFile> df = dst.create(sf.id(), WyjcBuildTask.ContentType);
      generatedFiles.add(df);

      // Translate WyilFile into JVM ClassFile
      ArrayList<ClassFile> lambdas = new ArrayList<ClassFile>();
      ClassFile contents = build(sf.read(), lambdas);

      // FIXME: deadCode elimination is currently unsafe because the
      // LineNumberTable and Exceptions attributes do not deal with rewrites
      // properly.

      // eliminate any dead code that was introduced.
      // new DeadCodeElimination().apply(file);

      // Write class file into its destination
      df.write(contents);

      // Finally, write out any lambda classes created to support the
      // main class. This is necessary because every occurrence of a
      // lambda expression in the WyilFile generates an inner class
      // responsible for calling the given function.
      Path.ID parent = df.id();
      Path.ID pkg = parent.subpath(0, parent.size() - 1);
      for (int i = 0; i != lambdas.size(); ++i) {
        Path.ID id = pkg.append(parent.last() + "$" + i);
        Path.Entry<ClassFile> lf = dst.create(id, WyjcBuildTask.ContentType);
        lf.write(lambdas.get(i));
        generatedFiles.add(lf);
      }
    }

    // ========================================================================
    // Done
    // ========================================================================

    long endTime = System.currentTimeMillis();
    logger.logTimedMessage("Wyil => Java: compiled " + delta.size() + " file(s)",
        endTime - start, memory - runtime.freeMemory());

    return generatedFiles;
  }

  private ClassFile build(WyilFile module,
      ArrayList<ClassFile> lambdas) {
    owner = new JvmType.Clazz(module.id().parent().toString().replace('.','/'),
        module.id().last());
    ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
    modifiers.add(Modifier.ACC_PUBLIC);
    modifiers.add(Modifier.ACC_FINAL);
    ClassFile cf = new ClassFile(CLASS_VERSION, owner, JAVA_LANG_OBJECT,
        new ArrayList<JvmType.Clazz>(), modifiers);

    this.filename = module.filename();

    if(filename != null) {
      cf.attributes().add(new SourceFile(filename));
    }

    boolean addMainLauncher = false;

    HashMap<JvmConstant,Integer> constants = new HashMap<JvmConstant,Integer>();
    for(WyilFile.FunctionOrMethodDeclaration method : module.functionOrMethods()) {
      if(method.name().equals("main")) {
        addMainLauncher = true;
      }
      cf.methods().addAll(build(method, constants, lambdas));
    }

    buildConstants(constants,lambdas,cf);

    if(addMainLauncher) {
      cf.methods().add(buildMainLauncher(owner));
    }

    return cf;
  }

  private void buildConstants(HashMap<JvmConstant, Integer> constants,
      ArrayList<ClassFile> lambdas, ClassFile cf) {
    buildCoercions(constants,cf);
    buildValues(constants,lambdas,cf);
  }

  private void buildCoercions(HashMap<JvmConstant,Integer> constants, ClassFile cf) {
    HashSet<JvmConstant> done = new HashSet<JvmConstant>();
    HashMap<JvmConstant,Integer> original = constants;
    // this could be a little more efficient I think!!
    while(done.size() != constants.size()) {
      // We have to clone the constants map, since it may be expanded as a
      // result of buildCoercion(). This will occur if the coercion
      // constructed requires a helper coercion that was not in the
      // original constants map.
      HashMap<JvmConstant,Integer> nconstants = new HashMap<JvmConstant,Integer>(constants);
      for(Map.Entry<JvmConstant,Integer> entry : constants.entrySet()) {
        JvmConstant e = entry.getKey();
        if(!done.contains(e) && e instanceof JvmCoercion) {
          JvmCoercion c = (JvmCoercion) e;
          buildCoercion(c.from,c.to,entry.getValue(),nconstants,cf);
        }
        done.add(e);
      }
      constants = nconstants;
    }
    original.putAll(constants);
  }

  private void buildValues(HashMap<JvmConstant, Integer> constants,
      ArrayList<ClassFile> lambdas, ClassFile cf) {
    int nvalues = 0;
    ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();

    for(Map.Entry<JvmConstant,Integer> entry : constants.entrySet()) {
      JvmConstant c = entry.getKey();
      if(c instanceof JvmValue) {
        nvalues++;
        Constant constant = ((JvmValue)c).value;
        int index = entry.getValue();

        // First, create the static final field that will hold this constant
        String name = "constant$" + index;
        ArrayList<Modifier> fmods = new ArrayList<Modifier>();
        fmods.add(Modifier.ACC_PRIVATE);
        fmods.add(Modifier.ACC_STATIC);
        fmods.add(Modifier.ACC_FINAL);
        JvmType type = convertType(constant.type());
        ClassFile.Field field = new ClassFile.Field(name, type, fmods);
        cf.fields().add(field);

        // Now, create code to intialise this field
        translate(constant,0,lambdas,bytecodes);
        bytecodes.add(new Bytecode.PutField(owner, name, type, Bytecode.FieldMode.STATIC));
      }
    }

    if(nvalues > 0) {
      // create static initialiser method, but only if we really need to.
      bytecodes.add(new Bytecode.Return(null));

      ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
      modifiers.add(Modifier.ACC_PUBLIC);
      modifiers.add(Modifier.ACC_STATIC);
      modifiers.add(Modifier.ACC_SYNTHETIC);
      JvmType.Function ftype = new JvmType.Function(new JvmType.Void());
      ClassFile.Method clinit = new ClassFile.Method("<clinit>", ftype, modifiers);
      cf.methods().add(clinit);

      // finally add code for staticinitialiser method
      jasm.attributes.Code code = new jasm.attributes.Code(bytecodes,new ArrayList(),clinit);
      clinit.attributes().add(code);
    }
  }

  private ClassFile.Method buildMainLauncher(JvmType.Clazz owner) {
    ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
    modifiers.add(Modifier.ACC_PUBLIC);
    modifiers.add(Modifier.ACC_STATIC);
    modifiers.add(Modifier.ACC_SYNTHETIC);
    JvmType.Function ft1 = new JvmType.Function(T_VOID, new JvmType.Array(
        JAVA_LANG_STRING));
    ClassFile.Method cm = new ClassFile.Method("main", ft1, modifiers);
    JvmType.Array strArr = new JvmType.Array(JAVA_LANG_STRING);
    ArrayList<Bytecode> codes = new ArrayList<Bytecode>();
    ft1 = new JvmType.Function(WHILEYRECORD, new JvmType.Array(
        JAVA_LANG_STRING));
    codes.add(new Bytecode.Load(0, strArr));
    codes.add(new Bytecode.Invoke(WHILEYUTIL, "systemConsole", ft1,
        Bytecode.InvokeMode.STATIC));
    Type.Method wyft = Type.Method(Type.T_VOID, Type.T_VOID,
        WHILEY_SYSTEM_T);
    JvmType.Function ft3 = convertFunType(wyft);
    codes.add(new Bytecode.Invoke(owner, nameMangle("main", wyft), ft3,
        Bytecode.InvokeMode.STATIC));
    codes.add(new Bytecode.Return(null));

    jasm.attributes.Code code = new jasm.attributes.Code(codes,
        new ArrayList(), cm);
    cm.attributes().add(code);

    return cm;
  }

  private List<ClassFile.Method> build(
      WyilFile.FunctionOrMethodDeclaration method,
      HashMap<JvmConstant, Integer> constants,
      ArrayList<ClassFile> lambdas) {
    ArrayList<ClassFile.Method> methods = new ArrayList<ClassFile.Method>();
    int num = 1;
    for (WyilFile.Case c : method.cases()) {
      if (method.hasModifier(wyil.lang.Modifier.NATIVE)) {
        methods.add(buildNativeOrExport(c, method, constants));
      } else {
        if (method.hasModifier(wyil.lang.Modifier.EXPORT)) {
          methods.add(buildNativeOrExport(c, method, constants));
        }
        methods.add(build(num++, c, method, constants, lambdas));
      }
    }
    return methods;
  }

  private ClassFile.Method build(int caseNum, WyilFile.Case mcase,
      WyilFile.FunctionOrMethodDeclaration method,
      HashMap<JvmConstant, Integer> constants,
      ArrayList<ClassFile> lambdas) {

    ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
    if(method.hasModifier(wyil.lang.Modifier.PUBLIC)) {
      modifiers.add(Modifier.ACC_PUBLIC);
    }
    modifiers.add(Modifier.ACC_STATIC);
    JvmType.Function ft = convertFunType(method.type());

    String name = nameMangle(method.name(),method.type());

    /* need to put this back somehow?
    if(method.cases().size() > 1) {
      name = name + "$" + caseNum;
    }
    */

    ClassFile.Method cm = new ClassFile.Method(name,ft,modifiers);
    for(Attribute a : mcase.attributes()) {
      if(a instanceof BytecodeAttribute) {
        // FIXME: this is a hack
        cm.attributes().add((BytecodeAttribute)a);
      }
    }

    ArrayList<Handler> handlers = new ArrayList<Handler>();
    ArrayList<LineNumberTable.Entry> lineNumbers = new ArrayList<LineNumberTable.Entry>();
    ArrayList<Bytecode> codes;
    codes = translate(mcase,constants,lambdas,handlers,lineNumbers);
    jasm.attributes.Code code = new jasm.attributes.Code(codes,handlers,cm);
    if(!lineNumbers.isEmpty()) {
      code.attributes().add(new LineNumberTable(lineNumbers));
    }
    cm.attributes().add(code);

    return cm;
  }

  private ClassFile.Method buildNativeOrExport(WyilFile.Case mcase,
      WyilFile.FunctionOrMethodDeclaration method, HashMap<JvmConstant,Integer> constants) {
    ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
    if (method.hasModifier(wyil.lang.Modifier.PUBLIC)
        || method.hasModifier(wyil.lang.Modifier.PUBLIC)) {
      modifiers.add(Modifier.ACC_PUBLIC);
    }
    modifiers.add(Modifier.ACC_STATIC);
    JvmType.Function ft = convertFunType(method.type());

    String name = method.name();
    if(method.hasModifier(wyil.lang.Modifier.NATIVE)) {
      name = nameMangle(method.name(),method.type());
    }

    ClassFile.Method cm = new ClassFile.Method(name,ft,modifiers);
    for(Attribute a : mcase.attributes()) {
      if(a instanceof BytecodeAttribute) {
        // FIXME: this is a hack
        cm.attributes().add((BytecodeAttribute)a);
      }
    }

    ArrayList<Handler> handlers = new ArrayList<Handler>();
    ArrayList<Bytecode> codes;
    codes = translateNativeOrExport(method);
    jasm.attributes.Code code = new jasm.attributes.Code(codes,handlers,cm);

    cm.attributes().add(code);

    return cm;
  }

  private ArrayList<Bytecode> translateNativeOrExport(WyilFile.FunctionOrMethodDeclaration method) {

    ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();
    Type.FunctionOrMethod ft = method.type();
    int slot = 0;

    for (Type param : ft.params()) {
      bytecodes.add(new Bytecode.Load(slot++, convertType(param)));
    }

    if (method.hasModifier(wyil.lang.Modifier.NATIVE)) {
      JvmType.Clazz redirect = new JvmType.Clazz(owner.pkg(), owner
          .components().get(0).first(), "native");
      bytecodes.add(new Bytecode.Invoke(redirect, method.name(),
          convertFunType(ft), Bytecode.InvokeMode.STATIC));
    } else {
      JvmType.Clazz redirect = new JvmType.Clazz(owner.pkg(), owner
          .components().get(0).first());
      bytecodes.add(new Bytecode.Invoke(redirect, nameMangle(
          method.name(), method.type()), convertFunType(ft),
          Bytecode.InvokeMode.STATIC));
    }

    if (ft.ret() == Type.T_VOID) {
      bytecodes.add(new Bytecode.Return(null));
    } else {
      bytecodes.add(new Bytecode.Return(convertType(ft.ret())));
    }

    return bytecodes;
  }

  private ArrayList<Bytecode> translate(WyilFile.Case mcase,
      HashMap<JvmConstant, Integer> constants,
      ArrayList<ClassFile> lambdas,
      ArrayList<Handler> handlers,
      ArrayList<LineNumberTable.Entry> lineNumbers) {
    ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();
    Code.Block block = mcase.body();
    translate(block, block.numSlots(), constants, lambdas, handlers,
        lineNumbers, bytecodes);
    return bytecodes;
  }

  /**
   * Translate the given block into bytecodes.
   *
   * @param blk
   *            --- wyil block to be translated.
   * @param freeSlot
   *            --- identifies the first unsused bytecode register.
   * @param bytecodes
   *            --- list to insert bytecodes into *
   */
  private void translate(Code.Block blk, int freeSlot,
      HashMap<JvmConstant, Integer> constants,
      ArrayList<ClassFile> lambdas,
      ArrayList<Handler> handlers,
      ArrayList<LineNumberTable.Entry> lineNumbers,
      ArrayList<Bytecode> bytecodes) {

    ArrayList<UnresolvedHandler> unresolvedHandlers = new ArrayList<UnresolvedHandler>();
    for (Code.Block.Entry s : blk) {
      Attribute.Source loc = s.attribute(Attribute.Source.class);
      if(loc != null) {
        lineNumbers.add(new LineNumberTable.Entry(bytecodes.size(),loc.line));
      }
      freeSlot = translate(s, freeSlot, constants, lambdas,
          unresolvedHandlers, bytecodes);
    }

    if (unresolvedHandlers.size() > 0) {
      HashMap<String, Integer> labels = new HashMap<String, Integer>();

      for (int i = 0; i != bytecodes.size(); ++i) {
        Bytecode b = bytecodes.get(i);
        if (b instanceof Bytecode.Label) {
          Bytecode.Label lab = (Bytecode.Label) b;
          labels.put(lab.name, i);
        }
      }

      for (UnresolvedHandler ur : unresolvedHandlers) {
        int start = labels.get(ur.start);
        int end = labels.get(ur.end);
        Handler handler = new Handler(start, end, ur.target,
            ur.exception);
        handlers.add(handler);
      }
    }

    // here, we need to resolve the handlers.
  }

  private int translate(Code.Block.Entry entry, int freeSlot,
      HashMap<JvmConstant, Integer> constants,
      ArrayList<ClassFile> lambdas,
      ArrayList<UnresolvedHandler> handlers,
      ArrayList<Bytecode> bytecodes) {
    try {
      Code code = entry.code;
      if(code instanceof Codes.BinaryOperator) {
         translate((Codes.BinaryOperator)code,entry,freeSlot,bytecodes);
      } else if(code instanceof Codes.Convert) {
         translate((Codes.Convert)code,freeSlot,constants,bytecodes);
      } else if(code instanceof Codes.Const) {
        translate((Codes.Const) code, freeSlot, constants, lambdas, bytecodes);
      } else if(code instanceof Codes.Debug) {
         translate((Codes.Debug)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.LoopEnd) {
         translate((Codes.LoopEnd)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.AssertOrAssume) {
         translate((Codes.AssertOrAssume)code,entry,freeSlot,bytecodes);
      } else if(code instanceof Codes.Fail) {
         translate((Codes.Fail)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.FieldLoad) {
         translate((Codes.FieldLoad)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.ForAll) {
         freeSlot = translate((Codes.ForAll)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Goto) {
         translate((Codes.Goto)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.If) {
        translateIfGoto((Codes.If) code, entry, freeSlot, bytecodes);
      } else if(code instanceof Codes.IfIs) {
        translate((Codes.IfIs) code, entry, freeSlot, constants, bytecodes);
      } else if(code instanceof Codes.IndirectInvoke) {
         translate((Codes.IndirectInvoke)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Invoke) {
         translate((Codes.Invoke)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Invert) {
         translate((Codes.Invert)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Label) {
        translate((Codes.Label)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.ListOperator) {
         translate((Codes.ListOperator)code,entry,freeSlot,bytecodes);
      } else if(code instanceof Codes.Lambda) {
         translate((Codes.Lambda)code,freeSlot,lambdas,bytecodes);
      } else if(code instanceof Codes.LengthOf) {
         translate((Codes.LengthOf)code,entry,freeSlot,bytecodes);
      } else if(code instanceof Codes.SubList) {
         translate((Codes.SubList)code,entry,freeSlot,bytecodes);
      } else if(code instanceof Codes.IndexOf) {
         translate((Codes.IndexOf)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Assign) {
         translate((Codes.Assign)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Loop) {
         translate((Codes.Loop)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Move) {
         translate((Codes.Move)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Update) {
         translate((Codes.Update)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.NewMap) {
         translate((Codes.NewMap)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.NewList) {
         translate((Codes.NewList)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.NewRecord) {
         translate((Codes.NewRecord)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.NewSet) {
         translate((Codes.NewSet)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.NewTuple) {
         translate((Codes.NewTuple)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.UnaryOperator) {
         translate((Codes.UnaryOperator)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Dereference) {
         translate((Codes.Dereference)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Return) {
         translate((Codes.Return)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Nop) {
        // do nothing
      } else if(code instanceof Codes.SetOperator) {
         translate((Codes.SetOperator)code,entry,freeSlot,bytecodes);
      } else if(code instanceof Codes.StringOperator) {
         translate((Codes.StringOperator)code,entry,freeSlot,bytecodes);
      } else if(code instanceof Codes.SubString) {
         translate((Codes.SubString)code,entry,freeSlot,bytecodes);
      } else if(code instanceof Codes.Switch) {
         translate((Codes.Switch)code,entry,freeSlot,lambdas,bytecodes);
      } else if(code instanceof Codes.TryCatch) {
         translate((Codes.TryCatch)code,entry,freeSlot,handlers,constants,bytecodes);
      } else if(code instanceof Codes.NewObject) {
         translate((Codes.NewObject)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.Throw) {
         translate((Codes.Throw)code,freeSlot,bytecodes);
      } else if(code instanceof Codes.TupleLoad) {
         translate((Codes.TupleLoad)code,freeSlot,bytecodes);
      } else {
        internalFailure("unknown wyil code encountered (" + code + ")", filename, entry);
      }

    } catch (SyntaxError ex) {
      throw ex;
    } catch (Exception ex) {
      internalFailure(ex.getMessage(), filename, entry, ex);
    }

    return freeSlot;
  }

  private void translate(Codes.Const c, int freeSlot, HashMap<JvmConstant,Integer> constants,
      ArrayList<ClassFile> lambdas, ArrayList<Bytecode> bytecodes) {
    Constant constant = c.constant;
    JvmType jt = convertType(constant.type());

    if (constant instanceof Constant.Decimal || constant instanceof Constant.Bool
        || constant instanceof Constant.Null || constant instanceof Constant.Byte) {
      translate(constant,freeSlot,lambdas,bytecodes);
    } else {
      int id = JvmValue.get(constant,constants);
      String name = "constant$" + id;
      bytecodes.add(new Bytecode.GetField(owner, name, jt, Bytecode.FieldMode.STATIC));
      // the following is necessary to prevent in-place updates of our
      // constants!
      addIncRefs(constant.type(),bytecodes);
    }
    bytecodes.add(new Bytecode.Store(c.target(), jt));
  }

  private void translate(Codes.Convert c, int freeSlot,
      HashMap<JvmConstant, Integer> constants, ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Load(c.operand(0), convertType(c.type())));
    addCoercion(c.type(), c.result, freeSlot, constants, bytecodes);
    bytecodes.add(new Bytecode.Store(c.target(), convertType(c.result)));
  }

  private void translate(Codes.Update code, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Load(code.target(), convertType(code.type())));
    translateUpdate(code.iterator(), code, bytecodes);
    bytecodes.add(new Bytecode.Store(code.target(),
        convertType(code.afterType)));
  }

  /**
   * Iterate down the chain of updates reading values out, updating them, and
   * writing them back.
   *
   * @param iterator
   *            --- update iterator.
   * @param rhsOperand
   *            --- register operand of right-hand side
   * @param bytecodes
   *            --- list of bytecodes to append to.
   */
  private void translateUpdate(Iterator<Codes.LVal> iterator, Codes.Update code,
      ArrayList<Bytecode> bytecodes) {
    Codes.LVal lv = iterator.next();
    if(lv instanceof Codes.ListLVal) {
      Codes.ListLVal l = (Codes.ListLVal) lv;
      if(iterator.hasNext()) {
        // In this case, we're partially updating the element at a
        // given position.
        bytecodes.add(new Bytecode.Dup(WHILEYLIST));
        bytecodes.add(new Bytecode.Load(l.indexOperand,WHILEYINT));
        JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
            WHILEYLIST,WHILEYINT);
        bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "internal_get", ftype,
            Bytecode.InvokeMode.STATIC));
        addReadConversion(l.rawType().element(),bytecodes);
        translateUpdate(iterator,code,bytecodes);
        bytecodes.add(new Bytecode.Load(l.indexOperand,WHILEYINT));
        bytecodes.add(new Bytecode.Swap());
      } else {
        bytecodes.add(new Bytecode.Load(l.indexOperand,WHILEYINT));
        bytecodes.add(new Bytecode.Load(code.result(), convertType(l
            .rawType().element())));
        addWriteConversion(code.rhs(),bytecodes);
      }

      JvmType.Function ftype = new JvmType.Function(WHILEYLIST,
          WHILEYLIST,WHILEYINT,JAVA_LANG_OBJECT);
      bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "set", ftype,
          Bytecode.InvokeMode.STATIC));

    } else if(lv instanceof Codes.StringLVal) {
      Codes.StringLVal l = (Codes.StringLVal) lv;
      // assert: level must be zero here
      bytecodes.add(new Bytecode.Load(l.indexOperand, WHILEYINT));
      bytecodes.add(new Bytecode.Load(code.result(), T_CHAR));

      JvmType.Function ftype = new JvmType.Function(JAVA_LANG_STRING,
          JAVA_LANG_STRING, WHILEYINT, T_CHAR);
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "set", ftype,
          Bytecode.InvokeMode.STATIC));
    } else if(lv instanceof Codes.MapLVal) {
      Codes.MapLVal l = (Codes.MapLVal) lv;
      JvmType keyType = convertType(l.rawType().key());
      JvmType valueType = convertType(l.rawType().value());
      if(iterator.hasNext()) {
        // In this case, we're partially updating the element at a
        // given position.
        bytecodes.add(new Bytecode.Dup(WHILEYMAP));
        bytecodes.add(new Bytecode.Load(l.keyOperand,keyType));
        addWriteConversion(l.rawType().key(),bytecodes);
        JvmType.Function ftype = new JvmType.Function(
            JAVA_LANG_OBJECT, WHILEYMAP, JAVA_LANG_OBJECT);
        bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "internal_get", ftype,
          Bytecode.InvokeMode.STATIC));
        addReadConversion(l.rawType().value(),bytecodes);
        translateUpdate(iterator,code,bytecodes);
        bytecodes.add(new Bytecode.Load(l.keyOperand,keyType));
        addWriteConversion(l.rawType().key(),bytecodes);
        bytecodes.add(new Bytecode.Swap());
      } else {
        bytecodes.add(new Bytecode.Load(l.keyOperand,keyType));
        addWriteConversion(l.rawType().key(),bytecodes);
        bytecodes.add(new Bytecode.Load(code.result(), valueType));
        addWriteConversion(l.rawType().value(),bytecodes);
      }

      JvmType.Function ftype = new JvmType.Function(WHILEYMAP,
          WHILEYMAP,JAVA_LANG_OBJECT,JAVA_LANG_OBJECT);
      bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "put", ftype,
          Bytecode.InvokeMode.STATIC));

    } else if(lv instanceof Codes.RecordLVal) {
      Codes.RecordLVal l = (Codes.RecordLVal) lv;
      Type.EffectiveRecord type = l.rawType();

      if (iterator.hasNext()) {
        bytecodes.add(new Bytecode.Dup(WHILEYRECORD));
        bytecodes.add(new Bytecode.LoadConst(l.field));
        JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
            WHILEYRECORD, JAVA_LANG_STRING);
        bytecodes.add(new Bytecode.Invoke(WHILEYRECORD, "internal_get",
            ftype, Bytecode.InvokeMode.STATIC));
        addReadConversion(type.field(l.field), bytecodes);
        translateUpdate(iterator, code, bytecodes);
        bytecodes.add(new Bytecode.LoadConst(l.field));
        bytecodes.add(new Bytecode.Swap());
      } else {
        bytecodes.add(new Bytecode.LoadConst(l.field));
        bytecodes.add(new Bytecode.Load(code.result(), convertType(type
            .field(l.field))));
        addWriteConversion(type.field(l.field), bytecodes);
      }

      JvmType.Function ftype = new JvmType.Function(WHILEYRECORD,WHILEYRECORD,JAVA_LANG_STRING,JAVA_LANG_OBJECT);
      bytecodes.add(new Bytecode.Invoke(WHILEYRECORD,"put",ftype,Bytecode.InvokeMode.STATIC));
    } else {
      Codes.ReferenceLVal l = (Codes.ReferenceLVal) lv;
      bytecodes.add(new Bytecode.Dup(WHILEYOBJECT));
      JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT);
      bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "state", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      addReadConversion(l.rawType().element(), bytecodes);
      translateUpdate(iterator, code, bytecodes);
      ftype = new JvmType.Function(WHILEYOBJECT, JAVA_LANG_OBJECT);
      bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "setState", ftype,
          Bytecode.InvokeMode.VIRTUAL));
    }
  }

  private void translate(Codes.Return c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    if (c.type == Type.T_VOID) {
      bytecodes.add(new Bytecode.Return(null));
    } else {
      JvmType jt = convertType(c.type);
      bytecodes.add(new Bytecode.Load(c.operand,jt));
      bytecodes.add(new Bytecode.Return(jt));
    }
  }

  private void translate(Codes.Throw c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.New(WHILEYEXCEPTION));
    bytecodes.add(new Bytecode.Dup(WHILEYEXCEPTION));
    bytecodes.add(new Bytecode.Load(c.operand,convertType(c.type)));
    JvmType.Function ftype = new JvmType.Function(T_VOID,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYEXCEPTION, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));
    bytecodes.add(new Bytecode.Throw());
  }

  private void translate(Codes.TupleLoad c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
        WHILEYTUPLE, T_INT);
    bytecodes.add(new Bytecode.Load(c.operand(0),convertType((Type) c.type())));
    bytecodes.add(new Bytecode.LoadConst(c.index));
    bytecodes.add(new Bytecode.Invoke(WHILEYTUPLE, "get", ftype,
        Bytecode.InvokeMode.STATIC));
    addReadConversion(c.type().elements().get(c.index), bytecodes);
    bytecodes.add(new Bytecode.Store(c.target(),convertType(c.type()
        .element(c.index))));
  }

  private void translate(Codes.Switch c, Code.Block.Entry entry, int freeSlot,
      ArrayList<ClassFile> lambdas, ArrayList<Bytecode> bytecodes) {

    ArrayList<jasm.util.Pair<Integer, String>> cases = new ArrayList();
    boolean canUseSwitchBytecode = true;
    for (Pair<Constant, String> p : c.branches) {
      // first, check whether the switch value is indeed an integer.
      Constant v = (Constant) p.first();
      if (!(v instanceof Constant.Integer)) {
        canUseSwitchBytecode = false;
        break;
      }
      // second, check whether integer value can fit into a Java int
      Constant.Integer vi = (Constant.Integer) v;
      int iv = vi.value.intValue();
      if (!BigInteger.valueOf(iv).equals(vi.value)) {
        canUseSwitchBytecode = false;
        break;
      }
      // ok, we're all good so far
      cases.add(new jasm.util.Pair(iv, p.second()));
    }

    if (canUseSwitchBytecode) {
      JvmType.Function ftype = new JvmType.Function(T_INT);
      bytecodes.add(new Bytecode.Load(c.operand,convertType((Type) c.type)));
      bytecodes.add(new Bytecode.Invoke(WHILEYINT, "intValue", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Switch(c.defaultTarget, cases));
    } else {
      // ok, in this case we have to fall back to series of the if
      // conditions. Not ideal.
      for (Pair<Constant, String> p : c.branches) {
        Constant value = p.first();
        String target = p.second();
        translate(value, freeSlot, lambdas, bytecodes);
        bytecodes
            .add(new Bytecode.Load(c.operand, convertType(c.type)));
        translateIfGoto(value.type(), Codes.Comparator.EQ, target, entry,
            freeSlot + 1, bytecodes);
      }
      bytecodes.add(new Bytecode.Goto(c.defaultTarget));
    }
  }

  private void translate(Codes.TryCatch c, Code.Block.Entry entry, int freeSlot,
      ArrayList<UnresolvedHandler> handlers,
      HashMap<JvmConstant, Integer> constants, ArrayList<Bytecode> bytecodes) {

    // this code works by redirecting *all* whiley exceptions into the
    // trampoline block. The trampoline then pulls out the matching ones,
    // and lets the remainder be rethrown.

    String start = freshLabel();
    String trampolineStart = freshLabel();
    bytecodes.add(new Bytecode.Goto(start));
    // trampoline goes here
    bytecodes.add(new Bytecode.Label(trampolineStart));
    bytecodes.add(new Bytecode.Dup(WHILEYEXCEPTION));
    bytecodes.add(new Bytecode.Store(freeSlot, WHILEYEXCEPTION));
    bytecodes.add(new Bytecode.GetField(WHILEYEXCEPTION, "value", JAVA_LANG_OBJECT, Bytecode.FieldMode.NONSTATIC));
    ArrayList<String> bounces = new ArrayList<String>();
    for (Pair<Type, String> handler : c.catches) {
      String bounce = freshLabel();
      bounces.add(bounce);
      bytecodes.add(new Bytecode.Dup(JAVA_LANG_OBJECT));
      translateTypeTest(bounce, Type.T_ANY, handler.first(),
          bytecodes, constants);
    }
    // rethrow what doesn't match
    bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    bytecodes.add(new Bytecode.Load(freeSlot, WHILEYEXCEPTION));
    bytecodes.add(new Bytecode.Throw());
    for(int i=0;i!=bounces.size();++i) {
      String bounce = bounces.get(i);
      Pair<Type,String> handler = c.catches.get(i);
      bytecodes.add(new Bytecode.Label(bounce));
      addReadConversion(handler.first(),bytecodes);
      bytecodes.add(new Bytecode.Store(c.operand, convertType(handler
          .first())));
      bytecodes.add(new Bytecode.Goto(handler.second()));
    }
    bytecodes.add(new Bytecode.Label(start));

    UnresolvedHandler trampolineHandler = new UnresolvedHandler(start,
        c.target, trampolineStart, WHILEYEXCEPTION);
    handlers.add(trampolineHandler);
  }

  private void translateIfGoto(Codes.If code, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType jt = convertType(code.type);
    bytecodes.add(new Bytecode.Load(code.leftOperand,jt));
    bytecodes.add(new Bytecode.Load(code.rightOperand,jt));
    translateIfGoto(code.type,code.op,code.target,stmt,freeSlot,bytecodes);
  }

  private void translateIfGoto(Type c_type, Codes.Comparator cop, String target, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    JvmType type = convertType(c_type);
    if(c_type == Type.T_BOOL) {
      // boolean is a special case, since it is not implemented as an
      // object on the JVM stack. Therefore, we need to use the "if_cmp"
      // bytecode, rather than calling .equals() and using "if" bytecode.
      switch(cop) {
      case EQ:
        bytecodes.add(new Bytecode.IfCmp(Bytecode.IfCmp.EQ, type, target));
        break;
      case NEQ:
        bytecodes.add(new Bytecode.IfCmp(Bytecode.IfCmp.NE, type, target));
        break;
      }
    } else if(c_type == Type.T_CHAR || c_type == Type.T_BYTE) {
      int op;
      switch(cop) {
      case EQ:
        op = Bytecode.IfCmp.EQ;
        break;
      case NEQ:
        op = Bytecode.IfCmp.NE;
        break;
      case LT:
        op = Bytecode.IfCmp.LT;
        break;
      case LTEQ:
        op = Bytecode.IfCmp.LE;
        break;
      case GT:
        op = Bytecode.IfCmp.GT;
        break;
      case GTEQ:
        op = Bytecode.IfCmp.GE;
        break;
      default:
        internalFailure("unknown if condition encountered",filename,stmt);
        return;
      }
      bytecodes.add(new Bytecode.IfCmp(op, T_BYTE,target));
    } else {
      // Non-primitive case. Just use the Object.equals() method, followed
      // by "if" bytecode.
      Bytecode.IfMode op;
      switch(cop) {
      case EQ:
      {
        if(Type.isSubtype(c_type, Type.T_NULL)) {
          // this indicates an interesting special case. The left
          // handside of this equality can be null. Therefore, we
          // cannot directly call "equals()" on this method, since
          // this would cause a null pointer exception!
          JvmType.Function ftype = new JvmType.Function(T_BOOL,JAVA_LANG_OBJECT,JAVA_LANG_OBJECT);
          bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "equals", ftype,
              Bytecode.InvokeMode.STATIC));
        } else {
          JvmType.Function ftype = new JvmType.Function(T_BOOL,JAVA_LANG_OBJECT);
          bytecodes.add(new Bytecode.Invoke((JvmType.Clazz)type, "equals", ftype,
              Bytecode.InvokeMode.VIRTUAL));
        }
        op = Bytecode.IfMode.NE;
        break;
      }
      case NEQ:
      {
        if (Type.isSubtype(c_type, Type.T_NULL)) {
          // this indicates an interesting special case. The left
          // handside of this equality can be null. Therefore, we
          // cannot directly call "equals()" on this method, since
          // this would cause a null pointer exception!
          JvmType.Function ftype = new JvmType.Function(T_BOOL,
              JAVA_LANG_OBJECT, JAVA_LANG_OBJECT);
          bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "equals",
              ftype, Bytecode.InvokeMode.STATIC));
        } else {
          JvmType.Function ftype = new JvmType.Function(T_BOOL,
              JAVA_LANG_OBJECT);
          bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
              "equals", ftype, Bytecode.InvokeMode.VIRTUAL));
        }
        op = Bytecode.IfMode.EQ;
        break;
      }
      case LT:
      {
        JvmType.Function ftype = new JvmType.Function(T_INT,type);
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "compareTo", ftype,
            Bytecode.InvokeMode.VIRTUAL));
        op = Bytecode.IfMode.LT;
        break;
      }
      case LTEQ:
      {
        JvmType.Function ftype = new JvmType.Function(T_INT,type);
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
            "compareTo", ftype, Bytecode.InvokeMode.VIRTUAL));
        op = Bytecode.IfMode.LE;
        break;
      }
      case GT:
      {
        JvmType.Function ftype = new JvmType.Function(T_INT, type);
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
            "compareTo", ftype, Bytecode.InvokeMode.VIRTUAL));
        op = Bytecode.IfMode.GT;
        break;
      }
      case GTEQ:
      {
        JvmType.Function ftype = new JvmType.Function(T_INT,type);
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
            "compareTo", ftype, Bytecode.InvokeMode.VIRTUAL));
        op = Bytecode.IfMode.GE;
        break;
      }
      case SUBSETEQ:
      {
        JvmType.Function ftype = new JvmType.Function(T_BOOL,WHILEYSET,WHILEYSET);
        bytecodes.add(new Bytecode.Invoke(WHILEYSET, "subsetEq", ftype,
            Bytecode.InvokeMode.STATIC));
        op = Bytecode.IfMode.NE;
        break;
      }
      case SUBSET:
      {
        JvmType.Function ftype = new JvmType.Function(T_BOOL,WHILEYSET,WHILEYSET);
        bytecodes.add(new Bytecode.Invoke(WHILEYSET, "subset", ftype,
            Bytecode.InvokeMode.STATIC));
        op = Bytecode.IfMode.NE;
        break;
      }
      case IN:
      {
        JvmType.Function ftype = new JvmType.Function(T_BOOL,
            JAVA_LANG_OBJECT);
        bytecodes.add(new Bytecode.Swap());
        bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_COLLECTION, "contains",
            ftype, Bytecode.InvokeMode.INTERFACE));
        op = Bytecode.IfMode.NE;
        break;
      }

      default:
        syntaxError("unknown if condition encountered",filename,stmt);
        return;
      }

      // do the jump
      bytecodes.add(new Bytecode.If(op, target));
    }
  }

  private void translate(Codes.IfIs c, Code.Block.Entry stmt, int freeSlot,
      HashMap<JvmConstant,Integer> constants, ArrayList<Bytecode> bytecodes) {

    // In this case, we're updating the type of a local variable. To
    // make this work, we must update the JVM type of that slot as well
    // using a checkcast.
    String exitLabel = freshLabel();
    String trueLabel = freshLabel();

    bytecodes.add(new Bytecode.Load(c.operand, convertType(c.type)));
    translateTypeTest(trueLabel, c.type, c.rightOperand, bytecodes, constants);

    Type gdiff = Type.intersect(c.type,Type.Negation(c.rightOperand));
    bytecodes.add(new Bytecode.Load(c.operand, convertType(c.type)));
    // now, add checkcast
    addReadConversion(gdiff,bytecodes);
    bytecodes.add(new Bytecode.Store(c.operand,convertType(gdiff)));
    bytecodes.add(new Bytecode.Goto(exitLabel));
    bytecodes.add(new Bytecode.Label(trueLabel));
    Type glb = Type.intersect(c.type, c.rightOperand);
    bytecodes.add(new Bytecode.Load(c.operand, convertType(c.type)));
    // now, add checkcast
    addReadConversion(glb,bytecodes);
    bytecodes.add(new Bytecode.Store(c.operand,convertType(glb)));
    bytecodes.add(new Bytecode.Goto(c.target));
    bytecodes.add(new Bytecode.Label(exitLabel));
  }

  // The purpose of this method is to translate a type test. We're testing to
  // see whether what's on the top of the stack (the value) is a subtype of
  // the type being tested.
  protected void translateTypeTest(String trueTarget, Type src, Type test,
      ArrayList<Bytecode> bytecodes, HashMap<JvmConstant,Integer> constants) {

    // First, try for the easy cases

    if (test instanceof Type.Null) {
      // Easy case
      bytecodes.add(new Bytecode.If(Bytecode.IfMode.NULL, trueTarget));
    } else if(test instanceof Type.Bool) {
      bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_BOOLEAN));
      bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, trueTarget));
    } else if(test instanceof Type.Char) {
      bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_CHARACTER));
      bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, trueTarget));
    } else if(test instanceof Type.Int) {
      bytecodes.add(new Bytecode.InstanceOf(WHILEYINT));
      bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, trueTarget));
    } else if(test instanceof Type.Real) {
      bytecodes.add(new Bytecode.InstanceOf(WHILEYRAT));
      bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, trueTarget));
    } else if(test instanceof Type.Strung) {
      bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_STRING));
      bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, trueTarget));

    } else {
      // Fall-back to an external (recursive) check
      Constant constant = Constant.V_TYPE(test);
      int id = JvmValue.get(constant,constants);
      String name = "constant$" + id;

      bytecodes.add(new Bytecode.GetField(owner, name, WHILEYTYPE, Bytecode.FieldMode.STATIC));

      JvmType.Function ftype = new JvmType.Function(T_BOOL,convertType(src),WHILEYTYPE);
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "instanceOf",
          ftype, Bytecode.InvokeMode.STATIC));
      bytecodes.add(new Bytecode.If(Bytecode.IfMode.NE, trueTarget));
    }
  }

  private void translate(Codes.Loop c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Label(c.target + "$head"));
  }

  protected void translate(Codes.LoopEnd end,
      int freeSlot, ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Goto(end.label + "$head"));
    bytecodes.add(new Bytecode.Label(end.label));
  }

  private int translate(Codes.ForAll c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    Type elementType = c.type.element();

    bytecodes.add(new Bytecode.Load(c.sourceOperand, convertType((Type) c.type)));
    JvmType.Function ftype = new JvmType.Function(JAVA_UTIL_ITERATOR,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYCOLLECTION, "iterator", ftype, Bytecode.InvokeMode.STATIC));
    ftype = new JvmType.Function(JAVA_UTIL_ITERATOR);
    bytecodes.add(new Bytecode.Store(freeSlot, JAVA_UTIL_ITERATOR));
    bytecodes.add(new Bytecode.Label(c.target + "$head"));
    ftype = new JvmType.Function(T_BOOL);
    bytecodes.add(new Bytecode.Load(freeSlot, JAVA_UTIL_ITERATOR));
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "hasNext", ftype,
        Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.If(Bytecode.IfMode.EQ, c.target));
    bytecodes.add(new Bytecode.Load(freeSlot, JAVA_UTIL_ITERATOR));
    ftype = new JvmType.Function(JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "next", ftype,
        Bytecode.InvokeMode.INTERFACE));
    addReadConversion(elementType, bytecodes);
    bytecodes.add(new Bytecode.Store(c.indexOperand, convertType(elementType)));

    // we need to increase the freeSlot, since we've allocated one slot to
    // hold the iterator.

    return freeSlot + 1;
  }

  private void translate(Codes.Goto c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Goto(c.target));
  }
  private void translate(Codes.Label c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Label(c.label));
  }

  private void translate(Codes.Debug c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(T_VOID,JAVA_LANG_STRING);
    bytecodes.add(new Bytecode.Load(c.operand, JAVA_LANG_STRING));
    bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "debug", ftype,
        Bytecode.InvokeMode.STATIC));
  }

  private void translate(Codes.AssertOrAssume c, Code.Block.Entry entry, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    // essentially a no-op for now
  }

  private void translate(Codes.Assign c, int freeSlot, ArrayList<Bytecode> bytecodes) {
    JvmType jt = convertType(c.type());
    bytecodes.add(new Bytecode.Load(c.operand(0), jt));
    addIncRefs(c.type(),bytecodes);
    bytecodes.add(new Bytecode.Store(c.target(), jt));
  }

  private void translate(Codes.Move c, int freeSlot, ArrayList<Bytecode> bytecodes) {
    JvmType jt = convertType(c.type());
    bytecodes.add(new Bytecode.Load(c.operand(0), jt));
    bytecodes.add(new Bytecode.Store(c.target(), jt));
  }

  private void translate(Codes.ListOperator c, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType leftType;
    JvmType rightType;

    switch(c.kind) {
    case APPEND:
      leftType = WHILEYLIST;
      rightType = WHILEYLIST;
      bytecodes.add(new Bytecode.Load(c.operand(0), leftType));
      bytecodes.add(new Bytecode.Load(c.operand(1), rightType));
      break;
    case LEFT_APPEND:
      leftType = WHILEYLIST;
      rightType = JAVA_LANG_OBJECT;
      bytecodes.add(new Bytecode.Load(c.operand(0), leftType));
      bytecodes.add(new Bytecode.Load(c.operand(1), convertType(c.type().element())));
      addWriteConversion(c.type().element(),bytecodes);
      break;
    case RIGHT_APPEND:
      leftType = JAVA_LANG_OBJECT;
      rightType = WHILEYLIST;
      bytecodes.add(new Bytecode.Load(c.operand(0), convertType(c.type().element())));
      addWriteConversion(c.type().element(),bytecodes);
      bytecodes.add(new Bytecode.Load(c.operand(1), rightType));
      break;
    default:
      internalFailure("unknown list operation",filename,stmt);
      return;
    }

    JvmType.Function ftype = new JvmType.Function(WHILEYLIST,leftType,rightType);
    bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "append", ftype,
        Bytecode.InvokeMode.STATIC));
    bytecodes.add(new Bytecode.Store(c.target(), WHILEYLIST));
  }

  private void translate(Codes.LengthOf c, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Load(c.operand(0), convertType((Type) c.type())));
    JvmType.Clazz ctype = JAVA_LANG_OBJECT;
    JvmType.Function ftype = new JvmType.Function(WHILEYINT, ctype);
    bytecodes.add(new Bytecode.Invoke(WHILEYCOLLECTION, "length", ftype,
        Bytecode.InvokeMode.STATIC));
    bytecodes.add(new Bytecode.Store(c.target(), WHILEYINT));
  }

  private void translate(Codes.SubList c, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Load(c.operands()[0], WHILEYLIST));
    bytecodes.add(new Bytecode.Load(c.operands()[1], WHILEYINT));
    bytecodes.add(new Bytecode.Load(c.operands()[2], WHILEYINT));

    JvmType.Function ftype = new JvmType.Function(WHILEYLIST, WHILEYLIST,
        WHILEYINT, WHILEYINT);
    bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "sublist", ftype,
        Bytecode.InvokeMode.STATIC));

    bytecodes.add(new Bytecode.Store(c.target(), WHILEYLIST));
  }

  private void translate(Codes.IndexOf c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYLIST));
    bytecodes.add(new Bytecode.Load(c.operand(1), convertType(c.type().key())));
    addWriteConversion(c.type().key(),bytecodes);
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
        JAVA_LANG_OBJECT, JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYCOLLECTION, "indexOf", ftype,
        Bytecode.InvokeMode.STATIC));
    addReadConversion(c.type().value(), bytecodes);

    bytecodes.add(new Bytecode.Store(c.target(),
        convertType(c.type().element())));
  }

  private void translate(Codes.Fail c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.New(JAVA_LANG_RUNTIMEEXCEPTION));
    bytecodes.add(new Bytecode.Dup(JAVA_LANG_RUNTIMEEXCEPTION));
    bytecodes.add(new Bytecode.LoadConst(c.message.value));
    JvmType.Function ftype = new JvmType.Function(T_VOID, JAVA_LANG_STRING);
    bytecodes.add(new Bytecode.Invoke(JAVA_LANG_RUNTIMEEXCEPTION, "<init>",
        ftype, Bytecode.InvokeMode.SPECIAL));
    bytecodes.add(new Bytecode.Throw());
  }

  private void translate(Codes.FieldLoad c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYRECORD));

    bytecodes.add(new Bytecode.LoadConst(c.field));
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,WHILEYRECORD,JAVA_LANG_STRING);
    bytecodes.add(new Bytecode.Invoke(WHILEYRECORD,"get",ftype,Bytecode.InvokeMode.STATIC));
    addReadConversion(c.fieldType(),bytecodes);

    bytecodes.add(new Bytecode.Store(c.target(), convertType(c.fieldType())));
  }

  private void translate(Codes.BinaryOperator c, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    JvmType type = convertType(c.type());
    JvmType.Function ftype = new JvmType.Function(type,type);

    // first, load operands
    switch(c.kind) {
      case ADD:
      case SUB:
      case MUL:
      case DIV:
      case REM:
      case BITWISEAND:
      case BITWISEOR:
      case BITWISEXOR:
        bytecodes.add(new Bytecode.Load(c.operand(0), type));
        bytecodes.add(new Bytecode.Load(c.operand(1), type));
        break;
      case LEFTSHIFT:
      case RIGHTSHIFT:
        bytecodes.add(new Bytecode.Load(c.operand(0), type));
        bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT));
        break;
      case RANGE:
        bytecodes.add(new Bytecode.Load(c.operand(0), WHILEYINT));
        bytecodes.add(new Bytecode.Load(c.operand(1), WHILEYINT));
        break;
    }

    // second, apply operation
    switch(c.kind) {
    case ADD:
      if(type instanceof JvmType.Char) {
        // Hack for #418
        bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.ADD, JvmTypes.T_INT));
      } else {
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz)type, "add", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      }
      break;
    case SUB:
      if(type instanceof JvmType.Char) {
        // Hack for #418
        bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.SUB, JvmTypes.T_INT));
      } else {
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz)type, "subtract", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      }
      break;
    case MUL:
      if(type instanceof JvmType.Char) {
        // Hack for #418
        bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.MUL, JvmTypes.T_INT));
      } else {
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz)type, "multiply", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      }
      break;
    case DIV:
      if(type instanceof JvmType.Char) {
        // Hack for #418
        bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.DIV, JvmTypes.T_INT));
      } else {
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz)type, "divide", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      }
      break;
    case REM:
      if(type instanceof JvmType.Char) {
        // Hack for #418
        bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.REM, JvmTypes.T_INT));
      } else {
        bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
            "remainder", ftype, Bytecode.InvokeMode.VIRTUAL));
      }
      break;
    case RANGE:
      ftype = new JvmType.Function(WHILEYLIST,WHILEYINT,WHILEYINT);
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,
          "range", ftype, Bytecode.InvokeMode.STATIC));
      break;
    case BITWISEAND:
      bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.AND,T_INT));
      break;
    case BITWISEOR:
      bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.OR,T_INT));
      break;
    case BITWISEXOR:
      bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.XOR,T_INT));
      break;
    case LEFTSHIFT:
      ftype = new JvmType.Function(type,type,WHILEYINT);
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,
          "leftshift", ftype, Bytecode.InvokeMode.STATIC));
      break;
    case RIGHTSHIFT:
      ftype = new JvmType.Function(type,type,WHILEYINT);
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,
          "rightshift", ftype, Bytecode.InvokeMode.STATIC));
      break;
    default:
      internalFailure("unknown binary expression encountered",filename,stmt);
    }

    bytecodes.add(new Bytecode.Store(c.target(), type));
  }

  private void translate(Codes.SetOperator c, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    JvmType leftType;
    JvmType rightType;

    // First, load operands
    switch(c.kind) {
      case UNION:
      case DIFFERENCE:
      case INTERSECTION:
        leftType = WHILEYSET;
        rightType = WHILEYSET;
        bytecodes.add(new Bytecode.Load(c.operand(0), leftType));
        bytecodes.add(new Bytecode.Load(c.operand(1), rightType));
        break;
      case LEFT_UNION:
      case LEFT_DIFFERENCE:
      case LEFT_INTERSECTION:
        leftType = WHILEYSET;
        rightType = JAVA_LANG_OBJECT;
        bytecodes.add(new Bytecode.Load(c.operand(0), leftType));
        bytecodes.add(new Bytecode.Load(c.operand(1), convertType(c.type().element())));
        addWriteConversion(c.type().element(),bytecodes);
        break;
      case RIGHT_UNION:
      case RIGHT_INTERSECTION:
        leftType = JAVA_LANG_OBJECT;
        rightType = WHILEYSET;
        bytecodes.add(new Bytecode.Load(c.operand(0), convertType(c.type().element())));
        addWriteConversion(c.type().element(),bytecodes);
        bytecodes.add(new Bytecode.Load(c.operand(1), rightType));
        break;
      default:
        internalFailure("Unknown set operation encountered: ",filename,stmt);
        return; // dead-code
    }

    JvmType.Function ftype= new JvmType.Function(WHILEYSET,leftType,rightType);

    // Second, select operation
    String operation;
    switch(c.kind) {
    case UNION:
    case LEFT_UNION:
    case RIGHT_UNION:
      operation = "union";
      break;
    case INTERSECTION:
    case LEFT_INTERSECTION:
    case RIGHT_INTERSECTION:
      operation = "intersect";
      break;
    case DIFFERENCE:
    case LEFT_DIFFERENCE:
      operation = "difference";
      break;
    default:
      internalFailure("Unknown set operation encountered: ",filename,stmt);
      return; // dead-code
    }

    bytecodes.add(new Bytecode.Invoke(WHILEYSET, operation, ftype,
        Bytecode.InvokeMode.STATIC));

    bytecodes.add(new Bytecode.Store(c.target(), WHILEYSET));
  }

  private void translate(Codes.StringOperator c, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType leftType;
    JvmType rightType;

    switch(c.kind) {
    case APPEND:
      leftType = JAVA_LANG_STRING;
      rightType = JAVA_LANG_STRING;
      break;
    case LEFT_APPEND:
      leftType = JAVA_LANG_STRING;
      rightType = T_CHAR;
      break;
    case RIGHT_APPEND:
      leftType = T_CHAR;
      rightType = JAVA_LANG_STRING;
      break;
    default:
      internalFailure("Unknown string operation encountered: ",filename,stmt);
      return; // dead-code
    }

    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_STRING,leftType,rightType);
    bytecodes.add(new Bytecode.Load(c.operand(0), leftType));
    bytecodes.add(new Bytecode.Load(c.operand(1), rightType));

    bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "append", ftype,
        Bytecode.InvokeMode.STATIC));

    bytecodes.add(new Bytecode.Store(c.target(), JAVA_LANG_STRING));
  }

  private void translate(Codes.SubString c, Code.Block.Entry stmt, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.Load(c.operands()[0], JAVA_LANG_STRING));
    bytecodes.add(new Bytecode.Load(c.operands()[1], WHILEYINT));
    bytecodes.add(new Bytecode.Load(c.operands()[2], WHILEYINT));

    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_STRING,JAVA_LANG_STRING,
        WHILEYINT, WHILEYINT);

    bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "substring", ftype,
        Bytecode.InvokeMode.STATIC));

    bytecodes.add(new Bytecode.Store(c.target(), JAVA_LANG_STRING));
  }

  private void translate(Codes.Invert c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType type = convertType(c.type());
    bytecodes.add(new Bytecode.Load(c.operand(0), type));
    bytecodes.add(new Bytecode.LoadConst(-1));
    bytecodes.add(new Bytecode.BinOp(Bytecode.BinOp.XOR, T_INT));
    bytecodes.add(new Bytecode.Store(c.target(), type));
  }

  private void translate(Codes.UnaryOperator c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType srcType = convertType(c.type());
    JvmType targetType = null;
    String name = null;
    switch(c.kind) {
      case NEG:
        targetType = srcType;
        name = "negate";
        break;
      case NUMERATOR:
        targetType = WHILEYINT;
        name = "numerator";
        break;
      case DENOMINATOR:
        targetType = WHILEYINT;
        name = "denominator";
        break;
    }
    JvmType.Function ftype = new JvmType.Function(targetType);
    bytecodes.add(new Bytecode.Load(c.operand(0), srcType));
    bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) srcType, name,
        ftype, Bytecode.InvokeMode.VIRTUAL));
    bytecodes.add(new Bytecode.Store(c.target(), targetType));
  }

  private void translate(Codes.NewObject c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType type = convertType(c.type());
    bytecodes.add(new Bytecode.New(WHILEYOBJECT));
    bytecodes.add(new Bytecode.Dup(WHILEYOBJECT));
    bytecodes.add(new Bytecode.Load(c.operand(0), convertType(c.type().element())));
    addWriteConversion(c.type().element(),bytecodes);
    JvmType.Function ftype = new JvmType.Function(T_VOID,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));
    bytecodes.add(new Bytecode.Store(c.target(), type));
  }

  private void translate(Codes.Dereference c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JvmType type = convertType(c.type());
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Load(c.operand(0), type));
    bytecodes.add(new Bytecode.Invoke(WHILEYOBJECT, "state", ftype,
        Bytecode.InvokeMode.VIRTUAL));
    // finally, we need to cast the object we got back appropriately.
    Type.Reference pt = (Type.Reference) c.type();
    addReadConversion(pt.element(), bytecodes);
    bytecodes.add(new Bytecode.Store(c.target(), convertType(c.type().element())));
  }

  protected void translate(Codes.NewList c, int freeSlot, ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.New(WHILEYLIST));
    bytecodes.add(new Bytecode.Dup(WHILEYLIST));
    bytecodes.add(new Bytecode.LoadConst(c.operands().length));
    JvmType.Function ftype = new JvmType.Function(T_VOID,T_INT);
    bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));

    ftype = new JvmType.Function(WHILEYLIST, WHILEYLIST, JAVA_LANG_OBJECT);
    for (int i = 0; i != c.operands().length; ++i) {
      bytecodes.add(new Bytecode.Load(c.operands()[i], convertType(c.type()
          .element())));
      addWriteConversion(c.type().element(), bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "internal_add",
          ftype, Bytecode.InvokeMode.STATIC));
    }

    bytecodes.add(new Bytecode.Store(c.target(), WHILEYLIST));
  }

  protected void translate(Codes.NewMap c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    construct(WHILEYMAP, freeSlot, bytecodes);
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
        JAVA_LANG_OBJECT, JAVA_LANG_OBJECT);
    JvmType keyType = convertType(c.type().key());
    JvmType valueType = convertType(c.type().value());

    for (int i = 0; i != c.operands().length; i=i+2) {
      bytecodes.add(new Bytecode.Dup(WHILEYMAP));
      bytecodes.add(new Bytecode.Load(c.operands()[i], keyType));
      addWriteConversion(c.type().key(), bytecodes);
      bytecodes.add(new Bytecode.Load(c.operands()[i + 1],valueType));
      addWriteConversion(c.type().value(), bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "put", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    }

    bytecodes.add(new Bytecode.Store(c.target(), WHILEYMAP));
  }

  private void translate(Codes.NewRecord code, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    construct(WHILEYRECORD, freeSlot, bytecodes);
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
        JAVA_LANG_OBJECT, JAVA_LANG_OBJECT);

    HashMap<String,Type> fields = code.type().fields();
    ArrayList<String> keys = new ArrayList<String>(fields.keySet());
    Collections.sort(keys);
    for (int i = 0; i != code.operands().length; i++) {
      int register = code.operands()[i];
      String key = keys.get(i);
      Type fieldType = fields.get(key);
      bytecodes.add(new Bytecode.Dup(WHILEYRECORD));
      bytecodes.add(new Bytecode.LoadConst(key));
      bytecodes.add(new Bytecode.Load(register, convertType(fieldType)));
      addWriteConversion(fieldType,bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYRECORD,"put",ftype,Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    }

    bytecodes.add(new Bytecode.Store(code.target(), WHILEYRECORD));
  }

  protected void translate(Codes.NewSet c, int freeSlot, ArrayList<Bytecode> bytecodes) {
    construct(WHILEYSET, freeSlot, bytecodes);
    JvmType.Function ftype = new JvmType.Function(WHILEYSET,
        WHILEYSET,JAVA_LANG_OBJECT);

    for(int i=0;i!=c.operands().length;++i) {
      bytecodes.add(new Bytecode.Load(c.operands()[i], convertType(c.type()
          .element())));
      addWriteConversion(c.type().element(),bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYSET,"internal_add",ftype,Bytecode.InvokeMode.STATIC));
    }

    bytecodes.add(new Bytecode.Store(c.target(), WHILEYSET));
  }

  protected void translate(Codes.NewTuple c, int freeSlot, ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.New(WHILEYTUPLE ));
    bytecodes.add(new Bytecode.Dup(WHILEYTUPLE ));
    bytecodes.add(new Bytecode.LoadConst(c.operands().length));
    JvmType.Function ftype = new JvmType.Function(T_VOID,T_INT);
    bytecodes.add(new Bytecode.Invoke(WHILEYTUPLE , "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));

    ftype = new JvmType.Function(WHILEYTUPLE , WHILEYTUPLE , JAVA_LANG_OBJECT);
    for (int i = 0; i != c.operands().length; ++i) {
      Type elementType = c.type().elements().get(i);
      bytecodes.add(new Bytecode.Load(c.operands()[i], convertType(elementType)));
      addWriteConversion(elementType, bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYTUPLE , "internal_add",
          ftype, Bytecode.InvokeMode.STATIC));
    }

    bytecodes.add(new Bytecode.Store(c.target(), WHILEYTUPLE));
  }

  private void translate(Codes.Lambda c, int freeSlot,
      ArrayList<ClassFile> lambdas,
      ArrayList<Bytecode> bytecodes) {

    // First, build and register lambda class which calls the given function
    // or method. This class will extend class wyjc.runtime.WyLambda.
    int lambda_id = lambdas.size();
    lambdas.add(buildLambda(c.name, c.type(), lambda_id));

    // Second, create and duplicate new lambda object. This will then stay
    // on the stack (whilst the parameters are constructed) until the
    // object's constructor is called.
    JvmType.Clazz lambdaClassType = new JvmType.Clazz(owner.pkg(), owner
        .lastComponent().first(), Integer.toString(lambda_id));

    bytecodes.add(new Bytecode.New(lambdaClassType));
    bytecodes.add(new Bytecode.Dup(lambdaClassType));

    // Third, construct the parameter for lambda class constructor. In the
    // case that a binding is given for this lambda, then we need to supply
    // this as an argument to the lambda class constructor; otherwise, we
    // just pass null. To do this, we first check whether or not a binding
    // is required.
    boolean hasBinding = false;

    for(int operand : c.operands()) {
      if(operand != Codes.NULL_REG) {
        hasBinding = true;
        break;
      }
    }

    if(hasBinding) {
      // Yes, binding is required.
      bytecodes.add(new Bytecode.LoadConst(c.operands().length));
      bytecodes.add(new Bytecode.New(JAVA_LANG_OBJECT_ARRAY));

      for (int i = 0; i != c.operands().length; ++i) {
        bytecodes.add(new Bytecode.Dup(JAVA_LANG_OBJECT_ARRAY));
        bytecodes.add(new Bytecode.LoadConst(i));
        int operand = c.operands()[i];

        if (operand != Codes.NULL_REG) {
          Type pt = c.type().params().get(i);
          bytecodes.add(new Bytecode.Load(operand, convertType(pt)));
          addWriteConversion(pt, bytecodes);
        } else {
          bytecodes.add(new Bytecode.LoadConst(null));
        }
        bytecodes.add(new Bytecode.ArrayStore(JAVA_LANG_OBJECT_ARRAY));
      }
    } else {
      // No, binding not required.
      bytecodes.add(new Bytecode.LoadConst(null));
    }

    // Fifth, invoke lambda class constructor
    JvmType.Function ftype = new JvmType.Function(T_VOID, JAVA_LANG_OBJECT_ARRAY);
    bytecodes.add(new Bytecode.Invoke(lambdaClassType, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));

    // Sixth, assign newly created lambda object to target register
    JvmType.Clazz clazz = (JvmType.Clazz) convertType(c.type());
    bytecodes.add(new Bytecode.Store(c.target(), clazz));
  }

  private void translate(Codes.Invoke c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    for (int i = 0; i != c.operands().length; ++i) {
      int register = c.operands()[i];
      JvmType parameterType = convertType(c.type().params().get(i));
      bytecodes.add(new Bytecode.Load(register, parameterType));
    }

    Path.ID mid = c.name.module();
    String mangled = nameMangle(c.name.name(), c.type());
    JvmType.Clazz owner = new JvmType.Clazz(mid.parent().toString()
        .replace('/', '.'), mid.last());
    JvmType.Function type = convertFunType(c.type());
    bytecodes
        .add(new Bytecode.Invoke(owner, mangled, type, Bytecode.InvokeMode.STATIC));

    // now, handle the case of an invoke which returns a value that should
    // be discarded.
    if(c.target() != Codes.NULL_REG){
      bytecodes.add(new Bytecode.Store(c.target(), convertType(c.type().ret())));
    } else if(c.target() == Codes.NULL_REG && c.type().ret() != Type.T_VOID) {
      bytecodes.add(new Bytecode.Pop(convertType(c.type().ret())));
    }
  }

  private void translate(Codes.IndirectInvoke c, int freeSlot,
      ArrayList<Bytecode> bytecodes) {

    Type.FunctionOrMethod ft = c.type();
    JvmType.Clazz owner = (JvmType.Clazz) convertType(ft);
    bytecodes.add(new Bytecode.Load(c.reference(),convertType(ft)));
    bytecodes.add(new Bytecode.LoadConst(ft.params().size()));
    bytecodes.add(new Bytecode.New(JAVA_LANG_OBJECT_ARRAY));

    int[] parameters = c.parameters();
    for (int i = 0; i != parameters.length; ++i) {
      int register = parameters[i];
      Type pt = c.type().params().get(i);
      JvmType jpt = convertType(pt);
      bytecodes.add(new Bytecode.Dup(JAVA_LANG_OBJECT_ARRAY));
      bytecodes.add(new Bytecode.LoadConst(i));
      bytecodes.add(new Bytecode.Load(register, jpt));
      addWriteConversion(pt,bytecodes);
      bytecodes.add(new Bytecode.ArrayStore(JAVA_LANG_OBJECT_ARRAY));
    }

    JvmType.Function type = new JvmType.Function(JAVA_LANG_OBJECT,JAVA_LANG_OBJECT_ARRAY);

    bytecodes.add(new Bytecode.Invoke(owner, "call", type,
        Bytecode.InvokeMode.VIRTUAL));

    // now, handle the case of an invoke which returns a value that should
    // be discarded.
    if (c.target() != Codes.NULL_REG) {
      addReadConversion(ft.ret(),bytecodes);
      bytecodes.add(new Bytecode.Store(c.target(),
          convertType(c.type().ret())));
    } else if (c.target() == Codes.NULL_REG) {
      bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    }
  }

  private void translate(Constant v, int freeSlot,
      ArrayList<ClassFile> lambdas,
      ArrayList<Bytecode> bytecodes) {
    if(v instanceof Constant.Null) {
      translate((Constant.Null)v,freeSlot,bytecodes);
    } else if(v instanceof Constant.Bool) {
      translate((Constant.Bool)v,freeSlot,bytecodes);
    } else if(v instanceof Constant.Byte) {
      translate((Constant.Byte)v,freeSlot,bytecodes);
    } else if(v instanceof Constant.Char) {
      translate((Constant.Char)v,freeSlot,bytecodes);
    } else if(v instanceof Constant.Integer) {
      translate((Constant.Integer)v,freeSlot,bytecodes);
    } else if(v instanceof Constant.Type) {
      translate((Constant.Type)v,freeSlot,bytecodes);
    } else if(v instanceof Constant.Decimal) {
      translate((Constant.Decimal)v,freeSlot,bytecodes);
    } else if(v instanceof Constant.Strung) {
      translate((Constant.Strung)v,freeSlot,bytecodes);
    } else if(v instanceof Constant.Set) {
      translate((Constant.Set)v,freeSlot,lambdas,bytecodes);
    } else if(v instanceof Constant.List) {
      translate((Constant.List)v,freeSlot,lambdas,bytecodes);
    } else if(v instanceof Constant.Record) {
      translate((Constant.Record)v,freeSlot,lambdas,bytecodes);
    } else if(v instanceof Constant.Map) {
      translate((Constant.Map)v,freeSlot,lambdas,bytecodes);
    } else if(v instanceof Constant.Tuple) {
      translate((Constant.Tuple)v,freeSlot,lambdas,bytecodes);
    } else if(v instanceof Constant.Lambda) {
      translate((Constant.Lambda)v,freeSlot,lambdas,bytecodes);
    } else {
      throw new IllegalArgumentException("unknown value encountered:" + v);
    }
  }

  protected void translate(Constant.Null e, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.LoadConst(null));
  }

  protected void translate(Constant.Bool e, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    if (e.value) {
      bytecodes.add(new Bytecode.LoadConst(1));
    } else {
      bytecodes.add(new Bytecode.LoadConst(0));
    }
  }

  protected void translate(Constant.Type e, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    JavaIdentifierOutputStream jout = new JavaIdentifierOutputStream();
    BinaryOutputStream bout = new BinaryOutputStream(jout);
    Type.BinaryWriter writer = new Type.BinaryWriter(bout);
    try {
      writer.write(e.type);
      writer.close();
    } catch(IOException ex) {
      throw new RuntimeException(ex.getMessage(),ex);
    }

    bytecodes.add(new Bytecode.LoadConst(jout.toString()));
    JvmType.Function ftype = new JvmType.Function(WHILEYTYPE,
        JAVA_LANG_STRING);
    bytecodes.add(new Bytecode.Invoke(WHILEYTYPE, "valueOf", ftype,
        Bytecode.InvokeMode.STATIC));
  }

  protected void translate(Constant.Byte e, int freeSlot, ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.LoadConst(e.value));
  }

  protected void translate(Constant.Char e, int freeSlot, ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.LoadConst(e.value));
  }

  protected void translate(Constant.Integer e, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    BigInteger num = e.value;

    if(num.bitLength() < 32) {
      bytecodes.add(new Bytecode.LoadConst(num.intValue()));
      bytecodes.add(new Bytecode.Conversion(T_INT,T_LONG));
      JvmType.Function ftype = new JvmType.Function(WHILEYINT,T_LONG);
      bytecodes.add(new Bytecode.Invoke(WHILEYINT, "valueOf", ftype,
          Bytecode.InvokeMode.STATIC));
    } else if(num.bitLength() < 64) {
      bytecodes.add(new Bytecode.LoadConst(num.longValue()));
      JvmType.Function ftype = new JvmType.Function(WHILEYINT,T_LONG);
      bytecodes.add(new Bytecode.Invoke(WHILEYINT, "valueOf", ftype,
          Bytecode.InvokeMode.STATIC));
    } else {
      // in this context, we need to use a byte array to construct the
      // integer object.
      byte[] bytes = num.toByteArray();
      JvmType.Array bat = new JvmType.Array(JvmTypes.T_BYTE);

      bytecodes.add(new Bytecode.New(WHILEYINT));
      bytecodes.add(new Bytecode.Dup(WHILEYINT));
      bytecodes.add(new Bytecode.LoadConst(bytes.length));
      bytecodes.add(new Bytecode.New(bat));
      for(int i=0;i!=bytes.length;++i) {
        bytecodes.add(new Bytecode.Dup(bat));
        bytecodes.add(new Bytecode.LoadConst(i));
        bytecodes.add(new Bytecode.LoadConst(bytes[i]));
        bytecodes.add(new Bytecode.ArrayStore(bat));
      }

      JvmType.Function ftype = new JvmType.Function(T_VOID,bat);
      bytecodes.add(new Bytecode.Invoke(WHILEYINT, "<init>", ftype,
          Bytecode.InvokeMode.SPECIAL));
    }

  }

  protected void translate(Constant.Decimal e, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    BigRational rat = new BigRational(e.value);
    BigInteger den = rat.denominator();
    BigInteger num = rat.numerator();
    if(rat.isInteger()) {
      // this
      if(num.bitLength() < 32) {
        bytecodes.add(new Bytecode.LoadConst(num.intValue()));
        JvmType.Function ftype = new JvmType.Function(WHILEYRAT,T_INT);
        bytecodes.add(new Bytecode.Invoke(WHILEYRAT, "valueOf", ftype,
            Bytecode.InvokeMode.STATIC));
      } else if(num.bitLength() < 64) {
        bytecodes.add(new Bytecode.LoadConst(num.longValue()));
        JvmType.Function ftype = new JvmType.Function(WHILEYRAT,T_LONG);
        bytecodes.add(new Bytecode.Invoke(WHILEYRAT, "valueOf", ftype,
            Bytecode.InvokeMode.STATIC));
      } else {
        // in this context, we need to use a byte array to construct the
        // integer object.
        byte[] bytes = num.toByteArray();
        JvmType.Array bat = new JvmType.Array(JvmTypes.T_BYTE);

        bytecodes.add(new Bytecode.New(WHILEYRAT));
        bytecodes.add(new Bytecode.Dup(WHILEYRAT));
        bytecodes.add(new Bytecode.LoadConst(bytes.length));
        bytecodes.add(new Bytecode.New(bat));
        for(int i=0;i!=bytes.length;++i) {
          bytecodes.add(new Bytecode.Dup(bat));
          bytecodes.add(new Bytecode.LoadConst(i));
          bytecodes.add(new Bytecode.LoadConst(bytes[i]));
          bytecodes.add(new Bytecode.ArrayStore(bat));
        }

        JvmType.Function ftype = new JvmType.Function(T_VOID,bat);
        bytecodes.add(new Bytecode.Invoke(WHILEYRAT, "<init>", ftype,
            Bytecode.InvokeMode.SPECIAL));
      }
    } else if(num.bitLength() < 32 && den.bitLength() < 32) {
      bytecodes.add(new Bytecode.LoadConst(num.intValue()));
      bytecodes.add(new Bytecode.LoadConst(den.intValue()));
      JvmType.Function ftype = new JvmType.Function(WHILEYRAT,T_INT,T_INT);
      bytecodes.add(new Bytecode.Invoke(WHILEYRAT, "valueOf", ftype,
          Bytecode.InvokeMode.STATIC));
    } else if(num.bitLength() < 64 && den.bitLength() < 64) {
      bytecodes.add(new Bytecode.LoadConst(num.longValue()));
      bytecodes.add(new Bytecode.LoadConst(den.longValue()));
      JvmType.Function ftype = new JvmType.Function(WHILEYRAT,T_LONG,T_LONG);
      bytecodes.add(new Bytecode.Invoke(WHILEYRAT, "valueOf", ftype,
          Bytecode.InvokeMode.STATIC));
    } else {
      // First, do numerator bytes
      byte[] bytes = num.toByteArray();
      JvmType.Array bat = new JvmType.Array(JvmTypes.T_BYTE);

      bytecodes.add(new Bytecode.New(WHILEYRAT));
      bytecodes.add(new Bytecode.Dup(WHILEYRAT));
      bytecodes.add(new Bytecode.LoadConst(bytes.length));
      bytecodes.add(new Bytecode.New(bat));
      for(int i=0;i!=bytes.length;++i) {
        bytecodes.add(new Bytecode.Dup(bat));
        bytecodes.add(new Bytecode.LoadConst(i));
        bytecodes.add(new Bytecode.LoadConst(bytes[i]));
        bytecodes.add(new Bytecode.ArrayStore(bat));
      }

      // Second, do denominator bytes
      bytes = den.toByteArray();
      bytecodes.add(new Bytecode.LoadConst(bytes.length));
      bytecodes.add(new Bytecode.New(bat));
      for(int i=0;i!=bytes.length;++i) {
        bytecodes.add(new Bytecode.Dup(bat));
        bytecodes.add(new Bytecode.LoadConst(i));
        bytecodes.add(new Bytecode.LoadConst(bytes[i]));
        bytecodes.add(new Bytecode.ArrayStore(bat));
      }

      // Finally, construct BigRational object
      JvmType.Function ftype = new JvmType.Function(T_VOID,bat,bat);
      bytecodes.add(new Bytecode.Invoke(WHILEYRAT, "<init>", ftype,
          Bytecode.InvokeMode.SPECIAL));
    }
  }

  protected void translate(Constant.Strung e, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.LoadConst(e.value));
  }

  protected void translate(Constant.Set lv, int freeSlot,
      ArrayList<ClassFile> lambdas,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.New(WHILEYSET));
    bytecodes.add(new Bytecode.Dup(WHILEYSET));
    JvmType.Function ftype = new JvmType.Function(T_VOID);
    bytecodes.add(new Bytecode.Invoke(WHILEYSET, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));

    ftype = new JvmType.Function(T_BOOL, JAVA_LANG_OBJECT);
    for (Constant e : lv.values) {
      bytecodes.add(new Bytecode.Dup(WHILEYSET));
      translate(e, freeSlot, lambdas, bytecodes);
      addWriteConversion(e.type(), bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYSET, "add", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(T_BOOL));
    }
  }

  protected void translate(Constant.List lv, int freeSlot,
      ArrayList<ClassFile> lambdas,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.New(WHILEYLIST));
    bytecodes.add(new Bytecode.Dup(WHILEYLIST));
    bytecodes.add(new Bytecode.LoadConst(lv.values.size()));
    JvmType.Function ftype = new JvmType.Function(T_VOID,T_INT);
    bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));

    ftype = new JvmType.Function(T_BOOL, JAVA_LANG_OBJECT);
    for (Constant e : lv.values) {
      bytecodes.add(new Bytecode.Dup(WHILEYLIST));
      translate(e, freeSlot, lambdas, bytecodes);
      addWriteConversion(e.type(), bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "add", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(T_BOOL));
    }
  }

  protected void translate(Constant.Tuple lv, int freeSlot,
      ArrayList<ClassFile> lambdas,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.New(WHILEYTUPLE));
    bytecodes.add(new Bytecode.Dup(WHILEYTUPLE));
    bytecodes.add(new Bytecode.LoadConst(lv.values.size()));
    JvmType.Function ftype = new JvmType.Function(T_VOID,T_INT);
    bytecodes.add(new Bytecode.Invoke(WHILEYTUPLE, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));

    ftype = new JvmType.Function(T_BOOL, JAVA_LANG_OBJECT);
    for (Constant e : lv.values) {
      bytecodes.add(new Bytecode.Dup(WHILEYTUPLE));
      translate(e, freeSlot, lambdas, bytecodes);
      addWriteConversion(e.type(), bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYTUPLE, "add", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(T_BOOL));
    }
  }

  protected void translate(Constant.Record expr, int freeSlot,
      ArrayList<ClassFile> lambdas,
      ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
        JAVA_LANG_OBJECT, JAVA_LANG_OBJECT);
    construct(WHILEYRECORD, freeSlot, bytecodes);
    for (Map.Entry<String, Constant> e : expr.values.entrySet()) {
      Type et = e.getValue().type();
      bytecodes.add(new Bytecode.Dup(WHILEYRECORD));
      bytecodes.add(new Bytecode.LoadConst(e.getKey()));
      translate(e.getValue(), freeSlot, lambdas, bytecodes);
      addWriteConversion(et, bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYRECORD, "put", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    }
  }

  protected void translate(Constant.Map expr, int freeSlot,
      ArrayList<ClassFile> lambdas,
      ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
        JAVA_LANG_OBJECT, JAVA_LANG_OBJECT);

    construct(WHILEYMAP, freeSlot, bytecodes);

    for (Map.Entry<Constant, Constant> e : expr.values.entrySet()) {
      Type kt = e.getKey().type();
      Type vt = e.getValue().type();
      bytecodes.add(new Bytecode.Dup(WHILEYMAP));
      translate(e.getKey(), freeSlot, lambdas, bytecodes);
      addWriteConversion(kt, bytecodes);
      translate(e.getValue(), freeSlot, lambdas, bytecodes);
      addWriteConversion(vt, bytecodes);
      bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "put", ftype,
          Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    }
  }

  protected void translate(Constant.Lambda c, int freeSlot,
      ArrayList<ClassFile> lambdas,
      ArrayList<Bytecode> bytecodes) {

    // First, build and register lambda class which calls the given function
    // or method. This class will extend class wyjc.runtime.WyLambda.
    int lambda_id = lambdas.size();
    lambdas.add(buildLambda(c.name, c.type, lambda_id));

    // Second, create and duplicate new lambda object. This will then stay
    // on the stack (whilst the parameters are constructed) until the
    // object's constructor is called.
    JvmType.Clazz lambdaClassType = new JvmType.Clazz(owner.pkg(), owner
        .lastComponent().first(), Integer.toString(lambda_id));

    bytecodes.add(new Bytecode.New(lambdaClassType));
    bytecodes.add(new Bytecode.Dup(lambdaClassType));

    // Third, invoke lambda class constructor
    JvmType.Function ftype = new JvmType.Function(T_VOID,
        JAVA_LANG_OBJECT_ARRAY);
    bytecodes.add(new Bytecode.LoadConst(null));
    bytecodes.add(new Bytecode.Invoke(lambdaClassType, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));
  }

  /**
   * Construct a class which implements a lambda expression. This must be a
   * subtype of wyjc.runtime.WyLambda and must call the given function, whilst
   * decoding and passing through the appropriate parameters.
   *
   * @param name
   *            Name of function or method which this lambda should invoke.
   * @param type
   *            Type of function or method which this lambda should invoke.
   * @return
   */
  protected ClassFile buildLambda(NameID name, Type.FunctionOrMethod type,
      int id) {
    // === (1) Determine the fully qualified type of the lambda class ===

    // start with fully qualified type of this class.
    JvmType.Clazz lambdaClassType = new JvmType.Clazz(owner.pkg(), owner
        .lastComponent().first(), Integer.toString(id));

    // === (2) Construct an empty class ===
    ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
    modifiers.add(Modifier.ACC_PUBLIC);
    modifiers.add(Modifier.ACC_FINAL);
    ClassFile cf = new ClassFile(CLASS_VERSION, lambdaClassType, WHILEYLAMBDA,
        new ArrayList<JvmType.Clazz>(), modifiers);

    // === (3) Add constructor ===
    modifiers = new ArrayList<Modifier>();
    modifiers.add(Modifier.ACC_PUBLIC);
    JvmType.Function constructorType = new JvmType.Function(JvmTypes.T_VOID,JAVA_LANG_OBJECT_ARRAY);
    // Create constructor method
    ClassFile.Method constructor = new ClassFile.Method("<init>", constructorType, modifiers);
    cf.methods().add(constructor);
    // Create body of constructor
    ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();
    bytecodes.add(new Bytecode.Load(0, lambdaClassType));
    bytecodes.add(new Bytecode.Load(1, JAVA_LANG_OBJECT_ARRAY));
    bytecodes.add(new Bytecode.Invoke(WHILEYLAMBDA, "<init>",
        constructorType, Bytecode.InvokeMode.SPECIAL));
    bytecodes.add(new Bytecode.Return(null));
    // Add code attribute to constructor
    jasm.attributes.Code code = new jasm.attributes.Code(bytecodes,new ArrayList<Handler>(),constructor);
    constructor.attributes().add(code);

    // === (4) Add implementation of WyLambda.call(Object[]) ===
    modifiers = new ArrayList<Modifier>();
    modifiers.add(Modifier.ACC_PUBLIC);
    modifiers.add(Modifier.ACC_FINAL);
    JvmType.Function callFnType = new JvmType.Function(JvmTypes.JAVA_LANG_OBJECT,JAVA_LANG_OBJECT_ARRAY);
    // Create constructor method
    ClassFile.Method callFn = new ClassFile.Method("call", callFnType, modifiers);
    cf.methods().add(callFn);
    // Create body of call method
    bytecodes = new ArrayList<Bytecode>();
    // Call WyFunction.bindParameters()
    bytecodes.add(new Bytecode.Load(0,lambdaClassType));
    bytecodes.add(new Bytecode.Load(1,JAVA_LANG_OBJECT_ARRAY));
    bytecodes.add(new Bytecode.Invoke(WHILEYLAMBDA, "bindParameters",
        new JvmType.Function(JAVA_LANG_OBJECT_ARRAY,
            JAVA_LANG_OBJECT_ARRAY), Bytecode.InvokeMode.VIRTUAL));
    bytecodes.add(new Bytecode.Store(1,JAVA_LANG_OBJECT_ARRAY));
    // Load parameters onto stack
    List<Type> type_params = type.params();
    for (int i = 0; i != type_params.size(); ++i) {
      bytecodes.add(new Bytecode.Load(1,JAVA_LANG_OBJECT_ARRAY));
      bytecodes.add(new Bytecode.LoadConst(i));
      bytecodes.add(new Bytecode.ArrayLoad(JAVA_LANG_OBJECT_ARRAY));
      addReadConversion(type_params.get(i),bytecodes);
    }

    Path.ID mid = name.module();
    String mangled = nameMangle(name.name(), type);
    JvmType.Clazz owner = new JvmType.Clazz(mid.parent().toString()
        .replace('/', '.'), mid.last());
    JvmType.Function fnType = convertFunType(type);
    bytecodes
        .add(new Bytecode.Invoke(owner, mangled, fnType, Bytecode.InvokeMode.STATIC));
    if(type.ret() instanceof Type.Void) {
      // Called function doesn't return anything, but we have to.
      // Therefore, push on dummy null value.
      bytecodes.add(new Bytecode.LoadConst(null));
    } else {
      addWriteConversion(type.ret(),bytecodes);
    }

    bytecodes.add(new Bytecode.Return(JAVA_LANG_OBJECT));

    // Add code attribute to call method
    code = new jasm.attributes.Code(bytecodes,new ArrayList<Handler>(),callFn);
    callFn.attributes().add(code);

    // Done
    return cf;
  }

  protected void addCoercion(Type from, Type to, int freeSlot,
      HashMap<JvmConstant, Integer> constants, ArrayList<Bytecode> bytecodes) {

    // First, deal with coercions which require a change of representation
    // when going into a union.  For example, bool must => Boolean.
    if (!(to instanceof Type.Bool) && from instanceof Type.Bool) {
      // this is either going into a union type, or the any type
      buildCoercion((Type.Bool) from, to, freeSlot, bytecodes);
    } else if(from == Type.T_BYTE) {
      buildCoercion((Type.Byte)from, to, freeSlot,bytecodes);
    } else if(from == Type.T_CHAR) {
      buildCoercion((Type.Char)from, to, freeSlot,bytecodes);
    } else if (Type.intersect(from, to).equals(from)) {
      // do nothing!
      // (note, need to check this after primitive types to avoid risk of
      // missing coercion to any)
    } else  if(from == Type.T_INT) {
      buildCoercion((Type.Int)from, to, freeSlot,bytecodes);
    } else if(from == Type.T_STRING && to instanceof Type.List) {
      buildCoercion((Type.Strung)from, (Type.List) to, freeSlot,bytecodes);
    } else if(from == Type.T_STRING && to instanceof Type.Set) {
      buildCoercion((Type.Strung)from, (Type.Set) to, freeSlot,bytecodes);
    } else {
      // ok, it's a harder case so we use an explicit coercion function
      int id = JvmCoercion.get(from,to,constants);
      String name = "coercion$" + id;
      JvmType.Function ft = new JvmType.Function(convertType(to), convertType(from));
      bytecodes.add(new Bytecode.Invoke(owner, name, ft, Bytecode.InvokeMode.STATIC));
    }
  }

  private void buildCoercion(Type.Bool fromType, Type toType,
      int freeSlot, ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_BOOLEAN,T_BOOL);
    bytecodes.add(new Bytecode.Invoke(JAVA_LANG_BOOLEAN,"valueOf",ftype,Bytecode.InvokeMode.STATIC));
    // done deal!
  }

  private void buildCoercion(Type.Byte fromType, Type toType,
      int freeSlot, ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(JAVA_LANG_BYTE,T_BYTE);
    bytecodes.add(new Bytecode.Invoke(JAVA_LANG_BYTE,"valueOf",ftype,Bytecode.InvokeMode.STATIC));
    // done deal!
  }

  private void buildCoercion(Type.Int fromType, Type toType,
      int freeSlot, ArrayList<Bytecode> bytecodes) {
    Type glb = Type.intersect(Type.T_REAL, toType);
    if(glb == Type.T_REAL) {
      // coercion required!
      JvmType.Function ftype = new JvmType.Function(WHILEYRAT,WHILEYINT);
      bytecodes.add(new Bytecode.Invoke(WHILEYRAT,"valueOf",ftype,Bytecode.InvokeMode.STATIC));
    } else {
      // must be => char
      JvmType.Function ftype = new JvmType.Function(T_INT);
      bytecodes.add(new Bytecode.Invoke(WHILEYINT,"intValue",ftype,Bytecode.InvokeMode.VIRTUAL));
    }
  }

  private void buildCoercion(Type.Char fromType, Type toType,
      int freeSlot, ArrayList<Bytecode> bytecodes) {
    if(!Type.isSubtype(toType,fromType)) {
      if(toType == Type.T_REAL) {
        // coercion required!
        JvmType.Function ftype = new JvmType.Function(WHILEYRAT,T_INT);
        bytecodes.add(new Bytecode.Invoke(WHILEYRAT,"valueOf",ftype,Bytecode.InvokeMode.STATIC));
      } else {
        bytecodes.add(new Bytecode.Conversion(T_INT, T_LONG));
        JvmType.Function ftype = new JvmType.Function(WHILEYINT,T_LONG);
        bytecodes.add(new Bytecode.Invoke(WHILEYINT,"valueOf",ftype,Bytecode.InvokeMode.STATIC));
      }
    } else {
      JvmType.Function ftype = new JvmType.Function(JAVA_LANG_CHARACTER,T_CHAR);
      bytecodes.add(new Bytecode.Invoke(JAVA_LANG_CHARACTER,"valueOf",ftype,Bytecode.InvokeMode.STATIC));
    }
  }

  private void buildCoercion(Type.Strung fromType, Type.List toType,
      int freeSlot, ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(WHILEYLIST,JAVA_LANG_STRING);

    if(toType.element() == Type.T_CHAR) {
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,"str2cl",ftype,Bytecode.InvokeMode.STATIC));
    } else {
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,"str2il",ftype,Bytecode.InvokeMode.STATIC));
    }
  }

  private void buildCoercion(Type.Strung fromType, Type.Set toType,
      int freeSlot, ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(WHILEYSET,JAVA_LANG_STRING);

    if(toType.element() == Type.T_CHAR) {
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,"str2cs",ftype,Bytecode.InvokeMode.STATIC));
    } else {
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,"str2is",ftype,Bytecode.InvokeMode.STATIC));
    }
  }

  /**
   * The build coercion method constructs a static final private method which
   * accepts a value of type "from", and coerces it into a value of type "to".
   *
   * @param to
   * @param from
   *
   */
  protected void buildCoercion(Type from, Type to, int id,
      HashMap<JvmConstant, Integer> constants, ClassFile cf) {
    ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();

    int freeSlot = 1;
    bytecodes.add(new Bytecode.Load(0,convertType(from)));
    buildCoercion(from,to,freeSlot,constants,bytecodes);
    bytecodes.add(new Bytecode.Return(convertType(to)));

    ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
    modifiers.add(Modifier.ACC_PRIVATE);
    modifiers.add(Modifier.ACC_STATIC);
    modifiers.add(Modifier.ACC_SYNTHETIC);
    JvmType.Function ftype = new JvmType.Function(convertType(to),convertType(from));
    String name = "coercion$" + id;
    ClassFile.Method method = new ClassFile.Method(name, ftype, modifiers);
    cf.methods().add(method);
    jasm.attributes.Code code = new jasm.attributes.Code(bytecodes,new ArrayList(),method);
    method.attributes().add(code);
  }

  protected void buildCoercion(Type from, Type to, int freeSlot,
      HashMap<JvmConstant, Integer> constants, ArrayList<Bytecode> bytecodes) {

    // Second, case analysis on the various kinds of coercion
    if(from instanceof Type.Tuple && to instanceof Type.Tuple) {
      buildCoercion((Type.Tuple) from, (Type.Tuple) to, freeSlot, constants, bytecodes);
    } else if(from instanceof Type.Reference && to instanceof Type.Reference) {
      // TODO
    } else if(from instanceof Type.Set && to instanceof Type.Set) {
      buildCoercion((Type.Set) from, (Type.Set) to, freeSlot, constants, bytecodes);
    } else if(from instanceof Type.Set && to instanceof Type.Map) {
      buildCoercion((Type.Set) from, (Type.Map) to, freeSlot, constants, bytecodes);
    } else if(from instanceof Type.List && to instanceof Type.Set) {
      buildCoercion((Type.List) from, (Type.Set) to, freeSlot, constants, bytecodes);
    } else if(from instanceof Type.Map && to instanceof Type.Map) {
      buildCoercion((Type.Map) from, (Type.Map) to, freeSlot, constants, bytecodes);
    } else if(from instanceof Type.List && to instanceof Type.Map) {
      buildCoercion((Type.List) from, (Type.Map) to, freeSlot, constants, bytecodes);
    } else if(from instanceof Type.List && to instanceof Type.List) {
      buildCoercion((Type.List) from, (Type.List) to, freeSlot, constants, bytecodes);
    } else if(to instanceof Type.Record && from instanceof Type.Record) {
      buildCoercion((Type.Record) from, (Type.Record) to, freeSlot, constants, bytecodes);
    } else if(to instanceof Type.Function && from instanceof Type.Function) {
      // TODO
    } else if(from instanceof Type.Negation || to instanceof Type.Negation) {
      // no need to do anything, since convertType on a negation returns java/lang/Object
    } else if(from instanceof Type.Union) {
      buildCoercion((Type.Union) from, to, freeSlot, constants, bytecodes);
    } else if(to instanceof Type.Union) {
      buildCoercion(from, (Type.Union) to, freeSlot, constants, bytecodes);
    } else {
      throw new RuntimeException("invalid coercion encountered: " + from + " => " + to);
    }
  }

  protected void buildCoercion(Type.Tuple fromType, Type.Tuple toType,
      int freeSlot, HashMap<JvmConstant, Integer> constants,
      ArrayList<Bytecode> bytecodes) {
    int oldSlot = freeSlot++;
    int newSlot = freeSlot++;
    bytecodes.add(new Bytecode.Store(oldSlot,WHILEYTUPLE));
    construct(WHILEYTUPLE,freeSlot,bytecodes);
    bytecodes.add(new Bytecode.Store(newSlot,WHILEYTUPLE));
    List<Type> from_elements = fromType.elements();
    List<Type> to_elements = toType.elements();
    for(int i=0;i!=to_elements.size();++i) {
      Type from = from_elements.get(i);
      Type to = to_elements.get(i);
      bytecodes.add(new Bytecode.Load(newSlot,WHILEYTUPLE));
      bytecodes.add(new Bytecode.Load(oldSlot,WHILEYTUPLE));
      bytecodes.add(new Bytecode.LoadConst(i));
      JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,T_INT);
      bytecodes.add(new Bytecode.Invoke(WHILEYTUPLE,"get",ftype,Bytecode.InvokeMode.VIRTUAL));
      addReadConversion(from,bytecodes);
      // now perform recursive conversion
      addCoercion(from,to,freeSlot,constants,bytecodes);
      ftype = new JvmType.Function(T_BOOL,JAVA_LANG_OBJECT);
      bytecodes.add(new Bytecode.Invoke(WHILEYTUPLE,"add",ftype,Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(T_BOOL));
    }
    bytecodes.add(new Bytecode.Load(newSlot,WHILEYTUPLE));
  }


  protected void buildCoercion(Type.List fromType, Type.List toType,
      int freeSlot, HashMap<JvmConstant, Integer> constants,
      ArrayList<Bytecode> bytecodes) {

    if(fromType.element() == Type.T_VOID) {
      // nothing to do, in this particular case
      return;
    }

    // The following piece of code implements a java for-each loop which
    // iterates every element of the input collection, and recursively
    // converts it before loading it back onto a new WhileyList.

    String loopLabel = freshLabel();
    String exitLabel = freshLabel();
    int iter = freeSlot++;
    int tmp = freeSlot++;
    JvmType.Function ftype = new JvmType.Function(JAVA_UTIL_ITERATOR);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_COLLECTION, "iterator",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.Store(iter,
        JAVA_UTIL_ITERATOR));
    construct(WHILEYLIST,freeSlot,bytecodes);
    bytecodes.add(new Bytecode.Store(tmp, WHILEYLIST));
    bytecodes.add(new Bytecode.Label(loopLabel));
    ftype = new JvmType.Function(T_BOOL);
    bytecodes.add(new Bytecode.Load(iter,JAVA_UTIL_ITERATOR));
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "hasNext",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.If(Bytecode.IfMode.EQ, exitLabel));
    bytecodes.add(new Bytecode.Load(tmp,WHILEYLIST));
    bytecodes.add(new Bytecode.Load(iter,JAVA_UTIL_ITERATOR));
    ftype = new JvmType.Function(JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "next",
        ftype, Bytecode.InvokeMode.INTERFACE));
    addReadConversion(fromType.element(),bytecodes);
    addCoercion(fromType.element(), toType.element(), freeSlot,
        constants, bytecodes);
    ftype = new JvmType.Function(T_BOOL,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "add",
        ftype, Bytecode.InvokeMode.VIRTUAL));
    bytecodes.add(new Bytecode.Pop(T_BOOL));
    bytecodes.add(new Bytecode.Goto(loopLabel));
    bytecodes.add(new Bytecode.Label(exitLabel));
    bytecodes.add(new Bytecode.Load(tmp,WHILEYLIST));
  }

  protected void buildCoercion(Type.List fromType, Type.Map toType,
      int freeSlot, HashMap<JvmConstant, Integer> constants,
      ArrayList<Bytecode> bytecodes) {

    if(fromType.element() == Type.T_VOID) {
      // nothing to do, in this particular case
      return;
    }

    // The following piece of code implements a java for-each loop which
    // iterates every element of the input collection, and recursively
    // converts it before loading it back onto a new WhileyList.
    String loopLabel = freshLabel();
    String exitLabel = freshLabel();
    int iter = freeSlot++;
    int source = freeSlot++;
    int target = freeSlot++;
    bytecodes.add(new Bytecode.Store(source,JAVA_UTIL_LIST));
    bytecodes.add(new Bytecode.LoadConst(0));
    bytecodes.add(new Bytecode.Store(iter,T_INT));

    construct(WHILEYMAP,freeSlot,bytecodes);
    bytecodes.add(new Bytecode.Store(target, WHILEYMAP));
    bytecodes.add(new Bytecode.Label(loopLabel));
    JvmType.Function ftype = new JvmType.Function(T_INT);
    bytecodes.add(new Bytecode.Load(iter,JvmTypes.T_INT));
    bytecodes.add(new Bytecode.Load(source,JAVA_UTIL_LIST));
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_LIST, "size",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.IfCmp(Bytecode.IfCmp.GE, T_INT, exitLabel));
    bytecodes.add(new Bytecode.Load(target,WHILEYSET));
    bytecodes.add(new Bytecode.Load(iter,T_INT));
    bytecodes.add(new Bytecode.Conversion(T_INT,T_LONG));
    ftype = new JvmType.Function(WHILEYINT,T_LONG);
    bytecodes.add(new Bytecode.Invoke(WHILEYINT, "valueOf",
        ftype, Bytecode.InvokeMode.STATIC));
    bytecodes.add(new Bytecode.Load(source,WHILEYMAP));
    bytecodes.add(new Bytecode.Load(iter,T_INT));
    ftype = new JvmType.Function(JAVA_LANG_OBJECT,T_INT);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_LIST, "get",
        ftype, Bytecode.InvokeMode.INTERFACE));
    addReadConversion(fromType.element(),bytecodes);
    addCoercion(fromType.element(), toType.value(), freeSlot,
        constants, bytecodes);
    ftype = new JvmType.Function(JAVA_LANG_OBJECT,JAVA_LANG_OBJECT,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "put",
        ftype, Bytecode.InvokeMode.VIRTUAL));
    bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    bytecodes.add(new Bytecode.Iinc(iter,1));
    bytecodes.add(new Bytecode.Goto(loopLabel));
    bytecodes.add(new Bytecode.Label(exitLabel));
    bytecodes.add(new Bytecode.Load(target,WHILEYMAP));
  }

  protected void buildCoercion(Type.Map fromType, Type.Map toType,
      int freeSlot, HashMap<JvmConstant, Integer> constants,
      ArrayList<Bytecode> bytecodes) {

    if (fromType.key() == Type.T_VOID || toType.key() == Type.T_VOID) {
      // nothing to do, in this particular case
      return;
    }

    // The following piece of code implements a java for-each loop which
    // iterates every element of the input collection, and recursively
    // converts it before loading it back onto a new WhileyList.
    String loopLabel = freshLabel();
    String exitLabel = freshLabel();

    int iter = freeSlot++;
    int source = freeSlot++;
    int target = freeSlot++;

    bytecodes.add(new Bytecode.Dup(WHILEYMAP));
    bytecodes.add(new Bytecode.Store(source, WHILEYMAP));
    construct(WHILEYMAP,freeSlot,bytecodes);
    bytecodes.add(new Bytecode.Store(target, WHILEYMAP));

    JvmType.Function ftype = new JvmType.Function(JAVA_UTIL_SET);
    bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "keySet",
        ftype, Bytecode.InvokeMode.VIRTUAL));
    ftype = new JvmType.Function(JAVA_UTIL_ITERATOR);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_SET, "iterator",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.Store(iter,
        JAVA_UTIL_ITERATOR));

    bytecodes.add(new Bytecode.Label(loopLabel));
    ftype = new JvmType.Function(T_BOOL);
    bytecodes.add(new Bytecode.Load(iter,JAVA_UTIL_ITERATOR));
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "hasNext",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.If(Bytecode.IfMode.EQ, exitLabel));

    bytecodes.add(new Bytecode.Load(target,WHILEYMAP));
    bytecodes.add(new Bytecode.Load(iter,JAVA_UTIL_ITERATOR));
    ftype = new JvmType.Function(JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "next",
        ftype, Bytecode.InvokeMode.INTERFACE));
    addReadConversion(fromType.key(),bytecodes);
    bytecodes.add(new Bytecode.Dup(convertType(fromType.key())));
    addCoercion(fromType.key(), toType.key(), freeSlot,
        constants, bytecodes);
    addWriteConversion(toType.key(),bytecodes);
    bytecodes.add(new Bytecode.Swap());
    bytecodes.add(new Bytecode.Load(source,WHILEYMAP));
    bytecodes.add(new Bytecode.Swap());
    ftype = new JvmType.Function(JAVA_LANG_OBJECT,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "get",
        ftype, Bytecode.InvokeMode.VIRTUAL));
    addReadConversion(fromType.value(),bytecodes);
    addCoercion(fromType.value(), toType.value(), freeSlot,
        constants, bytecodes);
    addWriteConversion(toType.value(),bytecodes);
    ftype = new JvmType.Function(JAVA_LANG_OBJECT,JAVA_LANG_OBJECT,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "put",
        ftype, Bytecode.InvokeMode.VIRTUAL));
    bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    bytecodes.add(new Bytecode.Goto(loopLabel));
    bytecodes.add(new Bytecode.Label(exitLabel));
    bytecodes.add(new Bytecode.Load(target,WHILEYMAP));
  }

  protected void buildCoercion(Type.Set fromType, Type.Map toType,
      int freeSlot, HashMap<JvmConstant, Integer> constants,
      ArrayList<Bytecode> bytecodes) {
    // this case can only happen in one situation --- when the set is empty.

    if (fromType.element() != Type.T_VOID) {
      throw new RuntimeException("invalid coercion encountered: "
          + fromType + " => " + toType);
    }

    bytecodes.add(new Bytecode.Pop(WHILEYSET));
    construct(WHILEYMAP, freeSlot, bytecodes);
  }

  protected void buildCoercion(Type.List fromType, Type.Set toType,
      int freeSlot, HashMap<JvmConstant,Integer> constants,
      ArrayList<Bytecode> bytecodes) {

    if(fromType.element() == Type.T_VOID) {
      // nothing to do, in this particular case
      return;
    }

    // The following piece of code implements a java for-each loop which
    // iterates every element of the input collection, and recursively
    // converts it before loading it back onto a new WhileyList.
    String loopLabel = freshLabel();
    String exitLabel = freshLabel();
    int iter = freeSlot++;
    int tmp = freeSlot++;
    JvmType.Function ftype = new JvmType.Function(JAVA_UTIL_ITERATOR);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_COLLECTION, "iterator",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.Store(iter,
        JAVA_UTIL_ITERATOR));
    construct(WHILEYSET,freeSlot,bytecodes);
    bytecodes.add(new Bytecode.Store(tmp, WHILEYSET));
    bytecodes.add(new Bytecode.Label(loopLabel));
    ftype = new JvmType.Function(T_BOOL);
    bytecodes.add(new Bytecode.Load(iter,JAVA_UTIL_ITERATOR));
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "hasNext",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.If(Bytecode.IfMode.EQ, exitLabel));
    bytecodes.add(new Bytecode.Load(tmp,WHILEYSET));
    bytecodes.add(new Bytecode.Load(iter,JAVA_UTIL_ITERATOR));
    ftype = new JvmType.Function(JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "next",
        ftype, Bytecode.InvokeMode.INTERFACE));
    addReadConversion(fromType.element(),bytecodes);
    addCoercion(fromType.element(), toType.element(), freeSlot,
        constants, bytecodes);
    ftype = new JvmType.Function(T_BOOL,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYSET, "add",
        ftype, Bytecode.InvokeMode.VIRTUAL));
    bytecodes.add(new Bytecode.Pop(T_BOOL));
    bytecodes.add(new Bytecode.Goto(loopLabel));
    bytecodes.add(new Bytecode.Label(exitLabel));
    bytecodes.add(new Bytecode.Load(tmp,WHILEYSET));
  }

  protected void buildCoercion(Type.Set fromType, Type.Set toType,
      int freeSlot, HashMap<JvmConstant,Integer> constants,
      ArrayList<Bytecode> bytecodes) {

    if(fromType.element() == Type.T_VOID) {
      // nothing to do, in this particular case
      return;
    }

    // The following piece of code implements a java for-each loop which
    // iterates every element of the input collection, and recursively
    // converts it before loading it back onto a new WhileyList.
    String loopLabel = freshLabel();
    String exitLabel = freshLabel();
    int iter = freeSlot++;
    int tmp = freeSlot++;
    JvmType.Function ftype = new JvmType.Function(JAVA_UTIL_ITERATOR);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_COLLECTION, "iterator",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.Store(iter,
        JAVA_UTIL_ITERATOR));
    construct(WHILEYSET,freeSlot,bytecodes);
    bytecodes.add(new Bytecode.Store(tmp, WHILEYSET));
    bytecodes.add(new Bytecode.Label(loopLabel));
    ftype = new JvmType.Function(T_BOOL);
    bytecodes.add(new Bytecode.Load(iter,JAVA_UTIL_ITERATOR));
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "hasNext",
        ftype, Bytecode.InvokeMode.INTERFACE));
    bytecodes.add(new Bytecode.If(Bytecode.IfMode.EQ, exitLabel));
    bytecodes.add(new Bytecode.Load(tmp,WHILEYSET));
    bytecodes.add(new Bytecode.Load(iter,JAVA_UTIL_ITERATOR));
    ftype = new JvmType.Function(JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(JAVA_UTIL_ITERATOR, "next",
        ftype, Bytecode.InvokeMode.INTERFACE));
    addReadConversion(fromType.element(),bytecodes);
    addCoercion(fromType.element(), toType.element(), freeSlot,
        constants, bytecodes);
    ftype = new JvmType.Function(T_BOOL,JAVA_LANG_OBJECT);
    bytecodes.add(new Bytecode.Invoke(WHILEYSET, "add",
        ftype, Bytecode.InvokeMode.VIRTUAL));
    bytecodes.add(new Bytecode.Pop(T_BOOL));
    bytecodes.add(new Bytecode.Goto(loopLabel));
    bytecodes.add(new Bytecode.Label(exitLabel));
    bytecodes.add(new Bytecode.Load(tmp,WHILEYSET));
  }

  private void buildCoercion(Type.Record fromType, Type.Record toType,
      int freeSlot, HashMap<JvmConstant,Integer> constants,
      ArrayList<Bytecode> bytecodes) {
    int oldSlot = freeSlot++;
    int newSlot = freeSlot++;
    bytecodes.add(new Bytecode.Store(oldSlot,WHILEYRECORD));
    construct(WHILEYRECORD,freeSlot,bytecodes);
    bytecodes.add(new Bytecode.Store(newSlot,WHILEYRECORD));
    Map<String,Type> toFields = toType.fields();
    Map<String,Type> fromFields = fromType.fields();
    for(String key : toFields.keySet()) {
      Type to = toFields.get(key);
      Type from = fromFields.get(key);
      bytecodes.add(new Bytecode.Load(newSlot,WHILEYRECORD));
      bytecodes.add(new Bytecode.LoadConst(key));
      bytecodes.add(new Bytecode.Load(oldSlot,WHILEYRECORD));
      bytecodes.add(new Bytecode.LoadConst(key));
      JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,JAVA_LANG_OBJECT);
      bytecodes.add(new Bytecode.Invoke(WHILEYRECORD,"get",ftype,Bytecode.InvokeMode.VIRTUAL));
      // TODO: in cases when the read conversion is a no-op, we can do
      // better here.
      addReadConversion(from,bytecodes);
      addCoercion(from,to,freeSlot,constants,bytecodes);
      addWriteConversion(from,bytecodes);
      ftype = new JvmType.Function(JAVA_LANG_OBJECT,JAVA_LANG_OBJECT,JAVA_LANG_OBJECT);
      bytecodes.add(new Bytecode.Invoke(WHILEYRECORD,"put",ftype,Bytecode.InvokeMode.VIRTUAL));
      bytecodes.add(new Bytecode.Pop(JAVA_LANG_OBJECT));
    }
    bytecodes.add(new Bytecode.Load(newSlot,WHILEYRECORD));
  }

  private void buildCoercion(Type.Union from, Type to,
      int freeSlot, HashMap<JvmConstant,Integer> constants,
      ArrayList<Bytecode> bytecodes) {

    String exitLabel = freshLabel();
    List<Type> bounds = new ArrayList<Type>(from.bounds());
    ArrayList<String> labels = new ArrayList<String>();

    // basically, we're building a big dispatch table. I think there's no
    // question that this could be more efficient in some cases.
    for(int i=0;i!=bounds.size();++i) {
      Type bound = bounds.get(i);
      if((i+1) == bounds.size()) {
        addReadConversion(bound,bytecodes);
        addCoercion(bound,to,freeSlot,constants,bytecodes);
        bytecodes.add(new Bytecode.Goto(exitLabel));
      } else {
        String label = freshLabel();
        labels.add(label);
        bytecodes.add(new Bytecode.Dup(convertType(from)));
        translateTypeTest(label,from,bound,bytecodes,constants);
      }
    }

    for(int i=0;i<labels.size();++i) {
      String label = labels.get(i);
      Type bound = bounds.get(i);
      bytecodes.add(new Bytecode.Label(label));
      addReadConversion(bound,bytecodes);
      addCoercion(bound,to,freeSlot,constants,bytecodes);
      bytecodes.add(new Bytecode.Goto(exitLabel));
    }

    bytecodes.add(new Bytecode.Label(exitLabel));
  }

  private void buildCoercion(Type from, Type.Union to,
      int freeSlot, HashMap<JvmConstant,Integer> constants,
      ArrayList<Bytecode> bytecodes) {
    Type.Union t2 = (Type.Union) to;

    // First, check for identical type (i.e. no coercion necessary)
    for (Type b : t2.bounds()) {
      if (from.equals(b)) {
        // nothing to do
        return;
      }
    }

    // Second, check for single non-coercive match
    for (Type b : t2.bounds()) {
      if (Type.isSubtype(b, from)) {
        buildCoercion(from,b,freeSlot,constants,bytecodes);
        return;
      }
    }

    // Third, test for single coercive match
    for (Type b : t2.bounds()) {
      if (Type.isExplicitCoerciveSubtype(b, from)) {
        buildCoercion(from,b,freeSlot,constants,bytecodes);
        return;
      }
    }

    // I don't think we should be able to get here!
  }

  /**
   * The read conversion is necessary in situations where we're reading a
   * value from a collection (e.g. WhileyList, WhileySet, etc) and then
   * putting it on the stack. In such case, we need to convert boolean values
   * from Boolean objects to bool primitives.
   */
  private void addReadConversion(Type et, ArrayList<Bytecode> bytecodes) {
    if(et instanceof Type.Bool) {
      bytecodes.add(new Bytecode.CheckCast(JAVA_LANG_BOOLEAN));
      JvmType.Function ftype = new JvmType.Function(T_BOOL);
      bytecodes.add(new Bytecode.Invoke(JAVA_LANG_BOOLEAN,
          "booleanValue", ftype, Bytecode.InvokeMode.VIRTUAL));
    } else if(et instanceof Type.Byte) {
      bytecodes.add(new Bytecode.CheckCast(JAVA_LANG_BYTE));
      JvmType.Function ftype = new JvmType.Function(T_BYTE);
      bytecodes.add(new Bytecode.Invoke(JAVA_LANG_BYTE,
          "byteValue", ftype, Bytecode.InvokeMode.VIRTUAL));
    } else if(et instanceof Type.Char) {
      bytecodes.add(new Bytecode.CheckCast(JAVA_LANG_CHARACTER));
      JvmType.Function ftype = new JvmType.Function(T_CHAR);
      bytecodes.add(new Bytecode.Invoke(JAVA_LANG_CHARACTER,
          "charValue", ftype, Bytecode.InvokeMode.VIRTUAL));
    } else {
      addCheckCast(convertType(et),bytecodes);
    }
  }

  /**
   * The write conversion is necessary in situations where we're write a value
   * from the stack into a collection (e.g. WhileyList, WhileySet, etc). In
   * such case, we need to convert boolean values from bool primitives to
   * Boolean objects.
   */
  private void addWriteConversion(Type et, ArrayList<Bytecode> bytecodes) {
    if(et instanceof Type.Bool) {
      JvmType.Function ftype = new JvmType.Function(JAVA_LANG_BOOLEAN,T_BOOL);
      bytecodes.add(new Bytecode.Invoke(JAVA_LANG_BOOLEAN,
          "valueOf", ftype, Bytecode.InvokeMode.STATIC));
    } else if(et instanceof Type.Byte) {
      JvmType.Function ftype = new JvmType.Function(JAVA_LANG_BYTE,
          T_BYTE);
      bytecodes.add(new Bytecode.Invoke(JAVA_LANG_BYTE, "valueOf", ftype,
          Bytecode.InvokeMode.STATIC));
    } else if(et instanceof Type.Char) {
      JvmType.Function ftype = new JvmType.Function(JAVA_LANG_CHARACTER,
          T_CHAR);
      bytecodes.add(new Bytecode.Invoke(JAVA_LANG_CHARACTER, "valueOf", ftype,
          Bytecode.InvokeMode.STATIC));
    }
  }

  private void addCheckCast(JvmType type, ArrayList<Bytecode> bytecodes) {
    // The following can happen in situations where a variable has type
    // void. In principle, we could remove this as obvious dead-code, but
    // for now I just avoid it.
    if(type instanceof JvmType.Void) {
      return;
    } else if(!type.equals(JAVA_LANG_OBJECT)) {
      // pointless to add a cast for object
      bytecodes.add(new Bytecode.CheckCast(type));
    }
  }

  /**
   * Return true if this type is, or maybe reference counted.
   *
   * @param t
   * @return
   */
  private static boolean isRefCounted(Type t) {
    if (t instanceof Type.Union) {
      Type.Union n = (Type.Union) t;
      for (Type b : n.bounds()) {
        if (isRefCounted(b)) {
          return true;
        }
      }
      return false;
    } else {
      // FIXME: what about negations?
      return t instanceof Type.Any || t instanceof Type.List
          || t instanceof Type.Tuple || t instanceof Type.Set
          || t instanceof Type.Map || t instanceof Type.Record;
    }
  }

  /**
   * Add bytecodes for incrementing the reference count.
   *
   * @param type
   * @param bytecodes
   */
  private static void addIncRefs(Type type, ArrayList<Bytecode> bytecodes) {
    if(isRefCounted(type)){
      JvmType jtype = convertType(type);
      JvmType.Function ftype = new JvmType.Function(jtype,jtype);
      bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,"incRefs",ftype,Bytecode.InvokeMode.STATIC));
    }
  }

  private static void addIncRefs(Type.List type, ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(WHILEYLIST,WHILEYLIST);
    bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,"incRefs",ftype,Bytecode.InvokeMode.STATIC));
  }

  private static void addIncRefs(Type.Record type, ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(WHILEYRECORD,WHILEYRECORD);
    bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,"incRefs",ftype,Bytecode.InvokeMode.STATIC));
  }

  private static void addIncRefs(Type.Map type, ArrayList<Bytecode> bytecodes) {
    JvmType.Function ftype = new JvmType.Function(WHILEYMAP,WHILEYMAP);
    bytecodes.add(new Bytecode.Invoke(WHILEYUTIL,"incRefs",ftype,Bytecode.InvokeMode.STATIC));
  }

  /**
   * The construct method provides a generic way to construct a Java object.
   *
   * @param owner
   * @param freeSlot
   * @param bytecodes
   * @param params
   */
  private void construct(JvmType.Clazz owner, int freeSlot,
      ArrayList<Bytecode> bytecodes) {
    bytecodes.add(new Bytecode.New(owner));
    bytecodes.add(new Bytecode.Dup(owner));
    ArrayList<JvmType> paramTypes = new ArrayList<JvmType>();
    JvmType.Function ftype = new JvmType.Function(T_VOID,paramTypes);
    bytecodes.add(new Bytecode.Invoke(owner, "<init>", ftype,
        Bytecode.InvokeMode.SPECIAL));
  }

  private final static Type.Record WHILEY_PRINTWRITER_T = Type.Record(false,
      new HashMap() {
    {
      put("print", Type.Method(Type.T_VOID, Type.T_VOID, Type.T_ANY));
      put("println", Type.Method(Type.T_VOID, Type.T_VOID, Type.T_ANY));
    }
  });

  private final static Type WHILEY_SYSTEM_T = Type.Record(false,
      new HashMap() {
        {
          put("out", WHILEY_PRINTWRITER_T);
          put("args", Type.List(Type.T_STRING,false));
        }
      });

  private final static JvmType.Clazz WHILEYUTIL = new JvmType.Clazz("wyjc.runtime","Util");
  private final static JvmType.Clazz WHILEYLIST = new JvmType.Clazz("wyjc.runtime","WyList");
  private final static JvmType.Clazz WHILEYSET = new JvmType.Clazz("wyjc.runtime","WySet");
  private final static JvmType.Clazz WHILEYTUPLE = new JvmType.Clazz("wyjc.runtime","WyTuple");
  private final static JvmType.Clazz WHILEYCOLLECTION = new JvmType.Clazz("wyjc.runtime","WyCollection");
  private final static JvmType.Clazz WHILEYTYPE = new JvmType.Clazz("wyjc.runtime","WyType");
  private final static JvmType.Clazz WHILEYMAP = new JvmType.Clazz("wyjc.runtime","WyMap");
  private final static JvmType.Clazz WHILEYRECORD = new JvmType.Clazz("wyjc.runtime","WyRecord");
  private final static JvmType.Clazz WHILEYOBJECT = new JvmType.Clazz("wyjc.runtime", "WyObject");
  private final static JvmType.Clazz WHILEYEXCEPTION = new JvmType.Clazz("wyjc.runtime","WyException");
  private final static JvmType.Clazz WHILEYINT = new JvmType.Clazz("java.math","BigInteger");
  private final static JvmType.Clazz WHILEYRAT = new JvmType.Clazz("wyjc.runtime","WyRat");
  private final static JvmType.Clazz WHILEYLAMBDA = new JvmType.Clazz("wyjc.runtime","WyLambda");

  private static final JvmType.Clazz JAVA_LANG_CHARACTER = new JvmType.Clazz("java.lang","Character");
  private static final JvmType.Clazz JAVA_LANG_SYSTEM = new JvmType.Clazz("java.lang","System");
  private static final JvmType.Array JAVA_LANG_OBJECT_ARRAY = new JvmType.Array(JAVA_LANG_OBJECT);
  private static final JvmType.Clazz JAVA_UTIL_LIST = new JvmType.Clazz("java.util","List");
  private static final JvmType.Clazz JAVA_UTIL_SET = new JvmType.Clazz("java.util","Set");
  //private static final JvmType.Clazz JAVA_LANG_REFLECT_METHOD = new JvmType.Clazz("java.lang.reflect","Method");
  private static final JvmType.Clazz JAVA_IO_PRINTSTREAM = new JvmType.Clazz("java.io","PrintStream");
  private static final JvmType.Clazz JAVA_LANG_RUNTIMEEXCEPTION = new JvmType.Clazz("java.lang","RuntimeException");
  private static final JvmType.Clazz JAVA_LANG_ASSERTIONERROR = new JvmType.Clazz("java.lang","AssertionError");
  private static final JvmType.Clazz JAVA_UTIL_COLLECTION = new JvmType.Clazz("java.util","Collection");

  private JvmType.Function convertFunType(Type.FunctionOrMethod ft) {
    ArrayList<JvmType> paramTypes = new ArrayList<JvmType>();
    for(Type pt : ft.params()) {
      paramTypes.add(convertType(pt));
    }
    JvmType rt = convertType(ft.ret());
    return new JvmType.Function(rt,paramTypes);
  }

  private static JvmType convertType(Type t) {
    if(t == Type.T_VOID) {
      return T_VOID;
    } else if(t == Type.T_ANY) {
      return JAVA_LANG_OBJECT;
    } else if(t == Type.T_NULL) {
      return JAVA_LANG_OBJECT;
    } else if(t instanceof Type.Bool) {
      return T_BOOL;
    } else if(t instanceof Type.Byte) {
      return T_BYTE;
    } else if(t instanceof Type.Char) {
      return T_CHAR;
    } else if(t instanceof Type.Int) {
      return WHILEYINT;
    } else if(t instanceof Type.Real) {
      return WHILEYRAT;
    } else if(t instanceof Type.Meta) {
      return WHILEYTYPE;
    } else if(t instanceof Type.Strung) {
      return JAVA_LANG_STRING;
    } else if(t instanceof Type.EffectiveList) {
      return WHILEYLIST;
    } else if(t instanceof Type.EffectiveSet) {
      return WHILEYSET;
    } else if(t instanceof Type.EffectiveMap) {
      return WHILEYMAP;
    } else if(t instanceof Type.EffectiveRecord) {
      return WHILEYRECORD;
    } else if(t instanceof Type.EffectiveTuple) {
      return WHILEYTUPLE;
    } else if(t instanceof Type.Reference) {
      return WHILEYOBJECT;
    } else if(t instanceof Type.Negation) {
      // can we do any better?
      return JAVA_LANG_OBJECT;
    } else if(t instanceof Type.Union) {
      return JAVA_LANG_OBJECT;
    } else if(t instanceof Type.Meta) {
      return JAVA_LANG_OBJECT;
    } else if(t instanceof Type.FunctionOrMethod) {
      return WHILEYLAMBDA;
    } else {
      throw new RuntimeException("unknown type encountered: " + t);
    }
  }

  protected int label = 0;
  protected String freshLabel() {
    return "cfblab" + label++;
  }

  private static String nameMangle(String name, Type.FunctionOrMethod ft) {
    try {
      return name + "$" + typeMangle(ft);
    } catch(IOException e) {
      throw new RuntimeException(e);
    }
  }

  private static String typeMangle(Type.FunctionOrMethod ft) throws IOException {
    JavaIdentifierOutputStream jout = new JavaIdentifierOutputStream();
    BinaryOutputStream binout = new BinaryOutputStream(jout);
    Type.BinaryWriter tm = new Type.BinaryWriter(binout);
    tm.write(ft);
    binout.close(); // force flush
    return jout.toString();
  }

  /**
   * A constant is some kind of auxillary functionality used in generated code, which can be reused at multiple sites.  This includes value constants, and coercion functions.
   * @author David J. Pearce
   *
   */
  private abstract static class JvmConstant {}

  private static final class JvmValue extends JvmConstant {
    public final Constant value;
    public JvmValue(Constant v) {
      value = v;
    }
    public boolean equals(Object o) {
      if(o instanceof JvmValue) {
        JvmValue vc = (JvmValue) o;
        return value.equals(vc.value);
      }
      return false;
    }
    public int hashCode() {
      return value.hashCode();
    }
    public static int get(Constant value, HashMap<JvmConstant,Integer> constants) {
      JvmValue vc = new JvmValue(value);
      Integer r = constants.get(vc);
      if(r != null) {
        return r;
      } else {
        int x = constants.size();
        constants.put(vc, x);
        return x;
      }
    }
  }
  private static final class JvmCoercion extends JvmConstant {
    public final Type from;
    public final Type to;
    public JvmCoercion(Type from, Type to) {
      this.from = from;
      this.to = to;
    }
    public boolean equals(Object o) {
      if(o instanceof JvmCoercion) {
        JvmCoercion c = (JvmCoercion) o;
        return from.equals(c.from) && to.equals(c.to);
      }
      return false;
    }
    public int hashCode() {
      return from.hashCode() + to.hashCode();
    }
    public static int get(Type from, Type to, HashMap<JvmConstant,Integer> constants) {
      JvmCoercion vc = new JvmCoercion(from,to);
      Integer r = constants.get(vc);
      if(r != null) {
        return r;
      } else {
        int x = constants.size();
        constants.put(vc, x);
        return x;
      }
    }
  }

  private static class UnresolvedHandler {
    public String start;
    public String end;
    public String target;
    public JvmType.Clazz exception;

    public UnresolvedHandler(String start, String end, String target,
        JvmType.Clazz exception) {
      this.start = start;
      this.end = end;
      this.target = target;
      this.exception = exception;
    }
  }

  /*
  public static void testMangle1(Type.Fun ft) throws IOException {
    IdentifierOutputStream jout = new IdentifierOutputStream();
    BinaryOutputStream binout = new BinaryOutputStream(jout);
    Types.BinaryWriter tm = new Types.BinaryWriter(binout);
    Type.build(tm,ft);
    binout.close();
    System.out.println("MANGLED: " + ft + " => " + jout.toString());
    Type.Fun type = (Type.Fun) new Types.BinaryReader(
        new BinaryInputStream(new IdentifierInputStream(
            jout.toString()))).read();
    System.out.println("UNMANGLED TO: " + type);
    if(!type.equals(ft)) {
      throw new RuntimeException("INVALID TYPE RECONSTRUCTED");
    }
  }
  */
}
 
TOP

Related Classes of wyjc.Wyil2JavaBuilder$JvmCoercion

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.