|
| 1 | +package dotty.tools.dotc.transform |
| 2 | + |
| 3 | +import dotty.tools.dotc.CompilationUnit |
| 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 | + import LinkAll._ |
| 20 | + |
| 21 | + override def phaseName = "linkAll" |
| 22 | + |
| 23 | + /** Do not transform the any tree, runOn will traverse the trees and reload compilation units if needed */ |
| 24 | + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = NoTransform |
| 25 | + |
| 26 | + override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { |
| 27 | + /** Loads and processes new compilation units, possibly loading more units. */ |
| 28 | + def allUnits(processed: Set[CompilationUnit], unprocessed: Set[CompilationUnit], loadedClasses: Set[ClassDenotation])(implicit ctx: Context): List[CompilationUnit] = { |
| 29 | + if (unprocessed.isEmpty) processed.toList |
| 30 | + else { |
| 31 | + val accum = new ClassesToLoadAccumulator |
| 32 | + val classesToLoad = unprocessed.foldLeft(Set.empty[ClassDenotation])((acc, unit) => accum.apply(acc, unit.tpdTree)) -- loadedClasses |
| 33 | + val loadedUnits = classesToLoad.flatMap(cls => loadCompilationUnit(cls)) |
| 34 | + allUnits(processed ++ unprocessed, loadedUnits, loadedClasses ++ classesToLoad) |
| 35 | + } |
| 36 | + } |
| 37 | + |
| 38 | + if (ctx.settings.XlinkOptimise.value) super.runOn(allUnits(Set.empty, units.toSet, Set.empty)) |
| 39 | + else super.runOn(units) |
| 40 | + } |
| 41 | + |
| 42 | + /** Collects all class denotations that may need to be loaded. */ |
| 43 | + private class ClassesToLoadAccumulator extends TreeAccumulator[Set[ClassDenotation]] { |
| 44 | + private var inParents = false |
| 45 | + override def apply(acc: Set[ClassDenotation], tree: tpd.Tree)(implicit ctx: Context): Set[ClassDenotation] = tree match { |
| 46 | + case New(tpt) => accum(acc, tpt.tpe.classSymbol) |
| 47 | + case AppliedTypeTree(tpt, _) if inParents => accum(acc, tpt.symbol) |
| 48 | + case tree: RefTree if inParents || tree.symbol.is(Module) => |
| 49 | + foldOver(accum(acc, tree.symbol), tree) |
| 50 | + case tree @ Template(constr, parents, self, _) => |
| 51 | + val acc1 = this(acc, constr) |
| 52 | + inParents = true |
| 53 | + val acc2 = this(acc1, parents) |
| 54 | + inParents = false |
| 55 | + this(this(acc2, self), tree.body) |
| 56 | + case _ => foldOver(acc, tree) |
| 57 | + } |
| 58 | + |
| 59 | + /** Accumulate class denotation for `sym` if needed */ |
| 60 | + private def accum(acc: Set[ClassDenotation], sym: Symbol)(implicit ctx: Context): Set[ClassDenotation] = { |
| 61 | + val topClass = sym.topLevelClass.denot.asClass |
| 62 | + if (topClass.is(JavaDefined) || topClass.is(Scala2x) || topClass.symbol == defn.ObjectClass) acc |
| 63 | + else acc + topClass |
| 64 | + } |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +object LinkAll { |
| 69 | + |
| 70 | + private[LinkAll] def loadCompilationUnit(clsd: ClassDenotation)(implicit ctx: Context): Option[CompilationUnit] = { |
| 71 | + assert(ctx.settings.XlinkOptimise.value) |
| 72 | + val tree = clsd.symbol.asClass.tree |
| 73 | + if (tree.isEmpty) None |
| 74 | + else { |
| 75 | + ctx.log("Loading compilation unit for: " + clsd) |
| 76 | + Some(CompilationUnit.mkCompilationUnit(clsd, tree, forceTrees = false)) |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | +} |
0 commit comments