/* NSC -- new scala compiler
 * Copyright 2004-2013 LAMP/EPFL
 */

package scala.tools.nsc
package symtab
package clr

import java.io.IOException
import io.MsilFile
import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute, _}
import scala.collection.{ mutable, immutable }
import scala.reflect.internal.pickling.UnPickler
import ch.epfl.lamp.compiler.msil.Type.TMVarUsage
import scala.language.implicitConversions

/**
 *  @author Nikolay Mihaylov
 */
abstract class TypeParser {

  val global: Global

  import global._
  import loaders.clrTypes

  //##########################################################################

  private var clazz: Symbol = _
  private var instanceDefs: Scope = _   // was members
  private var staticModule: Symbol = _  // was staticsClass
  private var staticDefs: Scope = _     // was statics

  protected def statics: Symbol = staticModule.moduleClass

  protected var busy: Boolean = false       // lock to detect recursive reads

  private object unpickler extends UnPickler {
    val global: TypeParser.this.global.type = TypeParser.this.global
  }

  def parse(typ: MSILType, root: Symbol) {

    def handleError(e: Throwable) = {
      if (settings.debug.value) e.printStackTrace()  //debug
      throw new IOException("type '" + typ.FullName + "' is broken\n(" + e.getMessage() + ")")
    }
    assert(!busy)
    busy = true

    if (root.isModule) {
      this.clazz = root.companionClass
      this.staticModule = root
    } else {
      this.clazz = root
      this.staticModule = root.companionModule
    }
    try {
      parseClass(typ)
    } catch {
      case e: FatalError => handleError(e)
      case e: RuntimeException => handleError(e)
    }
    busy = false
  }

  class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter {
    override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") }
  }

  /* the names `classTParams` and `newTParams` stem from the forJVM version (ClassfileParser.sigToType())
  *  but there are differences that should be kept in mind.
  *  forMSIL, a nested class knows nothing about any type-params in the nesting class,
  *  therefore newTParams is redundant (other than for recording lexical order),
  *  it always contains the same elements as classTParams.value */
  val classTParams = scala.collection.mutable.Map[Int,Symbol]() // TODO should this be a stack? (i.e., is it possible for >1 invocation to getCLRType on the same TypeParser instance be active )
  val newTParams = new scala.collection.mutable.ListBuffer[Symbol]()
  val methodTParams = scala.collection.mutable.Map[Int,Symbol]()

  private def sig2typeBounds(tvarCILDef: GenericParamAndConstraints): Type = {
    val ts = new scala.collection.mutable.ListBuffer[Type]
    for (cnstrnt <- tvarCILDef.Constraints) {
      ts += getCLRType(cnstrnt) // TODO we're definitely not at or after erasure, no need to call objToAny, right?
    }
    TypeBounds.upper(intersectionType(ts.toList, clazz))
    // TODO variance???
  }

  private def createViewFromTo(viewSuffix : String, fromTpe : Type, toTpe : Type,
                               addToboxMethodMap : Boolean, isAddressOf : Boolean) : Symbol = {
    val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead?
    val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(List(fromTpe)), toTpe)
    val vmsym = createMethod(nme.view_ + viewSuffix, flags, viewMethodType, null, true);
    // !!! this used to mutate a mutable map in definitions, but that map became
    // immutable and this kept "working" with a no-op.  So now it's commented out
    // since I retired the deprecated code which allowed for that bug.
    //
    // if (addToboxMethodMap) definitions.boxMethod(clazz) = vmsym

