|
| 1 | +package dotty.tools |
| 2 | +package dotc |
| 3 | + |
| 4 | +import core._ |
| 5 | +import Contexts._ |
| 6 | +import SymDenotations.ClassDenotation |
| 7 | +import Symbols._ |
| 8 | +import util.{FreshNameCreator, SourceFile, NoSource} |
| 9 | +import util.Spans.Span |
| 10 | +import ast.{tpd, untpd} |
| 11 | +import tpd.{Tree, TreeTraverser} |
| 12 | +import ast.Trees.{Import, Ident} |
| 13 | +import typer.Nullables |
| 14 | +import transform.SymUtils._ |
| 15 | +import core.Decorators._ |
| 16 | +import config.{SourceVersion, Feature} |
| 17 | +import StdNames.nme |
| 18 | +import scala.annotation.internal.sharable |
| 19 | + |
| 20 | +class CompilationUnit protected (val source: SourceFile) { |
| 21 | + |
| 22 | + override def toString: String = source.toString |
| 23 | + |
| 24 | + var untpdTree: untpd.Tree = untpd.EmptyTree |
| 25 | + |
| 26 | + var tpdTree: tpd.Tree = tpd.EmptyTree |
| 27 | + |
| 28 | + /** Is this the compilation unit of a Java file */ |
| 29 | + def isJava: Boolean = source.file.name.endsWith(".java") |
| 30 | + |
| 31 | + /** The source version for this unit, as determined by a language import */ |
| 32 | + var sourceVersion: Option[SourceVersion] = None |
| 33 | + |
| 34 | + /** Pickled TASTY binaries, indexed by class. */ |
| 35 | + var pickled: Map[ClassSymbol, () => Array[Byte]] = Map() |
| 36 | + |
| 37 | + /** The fresh name creator for the current unit. |
| 38 | + * FIXME(#7661): This is not fine-grained enough to enable reproducible builds, |
| 39 | + * see https://github.com/scala/scala/commit/f50ec3c866263448d803139e119b33afb04ec2bc |
| 40 | + */ |
| 41 | + val freshNames: FreshNameCreator = new FreshNameCreator.Default |
| 42 | + |
| 43 | + /** Will be set to `true` if there are inline call that must be inlined after typer. |
| 44 | + * The information is used in phase `Inlining` in order to avoid traversing trees that need no transformations. |
| 45 | + */ |
| 46 | + var needsInlining: Boolean = false |
| 47 | + |
| 48 | + /** Set to `true` if inliner added anonymous mirrors that need to be completed */ |
| 49 | + var needsMirrorSupport: Boolean = false |
| 50 | + |
| 51 | + /** Will be set to `true` if contains `Quote`. |
| 52 | + * The information is used in phase `Staging`/`Splicing`/`PickleQuotes` in order to avoid traversing trees that need no transformations. |
| 53 | + */ |
| 54 | + var needsStaging: Boolean = false |
| 55 | + |
| 56 | + /** Will be set to true if the unit contains a captureChecking language import */ |
| 57 | + var needsCaptureChecking: Boolean = false |
| 58 | + |
| 59 | + /** Will be set to true if the unit contains a pureFunctions language import */ |
| 60 | + var knowsPureFuns: Boolean = false |
| 61 | + |
| 62 | + var suspended: Boolean = false |
| 63 | + var suspendedAtInliningPhase: Boolean = false |
| 64 | + |
| 65 | + /** Can this compilation unit be suspended */ |
| 66 | + def isSuspendable: Boolean = true |
| 67 | + |
| 68 | + /** Suspends the compilation unit by thowing a SuspendException |
| 69 | + * and recording the suspended compilation unit |
| 70 | + */ |
| 71 | + def suspend()(using Context): Nothing = |
| 72 | + assert(isSuspendable) |
| 73 | + if !suspended then |
| 74 | + if (ctx.settings.XprintSuspension.value) |
| 75 | + report.echo(i"suspended: $this") |
| 76 | + suspended = true |
| 77 | + ctx.run.nn.suspendedUnits += this |
| 78 | + if ctx.phase == Phases.inliningPhase then |
| 79 | + suspendedAtInliningPhase = true |
| 80 | + throw CompilationUnit.SuspendException() |
| 81 | + |
| 82 | + private var myAssignmentSpans: Map[Int, List[Span]] | Null = null |
| 83 | + |
| 84 | + /** A map from (name-) offsets of all local variables in this compilation unit |
| 85 | + * that can be tracked for being not null to the list of spans of assignments |
| 86 | + * to these variables. |
| 87 | + */ |
| 88 | + def assignmentSpans(using Context): Map[Int, List[Span]] = |
| 89 | + if myAssignmentSpans == null then myAssignmentSpans = Nullables.assignmentSpans |
| 90 | + myAssignmentSpans.nn |
| 91 | +} |
| 92 | + |
| 93 | +@sharable object NoCompilationUnit extends CompilationUnit(NoSource) { |
| 94 | + |
| 95 | + override def isJava: Boolean = false |
| 96 | + |
| 97 | + override def suspend()(using Context): Nothing = |
| 98 | + throw CompilationUnit.SuspendException() |
| 99 | + |
| 100 | + override def assignmentSpans(using Context): Map[Int, List[Span]] = Map.empty |
| 101 | +} |
| 102 | + |
| 103 | +object CompilationUnit { |
| 104 | + |
| 105 | + class SuspendException extends Exception |
| 106 | + |
| 107 | + /** Make a compilation unit for top class `clsd` with the contents of the `unpickled` tree */ |
| 108 | + def apply(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit = |
| 109 | + val file = clsd.symbol.associatedFile.nn |
| 110 | + apply(SourceFile(file, Array.empty[Char]), unpickled, forceTrees) |
| 111 | + |
| 112 | + /** Make a compilation unit, given picked bytes and unpickled tree */ |
| 113 | + def apply(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit = { |
| 114 | + assert(!unpickled.isEmpty, unpickled) |
| 115 | + val unit1 = new CompilationUnit(source) |
| 116 | + unit1.tpdTree = unpickled |
| 117 | + if (forceTrees) { |
| 118 | + val force = new Force |
| 119 | + force.traverse(unit1.tpdTree) |
| 120 | + unit1.needsStaging = force.containsQuote |
| 121 | + unit1.needsInlining = force.containsInline |
| 122 | + } |
| 123 | + unit1 |
| 124 | + } |
| 125 | + |
| 126 | + /** Create a compilation unit corresponding to `source`. |
| 127 | + * If `mustExist` is true, this will fail if `source` does not exist. |
| 128 | + */ |
| 129 | + def apply(source: SourceFile, mustExist: Boolean = true)(using Context): CompilationUnit = { |
| 130 | + val src = |
| 131 | + if (!mustExist) |
| 132 | + source |
| 133 | + else if (source.file.isDirectory) { |
| 134 | + report.error(s"expected file, received directory '${source.file.path}'") |
| 135 | + NoSource |
| 136 | + } |
| 137 | + else if (!source.file.exists) { |
| 138 | + report.error(s"source file not found: ${source.file.path}") |
| 139 | + NoSource |
| 140 | + } |
| 141 | + else source |
| 142 | + new CompilationUnit(src) |
| 143 | + } |
| 144 | + |
| 145 | + /** Force the tree to be loaded */ |
| 146 | + private class Force extends TreeTraverser { |
| 147 | + var containsQuote = false |
| 148 | + var containsInline = false |
| 149 | + var containsCaptureChecking = false |
| 150 | + def traverse(tree: Tree)(using Context): Unit = { |
| 151 | + if (tree.symbol.isQuote) |
| 152 | + containsQuote = true |
| 153 | + if tree.symbol.is(Flags.Inline) then |
| 154 | + containsInline = true |
| 155 | + tree match |
| 156 | + case Import(qual, selectors) => |
| 157 | + tpd.languageImport(qual) match |
| 158 | + case Some(prefix) => |
| 159 | + for case untpd.ImportSelector(untpd.Ident(imported), untpd.EmptyTree, _) <- selectors do |
| 160 | + Feature.handleGlobalLanguageImport(prefix, imported) |
| 161 | + case _ => |
| 162 | + case _ => |
| 163 | + traverseChildren(tree) |
| 164 | + } |
| 165 | + } |
| 166 | +} |
0 commit comments