summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
blob: e9061af15429ceccac1eca8a4a48551ff241048c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

                                
                           











                                                              







































































































































































































































































                                                                                                                         
                              














                                                                         
                                                                                                           



                                                                             
                                                                                                       










                                                                                                 
                                                             










                                                                                 
                                                                                                  




























































                                                                                            
                                                          





































































                                                                                                 
                                 
































                                                                                           
                                                                             































































                                                                                                                        
                                                













































































































































                                                                                                                  
                         















































































                                                                                         
                              





































































































                                                                                                               
                                                                                                                        



                                                                                 
                                                                                                   
                                
                                                      
                                                    

               
                                                                                                                                                                             
































                                                                                                                    
                                                                   



                                                 
                              







                                                                                                                                                       
                                                              


























                                                                                                                     
                                                                                                                  
















                                                                                               
                                                                                                                                      










                                                                                                                  
                                                                                                                                               
































































                                                                                                                                             
                                               



                                                            
                                           
                                
                                           




                                                                                            
                                       


































































































                                                                                                   
                                                                


































                                                                                    
                                      

















































                                                                                  
                                      

























































                                                                                     
                                         






















































                                                                                                   
                                                                                        



                                                                                                  

                                                          





























































































                                                                                                   
                             
                                                   
                            



                                                      
                    

























                                                                                     
                                      

































                                                                                       

                                                   



























                                                                              
                                                                   

















































                                                                                       
                                         




































































































                                                                                  
                                                     













                                                              
                         


                                                            
                                  




                                         
                          


                                                                 
                                  













































                                                                                                         
                                                      

















































                                                                                
                                                  






































                                                                                     
                              












                                                                            
                                                                         






























































                                                                                  
                                                          























                                                                                     
                                     










                                                                                      
                                          












                                                                                      
                                                           







                                                                                          
                                                          




























                                                                                          
                                                         












































































                                                                                                         

                                                                                  





                                                                                               
                                              



















































                                                                                       

                                                                               





                                                                                   
                                          










                                                                                    
                                            










                                                 
                                                                                     








                                                                       
                                                                                       








































































































































































































                                                                                                                  
/* NSC -- new scala compiler
 * Copyright 2005-2007 LAMP/EPFL
 * @author Nikolay Mihaylov
 */

// $Id$

package scala.tools.nsc.backend.msil

import java.io.File
import java.nio.{ByteBuffer, ByteOrder}

import scala.collection.mutable.{Map, HashMap, HashSet, Stack}
import scala.tools.nsc.symtab._
import scala.tools.nsc.util.Position

import ch.epfl.lamp.compiler.msil.{Type => MsilType, _}
import ch.epfl.lamp.compiler.msil.emit._

/**
 */
abstract class GenMSIL extends SubComponent {
  import global._
  import loaders.clrTypes
  import clrTypes.{types, constructors, methods, fields}
  import icodes._
  import icodes.opcodes._

  /** Create a new phase */
  override def newPhase(p: Phase) = new MsilPhase(p)

  val phaseName = "msil"
  /** MSIL code generation phase
   */
  class MsilPhase(prev: Phase) extends GlobalPhase(prev) {
    def name = phaseName
    override def newFlags = phaseNewFlags

    override def erasedTypes = true

    override def run: Unit = {
      if (settings.debug.value) inform("[running phase " + name + " on icode]")

      val codeGenerator = new BytecodeGenerator

      //classes is ICodes.classes, a HashMap[Symbol, IClass]
      classes.values foreach codeGenerator.findEntryPoint

      codeGenerator.initAssembly

      classes.values foreach codeGenerator.createTypeBuilder
      classes.values foreach codeGenerator.createClassMembers


      try {
        classes.values foreach codeGenerator.genClass
      } finally {
        codeGenerator.writeAssembly
      }
    }

    override def apply(unit: CompilationUnit): Unit =
      abort("MSIL works on icode classes, not on compilation units!")
  }

  /**
   * MSIL bytecode generator.
   *
   */
  class BytecodeGenerator {

    val MODULE_INSTANCE_NAME = "MODULE$"

    import clrTypes.{VOID => MVOID, BOOLEAN => MBOOL, UBYTE => MBYTE, SHORT => MSHORT,
                   CHAR => MCHAR, INT => MINT, LONG => MLONG, FLOAT => MFLOAT,
                   DOUBLE => MDOUBLE, OBJECT => MOBJECT, STRING => MSTRING,
                   STRING_ARRAY => MSTRING_ARRAY, SCALA_SYMTAB_ATTR => SYMTAB_ATTRIBUTE,
                   SYMTAB_CONSTR => SYMTAB_ATTRIBUTE_CONSTRUCTOR,
                   SYMTAB_DEFAULT_CONSTR => SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR}

    val EXCEPTION = clrTypes.getType("System.Exception")
    val MBYTE_ARRAY = clrTypes.mkArrayType(MBYTE)

    val ICLONEABLE = clrTypes.getType("System.ICloneable")
    val MEMBERWISE_CLONE = MOBJECT.GetMethod("MemberwiseClone", MsilType.EmptyTypes)

    val MMONITOR       = clrTypes.getType("System.Threading.Monitor")
    val MMONITOR_ENTER = MMONITOR.GetMethod("Enter", Array(MOBJECT))
    val MMONITOR_EXIT  = MMONITOR.GetMethod("Exit", Array(MOBJECT))

    val MSTRING_BUILDER = clrTypes.getType("System.Text.StringBuilder")
    val MSTRING_BUILDER_CONSTR = MSTRING_BUILDER.GetConstructor(MsilType.EmptyTypes)
    val MSTRING_BUILDER_TOSTRING = MSTRING_BUILDER.GetMethod("ToString",
							     MsilType.EmptyTypes)

    val TYPE_FROM_HANDLE =
      clrTypes.getType("System.Type").GetMethod("GetTypeFromHandle", Array(clrTypes.getType("System.RuntimeTypeHandle")))

    val INT_PTR = clrTypes.getType("System.IntPtr")

    val JOBJECT = definitions.ObjectClass
    val JSTRING = definitions.StringClass

    var JSTRING_SUBSTRING_INT_INT: Symbol = _

    val SystemConvert = clrTypes.getType("System.Convert")

    val objParam = Array(MOBJECT)

//     val toBool:   MethodInfo = SystemConvert.GetMethod("ToBoolean", objParam)
    val toByte:   MethodInfo = SystemConvert.GetMethod("ToByte", objParam)
    val toShort:  MethodInfo = SystemConvert.GetMethod("ToInt16", objParam)
    val toChar:   MethodInfo = SystemConvert.GetMethod("ToChar", objParam)
    val toInt:    MethodInfo = SystemConvert.GetMethod("ToInt32", objParam)
    val toLong:   MethodInfo = SystemConvert.GetMethod("ToInt64", objParam)
    val toFloat:  MethodInfo = SystemConvert.GetMethod("ToSingle", objParam)
    val toDouble: MethodInfo = SystemConvert.GetMethod("ToDouble", objParam)

    //val boxedUnit: FieldInfo = msilType(definitions.BoxedUnitModule.info).GetField("UNIT")
    val boxedUnit: FieldInfo = fields(definitions.BoxedUnit_UNIT)

    // Scala attributes
    // symtab.Definitions -> object (singleton..)
    val SerializableAttr = definitions.SerializableAttr.tpe
    val CloneableAttr    = definitions.getClass("scala.cloneable").tpe
    val TransientAtt     = definitions.getClass("scala.transient").tpe
    // remoting: the architectures are too different, no mapping (no portable code
    // possible)

    // java instance methods that are mapped to static methods in .net
    // these will need to be called with OpCodes.Call (not Callvirt)
    val dynToStatMapped: HashSet[Symbol] = new HashSet()

    initMappings()
    // ********************************************************************
    // Create the mappings
    private def initMappings(): Unit = {
      mapType(definitions.AnyClass, MOBJECT)
      mapType(definitions.AnyRefClass, MOBJECT)
      //mapType(definitions.AllRefClass, clrTypes.getType("scala.AllRef$"))
      //mapType(definitions.AllClass, clrTypes.getType("scala.All$"))
      // FIXME: for some reason the upper two lines map to null
      mapType(definitions.AllRefClass, EXCEPTION)
      mapType(definitions.AllClass, EXCEPTION)

      val jEmpty = new Array[Type](0)
      val jString1 = Array(JSTRING.tpe)
      val jInt1 = Array(definitions.IntClass.tpe)
      val jInt2 = Array(definitions.IntClass.tpe, definitions.IntClass.tpe)
      val jLong1 = Array(definitions.LongClass.tpe)
      val jStringInt = Array(JSTRING.tpe, definitions.IntClass.tpe)
      val jChar2 = Array(definitions.CharClass.tpe, definitions.CharClass.tpe)

      val mObject1 = Array(MOBJECT)
      val mString1 = Array(MSTRING)
      val mString2 = Array(MSTRING, MSTRING)
      val mChar1 = Array(MCHAR)
      val mCharInt = Array(MCHAR, MINT)

      JSTRING_SUBSTRING_INT_INT = lookupMethod(JSTRING, "substring", jInt2)

      mapMethod(JOBJECT, "clone", MOBJECT, "MemberwiseClone")
      mapMethod(JOBJECT, nme.equals_, MOBJECT, "Equals")
      mapMethod(JOBJECT, nme.hashCode_, MOBJECT, "GetHashCode")
      mapMethod(JOBJECT, nme.toString_, MOBJECT, "ToString")
      mapMethod(JOBJECT, nme.finalize_, MOBJECT, "Finalize")
      mapMethod(JOBJECT, nme.wait_, jEmpty, MMONITOR, "Wait", mObject1)
      mapMethod(JOBJECT, nme.wait_, jLong1, MMONITOR, "Wait", Array(MOBJECT, MINT))
      mapMethod(JOBJECT, nme.notify_, jEmpty, MMONITOR, "Pulse", mObject1)
      mapMethod(JOBJECT, nme.notifyAll_, jEmpty, MMONITOR, "PulseAll", mObject1)

      mapMethod(JSTRING, "compareTo",MSTRING, "CompareTo")
      mapMethod(JSTRING, "length", MSTRING, "get_Length")
      mapMethod(JSTRING, "charAt", MSTRING, "get_Chars")

      mapMethod(JSTRING, "concat", jString1, MSTRING, "Concat", mString2)
      mapMethod(JSTRING, "indexOf", jInt1, MSTRING, "IndexOf", mChar1)
      mapMethod(JSTRING, "indexOf", jInt2, MSTRING, "IndexOf", mCharInt)

      mapMethod(JSTRING, "indexOf", jString1, MSTRING, "IndexOf")
      mapMethod(JSTRING, "indexOf", jStringInt, MSTRING, "IndexOf")
      mapMethod(JSTRING, "lastIndexOf", jInt1, MSTRING, "LastIndexOf", mChar1)
      mapMethod(JSTRING, "lastIndexOf", jInt2, MSTRING, "LastIndexOf", mCharInt)
      mapMethod(JSTRING, "lastIndexOf", jString1, MSTRING, "LastIndexOf")
      mapMethod(JSTRING, "lastIndexOf", jStringInt, MSTRING, "LastIndexOf")

      mapMethod(JSTRING, "toLowerCase", jEmpty, MSTRING, "ToLower")
      mapMethod(JSTRING, "toUpperCase", jEmpty, MSTRING, "ToUpper")
      mapMethod(JSTRING, "startsWith", jString1, MSTRING, "StartsWith")
      mapMethod(JSTRING, "endsWith", jString1, MSTRING, "EndsWith")
      mapMethod(JSTRING, "substring", jInt1, MSTRING, "Substring")
      mapMethod(JSTRING, "substring", jInt2, MSTRING, "Substring")
      mapMethod(JSTRING, "trim", jEmpty, MSTRING, "Trim")
      mapMethod(JSTRING, "intern", jEmpty, MSTRING, "Intern", mString1)
      mapMethod(JSTRING, "replace", jChar2, MSTRING, "Replace")
      mapMethod(JSTRING, "toCharArray", MSTRING, "ToCharArray")

      mapType(definitions.BooleanClass, MBOOL)
      mapType(definitions.ByteClass, MBYTE)
      mapType(definitions.ShortClass, MSHORT)
      mapType(definitions.CharClass, MCHAR)
      mapType(definitions.IntClass, MINT)
      mapType(definitions.LongClass, MLONG)
      mapType(definitions.FloatClass, MFLOAT)
      mapType(definitions.DoubleClass, MDOUBLE)

    }