    if (isAddressOf) clrTypes.addressOfViews += vmsym
    vmsym
  }

  private def createDefaultConstructor(typ: MSILType) {
    val attrs = MethodAttributes.Public | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName // TODO instance
    val declType= typ
    val method = new ConstructorInfo(declType, attrs, Array[MSILType]())
    val flags = Flags.JAVA
    val owner = clazz
    val methodSym = owner.newMethod(nme.CONSTRUCTOR, NoPosition, flags)
    val rettype = clazz.tpe
    val mtype = methodType(Array[MSILType](), rettype);
    val mInfo = mtype(methodSym)
    methodSym.setInfo(mInfo)
    instanceDefs.enter(methodSym);
    clrTypes.constructors(methodSym) = method
  }

  private def parseClass(typ: MSILType) {

    {
      val t4c = clrTypes.types.get(clazz)
      assert(t4c == None || t4c == Some(typ))
    }
    clrTypes.types(clazz) = typ

    {
      val c4t = clrTypes.sym2type.get(typ)
      assert(c4t == None || c4t == Some(clazz))
    }
    clrTypes.sym2type(typ) = clazz

    if (typ.IsDefined(clrTypes.SCALA_SYMTAB_ATTR, false)) {
      val attrs = typ.GetCustomAttributes(clrTypes.SCALA_SYMTAB_ATTR, false);
      assert (attrs.length == 1, attrs.length);
      val a = attrs(0).asInstanceOf[MSILAttribute];
      assert (a.getConstructor() == clrTypes.SYMTAB_CONSTR);
      val symtab = a.getConstructorArguments()(0).asInstanceOf[Array[Byte]]
      unpickler.unpickle(symtab, 0, clazz, staticModule, typ.FullName);
      val mClass = clrTypes.getType(typ.FullName + "$");
      if (mClass != null) {
        clrTypes.types(statics) = mClass;
        val moduleInstance = mClass.GetField("MODULE$");
        assert (moduleInstance != null, mClass);
        clrTypes.fields(statics) = moduleInstance;
      }
      return
    }
    val flags = translateAttributes(typ)

    var clazzBoxed : Symbol = NoSymbol
    var clazzMgdPtr : Symbol = NoSymbol

    val canBeTakenAddressOf = (typ.IsValueType || typ.IsEnum) && (typ.FullName != "System.Enum")

    if(canBeTakenAddressOf) {
      clazzBoxed = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("Boxed"))
      clazzMgdPtr = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("MgdPtr"))
      clrTypes.mdgptrcls4clssym(clazz) =  clazzMgdPtr
      /* adding typMgdPtr to clrTypes.sym2type should happen early (before metadata for supertypes is parsed,
         before metadata for members are parsed) so that clazzMgdPtr can be found by getClRType. */
      val typMgdPtr = MSILType.mkByRef(typ)
      clrTypes.types(clazzMgdPtr) = typMgdPtr
      clrTypes.sym2type(typMgdPtr) = clazzMgdPtr
      /* clazzMgdPtr but not clazzBoxed is mapped by clrTypes.types into an msil.Type instance,
         because there's no metadata-level representation for a "boxed valuetype" */
      val instanceDefsMgdPtr = newScope
      val classInfoMgdPtr = ClassInfoType(definitions.anyvalparam, instanceDefsMgdPtr, clazzMgdPtr)
      clazzMgdPtr.setFlag(flags)
      clazzMgdPtr.setInfo(classInfoMgdPtr)
    }

/* START CLR generics (snippet 1) */
    // first pass
    for (tvarCILDef <- typ.getSortedTVars() ) {
      val tpname = newTypeName(tvarCILDef.Name.replaceAll("!", "")) // TODO are really all type-params named in all assemblies out there? (NO)
      val tpsym = clazz.newTypeParameter(tpname)
      classTParams.put(tvarCILDef.Number, tpsym)
      newTParams += tpsym
      // TODO wouldn't the following also be needed later, i.e. during getCLRType
      tpsym.setInfo(definitions.AnyClass.tpe)
    }
    // second pass
    for (tvarCILDef <- typ.getSortedTVars() ) {
      val tpsym = classTParams(tvarCILDef.Number)
      tpsym.setInfo(sig2typeBounds(tvarCILDef)) // we never skip bounds unlike in forJVM
    }
/* END CLR generics (snippet 1) */
    val ownTypeParams = newTParams.toList
/* START CLR generics (snippet 2) */
    if (!ownTypeParams.isEmpty) {
      clazz.setInfo(new TypeParamsType(ownTypeParams))
      if(typ.IsValueType && !typ.IsEnum) {
        clazzBoxed.setInfo(new TypeParamsType(ownTypeParams))
      }
    }
