/* NSC -- new Scala compiler
* Copyright 2005-2009 LAMP/EPFL
* @author Martin Odersky
*/
// $Id$
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 lazy val serializableAnnotation =
AnnotationInfo(SerializableAttr.tpe, Nil, Nil)
private lazy val serialVersionUIDAnnotation = {
val attr = definitions.getClass("scala.SerialVersionUID")
AnnotationInfo(attr.tpe, List(Literal(Constant(0))), List())
}
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 serializableAnnotation
sym addAnnotation serialVersionUIDAnnotation
}
}
super.transform(tree)
case _ =>
super.transform(tree)
}
} // CleanUpTransformer
}