summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/transform/CleanUp.scala
blob: 1a3eaea55f4cb95e7949a9123ee783587e1401f0 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12


                                
   
       
 





                                                     

                                          


                          

                                                                         
                                   
 

                                                                    


                                                                       

                                                                                   


                                                               



                                                                         
                   





                                                                         




                                                                        
     
 
                                                 
 
                                                                                                            


                        
                                                                            

                                          
                                                                                 








                                                                                  
                                                                                

















                                                                                            












                                                                                                      
                                                                                         









                                                            
                                                                                         









                                                            
                                                                                         
                                        














































                                                                                                                  
                                                       
                                      
 




                                                                               
                                                           

































                                                                              












































                                                                                                            
                                                                                                          



















































                                                                                                                        











                                                                             
                            
                                                



                                                   
                                                        
                                                                                                                          




                                                                                                 






                                                                 


                 



                                                                                                         









                                                                                 
                              
                                                       
                                                                                                                            






























                                                                                                                                           















                                                                                            
                         
                                                                                                                                 







                                                                                                            
                



                                                                                    
              





                                                                                                        



















                                                                          



                                                                                   
                                                                 



















                                                                                         
                                              
                                                                                                                



                                                     































                                                                      
                                           
                                                      






                                                                      

                                                          


                             

                                                                                         
                                                                                     
                                                                    


                                                                                   
                     

           





                                                                                                   
                                                                                                                 
                                  
                                                                                                          

                                              
                                                                                            
                                                                               




                                                                     
         
                                                                          


                                                                                 






                                                                    
                                                                            
                                                           







                                                                            

                                                                                      






                                                                                                
                               


                               


                             

                         
 
/* NSC -- new Scala compiler
 * Copyright 2005-2006 LAMP/EPFL
 * @author Martin Odersky
 */
// $Id$

package scala.tools.nsc.transform

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

abstract class CleanUp extends Transform {
  import global._
  import definitions._
  import posAssigner.atPos

  /** 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 MethodClass = if (forCLDC || forMSIL) null
                              else definitions.getClass("java.lang.reflect.Method")
    private val newDefs = 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")
      if (!forCLDC) {
        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 def classConstantMethod(pos: Position, sig: String): Symbol = classConstantMeth.get(sig) match {
      case Some(meth) =>
        meth
      case None =>
        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 =
          localTyper.typed {
            atPos(pos) {
              ValDef(cvar, Literal(Constant(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 =
          localTyper.typed {
            atPos(pos) {
              DefDef(meth, vparamss =>
                gen.mkCached(
                  cvar,
                  Apply(
                    gen.mkAttributedRef(forName), List(Literal(sig)))))
            }
          }

        newDefs.append(cdef, mdef);
        classConstantMeth.update(sig, meth)
        meth
    }

    private val existingReflectiveMethodCache = new HashMap[(String, List[Type]), Symbol]

    /* Transforms a list of types into a list of trees representing these types
     * as java.lang.Class instances. */
    private def paramTypeClasses(paramTypes: List[Type]): List[Tree] =
      paramTypes map { pt => Literal(Constant(pt)) }