/* END CLR generics (snippet 2) */
    instanceDefs = newScope
    staticDefs = newScope

    val classInfoAsInMetadata = {
        val ifaces: Array[MSILType] = typ.getInterfaces()
        val superType = if (typ.BaseType() != null) getCLRType(typ.BaseType())
                        else if (typ.IsInterface()) definitions.ObjectClass.tpe
                        else definitions.AnyClass.tpe; // this branch activates for System.Object only.
        // parents (i.e., base type and interfaces)
        val parents = new scala.collection.mutable.ListBuffer[Type]()
        parents += superType
        for (iface <- ifaces) {
          parents += getCLRType(iface)  // here the variance doesn't matter
        }
        // methods, properties, events, fields are entered in a moment
        if (canBeTakenAddressOf) {
          val instanceDefsBoxed = newScope
          ClassInfoType(parents.toList, instanceDefsBoxed, clazzBoxed)
        } else
          ClassInfoType(parents.toList, instanceDefs, clazz)
      }

    val staticInfo = ClassInfoType(List(), staticDefs, statics)

    clazz.setFlag(flags)

    if (canBeTakenAddressOf) {
      clazzBoxed.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata
                          else genPolyType(ownTypeParams, classInfoAsInMetadata) )
      clazzBoxed.setFlag(flags)
      val rawValueInfoType = ClassInfoType(definitions.anyvalparam, instanceDefs, clazz)
      clazz.setInfo( if (ownTypeParams.isEmpty) rawValueInfoType
                     else genPolyType(ownTypeParams, rawValueInfoType) )
    } else {
      clazz.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata
                     else genPolyType(ownTypeParams, classInfoAsInMetadata) )
    }

    // TODO I don't remember if statics.setInfo and staticModule.setInfo should also know about type params
    statics.setFlag(Flags.JAVA)
    statics.setInfo(staticInfo)
    staticModule.setFlag(Flags.JAVA)
    staticModule.setInfo(statics.tpe)


    if (canBeTakenAddressOf) {
      //  implicit conversions are owned by staticModule.moduleClass
      createViewFromTo("2Boxed", clazz.tpe, clazzBoxed.tpe, addToboxMethodMap = true, isAddressOf = false)
      // createViewFromTo("2Object", clazz.tpe, definitions.ObjectClass.tpe, addToboxMethodMap = true, isAddressOf = false)
      createViewFromTo("2MgdPtr", clazz.tpe, clazzMgdPtr.tpe, addToboxMethodMap = false, isAddressOf = true)
      // a return can't have type managed-pointer, thus a dereference-conversion is not needed
      // similarly, a method can't declare as return type "boxed valuetype"
      if (!typ.IsEnum) {
        // a synthetic default constructor for raw-type allows `new X' syntax
        createDefaultConstructor(typ)
      }
    }

    // import nested types
    for (ntype <- typ.getNestedTypes() if !(ntype.IsNestedPrivate || ntype.IsNestedAssembly || ntype.IsNestedFamANDAssem)
				                                 || ntype.IsInterface /* TODO why shouldn't nested ifaces be type-parsed too? */ )
      {
        val loader = new loaders.MsilFileLoader(new MsilFile(ntype))
              val nclazz = statics.newClass(ntype.Name)
        val nmodule = statics.newModule(ntype.Name)
	nclazz.setInfo(loader)
	nmodule.setInfo(loader)
	staticDefs.enter(nclazz)
	staticDefs.enter(nmodule)

	assert(nclazz.companionModule == nmodule, nmodule)
	assert(nmodule.companionClass == nclazz, nclazz)
      }

    val fields = typ.getFields()
    for (field <- fields
         if !(field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly)
         if (getCLRType(field.FieldType) != null)
         ) {
      assert (!field.FieldType.IsPointer && !field.FieldType.IsByRef, "CLR requirement")
      val flags = translateAttributes(field);
      val name = newTermName(field.Name);
      val fieldType =
        if (field.IsLiteral && !field.FieldType.IsEnum && isDefinedAtgetConstant(getCLRType(field.FieldType)))
	      ConstantType(getConstant(getCLRType(field.FieldType), field.getValue))
	    else
	      getCLRType(field.FieldType)
      val owner = if (field.IsStatic()) statics else clazz;
      val sym = owner.newValue(name, NoPosition, flags).setInfo(fieldType);
        // TODO: set private within!!! -> look at typechecker/Namers.scala
        (if (field.IsStatic()) staticDefs else instanceDefs).enter(sym);
      clrTypes.fields(sym) = field;
    }

    for (constr <- typ.getConstructors() if !constr.IsStatic() && !constr.IsPrivate() &&
         !constr.IsAssembly() && !constr.IsFamilyAndAssembly() && !constr.HasPtrParamOrRetType())
      createMethod(constr);

    // initially also contains getters and setters of properties.
    val methodsSet = new mutable.HashSet[MethodInfo]();
    methodsSet ++= typ.getMethods();

    for (prop <- typ.getProperties) {
      val propType: Type = getCLSType(prop.PropertyType);
      if (propType != null) {
	val getter: MethodInfo = prop.GetGetMethod(true);
	val setter: MethodInfo = prop.GetSetMethod(true);
	var gparamsLength: Int = -1;
	if (!(getter == null || getter.IsPrivate || getter.IsAssembly
              || getter.IsFamilyAndAssembly || getter.HasPtrParamOrRetType))
	  {
	    assert(prop.PropertyType == getter.ReturnType);
	    val gparams: Array[ParameterInfo] = getter.GetParameters();
	    gparamsLength = gparams.length;
            val name: TermName = if (gparamsLength == 0) prop.Name else nme.apply;
	    val flags = translateAttributes(getter);
	    val owner: Symbol = if (getter.IsStatic) statics else clazz;
            val methodSym = owner.newMethod(name, NoPosition, flags)
      val mtype: Type = if (gparamsLength == 0) NullaryMethodType(propType) // .NET properties can't be polymorphic
                        else methodType(getter, getter.ReturnType)(methodSym)
        methodSym.setInfo(mtype);
	    methodSym.setFlag(Flags.ACCESSOR);
	    (if (getter.IsStatic) staticDefs else instanceDefs).enter(methodSym)
	    clrTypes.methods(methodSym) = getter;
	    methodsSet -= getter;
	  }
	if (!(setter == null || setter.IsPrivate || setter.IsAssembly
             || setter.IsFamilyAndAssembly || setter.HasPtrParamOrRetType))
	  {
	    val sparams: Array[ParameterInfo] = setter.GetParameters()
	    if(getter != null)
	      assert(getter.IsStatic == setter.IsStatic);
	    assert(setter.ReturnType == clrTypes.VOID);
	    if(getter != null)
	      assert(sparams.length == gparamsLength + 1, "" + getter + "; " + setter);

            val name: TermName = if (gparamsLength == 0) nme.getterToSetter(prop.Name)
			     else nme.update;
	    val flags = translateAttributes(setter);
	    val mtype = methodType(setter, definitions.UnitClass.tpe);
	    val owner: Symbol = if (setter.IsStatic) statics else clazz;
            val methodSym = owner.newMethod(name, NoPosition, flags)
        methodSym.setInfo(mtype(methodSym))
	    methodSym.setFlag(Flags.ACCESSOR);
	    (if (setter.IsStatic) staticDefs else instanceDefs).enter(methodSym);
	    clrTypes.methods(methodSym) = setter;
	    methodsSet -= setter;
	  }
      }
    }

