Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add expression compiler #22597

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft

Add expression compiler #22597

wants to merge 19 commits into from

Conversation

adpi2
Copy link
Member

@adpi2 adpi2 commented Feb 13, 2025

This PR migrates the expression compiler from scalacenter/scala-debug-adapter to the scala3 repository. This change was agreed upon during the core meeting a few months ago to simplify the release process and coordination. By doing so, we eliminate the need to release any part of scalacenter/scala-debug-adapter after each Scala 3 release to provide full debugging support in Metals.

The ExpressionCompiler and ExpressionCompilerBridge is placed in the main compiler module to streamline integration with external tools like sbt, Metals, Bloop, IJ Scala plugin. This avoids the complexity for those tools them to download additional jars.

The expression compiler

The expression compiler extends the main compiler with 3 phases:

  • InsertExpression: Just after parsing. It parses and inserts the expression in the original source tree. It also inserts the expression class, with a placeholder def evaluate: Any method.

  • ExtractExpression:
    After Typer, the pickler phases, ExtensionMethods and ElimByName.
    We must extract the expression before LambdaLift, otherwise the expression itself can influence how lambdas are going to be lifted. Notably it can contains lambda that needs to be lifted to the expression class.
    ExtractExpression extracts the expression from the original tree to the evaluate method of the expression class.
    At this time it is too early to know exactly how we can evaluate local variables and inaccessible members, so it transform such trees into magic calls of the reflectEval method, whith a ReflectEvalStrategy attachment.

  • ResolveReflectEval:
    At the end of the transform phases, it transforms all calls to reflectEval into real reflective calls, using info from the ReflectEvalStrategy.

Usage of the expression compiler

The expression compiler is used by the debuggers such as scalacenter/scala-debug-adapter and the IJ Scala plugin to provide support for the debug console. It allows to compile any Scala expression against a running Scala program. The compiled class can then be loaded to compute the evaluation output.

The ExpressionCompilerBridge can easily be invoked by reflection from an isolated classloader. It takes as input:

  • outputDir: Path
  • classPath: String
  • options: Array[String]: the compiler options
  • sourceFile: Path: the source file in which we compile the expression
  • config: ExpressionCompilerConfig:
    • packageName: the current package name of the evaluation frame
    • outputClassName: String: the name of the output class, that will be loaded by the debuggee to evaluated the expression
    • breakpointLine: Int: the line in source file where we evaluate the expression
    • expression: String: the expression to compile
    • localVariables: Set[String]: a set of visible local variables (useful for checking captures)
    • errorReporter: Consumer[String]

The ExpressionCompilerConfig is a factory class. It can evolve in binary compatible ways to allow older and newer versions of the debugger to use older and newer versions of the ExpressionCompilerBridge.

@adpi2 adpi2 force-pushed the add-expression-compiler branch from 37985f6 to 839713b Compare February 13, 2025 15:42
@tgodzik
Copy link
Contributor

tgodzik commented Feb 17, 2025

Looks like the tests are not compiling due to usage of newer JDK API?

@adpi2
Copy link
Member Author

adpi2 commented Feb 17, 2025

Looks like the tests are not compiling due to usage of newer JDK API?

I need to configure sbt-jdi-tools to enable JDI on JDK 8.

@adpi2 adpi2 force-pushed the add-expression-compiler branch 3 times, most recently from 55e7262 to df7f853 Compare February 18, 2025 14:34
@adpi2 adpi2 force-pushed the add-expression-compiler branch from 7df3f19 to f9f8566 Compare February 24, 2025 12:44
@adpi2 adpi2 force-pushed the add-expression-compiler branch from f9f8566 to 8106811 Compare February 24, 2025 15:07
@adpi2 adpi2 force-pushed the add-expression-compiler branch 3 times, most recently from 7556b14 to d60954c Compare February 27, 2025 12:55
@adpi2 adpi2 force-pushed the add-expression-compiler branch from d60954c to b207244 Compare February 27, 2025 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants