1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
package dotty.tools
package dotc
import core._
import Contexts._
import Periods._
import Symbols._
import Phases._
import Decorators._
import dotty.tools.dotc.transform.TreeTransforms.TreeTransformer
import io.PlainFile
import scala.io.Codec
import util._
import reporting.Reporter
import transform.TreeChecker
import rewrite.Rewrites
import java.io.{BufferedWriter, OutputStreamWriter}
import scala.annotation.tailrec
import scala.reflect.io.VirtualFile
import scala.util.control.NonFatal
/** A compiler run. Exports various methods to compile source files */
class Run(comp: Compiler)(implicit ctx: Context) {
assert(comp.phases.last.last.id <= Periods.MaxPossiblePhaseId)
assert(ctx.runId <= Periods.MaxPossibleRunId)
var units: List[CompilationUnit] = _
def getSource(fileName: String): SourceFile = {
val f = new PlainFile(fileName)
if (f.isDirectory) {
ctx.error(s"expected file, received directory '$fileName'")
NoSource
} else if (f.exists) {
val encoding = ctx.settings.encoding.value
new SourceFile(f, Codec(encoding))
} else {
ctx.error(s"not found: $fileName")
NoSource
}
}
def compile(fileNames: List[String]): Unit = try {
val sources = fileNames map getSource
compileSources(sources)
} catch {
case NonFatal(ex) =>
ctx.echo(i"exception occurred while compiling $units%, %")
throw ex
}
/** TODO: There's a fundamental design problem here: We assemble phases using `squash`
* when we first build the compiler. But we modify them with -Yskip, -Ystop
* on each run. That modification needs to either transform the tree structure,
* or we need to assemble phases on each run, and take -Yskip, -Ystop into
* account. I think the latter would be preferable.
*/
def compileSources(sources: List[SourceFile]) =
if (sources forall (_.exists)) {
units = sources map (new CompilationUnit(_))
compileUnits()
}
protected def compileUnits() = Stats.monitorHeartBeat {
ctx.checkSingleThreaded()
val phases = ctx.squashPhases(ctx.phasePlan,
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
ctx.usePhases(phases)
var lastPrintedTree: PrintedTree = NoPrintedTree
for (phase <- ctx.allPhases)
if (!ctx.reporter.hasErrors) {
val start = System.currentTimeMillis
units = phase.runOn(units)
if (ctx.settings.Xprint.value.containsPhase(phase)) {
for (unit <- units) {
lastPrintedTree =
printTree(lastPrintedTree)(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
}
}
ctx.informTime(s"$phase ", start)
}
if (!ctx.reporter.hasErrors) Rewrites.writeBack()
}
private sealed trait PrintedTree
private final case class SomePrintedTree(phase: String, tree: String) extends PrintedTree
private object NoPrintedTree extends PrintedTree
private def printTree(last: PrintedTree)(implicit ctx: Context): PrintedTree = {
val unit = ctx.compilationUnit
val prevPhase = ctx.phase.prev // can be a mini-phase
val squashedPhase = ctx.squashed(prevPhase)
val treeString = unit.tpdTree.show
ctx.echo(s"result of $unit after $squashedPhase:")
last match {
case SomePrintedTree(phase, lastTreeSting) if lastTreeSting != treeString =>
val msg =
if (!ctx.settings.XprintDiff.value && !ctx.settings.XprintDiffDel.value) treeString
else DiffUtil.mkColoredCodeDiff(treeString, lastTreeSting, ctx.settings.XprintDiffDel.value)
ctx.echo(msg)
SomePrintedTree(squashedPhase.toString, treeString)
case SomePrintedTree(phase, lastTreeSting) =>
ctx.echo(" Unchanged since " + phase)
last
case NoPrintedTree =>
ctx.echo(treeString)
SomePrintedTree(squashedPhase.toString, treeString)
}
}
def compile(sourceCode: String): Unit = {
val virtualFile = new VirtualFile(sourceCode) // use source code as name as it's used for equals
val writer = new BufferedWriter(new OutputStreamWriter(virtualFile.output, "UTF-8")) // buffering is still advised by javadoc
writer.write(sourceCode)
writer.close()
compileSources(List(new SourceFile(virtualFile, Codec.UTF8)))
}
/** The context created for this run */
def runContext = ctx
/** Print summary; return # of errors encountered */
def printSummary(): Reporter = {
ctx.runInfo.printMaxConstraint()
val r = ctx.reporter
r.printSummary
r
}
}
|