<commit_before>// Copyright (c) 2011, <NAME> (<EMAIL>)
// 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.io;

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

import wyjc.attributes.WhileyDefine;
import wyjc.attributes.WhileyType;
import wyjc.attributes.WhileyVersion;
import wyil.*;
import static wyil.util.SyntaxError.*;
import wyil.util.*;
import wyil.lang.*;
import wyil.lang.Code.*;
import static wyil.lang.Block.*;
import wyjc.runtime.BigRational;
import wyjvm.io.BinaryInputStream;
import wyjvm.io.BinaryOutputStream;
import wyjvm.lang.*;
import static wyjvm.lang.JvmTypes.*;

/**
 * The purpose of the class file builder is to construct a jvm class file from a
 * given WhileyFile.
 * 
 * @author djp
 */
public class ClassFileBuilder {
	protected int CLASS_VERSION = 49;
	protected int WHILEY_MINOR_VERSION;
	protected int WHILEY_MAJOR_VERSION;
	protected ModuleLoader loader;	
	protected String filename;
	protected JvmType.Clazz owner;
	
	public ClassFileBuilder(ModuleLoader loader, int whileyMajorVersion, int whileyMinorVersion) {
		this.loader = loader;
		this.WHILEY_MINOR_VERSION = whileyMinorVersion;
		this.WHILEY_MAJOR_VERSION = whileyMajorVersion;
	}

	public ClassFile build(Module module) {
		owner = new JvmType.Clazz(module.id().pkg().toString(),
				module.id().module().toString());
		ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
		modifiers.add(Modifier.ACC_PUBLIC);
		modifiers.add(Modifier.ACC_FINAL);
		ClassFile cf = new ClassFile(49, owner, JAVA_LANG_OBJECT,
				new ArrayList<JvmType.Clazz>(), modifiers);
	
		this.filename = module.filename();
		
		boolean addMainLauncher = false;		
				
		for(Module.ConstDef cd : module.constants()) {	
			// FIXME: this is an ugly hack for now
			ArrayList<BytecodeAttribute> attrs = new ArrayList<BytecodeAttribute>();
			for(Attribute a : cd.attributes()) {
				if(a instanceof BytecodeAttribute) {
					attrs.add((BytecodeAttribute)a);
				}
			}
			WhileyDefine wd = new WhileyDefine(cd.name(),cd.constant(),attrs);
			cf.attributes().add(wd);
		}
		
		for(Module.TypeDef td : module.types()) {
			// FIXME: this is an ugly hack for now
			ArrayList<BytecodeAttribute> attrs = new ArrayList<BytecodeAttribute>();
			for(Attribute a : td.attributes()) {
				if(a instanceof BytecodeAttribute) {
					attrs.add((BytecodeAttribute)a);
				}
			}
			Type t = td.type();			
			WhileyDefine wd = new WhileyDefine(td.name(),t,attrs);
			cf.attributes().add(wd);
		}
		
		HashMap<Value,Integer> constants = new HashMap<Value,Integer>();
		for(Module.Method method : module.methods()) {				
			if(method.name().equals("main")) { 
				addMainLauncher = true;
			}			
			cf.methods().addAll(build(method, constants));			
		}		
		
		if(constants.size() > 0) {
			buildConstants(constants,cf);
		}
				
		if(addMainLauncher) {
			cf.methods().add(buildMainLauncher(owner));
		}
		
		cf.attributes().add(
				new WhileyVersion(WHILEY_MAJOR_VERSION, WHILEY_MINOR_VERSION));
		
		return cf;
	}	
	
	public void buildConstants(HashMap<Value,Integer> constants, ClassFile cf) {		
		ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();
		
		for(Map.Entry<Value,Integer> entry : constants.entrySet()) {			
			Value constant = entry.getKey();
			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,bytecodes);
			bytecodes.add(new Bytecode.PutField(owner, name, type, Bytecode.STATIC));
		}
		
		bytecodes.add(new Bytecode.Return(null));
		
