diff --git a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraphBuilder.scala b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraphBuilder.scala index a06fe8f144db..f478b60ec5ed 100644 --- a/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraphBuilder.scala +++ b/compiler/src/dotty/tools/dotc/transform/linker/callgraph/CallGraphBuilder.scala @@ -502,7 +502,11 @@ class CallGraphBuilder(collectedSummaries: Map[Symbol, MethodSummary], mode: Int summary.accessedModules.foreach(x => addReachableType(new TypeWithContext(x.info, parentRefinements(x.info)), method)) summary.definedClosures.foreach(x => addReachableClosure(x, method)) summary.methodsCalled.foreach { - case (receiver, theseCallSites) => theseCallSites.foreach(callSite => processCallSite(callSite, instantiatedTypes, method, receiver)) + case (receiver, theseCallSites) => theseCallSites.foreach { callSite => + val nw = instantiateCallSite(method, receiver, callSite, instantiatedTypes) + reachableMethods ++= nw + method.addOutEdges(callSite, nw) + } } case None => @@ -534,65 +538,71 @@ class CallGraphBuilder(collectedSummaries: Map[Symbol, MethodSummary], mode: Int } private def processCallsFromJava(instantiatedTypes: immutable.Set[TypeWithContext], method: CallInfoWithContext): Unit = { - def allNonJavaDecls(argType: Type) = { - (argType.decls ++ argType.parents.filter(!_.symbol.is(JavaDefined)).flatMap(_.decls)).toSet - } + def allDecls(argType: Type) = + (argType.decls ++ argType.parents.flatMap(_.decls)).toSet - val addedTypes = mutable.HashSet.empty[Type] + def isJavaClass(sym: Symbol) = + sym == defn.AnyClass || sym == defn.ObjectClass || sym.is(JavaDefined) - def addAllPotentialCallsFor(argType: Type): Unit = { - if (!defn.isPrimitiveClass(argType.classSymbol) && !addedTypes.contains(argType)) { - addedTypes.add(argType) - for { - decl <- allNonJavaDecls(argType) - if decl.isTerm && !decl.isConstructor - if decl.name != nme.isInstanceOf_ && decl.name != nme.asInstanceOf_ && decl.name != nme.synchronized_ - } { - val termName = decl.name.asTermName - val paramTypes = decl.info.paramTypess.flatten - - def addCall(call: TermRef): Unit = { - val targs = call.widenDealias match { - case call: PolyType => call.paramBounds.map(_.hi) - case _ => Nil - } - processCallSite(CallInfo(call, targs, paramTypes), instantiatedTypes, method, argType) - // TODO add transitively reachable calls from java (fix link-code-from-java-3.scala) - // val resultType = call.widenDealias.finalResultType.widenDealias - // addAllPotentialCallsFor(resultType) + def allPotentialCallsFor(argType: Type): Set[CallInfo] = { + if (defn.isPrimitiveClass(argType.classSymbol) || isJavaClass(argType.widenDealias.classSymbol)) { + Set.empty + } else { + def potentialCall(decl: Symbol) = { + def paramTypes = decl.info.paramTypess.flatten + val call = new TermRefWithFixedSym(argType, decl.name.asTermName, decl.asTerm) + val targs = call.widenDealias match { + case call: PolyType => + def erasedBounds(tp: TypeBounds): Type = tp.hi match { + case RefinedType(parent, refinedName, refinedInfo: TypeBounds) => + RefinedType(parent, refinedName, erasedBounds(refinedInfo)) + case t => t + } + call.paramBounds.map(erasedBounds) + + case _ => Nil } - val definedInJavaClass: Boolean = { - def isDefinedInJavaClass(sym: Symbol) = - sym.owner == defn.AnyClass || sym.owner.is(JavaDefined) + def isDefinedInJavaClass(sym: Symbol) = + sym.owner == defn.AnyClass || sym.owner.is(JavaDefined) + val definedInJavaClass: Boolean = isDefinedInJavaClass(decl) || decl.allOverriddenSymbols.exists(isDefinedInJavaClass) - } argType match { case argType: PreciseType => - if (definedInJavaClass) - addCall(new TermRefWithFixedSym(argType, termName, decl.asTerm)) + if (!definedInJavaClass) Nil + else List(CallInfo(call, targs, paramTypes)) + case _ => - // FIXME -// val argTypeWiden = argType.widenDealias -// if (argTypeWiden.member(termName).exists) { -// val sym = argTypeWiden.classSymbol.requiredMethod(termName, paramTypes) -// if (!definedInJavaClass || !sym.isEffectivelyFinal) -// addCall(TermRef(argType, sym)) -// } + val argTypeWiden = argType.widenDealias + lazy val sym = argTypeWiden.classSymbol.requiredMethod(decl.name, paramTypes) + if (!argTypeWiden.member(decl.name).exists || !definedInJavaClass || (sym.isEffectivelyFinal && isDefinedInJavaClass(decl))) Nil + else List(CallInfo(TermRef(argType, sym), targs, paramTypes)) + } } + + for { + decl <- allDecls(argType) + if decl.isTerm && !decl.isConstructor && decl.name != nme.COMPANION_MODULE_METHOD + if decl.name != nme.isInstanceOf_ && decl.name != nme.asInstanceOf_ && decl.name != nme.synchronized_ + callInfo <- potentialCall(decl) + } yield callInfo } } - method.argumentsPassed.foreach(addAllPotentialCallsFor) - } - - private def processCallSite(callSite: CallInfo, instantiatedTypes: immutable.Set[TypeWithContext], method: CallInfoWithContext, receiver: Type): Unit = { - val nw = instantiateCallSite(method, receiver, callSite, instantiatedTypes) - reachableMethods ++= nw - method.addOutEdges(callSite, nw) + // FIXME java method could potentially call this.xyz() + // addAllPotentialCallsFor(method.call.normalizedPrefix) + for { + rec <- method.argumentsPassed.distinct + potentialCall <- allPotentialCallsFor(rec) + if method.getOutEdges(potentialCall).isEmpty + } { + val nw = instantiateCallSite(method, rec, potentialCall, instantiatedTypes) + reachableMethods ++= nw + method.addOutEdges(potentialCall, nw) + } } } diff --git a/tests/link-dce-failing/link-code-from-java-7.check b/tests/link-dce-failing/link-code-from-java-7.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/link-dce-failing/link-code-from-java-7.check @@ -0,0 +1 @@ +42 diff --git a/tests/link-dce-failing/link-code-from-java-7.scala b/tests/link-dce-failing/link-code-from-java-7.scala new file mode 100644 index 000000000000..b12ca38f377f --- /dev/null +++ b/tests/link-dce-failing/link-code-from-java-7.scala @@ -0,0 +1,21 @@ + +import java.lang.InheritableThreadLocal + +import scala.annotation.internal + + +object Test { +// @internal.link.CallGraphBounds(reachableClasses = 20, classesWithReachableMethods = 6, reachableMethods = 7) + def main(args: Array[String]): Unit = { + System.out.println(new DynamicVariableFoo[String]("42").value) + } +} + + +class DynamicVariableFoo[T](init: T) { + private val tl = new InheritableThreadLocal[T] { + override def initialValue = init.asInstanceOf[T with AnyRef] + } + + def value: T = tl.get.asInstanceOf[T] +} diff --git a/tests/link-dce/link-code-from-java-5.scala b/tests/link-dce/link-code-from-java-5.scala index 9d59ebd884c3..494c972c8625 100644 --- a/tests/link-dce/link-code-from-java-5.scala +++ b/tests/link-dce/link-code-from-java-5.scala @@ -2,7 +2,7 @@ import scala.annotation.internal import scala.math.Ordering object Test { - @internal.link.CallGraphBounds(reachableClasses = 151, classesWithReachableMethods = 19, reachableMethods = 82) + @internal.link.CallGraphBounds(reachableClasses = 152, classesWithReachableMethods = 21, reachableMethods = 90) def main(args: Array[String]): Unit = { val arr = new Array[Object](1) arr(0) = 42 diff --git a/tests/link-dce/link-innerClass-1.scala b/tests/link-dce/link-innerClass-1.scala index b7cd8683d0cd..710fdd4f0d0e 100644 --- a/tests/link-dce/link-innerClass-1.scala +++ b/tests/link-dce/link-innerClass-1.scala @@ -1,7 +1,7 @@ import scala.annotation.internal object Test { - @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 8, reachableMethods = 11) + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 8, reachableMethods = 11) def main(args: Array[String]): Unit = { new Bar().test() } diff --git a/tests/link-dce/link-mixinInit-2.scala b/tests/link-dce/link-mixinInit-2.scala index 6c51babcf4de..36dc20c985cb 100644 --- a/tests/link-dce/link-mixinInit-2.scala +++ b/tests/link-dce/link-mixinInit-2.scala @@ -1,7 +1,7 @@ import scala.annotation.internal object Test { - @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 8, reachableMethods = 10) + @internal.link.CallGraphBounds(reachableClasses = 22, classesWithReachableMethods = 8, reachableMethods = 10) def main(args: Array[String]): Unit = { new Bar } diff --git a/tests/link-dce/link-secondary-constructor-0.scala b/tests/link-dce/link-secondary-constructor-0.scala index 189248d7bd9b..8ad862b9c618 100644 --- a/tests/link-dce/link-secondary-constructor-0.scala +++ b/tests/link-dce/link-secondary-constructor-0.scala @@ -1,7 +1,7 @@ import scala.annotation.internal object Test { - @internal.link.CallGraphBounds(reachableClasses = 20, classesWithReachableMethods = 7, reachableMethods = 11) + @internal.link.CallGraphBounds(reachableClasses = 21, classesWithReachableMethods = 7, reachableMethods = 11) def main(args: Array[String]): Unit = { new Foo() }