/* NSC -- new Scala compiler
 * Copyright 2005-2009 LAMP/EPFL
 * @author Martin Odersky
 */
// $Id: CleanUp.scala 18387 2009-07-24 15:28:37Z odersky $

package scala.tools.nsc
package transform

import symtab._
import Flags._
import scala.tools.nsc.util.Position
import scala.collection.mutable.{ListBuffer, HashMap}

abstract class CleanUp extends Transform with ast.TreeDSL {
  import global._
  import definitions._
  import CODE._

  /** the following two members override abstract members in Transform */
  val phaseName: String = "cleanup"

  protected def newTransformer(unit: CompilationUnit): Transformer =
    new CleanUpTransformer(unit)

  class CleanUpTransformer(unit: CompilationUnit) extends Transformer {
    private val newDefs = new ListBuffer[Tree]
    private val newInits = new ListBuffer[Tree]
    
    private val classConstantMeth = new HashMap[String, Symbol]

    // a map from the symbols of the Scala primitive types to the symbols
    // of the modules of the Java box classes
    private val javaBoxClassModule = new HashMap[Symbol, Symbol]

    if (!forMSIL) {
      javaBoxClassModule(BooleanClass) = getModule("java.lang.Boolean")
      javaBoxClassModule(ByteClass)    = getModule("java.lang.Byte")
      javaBoxClassModule(ShortClass)   = getModule("java.lang.Short")
      javaBoxClassModule(IntClass)     = getModule("java.lang.Integer")
      javaBoxClassModule(CharClass)    = getModule("java.lang.Character")
      javaBoxClassModule(LongClass)    = getModule("java.lang.Long")
      javaBoxClassModule(FloatClass)   = getModule("java.lang.Float")
      javaBoxClassModule(DoubleClass)  = getModule("java.lang.Double")
      javaBoxClassModule(UnitClass)    = getModule("java.lang.Void")
    }

    private var localTyper: analyzer.Typer = null
    
    private object MethodDispatchType extends scala.Enumeration {
      val NO_CACHE, MONO_CACHE, POLY_CACHE = Value
    }
    import MethodDispatchType.{ NO_CACHE, MONO_CACHE, POLY_CACHE }
    private def dispatchType() = settings.refinementMethodDispatch.value match {
      case "no-cache"   => NO_CACHE
      case "mono-cache" => MONO_CACHE
      case "poly-cache" => POLY_CACHE
    }
      
    private def typedWithPos(pos: Position)(tree: Tree) =
      localTyper typed { atPos(pos)(tree) }

    private def classConstantMethod(pos: Position, sig: String): Symbol = 
      (classConstantMeth get sig) getOrElse {
        val forName = getMember(ClassClass.linkedModuleOfClass, nme.forName)
        val owner = currentOwner.enclClass

        val cvar = owner.newVariable(pos, unit.fresh.newName(pos, "class$Cache"))
          .setFlag(PRIVATE | STATIC | MUTABLE | SYNTHETIC).setInfo(ClassClass.tpe)
        owner.info.decls enter cvar
        val cdef = typedWithPos(pos) { VAL(cvar) === NULL }

        val meth = owner.newMethod(pos, unit.fresh.newName(pos, "class$Method"))
          .setFlag(PRIVATE | STATIC | SYNTHETIC).setInfo(MethodType(List(), ClassClass.tpe))
        owner.info.decls enter meth
        val mdef = typedWithPos(pos)(DEF(meth) === 
          gen.mkCached(cvar, Apply(REF(forName), List(Literal(sig))))
        )

        newDefs.append(cdef, mdef)
        classConstantMeth.update(sig, meth)
        meth
      }
    
    override def transformUnit(unit: CompilationUnit) =
      unit.body = transform(unit.body)
        
    /** A value class is defined to be only Java-compatible values: unit is
      * not part of it, as opposed to isValueClass in definitions. scala.Int is
      * a value class, java.lang.Integer is not. */
    def isValueClass(sym: Symbol) = boxedClass contains sym

