Skip to content

Commit 7faf4b4

Browse files
committed
Add ExpressionCompiler in compiler module
1 parent bb2aadb commit 7faf4b4

11 files changed

+1168
-3
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dotty.tools.debug
2+
3+
import dotty.tools.dotc.Compiler
4+
import dotty.tools.dotc.core.Contexts.Context
5+
import dotty.tools.dotc.core.Phases.Phase
6+
import dotty.tools.dotc.transform.ElimByName
7+
8+
class ExpressionCompiler(config: ExpressionCompilerConfig) extends Compiler:
9+
10+
override protected def frontendPhases: List[List[Phase]] =
11+
val parser :: others = super.frontendPhases: @unchecked
12+
parser :: List(InsertExpression(config)) :: others
13+
14+
override protected def transformPhases: List[List[Phase]] =
15+
val store = ExpressionStore()
16+
// the ExtractExpression phase should be after ElimByName and ExtensionMethods, and before LambdaLift
17+
val transformPhases = super.transformPhases
18+
val index = transformPhases.indexWhere(_.exists(_.phaseName == ElimByName.name))
19+
val (before, after) = transformPhases.splitAt(index + 1)
20+
(before :+ List(ExtractExpression(config, store))) ++ (after :+ List(ResolveReflectEval(config, store)))
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package dotty.tools.debug
2+
3+
import java.nio.file.Path
4+
import java.util.function.Consumer
5+
import java.{util => ju}
6+
import scala.jdk.CollectionConverters.*
7+
import scala.util.control.NonFatal
8+
import dotty.tools.dotc.reporting.StoreReporter
9+
import dotty.tools.dotc.core.Contexts.Context
10+
import dotty.tools.dotc.Driver
11+
12+
class ExpressionCompilerBridge:
13+
def run(
14+
outputDir: Path,
15+
classPath: String,
16+
options: Array[String],
17+
sourceFile: Path,
18+
config: ExpressionCompilerConfig
19+
): Boolean =
20+
val args = Array(
21+
"-d",
22+
outputDir.toString,
23+
"-classpath",
24+
classPath,
25+
"-Yskip:pureStats"
26+
// Debugging: Print the tree after phases of the debugger
27+
// "-Vprint:insert-expression,resolve-reflect-eval",
28+
) ++ options :+ sourceFile.toString
29+
val driver = new Driver:
30+
protected override def newCompiler(using Context): ExpressionCompiler = ExpressionCompiler(config)
31+
val reporter = ExpressionReporter(error => config.errorReporter.accept(error))
32+
try
33+
driver.process(args, reporter)
34+
!reporter.hasErrors
35+
catch
36+
case NonFatal(cause) =>
37+
cause.printStackTrace()
38+
throw cause
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package dotty.tools.debug
2+
3+
import dotty.tools.dotc.ast.tpd.*
4+
import dotty.tools.dotc.core.Symbols.*
5+
import dotty.tools.dotc.core.Types.*
6+
import dotty.tools.dotc.core.Names.*
7+
import dotty.tools.dotc.core.Flags.*
8+
import dotty.tools.dotc.core.Contexts.*
9+
import dotty.tools.dotc.core.SymUtils
10+
11+
import java.{util => ju}
12+
import ju.function.Consumer
13+
14+
class ExpressionCompilerConfig private[debug] (
15+
packageName: String,
16+
outputClassName: String,
17+
private[debug] val breakpointLine: Int,
18+
private[debug] val expression: String,
19+
private[debug] val localVariables: ju.Set[String],
20+
private[debug] val errorReporter: Consumer[String],
21+
private[debug] val testMode: Boolean
22+
):
23+
def this() = this(
24+
packageName = "",
25+
outputClassName = "",
26+
breakpointLine = -1,
27+
expression = "",
28+
localVariables = ju.Collections.emptySet,
29+
errorReporter = _ => (),
30+
testMode = false,
31+
)
32+
33+
def withPackageName(packageName: String): ExpressionCompilerConfig = copy(packageName = packageName)
34+
def withOutputClassName(outputClassName: String): ExpressionCompilerConfig = copy(outputClassName = outputClassName)
35+
def withBreakpointLine(breakpointLine: Int): ExpressionCompilerConfig = copy(breakpointLine = breakpointLine)
36+
def withExpression(expression: String): ExpressionCompilerConfig = copy(expression = expression)
37+
def withLocalVariables(localVariables: ju.Set[String]): ExpressionCompilerConfig = copy(localVariables = localVariables)
38+
def withErrorReporter(errorReporter: Consumer[String]): ExpressionCompilerConfig = copy(errorReporter = errorReporter)
39+
40+
private[debug] val expressionTermName: TermName = termName(outputClassName.toLowerCase.toString)
41+
private[debug] val expressionClassName: TypeName = typeName(outputClassName)
42+
43+
private[debug] def expressionClass(using Context): ClassSymbol =
44+
if packageName.isEmpty then requiredClass(outputClassName)
45+
else requiredClass(s"$packageName.$outputClassName")
46+
47+
private[debug] def evaluateMethod(using Context): Symbol =
48+
expressionClass.info.decl(termName("evaluate")).symbol
49+
50+
private def copy(
51+
packageName: String = packageName,
52+
outputClassName: String = outputClassName,
53+
breakpointLine: Int = breakpointLine,
54+
expression: String = expression,
55+
localVariables: ju.Set[String] = localVariables,
56+
errorReporter: Consumer[String] = errorReporter,
57+
) = new ExpressionCompilerConfig(
58+
packageName,
59+
outputClassName,
60+
breakpointLine,
61+
expression,
62+
localVariables,
63+
errorReporter,
64+
testMode
65+
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dotty.tools.debug
2+
3+
import dotty.tools.dotc.core.Contexts.*
4+
import dotty.tools.dotc.reporting.AbstractReporter
5+
import dotty.tools.dotc.reporting.Diagnostic
6+
7+
private class ExpressionReporter(reportError: String => Unit) extends AbstractReporter:
8+
override def doReport(dia: Diagnostic)(using Context): Unit =
9+
// Debugging: println(messageAndPos(dia))
10+
dia match
11+
case error: Diagnostic.Error =>
12+
val newPos = error.pos.source.positionInUltimateSource(error.pos)
13+
val errorWithNewPos = new Diagnostic.Error(error.msg, newPos)
14+
reportError(stripColor(messageAndPos(errorWithNewPos)))
15+
case _ =>
16+
// TODO report the warnings in the expression
17+
()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dotty.tools.debug
2+
3+
import dotty.tools.dotc.ast.tpd.*
4+
import dotty.tools.dotc.core.Symbols.*
5+
import dotty.tools.dotc.core.Types.*
6+
import dotty.tools.dotc.core.Names.*
7+
import dotty.tools.dotc.core.Flags.*
8+
import dotty.tools.dotc.core.Contexts.*
9+
import dotty.tools.dotc.core.SymUtils
10+
11+
private class ExpressionStore:
12+
var symbol: TermSymbol | Null = null
13+
// To resolve captured variables, we store:
14+
// - All classes in the chain of owners of the expression
15+
// - The first local method enclosing the expression
16+
var classOwners: Seq[ClassSymbol] = Seq.empty
17+
var capturingMethod: Option[TermSymbol] = None
18+
19+
def store(exprSym: Symbol)(using Context): Unit =
20+
symbol = exprSym.asTerm
21+
classOwners = exprSym.ownersIterator.collect { case cls: ClassSymbol => cls }.toSeq
22+
capturingMethod = exprSym.ownersIterator
23+
.find(sym => (sym.isClass || sym.is(Method)) && sym.enclosure.is(Method)) // the first local class or method
24+
.collect { case sym if sym.is(Method) => sym.asTerm } // if it is a method

0 commit comments

Comments
 (0)