    var clasz: IClass = _
    var method: IMethod = _
    var code: Code = _

    var massembly: AssemblyBuilder = _
    var mmodule: ModuleBuilder = _
    var mcode: ILGenerator = _

    var assemName : String = _
    var firstSourceName = ""
    var outDir : File = _

    def initAssembly(): Unit = {

      assemName = settings.assemname.value
      if (assemName == "") {

	if (entryPoint != null) {
	  assemName = msilName(entryPoint.enclClass)
	  // remove the $ at the end (from module-name)
	  assemName = assemName.substring(0, assemName.length() - 1)
	} else {
	  // assuming filename of first source file
	  assert(firstSourceName.endsWith(".scala"), "Source file doesn't end with .scala")
	  assemName = firstSourceName.substring(0, firstSourceName.length() - 6)
	}
      } else {
	if (assemName.endsWith(".msil"))
	  assemName = assemName.substring(0, assemName.length()-5)
	if (assemName.endsWith(".il"))
	  assemName = assemName.substring(0, assemName.length()-3)
	val f: File = new File(assemName)
	outDir = f.getParentFile()
	assemName = f.getName()
      }
      if (outDir == null)
	outDir = new File(".")


      val assemblyName = new AssemblyName()
      assemblyName.Name = assemName
      massembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName)

      val moduleName = assemName + (if (entryPoint == null) ".dll" else ".exe")
      // filename here: .dll or .exe (in both parameters), second: give absolute-path
      mmodule = massembly.DefineDynamicModule(moduleName,
					      new File(outDir, moduleName).getAbsolutePath())
      assert (mmodule != null)
      initMappings()

    }


    /**
     * Form of the custom Attribute parameter (Ecma-335.pdf)
     *      - p. 163 for CustomAttrib Form,
     *      - p. 164 for FixedArg Form (Array and Element) (if array or not is known!)
     *  !! least significant *byte* first if values longer than one byte !!
     *
     * 1: Prolog (unsigned int16, value 0x0001) -> symtab[0] = 0x01, symtab[1] = 0x00
     * 2: FixedArgs (directly the data, get number and types from related constructor)
     *  2.1: length of the array (unsigned int32, take care on order of the 4 bytes)
     *  2.2: the byte array data
     * 3: NumNamed (unsigned int16, number of named fields and properties, 0x0000)
     *
     **/
    def addSymtabAttribute(sym: Symbol, tBuilder: TypeBuilder): Unit = currentRun.symData.get(sym) match {
      case Some(pickle) =>
	val symtab: Array[Byte] = new Array[Byte](pickle.writeIndex + 8)
	symtab(0) = 1.toByte
	var size:Int = pickle.writeIndex
	for (i <- 2 until 6) {
	  symtab(i) = (size & 0xff).toByte
	  size = size >> 8
	}

	System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex)

	tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab)

        currentRun.symData -= sym
	currentRun.symData -= sym.linkedSym
	//log("Generated ScalaSig Attr for " + sym)//debug
      case _ =>
        log("Could not find pickle information for " + sym)
    }

    def addAttributes(member: ICustomAttributeSetter, attributes: List[AnnotationInfo[Constant]]): Unit = {
      return // FIXME

      if (settings.debug.value)
	log("creating attributes: " + attributes + " for member : " + member)
      for (AnnotationInfo(typ, consts, nvPairs) <- attributes /* !typ.symbol.hasFlag(Flags.JAVA) */ ) {
//	assert(consts.length <= 1,
//	       "too many constant arguments for attribute; "+consts.toString())

	// Problem / TODO having the symbol of the attribute type would be nicer
	// (i hope that type.symbol is the same as the one in types2create)
	// AND: this will crash if the attribute Type is already compiled (-> not a typeBuilder)
	// when this is solved, types2create will be the same as icodes.classes, thus superfluous
	val attrType: TypeBuilder = getType(typ.symbol).asInstanceOf[TypeBuilder]
//	val attrType: MsilType = getType(typ.symbol)

	// Problem / TODO: i have no idea which constructor is used. This
	// information should be available in AnnotationInfo.
	attrType.CreateType() // else, GetConstructors can't be used
	val constr: ConstructorInfo = attrType.GetConstructors()(0)
	// prevent a second call of CreateType, only needed because there's no
	// otehr way than GetConstructors()(0) to get the constructor, if there's
	// no constructor symbol available.

	val args: Array[Byte] = getAttributeArgs(consts, nvPairs)
	member.SetCustomAttribute(constr, args)
      }
    }

    def getAttributeArgs(consts: List[Constant], nvPairs: List[(Name, Constant)]): Array[Byte] = {
      val buf = ByteBuffer.allocate(2048) // FIXME: this may be not enough!
      buf.order(ByteOrder.LITTLE_ENDIAN)
      buf.putShort(1.toShort) // signature

      def emitSerString(str: String) = {
	  // this is wrong, it has to be the length of the UTF-8 byte array, which
	  // may be longer (see clr-book on page 302)
//	  val length: Int = str.length
	    val strBytes: Array[Byte] = try {
	      str.getBytes("UTF-8")
	    } catch {
	      case _: Error => abort("could not get byte-array for string: " + str)
	    }
	    val length: Int = strBytes.length //this length is stored big-endian
	    if (length < 128)
	      buf.put(length.toByte)
	    else if (length < (1<<14)) {
	      buf.put(((length >> 8) | 0x80).toByte) // the bits 14 and 15 of length are '0'
	      buf.put((length | 0xff).toByte)
	    } else if (length < (1 << 29)) {
              buf.put(((length >> 24) | 0xc0).toByte)
              buf.put(((length >> 16) & 0xff).toByte)
              buf.put(((length >>  8) & 0xff).toByte)
              buf.put(((length      ) & 0xff).toByte)
	    } else
	      abort("string too long for attribute parameter: " + length)
	    buf.put(strBytes)
      }

      def emitConst(const: Constant): Unit = const.tag match {
	case BooleanTag => buf.put((if (const.booleanValue) 1 else 0).toByte)
	case ByteTag => buf.put(const.byteValue)
	case ShortTag => buf.putShort(const.shortValue)
	case CharTag => buf.putChar(const.charValue)
	case IntTag => buf.putInt(const.intValue)
	case LongTag => buf.putLong(const.longValue)
	case FloatTag => buf.putFloat(const.floatValue)
	case DoubleTag => buf.putDouble(const.doubleValue)
	case StringTag =>
	  val str: String = const.stringValue
	  if (str == null) {
	    buf.put(0xff.toByte)
	  } else {
	    emitSerString(str)
	  }
	case ArrayTag =>
	  val arr: Array[Constant] = const.arrayValue
	  if (arr == null) {
	    buf.putInt(0xffffffff)
	  } else {
	    buf.putInt(arr.length)
	    arr.foreach(emitConst)
	  }

	// TODO: other Tags: NoTag, UnitTag, ClassTag, EnumTag, ArrayTag ???

	case _ => abort("could not handle attribute argument: " + const)
      }

      consts foreach emitConst
      buf.putShort(nvPairs.length.toShort)
      def emitNamedArg(nvPair: (Name, Constant)): Unit = {
	// the named argument is a property of the attribute (it can't be a field, since
	//  all fields in scala are private)
	buf.put(0x54.toByte)

	def emitType(c: Constant) = c.tag match { // type of the constant, Ecma-335.pdf, page 151
	  case BooleanTag => buf.put(0x02.toByte)
	  case ByteTag =>    buf.put(0x05.toByte)
	  case ShortTag =>   buf.put(0x06.toByte)
	  case CharTag =>    buf.put(0x07.toByte)
	  case IntTag =>     buf.put(0x08.toByte)
	  case LongTag =>    buf.put(0x0a.toByte)
	  case FloatTag =>   buf.put(0x0c.toByte)
	  case DoubleTag =>  buf.put(0x0d.toByte)
	  case StringTag =>  buf.put(0x0e.toByte)

	  // TODO: other Tags: NoTag, UnitTag, ClassTag, EnumTag ???

	  // ArrayTag falls in here
	  case _ => abort("could not handle attribute argument: " + c)
	}

	val cnst: Constant = nvPair._2
	if (cnst.tag == ArrayTag) {
	  buf.put(0x1d.toByte)
	  emitType(cnst.arrayValue(0)) // FIXME: will crash if array length = 0
	} else if (cnst.tag == EnumTag) {
	  buf.put(0x55.toByte)
	  // TODO: put a SerString (don't know what exactly, names of the enums somehow..)
      	} else {
	  buf.put(0x51.toByte)
	  emitType(cnst)
	}

	emitSerString(nvPair._1.toString)
	emitConst(nvPair._2)
      }

      val length = buf.position()
      buf.array().subArray(0, length)
    }

    def writeAssembly(): Unit = {
      if (entryPoint != null) {
	assert(entryPoint.enclClass.isModuleClass, "main-method not defined in a module")
	val mainMethod = methods(entryPoint)
	val stringArrayTypes: Array[MsilType] = Array(MSTRING_ARRAY)
	val globalMain = mmodule.DefineGlobalMethod(
	  "Main", MethodAttributes.Public | MethodAttributes.Static,
	  MVOID, stringArrayTypes)
	globalMain.DefineParameter(0, ParameterAttributes.None, "args")
	massembly.SetEntryPoint(globalMain)
	val code = globalMain.GetILGenerator()
	val moduleField = getModuleInstanceField(entryPoint.enclClass)
	code.Emit(OpCodes.Ldsfld, moduleField)
	code.Emit(OpCodes.Ldarg_0)
	code.Emit(OpCodes.Callvirt, mainMethod)
	code.Emit(OpCodes.Ret)
      }
      createTypes()
      val filename = new File(outDir, assemName + ".msil").getPath()
      if (settings.debug.value)
	log("output file name: " + filename)
      try {
	massembly.Save(filename)
      } catch {
	case _: Error => abort("Could not save file " + filename)
      }
    }

    private def createTypes(): Unit =
      for (sym <- classes.keys) {
	if (settings.debug.value)
	  log("Calling CreatType for " +  sym + ", " + types(sym))
	types(sym).asInstanceOf[TypeBuilder].CreateType()
      }

    private[GenMSIL] def genClass(iclass: IClass): Unit = {
      val sym = iclass.symbol
      if (settings.debug.value)
        log("Generating class " + sym + " flags: " + Flags.flagsToString(sym.flags))
      clasz = iclass

      val tBuilder = getType(sym).asInstanceOf[TypeBuilder]
      if (isCloneable(sym)){
	// FIXME: why there's no nme.clone_ ?
	// "Clone": if the code is non-portable, "Clone" is defined, not "clone"
        // TODO: improve condition (should override AnyRef.clone)
	if (iclass.methods.forall(m => {
	  !((m.symbol.name.toString() != "clone" || m.symbol.name.toString() != "Clone") &&
	    m.symbol.tpe.paramTypes.length != 0)
	})) {
	  if (settings.debug.value)
	    log("auto-generating cloneable method for " + sym)
	  val attrs: Short = (MethodAttributes.Public | MethodAttributes.Virtual |
			      MethodAttributes.HideBySig)
	  val cloneMethod = tBuilder.DefineMethod("Clone", attrs, MOBJECT,
						  MsilType.EmptyTypes)
	  val clCode = cloneMethod.GetILGenerator()
	  clCode.Emit(OpCodes.Ldarg_0)
	  clCode.Emit(OpCodes.Call, MEMBERWISE_CLONE)
	  clCode.Emit(OpCodes.Ret)
	}
      }

      tBuilder.setPosition((sym.pos).line.get, iclass.cunit.source.file.name)

      if (isTopLevelModule(sym)) {
	if (settings.debug.value)
	  log("TopLevelModule: " + sym)
	if (sym.linkedClassOfModule == NoSymbol) {
	  if (settings.debug.value)
	    log(" no linked class: " + sym)
	  dumpMirrorClass(sym)
	} else if (!currentRun.compiles(sym.linkedClassOfModule)) {
	  if (settings.debug.value)
	    log(" not compiling linked class: " + sym)
	  dumpMirrorClass(sym)
	}
      }

      // the pickling info is not written to the module class, but to it's
      // linked class (the mirror class eventually dumped)
      if (!(tBuilder.Name.endsWith("$") && sym.isModuleClass)){
	// think the if inside could be removed, because in this case, addSymtabAttribute is
	// called in the dumpMirrorClass method
	addSymtabAttribute(if (isTopLevelModule(sym)) sym.sourceModule else sym, tBuilder)

	// TODO: remove; check the above think:
	assert(!isTopLevelModule(sym), "can't remove the 'if'")
      }

      addAttributes(tBuilder, sym.attributes)

      if (iclass.symbol != definitions.ArrayClass)
        iclass.methods foreach genMethod

    } //genClass


    private def genMethod(m: IMethod): Unit = {
      if (settings.debug.value)
	log("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) +
	    " owner: " + m.symbol.owner)
      method = m
      localBuilders.clear
      computeLocalVarsIndex(m)

      if (m.symbol.isClassConstructor){
	mcode = constructors(m.symbol).asInstanceOf[ConstructorBuilder].GetILGenerator()
      } else {
	val mBuilder = methods(m.symbol).asInstanceOf[MethodBuilder]
	if (!mBuilder.IsAbstract())
          try {
	    mcode = mBuilder.GetILGenerator()
          } catch {
            case e: Exception =>
              System.out.println("m.symbol       = " + Flags.flagsToString(m.symbol.flags) + " " + m.symbol)
              System.out.println("m.symbol.owner = " + Flags.flagsToString(m.symbol.owner.flags) + " " + m.symbol.owner)
              System.out.println("mBuilder       = " + mBuilder)
              System.out.println("mBuilder.DeclaringType = " +
                                 TypeAttributes.toString(mBuilder.DeclaringType.Attributes) +
                                 "::" + mBuilder.DeclaringType)
              throw e
          }
	  else
	    mcode = null
      }

      if (mcode != null) {
	for (local <- m.locals.diff(m.params)) {
	  if (settings.debug.value)
	    log("add local var: " + local + ", of kind " + local.kind)
	  val t: MsilType = msilType(local.kind)
	  val localBuilder = mcode.DeclareLocal(t)
	  localBuilder.SetLocalSymInfo(msilName(local.sym))
	  localBuilders(local) = localBuilder
	}
	genCode(m)
      }

    }

    var linearization: List[BasicBlock] = Nil
    // a "ret" instruction is needed (which is not present in
    //  icode) if there's no code after a try-catch block
    var needAdditionalRet: Boolean = false

    def genCode(m: IMethod): Unit = {

      code = m.code

      labels.clear
      linearization = linearizer.linearize(m)
      val orderedBlocks = (if (m.exh != Nil) orderBlocksForExh(linearization, m.exh)
			   else linearization)

      makeLabels(orderedBlocks) // orderBlocksForExh may create new Blocks -> new Labels
      genBlocks(orderedBlocks)
      if (needAdditionalRet) {
	mcode.Emit(OpCodes.Ret)
	needAdditionalRet = false
      }
    }

    abstract class ExHInstruction(handler: ExceptionHandler) { }
    case class BeginExceptionBlock(handler: ExceptionHandler) extends ExHInstruction(handler)
    case class BeginCatchBlock(handler: ExceptionHandler, exceptionType: MsilType) extends ExHInstruction(handler)
    case class BeginFinallyBlock(handler: ExceptionHandler) extends ExHInstruction(handler)
    case class EndExceptionBlock(handler: ExceptionHandler) extends ExHInstruction(handler)


    abstract class Block {
      var closed: Boolean = false
      def parentBlockList: Option[BlockList0]
      def firstBasicBlock: BasicBlock
      def lastBasicBlock: BasicBlock
//      def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock]
      def close(): Unit
/*      protected def findExceptionBlock(list: List[Block], exh: ExceptionHandler): Option[ExceptionBlock] = {
	var res: Option[ExceptionBlock] = None
	var i: Int = 0
	while (i < list.length && res == None) {
	  val b = list(i)
	  val exB = b.getExceptionBlock(exh)
	  exB match {
	    case some: Some[ExceptionBlock] => res = some
	    case None => ()
	  }
	  i = i + 1
	}
	res
      } */
    }
    case class CodeBlock(parent: BlockList0) extends Block {
      var basicBlocks: List[BasicBlock] = Nil
      def isEmpty = basicBlocks.isEmpty
      override def firstBasicBlock: BasicBlock = {
	if(isEmpty) null
	else {
	  if (closed) basicBlocks.head
	  else basicBlocks.last
	}
      }
      override def lastBasicBlock: BasicBlock = {
	if(isEmpty) null
	else {
	  if (closed) basicBlocks.last
	  else basicBlocks.head
	}
      }
      override def parentBlockList = Some(parent)
//      override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = None
      override def close(): Unit = {
	basicBlocks = basicBlocks.reverse
	closed = true
      }
      override def toString() = {
	var res = ""
	res = res + TopBlock.indent + "CodeBlock(" + basicBlocks + ")\n"
	res
      }
    }
    abstract class BlockList0 extends Block {
      var blocks: List[Block] = Nil
      override def firstBasicBlock: BasicBlock = {
	if(blocks.isEmpty) null
	else {
	  if (closed) blocks.head.firstBasicBlock
	  else blocks.last.firstBasicBlock
	}
      }
      override def lastBasicBlock: BasicBlock = {
	if(blocks.isEmpty) null
	else {
	  if (closed) blocks.last.lastBasicBlock
	  else blocks.head.lastBasicBlock
	}
      }
/*      override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = {
	findExceptionBlock(blocks, exh)
      } */
      def addExceptionBlock(exh: ExceptionHandler) = {
	if (settings.debug.value)
	  log("new exc block with " + exh + " to " + this)
	val e = new ExceptionBlock(this, exh)
	blocks = e :: blocks
	e
      }
      def addBasicBlock(bb: BasicBlock) = {
	if (settings.debug.value)
	  log("adding bb " + bb + " to " + this)
	var cb: CodeBlock = if (!blocks.isEmpty) {
	  blocks.head match {
	    case blk: CodeBlock => blk
	    case _ => null
	  }
	} else null
	if (cb == null) {
	  cb = new CodeBlock(this)
	  blocks = cb :: blocks
	}
	cb.basicBlocks = bb :: cb.basicBlocks
      }
      override def close(): Unit = {
	blocks.foreach(.close)
	blocks = blocks.reverse
	closed = true
      }
      override def toString() = {
	var res = ""
	res = res + TopBlock.indent + "BlockList0:\n"
	TopBlock.indent = TopBlock.indent + "  "
	for (b <- blocks)
	  res = res + b + "\n"
	TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-2)
	res
      }
    }
    case class BlockList(parent: Block) extends BlockList0 {
      override def parentBlockList: Option[BlockList0] = {
	if (parent == TopBlock)
	  Some(TopBlock)
	else parent match {
	  case bl: BlockList => Some(bl)
	  case cb: CatchBlock => Some(cb)
	  case _ => parent.parentBlockList
	}
      }
      override def toString() = {
	var res = ""
	res = res + TopBlock.indent + "BlockList:\n"
	res = res + super.toString()
	res
      }
    }
    case class ExceptionBlock(parent: Block, handler: ExceptionHandler) extends Block {
      var tryBlock: BlockList = new BlockList(this)
      var catchBlocks: List[CatchBlock] = Nil
      var finallyBlock: BlockList = new BlockList(this)
      override def firstBasicBlock = {
	tryBlock.firstBasicBlock
      }
      override def lastBasicBlock = {
	if (!finallyBlock.blocks.isEmpty)
	  finallyBlock.lastBasicBlock
	else if(!catchBlocks.isEmpty) {
	  if (closed) catchBlocks.last.lastBasicBlock
	  else catchBlocks.head.lastBasicBlock
	} else {
	  tryBlock.lastBasicBlock
	}
      }
      override def parentBlockList: Option[BlockList0] = {
	if (parent == TopBlock)
	  Some(TopBlock)
	else parent match {
	  case bl: BlockList => Some(bl)
	  case cb: CatchBlock => Some(cb)
	  case _ => parent.parentBlockList
	}
      }
/*      override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = {
	if (exh == handler) Some(this)
	else {
	  val t = if (tryBlock == null) Nil else List(tryBlock)
	  val f = if (finallyBlock == null) Nil else List(finallyBlock)
	  findExceptionBlock(t ::: catchBlocks ::: f, exh)
	}
      }
*/
      def addCatchBlock(exSym: Symbol): CatchBlock = {
	if (settings.debug.value)
	  log("new catch block with " + exSym + " to " + this)
	val c = new CatchBlock(this, exSym)
	catchBlocks = c :: catchBlocks
	c
      }
      override def close(): Unit = {
	tryBlock.close
	catchBlocks.foreach(.close)
	catchBlocks = catchBlocks.reverse
	finallyBlock.close
	closed = true
      }
      override def toString() = {
	var res = ""
	res = res + TopBlock.indent + "ExceptionBlock, handler: " + handler + "\n"
	res = res + TopBlock.indent + "  " + "try:\n"
	TopBlock.indent = TopBlock.indent + "    "
	res = res + tryBlock + "\n"
	TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4)
	res = res + TopBlock.indent + "  " + "catch:\n"
	TopBlock.indent = TopBlock.indent + "    "
	for (b <- catchBlocks)
	  res = res + b + "\n"
	TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4)
	res = res + TopBlock.indent + "  " + "finally:\n"
	TopBlock.indent = TopBlock.indent + "    "
	res = res + finallyBlock + "\n"
	TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4)
	res
      }
    }
    case class CatchBlock(parent: ExceptionBlock, exSym: Symbol) extends BlockList0 {
      override def parentBlockList: Option[BlockList0] = {
	parent.parentBlockList
      }
      override def toString() = {
	var res = ""
	res = res + TopBlock.indent + "CatchBlock:\n"
	res = res + super.toString()
	res
      }
    }
    case object TopBlock extends BlockList0 {
      var indent = ""
      override def parentBlockList = None
      override def toString() = {
	var res = ""
	res = res + TopBlock.indent + "TopBlock:\n"
	res = res + super.toString()
	res
      }
    }

    // for every basic block, a list of ExHInstructions to be executed:
    //   - Begin_ are executed before the block
    //   - EndExceptionBlock is executed after the block
    val bb2exHInstructions: HashMap[BasicBlock, List[ExHInstruction]] = new HashMap()
    // at the end of a try, catch or finally block, the jumps must not be emitted,
    // the automatically generated leave (or endfinally) will do the job.
    val omitJumpBlocks: HashSet[BasicBlock] = new HashSet()

    // suposes that finalizers are the same for different handlers
    // covering the same blocks
    def orderBlocksForExh(blocks: List[BasicBlock], exH: List[ExceptionHandler]): List[BasicBlock] = {

      var blocksToPut: List[BasicBlock] = blocks
      var nextBlock: BasicBlock = null
      var untreatedHandlers: List[ExceptionHandler] = exH
      TopBlock.blocks = Nil
      var currentBlock: BlockList0 = TopBlock
      def addBlocks(b: List[BasicBlock]):Unit = b match {
	case Nil => if (settings.debug.value) log("adding " + b)

	case x :: xs =>
	  if (settings.debug.value) log("adding " + b)
	  // problem: block may already be added, and and needs to be moved.
	  // if nextblock NOT in b: check if nextblock in blocksToPut, if NOT, check if movable, else don't put
	  if (nextBlock != null && b.contains(nextBlock)) {
	    val blocksToAdd = nextBlock :: b.diff(List(nextBlock))
	    nextBlock = null
	    addBlocks(blocksToAdd)
	  }
	  else if (untreatedHandlers.forall(h => !(h.covers(x)))) {

	    if (settings.debug.value) log(" no new handler for " + x)
	    if (untreatedHandlers.forall(h => !(h.blocks.contains(x) ||
						(h.finalizer != null &&
						 h.finalizer.covers(x)))))
	      {
		// the block is not part of some catch or finally code
		currentBlock.addBasicBlock(x)
		blocksToPut = blocksToPut.diff(List(x))
		if (settings.debug.value) log(" -> addBlocks(" + xs + ")")
		addBlocks(xs)
	      } else {
		if (settings.debug.value) log("x is part of catch or finally block")

		// check if the covered code of the handler x belongs to is empty
		// this check is not needed for finalizers: empty try with finalizer
		// is optimized by compiler (no try left)
		if(untreatedHandlers.forall(h =>
		  (!h.blocks.contains(x) || h.covered.isEmpty))) {
		    blocksToPut = blocksToPut.diff(List(x))
		    addBlocks(xs)
		  } else
		    addBlocks(xs ::: List(x))
	      }
	  } else { // there are new handlers for this block

	    var firstBlockAfter: HashMap[ExceptionHandler,BasicBlock] = new HashMap()
	    val savedCurrentBlock = currentBlock
	    /**
	     * the output blocks of this method are changed so that:
	     *  - only one block has a successor outside the set of blocks
	     *  - this block is the last of the reusulting list
	     *
	     * side-effect: it stores the successor in the hashMap
	     *  firstBlockAfter, which has to be emitted first after try/catch/finally,
	     *  because the target of the Leave-instruction will always be the first
	     *  instruction after EndExceptionBlock
	     *
	     * returns: the output blocks plus an Option containing the possibly created
	     * new block
	     **/
	    def adaptBlocks(blocks: List[BasicBlock], exh: ExceptionHandler): (List[BasicBlock], Option[BasicBlock]) = {
	      def outsideTargets(block: BasicBlock, blocks: List[BasicBlock]) = {
		block.successors.filter(scc => !blocks.contains(scc))
	      }
	      // get leaving blocks and their outside targets
	      def leavingBlocks(blocks: List[BasicBlock]): List[(BasicBlock, List[BasicBlock])] = {
		for {b <- blocks
		     val t = outsideTargets(b, blocks)
		     if t.length != 0 } yield (b, t)
	      }

	      def replaceOutJumps(blocks: List[BasicBlock], leaving: List[(BasicBlock, List[BasicBlock])], exh: ExceptionHandler): (List[BasicBlock], Option[BasicBlock]) = {
		def replaceJump(block: BasicBlock, from: BasicBlock, to: BasicBlock) = block.lastInstruction match {
		  case JUMP(where) =>
		    assert(from == where)
		  block.replaceInstruction(block.lastInstruction, JUMP(to))
		  case CJUMP(success, failure, cond, kind) =>
		    if (from == success)
		      block.replaceInstruction(block.lastInstruction, CJUMP(to, failure, cond, kind))
		    else
		      assert(from == failure)
		    if (from == failure)
		      block.replaceInstruction(block.lastInstruction, CJUMP(success, to, cond, kind))
		  case CZJUMP(success, failure, cond, kind) =>
		    if (from == success)
		      block.replaceInstruction(block.lastInstruction, CZJUMP(to, failure, cond, kind))
		    else
		      assert(from == failure)
		    if (from == failure)
		      block.replaceInstruction(block.lastInstruction, CZJUMP(success, to, cond, kind))
		  case SWITCH(tags, labels) => // labels: List[BasicBlock]
		    val newLabels = labels.map(b => if (b == from) to else b)
		    assert(newLabels.contains(to))
		    block.replaceInstruction(block.lastInstruction, SWITCH(tags, newLabels))
		  case _ => abort("expected branch at the end of block " + block)
		}

		val jumpOutBlock = blocks.last.code.newBlock
		jumpOutBlock.emit(JUMP(firstBlockAfter(exh)))
		jumpOutBlock.close
		leaving.foreach(p => {
		  val lBlock = p._1
		  val target = p._2(0) // the elemets of p._2 are all the same, checked before
		  replaceJump(lBlock, target, jumpOutBlock)
		})
		(blocks ::: List(jumpOutBlock), Some(jumpOutBlock))
	      }

	      val leaving = leavingBlocks(blocks)
	      if (leaving.length == 0)
		(blocks, None)
	      else if (leaving.length == 1) {
		val outside = leaving(0)._2
		assert(outside.forall(b => b == outside(0)), "exception-block leaving to multiple targets")
		if (!firstBlockAfter.isDefinedAt(exh))
		  firstBlockAfter(exh) = outside(0)
		else
		  assert(firstBlockAfter(exh) == outside(0), "try/catch leaving to multiple targets: " + firstBlockAfter(exh) + ", new: " + outside(0))
		val last = leaving(0)._1
		(blocks.diff(List(last)) ::: List(last), None)
	      } else {
		val outside = leaving.flatMap(p => p._2)
		assert(outside.forall(b => b == outside(0)), "exception-block leaving to multiple targets")
		if (!firstBlockAfter.isDefinedAt(exh))
		  firstBlockAfter(exh) = outside(0)
		else
		  assert(firstBlockAfter(exh) == outside(0), "try/catch leaving to multiple targets")
		replaceOutJumps(blocks, leaving, exh)
	      }
	    }

	    var affectedHandlers: List[ExceptionHandler] = Nil
	    untreatedHandlers.foreach( (h) => {
	      if (h.covers(x)){
		affectedHandlers = h :: affectedHandlers
	      }
	    })
	    affectedHandlers = affectedHandlers.filter(h => {h.covered.length == affectedHandlers(0).covered.length})
	    untreatedHandlers = untreatedHandlers.diff(affectedHandlers)

	    // shorter try-catch-finally last (the ones contained in another)
	    affectedHandlers = affectedHandlers.sort({(h1, h2) => h1.covered.length > h2.covered.length})

	    // more than one catch produces more than one exh, but we only need one
	    var singleAffectedHandler: ExceptionHandler = affectedHandlers(0) // List[ExceptionHandler] = Nil
	    var exceptionBlock: Option[ExceptionBlock] = None
	    affectedHandlers.foreach(h1 => {
	      val (adaptedBlocks, newBlock) = adaptBlocks(blocksToPut.intersect(h1.blocks), singleAffectedHandler)
	      newBlock match {
		case Some(block) =>
		  blocksToPut = blocksToPut ::: List(block)
		  h1.addBlock(block)
		case None => ()
	      }
	      val orderedCatchBlocks = h1.startBlock :: adaptedBlocks.diff(List(h1.startBlock))

	      exceptionBlock match {
		case Some(excBlock) =>
		  val catchBlock = excBlock.addCatchBlock(h1.cls)
		  currentBlock = catchBlock
		  addBlocks(orderedCatchBlocks)
		case None =>
		  val excBlock = currentBlock.addExceptionBlock(singleAffectedHandler)
		  exceptionBlock = Some(excBlock)

		  val (tryBlocks, newBlock) = adaptBlocks(blocksToPut.intersect(singleAffectedHandler.covered), singleAffectedHandler)

		  newBlock match {
		    case Some(block) =>
		      blocksToPut = blocksToPut ::: List(block)
		      singleAffectedHandler.addCoveredBlock(block)
		    case None => ()
		  }
		  currentBlock = excBlock.tryBlock
		  addBlocks(tryBlocks)

		  if (singleAffectedHandler.finalizer != null && singleAffectedHandler.finalizer != NoFinalizer) {
		    val (blocks0, newBlock) = adaptBlocks(blocksToPut.intersect(singleAffectedHandler.finalizer.blocks), singleAffectedHandler)
		    newBlock match {
		      case Some(block) =>
			blocksToPut = blocksToPut ::: List(block)
			singleAffectedHandler.finalizer.addBlock(block)
		      case None => ()
		    }
		    val blocks = singleAffectedHandler.finalizer.startBlock :: blocks0.diff(List(singleAffectedHandler.finalizer.startBlock))
		    currentBlock = excBlock.finallyBlock
		    addBlocks(blocks)
		  }

		  val catchBlock = excBlock.addCatchBlock(singleAffectedHandler.cls)
		  currentBlock = catchBlock
		  addBlocks(orderedCatchBlocks)
	      }
	      if (firstBlockAfter.isDefinedAt(singleAffectedHandler))
		nextBlock = firstBlockAfter(singleAffectedHandler)
	      else
		nextBlock = null
	    })

	    currentBlock = savedCurrentBlock

	    if (settings.debug.value)
	      log(" -> addBlocks(" + xs.intersect(blocksToPut) + ")")
	    addBlocks(xs.intersect(blocksToPut))
	  }
      }

      // begin method orderBlocksForExh

      if (settings.debug.value)
	log("before: " + blocks)
      // some blocks may have been removed by linearization
      untreatedHandlers.foreach(h => {
	h.blocks = h.blocks.intersect(blocksToPut)
	h.covered = h.covered.intersect(blocksToPut)
	if (h.finalizer != null && h.finalizer != NoFinalizer)
	  h.finalizer.blocks = h.finalizer.blocks.intersect(blocksToPut)
      })
      addBlocks(blocks)

      TopBlock.close()

      if (settings.debug.value) log("TopBlock tree is: ")
      if (settings.debug.value) Console.println(TopBlock)

      bb2exHInstructions.clear
      def addExHInstruction(b: BasicBlock, ehi: ExHInstruction) = {
	if (settings.debug.value)
	  log("adding exhinstr: " + b + " -> " + ehi)

	if (bb2exHInstructions.contains(b)){
	  bb2exHInstructions(b) = ehi :: bb2exHInstructions(b)
	} else {
	  bb2exHInstructions(b) = List(ehi)
	}
      }
      omitJumpBlocks.clear
      def omitJump(blk: BasicBlock) = {
	omitJumpBlocks += blk
      }
      var orderedBlocks: List[BasicBlock] = Nil
      def flatten(block: Block): Unit = {
	if (block == TopBlock) {
	  for (b <- TopBlock.blocks) flatten(b)
	} else block match {
	  case cb: CodeBlock =>
	    orderedBlocks = orderedBlocks ::: cb.basicBlocks
	  case bl: BlockList =>
	    for (b <- bl.blocks) flatten(b)
	  case cb: CatchBlock =>
	    for (b <- cb.blocks) flatten(b)
	  case eb: ExceptionBlock =>
	    val handler = eb.handler
	    addExHInstruction(eb.tryBlock.firstBasicBlock, new BeginExceptionBlock(handler))
	    omitJump(eb.tryBlock.lastBasicBlock)
	    flatten(eb.tryBlock)
	    for (c <- eb.catchBlocks) {
	      val t: MsilType = (if (c.exSym == NoSymbol) EXCEPTION
				 else getType(c.exSym))
	      addExHInstruction(c.firstBasicBlock, new BeginCatchBlock(handler, t))
	      omitJump(c.lastBasicBlock)
	      flatten(c)
	    }
	    if (handler.finalizer != null && handler.finalizer != NoFinalizer) {
	      addExHInstruction(eb.finallyBlock.firstBasicBlock, new BeginFinallyBlock(handler))
	      flatten(eb.finallyBlock)
	      addExHInstruction(eb.finallyBlock.lastBasicBlock, new EndExceptionBlock(handler))
	      omitJump(eb.finallyBlock.lastBasicBlock)
	    } else {
	      addExHInstruction(eb.catchBlocks.last.lastBasicBlock, new EndExceptionBlock(handler))
	    }
	}
      }

      flatten(TopBlock)

      assert(untreatedHandlers.forall((h) => h.covered.isEmpty),
	     "untreated exception handlers left: " + untreatedHandlers)
      // remove catch blocks from empty handlers (finally-blocks remain)
      untreatedHandlers.foreach((h) => {
	orderedBlocks = orderedBlocks.diff(h.blocks)
      })

      // take care of order in which exHInstructions are executed (BeginExceptionBlock as last)
      bb2exHInstructions.keys.foreach((b) => {
	bb2exHInstructions(b).sort((i1, i2) => (!i1.isInstanceOf[BeginExceptionBlock]))
      })


      if (settings.debug.value){
	log("after: " + orderedBlocks)
	log(" exhInstr: " + bb2exHInstructions)
      }

      orderedBlocks
    }

    var currentBlock: BasicBlock = _
    var lastBlock: BasicBlock = _
    var nextBlock: BasicBlock = _

    def genBlocks(l: List[BasicBlock]): Unit = l match {
      case Nil => ()
      case x :: Nil => currentBlock = x; nextBlock = null; genBlock(x)
      case x :: y :: ys => currentBlock = x; nextBlock = y; genBlock(x); genBlocks(y :: ys)
    }


    var ignoreNextDup: Boolean = false
    val excResultLocals: Stack[LocalBuilder] = new Stack()

    def genBlock(b: BasicBlock): Unit = {
      // at begin of the first block, there's nothing to save =>
      //  lastBlock != null is secure
      def saveResult(resType: MsilType) = if (resType != MVOID && lastBlock != null) {
	lastBlock.lastInstruction match {
	  case THROW() => ()
	  case _ =>
	    val lb: LocalBuilder = excResultLocals.top
	    mcode.Emit(OpCodes.Stloc, lb)
	}
      }

      if (bb2exHInstructions.contains(b)){
	bb2exHInstructions(b).foreach((i) => i match {
	  case BeginExceptionBlock(handler) =>
	    if (settings.debug.value) log("begin ex blk: " + handler)
	    mcode.BeginExceptionBlock()
	    val resType = msilType(handler.resultKind)
	    if (resType != MVOID) {
	      val l = mcode.DeclareLocal(resType)
	      l.SetLocalSymInfo("$exhResult")
	      excResultLocals.push(l)
	    }
	  case BeginCatchBlock(handler, exType) =>
	    if (settings.debug.value) log("begin catch blk: " + handler + ", tpe: " + exType)
	    saveResult(msilType(handler.resultKind))
	    mcode.BeginCatchBlock(exType)
	  case BeginFinallyBlock(handler) =>
	    saveResult(msilType(handler.resultKind))
	    mcode.BeginFinallyBlock()
	  case EndExceptionBlock(handler) => ()
	  case _ => abort("unknown case: " + i)
	})
      }

      mcode.MarkLabel(labels(b))
      if (settings.debug.value)
        log("Generating code for block: " + b)

      var lastLineNr: Int = 0

      b traverse ( instr => {

	needAdditionalRet = false

	var currentLineNr = try { (instr.pos).line.get } catch {
          case _: Error =>
            log("Warning: wrong position in: " + method)
          lastLineNr
	} // if getting line number fails

	if (currentLineNr != lastLineNr) {
	  mcode.setPosition(currentLineNr)
	  lastLineNr = currentLineNr
	}

        instr match {
          case THIS(clasz) =>
            mcode.Emit(OpCodes.Ldarg_0)

          case CONSTANT(const) =>
            const.tag match {
              case UnitTag    => ()
              case BooleanTag => mcode.Emit(if (const.booleanValue) OpCodes.Ldc_I4_1
					    else OpCodes.Ldc_I4_0)
              case ByteTag    => loadI4(const.byteValue, mcode)
              case ShortTag   => loadI4(const.shortValue, mcode)
              case CharTag    => loadI4(const.charValue, mcode)
              case IntTag     => loadI4(const.intValue, mcode)
              case LongTag    => mcode.Emit(OpCodes.Ldc_I8, const.longValue)
              case FloatTag   => mcode.Emit(OpCodes.Ldc_R4, const.floatValue)
              case DoubleTag  => mcode.Emit(OpCodes.Ldc_R8, const.doubleValue)
              case StringTag  => mcode.Emit(OpCodes.Ldstr, const.stringValue)
              case NullTag    => mcode.Emit(OpCodes.Ldnull)
              case ClassTag   =>
                mcode.Emit(OpCodes.Ldtoken, msilType(const.typeValue))
                mcode.Emit(OpCodes.Call, TYPE_FROM_HANDLE)
              case _          => abort("Unknown constant value: " + const)
            }

          case LOAD_ARRAY_ITEM(kind) =>
	    (kind: @unchecked) match {
	      case BOOL           => mcode.Emit(OpCodes.Ldelem_I1)
	      case BYTE           => mcode.Emit(OpCodes.Ldelem_U1)
	      case SHORT          => mcode.Emit(OpCodes.Ldelem_I2)
	      case CHAR           => mcode.Emit(OpCodes.Ldelem_U2)
	      case INT            => mcode.Emit(OpCodes.Ldelem_I4)
	      case LONG           => mcode.Emit(OpCodes.Ldelem_I8)
	      case FLOAT          => mcode.Emit(OpCodes.Ldelem_R4)
	      case DOUBLE         => mcode.Emit(OpCodes.Ldelem_R8)
	      case REFERENCE(cls) => mcode.Emit(OpCodes.Ldelem_Ref)

	      // case ARRAY(elem) is not possible, for Array[Array[Int]], the
	      //  load will be case REFERENCE(java.lang.Object)

	      // case UNIT is not possible: an Array[Unit] will be an
	      //  Array[scala.runtime.BoxedUnit] (-> case REFERENCE)
	    }

          case LOAD_LOCAL(local) =>
	    if (settings.debug.value)
	      log("load_local for " + local)
	    val isArg: Boolean = local.arg
	    val i = local.index
            if (isArg) {
	      loadArg(mcode)(i)
	    }
	    else {
	      loadLocal(i, local, mcode)
	    }

          case LOAD_FIELD(field, isStatic) =>
            if (settings.debug.value)
              log("LOAD_FIELD with owner: " + field.owner +
		  " flags: " + Flags.flagsToString(field.owner.flags))

	    var fieldInfo: FieldInfo = fields.get(field) match {
	      case Some(fInfo) => fInfo
	      case None =>
		val fInfo = getType(field.owner).GetField(msilName(field))
		fields(field) = fInfo
                fInfo
	    }
	    mcode.Emit(if (isStatic) OpCodes.Ldsfld else OpCodes.Ldfld, fieldInfo)


          case LOAD_MODULE(module) =>
            if (settings.debug.value)
              log("genearting LOAD_MODULE for: " + showsym(module))
	    mcode.Emit(OpCodes.Ldsfld, getModuleInstanceField(module))

          case STORE_ARRAY_ITEM(kind) =>
	    (kind: @unchecked) match {
	      case BOOL           => mcode.Emit(OpCodes.Stelem_I1)
	      case BYTE           => mcode.Emit(OpCodes.Stelem_I1)
	      case SHORT          => mcode.Emit(OpCodes.Stelem_I2)
	      case CHAR           => mcode.Emit(OpCodes.Stelem_I2)
	      case INT            => mcode.Emit(OpCodes.Stelem_I4)
	      case LONG           => mcode.Emit(OpCodes.Stelem_I8)
	      case FLOAT          => mcode.Emit(OpCodes.Stelem_R4)
	      case DOUBLE         => mcode.Emit(OpCodes.Stelem_R8)
	      case REFERENCE(cls) => mcode.Emit(OpCodes.Stelem_Ref)

	      // case UNIT / ARRRAY are not possible (see comment at LOAD_ARRAY_ITEM)
	    }

          case STORE_LOCAL(local) =>
	    val isArg: Boolean = local.arg
	    val i = local.index
	    if (settings.debug.value)
	      log("store_local for " + local + ", index " + i)

	    // there are some locals defined by the compiler that
	    // are isArg and are need to be stored.
            if (isArg) {
	      if (i >= -128 && i <= 127)
		mcode.Emit(OpCodes.Starg_S, i)
	      else
		mcode.Emit(OpCodes.Starg, i)
	    } else {
	      i match {
		case 0 => mcode.Emit(OpCodes.Stloc_0)
		case 1 => mcode.Emit(OpCodes.Stloc_1)
		case 2 => mcode.Emit(OpCodes.Stloc_2)
		case 3 => mcode.Emit(OpCodes.Stloc_3)
		case _      =>
		  if (i >= -128 && i <= 127)
		    mcode.Emit(OpCodes.Stloc_S, localBuilders(local))
		  else
		    mcode.Emit(OpCodes.Stloc, localBuilders(local))
	      }
	    }

          case STORE_FIELD(field, isStatic) =>
	    val fieldInfo: FieldInfo = fields.get(field) match {
	      case Some(fInfo) => fInfo
	      case None =>
		val fInfo = getType(field.owner).GetField(msilName(field))
		fields(field) = fInfo
                fInfo
	    }
	    mcode.Emit(if (isStatic) OpCodes.Stsfld else OpCodes.Stfld, fieldInfo)


          case CALL_PRIMITIVE(primitive) =>
            genPrimitive(primitive, instr.pos)


          case CALL_METHOD(msym, style) =>
	    if (msym.isClassConstructor) {
	      val constructorInfo: ConstructorInfo = getConstructor(msym)
	      (style: @unchecked) match {
		// normal constructor calls are Static..
		case Static(_) =>
                  if (method.symbol.isClassConstructor && method.symbol.owner == msym.owner)
                    mcode.Emit(OpCodes.Call, constructorInfo)
                  else
		    mcode.Emit(OpCodes.Newobj, constructorInfo)
		case SuperCall(_) =>
		  mcode.Emit(OpCodes.Call, constructorInfo)
		  if (isStaticModule(clasz.symbol) &&
		     notInitializedModules.contains(clasz.symbol))
		    {
		      notInitializedModules -= clasz.symbol
		      mcode.Emit(OpCodes.Ldarg_0)
		      mcode.Emit(OpCodes.Stsfld, getModuleInstanceField(clasz.symbol))
		    }
	      }

	    } else {
	      // java.lang.String.substring(int start_incl, int end_excl)
	      // System.String.Substring(int start_incl, int length)
	      if (msym == JSTRING_SUBSTRING_INT_INT) {
		val endLocal = mcode.DeclareLocal(MINT)
		endLocal.SetLocalSymInfo("$substring_end")
		mcode.Emit(OpCodes.Stloc, endLocal)
		mcode.Emit(OpCodes.Dup) // duplicate start_incl
		mcode.Emit(OpCodes.Neg)
		mcode.Emit(OpCodes.Ldloc, endLocal) // load end_excl
		mcode.Emit(OpCodes.Add) // compute length (-start + end)
	      }

	      var doEmit: Boolean = true
	      types.get(msym.owner) match {
                case Some(typ) if (typ.IsEnum) => {
		  def negBool = {
		    mcode.Emit(OpCodes.Ldc_I4_0)
		    mcode.Emit(OpCodes.Ceq)
		  }
		  doEmit = false
                  val name = msym.name
		  if (name eq nme.EQ)       { mcode.Emit(OpCodes.Ceq) }
                  else if (name eq nme.NE)  { mcode.Emit(OpCodes.Ceq); negBool }
                  else if (name eq nme.LT)  { mcode.Emit(OpCodes.Clt) }
                  else if (name eq nme.LE)  { mcode.Emit(OpCodes.Cgt); negBool }
                  else if (name eq nme.GT)  { mcode.Emit(OpCodes.Cgt) }
                  else if (name eq nme.GE)  { mcode.Emit(OpCodes.Clt); negBool }
                  else if (name eq nme.OR)  { mcode.Emit(OpCodes.Or) }
                  else if (name eq nme.AND) { mcode.Emit(OpCodes.And) }
                  else if (name eq nme.XOR) { mcode.Emit(OpCodes.Xor) }
                  else
		    doEmit = true
	        }
                case _ => ()
              }

	      // method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType
	      val (isDelegateView, paramType, resType) = atPhase(currentRun.typerPhase){
		msym.tpe match {
		  case MethodType(parameterTypes, resultType)
		  if (parameterTypes.length == 1 && msym.name == nme.view_) =>
		    val isDel = definitions.isCorrespondingDelegate(resultType, parameterTypes(0))
		    (isDel, parameterTypes(0), resultType)
		  case _ => (false, null, null)
		}
	      }
	      if (doEmit && isDelegateView) {
		doEmit = false
		createDelegateCaller(paramType, resType)
	      }

	      if (doEmit &&
		  (msym.name == nme.PLUS || msym.name == nme.MINUS)
		  && clrTypes.isDelegateType(msilType(msym.owner.tpe)))
		{
		doEmit = false
		val methodInfo: MethodInfo = getMethod(msym)
		// call it as a static method, even if the compiler (symbol) thinks it's virtual
		mcode.Emit(OpCodes.Call, methodInfo)
		mcode.Emit(OpCodes.Castclass, msilType(msym.owner.tpe))
	      }

	      if (doEmit && definitions.Delegate_scalaCallers.contains(msym)) {
		doEmit = false
		val methodSym: Symbol = definitions.Delegate_scalaCallerTargets(msym)
		val delegateType: Type = msym.tpe match {
		  case MethodType(_, retType) => retType
		  case _ => abort("not a method type: " + msym.tpe)
		}
		val method: MethodInfo = getMethod(methodSym)
		val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR))
		if (methodSym.isStatic) {
		  mcode.Emit(OpCodes.Ldftn, method)
		} else {
		  mcode.Emit(OpCodes.Dup)
		  mcode.Emit(OpCodes.Ldvirtftn, method)
		}
		mcode.Emit(OpCodes.Newobj, delegCtor)
	      }

	      if (doEmit) {
		val methodInfo: MethodInfo = getMethod(msym)
		style match {
		  case SuperCall(_) =>
		    mcode.Emit(OpCodes.Call, methodInfo)
		  case Dynamic =>
		    mcode.Emit(if (dynToStatMapped(msym)) OpCodes.Call else OpCodes.Callvirt,
                               methodInfo)
                  case Static(_) =>
		    mcode.Emit(OpCodes.Call, methodInfo)
		}
	      }
	    }

          case BOX(boxType) => emitBox(mcode, boxType) //mcode.Emit(OpCodes.Box, msilType(boxType))

          case UNBOX(boxType) => emitUnbox(mcode, boxType)

          case NEW(REFERENCE(cls)) =>
	    ignoreNextDup = true

          // works also for arrays and reference-types
          case CREATE_ARRAY(elem) => mcode.Emit(OpCodes.Newarr, msilType(elem))

          // works for arrays and reference-types
          case IS_INSTANCE(tpe) =>
            mcode.Emit(OpCodes.Isinst, msilType(tpe))
            mcode.Emit(OpCodes.Ldnull)
            mcode.Emit(OpCodes.Ceq)
            mcode.Emit(OpCodes.Ldc_I4_0)
            mcode.Emit(OpCodes.Ceq)


	  // works for arrays and reference-types
	  // part from the scala reference: "S <: T does not imply
	  //  Array[S] <: Array[T] in Scala. However, it is possible
	  //  to cast an array of S to an array of T if such a cast
	  //  is permitted in the host environment."
          case CHECK_CAST(tpe) => mcode.Emit(OpCodes.Castclass, msilType(tpe))


	  // no SWITCH is generated when there's
	  //  - a default case ("case _ => ...") in the matching expr
	  //  - OR is used ("case 1 | 2 => ...")
          case SWITCH(tags, branches) =>
	    // tags is List[List[Int]]; a list of integers for every label.
	    //    if the int on stack is 4, and 4 is in the second list => jump
	    //    to second label
	    // branches is List[BasicBlock]
	    //    the labels to jump to (the last one ist the default one)

	    val switchLocal = mcode.DeclareLocal(MINT)
	    // several switch variables will appear with the same name in the
	    //  assembly code, but this makes no truble
	    switchLocal.SetLocalSymInfo("$switch_var")

	    mcode.Emit(OpCodes.Stloc, switchLocal)
	    var i: Int = 0
	    for (l <- tags) {
	      var targetLabel = labels(branches(i))
	      for (i <- l) {
		mcode.Emit(OpCodes.Ldloc, switchLocal)
		loadI4(i, mcode)
		mcode.Emit(OpCodes.Beq, targetLabel)
	      }
	      i += 1
	    }
	    val defaultTarget = labels(branches(i))
            if (nextBlock != defaultTarget && !omitJumpBlocks.contains(currentBlock))
	      mcode.Emit(OpCodes.Br, defaultTarget)


          case JUMP(where) =>
            if (nextBlock != where && !omitJumpBlocks.contains(currentBlock))
	      mcode.Emit(OpCodes.Br, labels(where))


          case CJUMP(success, failure, cond, kind) =>
	    // cond is TestOp (see Primitives.scala), and can take
	    // values EQ, NE, LT, GE LE, GT
	    // kind is TypeKind
	    val isFloat = kind == FLOAT || kind == DOUBLE
	    if (nextBlock == success || omitJumpBlocks.contains(currentBlock)) {
	      emitBr(cond.negate, labels(failure), isFloat)
	    } else {
	      emitBr(cond, labels(success), isFloat)
	      if (nextBlock != failure && !omitJumpBlocks.contains(currentBlock)) {
		mcode.Emit(OpCodes.Br, labels(failure))
	      }
	    }

          case CZJUMP(success, failure, cond, kind) =>
	    (kind: @unchecked) match {
	      case BOOL | REFERENCE(_) =>
		if (nextBlock == success || omitJumpBlocks.contains(currentBlock)) {
		  emitBrBool(cond.negate, labels(failure))
		} else {
		  emitBrBool(cond, labels(success))
		  if (nextBlock != failure && !omitJumpBlocks.contains(currentBlock)) {
		    mcode.Emit(OpCodes.Br, labels(failure))
		  }
		}
	    }

          case RETURN(kind) =>
	    mcode.Emit(OpCodes.Ret)

          case THROW() =>
	    mcode.Emit(OpCodes.Throw)

          case DROP(kind) =>
	    mcode.Emit(OpCodes.Pop)

          case DUP(kind) =>
	    // needed to create new instances
	    if (!ignoreNextDup) {
	      mcode.Emit(OpCodes.Dup)
	    } else {
	      ignoreNextDup = false
	    }


          case MONITOR_ENTER() =>
	    mcode.Emit(OpCodes.Call, MMONITOR_ENTER)

          case MONITOR_EXIT() =>
	    mcode.Emit(OpCodes.Call, MMONITOR_EXIT)

          case SCOPE_ENTER(_) | SCOPE_EXIT(_) => ()
        }

      }) // end b traverse instr => { .. }

      lastBlock = b // this way, saveResult knows lastBlock

      if (bb2exHInstructions.contains(b)){
	bb2exHInstructions(b).foreach((i) => i match {
	  case BeginExceptionBlock(handler) => ()
	  case BeginCatchBlock(handler, exType) => ()
	  case BeginFinallyBlock(handler) => ()
	  case EndExceptionBlock(handler) =>
	    if (settings.debug.value) log("end ex blk: " + handler)
	    val resType = msilType(handler.resultKind)
	    if (handler.finalizer == null || handler.finalizer == NoFinalizer)
	      saveResult(resType)
	    mcode.EndExceptionBlock()
	    if (resType != MVOID) {
	      val lb: LocalBuilder = excResultLocals.pop
	      mcode.Emit(OpCodes.Ldloc, lb)
	    } else
	      needAdditionalRet = true
	  case _ => abort("unknown case: " + i)
	})
      }

    } // end genBlock

    def genPrimitive(primitive: Primitive, pos: Position): Unit = {
      primitive match {
        case Negation(kind) =>
          kind match {
	    // CHECK: is ist possible to get this for BOOL? in this case, verify.
	    case BOOL | BYTE | CHAR | SHORT | INT | LONG | FLOAT | DOUBLE =>
	      mcode.Emit(OpCodes.Neg)

            case _ => abort("Impossible to negate a " + kind)
          }

        case Arithmetic(op, kind) =>
          op match {
            case ADD => mcode.Emit(OpCodes.Add)
            case SUB => mcode.Emit(OpCodes.Sub)
            case MUL => mcode.Emit(OpCodes.Mul)
            case DIV => mcode.Emit(OpCodes.Div)
            case REM => mcode.Emit(OpCodes.Rem)
            case NOT => mcode.Emit(OpCodes.Not) //bitwise complement (one's complement)
            case _ => abort("Unknown arithmetic primitive " + primitive )
          }

        case Logical(op, kind) => op match {
	  case AND => mcode.Emit(OpCodes.And)
	  case OR => mcode.Emit(OpCodes.Or)
	  case XOR => mcode.Emit(OpCodes.Xor)
	}


        case Shift(op, kind) => op match {
	  case LSL => mcode.Emit(OpCodes.Shl)
	  case ASR => mcode.Emit(OpCodes.Shr)
	  case LSR => mcode.Emit(OpCodes.Shr_Un)
	}


        case Conversion(src, dst) =>
          if (settings.debug.value)
            log("Converting from: " + src + " to: " + dst)

	dst match {
	  case BYTE =>   mcode.Emit(OpCodes.Conv_U1)
	  case SHORT =>  mcode.Emit(OpCodes.Conv_I2)
	  case CHAR =>   mcode.Emit(OpCodes.Conv_U2)
	  case INT =>    mcode.Emit(OpCodes.Conv_I4)
	  case LONG =>   mcode.Emit(OpCodes.Conv_I8)
	  case FLOAT =>  mcode.Emit(OpCodes.Conv_R4)
	  case DOUBLE => mcode.Emit(OpCodes.Conv_R8)
          case _ =>
	    Console.println("Illegal conversion at: " + clasz +
			    " at: " + method.sourceFile + ":" +
			    pos.line.get)
	}

        case ArrayLength(_) =>
          mcode.Emit(OpCodes.Ldlen)

        case StartConcat =>
	  mcode.Emit(OpCodes.Newobj, MSTRING_BUILDER_CONSTR)


        case StringConcat(el) =>
          val elemType : MsilType = el match {
            case REFERENCE(_) | ARRAY(_) => MOBJECT
            case _ => msilType(el)
          }

	  val argTypes:Array[MsilType] = Array(elemType)
	  val stringBuilderAppend = MSTRING_BUILDER.GetMethod("Append", argTypes )
	  mcode.Emit(OpCodes.Callvirt,  stringBuilderAppend)

        case EndConcat =>
	  mcode.Emit(OpCodes.Callvirt, MSTRING_BUILDER_TOSTRING)

        case _ => abort("Unimplemented primitive " + primitive)
      }
    }


    ////////////////////// loading ///////////////////////

    def loadI4(value: Int, code: ILGenerator):Unit = value match {
      case -1 => code.Emit(OpCodes.Ldc_I4_M1)
      case 0  => code.Emit(OpCodes.Ldc_I4_0)
      case 1  => code.Emit(OpCodes.Ldc_I4_1)
      case 2  => code.Emit(OpCodes.Ldc_I4_2)
      case 3  => code.Emit(OpCodes.Ldc_I4_3)
      case 4  => code.Emit(OpCodes.Ldc_I4_4)
      case 5  => code.Emit(OpCodes.Ldc_I4_5)
      case 6  => code.Emit(OpCodes.Ldc_I4_6)
      case 7  => code.Emit(OpCodes.Ldc_I4_7)
      case 8  => code.Emit(OpCodes.Ldc_I4_8)
      case _  =>
	if (value >= -128 && value <= 127)
	  code.Emit(OpCodes.Ldc_I4_S, value)
	else
	  code.Emit(OpCodes.Ldc_I4, value)
    }

    def loadArg(code: ILGenerator)(i: Int) = i match {
      case 0 => code.Emit(OpCodes.Ldarg_0)
      case 1 => code.Emit(OpCodes.Ldarg_1)
      case 2 => code.Emit(OpCodes.Ldarg_2)
      case 3 => code.Emit(OpCodes.Ldarg_3)
      case _      =>
	if (i >= -128 && i <= 127)
	  code.Emit(OpCodes.Ldarg_S, i)
	else
	  code.Emit(OpCodes.Ldarg, i)
    }

    def loadLocal(i: Int, local: Local, code: ILGenerator) = i match {
      case 0 => code.Emit(OpCodes.Ldloc_0)
      case 1 => code.Emit(OpCodes.Ldloc_1)
      case 2 => code.Emit(OpCodes.Ldloc_2)
      case 3 => code.Emit(OpCodes.Ldloc_3)
      case _      =>
	if (i >= -128 && i <= 127)
	  code.Emit(OpCodes.Ldloc_S, localBuilders(local))
	else
	  code.Emit(OpCodes.Ldloc, localBuilders(local))
    }

    ////////////////////// labels ///////////////////////


    val labels: HashMap[BasicBlock, Label] = new HashMap() // labels for branches

    def emitBr(condition: TestOp, dest: Label, isFloat: Boolean) = {
      condition match {
	case EQ => mcode.Emit(OpCodes.Beq, dest)
	case NE => mcode.Emit(OpCodes.Bne_Un, dest)
	case LT => mcode.Emit(if (isFloat) OpCodes.Blt_Un else OpCodes.Blt, dest)
	case GE => mcode.Emit(if (isFloat) OpCodes.Bge_Un else OpCodes.Bge, dest)
	case LE => mcode.Emit(if (isFloat) OpCodes.Ble_Un else OpCodes.Ble, dest)
	case GT => mcode.Emit(if (isFloat) OpCodes.Bgt_Un else OpCodes.Bgt, dest)
      }
    }

    def emitBrBool(cond: TestOp, dest: Label) = {
      cond match {
	// EQ -> Brfalse, NE -> Brtrue; this is because we come from
	// a CZJUMP. If the value on the stack is 0 (e.g. a boolen
	// method returned false), and we are in the case EQ, then
	// we need to emit Brfalse (EQ Zero means false). vice versa
        case EQ => mcode.Emit(OpCodes.Brfalse, dest)
        case NE => mcode.Emit(OpCodes.Brtrue, dest)
      }
    }

    def makeLabels(bs: List[BasicBlock]) = {
      if (settings.debug.value)
        log("Making labels for: " + method)
      for (bb <- bs) labels(bb) = mcode.DefineLabel()
    }

    ////////////////////// local vars ///////////////////////

    /**
     * Compute the indexes of each local variable of the given
     * method.
     */
    def computeLocalVarsIndex(m: IMethod): Unit = {
      val params = m.params
      var idx = 1
      if (isStaticSymbol(m.symbol))
        idx = 0

      for (l <- params) {
        if (settings.debug.value)
          log("Index value for parameter " + l + ": " + idx)
        l.index = idx
        idx += 1 // sizeOf(l.kind)
      }

      val locvars = m.locals.diff(params)
      idx = 0

      for (l <- locvars) {
        if (settings.debug.value)
          log("Index value for local variable " + l + ": " + idx)
        l.index = idx
        idx += 1 // sizeOf(l.kind)
      }

    }

    ////////////////////// Utilities ////////////////////////

    /** Return the a name of this symbol that can be used on the .NET
     * platform. It removes spaces from names.
     *
     * Special handling: scala.All and scala.AllRef are 'erased' to
     * scala.All$ and scala.AllRef$. This is needed because they are
     * not real classes, and they mean 'abrupt termination upon evaluation
     * of that expression' or 'null' respectively. This handling is
     * done already in GenICode, but here we need to remove references
     * from method signatures to these types, because such classes can
     * not exist in the classpath: the type checker will be very confused.
     */
    def msilName(sym: Symbol): String = {
      val suffix: String = if (sym.hasFlag(Flags.MODULE) && !sym.isMethod &&
                               !sym.isImplClass &&
                               !sym.hasFlag(Flags.JAVA)) "$" else ""
      // Flags.JAVA: "symbol was not defined by a scala-class" (java, or .net-class)

      if (sym == definitions.AllClass)
        return "scala.All$"
      else if (sym == definitions.AllRefClass)
        return "scala.AllRef$"

      (if (sym.isClass || (sym.isModule && !sym.isMethod))
	sym.fullNameString
       else
	 sym.simpleName.toString().trim()) + suffix
    }


    ////////////////////// flags ///////////////////////

    def msilTypeFlags(sym: Symbol): Int = {
      var mf: Int = TypeAttributes.AutoLayout | TypeAttributes.AnsiClass

      mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NotPublic else TypeAttributes.Public)
      mf = mf | (if (sym hasFlag Flags.ABSTRACT) TypeAttributes.Abstract else 0)
      mf = mf | (if (sym.isTrait && !sym.isImplClass) TypeAttributes.Interface else TypeAttributes.Class)
      mf = mf | (if (sym isFinal) TypeAttributes.Sealed else 0)

      sym.attributes foreach { a => a match {
	case AnnotationInfo(SerializableAttr, _, _) =>
	  // TODO: add the Serializable TypeAttribute also if the attribute
	  // System.SerializableAttribute is present (.net attribute, not scala)
	  //  Best way to do it: compare with
	  //  definitions.getClass("System.SerializableAttribute").tpe
	  //  when frontend available
	  mf = mf | TypeAttributes.Serializable
	case _ => ()
      }}

      mf
      // static: not possible (or?)
    }

    def msilMethodFlags(sym: Symbol): Short = {
      var mf: Int = MethodAttributes.HideBySig |
        (if (sym hasFlag Flags.PRIVATE) MethodAttributes.Private
	 else if (sym hasFlag Flags.PROTECTED) MethodAttributes.Family
	 else MethodAttributes.Public)

      if (!sym.isClassConstructor) {
        if (isStaticSymbol(sym))
	  mf = mf | FieldAttributes.Static
        else {
	  mf = mf | MethodAttributes.Virtual
	  if (sym.isFinal && !types(sym.owner).IsInterface)
	    mf = mf | MethodAttributes.Final
	  if (sym.hasFlag(Flags.DEFERRED) || types(sym.owner).IsInterface)
	    mf = mf | MethodAttributes.Abstract
        }
      }

      mf.toShort
    }

    def msilFieldFlags(sym: Symbol): Short = {
      var mf: Int =
        if (sym hasFlag Flags.PRIVATE) FieldAttributes.Private
	else if (sym hasFlag Flags.PROTECTED) FieldAttributes.FamORAssem
	else FieldAttributes.Public

      if (sym hasFlag Flags.FINAL)
	mf = mf | FieldAttributes.InitOnly

      if (isStaticSymbol(sym))
	mf = mf | FieldAttributes.Static

      // TRANSIENT: "not nerialized", VOLATILE: doesn't exist on .net
      // TODO: add this attribute also if the class has the custom attribute
      // System.NotSerializedAttribute
      sym.attributes.foreach( a => a match {
        case AnnotationInfo(TransientAtt, _, _) =>
	  mf = mf | FieldAttributes.NotSerialized
        case _ => ()
      })

      mf.toShort
    }


    def isStaticSymbol(s: Symbol): Boolean =
      s.hasFlag(Flags.STATIC) || s.hasFlag(Flags.STATICMEMBER) || s.owner.isImplClass


    ////////////////////// builders, types ///////////////////////

    var entryPoint: Symbol = _

    val notInitializedModules: HashSet[Symbol] = new HashSet()

    // TODO: create fields also in def createType, and not in genClass,
    // add a getField method (it only works as it is because fields never
    // accessed from outside a class)

    val localBuilders: HashMap[Local, LocalBuilder] = new HashMap()

    private[GenMSIL] def findEntryPoint(cls: IClass) = {
      def isEntryPoint(sym: Symbol):Boolean = {
        if (isStaticModule(sym.owner) && msilName(sym) == "main")
	  if (sym.tpe.paramTypes.length == 1) {
	    toTypeKind(sym.tpe.paramTypes(0)) match {
	      case ARRAY(elem) =>
	        if (elem.toType.symbol == definitions.StringClass){
		  return true
	        }
	      case _ => ()
	    }
	  }
        false
      }

      for (m <- cls.methods) {
	if (isEntryPoint(m.symbol)) {
	  if (entryPoint == null)
	    entryPoint = m.symbol
	}
      }
      if (firstSourceName == "")
	if (cls.symbol.sourceFile != null) // is null for nested classes
	  firstSourceName = cls.symbol.sourceFile.name
    }

    // #####################################################################
    // get and create types

    private def msilType(t: TypeKind): MsilType = (t: @unchecked) match {
      case UNIT           => MVOID
      case BOOL           => MBOOL
      case BYTE           => MBYTE
      case SHORT          => MSHORT
      case CHAR           => MCHAR
      case INT            => MINT
      case LONG           => MLONG
      case FLOAT          => MFLOAT
      case DOUBLE         => MDOUBLE
      case REFERENCE(cls) => getType(cls)
      case ARRAY(elem)    => clrTypes.mkArrayType(msilType(elem))
    }

    private def msilType(tpe: Type): MsilType = msilType(toTypeKind(tpe))

    private def msilParamTypes(sym: Symbol): Array[MsilType] = {
      sym.tpe.paramTypes.map(msilType).toArray
    }

    def getType(sym: Symbol): MsilType = types.get(sym) match {
      case Some(typ) => typ
      case None =>
        val name = if (sym.isModuleClass && !sym.isTrait) sym.fullNameString + "$"
                   else sym.fullNameString
        val typ = clrTypes.getType(name)
        if (typ == null)
          throw new Error(showsym(sym) + " with name " + name)
        else {
          clrTypes.types(sym) = typ
          typ
        }
    }

    def mapType(sym: Symbol, mType: MsilType) = {
      assert(mType != null, showsym(sym))
      types(sym) = mType
    }

    def createTypeBuilder(iclass: IClass): Unit = {
      def getMsilType(tpe: Type): MsilType = {
        val sym = tpe.symbol
        types.get(sym) match {
          case Some(mtype) => mtype
          case None => createTypeBuilder(classes(sym)); types(sym)
        }
      }

      val sym = iclass.symbol
      if (types contains sym) return

      def isInterface(s: Symbol) = s.isTrait && !s.isImplClass
      val parents: List[Type] =
        if (sym.info.parents.isEmpty) List(definitions.ObjectClass.tpe)
        else sym.info.parents.removeDuplicates

      val superType = if (isInterface(sym)) null else getMsilType(parents.head)
      if (settings.debug.value)
	log("super type: " + parents(0).symbol + ", msil type: " + superType)

      val interfaces: Array[MsilType] = parents.tail.map(getMsilType).toArray
      if (parents.length > 1) {
	if (settings.debug.value){
	  log("interfaces:")
	  for (i <- Iterator.range(0, interfaces.length)){
	    log("  type: " + parents(i + 1).symbol + ", msil type: " + interfaces(i))
	  }
	}
      }

      //TODO here: if the class is not top-level, use DefineNestedType?
      val tBuilder =
        mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces)
      mapType(sym, tBuilder)
    } // createTypeBuilder


    def createClassMembers(iclass: IClass): Unit = try {
      createClassMembers0(iclass)
    }
    catch {
      case e: Throwable =>
        System.err.println(showsym(iclass.symbol))
        System.err.println("with methods = " + iclass.methods)
        throw e
    }

    def createClassMembers0(iclass: IClass): Unit = {
      val mtype = getType(iclass.symbol).asInstanceOf[TypeBuilder]
      for (ifield <- iclass.fields) {
        val sym = ifield.symbol
        if (settings.debug.value)
          log("Adding field: " + sym.fullNameString)

        var attributes = msilFieldFlags(sym)
        val fBuilder = mtype.DefineField(msilName(sym), msilType(sym.tpe), attributes)
        fields(sym) = fBuilder
        addAttributes(fBuilder, sym.attributes)
      }

      if (iclass.symbol != definitions.ArrayClass)
      for (m: IMethod <- iclass.methods) {
        val sym = m.symbol
        if (settings.debug.value)
          log("Creating MethodBuilder for " + Flags.flagsToString(sym.flags) + " " +
              sym.owner.fullNameString + "::" + sym.name)

        val ownerType = getType(sym.enclClass).asInstanceOf[TypeBuilder]
        assert(mtype == ownerType, "mtype = " + mtype + "; ownerType = " + ownerType)
        var paramTypes = msilParamTypes(sym)
        val attr = msilMethodFlags(sym)

        if (m.symbol.isClassConstructor) {
	  val constr =
            ownerType.DefineConstructor(attr, CallingConventions.Standard, paramTypes)
          for (i <- Iterator.range(0, paramTypes.length)) {
	    constr.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym))
	  }
	  mapConstructor(sym, constr)
	  addAttributes(constr, sym.attributes)
        } else {
	  var resType = msilType(m.returnType)
	  val method =
            ownerType.DefineMethod(getMethodName(sym), attr, resType, paramTypes)
          for (i <- Iterator.range(0, paramTypes.length)){
	    method.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym))
	  }
          if (!methods.contains(sym))
	    mapMethod(sym, method)
	  addAttributes(method, sym.attributes)
          if (settings.debug.value)
            log("\t created MethodBuilder " + method)
        }
      }

      if (isStaticModule(iclass.symbol)){
	addModuleInstanceField(iclass.symbol)
	notInitializedModules += iclass.symbol
	addStaticInit(iclass.symbol)
      }

    } // createClassMembers

    private def isTopLevelModule(sym: Symbol): Boolean =
      atPhase (currentRun.refchecksPhase) {
        sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
      }

    private def isStaticModule(sym: Symbol): Boolean = {
      sym.isModuleClass && !sym.isImplClass && !sym.hasFlag(Flags.LIFTED)
    }

    private def isCloneable(sym: Symbol): Boolean = {
      !sym.attributes.forall( a => a match {
	case AnnotationInfo(CloneableAttr, _, _) => false
	case _ => true
      })
    }

    private def addModuleInstanceField(sym: Symbol): Unit = {
      if (settings.debug.value)
	log("Adding Module-Instance Field for " + showsym(sym))
      val tBuilder = getType(sym).asInstanceOf[TypeBuilder]
      val fb = tBuilder.DefineField(MODULE_INSTANCE_NAME,
			   tBuilder,
			   (FieldAttributes.Public |
			    //FieldAttributes.InitOnly |
			    FieldAttributes.Static).toShort)
      fields(sym) = fb
    }


    // the symbol may be a object-symbol (module-symbol), or a module-class-symbol
    private def getModuleInstanceField(sym: Symbol): FieldInfo = {
      assert(sym.isModule || sym.isModuleClass, "Expected module: " + showsym(sym))

      // when called by LOAD_MODULE, the corresponding type maybe doesn't
      // exist yet -> make a getType
      val moduleClassSym = if (sym.isModule) sym.moduleClass else sym

      // TODO: get module field for modules not defined in the
      // source currently compiling (e.g. Console)

      fields get moduleClassSym match {
        case Some(sym) => sym
        case None =>
          //val mclass = types(moduleClassSym)
          val mClass = clrTypes.getType(moduleClassSym.fullNameString + "$")
          val mfield = mClass.GetField("MODULE$")
          assert(mfield ne null, "module not found " + showsym(moduleClassSym))
          fields(moduleClassSym) = mfield
          mfield
      }

      //fields(moduleClassSym)
    }

    private def addStaticInit(sym: Symbol): Unit = {
      val tBuilder = getType(sym).asInstanceOf[TypeBuilder]

      val staticInit = tBuilder.DefineConstructor(
	(MethodAttributes.Static | MethodAttributes.Public).toShort,
	CallingConventions.Standard,
	MsilType.EmptyTypes)

      val sicode = staticInit.GetILGenerator()

      val instanceConstructor = constructors(sym.primaryConstructor)

      sicode.Emit(OpCodes.Newobj, instanceConstructor)
      // the stsfld is done in the instance constructor, just after the super call.
      sicode.Emit(OpCodes.Pop)

      sicode.Emit(OpCodes.Ret)
    }


    private def dumpMirrorClass(sym: Symbol): Unit = {
      val tBuilder = getType(sym)
      assert(sym.isModuleClass, "Can't generate Mirror-Class for the Non-Module class " + sym)
      if (settings.debug.value)
        log("Dumping mirror class for object: " + sym)
      val moduleName = msilName(sym)
      val mirrorName = moduleName.substring(0, moduleName.length() - 1)
      val mirrorTypeBuilder = mmodule.DefineType(mirrorName,
						 TypeAttributes.Class |
						 TypeAttributes.Public |
						 TypeAttributes.Sealed,
		//FIXME: an object may have a super-type (a class, not an object) -> not in mirror class?
						 MOBJECT,
						 MsilType.EmptyTypes)

      for (m <- sym.tpe.nonPrivateMembers
           if m.owner != definitions.ObjectClass && !m.hasFlag(Flags.PROTECTED) &&
           m.isMethod && !m.isClassConstructor && !isStaticSymbol(m) && !m.hasFlag(Flags.CASE))
	{
	  if (settings.debug.value)
            log("   Mirroring method: " + m)
          val paramTypes = msilParamTypes(m)
          val paramNames: Array[String] = new Array[String](paramTypes.length)
          for (i <- 0 until paramTypes.length)
            paramNames(i) = "x_" + i

	  // CHECK: verify if getMethodName is better than msilName
	  val mirrorMethod = mirrorTypeBuilder.DefineMethod(getMethodName(m),
							    MethodAttributes.Public |
							    MethodAttributes.Static,
							    msilType(m.tpe.resultType),
							    paramTypes)

	  var i = 0
	  while(i < paramTypes.length) {
	    mirrorMethod.DefineParameter(i, ParameterAttributes.None, paramNames(i))
	    i = i + 1
	  }

	  val mirrorCode = mirrorMethod.GetILGenerator()
	  mirrorCode.Emit(OpCodes.Ldsfld, getModuleInstanceField(sym))
          Iterator.range(0, paramTypes.length) foreach loadArg(mirrorCode)

	  mirrorCode.Emit(OpCodes.Call, getMethod(m))
	  mirrorCode.Emit(OpCodes.Ret)
	}

      addSymtabAttribute(sym.sourceModule, mirrorTypeBuilder)

      mirrorTypeBuilder.CreateType()
    }


    // #####################################################################
    // delegate callers

    var delegateCallers: TypeBuilder = _
    var nbDelegateCallers: Int = 0

    private def initDelegateCallers() = {
      delegateCallers = mmodule.DefineType("$DelegateCallers", TypeAttributes.Public |
					  TypeAttributes.Sealed)
    }

    private def createDelegateCaller(functionType: Type, delegateType: Type) = {
      if (delegateCallers == null)
	initDelegateCallers()
      // create a field an store the function-object
      val mFunctionType: MsilType = msilType(functionType)
      val anonfunField: FieldBuilder = delegateCallers.DefineField(
	"$anonfunField$$" + nbDelegateCallers, mFunctionType,
	FieldAttributes.InitOnly | FieldAttributes.Public | FieldAttributes.Static)
      mcode.Emit(OpCodes.Stsfld, anonfunField)


      // create the static caller method and the delegate object
      val (paramTypes, returnType) = delegateType.member(nme.apply).tpe match {
	case MethodType(delParams, delReturn) => (delParams, delReturn)
	case _ => abort("not a delegate type: "  + delegateType)
      }
      val caller: MethodBuilder = delegateCallers.DefineMethod(
	"$delegateCaller$$" + nbDelegateCallers,
	MethodAttributes.Final | MethodAttributes.Public | MethodAttributes.Static,
	msilType(returnType), paramTypes.map(msilType).toArray)
      for (i <- 0 until paramTypes.length)
	caller.DefineParameter(i, ParameterAttributes.None, "arg" + i)
      val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR))
      mcode.Emit(OpCodes.Ldnull)
      mcode.Emit(OpCodes.Ldftn, caller)
      mcode.Emit(OpCodes.Newobj, delegCtor)


      // create the static caller method body
      val functionApply: MethodInfo = getMethod(functionType.member(nme.apply))
      val dcode: ILGenerator = caller.GetILGenerator()
      dcode.Emit(OpCodes.Ldsfld, anonfunField)
      for (i <- 0 until paramTypes.length) {
	loadArg(dcode)(i)
	emitBox(dcode, toTypeKind(paramTypes(i)))
      }
      dcode.Emit(OpCodes.Callvirt, functionApply)
      emitUnbox(dcode, toTypeKind(returnType))
      dcode.Emit(OpCodes.Ret)

      nbDelegateCallers = nbDelegateCallers + 1

    } //def createDelegateCaller

    def emitBox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match {
      // doesn't make sense, unit as parameter..
      case UNIT   => code.Emit(OpCodes.Ldsfld, boxedUnit)
      case BOOL | BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE =>
        code.Emit(OpCodes.Box, msilType(boxType))
      case REFERENCE(cls) if (definitions.unboxMethod.contains(cls)) =>
        code.Emit(OpCodes.Box, (msilType(boxType)))
      case REFERENCE(_) | ARRAY(_) => ()
    }

    def emitUnbox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match {
      case UNIT   => code.Emit(OpCodes.Pop)
      case BOOL   => code.Emit(OpCodes.Unbox, MBOOL); code.Emit(OpCodes.Ldind_I1)
      case BYTE   => code.Emit(OpCodes.Call, toByte)
      case SHORT  => code.Emit(OpCodes.Call, toShort)
      case CHAR   => code.Emit(OpCodes.Call, toChar)
      case INT    => code.Emit(OpCodes.Call, toInt)
      case LONG   => code.Emit(OpCodes.Call, toLong)
      case FLOAT  => code.Emit(OpCodes.Call, toFloat)
      case DOUBLE => code.Emit(OpCodes.Call, toDouble)
      case REFERENCE(cls) if (definitions.unboxMethod.contains(cls)) =>
        code.Emit(OpCodes.Unbox, msilType(boxType))
        code.Emit(OpCodes.Ldobj, msilType(boxType))
      case REFERENCE(_) | ARRAY(_) => ()
    }

    // #####################################################################
    // get and create methods / constructors

    def getConstructor(sym: Symbol): ConstructorInfo = constructors.get(sym) match {
      case Some(constr) => constr
      case None =>
        val mClass = getType(sym.owner)
        val constr = mClass.GetConstructor(msilParamTypes(sym))
        if (constr eq null) {
          System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name)
          System.out.println("scope = " + sym.owner.tpe.decls)
          throw new Error(sym.fullNameString)
        }
        else {
          mapConstructor(sym, constr)
          constr
        }
    }

    def mapConstructor(sym: Symbol, cInfo: ConstructorInfo) = {
      constructors(sym) = cInfo
    }

    private def getMethod(sym: Symbol): MethodInfo = {
    //private def getMethod(sym: Symbol): MethodInfo = sym match {
//       case SRToInt => toInt
//       case SRToDouble => toDouble
//       case SRToLong => toLong
//       case SRToChar => toChar
//       case SRToFloat => toFloat
//       case SRToBool => toBool
//       case SRToByte => toByte
//       case SRToShort => toShort
//       case _ =>

        methods.get(sym) match {
        case Some(method) => method
        case None =>
          val mClass = getType(sym.owner)
          try {
            val method = mClass.GetMethod(getMethodName(sym), msilParamTypes(sym),
                                          msilType(sym.tpe.resultType))
            if (method eq null) {
              System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym))
              System.out.println("scope = " + sym.owner.tpe.decls)
              throw new Error(sym.fullNameString)
            }
            else {
              mapMethod(sym, method)
              method
            }
          }
          catch {
            case e: Exception =>
              Console.println("While looking up " + mClass + "::" + sym.nameString)
            Console.println("\t" + showsym(sym))
            throw e
          }
      }
    }

    /*
     * add a mapping between sym and mInfo
     */
    private def mapMethod(sym: Symbol, mInfo: MethodInfo):Unit = {
      assert (mInfo != null, mInfo)
      methods(sym) = mInfo
    }

    /*
     * add mapping between sym and method with newName, paramTypes of newClass
     */
    private def mapMethod(sym: Symbol, newClass: MsilType, newName: String, paramTypes: Array[MsilType]): Unit = {
      val methodInfo = newClass.GetMethod(newName, paramTypes)
      assert(methodInfo != null, "Can't find mapping for " + sym + " -> " +
	     newName + "(" + paramTypes + ")")
      mapMethod(sym, methodInfo)
      if (methodInfo.IsStatic)
        dynToStatMapped += sym
    }

    /*
     * add mapping between method with name and paramTypes of clazz to
     * method with newName and newParamTypes of newClass (used for instance
     * for "wait")
     */
    private def mapMethod(
      clazz: Symbol, name: Name, paramTypes: Array[Type],
      newClass: MsilType, newName: String, newParamTypes: Array[MsilType]):Unit = {
	val methodSym = lookupMethod(clazz, name, paramTypes)
	assert(methodSym != null, "cannot find method " + name + "(" +
	       paramTypes + ")" + " in class " + clazz)
	mapMethod(methodSym, newClass, newName, newParamTypes)
      }

    /*
     * add maping for member with name and paramTypes to member
     * newName of newClass (same parameters)
     */
    private def mapMethod(
      clazz: Symbol, name: Name, paramTypes: Array[Type],
      newClass: MsilType, newName: String):Unit = {
	mapMethod(clazz, name, paramTypes, newClass, newName, paramTypes map msilType)
      }

    /*
     * add mapping for all methods with name of clazz to the corresponding
     * method (same parameters) with newName of newClass
     */
    private def mapMethod(
      clazz: Symbol, name: Name,
      newClass: MsilType, newName: String):Unit = {
	val memberSym: Symbol = clazz.tpe.member(name)
	memberSym.tpe match {
	  // alternatives: List[Symbol]
	  case OverloadedType(_, alternatives) =>
	    alternatives.foreach(s => mapMethod(s, newClass, newName, msilParamTypes(s)))

	  // paramTypes: List[Type], resType: Type
	  case MethodType(paramTypes, resType) =>
	    mapMethod(memberSym, newClass, newName, msilParamTypes(memberSym))

	  case _ => abort("member not found: " + clazz + ", " + name)
	}
      }


    /*
     * find the method in clazz with name and paramTypes
     */
    private def lookupMethod(clazz: Symbol, name: Name, paramTypes: Array[Type]):Symbol = {
      val memberSym = clazz.tpe.member(name)
      memberSym.tpe match {
	case OverloadedType(_, alternatives) =>
	  alternatives.find(s => {
	    var i: Int = 0
	    var typesOK: Boolean = true
	    if (paramTypes.length == s.tpe.paramTypes.length){
	      while(i < paramTypes.length){
		if (paramTypes(i) != s.tpe.paramTypes(i))
		  typesOK = false
		i = i + 1
	      }
	    } else {
	      typesOK = false
	    }
	    typesOK
	  }) match {
	    case Some(sym) => sym
	    case None => abort("member of " + clazz + ", " + name + "(" +
			       paramTypes + ") not found")
	  }

	case MethodType(_, _) => memberSym

	case _ => abort("member not found: " + name + " of " + clazz)
      }
    }

    def getMethodName(methodSym: Symbol): String = {
      val name = methodSym.name
      val params = methodSym.tpe.paramTypes
      if (name == nme.finalize_ && params.length == 0)
	"Finalize"
      else if (name == nme.toString_ && params.length == 0)
	"ToString"
      else if (name == nme.hashCode_ && params.length == 0)
	"GetHashCode"
      else if (name == nme.equals_ && params.length == 1 &&
	      params(0) == definitions.ObjectClass.tpe)
	"Equals"
      // FIXME: why is there no nme.clone_ ?
      else if (name.toString() == "clone" && params.length == 0)
	"Clone"
      else
	msilName(methodSym)
    }

    private def showsym(sym: Symbol): String = (sym.toString +
      "\n  symbol = " + Flags.flagsToString(sym.flags) + " " + sym +
      "\n  owner  = " + Flags.flagsToString(sym.owner.flags) + " " + sym.owner
    )

  } // class BytecodeGenerator

} // class GenMSIL