    override def transform(tree: Tree): Tree = tree match {
        
      /* Transforms dynamic calls (i.e. calls to methods that are undefined
       * in the erased type space) to -- dynamically -- unsafe calls using
       * reflection. This is used for structural sub-typing of refinement
       * types, but may be used for other dynamic calls in the future.
       * For 'a.f(b)' it will generate something like:
       * 'a.getClass().
       * '  getMethod("f", Array(classOf[b.type])).
       * '  invoke(a, Array(b)) 
       * plus all the necessary casting/boxing/etc. machinery required
       * for type-compatibility (see fixResult and fixParams).
       * 
       * USAGE CONTRACT:
       * There are a number of assumptions made on the way a dynamic apply
       * is used. Assumptions relative to type are handled by the erasure
       * phase.
       * - The applied arguments are compatible with AnyRef, which means
       *   that an argument tree typed as AnyVal has already been extended
       *   with the necessary boxing calls. This implies that passed
       *   arguments might not be strictly compatible with the method's
       *   parameter types (a boxed integer while int is expected).
       * - The expected return type is an AnyRef, even when the method's
       *   return type is an AnyVal. This means that the tree containing the
       *   call has already been extended with the necessary unboxing calls
       *   (or is happy with the boxed type).
       * - The type-checker has prevented dynamic applies on methods which
       *   parameter's erased types are not statically known at the call site.
       *   This is necessary to allow dispatching the call to the correct
       *   method (dispatching on paramters is static in Scala). In practice,
       *   this limitation only arises when the called method is defined as a
       *   refinement, where the refinement defines a parameter based on a
       *   type variable. */
      case ad@ApplyDynamic(qual0, params) =>
        def mkName(s: String = "") =
          if (s == "") unit.fresh newName ad.pos
          else unit.fresh.newName(ad.pos, s)
        def mkTerm(s: String = "") = newTermName(mkName(s))
        val typedPos = typedWithPos(ad.pos) _
        
        assert(ad.symbol.isPublic)
        var qual: Tree = qual0
          
        /* ### CREATING THE METHOD CACHE ### */
        
        def addStaticVariableToClass(forName: String, forType: Type, forInit: Tree): Symbol = {
          val varSym = currentClass.newVariable(ad.pos, mkName(forName))
            .setFlag(PRIVATE | STATIC | MUTABLE | SYNTHETIC)
            .setInfo(forType)
          currentClass.info.decls enter varSym
          
          val varDef = typedPos( VAL(varSym) === forInit )
          newDefs append transform(varDef)
          
          val varInit = typedPos( REF(varSym) === forInit )
          newInits append transform(varInit)
          
          varSym
        }
        
        def addStaticMethodToClass(forName: String, forArgsTypes: List[Type], forResultType: Type)
                                  (forBody: Pair[Symbol, List[Symbol]] => Tree): Symbol = {
          val methSym = currentClass.newMethod(ad.pos, mkName(forName))
            .setFlag(STATIC | SYNTHETIC)
            
          methSym.setInfo(MethodType(methSym.newSyntheticValueParams(forArgsTypes), forResultType))
          currentClass.info.decls enter methSym
          
          val methDef = typedPos( DefDef(methSym, { forBody(Pair(methSym, methSym.paramss(0))) }) )
          newDefs append transform(methDef)
          
          methSym
        }
        
        def fromTypesToClassArrayLiteral(paramTypes: List[Type]): Tree =
          ArrayValue(TypeTree(ClassClass.tpe), paramTypes map LIT)
        
        def theTypeClassArray =
          TypeRef(ArrayClass.tpe.prefix, ArrayClass, List(ClassClass.tpe))
        
        /* ... */
        def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = dispatchType match {
          case NO_CACHE =>
              
              /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)":
                
                var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
                
                def reflMethod$Method(forReceiver: JClass[_]): JMethod =
                  forReceiver.getMethod("xyz", reflParams$Cache)
              
              */

              val reflParamsCacheSym: Symbol =
                addStaticVariableToClass("reflParams$Cache", theTypeClassArray, fromTypesToClassArrayLiteral(paramTypes))
              
              addStaticMethodToClass("reflMethod$Method", List(ClassClass.tpe), MethodClass.tpe) {
                case Pair(reflMethodSym, List(forReceiverSym)) =>
                  (REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym))
              }
              
