package scala.tools.nsc
package transform
import symtab._
import Flags._
import scala.collection._
abstract class CleanUp extends Transform with ast.TreeDSL {
import global._
import definitions._
import CODE._
val phaseName: String = "cleanup"
protected def newTransformer(unit: CompilationUnit): Transformer =
new CleanUpTransformer(unit)
class CleanUpTransformer(unit: CompilationUnit) extends Transformer {
private val newStaticMembers = mutable.Buffer.empty[Tree]
private val newStaticInits = mutable.Buffer.empty[Tree]
private val symbolsStoredAsStatic = mutable.Map.empty[String, Symbol]
private def mkTerm(prefix: String): TermName = unit.freshTermName(prefix)
def safeREF(sym: Symbol) = {
def fix(tree: Tree): Unit = tree match {
case Select(qual @ This(_), name) if qual.symbol != currentClass =>
qual.setSymbol(currentClass).setType(currentClass.tpe)
case _ =>
}
val tree = REF(sym)
if (currentClass.isImplClass && sym.owner == currentClass) fix(tree)
tree
}
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) }
def isJavaValueClass(sym: Symbol) = boxedClass contains sym
def isJavaValueType(tp: Type) = isJavaValueClass(tp.typeSymbol)
def toBoxedType(tp: Type) = if (isJavaValueType(tp)) boxedClass(tp.typeSymbol).tpe else tp
override def transform(tree: Tree): Tree = tree match {
case ad@ApplyDynamic(qual0, params) =>
val typedPos = typedWithPos(ad.pos) _
assert(ad.symbol.isPublic)
var qual: Tree = qual0
def addStaticVariableToClass(forName: String, forType: Type, forInit: Tree, isFinal: Boolean): Symbol = {
val varSym = currentClass.newVariable(ad.pos, mkTerm(forName))
.setFlag(PRIVATE | STATIC | SYNTHETIC)
.setInfo(forType)
if (isFinal) varSym setFlag FINAL else varSym addAnnotation AnnotationInfo(VolatileAttr.tpe, Nil, Nil)
currentClass.info.decls enter varSym
val varDef = typedPos( VAL(varSym) === forInit )
newStaticMembers append transform(varDef)
val varInit = typedPos( safeREF(varSym) === forInit )
newStaticInits 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, mkTerm(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))) }) )
newStaticMembers append transform(methDef)
methSym
}
def fromTypesToClassArrayLiteral(paramTypes: List[Type]): Tree =
ArrayValue(TypeTree(ClassClass.tpe), paramTypes map LIT)
def theTypeClassArray = arrayType(ClassClass.tpe)
def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = dispatchType match {
case NO_CACHE =>
val reflParamsCacheSym: Symbol =
addStaticVariableToClass("reflParams$Cache", theTypeClassArray, fromTypesToClassArrayLiteral(paramTypes), true)
addStaticMethodToClass("reflMethod$Method", List(ClassClass.tpe), MethodClass.tpe) {
case Pair(reflMethodSym, List(forReceiverSym)) =>
(REF(forReceiverSym) DOT Class_getMethod)(LIT(method), safeREF(reflParamsCacheSym))
}
case MONO_CACHE =>
val reflParamsCacheSym: Symbol =
addStaticVariableToClass("reflParams$Cache", theTypeClassArray, fromTypesToClassArrayLiteral(paramTypes), true)
val reflMethodCacheSym: Symbol =
addStaticVariableToClass("reflMethod$Cache", MethodClass.tpe, NULL, false)
val reflClassCacheSym: Symbol =
addStaticVariableToClass("reflClass$Cache", SoftReferenceClass.tpe, NULL, false)
def getMethodSym = ClassClass.tpe member nme.getMethod_
def isCacheEmpty(receiver: Symbol): Tree =
reflClassCacheSym.IS_NULL() OR (reflClassCacheSym.GET() OBJ_NE REF(receiver))
addStaticMethodToClass("reflMethod$Method", List(ClassClass.tpe), MethodClass.tpe) {
case Pair(reflMethodSym, List(forReceiverSym)) =>
BLOCK(
IF (isCacheEmpty(forReceiverSym)) THEN BLOCK(
safeREF(reflMethodCacheSym) === ((REF(forReceiverSym) DOT getMethodSym)(LIT(method), safeREF(reflParamsCacheSym))) ,
safeREF(reflClassCacheSym) === gen.mkSoftRef(REF(forReceiverSym)),
UNIT
) ENDIF,
safeREF(reflMethodCacheSym)
)
}
case POLY_CACHE =>
val reflParamsCacheSym: Symbol =
addStaticVariableToClass("reflParams$Cache", theTypeClassArray, fromTypesToClassArrayLiteral(paramTypes), true)
def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe)))
val reflPolyCacheSym: Symbol = addStaticVariableToClass("reflPoly$Cache", SoftReferenceClass.tpe, mkNewPolyCache, false)
def getPolyCache = fn(safeREF(reflPolyCacheSym), nme.get) AS_ATTR MethodCacheClass.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(
IF (getPolyCache OBJ_EQ NULL) THEN (safeREF(reflPolyCacheSym) === mkNewPolyCache) ENDIF,
VAL(methodSym) === ((getPolyCache DOT methodCache_find)(REF(forReceiverSym))) ,
IF (REF(methodSym) OBJ_!= NULL) .
THEN (Return(REF(methodSym)))
ELSE {
def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), safeREF(reflParamsCacheSym)))
def cacheRHS = ((getPolyCache DOT methodCache_add)(REF(forReceiverSym), REF(methodSym)))
BLOCK(
REF(methodSym) === (REF(ensureAccessibleMethod) APPLY (methodSymRHS)),
safeREF(reflPolyCacheSym) === gen.mkSoftRef(cacheRHS),
Return(REF(methodSym))
)
}
)
}
}
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)
{
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 callAsReflective(paramTypes: List[Type], resType: Type): Tree = {
val methSym = ad.symbol
val boxedResType = toBoxedType(resType)
val resultSym = boxedResType.typeSymbol
def isJavaValueMethod = (
(resType :: paramTypes forall isJavaValueType) &&
(getPrimitiveReplacementForStructuralCall isDefinedAt methSym.name)
)
def isDefinitelyUnit = (resultSym == UnitClass)
def isMaybeUnit = (resultSym == ObjectClass) || isDefinitelyUnit
val isArrayMethodSignature = {
def typesMatchApply = paramTypes match {
case List(tp) => tp <:< IntClass.tpe
case _ => false
}
def typesMatchUpdate = paramTypes match {
case List(tp1, tp2) => (tp1 <:< IntClass.tpe) && isMaybeUnit
case _ => false
}
(methSym.name == nme.length && params.isEmpty) ||
(methSym.name == nme.clone_ && params.isEmpty) ||
(methSym.name == nme.apply && typesMatchApply) ||
(methSym.name == nme.update && typesMatchUpdate)
}
val qualSym = qual.tpe.typeSymbol
val args = qual :: params
def isDefinitelyArray = (qualSym == ArrayClass)
def isMaybeArray = (qualSym == ObjectClass) || isDefinitelyArray
def isMaybeBoxed = platform isMaybeBoxed qualSym
def fixResult(tree: Tree, mustBeUnit: Boolean = false) =
if (mustBeUnit || resultSym == UnitClass) BLOCK(tree, REF(BoxedUnit_UNIT))
else if (resultSym == ObjectClass) tree
else tree AS_ATTR boxedResType
def genDefaultCall = {
val invokeName = MethodClass.tpe member nme.invoke_
def cache = safeREF(reflectiveMethodCache(ad.symbol.name.toString, paramTypes))
def lookup = Apply(cache, List(qual GETCLASS))
def invokeArgs = ArrayValue(TypeTree(ObjectClass.tpe), params)
def invocation = (lookup DOT invokeName)(qual, invokeArgs)
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))
fixResult(TRY (invocation) CATCH { CASE (catchVar) ==> catchBody } ENDTRY)
}
def genValueCall(operator: Symbol) = fixResult(REF(operator) APPLY args)
def genValueCallWithTest = {
val (operator, test) = getPrimitiveReplacementForStructuralCall(methSym.name)
IF (test) THEN genValueCall(operator) ELSE genDefaultCall
}
def genArrayCall = fixResult(
methSym.name match {
case nme.length => REF(boxMethod(IntClass)) APPLY (REF(arrayLengthMethod) APPLY args)
case nme.update => REF(arrayUpdateMethod) APPLY List(args(0), (REF(unboxMethod(IntClass)) APPLY args(1)), args(2))
case nme.apply => REF(arrayApplyMethod) APPLY List(args(0), (REF(unboxMethod(IntClass)) APPLY args(1)))
case nme.clone_ => REF(arrayCloneMethod) APPLY List(args(0))
},
mustBeUnit = methSym.name == nme.update
)
def genArrayCallWithTest =
IF ((qual GETCLASS()) DOT nme.isArray) THEN genArrayCall ELSE genDefaultCall
localTyper typed (
if (isMaybeBoxed && isJavaValueMethod) genValueCallWithTest
else if (isArrayMethodSignature && isDefinitelyArray) genArrayCall
else if (isArrayMethodSignature && isMaybeArray) genArrayCallWithTest
else genDefaultCall
)
}
if (settings.refinementMethodDispatch.value == "invoke-dynamic") {
localTyper.typed(treeCopy.ApplyDynamic(ad, transform(qual), transformTrees(params)))
}
else {
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 = safeREF(sym)
BLOCK(
VAL(sym) === qual0,
callAsReflective(mparams map (_.tpe), resType)
)
}
}
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 _ => ""
}
log(
"""Dynamically application '%s.%s(%s)' %s - resulting code: '%s'""".format(
qual, ad.symbol.name, paramsToString(params), mstr, t
)
)
}
transform(t)
}
case Template(parents, self, body) =>
localTyper = typer.atOwner(tree, currentClass)
var savedNewStaticMembers : mutable.Buffer[Tree] = null
var savedNewStaticInits : mutable.Buffer[Tree] = null
var savedSymbolsStoredAsStatic : mutable.Map[String, Symbol] = null
if(forMSIL) {
savedNewStaticMembers = newStaticMembers.clone
savedNewStaticInits = newStaticInits.clone
savedSymbolsStoredAsStatic = symbolsStoredAsStatic.clone
}
newStaticMembers.clear
newStaticInits.clear
symbolsStoredAsStatic.clear
val transformedTemplate: Template = {
var newBody = transformTrees(body)
treeCopy.Template(tree, parents, self, transformTrees(newStaticMembers.toList) ::: newBody)
}
val res = addStaticInits(transformedTemplate)
newStaticMembers.clear
newStaticInits.clear
symbolsStoredAsStatic.clear
if(forMSIL) {
newStaticMembers ++= savedNewStaticMembers
newStaticInits ++= savedNewStaticInits
symbolsStoredAsStatic ++= savedSymbolsStoredAsStatic
}
res
case Literal(c) if (c.tag == ClassTag) && !forMSIL=>
val tpe = c.typeValue
typedWithPos(tree.pos) {
if (isValueClass(tpe.typeSymbol)) {
if (tpe.typeSymbol == UnitClass)
REF(BoxedUnit_TYPE)
else
Select(REF(boxedModule(tpe.typeSymbol)), nme.TYPE_)
}
else tree
}
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.newVariable(theTry.pos, mkTerm(nme.EXCEPTION_RESULT_PREFIX)).setInfo(tpe)
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))
typedWithPos(theTry.pos)(BLOCK(VAL(tempVar) === EmptyTree, newTry, Ident(tempVar)))
case symapp @ Apply(Select(Select(a @ Ident(nme.scala_), b @ nme.Symbol), nme.apply),
List(Literal(Constant(symname: String)))) =>
val rhs = gen.mkCast(Apply(gen.scalaDot(nme.Symbol), List(Literal(Constant(symname)))), symbolType)
val staticFieldSym = getSymbolStaticField(symapp.pos, symname, rhs, symapp)
val ntree = typedWithPos(symapp.pos)(safeREF(staticFieldSym))
super.transform(ntree)
case Apply(appMeth, List(Apply(wrapRefArrayMeth, List(array)), _))
if (wrapRefArrayMeth.symbol == Predef_wrapRefArray &&
appMeth.symbol == ArrayModule_overloadedApply.suchThat {
_.tpe.resultType.dealias.typeSymbol == ObjectClass
}) =>
super.transform(array)
case _ =>
super.transform(tree)
}
private def getSymbolStaticField(pos: Position, symname: String, rhs: Tree, tree: Tree): Symbol =
symbolsStoredAsStatic.getOrElseUpdate(symname, {
val theTyper = typer.atOwner(tree, currentClass)
val stfieldSym = currentClass.newVariable(pos, mkTerm("symbol$"))
.setFlag(PRIVATE | STATIC | SYNTHETIC | FINAL)
.setInfo(symbolType)
currentClass.info.decls enter stfieldSym
val stfieldDef = theTyper.typed { atPos(pos)(VAL(stfieldSym) === rhs) }
val stfieldInit = theTyper.typed { atPos(pos)(safeREF(stfieldSym) === rhs) }
newStaticMembers append stfieldDef
newStaticInits append stfieldInit
stfieldSym
})
private def findStaticCtor(template: Template): Option[Tree] =
template.body find {
case defdef @ DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => defdef.symbol.hasStaticFlag
case _ => false
}
private def addStaticInits(template: Template): Template =
if (newStaticInits.isEmpty)
template
else {
val newCtor = findStaticCtor(template) match {
case Some(ctor @ DefDef(mods, name, tparams, vparamss, tpt, rhs)) =>
val newBlock = rhs match {
case block @ Block(stats, expr) =>
treeCopy.Block(block, newStaticInits.toList ::: stats, expr)
case term: TermTree =>
treeCopy.Block(term, newStaticInits.toList, term)
}
treeCopy.DefDef(ctor, mods, name, tparams, vparamss, tpt, newBlock)
case None =>
val staticCtorSym = currentClass.newConstructor(template.pos)
.setFlag(STATIC)
.setInfo(UnitClass.tpe)
val rhs = Block(newStaticInits.toList, Literal(()))
val staticCtorTree = DefDef(staticCtorSym, rhs)
localTyper.typed { atPos(template.pos)(staticCtorTree) }
}
treeCopy.Template(template, template.parents, template.self, newCtor :: template.body)
}
}
}