    private def reflectiveMethodCache(pos: Position, method: String, paramTypes: List[Type]): Symbol =
      existingReflectiveMethodCache.get((method, paramTypes)) match {
        case Some(cache) => cache
        case None =>
          val owner = currentOwner.enclClass

          val rmvar = owner.newVariable(pos, unit.fresh.newName(pos, "reflMethod$Cache"))
            .setFlag(PRIVATE | STATIC | MUTABLE | SYNTHETIC)
            .setInfo(MethodClass.tpe)
          owner.info.decls.enter(rmvar)
          val rmdef =
            localTyper.typed {
              atPos(pos) {
                ValDef(rmvar, Literal(Constant(null)))
              }
            }

          val rmcvar = owner.newVariable(pos, unit.fresh.newName(pos, "reflClass$Cache"))
            .setFlag(PRIVATE | STATIC | MUTABLE | SYNTHETIC)
            .setInfo(ClassClass.tpe)
          owner.info.decls.enter(rmcvar)
          val rmcdef =
            localTyper.typed {
              atPos(pos) {
                ValDef(rmcvar, Literal(Constant(null)))
              }
            }

          val rmmeth = owner.newMethod(pos, unit.fresh.newName(pos, "reflMethod$Method"))
            .setFlag(STATIC | SYNTHETIC)
            .setInfo(MethodType(List(ClassClass.tpe), MethodClass.tpe))
          owner.info.decls.enter(rmmeth)
          val rmmdef =
            localTyper.typed {
              atPos(pos) {
                DefDef(rmmeth, { vparamss =>
                  val callClass = vparamss(0)(0)
                  Block(
                    List(
                      If(
                        gen.mkOr(
                          Apply(Select(Select(This(owner), rmvar), nme.eq), List(Literal(Constant(null)))),
                          Apply(Select(Select(This(owner), rmcvar), nme.ne), List(gen.mkAttributedRef(callClass)))
                        ),
                        Block(
                          List(
                            Assign(
                              Select(This(owner), rmvar),
                              Apply(
                                Select(
                                  gen.mkAttributedRef(callClass),
                                  ClassClass.tpe.member(nme.getMethod_)
                                ),
                                List(
                                  Literal(Constant(method)),
                                  ArrayValue(TypeTree(ClassClass.tpe), paramTypeClasses(paramTypes))
                                )
                              )
                            ),
                            Assign(Select(This(owner), rmcvar), gen.mkAttributedRef(callClass))
                          ),
                          Literal(Constant(()))
                        ),
                        EmptyTree
                      )
                    ),
                    Select(This(owner), rmvar)
                  )
                })
              }
            }

          newDefs.append(transform(rmdef), transform(rmcdef), transform(rmmdef));
          existingReflectiveMethodCache.update((method, paramTypes), rmmeth)
          rmmeth
      }

    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.
       * 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 integeer 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 keeps 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(qual, params) =>
        assert(ad.symbol.isPublic)

        val testForNumber: Tree =
          gen.mkOr(
            Apply(
              TypeApply(
                gen.mkAttributedSelect(qual, definitions.Object_isInstanceOf),
                List(TypeTree(BoxedNumberClass.tpe.normalize))
              ),
              List()
            ),
            Apply(
              TypeApply(
                gen.mkAttributedSelect(qual, definitions.Object_isInstanceOf),
                List(TypeTree(BoxedCharacterClass.tpe.normalize))
              ),
              List()
            )
          )

        val testForBoolean: Tree =
          Apply(
            TypeApply(
              gen.mkAttributedSelect(qual, definitions.Object_isInstanceOf),
              List(TypeTree(BoxedBooleanClass.tpe.normalize))
            ),
            List()
          )

        val testForNumberOrBoolean: Tree = gen.mkOr(testForNumber, testForBoolean)