		// now, create static initialiser method
		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
		wyjvm.attributes.Code code = new wyjvm.attributes.Code(bytecodes,new ArrayList(),clinit);
		clinit.attributes().add(code);				
	}
		
	public 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(WHILEYPROCESS);
		codes.add(new Bytecode.Invoke(WHILEYPROCESS,"systemProcess",ft1,Bytecode.STATIC));
		codes.add(new Bytecode.Load(0,strArr));
		JvmType.Function ft2 = new JvmType.Function(WHILEYLIST,
				new JvmType.Array(JAVA_LANG_STRING));
		codes.add(new Bytecode.Invoke(WHILEYUTIL,"fromStringList",ft2,Bytecode.STATIC));
		Type.Fun wyft = Type.T_FUN(WHILEY_SYSTEM_T,
				Type.T_VOID, Type.T_LIST(Type.T_STRING));
		JvmType.Function ft3 = convertFunType(wyft);
		
		// The following is a little bit of hack. Basically we flush the stdout
		// channel on exit
		codes.add(new Bytecode.Invoke(owner, nameMangle("main",wyft), ft3, Bytecode.STATIC));
		ft3 = new JvmType.Function(T_VOID);		
		codes.add(new Bytecode.Invoke(WHILEYIO, "flush", ft3, Bytecode.STATIC));
		codes.add(new Bytecode.Return(null));
		
		wyjvm.attributes.Code code = new wyjvm.attributes.Code(codes,
				new ArrayList(), cm);
		cm.attributes().add(code);
		
		return cm;	
	}
	
	public List<ClassFile.Method> build(Module.Method method,
			HashMap<Value, Integer> constants) {
		ArrayList<ClassFile.Method> methods = new ArrayList<ClassFile.Method>();
		int num = 1;
		for(Module.Case c : method.cases()) {
			methods.add(build(num++,c,method,constants));
		}
		return methods;
	}
	
	public ClassFile.Method build(int caseNum, Module.Case mcase,
			Module.Method method, HashMap<Value,Integer> constants) {		
		
		ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
		if(method.isPublic()) {
			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<Bytecode> codes = translate(mcase,constants);
		wyjvm.attributes.Code code = new wyjvm.attributes.Code(codes,new ArrayList(),cm);
		cm.attributes().add(code);		
		
		return cm;
	}
	
	public ArrayList<Bytecode> translate(Module.Case mcase, HashMap<Value,Integer> constants) {
		ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();				
		translate(mcase.body(),mcase.locals().size(),constants,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 *
	 */
	public void translate(Block blk, int freeSlot, HashMap<Value,Integer> constants,
			ArrayList<Bytecode> bytecodes) {
		for (Entry s : blk) {
			freeSlot = translate(s, freeSlot, constants, bytecodes);
		}
	}
	
	public int translate(Entry entry, int freeSlot,
			HashMap<Value,Integer> constants, ArrayList<Bytecode> bytecodes) {
		try {
			Code code = entry.code;
			if(code instanceof Assert) {
				 // translate((Assert)code,freeSlot,bytecodes);
			} else if(code instanceof BinOp) {
				 translate((BinOp)code,entry,freeSlot,bytecodes);
			} else if(code instanceof Convert) {
				 translate((Convert)code,freeSlot,constants,bytecodes);
			} else if(code instanceof Const) {
				translate((Const) code, freeSlot, constants, bytecodes);
			} else if(code instanceof Debug) {
				 translate((Debug)code,freeSlot,bytecodes);
			} else if(code instanceof Destructure) {
				 translate((Destructure)code,freeSlot,bytecodes);
			} else if(code instanceof DictLoad) {
				 translate((DictLoad)code,freeSlot,bytecodes);
			} else if(code instanceof End) {
				 translate((End)code,freeSlot,bytecodes);
			} else if(code instanceof ExternJvm) {
				translate((ExternJvm)code,freeSlot,bytecodes);
			} else if(code instanceof Fail) {
				 translate((Fail)code,freeSlot,bytecodes);
			} else if(code instanceof FieldLoad) {
				 translate((FieldLoad)code,freeSlot,bytecodes);
			} else if(code instanceof ForAll) {
				 freeSlot = translate((ForAll)code,freeSlot,bytecodes);
			} else if(code instanceof Goto) {
				 translate((Goto)code,freeSlot,bytecodes);
			} else if(code instanceof IfGoto) {
				translate((IfGoto) code, entry, freeSlot, bytecodes);
			} else if(code instanceof IfType) {
				translate((IfType) code, entry, freeSlot, constants, bytecodes);
			} else if(code instanceof IndirectInvoke) {
				 translate((IndirectInvoke)code,freeSlot,bytecodes);
			} else if(code instanceof IndirectSend) {
				 translate((IndirectSend)code,freeSlot,bytecodes);
			} else if(code instanceof Invoke) {
				 translate((Invoke)code,freeSlot,bytecodes);
			} else if(code instanceof Invert) {
				 translate((Invert)code,freeSlot,bytecodes);
			} else if(code instanceof Label) {
				translate((Label)code,freeSlot,bytecodes);
			} else if(code instanceof ListAppend) {
				 translate((ListAppend)code,entry,freeSlot,bytecodes);
			} else if(code instanceof ListLength) {
				 translate((ListLength)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SubList) {
				 translate((SubList)code,entry,freeSlot,bytecodes);
			} else if(code instanceof ListLoad) {
				 translate((ListLoad)code,freeSlot,bytecodes);
			} else if(code instanceof Load) {
				 translate((Load)code,freeSlot,bytecodes);
			} else if(code instanceof Loop) {
				 translate((Loop)code,freeSlot,bytecodes);
			} else if(code instanceof Update) {
				 translate((Update)code,freeSlot,bytecodes);
			} else if(code instanceof NewDict) {
				 translate((NewDict)code,freeSlot,bytecodes);
			} else if(code instanceof NewList) {
				 translate((NewList)code,freeSlot,bytecodes);
			} else if(code instanceof NewRecord) {
				 translate((NewRecord)code,freeSlot,bytecodes);
			} else if(code instanceof NewSet) {
				 translate((NewSet)code,freeSlot,bytecodes);
			} else if(code instanceof NewTuple) {
				 translate((NewTuple)code,freeSlot,bytecodes);
			} else if(code instanceof Negate) {
				 translate((Negate)code,freeSlot,bytecodes);
			} else if(code instanceof ProcLoad) {
				 translate((ProcLoad)code,freeSlot,bytecodes);
			} else if(code instanceof Return) {
				 translate((Return)code,freeSlot,bytecodes);
			} else if(code instanceof Skip) {
				// do nothing
			} else if(code instanceof Send) {
				 translate((Send)code,freeSlot,bytecodes);
			} else if(code instanceof SetLength) {
				 translate((SetLength)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SetUnion) {
				 translate((SetUnion)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SetIntersect) {
				 translate((SetIntersect)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SetDifference) {
				 translate((SetDifference)code,entry,freeSlot,bytecodes);
			} else if(code instanceof StringAppend) {
				 translate((StringAppend)code,entry,freeSlot,bytecodes);
			} else if(code instanceof StringLoad) {
				 translate((StringLoad)code,entry,freeSlot,bytecodes);
			} else if(code instanceof StringLength) {
				 translate((StringLength)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SubString) {
				 translate((SubString)code,entry,freeSlot,bytecodes);
			} else if(code instanceof Store) {
				 translate((Store)code,freeSlot,bytecodes);
			} else if(code instanceof Switch) {
				 translate((Switch)code,entry,freeSlot,bytecodes);
			} else if(code instanceof Spawn) {
				 translate((Spawn)code,freeSlot,bytecodes);
			} else if(code instanceof Throw) {
				 translate((Throw)code,freeSlot,bytecodes);
			} else {
				syntaxError("unknown wyil code encountered (" + code + ")", filename, entry);
			}
			
		} catch (SyntaxError ex) {
			throw ex;
		} catch (Exception ex) {		
			syntaxError("internal failure", filename, entry, ex);
		}
		
		return freeSlot;
	}
	
	public void translate(Code.Const c, int freeSlot,
			HashMap<Value,Integer> constants,
			ArrayList<Bytecode> bytecodes) {
		
		Value constant = c.constant;
		if (constant instanceof Value.Rational || constant instanceof Value.Bool
				|| constant instanceof Value.Null || constant instanceof Value.Byte) {
			translate(constant,freeSlot,bytecodes);					
		} else {
			int id;
			if(constants.containsKey(constant)) {
				id = constants.get(constant);
			} else {
				id = constants.size();
				constants.put(constant, id);				
			}
			String name = "constant$" + id;
			JvmType type = convertType(constant.type());
			bytecodes.add(new Bytecode.GetField(owner, name, type, Bytecode.STATIC));
		}		
	}
	
	public void translate(Code.Convert c, int freeSlot,
			HashMap<Value,Integer> constants, ArrayList<Bytecode> bytecodes) {		
		Type toType = c.to;
		Type fromType = c.from;

		if (Type.isomorphic(toType, fromType)) {		
			// do nothing!						
		} else if (!(toType instanceof Type.Bool) && fromType instanceof Type.Bool) {
			// this is either going into a union type, or the any type
			upConversion(toType, (Type.Bool) fromType, freeSlot, bytecodes);
		} else if(fromType == Type.T_INT) {									
			upConversion(toType, (Type.Int)fromType,freeSlot,bytecodes);  
		} else if(fromType == Type.T_CHAR) {									
			upConversion(toType, (Type.Char)fromType,freeSlot,bytecodes);  
		} else if(fromType == Type.T_BYTE) {									
			upConversion(toType, (Type.Byte)fromType,freeSlot,bytecodes); 
		} else if(!Type.isSubtype(toType, fromType)) {
			// Fall-back to an external (recursive) check
			int id;
			Value constant = Value.V_TYPE(toType);
			if(constants.containsKey(constant)) {
				id = constants.get(constant);
			} else {
				id = constants.size();
				constants.put(constant, id);				
			}
			String name = "constant$" + id;

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

			JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,convertType(fromType),WHILEYTYPE);
			bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "coerce",
					ftype, Bytecode.STATIC));			
			addReadConversion(toType,bytecodes);
		}
	}
	
	public void upConversion(Type toType, Type.Bool fromType,
			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.STATIC));			
		// done deal!
	}
	
	public void upConversion(Type toType, Type.Byte fromType,
			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.STATIC));			
		// done deal!
	}
	
	public void upConversion(Type toType, Type.Int fromType,
			int freeSlot, ArrayList<Bytecode> bytecodes) {
		if(!Type.isSubtype(toType,fromType)) {
			Type glb = Type.greatestLowerBound(Type.T_REAL, toType);
			if(glb == Type.T_REAL) { 
				// coercion required!
				JvmType.Function ftype = new JvmType.Function(BIG_RATIONAL,BIG_INTEGER);			
				bytecodes.add(new Bytecode.Invoke(BIG_RATIONAL,"valueOf",ftype,Bytecode.STATIC));
			} else {				
				// must be => char
				JvmType.Function ftype = new JvmType.Function(T_INT);			
				bytecodes.add(new Bytecode.Invoke(BIG_INTEGER,"intValue",ftype,Bytecode.VIRTUAL));				
			}
		}
	}

	public void upConversion(Type toType, Type.Char fromType,
			int freeSlot, ArrayList<Bytecode> bytecodes) {
		if(!Type.isSubtype(toType,fromType)) {					
			if(toType == Type.T_REAL) { 
				// coercion required!
				JvmType.Function ftype = new JvmType.Function(BIG_RATIONAL,T_INT);			
				bytecodes.add(new Bytecode.Invoke(BIG_RATIONAL,"valueOf",ftype,Bytecode.STATIC));
			} else {
				bytecodes.add(new Bytecode.Conversion(T_INT, T_LONG));
				JvmType.Function ftype = new JvmType.Function(BIG_INTEGER,T_LONG);			
				bytecodes.add(new Bytecode.Invoke(BIG_INTEGER,"valueOf",ftype,Bytecode.STATIC));				
			}
		} else {
			JvmType.Function ftype = new JvmType.Function(JAVA_LANG_CHARACTER,T_CHAR);			
			bytecodes.add(new Bytecode.Invoke(JAVA_LANG_CHARACTER,"valueOf",ftype,Bytecode.STATIC));	
		}
	}
	
	public void translate(Code.Store c, int freeSlot,			
			ArrayList<Bytecode> bytecodes) {		
		JvmType type = convertType(c.type);
		//addIncRefs(c.type,bytecodes);		
		bytecodes.add(new Bytecode.Store(c.slot, type));				
	}

	public void translate(Code.Update c, int freeSlot,ArrayList<Bytecode> bytecodes) {
		
		// Now, my general feeling is that the multistore bytecode could use
		// some work. Essentially, to simplify this process of figuring our what
		// is being updated.
		
		// First, check if this is updating the process' state
		Type type = c.type;
				
		if(c.slot == 0 && Type.isSubtype(Type.T_PROCESS(Type.T_ANY), type)) {
			Type.Process p = (Type.Process) type;
			type = p.element();
		}
		
		// Second, determine type of value being assigned
		
		ArrayList<String> fields = c.fields;
		int fi = 0;						
		Type iter = type;
		// ok, this is such an ugly hack...
		ArrayList<Type> indices = new ArrayList<Type>();
		for(int i=0;i!=c.level;++i) {
			if(Type.isSubtype(Type.T_DICTIONARY(Type.T_ANY, Type.T_ANY),iter)) {
				Type.Dictionary dict = Type.effectiveDictionaryType(iter);				
				indices.add(dict.key());
				iter = dict.value();
			} else if(Type.isSubtype(Type.T_STRING,iter)) {
				iter = Type.T_CHAR;
				indices.add(Type.T_INT);
			} else if(Type.isSubtype(Type.T_LIST(Type.T_ANY),iter)) {
				Type.List list = Type.effectiveListType(iter);
				iter = list.element();
				indices.add(Type.T_INT);
			} else {
				Type.Record rec = Type.effectiveRecordType(iter);
				String field = fields.get(fi++);
				iter = rec.fields().get(field);
			}	
		}
						
		int indexSlot = freeSlot;
		freeSlot += indices.size();
		// Third, store the value to be assigned				
		JvmType val_t = convertType(iter);		
		bytecodes.add(new Bytecode.Store(freeSlot++,val_t));
		
		for(int i=indices.size()-1;i>=0;--i) {
			JvmType t = convertType(indices.get(i));
			bytecodes.add(new Bytecode.Store(indexSlot+i,t));
		}
		
		bytecodes.add(new Bytecode.Load(c.slot, convertType(c.type)));
		
		// Fourth, finally process the assignment path and update the object in
		// question.		
		multiStoreHelper(c.type,c.level-1,fields.iterator(),indexSlot,val_t,freeSlot, bytecodes);		
		bytecodes.add(new Bytecode.Store(c.slot, convertType(c.type)));
	}

	public void multiStoreHelper(Type type, int level,
			Iterator<String> fields, int indexSlot, JvmType val_t, int freeSlot, 
			ArrayList<Bytecode> bytecodes) {
		
		// This method is major ugly. I'm sure there must be a better way of
		// doing this. Probably, if I change the multistore bytecode, that would
		// help.
		
		if(Type.isSubtype(Type.T_PROCESS(Type.T_ANY), type)) {			
			Type.Process pt = (Type.Process) type;
			bytecodes.add(new Bytecode.Dup(WHILEYPROCESS));
			JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT);		
			bytecodes.add(new Bytecode.Invoke(WHILEYPROCESS, "state", ftype,
					Bytecode.VIRTUAL));							
			addReadConversion(pt.element(),bytecodes);
			multiStoreHelper(pt.element(),level,fields,indexSlot,val_t,freeSlot,bytecodes);						
			ftype = new JvmType.Function(WHILEYPROCESS,JAVA_LANG_OBJECT);		
			bytecodes.add(new Bytecode.Invoke(WHILEYPROCESS, "setState", ftype,
					Bytecode.VIRTUAL));
			
		} else if(Type.isSubtype(Type.T_DICTIONARY(Type.T_ANY, Type.T_ANY),type)) {
			Type.Dictionary dict = Type.effectiveDictionaryType(type);				
			
			if(level != 0) {				
				bytecodes.add(new Bytecode.Dup(WHILEYMAP));				
				bytecodes.add(new Bytecode.Load(indexSlot,convertType(dict.key())));
				
				JvmType.Function ftype = new JvmType.Function(
						JAVA_LANG_OBJECT, WHILEYMAP, JAVA_LANG_OBJECT);
				bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "get", ftype,
					Bytecode.STATIC));				
				addReadConversion(dict.value(),bytecodes);
				multiStoreHelper(dict.value(),level-1,fields,indexSlot+1,val_t,freeSlot,bytecodes);
				bytecodes.add(new Bytecode.Load(indexSlot,convertType(dict.key())));
				bytecodes.add(new Bytecode.Swap());
			} else {
				bytecodes.add(new Bytecode.Load(indexSlot,convertType(dict.key())));
				bytecodes.add(new Bytecode.Load(indexSlot+1, val_t));	
				addWriteConversion(dict.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.STATIC));			
						
		} else if(Type.isSubtype(Type.T_STRING,type)) {
			
			// assert: level must be zero here
			bytecodes.add(new Bytecode.Load(indexSlot, BIG_INTEGER));
			bytecodes.add(new Bytecode.Load(indexSlot+1, val_t));
			addWriteConversion(Type.T_INT,bytecodes);			

			JvmType.Function ftype = new JvmType.Function(JAVA_LANG_STRING,
					JAVA_LANG_STRING,BIG_INTEGER,T_CHAR);			
			bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "set", ftype,
					Bytecode.STATIC));						
			
		} else if(Type.isSubtype(Type.T_LIST(Type.T_ANY),type)) {
			Type.List list = Type.effectiveListType(type);				
										
			if(level != 0) {
				bytecodes.add(new Bytecode.Dup(WHILEYLIST));											
				bytecodes.add(new Bytecode.Load(indexSlot,BIG_INTEGER));				
				JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
						WHILEYLIST,BIG_INTEGER);
				bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "get", ftype,
						Bytecode.STATIC));				
				addReadConversion(list.element(),bytecodes);
				multiStoreHelper(list.element(),level-1,fields,indexSlot+1,val_t,freeSlot,bytecodes);				
				bytecodes.add(new Bytecode.Load(indexSlot,BIG_INTEGER));
				bytecodes.add(new Bytecode.Swap());
			} else {				
				bytecodes.add(new Bytecode.Load(indexSlot,BIG_INTEGER));
				bytecodes.add(new Bytecode.Load(indexSlot+1, val_t));
				addWriteConversion(list.element(),bytecodes);
			}
			
			JvmType.Function ftype = new JvmType.Function(WHILEYLIST,
					WHILEYLIST,BIG_INTEGER,JAVA_LANG_OBJECT);			
			bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "set", ftype,
					Bytecode.STATIC));							
		} else {
			Type.Record rec = Type.effectiveRecordType(type);			
			String field = fields.next();			
			if(level != 0) {				
				bytecodes.add(new Bytecode.Dup(WHILEYRECORD));				
				bytecodes.add(new Bytecode.LoadConst(field));				
				JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,WHILEYRECORD,JAVA_LANG_STRING);
				bytecodes.add(new Bytecode.Invoke(WHILEYRECORD,"get",ftype,Bytecode.STATIC));
				addReadConversion(rec.fields().get(field),bytecodes);
				multiStoreHelper(rec.fields().get(field),level-1,fields,indexSlot,val_t,freeSlot,bytecodes);				
				bytecodes.add(new Bytecode.LoadConst(field));
				bytecodes.add(new Bytecode.Swap());
			} else {
				bytecodes.add(new Bytecode.LoadConst(field));				
				bytecodes.add(new Bytecode.Load(indexSlot, val_t));
				addWriteConversion(rec.fields().get(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.STATIC));					
		}
	}
	
	public void translate(Code.Return c, int freeSlot,
			ArrayList<Bytecode> bytecodes) {
		if (c.type == Type.T_VOID) {
			bytecodes.add(new Bytecode.Return(null));
		} else {
			bytecodes.add(new Bytecode.Return(convertType(c.type)));
		}
	}

	public void translate(Code.Throw c, int freeSlot,
			ArrayList<Bytecode> bytecodes) {			
		bytecodes.add(new Bytecode.New(WHILEYEXCEPTION));
		bytecodes.add(new Bytecode.DupX1());
		bytecodes.add(new Bytecode.Swap());				
		JvmType.Function ftype = new JvmType.Function(T_VOID,JAVA_LANG_OBJECT);
		bytecodes.add(new Bytecode.Invoke(WHILEYEXCEPTION, "<init>", ftype,
				Bytecode.SPECIAL));		
		bytecodes.add(new Bytecode.Throw());
	}
	
	public void translate(Code.Switch c, Block.Entry entry, int freeSlot,
			ArrayList<Bytecode> bytecodes) {

		ArrayList<Pair<Integer, String>> cases = new ArrayList();
		boolean canUseSwitchBytecode = true;
		for (Pair<Value, String> p : c.branches) {
			// first, check whether the switch value is indeed an integer.
			Value v = (Value) p.first();
			if (!(v instanceof Value.Integer)) {
				canUseSwitchBytecode = false;
				break;
			}
			// second, check whether integer value can fit into a Java int
			Value.Integer vi = (Value.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 Pair(iv, p.second()));
		}

		if (canUseSwitchBytecode) {
			JvmType.Function ftype = new JvmType.Function(T_INT);
			bytecodes.add(new Bytecode.Invoke(BIG_INTEGER, "intValue", ftype,
					Bytecode.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.  
			bytecodes.add(new Bytecode.Store(freeSlot, convertType(c.type)));
			for (Pair<Value, String> p : c.branches) {
				Value value = p.first();
				String target = p.second();
				translate(value,freeSlot+1,bytecodes);
				bytecodes.add(new Bytecode.Load(freeSlot, convertType(c.type)));				
				// Now, construct fake bytecode to do the comparison.
				// FIXME: bug if types require some kind of coercion
				Code.IfGoto ifgoto = Code.IfGoto(value.type(),Code.COp.EQ,target);			
				translate(ifgoto,entry,freeSlot+1,bytecodes);
			}
			bytecodes.add(new Bytecode.Goto(c.defaultTarget));
		}
	}
	
	public void translate(Code.IfGoto c, 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(c.op) {
			case EQ:				
				bytecodes.add(new Bytecode.IfCmp(Bytecode.IfCmp.EQ, type, c.target));
				break;			
			case NEQ:				
				bytecodes.add(new Bytecode.IfCmp(Bytecode.IfCmp.NE, type, c.target));				
				break;			
			}
		} else if(c.type == Type.T_CHAR) {
			int op;
			switch(c.op) {
			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.LT;
				break;
			case GT:				
				op = Bytecode.IfCmp.GT;
				break;
			case GTEQ:				
				op = Bytecode.IfCmp.GE;
				break;
			default:
				syntaxError("unknown if condition encountered",filename,stmt);
				return;
			}
			bytecodes.add(new Bytecode.IfCmp(op, T_CHAR,c.target));
		} else {
			// Non-boolean case. Just use the Object.equals() method, followed
			// by "if" bytecode.			
			int op;
			switch(c.op) {
			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.STATIC));
				} else {
					JvmType.Function ftype = new JvmType.Function(T_BOOL,JAVA_LANG_OBJECT);
					bytecodes.add(new Bytecode.Invoke((JvmType.Clazz)type, "equals", ftype,
							Bytecode.VIRTUAL));								
				}
				op = Bytecode.If.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.STATIC));
				} else {
					JvmType.Function ftype = new JvmType.Function(T_BOOL,
							JAVA_LANG_OBJECT);
					bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
							"equals", ftype, Bytecode.VIRTUAL));
				}
				op = Bytecode.If.EQ;
				break;
			}
			case LT:
			{							
				JvmType.Function ftype = new JvmType.Function(T_INT,type);
				bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "compareTo", ftype,
						Bytecode.VIRTUAL));				
				op = Bytecode.If.LT;			
				break;
			}
			case LTEQ:
			{			
				JvmType.Function ftype = new JvmType.Function(T_INT,type);
				bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
						"compareTo", ftype, Bytecode.VIRTUAL));			
				op = Bytecode.If.LE;
				break;
			}
			case GT:
			{						
				JvmType.Function ftype = new JvmType.Function(T_INT, type);
				bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
						"compareTo", ftype, Bytecode.VIRTUAL));				
				op = Bytecode.If.GT;
				break;
			}
			case GTEQ:
			{						
				JvmType.Function ftype = new JvmType.Function(T_INT,type);
				bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
						"compareTo", ftype, Bytecode.VIRTUAL));				
				op = Bytecode.If.GE;
				break;
			}
			case SUBSETEQ:
			{
				JvmType.Function ftype = new JvmType.Function(T_BOOL,WHILEYSET,WHILEYSET);
				bytecodes.add(new Bytecode.Invoke(WHILEYSET, "subsetEq", ftype,
						Bytecode.STATIC));
				op = Bytecode.If.NE;
				break;
			}
			case SUBSET:
			{
				JvmType.Function ftype = new JvmType.Function(T_BOOL,WHILEYSET,WHILEYSET);
				bytecodes.add(new Bytecode.Invoke(WHILEYSET, "subset", ftype,
						Bytecode.STATIC));
				op = Bytecode.If.NE;
				break;
			}
			case ELEMOF:
			{
				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.INTERFACE));
				op = Bytecode.If.NE;
				break;
			}			
			
			default:
				syntaxError("unknown if condition encountered",filename,stmt);
				return;
			}
			
			// do the jump
			bytecodes.add(new Bytecode.If(op, c.target));
		}
	}
	
	public void translate(Code.IfType c, Entry stmt, int freeSlot,
			HashMap<Value,Integer> constants, ArrayList<Bytecode> bytecodes) {						
		
		if(c.slot >= 0) {
			// 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.slot, convertType(c.type)));
			translateTypeTest(trueLabel, c.type, c.test, stmt, bytecodes, constants);

			Type gdiff = Type.leastDifference(c.type,c.test);			
			bytecodes.add(new Bytecode.Load(c.slot, convertType(c.type)));
			// now, add checkcase
			addReadConversion(gdiff,bytecodes);		
			bytecodes.add(new Bytecode.Store(c.slot,convertType(gdiff)));							
			bytecodes.add(new Bytecode.Goto(exitLabel));
			bytecodes.add(new Bytecode.Label(trueLabel));

			Type glb = Type.greatestLowerBound(c.type, c.test);
			bytecodes.add(new Bytecode.Load(c.slot, convertType(c.type)));
			// now, add checkcase
			addReadConversion(glb,bytecodes);		
			bytecodes.add(new Bytecode.Store(c.slot,convertType(glb)));			
			bytecodes.add(new Bytecode.Goto(c.target));
			bytecodes.add(new Bytecode.Label(exitLabel));
		} else {
			// This is the easy case. We're not updating the type of a local
			// variable; rather we're just type testing a value on the stack.
			translateTypeTest(c.target, c.type, c.test, stmt, bytecodes, constants);
		}
	}
	
	// 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,
			Entry stmt, ArrayList<Bytecode> bytecodes, HashMap<Value,Integer> constants) {		
		
		// First, try for the easy cases
		
		if (test instanceof Type.Null) {
			// Easy case		
			bytecodes.add(new Bytecode.If(Bytecode.If.NULL, trueTarget));
		} else if(test instanceof Type.Bool) {
			bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_BOOLEAN));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
		} else if(test instanceof Type.Char) {
			bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_CHARACTER));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
		} else if(test instanceof Type.Int) {
			bytecodes.add(new Bytecode.InstanceOf(BIG_INTEGER));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
		} else if(test instanceof Type.Real) {
			bytecodes.add(new Bytecode.InstanceOf(BIG_RATIONAL));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
		} else if(test instanceof Type.Strung) {
			bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_STRING));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
			
		} else {
			// Fall-back to an external (recursive) check
			int id;
			Value constant = Value.V_TYPE(test);
			if(constants.containsKey(constant)) {
				id = constants.get(constant);<commit_msg>Working on a bug fix for Char_Valid_2<commit_after>// Copyright (c) 2011, <NAME> (<EMAIL>)
// 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 <NAME> 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.io;

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

import wyjc.attributes.WhileyDefine;
import wyjc.attributes.WhileyType;
import wyjc.attributes.WhileyVersion;
import wyil.*;
import static wyil.util.SyntaxError.*;
import wyil.util.*;
import wyil.lang.*;
import wyil.lang.Code.*;
import static wyil.lang.Block.*;
import wyjc.runtime.BigRational;
import wyjvm.io.BinaryInputStream;
import wyjvm.io.BinaryOutputStream;
import wyjvm.lang.*;
import static wyjvm.lang.JvmTypes.*;

/**
 * The purpose of the class file builder is to construct a jvm class file from a
 * given WhileyFile.
 * 
 * @author djp
 */
public class ClassFileBuilder {
	protected int CLASS_VERSION = 49;
	protected int WHILEY_MINOR_VERSION;
	protected int WHILEY_MAJOR_VERSION;
	protected ModuleLoader loader;	
	protected String filename;
	protected JvmType.Clazz owner;
	
	public ClassFileBuilder(ModuleLoader loader, int whileyMajorVersion, int whileyMinorVersion) {
		this.loader = loader;
		this.WHILEY_MINOR_VERSION = whileyMinorVersion;
		this.WHILEY_MAJOR_VERSION = whileyMajorVersion;
	}

	public ClassFile build(Module module) {
		owner = new JvmType.Clazz(module.id().pkg().toString(),
				module.id().module().toString());
		ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
		modifiers.add(Modifier.ACC_PUBLIC);
		modifiers.add(Modifier.ACC_FINAL);
		ClassFile cf = new ClassFile(49, owner, JAVA_LANG_OBJECT,
				new ArrayList<JvmType.Clazz>(), modifiers);
	
		this.filename = module.filename();
		
		boolean addMainLauncher = false;		
				
		for(Module.ConstDef cd : module.constants()) {	
			// FIXME: this is an ugly hack for now
			ArrayList<BytecodeAttribute> attrs = new ArrayList<BytecodeAttribute>();
			for(Attribute a : cd.attributes()) {
				if(a instanceof BytecodeAttribute) {
					attrs.add((BytecodeAttribute)a);
				}
			}
			WhileyDefine wd = new WhileyDefine(cd.name(),cd.constant(),attrs);
			cf.attributes().add(wd);
		}
		
		for(Module.TypeDef td : module.types()) {
			// FIXME: this is an ugly hack for now
			ArrayList<BytecodeAttribute> attrs = new ArrayList<BytecodeAttribute>();
			for(Attribute a : td.attributes()) {
				if(a instanceof BytecodeAttribute) {
					attrs.add((BytecodeAttribute)a);
				}
			}
			Type t = td.type();			
			WhileyDefine wd = new WhileyDefine(td.name(),t,attrs);
			cf.attributes().add(wd);
		}
		
		HashMap<Value,Integer> constants = new HashMap<Value,Integer>();
		for(Module.Method method : module.methods()) {				
			if(method.name().equals("main")) { 
				addMainLauncher = true;
			}			
			cf.methods().addAll(build(method, constants));			
		}		
		
		if(constants.size() > 0) {
			buildConstants(constants,cf);
		}
				
		if(addMainLauncher) {
			cf.methods().add(buildMainLauncher(owner));
		}
		
		cf.attributes().add(
				new WhileyVersion(WHILEY_MAJOR_VERSION, WHILEY_MINOR_VERSION));
		
		return cf;
	}	
	
	public void buildConstants(HashMap<Value,Integer> constants, ClassFile cf) {		
		ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();
		
		for(Map.Entry<Value,Integer> entry : constants.entrySet()) {			
			Value constant = entry.getKey();
			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,bytecodes);
			bytecodes.add(new Bytecode.PutField(owner, name, type, Bytecode.STATIC));
		}
		
		bytecodes.add(new Bytecode.Return(null));
		
		// now, create static initialiser method
		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
		wyjvm.attributes.Code code = new wyjvm.attributes.Code(bytecodes,new ArrayList(),clinit);
		clinit.attributes().add(code);				
	}
		
	public 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(WHILEYPROCESS);
		codes.add(new Bytecode.Invoke(WHILEYPROCESS,"systemProcess",ft1,Bytecode.STATIC));
		codes.add(new Bytecode.Load(0,strArr));
		JvmType.Function ft2 = new JvmType.Function(WHILEYLIST,
				new JvmType.Array(JAVA_LANG_STRING));
		codes.add(new Bytecode.Invoke(WHILEYUTIL,"fromStringList",ft2,Bytecode.STATIC));
		Type.Fun wyft = Type.T_FUN(WHILEY_SYSTEM_T,
				Type.T_VOID, Type.T_LIST(Type.T_STRING));
		JvmType.Function ft3 = convertFunType(wyft);
		
		// The following is a little bit of hack. Basically we flush the stdout
		// channel on exit
		codes.add(new Bytecode.Invoke(owner, nameMangle("main",wyft), ft3, Bytecode.STATIC));
		ft3 = new JvmType.Function(T_VOID);		
		codes.add(new Bytecode.Invoke(WHILEYIO, "flush", ft3, Bytecode.STATIC));
		codes.add(new Bytecode.Return(null));
		
		wyjvm.attributes.Code code = new wyjvm.attributes.Code(codes,
				new ArrayList(), cm);
		cm.attributes().add(code);
		
		return cm;	
	}
	
	public List<ClassFile.Method> build(Module.Method method,
			HashMap<Value, Integer> constants) {
		ArrayList<ClassFile.Method> methods = new ArrayList<ClassFile.Method>();
		int num = 1;
		for(Module.Case c : method.cases()) {
			methods.add(build(num++,c,method,constants));
		}
		return methods;
	}
	
	public ClassFile.Method build(int caseNum, Module.Case mcase,
			Module.Method method, HashMap<Value,Integer> constants) {		
		
		ArrayList<Modifier> modifiers = new ArrayList<Modifier>();
		if(method.isPublic()) {
			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<Bytecode> codes = translate(mcase,constants);
		wyjvm.attributes.Code code = new wyjvm.attributes.Code(codes,new ArrayList(),cm);
		cm.attributes().add(code);		
		
		return cm;
	}
	
	public ArrayList<Bytecode> translate(Module.Case mcase, HashMap<Value,Integer> constants) {
		ArrayList<Bytecode> bytecodes = new ArrayList<Bytecode>();				
		translate(mcase.body(),mcase.locals().size(),constants,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 *
	 */
	public void translate(Block blk, int freeSlot, HashMap<Value,Integer> constants,
			ArrayList<Bytecode> bytecodes) {
		for (Entry s : blk) {
			freeSlot = translate(s, freeSlot, constants, bytecodes);
		}
	}
	
	public int translate(Entry entry, int freeSlot,
			HashMap<Value,Integer> constants, ArrayList<Bytecode> bytecodes) {
		try {
			Code code = entry.code;
			if(code instanceof Assert) {
				 // translate((Assert)code,freeSlot,bytecodes);
			} else if(code instanceof BinOp) {
				 translate((BinOp)code,entry,freeSlot,bytecodes);
			} else if(code instanceof Convert) {
				 translate((Convert)code,freeSlot,constants,bytecodes);
			} else if(code instanceof Const) {
				translate((Const) code, freeSlot, constants, bytecodes);
			} else if(code instanceof Debug) {
				 translate((Debug)code,freeSlot,bytecodes);
			} else if(code instanceof Destructure) {
				 translate((Destructure)code,freeSlot,bytecodes);
			} else if(code instanceof DictLoad) {
				 translate((DictLoad)code,freeSlot,bytecodes);
			} else if(code instanceof End) {
				 translate((End)code,freeSlot,bytecodes);
			} else if(code instanceof ExternJvm) {
				translate((ExternJvm)code,freeSlot,bytecodes);
			} else if(code instanceof Fail) {
				 translate((Fail)code,freeSlot,bytecodes);
			} else if(code instanceof FieldLoad) {
				 translate((FieldLoad)code,freeSlot,bytecodes);
			} else if(code instanceof ForAll) {
				 freeSlot = translate((ForAll)code,freeSlot,bytecodes);
			} else if(code instanceof Goto) {
				 translate((Goto)code,freeSlot,bytecodes);
			} else if(code instanceof IfGoto) {
				translate((IfGoto) code, entry, freeSlot, bytecodes);
			} else if(code instanceof IfType) {
				translate((IfType) code, entry, freeSlot, constants, bytecodes);
			} else if(code instanceof IndirectInvoke) {
				 translate((IndirectInvoke)code,freeSlot,bytecodes);
			} else if(code instanceof IndirectSend) {
				 translate((IndirectSend)code,freeSlot,bytecodes);
			} else if(code instanceof Invoke) {
				 translate((Invoke)code,freeSlot,bytecodes);
			} else if(code instanceof Invert) {
				 translate((Invert)code,freeSlot,bytecodes);
			} else if(code instanceof Label) {
				translate((Label)code,freeSlot,bytecodes);
			} else if(code instanceof ListAppend) {
				 translate((ListAppend)code,entry,freeSlot,bytecodes);
			} else if(code instanceof ListLength) {
				 translate((ListLength)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SubList) {
				 translate((SubList)code,entry,freeSlot,bytecodes);
			} else if(code instanceof ListLoad) {
				 translate((ListLoad)code,freeSlot,bytecodes);
			} else if(code instanceof Load) {
				 translate((Load)code,freeSlot,bytecodes);
			} else if(code instanceof Loop) {
				 translate((Loop)code,freeSlot,bytecodes);
			} else if(code instanceof Update) {
				 translate((Update)code,freeSlot,bytecodes);
			} else if(code instanceof NewDict) {
				 translate((NewDict)code,freeSlot,bytecodes);
			} else if(code instanceof NewList) {
				 translate((NewList)code,freeSlot,bytecodes);
			} else if(code instanceof NewRecord) {
				 translate((NewRecord)code,freeSlot,bytecodes);
			} else if(code instanceof NewSet) {
				 translate((NewSet)code,freeSlot,bytecodes);
			} else if(code instanceof NewTuple) {
				 translate((NewTuple)code,freeSlot,bytecodes);
			} else if(code instanceof Negate) {
				 translate((Negate)code,freeSlot,bytecodes);
			} else if(code instanceof ProcLoad) {
				 translate((ProcLoad)code,freeSlot,bytecodes);
			} else if(code instanceof Return) {
				 translate((Return)code,freeSlot,bytecodes);
			} else if(code instanceof Skip) {
				// do nothing
			} else if(code instanceof Send) {
				 translate((Send)code,freeSlot,bytecodes);
			} else if(code instanceof SetLength) {
				 translate((SetLength)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SetUnion) {
				 translate((SetUnion)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SetIntersect) {
				 translate((SetIntersect)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SetDifference) {
				 translate((SetDifference)code,entry,freeSlot,bytecodes);
			} else if(code instanceof StringAppend) {
				 translate((StringAppend)code,entry,freeSlot,bytecodes);
			} else if(code instanceof StringLoad) {
				 translate((StringLoad)code,entry,freeSlot,bytecodes);
			} else if(code instanceof StringLength) {
				 translate((StringLength)code,entry,freeSlot,bytecodes);
			} else if(code instanceof SubString) {
				 translate((SubString)code,entry,freeSlot,bytecodes);
			} else if(code instanceof Store) {
				 translate((Store)code,freeSlot,bytecodes);
			} else if(code instanceof Switch) {
				 translate((Switch)code,entry,freeSlot,bytecodes);
			} else if(code instanceof Spawn) {
				 translate((Spawn)code,freeSlot,bytecodes);
			} else if(code instanceof Throw) {
				 translate((Throw)code,freeSlot,bytecodes);
			} else {
				syntaxError("unknown wyil code encountered (" + code + ")", filename, entry);
			}
			
		} catch (SyntaxError ex) {
			throw ex;
		} catch (Exception ex) {		
			syntaxError("internal failure", filename, entry, ex);
		}
		
		return freeSlot;
	}
	
	public void translate(Code.Const c, int freeSlot,
			HashMap<Value,Integer> constants,
			ArrayList<Bytecode> bytecodes) {
		
		Value constant = c.constant;
		if (constant instanceof Value.Rational || constant instanceof Value.Bool
				|| constant instanceof Value.Null || constant instanceof Value.Byte) {
			translate(constant,freeSlot,bytecodes);					
		} else {
			int id;
			if(constants.containsKey(constant)) {
				id = constants.get(constant);
			} else {
				id = constants.size();
				constants.put(constant, id);				
			}
			String name = "constant$" + id;
			JvmType type = convertType(constant.type());
			bytecodes.add(new Bytecode.GetField(owner, name, type, Bytecode.STATIC));
		}		
	}
	
	public void translate(Code.Convert c, int freeSlot,
			HashMap<Value,Integer> constants, ArrayList<Bytecode> bytecodes) {		
		Type toType = c.to;
		Type fromType = c.from;

		if (Type.isomorphic(toType, fromType)) {		
			// do nothing!						
		} else if (!(toType instanceof Type.Bool) && fromType instanceof Type.Bool) {
			// this is either going into a union type, or the any type
			upConversion(toType, (Type.Bool) fromType, freeSlot, bytecodes);
		} else if(fromType == Type.T_INT) {									
			upConversion(toType, (Type.Int)fromType,freeSlot,bytecodes);  
		} else if(fromType == Type.T_CHAR) {									
			upConversion(toType, (Type.Char)fromType,freeSlot,bytecodes);  
		} else if(fromType == Type.T_BYTE) {									
			upConversion(toType, (Type.Byte)fromType,freeSlot,bytecodes); 
		} else if(!Type.isSubtype(toType, fromType)) {
			// Fall-back to an external (recursive) check
			int id;
			Value constant = Value.V_TYPE(toType);
			if(constants.containsKey(constant)) {
				id = constants.get(constant);
			} else {
				id = constants.size();
				constants.put(constant, id);				
			}
			String name = "constant$" + id;

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

			JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,convertType(fromType),WHILEYTYPE);
			bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "coerce",
					ftype, Bytecode.STATIC));			
			addReadConversion(toType,bytecodes);
		}
	}
	
	public void upConversion(Type toType, Type.Bool fromType,
			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.STATIC));			
		// done deal!
	}
	
	public void upConversion(Type toType, Type.Byte fromType,
			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.STATIC));			
		// done deal!
	}
	
	public void upConversion(Type toType, Type.Int fromType,
			int freeSlot, ArrayList<Bytecode> bytecodes) {
		if(!Type.isSubtype(toType,fromType)) {
			Type glb = Type.greatestLowerBound(Type.T_REAL, toType);
			if(glb == Type.T_REAL) { 
				// coercion required!
				JvmType.Function ftype = new JvmType.Function(BIG_RATIONAL,BIG_INTEGER);			
				bytecodes.add(new Bytecode.Invoke(BIG_RATIONAL,"valueOf",ftype,Bytecode.STATIC));
			} else {				
				// must be => char
				JvmType.Function ftype = new JvmType.Function(T_INT);			
				bytecodes.add(new Bytecode.Invoke(BIG_INTEGER,"intValue",ftype,Bytecode.VIRTUAL));				
			}
		}
	}

	public void upConversion(Type toType, Type.Char fromType,
			int freeSlot, ArrayList<Bytecode> bytecodes) {
		if(!Type.isSubtype(toType,fromType)) {					
			if(toType == Type.T_REAL) { 
				// coercion required!
				JvmType.Function ftype = new JvmType.Function(BIG_RATIONAL,T_INT);			
				bytecodes.add(new Bytecode.Invoke(BIG_RATIONAL,"valueOf",ftype,Bytecode.STATIC));
			} else {
				bytecodes.add(new Bytecode.Conversion(T_INT, T_LONG));
				JvmType.Function ftype = new JvmType.Function(BIG_INTEGER,T_LONG);			
				bytecodes.add(new Bytecode.Invoke(BIG_INTEGER,"valueOf",ftype,Bytecode.STATIC));				
			}
		} else {
			JvmType.Function ftype = new JvmType.Function(JAVA_LANG_CHARACTER,T_CHAR);			
			bytecodes.add(new Bytecode.Invoke(JAVA_LANG_CHARACTER,"valueOf",ftype,Bytecode.STATIC));	
		}
	}
	
	public void translate(Code.Store c, int freeSlot,			
			ArrayList<Bytecode> bytecodes) {		
		JvmType type = convertType(c.type);
		//addIncRefs(c.type,bytecodes);		
		bytecodes.add(new Bytecode.Store(c.slot, type));				
	}

	public void translate(Code.Update c, int freeSlot,ArrayList<Bytecode> bytecodes) {
		
		// Now, my general feeling is that the multistore bytecode could use
		// some work. Essentially, to simplify this process of figuring our what
		// is being updated.
		
		// First, check if this is updating the process' state
		Type type = c.type;
				
		if(c.slot == 0 && Type.isSubtype(Type.T_PROCESS(Type.T_ANY), type)) {
			Type.Process p = (Type.Process) type;
			type = p.element();
		}
		
		// Second, determine type of value being assigned
		
		ArrayList<String> fields = c.fields;
		int fi = 0;						
		Type iter = type;
		// ok, this is such an ugly hack...
		ArrayList<Type> indices = new ArrayList<Type>();
		for(int i=0;i!=c.level;++i) {
			if(Type.isSubtype(Type.T_DICTIONARY(Type.T_ANY, Type.T_ANY),iter)) {
				Type.Dictionary dict = Type.effectiveDictionaryType(iter);				
				indices.add(dict.key());
				iter = dict.value();
			} else if(Type.isSubtype(Type.T_STRING,iter)) {
				iter = Type.T_CHAR;
				indices.add(Type.T_INT);
			} else if(Type.isSubtype(Type.T_LIST(Type.T_ANY),iter)) {
				Type.List list = Type.effectiveListType(iter);
				iter = list.element();
				indices.add(Type.T_INT);
			} else {
				Type.Record rec = Type.effectiveRecordType(iter);
				String field = fields.get(fi++);
				iter = rec.fields().get(field);
			}	
		}
						
		int indexSlot = freeSlot;
		freeSlot += indices.size();
		// Third, store the value to be assigned				
		JvmType val_t = convertType(iter);		
		bytecodes.add(new Bytecode.Store(freeSlot++,val_t));
		
		for(int i=indices.size()-1;i>=0;--i) {
			JvmType t = convertType(indices.get(i));
			bytecodes.add(new Bytecode.Store(indexSlot+i,t));
		}
		
		bytecodes.add(new Bytecode.Load(c.slot, convertType(c.type)));
		
		// Fourth, finally process the assignment path and update the object in
		// question.		
		multiStoreHelper(c.type,c.level-1,fields.iterator(),indexSlot,val_t,freeSlot, bytecodes);		
		bytecodes.add(new Bytecode.Store(c.slot, convertType(c.type)));
	}

	public void multiStoreHelper(Type type, int level,
			Iterator<String> fields, int indexSlot, JvmType val_t, int freeSlot, 
			ArrayList<Bytecode> bytecodes) {
		
		// This method is major ugly. I'm sure there must be a better way of
		// doing this. Probably, if I change the multistore bytecode, that would
		// help.
		
		if(Type.isSubtype(Type.T_PROCESS(Type.T_ANY), type)) {			
			Type.Process pt = (Type.Process) type;
			bytecodes.add(new Bytecode.Dup(WHILEYPROCESS));
			JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT);		
			bytecodes.add(new Bytecode.Invoke(WHILEYPROCESS, "state", ftype,
					Bytecode.VIRTUAL));							
			addReadConversion(pt.element(),bytecodes);
			multiStoreHelper(pt.element(),level,fields,indexSlot,val_t,freeSlot,bytecodes);						
			ftype = new JvmType.Function(WHILEYPROCESS,JAVA_LANG_OBJECT);		
			bytecodes.add(new Bytecode.Invoke(WHILEYPROCESS, "setState", ftype,
					Bytecode.VIRTUAL));
			
		} else if(Type.isSubtype(Type.T_DICTIONARY(Type.T_ANY, Type.T_ANY),type)) {
			Type.Dictionary dict = Type.effectiveDictionaryType(type);				
			
			if(level != 0) {				
				bytecodes.add(new Bytecode.Dup(WHILEYMAP));				
				bytecodes.add(new Bytecode.Load(indexSlot,convertType(dict.key())));
				
				JvmType.Function ftype = new JvmType.Function(
						JAVA_LANG_OBJECT, WHILEYMAP, JAVA_LANG_OBJECT);
				bytecodes.add(new Bytecode.Invoke(WHILEYMAP, "get", ftype,
					Bytecode.STATIC));				
				addReadConversion(dict.value(),bytecodes);
				multiStoreHelper(dict.value(),level-1,fields,indexSlot+1,val_t,freeSlot,bytecodes);
				bytecodes.add(new Bytecode.Load(indexSlot,convertType(dict.key())));
				bytecodes.add(new Bytecode.Swap());
			} else {
				bytecodes.add(new Bytecode.Load(indexSlot,convertType(dict.key())));
				bytecodes.add(new Bytecode.Load(indexSlot+1, val_t));	
				addWriteConversion(dict.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.STATIC));			
						
		} else if(Type.isSubtype(Type.T_STRING,type)) {
			
			// assert: level must be zero here
			bytecodes.add(new Bytecode.Load(indexSlot, BIG_INTEGER));
			bytecodes.add(new Bytecode.Load(indexSlot+1, val_t));
			addWriteConversion(Type.T_INT,bytecodes);			

			JvmType.Function ftype = new JvmType.Function(JAVA_LANG_STRING,
					JAVA_LANG_STRING,BIG_INTEGER,T_CHAR);			
			bytecodes.add(new Bytecode.Invoke(WHILEYUTIL, "set", ftype,
					Bytecode.STATIC));						
			
		} else if(Type.isSubtype(Type.T_LIST(Type.T_ANY),type)) {
			Type.List list = Type.effectiveListType(type);				
										
			if(level != 0) {
				bytecodes.add(new Bytecode.Dup(WHILEYLIST));											
				bytecodes.add(new Bytecode.Load(indexSlot,BIG_INTEGER));				
				JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,
						WHILEYLIST,BIG_INTEGER);
				bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "get", ftype,
						Bytecode.STATIC));				
				addReadConversion(list.element(),bytecodes);
				multiStoreHelper(list.element(),level-1,fields,indexSlot+1,val_t,freeSlot,bytecodes);				
				bytecodes.add(new Bytecode.Load(indexSlot,BIG_INTEGER));
				bytecodes.add(new Bytecode.Swap());
			} else {				
				bytecodes.add(new Bytecode.Load(indexSlot,BIG_INTEGER));
				bytecodes.add(new Bytecode.Load(indexSlot+1, val_t));
				addWriteConversion(list.element(),bytecodes);
			}
			
			JvmType.Function ftype = new JvmType.Function(WHILEYLIST,
					WHILEYLIST,BIG_INTEGER,JAVA_LANG_OBJECT);			
			bytecodes.add(new Bytecode.Invoke(WHILEYLIST, "set", ftype,
					Bytecode.STATIC));							
		} else {
			Type.Record rec = Type.effectiveRecordType(type);			
			String field = fields.next();			
			if(level != 0) {				
				bytecodes.add(new Bytecode.Dup(WHILEYRECORD));				
				bytecodes.add(new Bytecode.LoadConst(field));				
				JvmType.Function ftype = new JvmType.Function(JAVA_LANG_OBJECT,WHILEYRECORD,JAVA_LANG_STRING);
				bytecodes.add(new Bytecode.Invoke(WHILEYRECORD,"get",ftype,Bytecode.STATIC));
				addReadConversion(rec.fields().get(field),bytecodes);
				multiStoreHelper(rec.fields().get(field),level-1,fields,indexSlot,val_t,freeSlot,bytecodes);				
				bytecodes.add(new Bytecode.LoadConst(field));
				bytecodes.add(new Bytecode.Swap());
			} else {
				bytecodes.add(new Bytecode.LoadConst(field));				
				bytecodes.add(new Bytecode.Load(indexSlot, val_t));
				addWriteConversion(rec.fields().get(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.STATIC));					
		}
	}
	
	public void translate(Code.Return c, int freeSlot,
			ArrayList<Bytecode> bytecodes) {
		if (c.type == Type.T_VOID) {
			bytecodes.add(new Bytecode.Return(null));
		} else {
			bytecodes.add(new Bytecode.Return(convertType(c.type)));
		}
	}

	public void translate(Code.Throw c, int freeSlot,
			ArrayList<Bytecode> bytecodes) {			
		bytecodes.add(new Bytecode.New(WHILEYEXCEPTION));
		bytecodes.add(new Bytecode.DupX1());
		bytecodes.add(new Bytecode.Swap());				
		JvmType.Function ftype = new JvmType.Function(T_VOID,JAVA_LANG_OBJECT);
		bytecodes.add(new Bytecode.Invoke(WHILEYEXCEPTION, "<init>", ftype,
				Bytecode.SPECIAL));		
		bytecodes.add(new Bytecode.Throw());
	}
	
	public void translate(Code.Switch c, Block.Entry entry, int freeSlot,
			ArrayList<Bytecode> bytecodes) {

		ArrayList<Pair<Integer, String>> cases = new ArrayList();
		boolean canUseSwitchBytecode = true;
		for (Pair<Value, String> p : c.branches) {
			// first, check whether the switch value is indeed an integer.
			Value v = (Value) p.first();
			if (!(v instanceof Value.Integer)) {
				canUseSwitchBytecode = false;
				break;
			}
			// second, check whether integer value can fit into a Java int
			Value.Integer vi = (Value.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 Pair(iv, p.second()));
		}

		if (canUseSwitchBytecode) {
			JvmType.Function ftype = new JvmType.Function(T_INT);
			bytecodes.add(new Bytecode.Invoke(BIG_INTEGER, "intValue", ftype,
					Bytecode.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.  
			bytecodes.add(new Bytecode.Store(freeSlot, convertType(c.type)));
			for (Pair<Value, String> p : c.branches) {
				Value value = p.first();
				String target = p.second();
				translate(value,freeSlot+1,bytecodes);
				bytecodes.add(new Bytecode.Load(freeSlot, convertType(c.type)));				
				// Now, construct fake bytecode to do the comparison.
				// FIXME: bug if types require some kind of coercion
				Code.IfGoto ifgoto = Code.IfGoto(value.type(),Code.COp.EQ,target);			
				translate(ifgoto,entry,freeSlot+1,bytecodes);
			}
			bytecodes.add(new Bytecode.Goto(c.defaultTarget));
		}
	}
	
	public void translate(Code.IfGoto c, 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(c.op) {
			case EQ:				
				bytecodes.add(new Bytecode.IfCmp(Bytecode.IfCmp.EQ, type, c.target));
				break;			
			case NEQ:				
				bytecodes.add(new Bytecode.IfCmp(Bytecode.IfCmp.NE, type, c.target));				
				break;			
			}
		} else if(c.type == Type.T_CHAR) {
			int op;
			switch(c.op) {
			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.LT;
				break;
			case GT:				
				op = Bytecode.IfCmp.GT;
				break;
			case GTEQ:				
				op = Bytecode.IfCmp.GE;
				break;
			default:
				syntaxError("unknown if condition encountered",filename,stmt);
				return;
			}
			bytecodes.add(new Bytecode.IfCmp(op, T_CHAR,c.target));
		} else {
			// Non-boolean case. Just use the Object.equals() method, followed
			// by "if" bytecode.			
			int op;
			switch(c.op) {
			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.STATIC));
				} else {
					JvmType.Function ftype = new JvmType.Function(T_BOOL,JAVA_LANG_OBJECT);
					bytecodes.add(new Bytecode.Invoke((JvmType.Clazz)type, "equals", ftype,
							Bytecode.VIRTUAL));								
				}
				op = Bytecode.If.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.STATIC));
				} else {
					JvmType.Function ftype = new JvmType.Function(T_BOOL,
							JAVA_LANG_OBJECT);
					bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
							"equals", ftype, Bytecode.VIRTUAL));
				}
				op = Bytecode.If.EQ;
				break;
			}
			case LT:
			{							
				JvmType.Function ftype = new JvmType.Function(T_INT,type);
				bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type, "compareTo", ftype,
						Bytecode.VIRTUAL));				
				op = Bytecode.If.LT;			
				break;
			}
			case LTEQ:
			{			
				JvmType.Function ftype = new JvmType.Function(T_INT,type);
				bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
						"compareTo", ftype, Bytecode.VIRTUAL));			
				op = Bytecode.If.LE;
				break;
			}
			case GT:
			{						
				JvmType.Function ftype = new JvmType.Function(T_INT, type);
				bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
						"compareTo", ftype, Bytecode.VIRTUAL));				
				op = Bytecode.If.GT;
				break;
			}
			case GTEQ:
			{						
				JvmType.Function ftype = new JvmType.Function(T_INT,type);
				bytecodes.add(new Bytecode.Invoke((JvmType.Clazz) type,
						"compareTo", ftype, Bytecode.VIRTUAL));				
				op = Bytecode.If.GE;
				break;
			}
			case SUBSETEQ:
			{
				JvmType.Function ftype = new JvmType.Function(T_BOOL,WHILEYSET,WHILEYSET);
				bytecodes.add(new Bytecode.Invoke(WHILEYSET, "subsetEq", ftype,
						Bytecode.STATIC));
				op = Bytecode.If.NE;
				break;
			}
			case SUBSET:
			{
				JvmType.Function ftype = new JvmType.Function(T_BOOL,WHILEYSET,WHILEYSET);
				bytecodes.add(new Bytecode.Invoke(WHILEYSET, "subset", ftype,
						Bytecode.STATIC));
				op = Bytecode.If.NE;
				break;
			}
			case ELEMOF:
			{
				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.INTERFACE));
				op = Bytecode.If.NE;
				break;
			}			
			
			default:
				syntaxError("unknown if condition encountered",filename,stmt);
				return;
			}
			
			// do the jump
			bytecodes.add(new Bytecode.If(op, c.target));
		}
	}
	
	public void translate(Code.IfType c, Entry stmt, int freeSlot,
			HashMap<Value,Integer> constants, ArrayList<Bytecode> bytecodes) {						
		
		if(c.slot >= 0) {
			// 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.slot, convertType(c.type)));
			translateTypeTest(trueLabel, c.type, c.test, stmt, bytecodes, constants);

			Type gdiff = Type.leastDifference(c.type,c.test);			
			bytecodes.add(new Bytecode.Load(c.slot, convertType(c.type)));
			// now, add checkcase
			addReadConversion(gdiff,bytecodes);		
			bytecodes.add(new Bytecode.Store(c.slot,convertType(gdiff)));							
			bytecodes.add(new Bytecode.Goto(exitLabel));
			bytecodes.add(new Bytecode.Label(trueLabel));

			Type glb = Type.greatestLowerBound(c.type, c.test);
			bytecodes.add(new Bytecode.Load(c.slot, convertType(c.type)));
			// now, add checkcase
			addReadConversion(glb,bytecodes);		
			bytecodes.add(new Bytecode.Store(c.slot,convertType(glb)));			
			bytecodes.add(new Bytecode.Goto(c.target));
			bytecodes.add(new Bytecode.Label(exitLabel));
		} else {
			// This is the easy case. We're not updating the type of a local
			// variable; rather we're just type testing a value on the stack.
			translateTypeTest(c.target, c.type, c.test, stmt, bytecodes, constants);
		}
	}
	
	// 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,
			Entry stmt, ArrayList<Bytecode> bytecodes, HashMap<Value,Integer> constants) {		
		
		// First, try for the easy cases
		
		if (test instanceof Type.Null) {
			// Easy case		
			bytecodes.add(new Bytecode.If(Bytecode.If.NULL, trueTarget));
		} else if(test instanceof Type.Bool) {
			bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_BOOLEAN));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
		} else if(test instanceof Type.Char) {
			bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_CHARACTER));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));			
		} else if(test instanceof Type.Int) {
			bytecodes.add(new Bytecode.InstanceOf(BIG_INTEGER));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
		} else if(test instanceof Type.Real) {
			bytecodes.add(new Bytecode.InstanceOf(BIG_RATIONAL));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
		} else if(test instanceof Type.Strung) {
			bytecodes.add(new Bytecode.InstanceOf(JAVA_LANG_STRING));			
			bytecodes.add(new Bytecode.If(Bytecode.If.NE, trueTarget));
			
		} else {
			// Fall-back to an external (recursive) check
			int id;
			Value constant = Value.V_TYPE(test);
			if(constants.containsKey(constant)) {
				id = constants.get(constant);
{'fraction_non_alphanumeric': 0.12395644283121597, 'fraction_numerical': 0.0012424961608264694, 'mean_word_length': 3.8110014104372354, 'pattern_counts': {'":': 0, '<': 142, '<?xml version=': 0, '>': 152, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4719008', 'n_tokens_mistral': 28129, 'n_tokens_neox': 23355, 'n_words': 5021}