The Scala Debug Adapter

(The Scala Center team is dedicated to providing regular and transparent community updates about project plans & progress. In this forum we created a new topic category to allow quicker orientation going forward: “Scala Center Updates”. Even though all feedback is welcome, we keep the right to make executive decisions about projects we lead. Overview of all our activities can always be found at Scala Center Public Records)

A good debugging experience makes programming more approachable. It guides programmers through the internals of a program and helps them find bugs.

How can we bring support for debugging Scala in the many code editors of the modern days?

The Debug Adapter Protocol (DAP) is here to help. It is one of those protocols, like LSP and BSP, that abstracts over a feature, in our case debugging, to reduce the integration effort.

The goal of this project is to provide a common implementation of the DAP server for Scala on the JVM.

The origins

A first implementation of the DAP server for Scala was made in Bloop, based on the Microsoft Java Debugger project. It is since used by Metals to start a debug session, when the user clicks the run, test or debug button.

We decided to extract this implementation out of Bloop so that it can be reused by other build tools of the ecosystem. This new library is called the scala-debug-adpater and it was first released in February 2021.

It is not a standalone tool because it must be hosted by a build tool that is able to configure the classpath and link each source to its class files.

Integration in sbt

The sbt-debug-adapter is an sbt plugin that augments the sbt server with the DAP capability by using the scala-debug-adapter library. It is compatible with sbt 1.4.x and greater.

Concretely, it makes it possible to debug an sbt project inside Metals using the sbt server.

It is added automatically by Metals, so you do not need to install it manually. Just open Metals, switch to sbt build server and click the debug button.

Acknowledgement

Special thanks to @er1c who motivated this project and contributed to it.

What’s next

  1. Code Evaluation is the next big feature that is currently explored by @tdudzik and @tgodzik at VirtusLab.

  2. The integration of the scala-debug-adapter in Mill is a Google Summer of Code project for this year.

  3. Scala 3 support should come out-of-the-box but we will add more tests and identify bugs or limitations.

Any comment, feedback or suggestion is welcomed. The scala-debug-adpater is also open for contributions.

Would it be desirable to support Scala.js? How to implement it? If you have an idea on these questions, please share with us.

21 Likes

Oct. 2021 - Update

The scala-debug-adapter is an implementation of the Debug Adapter Protocol (DAP) for the Scala language. It is the main component of the debugger in Metals.

The goal of this project is to improve the debugging experience in Metals.

Initial State

The version 1.0 of the scala-debug-adpater was released in February 2021. It is a mere extraction of the DebugServer from Bloop, with very few improvements. It allowed us to run the DebugServer in sbt through the sbt-debug-adapter plugin.

Whether you use Bloop or sbt as the build server you now have the same debugging experience in Metals.

Note that only the JVM is supported. It is not yet possible to debug a Scala JS or Native application.

Expression Evaluation in Scala 2.12 and 2.13

Some time after 1.0, Tomasz Dudzik (@tdudzik) from VirtusLab started working on expression evaluation for Scala 2.12 and Scala 2.13.

Expression evaluation is probably the most common debugging feature that is still missing in Metals. It involves compiling and executing a Scala expression at some breakpoint in your program at runtime.

This challenging task contains many difficulties such as:

  • to accept all valid Scala expressions and reject the invalid ones
  • to resolve implicit values and conversions
  • to read any accessible variable, including private fields and local variables.
  • to call any accessible method, including private and local methods.
  • to instantiate any class, including private, local or anonymous class

Tomasz found its inspiration in an experimentation made for Dotty by Guillaume Martres (@smarter) from LAMP, that he presented at Scala Days 2018. Tomasz adapted the idea to Scala 2, and implemented it using the Scala 2 compiler API.

The current state of the expression evaluator is very promising. It can already do some of the advanced stuff like resolving the implicit values, or calling a method with default values. Some other features are still not supported and we are constantly finding new bugs or limitation.

However I believe the current evaluator has strong foundations, strong enough to support all main features and many corner cases in the near future. And I believe it is time to make it available to all users of Metals.

Releasing version 2.0.x

We released scala-debug-adapter 2.0 in September 2021 featuring expression evaluation. Since then we fixed a number of bugs and we added support for JDK 17.

This new version is already used in Bloop and Metals main branches. So, if all goes according to plan, the primary support for expression evaluation in Scala 2 will be available to all users in the next Metals version.

Demo:

What’s next

Expression evaluation in Scala 3

The Scala 3 evaluator is in our roadmap.

Improved debugging steps

When compiling Scala code to JVM bytecode, the compiler generates some intermediate methods that do not contain any user code.

The problem is the user generally don’t want the debugger to step into those methods, because it is useless and sometimes confusing.

As such methods we identified:

  • Getters and Setters
  • Mixin Forwarders
  • Bridge Methods

We want to improve the debugger by skipping those steps when the user click the “step into” or the “step out” buttons.

More information can be found in the corresponding issue.

Running the debug server in Metals (instead of the build server)

In the current architecture, the debug server runs in the build server. That makes it available to all potential debug clients. But the ecosystem contains only one debug client (Metals), and 2 debug servers (sbt and Bloop) out of 4 build servers (with Mill and Bazel).

In contrast, we could run the debug server in Metals. That would make it possible to debug a project whatever is its build server. Thus we would get a debugger when using Mill or any other build server. We could even have a debugger on Ammonite scripts.

Also maintenance would be lighter since we would only have to upgrade the scala-debug-adapter in Metals.

Debugging Scala.js

Debugging a Scala.js program, or test class, running on Node.js would be useful. But we do not have a clear plan yet for this project.

It is open for contribution.

Acknowledgement

I would like to thank:

  • Tomasz Dudzik (@tdudzik) for its dedicated work on the expression evaluator
  • Tomasz Godzik (@tgodzik) for its precious help and support
  • Guillaume Martres (@smarter) for clearing the ground for the Scala expression evaluator
17 Likes

@adpi2 amazing work and progress!

1 Like