        def getPrimitiveReplacementForStructuralCall: PartialFunction[Name, (Symbol, Tree)] = {
        /* Unary arithmetic */
          case nme.UNARY_+ =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("positive")), testForNumber)
          case nme.UNARY_- =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("negate")), testForNumber)
        /* Unary logic */
          case nme.UNARY_~ =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("complement")), testForNumber)
          case nme.UNARY_! =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("takeNot")), testForBoolean)
        /* Binary arithmetic */
          case nme.ADD =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("add")), testForNumber)
          case nme.SUB =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("subtract")), testForNumber)
          case nme.MUL =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("multiply")), testForNumber)
          case nme.DIV =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("divide")), testForNumber)
          case nme.MOD =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("takeModulo")), testForNumber)
        /* Binary logic */
          case nme.OR =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("takeOr")), testForNumberOrBoolean)
          case nme.XOR =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("takeXor")), testForNumberOrBoolean)
          case nme.AND =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("takeAnd")), testForNumberOrBoolean)
          case nme.ZOR =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("takeConditionalOr")), testForBoolean)
          case nme.ZAND =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("takeConditionalAnd")), testForBoolean)
        /* Shifting */
          case nme.LSL =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("shiftSignedLeft")), testForNumber)
          case nme.LSR =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("shiftSignedRight")), testForNumber)
          case nme.ASR =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("shiftLogicalRight")), testForNumber)
          case nme.EQ =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testEqual")), testForNumberOrBoolean)
          case nme.NE =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testNotEqual")), testForNumberOrBoolean)
          case nme.LT =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testLessThan")), testForNumber)
          case nme.LE =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testLessOrEqualThan")), testForNumber)
          case nme.GE =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testGreaterOrEqualThan")), testForNumber)
          case nme.GT =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testGreaterThan")), testForNumber)
        /* Conversions */
          case nme.toByte =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toByte")), testForNumber)
          case nme.toShort =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toShort")), testForNumber)
          case nme.toChar =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toCharacter")), testForNumber)
          case nme.toInt =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toInteger")), testForNumber)
          case nme.toLong =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toLong")), testForNumber)
          case nme.toFloat =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toFloat")), testForNumber)
          case nme.toDouble =>
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toDouble")), testForNumber)
        }

        /* 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 {
            if (resType.typeSymbol == UnitClass)
              Block (
                List(tree),
                gen.mkAttributedRef(BoxedUnit_UNIT)
              )
            else if (resType.typeSymbol == ArrayClass) {
              val sym = currentOwner.newValue(tree.pos, newTermName(unit.fresh.newName(tree.pos))) setInfo ObjectClass.tpe
              Block(
                List(ValDef(sym, tree)),
                If(
                  Apply(Select(Literal(Constant(null)), Any_==), List(gen.mkAttributedRef(sym))),
                  Literal(Constant(null)),
                  Apply(
                    Select(
                      gen.mkAttributedRef(ScalaRunTimeModule),
                      ScalaRunTimeModule.tpe.member(nme.boxArray)
                    ),
                    List(gen.mkAttributedRef(sym))
                  )
                )
              )
            }
            else if (resType.typeSymbol == ObjectClass) // TODO: remove the cast always when unnecessary.
              tree
            else
              gen.mkAttributedCast(tree, resType)
          }

        /* Transforms the parameters of a dynamic apply (always AnyRefs) to
         * something compatible with reclective 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. */
        def fixParams(params: List[Tree], paramTypes: List[Type]): List[Tree] =
          (params zip paramTypes) map { case (param, paramType) =>
            localTyper.typed {
              if (paramType.typeSymbol == ArrayClass) {
                val sym = currentOwner.newValue(tree.pos, newTermName(unit.fresh.newName(tree.pos))) setInfo ObjectClass.tpe
                val arrayType = {
                  assert(paramType.typeArgs.length == 1)
                  paramType.typeArgs(0).normalize
                }
                Block(
                  List(ValDef(sym, param)),
                  If(
                    Apply(Select(Literal(Constant(null)), Any_==), List(gen.mkAttributedRef(sym))),
                    Literal(Constant(null)),
                    If(
                      Apply(
                        TypeApply(
                          gen.mkAttributedSelect(gen.mkAttributedRef(sym), definitions.Object_isInstanceOf),
                          List(TypeTree(BoxedArrayClass.tpe.normalize))
                        ),
                        List()
                      ),
                      Apply(
                        Select(gen.mkAttributedCast(gen.mkAttributedRef(sym), BoxedArrayClass.tpe), getMember(BoxedArrayClass, nme.unbox)),
                        List(Literal(Constant(arrayType)))
                      ),
                      gen.mkAttributedRef(sym)
                    )
                  )
                )
              }
              else
                param
            }
          }

        def callAsOperator(paramTypes: List[Type], resType: Type): Tree = localTyper.typed {
          if (getPrimitiveReplacementForStructuralCall isDefinedAt ad.symbol.name) {
            val (operator, test) = getPrimitiveReplacementForStructuralCall(ad.symbol.name)
            If(
              test,
              Apply(
                gen.mkAttributedRef(operator),
                qual :: fixParams(params, paramTypes)
              ),
              callAsMethod(paramTypes, resType)
            )
          }
          else callAsMethod(paramTypes, resType)
        }

        def callAsMethod(paramTypes: List[Type], resType: Type): Tree = localTyper.typed {
          val invokeExc =
            currentOwner.newValue(tree.pos, newTermName(unit.fresh.newName(tree.pos))) setInfo InvocationTargetExceptionClass.tpe
          Try(
            Apply(
              Select(
                Apply(
                  gen.mkAttributedRef(reflectiveMethodCache(tree.pos, ad.symbol.name.toString, paramTypes)),
                  List(Apply(Select(qual, ObjectClass.tpe.member(nme.getClass_)), Nil))
                ),
                MethodClass.tpe.member(nme.invoke_)
              ),
              List(
                qual,
                ArrayValue(TypeTree(ObjectClass.tpe), fixParams(params, paramTypes))
              )
            ),
            List(CaseDef(
              Bind(invokeExc, Typed(Ident(nme.WILDCARD), TypeTree(InvocationTargetExceptionClass.tpe))),
              EmptyTree,
              Throw(Apply(Select(Ident(invokeExc), nme.getCause), Nil))
            )),
            EmptyTree
          )
        }

        def mayRequirePrimitiveReplacement: Boolean = {

          def isBoxed(sym: Symbol): Boolean =
            if (forCLDC) {
              (sym isNonBottomSubClass ByteClass) ||
              (sym isNonBottomSubClass ShortClass) ||
              (sym isNonBottomSubClass CharClass) ||
              (sym isNonBottomSubClass IntClass) ||
              (sym isNonBottomSubClass LongClass)
            }
            else ((sym isNonBottomSubClass BoxedNumberClass) ||
              (!forMSIL && (sym isNonBottomSubClass BoxedCharacterClass)))

          val sym = qual.tpe.typeSymbol
          (sym == definitions.ObjectClass) || isBoxed(sym)

        }

        /* 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(paramTypes, resType) =>
            assert(params.length == paramTypes.length)
            atPos(tree.pos)(localTyper.typed {
              fixResult(if (isValueClass(resType.typeSymbol)) boxedClass(resType.typeSymbol).tpe else resType) {
                if (mayRequirePrimitiveReplacement)
                  callAsOperator(paramTypes, resType)
                else
                  callAsMethod(paramTypes, resType)
              }
            })
        }

        /* For testing purposes, the dynamic application's condition
         * can be printed-out in great detail. Remove? */
        if (settings.debug.value) {
          Console.println(
            "Dynamically applying '" + qual + "." + ad.symbol.name +
            "(" + params.map(_.toString).mkString(", ") + ")' with"
          )
          ad.symbol.tpe match {
            case MethodType(paramTypes, resType) =>
              Console.println(
                "  - declared parameters' types: " +
                (paramTypes.map(_.toString)).mkString("'",", ","'"))
              Console.println(
                "  - passed arguments' types:    " +
                (params.map(_.toString)).mkString("'",", ","'"))
              Console.println(
                "  - result type:                '" +
                resType.toString + "'")
          }
          Console.println("  - resulting code:    '" + t + "'")
        }

        /* 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 call transformer. */

      case Template(parents, self, body) =>
        localTyper = typer.atOwner(tree, currentOwner)
        if (!forMSIL) {
          classConstantMeth.clear
          newDefs.clear
          val body1 = transformTrees(body)
          copy.Template(tree, parents, self, newDefs.toList ::: body1)
        }
        else super.transform(tree)

      case Literal(c) if (c.tag == ClassTag) && !forMSIL=>
        val tpe = c.typeValue
        atPos(tree.pos) {
          localTyper.typed {
            if ((isValueClass(tpe.typeSymbol) || tpe.typeSymbol == definitions.UnitClass)
                && !forCLDC)
              Select(gen.mkAttributedRef(javaBoxClassModule(tpe.typeSymbol)), "TYPE")
            else if (settings.target.value != "jvm-1.5" && !forMSIL)
              Apply(
                gen.mkAttributedRef(classConstantMethod(tree.pos, signature(tpe))),
                List())
            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)

        val newBlock = super.transform(Block(Nil, Assign(Ident(tempVar), transform(block))))
        val newCatches = for (CaseDef(pattern, guard, body) <- catches) yield {
          CaseDef(
            super.transform(pattern),
            super.transform(guard),
            Block(Nil, Assign(Ident(tempVar), super.transform(body)))
          )
        }
        val newTry = Try(newBlock, newCatches, super.transform(finalizer))
        val res = Block(List(ValDef(tempVar, EmptyTree), newTry), Ident(tempVar))
        localTyper.typed(res)

      /* Adds @serializable annotation to anonymous function classes
       * which do not have references to classes
       * which are not marked @serializable.
       */
      case cdef @ ClassDef(mods, name, tparams, impl) =>
        val sym = cdef.symbol
        // is this an anonymous function class?
        if (!sym.hasAttribute(SerializableAttr) && sym.hasFlag(SYNTHETIC) &&
            (sym.name.toString.indexOf("anonfun") != -1)) {
          // check whether all of its field members are of serializable type
          val serializable =
            sym.info.members forall { m =>
              m.isMethod || {
                val typeSym = m.info.typeSymbol
                // Value types are assumed to be serializable,
                // reference types must be marked as such.
                isValueType(typeSym) ||
                typeSym.hasAttribute(SerializableAttr) ||
                (m.info.baseClasses exists { bc => bc hasAttribute SerializableAttr })
              }
            }

          if (serializable)
            sym.attributes =
              AnnotationInfo(definitions.SerializableAttr.tpe, List(), List()) :: sym.attributes

          super.transform(tree)
        } else
          super.transform(tree)

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

}