|
| 1 | +package dotty.tools.dotc.transform |
| 2 | + |
| 3 | +import dotty.tools.dotc.{CompilationUnit, FromTasty} |
| 4 | +import dotty.tools.dotc.ast.Trees._ |
| 5 | +import dotty.tools.dotc.ast.tpd |
| 6 | +import dotty.tools.dotc.core.Contexts._ |
| 7 | +import dotty.tools.dotc.core.SymDenotations.ClassDenotation |
| 8 | +import dotty.tools.dotc.core.Symbols._ |
| 9 | +import dotty.tools.dotc.core.Flags._ |
| 10 | +import dotty.tools.dotc.transform.TreeTransforms._ |
| 11 | + |
| 12 | +/** Loads all potentially reachable trees from tasty. ▲ |
| 13 | + * Only performed on whole world optimization mode. ▲ ▲ |
| 14 | + * |
| 15 | + * TODO: Next step is to only load compilation units reachable in the call graph |
| 16 | + */ |
| 17 | +class LinkAll extends MiniPhaseTransform { |
| 18 | + import tpd._ |
| 19 | + |
| 20 | + override def phaseName = "linkAll" |
| 21 | + |
| 22 | + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = NoTransform |
| 23 | + |
| 24 | + override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = |
| 25 | + if (ctx.settings.XlinkOptimise.value) super.runOn(allUnits(Set.empty, units.toSet, Set.empty)) |
| 26 | + else super.runOn(units) |
| 27 | + |
| 28 | + /** Loads and processes new compilation units, possibly loading more units. */ |
| 29 | + private def allUnits(processed: Set[CompilationUnit], unprocessed: Set[CompilationUnit], loadedClasses: Set[ClassDenotation])(implicit ctx: Context): List[CompilationUnit] = { |
| 30 | + if (unprocessed.isEmpty) processed.toList |
| 31 | + else { |
| 32 | + val accum = new ClassesToLoadAccumulator |
| 33 | + val classesToLoad = unprocessed.foldLeft(Set.empty[ClassDenotation])((acc, unit) => accum.apply(acc, unit.tpdTree)) -- loadedClasses |
| 34 | + val loadedUnits = classesToLoad.flatMap(cls => FromTasty.loadCompilationUnit(cls)) |
| 35 | + allUnits(processed ++ unprocessed, loadedUnits, loadedClasses ++ classesToLoad) |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + /** Collects all class denotations that may need to be loaded. */ |
| 40 | + private class ClassesToLoadAccumulator extends TreeAccumulator[Set[ClassDenotation]] { |
| 41 | + private var inParents = false |
| 42 | + override def apply(acc: Set[ClassDenotation], tree: tpd.Tree)(implicit ctx: Context): Set[ClassDenotation] = tree match { |
| 43 | + case New(tpt) => accum(acc, tpt.tpe.classSymbol) |
| 44 | + case AppliedTypeTree(tpt, _) if inParents => accum(acc, tpt.symbol) |
| 45 | + case tree: RefTree if inParents || tree.symbol.is(Module) => |
| 46 | + foldOver(accum(acc, tree.symbol), tree) |
| 47 | + case tree @ Template(constr, parents, self, _) => |
| 48 | + val acc1 = this(acc, constr) |
| 49 | + inParents = true |
| 50 | + val acc2 = this(acc1, parents) |
| 51 | + inParents = false |
| 52 | + this(this(acc2, self), tree.body) |
| 53 | + case _ => foldOver(acc, tree) |
| 54 | + } |
| 55 | + |
| 56 | + /** Accumulate class denotation for `sym` if needed */ |
| 57 | + private def accum(acc: Set[ClassDenotation], sym: Symbol)(implicit ctx: Context): Set[ClassDenotation] = { |
| 58 | + val topClass = sym.topLevelClass.denot.asClass |
| 59 | + if (topClass.is(JavaDefined) || topClass.is(Scala2x) || topClass.symbol == defn.ObjectClass) acc |
| 60 | + else acc + topClass |
| 61 | + } |
| 62 | + } |
| 63 | +} |
0 commit comments