/*    for (event <- typ.GetEvents) {
      // adding += and -= methods to add delegates to an event.
      // raising the event ist not possible from outside the class (this is so
      // generally in .net world)
      val adder: MethodInfo = event.GetAddMethod();
      val remover: MethodInfo = event.GetRemoveMethod();
      if (!(adder == null || adder.IsPrivate || adder.IsAssembly
	    || adder.IsFamilyAndAssembly))
	{
	  assert(adder.ReturnType == clrTypes.VOID);
	  assert(adder.GetParameters().map(_.ParameterType).toList == List(event.EventHandlerType));
	  val name = encode("+=");
	  val flags = translateAttributes(adder);
	  val mtype: Type = methodType(adder, adder.ReturnType);
	  createMethod(name, flags, mtype, adder, adder.IsStatic)
	  methodsSet -= adder;
	}
      if (!(remover == null || remover.IsPrivate || remover.IsAssembly
	    || remover.IsFamilyAndAssembly))
	{
	  assert(remover.ReturnType == clrTypes.VOID);
	  assert(remover.GetParameters().map(_.ParameterType).toList == List(event.EventHandlerType));
	  val name = encode("-=");
	  val flags = translateAttributes(remover);
	  val mtype: Type = methodType(remover, remover.ReturnType);
	  createMethod(name, flags, mtype, remover, remover.IsStatic)
	  methodsSet -= remover;
	}
    } */

/* Adds view amounting to syntax sugar for a CLR implicit overload.
   The long-form syntax can also be supported if "methodsSet -= method" (last statement) is removed.

    /* remember, there's typ.getMethods and type.GetMethods  */
    for (method <- typ.getMethods)
      if(!method.HasPtrParamOrRetType &&
              method.IsPublic && method.IsStatic && method.IsSpecialName &&
              method.Name == "op_Implicit") {
        // create a view: typ => method's return type
        val viewRetType: Type = getCLRType(method.ReturnType)
        val viewParamTypes: List[Type] = method.GetParameters().map(_.ParameterType).map(getCLSType).toList;
        /* The spec says "The operator method shall be defined as a static method on either the operand or return type."
         *  We don't consider the declaring type for the purposes of definitions.functionType,
         * instead we regard op_Implicit's argument type and return type as defining the view's signature.
         */
        if (viewRetType != null && !viewParamTypes.contains(null)) {
          /* The check above applies e.g. to System.Decimal that has a conversion from UInt16, a non-CLS type, whose CLS-mapping returns null */
          val funType: Type = definitions.functionType(viewParamTypes, viewRetType);
          val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead?
          val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(viewParamTypes), funType)
          val vmsym = createMethod(nme.view_, flags, viewMethodType, method, true);
          methodsSet -= method;
        }
      }