            case MONO_CACHE =>
              
              /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)":
                
                var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
              
                var reflMethod$Cache: JMethod = null
                
                var reflClass$Cache: JClass[_] = null
                
                def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
                  if (reflClass$Cache != forReceiver) {
                    reflMethod$Cache = forReceiver.getMethod("xyz", reflParams$Cache)
                    reflClass$Cache = forReceiver
                  }
                  reflMethod$Cache
                }
              
              */
              
              val reflParamsCacheSym: Symbol =
                addStaticVariableToClass("reflParams$Cache", theTypeClassArray, fromTypesToClassArrayLiteral(paramTypes))
              
              val reflMethodCacheSym: Symbol =
                addStaticVariableToClass("reflMethod$Cache", MethodClass.tpe, NULL)
              
              val reflClassCacheSym: Symbol =
                addStaticVariableToClass("reflClass$Cache", ClassClass.tpe, NULL)
                
              def getMethodSym = ClassClass.tpe member nme.getMethod_
              
              addStaticMethodToClass("reflMethod$Method", List(ClassClass.tpe), MethodClass.tpe) {
                case Pair(reflMethodSym, List(forReceiverSym)) =>
                  BLOCK(
                    IF (REF(reflClassCacheSym) ANY_NE REF(forReceiverSym)) THEN BLOCK(
                      REF(reflMethodCacheSym) === ((REF(forReceiverSym) DOT getMethodSym)(LIT(method), REF(reflParamsCacheSym))) ,
                      REF(reflClassCacheSym) === REF(forReceiverSym),
                      UNIT
                    ) ENDIF,
                    REF(reflMethodCacheSym)
                  )
              }
              
            case POLY_CACHE =>
              
              /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)":
                
                var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
              
                var reflPoly$Cache: scala.runtime.MethodCache = new EmptyMethodCache()
                
                def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
                  var method: JMethod = reflPoly$Cache.find(forReceiver)
                  if (method != null)
                    return method
                  else {
                    method = forReceiver.getMethod("xyz", reflParams$Cache)
                    reflPoly$Cache = reflPoly$Cache.add(forReceiver, method)
                    return method
                  }
                }
              
              */
              
              val reflParamsCacheSym: Symbol =
                addStaticVariableToClass("reflParams$Cache", theTypeClassArray, fromTypesToClassArrayLiteral(paramTypes))
              
              val reflPolyCacheSym: Symbol =
                addStaticVariableToClass("reflPoly$Cache", MethodCacheClass.tpe, NEW(TypeTree(EmptyMethodCacheClass.tpe)))
                
              addStaticMethodToClass("reflMethod$Method", List(ClassClass.tpe), MethodClass.tpe)
                { case Pair(reflMethodSym, List(forReceiverSym)) =>
                  val methodSym = reflMethodSym.newVariable(ad.pos, mkTerm("method")) setInfo MethodClass.tpe
                  
                  BLOCK(
                    VAL(methodSym) === ((REF(reflPolyCacheSym) DOT methodCache_find)(REF(forReceiverSym))) ,
                    IF (REF(methodSym) OBJ_!= NULL) .
                      THEN (Return(REF(methodSym)))
                    ELSE {
                      def methodSymRHS  = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym)))
                      def cacheRHS      = ((REF(reflPolyCacheSym) DOT methodCache_add)(REF(forReceiverSym), REF(methodSym)))
                      BLOCK(
                        REF(methodSym)        === methodSymRHS,
                        REF(reflPolyCacheSym) === cacheRHS,
                        Return(REF(methodSym))
                      )
                    }
                  )
                }
        }
        
        /* ### HANDLING METHODS NORMALLY COMPILED TO OPERATORS ### */
        
        def mayRequirePrimitiveReplacement: Boolean = {
        
          def isBoxed(sym: Symbol): Boolean =
            ((sym isNonBottomSubClass BoxedNumberClass) ||
              (!forMSIL && (sym isNonBottomSubClass BoxedCharacterClass)))
          
          val sym = qual.tpe.typeSymbol
          (sym == definitions.ObjectClass) || isBoxed(sym)
          
        }
        
        val testForNumber: Tree     = (qual IS_OBJ BoxedNumberClass.tpe) OR (qual IS_OBJ BoxedCharacterClass.tpe)
        val testForBoolean: Tree    = (qual IS_OBJ BoxedBooleanClass.tpe)
        val testForNumberOrBoolean  = testForNumber OR testForBoolean
        
        val getPrimitiveReplacementForStructuralCall: PartialFunction[Name, (Symbol, Tree)] = {
          val testsForNumber = Map() ++ List(
            nme.UNARY_+ -> "positive",
            nme.UNARY_- -> "negate",
            nme.UNARY_~ -> "complement",
            nme.ADD     -> "add",
            nme.SUB     -> "subtract",
            nme.MUL     -> "multiply",
            nme.DIV     -> "divide",
            nme.MOD     -> "takeModulo",
            nme.LSL     -> "shiftSignedLeft",
            nme.LSR     -> "shiftLogicalRight",
            nme.ASR     -> "shiftSignedRight",
            nme.LT      -> "testLessThan",
            nme.LE      -> "testLessOrEqualThan",
            nme.GE      -> "testGreaterOrEqualThan",
            nme.GT      -> "testGreaterThan",
            nme.toByte  -> "toByte",
            nme.toShort -> "toShort",
            nme.toChar  -> "toCharacter",
            nme.toInt   -> "toInteger",
            nme.toLong  -> "toLong",
            nme.toFloat -> "toFloat",
            nme.toDouble-> "toDouble"
          )
          val testsForBoolean = Map() ++ List(
            nme.UNARY_! -> "takeNot",
            nme.ZOR     -> "takeConditionalOr",
            nme.ZAND    -> "takeConditionalAnd"
          )
          val testsForNumberOrBoolean = Map() ++ List(
            nme.OR      -> "takeOr",
            nme.XOR     -> "takeXor",
            nme.AND     -> "takeAnd",
            nme.EQ      -> "testEqual",
            nme.NE      -> "testNotEqual"
          )
          def get(name: String) = getMember(BoxesRunTimeClass, name)
          
          /** Begin partial function. */
          {
            case x if testsForNumber contains x           => (get(testsForNumber(x)), testForNumber)
            case x if testsForBoolean contains x          => (get(testsForBoolean(x)), testForBoolean)
            case x if testsForNumberOrBoolean contains x  => (get(testsForNumberOrBoolean(x)), testForNumberOrBoolean)
          }
        }
        
        def boxArray(t: Tree) = {
          val sym = currentOwner.newValue(ad.pos, mkTerm()) setInfo ObjectClass.tpe            
          BLOCK(
            VAL(sym) === t,
            IF (NULL ANY_== REF(sym)) THEN NULL ELSE gen.mkRuntimeCall(nme.boxArray, List(REF(sym)))
          )
        }
        
        /* ### BOXING PARAMS & UNBOXING RESULTS ### */
        
        /* Transforms the result of a reflective call (always an AnyRef) to
         * the actual result value (an AnyRef too). The transformation
         * depends on the method's static return type.
         * - for units (void), the reflective call will return null: a new
         *   boxed unit is generated.
         * - for arrays, the reflective call will return an unboxed array:
         *   the resulting array is boxed.
         * - otherwise, the value is simply casted to the expected type. This
         *   is enough even for value (int et al.) values as the result of
         *   a dynamic call will box them as a side-effect. */
        def fixResult(resType: Type)(tree: Tree): Tree = localTyper typed {
          resType.typeSymbol match {
            case UnitClass    => BLOCK(tree, REF(BoxedUnit_UNIT))
            case ArrayClass   => boxArray(tree)
            case ObjectClass  => tree
            case _            => tree AS_ATTR resType
          }
        }
        
        /* Transforms the parameters of a dynamic apply (always AnyRefs) to
         * something compatible with reflective calls. The transformation depends
         * on the method's static parameter types.
         * - for (unboxed) arrays, the (non-null) value is tested for its erased
         *   type. If it is a boxed array, the array is unboxed. If it is an
         *   unboxed array, it is left alone - except that for varargs in structural
         *   types to work properly, if the parameter is an Array and the parameter
         *   type a Seq, it is routed through the boxed logic. */
        def fixParams(params: List[Tree], paramTypes: List[Type]): List[Tree] = {
          def doUnboxedArray(param: Tree, paramType: Type) = {
            val sym = currentOwner.newValue(ad.pos, mkTerm()) setInfo ObjectClass.tpe
            assert(paramType.typeArgs.length == 1)
            val arrayType = paramType.typeArgs(0).normalize
            lazy val unboxMethod = getMember(BoxedArrayClass, nme.unbox)
            
            BLOCK(
              VAL(sym) === param,
              IF (NULL ANY_== REF(sym)) .
                THEN (NULL) .
              ELSE (
                IF (REF(sym) IS_OBJ BoxedArrayClass.tpe) .
                  THEN (((REF(sym) AS_ATTR BoxedArrayClass.tpe) DOT unboxMethod)(LIT(arrayType)))
                ELSE
                  REF(sym)
              )
            )
          }
          for ((param, paramType) <- params zip paramTypes) yield localTyper typed {
            (param.tpe, paramType.typeSymbol) match {
              case (_, ArrayClass)                        => doUnboxedArray(param, paramType)
              case (TypeRef(_, ArrayClass, _), SeqClass)  => boxArray(param)  // ticket #1141
              case _                                      => param
            }
          }
        }
        
        /* ### CALLING THE APPLY -> one for operators (see above), one for normal methods ### */
        def callAsOperator(paramTypes: List[Type], resType: Type): Tree = localTyper typed {
          def default = callAsMethod(paramTypes, resType)
          // This is more indirect than it should be (and I don't think it spots every
          // case either) but it's the most direct way I could see to address ticket #1110
          // given the information available from this vantage.
          def useOperator =
            (getPrimitiveReplacementForStructuralCall isDefinedAt ad.symbol.name) &&
            ((resType :: paramTypes) forall (x => isValueClass(x.typeSymbol)))
          
          if (useOperator) {
            val (operator, test)  = getPrimitiveReplacementForStructuralCall(ad.symbol.name)
            def args              = qual :: fixParams(params, paramTypes)
            
            IF (test) THEN (REF(operator) APPLY args) ELSE default
          }
          else default
        }
        
        def callAsMethod(paramTypes: List[Type], resType: Type): Tree = localTyper.typed {
          // reflective method call machinery
          val invokeName  = MethodClass.tpe member nme.invoke_    // reflect.Method.invoke(...)
          def cache       = REF(reflectiveMethodCache(ad.symbol.name.toString, paramTypes)) // cache Symbol
          def lookup      = Apply(cache, List(qual GETCLASS))     // get Method object from cache
          def args        = ArrayValue(TypeTree(ObjectClass.tpe), fixParams(params, paramTypes))  // args for invocation
          def invocation  = (lookup DOT invokeName)(qual, args)   // .invoke(qual, ...)
          
          // exception catching machinery
          val invokeExc   = currentOwner.newValue(ad.pos, mkTerm()) setInfo InvocationTargetExceptionClass.tpe
          def catchVar    = Bind(invokeExc, Typed(Ident(nme.WILDCARD), TypeTree(InvocationTargetExceptionClass.tpe)))
          def catchBody   = Throw(Apply(Select(Ident(invokeExc), nme.getCause), Nil))

          // try { method.invoke } catch { case e: InvocationTargetExceptionClass => throw e.getCause() }
          TRY (invocation) CATCH { CASE (catchVar) ==> catchBody } ENDTRY
        }
        
        def getClass(q: Tree): Tree = (q DOT nme.getClass_)()
        
      if (settings.refinementMethodDispatch.value == "invoke-dynamic") {
/*        val guardCallSite: Tree = {
          val cachedClass = addStaticVariableToClass("cachedClass", definitions.ClassClass.tpe, EmptyTree)
          val tmpVar = currentOwner.newVariable(ad.pos, unit.fresh.newName(ad.pos, "x")).setInfo(definitions.AnyRefClass.tpe)
          atPos(ad.pos)(Block(List(
            ValDef(tmpVar, transform(qual))),
            If(Apply(Select(gen.mkAttributedRef(cachedClass), nme.EQ), List(getClass(Ident(tmpVar)))),
               Block(List(Assign(gen.mkAttributedRef(cachedClass), getClass(Ident(tmpVar)))),
                     treeCopy.ApplyDynamic(ad, Ident(tmpVar), transformTrees(params))),
               EmptyTree)))
        }
        //println(guardCallSite)
*/
        localTyper.typed(treeCopy.ApplyDynamic(ad, transform(qual), transformTrees(params)))
      } else {
        
        /* ### BODY OF THE TRANSFORMATION -> remember we're in case ad@ApplyDynamic(qual, params) ### */
        
        /* This creates the tree that does the reflective call (see general comment
         * on the apply-dynamic tree for its format). This tree is simply composed
         * of three succesive calls, first to getClass on the callee, then to
         * getMethod on the classs, then to invoke on the method.
         * - getMethod needs an array of classes for choosing one amongst many
         *   overloaded versions of the method. This is provided by paramTypeClasses
         *   and must be done on the static type as Scala's dispatching is static on
         *   the parameters.
         * - invoke needs an array of AnyRefs that are the method's arguments. The
         *   erasure phase guarantees that any parameter passed to a dynamic apply
         *   is compatible (through boxing). Boxed ints et al. is what invoke expects
         *   when the applied method expects ints, hence no change needed there.
         *   On the other hand, arrays must be dealt with as they must be entered
         *   unboxed in the parameter array of invoke. fixParams is responsible for 
         *   that.
         * - in the end, the result of invoke must be fixed, again to deal with arrays.
         *   This is provided by fixResult. fixResult will cast the invocation's result
         *   to the method's return type, which is generally ok, except when this type
         *   is a value type (int et al.) in which case it must cast to the boxed version
         *   because invoke only returns object and erasure made sure the result is
         *   expected to be an AnyRef. */
        val t: Tree = ad.symbol.tpe match {
          case MethodType(mparams, resType) =>
            assert(params.length == mparams.length)
            typedPos {
              val sym = currentOwner.newValue(ad.pos, mkTerm("qual")) setInfo qual0.tpe
              qual = REF(sym)
              
              def resTypeForFix = if (isValueClass(resType.typeSymbol)) boxedClass(resType.typeSymbol).tpe else resType
              def call          = if (mayRequirePrimitiveReplacement) (callAsOperator _) else (callAsMethod _)
              BLOCK(
                VAL(sym) === qual0,
                fixResult(resTypeForFix)(call(mparams map (_.tpe), resType))
              )
            }
        }
        
        /* For testing purposes, the dynamic application's condition
         * can be printed-out in great detail. Remove? */
        if (settings.debug.value) {
          def paramsToString(xs: Any*) = xs map (_.toString) mkString ", "
          val mstr = ad.symbol.tpe match {
            case MethodType(mparams, resType) =>
              """|  with
                 |  - declared parameter types: '%s'
                 |  - passed argument types:    '%s'
                 |  - result type:              '%s'""" .
                stripMargin.format(
                   paramsToString(mparams),
                   paramsToString(params),
                   resType.toString
                )
            case _ => ""
          }
          Console.printf("""Dynamically application '%s.%s(%s)' %s - resulting code: '%s'""",
              List(qual, ad.symbol.name, paramsToString(params), mstr, t) map (_.toString) : _*
          )
        }

        /* We return the dynamic call tree, after making sure no other
         * clean-up transformation are to be applied on it. */
        transform(t)
        }
        /* ### END OF DYNAMIC APPLY TRANSFORM ### */
      
      /* Some cleanup transformations add members to templates (classes, traits, etc).
       * When inside a template (i.e. the body of one of its members), two maps
       * (newDefs and newInits) are available in the tree transformer. Any mapping from
       * a symbol to a MemberDef (DefDef, ValDef, etc.) that is in newDefs once the
       * transformation of the template is finished will be added as a member to the
       * template. Any mapping from a symbol to a tree that is in newInits, will be added
       * as a statement of the form "symbol = tree" to the beginning of the default
       * constructor. */
      case Template(parents, self, body) =>
        localTyper = typer.atOwner(tree, currentClass)
        if (!forMSIL) {
          classConstantMeth.clear
          newDefs.clear
          newInits.clear
          var newBody =
            transformTrees(body)
          val firstConstructor =
            treeInfo.firstConstructor(newBody)
          newBody =
            transformTrees(newDefs.toList) ::: (
              for (member <- newBody) yield member match {
                case thePrimaryConstructor@DefDef(mods, name, tparams, vparamss, tpt, rhs) if (thePrimaryConstructor == firstConstructor) =>
                  val newRhs = rhs match {
                    case theRhs@Block(stats, expr) =>
                      treeCopy.Block(theRhs, transformTrees(newInits.toList) ::: stats, expr)
                  }
                  treeCopy.DefDef(thePrimaryConstructor, mods, name, tparams, vparamss, tpt, newRhs)
                case notThePrimaryConstructor =>
                  notThePrimaryConstructor
              }
            )
            treeCopy.Template(tree, parents, self, newBody)
        }
        else super.transform(tree)

      case Literal(c) if (c.tag == ClassTag) && !forMSIL=>
        val tpe = c.typeValue
        typedWithPos(tree.pos) {
          if (isValueClass(tpe.typeSymbol) || tpe.typeSymbol == definitions.UnitClass)
            Select(REF(javaBoxClassModule(tpe.typeSymbol)), "TYPE")
          else tree
        }

      /* MSIL requires that the stack is empty at the end of a try-block.
       * Hence, we here rewrite all try blocks with a result != {Unit, All} such that they
       * store their result in a local variable. The catch blocks are adjusted as well.
       * The try tree is subsituted by a block whose result expression is read of that variable. */
      case theTry @ Try(block, catches, finalizer) 
        if theTry.tpe.typeSymbol != definitions.UnitClass && theTry.tpe.typeSymbol != definitions.NothingClass =>
        val tpe = theTry.tpe.widen
        val tempVar = currentOwner.newValue(theTry.pos, unit.fresh.newName(theTry.pos, "exceptionResult"))
          .setInfo(tpe).setFlag(Flags.MUTABLE)
        def assignBlock(rhs: Tree) = super.transform(BLOCK(Ident(tempVar) === transform(rhs)))
        
        val newBlock    = assignBlock(block)
        val newCatches  = for (CaseDef(pattern, guard, body) <- catches) yield
          (CASE(super.transform(pattern)) IF (super.transform(guard))) ==> assignBlock(body)
        val newTry      = Try(newBlock, newCatches, super.transform(finalizer))
                
        localTyper typed { BLOCK(VAL(tempVar) === EmptyTree, newTry, Ident(tempVar)) }
        
      /* Adds @serializable annotation to anonymous function classes */
      case cdef @ ClassDef(mods, name, tparams, impl) =>
        if (settings.target.value == "jvm-1.5") {
          val sym = cdef.symbol
          // is this an anonymous function class?
          if (sym.isAnonymousFunction && !sym.hasAnnotation(SerializableAttr))
            sym addAnnotation AnnotationInfo(SerializableAttr.tpe, Nil, Nil)
        }
        super.transform(tree)

      case _ =>
        super.transform(tree)
    }
  } // CleanUpTransformer

}