*/

    for (method <- methodsSet.iterator)
      if (!method.IsPrivate() && !method.IsAssembly() && !method.IsFamilyAndAssembly()
           && !method.HasPtrParamOrRetType)
        createMethod(method);

    // Create methods and views for delegate support
    if (clrTypes.isDelegateType(typ)) {
      createDelegateView(typ)
      createDelegateChainers(typ)
    }

    // for enumerations introduce comparison and bitwise logical operations;
    // the backend will recognize them and replace them with comparison or
    // bitwise logical operations on the primitive underlying type

    if (typ.IsEnum) {
      val ENUM_CMP_NAMES = List(nme.EQ, nme.NE, nme.LT, nme.LE, nme.GT, nme.GE);
      val ENUM_BIT_LOG_NAMES = List(nme.OR, nme.AND, nme.XOR);

      val flags = Flags.JAVA | Flags.FINAL
      for (cmpName <- ENUM_CMP_NAMES) {
        val enumCmp = clazz.newMethod(cmpName)
        val enumCmpType = JavaMethodType(enumCmp.newSyntheticValueParams(List(clazz.tpe)), definitions.BooleanClass.tpe)
        enumCmp.setFlag(flags).setInfo(enumCmpType)
        instanceDefs.enter(enumCmp)
      }

      for (bitLogName <- ENUM_BIT_LOG_NAMES) {
        val enumBitLog = clazz.newMethod(bitLogName)
        val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), clazz.tpe /* was classInfo, infinite typer */)
        enumBitLog.setFlag(flags).setInfo(enumBitLogType)
        instanceDefs.enter(enumBitLog)
      }
    }

  } // parseClass

  private def populateMethodTParams(method: MethodBase, methodSym: MethodSymbol) : List[Symbol] = {
    if(!method.IsGeneric) Nil
    else {
      methodTParams.clear
      val newMethodTParams = new scala.collection.mutable.ListBuffer[Symbol]()

      // first pass
      for (mvarCILDef <- method.getSortedMVars() ) {
        val mtpname = newTypeName(mvarCILDef.Name.replaceAll("!", "")) // TODO are really all method-level-type-params named in all assemblies out there? (NO)
        val mtpsym = methodSym.newTypeParameter(mtpname)
        methodTParams.put(mvarCILDef.Number, mtpsym)
        newMethodTParams += mtpsym
        // TODO wouldn't the following also be needed later, i.e. during getCLRType
        mtpsym.setInfo(definitions.AnyClass.tpe)
      }
      // second pass
      for (mvarCILDef <- method.getSortedMVars() ) {
        val mtpsym = methodTParams(mvarCILDef.Number)
        mtpsym.setInfo(sig2typeBounds(mvarCILDef)) // we never skip bounds unlike in forJVM
      }

      newMethodTParams.toList
    }
  }

  private def createMethod(method: MethodBase) {

    val flags = translateAttributes(method);
    val owner = if (method.IsStatic()) statics else clazz;
    val methodSym = owner.newMethod(getName(method), NoPosition, flags)
    /* START CLR generics (snippet 3) */
    val newMethodTParams = populateMethodTParams(method, methodSym)
    /* END CLR generics (snippet 3) */

    val rettype = if (method.IsConstructor()) clazz.tpe
                  else getCLSType(method.asInstanceOf[MethodInfo].ReturnType);
    if (rettype == null) return;
    val mtype = methodType(method, rettype);
    if (mtype == null) return;
/* START CLR generics (snippet 4) */
    val mInfo = if (method.IsGeneric) genPolyType(newMethodTParams, mtype(methodSym))
                else mtype(methodSym)
/* END CLR generics (snippet 4) */
/* START CLR non-generics (snippet 4)
    val mInfo = mtype(methodSym)
   END CLR non-generics (snippet 4) */
    methodSym.setInfo(mInfo)
    (if (method.IsStatic()) staticDefs else instanceDefs).enter(methodSym);
    if (method.IsConstructor())
      clrTypes.constructors(methodSym) = method.asInstanceOf[ConstructorInfo]
    else clrTypes.methods(methodSym) = method.asInstanceOf[MethodInfo];
  }

  private def createMethod(name: TermName, flags: Long, args: Array[MSILType], retType: MSILType, method: MethodInfo, statik: Boolean): Symbol = {
    val mtype = methodType(args, getCLSType(retType))
    assert(mtype != null)
    createMethod(name, flags, mtype, method, statik)
  }

  private def createMethod(name: TermName, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = {
    val methodSym: Symbol = (if (statik)  statics else clazz).newMethod(name)
    methodSym.setFlag(flags).setInfo(mtype(methodSym))
    (if (statik) staticDefs else instanceDefs).enter(methodSym)
    if (method != null)
      clrTypes.methods(methodSym)  = method
    methodSym
  }

  private def createDelegateView(typ: MSILType) = {
    val invoke: MethodInfo = typ.GetMember("Invoke")(0).asInstanceOf[MethodInfo];
    val invokeRetType: Type = getCLRType(invoke.ReturnType);
    val invokeParamTypes: List[Type] =invoke.GetParameters().map(_.ParameterType).map(getCLSType).toList;
    val funType: Type = definitions.functionType(invokeParamTypes, invokeRetType);

    val typClrType: Type = getCLRType(typ);
    val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? think not needed

    // create the forward view: delegate => function
    val delegateParamTypes: List[Type] = List(typClrType);
    // not ImplicitMethodType, this is for methods with implicit parameters (not implicit methods)
    val forwardViewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(delegateParamTypes), funType)
    val fmsym = createMethod(nme.view_, flags, forwardViewMethodType, null, true);

    // create the backward view: function => delegate
    val functionParamTypes: List[Type] = List(funType);
    val backwardViewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(functionParamTypes), typClrType)
    val bmsym = createMethod(nme.view_, flags, backwardViewMethodType, null, true);
  }

  private def createDelegateChainers(typ: MSILType) = {
    val flags: Long = Flags.JAVA | Flags.FINAL
    val args: Array[MSILType] = Array(typ)

    var s = createMethod(encode("+="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_COMBINE, false);
    s = createMethod(encode("-="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_REMOVE, false);

    s = createMethod(nme.PLUS, flags, args, typ, clrTypes.DELEGATE_COMBINE, false);
    s = createMethod(nme.MINUS, flags, args, typ, clrTypes.DELEGATE_REMOVE, false);
  }

  private def getName(method: MethodBase): TermName = {

    def operatorOverload(name : String, paramsArity : Int) : Option[Name] = paramsArity match {
      case 1 => name match {
        // PartitionI.10.3.1
        case "op_Decrement" => Some(encode("--"))
        case "op_Increment" => Some(encode("++"))
        case "op_UnaryNegation" => Some(nme.UNARY_-)
        case "op_UnaryPlus" => Some(nme.UNARY_+)
        case "op_LogicalNot" => Some(nme.UNARY_!)
        case "op_OnesComplement" => Some(nme.UNARY_~)
        /* op_True and op_False have no operator symbol assigned,
           Other methods that will have to be written in full are:
           op_AddressOf & (unary)
           op_PointerDereference * (unary) */
        case _ => None
      }
      case 2 => name match {
        // PartitionI.10.3.2
        case "op_Addition" => Some(nme.ADD)
        case "op_Subtraction" => Some(nme.SUB)
        case "op_Multiply" => Some(nme.MUL)
        case "op_Division" => Some(nme.DIV)
        case "op_Modulus" => Some(nme.MOD)
        case "op_ExclusiveOr" => Some(nme.XOR)
        case "op_BitwiseAnd" => Some(nme.AND)
        case "op_BitwiseOr" => Some(nme.OR)
        case "op_LogicalAnd" => Some(nme.ZAND)
        case "op_LogicalOr" => Some(nme.ZOR)
        case "op_LeftShift" => Some(nme.LSL)
        case "op_RightShift" => Some(nme.ASR)
        case "op_Equality" => Some(nme.EQ)
        case "op_GreaterThan" => Some(nme.GT)
        case "op_LessThan" => Some(nme.LT)
        case "op_Inequality" => Some(nme.NE)
        case "op_GreaterThanOrEqual" => Some(nme.GE)
        case "op_LessThanOrEqual" => Some(nme.LE)

        /* op_MemberSelection is reserved in Scala  */

        /* The standard does not assign operator symbols to op_Assign , op_SignedRightShift , op_UnsignedRightShift ,
         *   and op_UnsignedRightShiftAssignment so those names will be used instead to invoke those methods. */

        /*
          The remaining binary operators are not overloaded in C# and are therefore not in widespread use. They have to be written in full.

          op_RightShiftAssignment      >>=
          op_MultiplicationAssignment  *=
          op_PointerToMemberSelection  ->*
          op_SubtractionAssignment     -=
          op_ExclusiveOrAssignment     ^=
          op_LeftShiftAssignment       <<=
          op_ModulusAssignment         %=
          op_AdditionAssignment        +=
          op_BitwiseAndAssignment      &=
          op_BitwiseOrAssignment       |=
          op_Comma                     ,
          op_DivisionAssignment        /=
        */
        case _ => None
      }
      case _ => None
    }

    if (method.IsConstructor()) return nme.CONSTRUCTOR;
    val name = method.Name;
    if (method.IsStatic()) {
      if(method.IsSpecialName) {
        val paramsArity = method.GetParameters().size
        // handle operator overload, otherwise handle as any static method
        val operName = operatorOverload(name, paramsArity)
        if (operName.isDefined) { return operName.get; }
      }
      return newTermName(name);
    }
    val params = method.GetParameters();
    name match {
      case "GetHashCode" if (params.length == 0) => nme.hashCode_;
      case "ToString" if (params.length == 0) => nme.toString_;
      case "Finalize" if (params.length == 0) => nme.finalize_;
      case "Equals" if (params.length == 1 && params(0).ParameterType == clrTypes.OBJECT) =>
        nme.equals_;
      case "Invoke" if (clrTypes.isDelegateType(method.DeclaringType)) => nme.apply;
      case _ => newTermName(name);
    }
  }

  //##########################################################################

  private def methodType(method: MethodBase, rettype: MSILType): Symbol => Type = {
    val rtype = getCLSType(rettype);
    if (rtype == null) null else methodType(method, rtype);
  }

  /** Return a method type for the given method. */
  private def methodType(method: MethodBase, rettype: Type): Symbol => Type =
    methodType(method.GetParameters().map(_.ParameterType), rettype);

  /** Return a method type for the provided argument types and return type. */
  private def methodType(argtypes: Array[MSILType], rettype: Type): Symbol => Type = {
    def paramType(typ: MSILType): Type =
      if (typ eq clrTypes.OBJECT) definitions.AnyClass.tpe // TODO a hack to compile scalalib, should be definitions.AnyRefClass.tpe
      else getCLSType(typ);
    val ptypes = argtypes.map(paramType).toList;
    if (ptypes.contains(null)) null
    else method => JavaMethodType(method.newSyntheticValueParams(ptypes), rettype);
  }

    //##########################################################################

  private def getClassType(typ: MSILType): Type = {
    assert(typ != null);
    val res = rootMirror.getClassByName(typ.FullName.replace('+', '.') : TypeName).tpe;
    //if (res.isError())
    //  global.reporter.error("unknown class reference " + type.FullName);
    res
  }

  private def getCLSType(typ: MSILType): Type = { // getCLS returns non-null for types GenMSIL can handle, be they CLS-compliant or not
    if (typ.IsTMVarUsage())
    /* START CLR generics (snippet 5) */
      getCLRType(typ)
    /* END CLR generics (snippet 5) */
    /* START CLR non-generics (snippet 5)
      null
       END CLR non-generics (snippet 5) */
    else if ( /* TODO hack if UBYE, uncommented, "ambiguous reference to overloaded definition" ensues, for example for System.Math.Max(x, y) */
              typ == clrTypes.USHORT || typ == clrTypes.UINT || typ == clrTypes.ULONG
      /*  || typ == clrTypes.UBYTE    */
          ||  typ.IsNotPublic()      || typ.IsNestedPrivate()
          ||  typ.IsNestedAssembly() || typ.IsNestedFamANDAssem()
          ||  typ.IsPointer()
          || (typ.IsArray() && getCLRType(typ.GetElementType()) == null)  /* TODO hack: getCLR instead of getCLS */
          || (typ.IsByRef() && !typ.GetElementType().CanBeTakenAddressOf()))
      null
    else
      getCLRType(typ)
  }

  private def getCLRTypeIfPrimitiveNullOtherwise(typ: MSILType): Type =
    if (typ == clrTypes.OBJECT)
      definitions.ObjectClass.tpe;
    else if (typ == clrTypes.VALUE_TYPE)
      definitions.AnyValClass.tpe
    else if (typ == clrTypes.STRING)
      definitions.StringClass.tpe;
    else if (typ == clrTypes.VOID)
      definitions.UnitClass.tpe
    else if (typ == clrTypes.BOOLEAN)
      definitions.BooleanClass.tpe
    else if (typ == clrTypes.CHAR)
      definitions.CharClass.tpe
    else if ((typ == clrTypes.BYTE)  || (typ == clrTypes.UBYTE)) // TODO U... is a hack to compile scalalib
      definitions.ByteClass.tpe
    else if ((typ == clrTypes.SHORT) || (typ == clrTypes.SHORT)) // TODO U... is a hack to compile scalalib
      definitions.ShortClass.tpe
    else if ((typ == clrTypes.INT)   || (typ == clrTypes.UINT))  // TODO U... is a hack to compile scalalib
      definitions.IntClass.tpe
    else if ((typ == clrTypes.LONG)  || (typ == clrTypes.LONG))  // TODO U... is a hack to compile scalalib
      definitions.LongClass.tpe
    else if (typ == clrTypes.FLOAT)
      definitions.FloatClass.tpe
    else if (typ == clrTypes.DOUBLE)
      definitions.DoubleClass.tpe
    else null


  private def getCLRType(tMSIL: MSILType): Type = {
     var res = getCLRTypeIfPrimitiveNullOtherwise(tMSIL)
     if (res != null) res
     else if (tMSIL.isInstanceOf[ConstructedType]) {
       val ct = tMSIL.asInstanceOf[ConstructedType]
       /* START CLR generics (snippet 6) */
             val cttpArgs = ct.typeArgs.map(tmsil => getCLRType(tmsil)).toList
             appliedType(getCLRType(ct.instantiatedType), cttpArgs)
       /* END CLR generics (snippet 6) */
       /* START CLR non-generics (snippet 6)
       getCLRType(ct.instantiatedType)
          END CLR non-generics (snippet 6) */
     } else if (tMSIL.isInstanceOf[TMVarUsage]) {
        /* START CLR generics (snippet 7) */
             val tVarUsage = tMSIL.asInstanceOf[TMVarUsage]
             val tVarNumber = tVarUsage.Number
             if (tVarUsage.isTVar) classTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst
             else methodTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst
        /* END CLR generics (snippet 7) */
       /* START CLR non-generics (snippet 7)
        null // definitions.ObjectClass.tpe
          END CLR non-generics (snippet 7) */
     } else if (tMSIL.IsArray()) {
        var elemtp = getCLRType(tMSIL.GetElementType())
        // cut&pasted from ClassfileParser
        // make unbounded Array[T] where T is a type variable into Array[T with Object]
        // (this is necessary because such arrays have a representation which is incompatible
        // with arrays of primitive types).
        // TODO does that incompatibility also apply to .NET?
        if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< definitions.ObjectClass.tpe))
          elemtp = intersectionType(List(elemtp, definitions.ObjectClass.tpe))
        appliedType(definitions.ArrayClass.tpe, List(elemtp))
     } else {
       res = clrTypes.sym2type.get(tMSIL) match {
         case Some(sym) => sym.tpe
         case None => if (tMSIL.IsByRef && tMSIL.GetElementType.IsValueType) {
                        val addressed = getCLRType(tMSIL.GetElementType)
                        val clasym = addressed.typeSymbolDirect // TODO should be .typeSymbol?
                        clasym.info.load(clasym)
                        val secondAttempt = clrTypes.sym2type.get(tMSIL)
                        secondAttempt match { case Some(sym) => sym.tpe
                                              case None => null
                                            }
                      } else getClassType(tMSIL)
       }
       if (res == null)
         null // TODO new RuntimeException()
       else res
     }
  }

  // the values are Java-Box-Classes (e.g. Integer, Boolean, Character)
  // java.lang.Number to get the value (if a number, not for boolean, character)
  // see ch.epfl.lamp.compiler.msil.util.PEStream.java
  def getConstant(constType: Type, value: Object): Constant = {
    val typeClass = constType.typeSymbol
    if (typeClass == definitions.BooleanClass)
      Constant(value.asInstanceOf[java.lang.Boolean].booleanValue)
    else if (typeClass == definitions.ByteClass)
      Constant(value.asInstanceOf[java.lang.Number].byteValue)
    else if (typeClass == definitions.ShortClass)
      Constant(value.asInstanceOf[java.lang.Number].shortValue)
    else if (typeClass == definitions.CharClass)
      Constant(value.asInstanceOf[java.lang.Character].charValue)
    else if (typeClass == definitions.IntClass)
      Constant(value.asInstanceOf[java.lang.Number].intValue)
    else if (typeClass == definitions.LongClass)
      Constant(value.asInstanceOf[java.lang.Number].longValue)
    else if (typeClass == definitions.FloatClass)
      Constant(value.asInstanceOf[java.lang.Number].floatValue)
    else if (typeClass == definitions.DoubleClass)
      Constant(value.asInstanceOf[java.lang.Number].doubleValue)
    else if (typeClass == definitions.StringClass)
      Constant(value.asInstanceOf[java.lang.String])
    else
      abort("illegal value: " + value + ", class-symbol: " + typeClass)
  }

  def isDefinedAtgetConstant(constType: Type): Boolean = {
    val typeClass = constType.typeSymbol
    if (    (typeClass == definitions.BooleanClass)
         || (typeClass == definitions.ByteClass)
         || (typeClass == definitions.ShortClass)
         || (typeClass == definitions.CharClass)
         || (typeClass == definitions.IntClass)
         || (typeClass == definitions.LongClass)
         || (typeClass == definitions.FloatClass)
         || (typeClass == definitions.DoubleClass)
         || (typeClass == definitions.StringClass)
       )
      true
    else
      false
  }

  private def translateAttributes(typ: MSILType): Long = {
    var flags: Long = Flags.JAVA;
    if (typ.IsNotPublic() || typ.IsNestedPrivate()
	|| typ.IsNestedAssembly() || typ.IsNestedFamANDAssem())
      flags = flags | Flags.PRIVATE;
    else if (typ.IsNestedFamily() || typ.IsNestedFamORAssem())
      flags = flags | Flags.PROTECTED;
    if (typ.IsAbstract())
      flags = flags | Flags.ABSTRACT;
    if (typ.IsSealed())
      flags = flags | Flags.FINAL;
    if (typ.IsInterface())
      flags = flags | Flags.INTERFACE | Flags.TRAIT | Flags.ABSTRACT;

    flags
  }

  private def translateAttributes(field: FieldInfo): Long = {
    var flags: Long = Flags.JAVA;
    if (field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly())
      flags = flags | Flags.PRIVATE;
    else if (field.IsFamily() || field.IsFamilyOrAssembly())
      flags = flags | Flags.PROTECTED;
    if (field.IsInitOnly() || field.IsLiteral())
      flags = flags | Flags.FINAL;
    else
      flags = flags | Flags.MUTABLE;
    if (field.IsStatic)
      flags = flags | Flags.STATIC

    flags
  }

  private def translateAttributes(method: MethodBase): Long = {
    var flags: Long = Flags.JAVA;
    if (method.IsPrivate() || method.IsAssembly() || method.IsFamilyAndAssembly())
      flags = flags | Flags.PRIVATE;
    else if (method.IsFamily() || method.IsFamilyOrAssembly())
      flags = flags | Flags.PROTECTED;
    if (method.IsAbstract())
      flags = flags | Flags.DEFERRED;
    if (method.IsStatic)
      flags = flags | Flags.STATIC

    flags
  }
}
