You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -9,11 +10,12 @@ The scala-debug-adapter is a server-side implementation of the [Debug Adapter Pr
9
10
It is based on and extends the [microsoft/java-debug](https://github.com/microsoft/java-debug) implementation.
10
11
11
12
The project originated in the [Bloop](https://github.com/scalacenter/bloop) repository, it is now released independently so that it can be used in other build tools of the Scala ecosystem.
12
-
For instance, the [sbt-debug-adapter](#sbt-debug-adapter) is an sbt plugin that provides sbt with the debug adapter capability.
13
+
For instance, the [sbt-debug-adapter](#sbt-debug-adapter plugin) is an sbt plugin that provides sbt with the debug adapter capability.
13
14
14
15
## Usage
15
16
16
17
You can add the `scala-debug-adapter` as a dependency in your `build.sbt`:
18
+
17
19
```scala
18
20
// build.sbt
19
21
scalaVersion :="2.12.16",
@@ -28,7 +30,7 @@ While it is always the case with Java 9, you probably want to use the `sbt-jdi-t
You can start a debug server by providing your own intance of `Debuggee`, `DebugToolsResolver` and `Logger`.
33
+
You can start a debug server by providing your own instance of `Debuggee`, `DebugToolsResolver` and `Logger`.
32
34
33
35
### The `Debuggee`
34
36
@@ -37,6 +39,7 @@ The `Debuggee` is a trait that describes the program that we want to debug.
37
39
It contains the list of modules of the current project, the list of libraries, the list of unmanaged entries and the java runtime.
38
40
39
41
There are a few difference between a module, a library and an unmanaged entry:
42
+
40
43
-`Module`: We know its exact scala version, and its compiler options.
41
44
They will be used by the debugger to evaluate expressions in the module.
42
45
-`Library`: We only know its binary version.
@@ -51,11 +54,12 @@ The sbt version of `Debuggee` can be found [here](https://github.com/scalacenter
51
54
52
55
### The `DebugToolsResolver`
53
56
54
-
Depending on the Scala versions specified in the `Debuggee`, the debugger will need to resolve some additionnal tools:
57
+
Depending on the Scala versions specified in the `Debuggee`, the debugger will need to resolve some additional tools:
58
+
55
59
- The expression compiler: to compile the Scala expression that the user wants to evaluate
56
-
- The step filter: to filter the intermediate steps of execution that are generated by the compiler, among which the mixin-forwarders, the bridges, the getters and setters, the synthetized methods.
60
+
- The step filter (renamed to `unpickler`): to filter the intermediate steps of execution that are generated by the compiler, among which the mixin-forwarders, the bridges, the getters and setters, the synthetized methods.
57
61
58
-
The role of the `DebugToolsResolver` is to resovle the debug tools and their dependencies, and to load them in a class-loader.
62
+
The role of the `DebugToolsResolver` is to resolve the debug tools and their dependencies, and to load them in a class-loader.
59
63
The debugger takes care of reusing those class-loaders as often as possible: it should call the `DebugToolsResolver` only once by Scala version.
60
64
61
65
To implement the `DebugToolsResolver` you can use the Maven coordinates of the required tools in `ch.epfl.scala.debugadapter.BuildInfo`.
@@ -74,21 +78,21 @@ val address = new DebugServer.Address()
// return address.uri for the DAP client to connect
81
85
address.uri
82
86
```
83
87
84
-
# sbt-debug-adapter
88
+
##sbt-debug-adapter plugin
85
89
86
90
The `sbt-debug-adapter` is an sbt plugin compatible with sbt `1.4.0` or greater.
87
91
It provides the sbt server with the BSP `debugSession/start` endpoint to start a Scala DAP server.
88
92
89
93
The specification of the `debugSession/start` endpoint can be found in the [Bloop documentation](https://scalacenter.github.io/bloop/docs/debugging-reference).
90
94
91
-
##Usage
95
+
### Plugin usage
92
96
93
97
As a global plugin use `~/.sbt/1.0/plugins/plugins.sbt`, otherwise add to your local project (e.g. `project/plugins.sbt`):
94
98
@@ -105,19 +109,86 @@ To do so you can use the `sbt-jdi-tools` plugin in the meta project (it goes to
The [`expression-compiler`](https://github.com/scalacenter/scala-debug-adapter/tree/main/modules/expression-compiler) is a module used to compile expression from the debug console. It will insert the expression in the source file, compile it, and return the result of the expression.
117
+
118
+
To do so, it adds 2 new phases to the Scala compiler:
119
+
120
+
-`extract-expression`: extract the expression from the source file in a new class from where we can evaluate it. To keep the context (local variables), we import them in the new class via a `Map`
121
+
-`resolve-reflect-eval`: use reflection to resolve some symbols of the expression
109
122
110
-
## tests
123
+
Debugging the `expression-compiler` requires to enable some logs to show the trees generated by the compiler and the expression compiler. To do so, you need to uncomment the following lines:
124
+
125
+
-`println(messageAndPos(...))` in `ExpressionReporter.scala`
126
+
- For Scala 3.1+: [file](https://github.com/iusildra/scala-debug-adapter/blob/main/modules/expression-compiler/src/main/scala-3.1+/dotty/tools/dotc/ExpressionReporter.scala)
127
+
- For Scala 3.0: [file](https://github.com/iusildra/scala-debug-adapter/blob/main/modules/expression-compiler/src/main/scala-3.0/dotty/tools/dotc/ExpressionReporter.scala)
128
+
-`// "-Vprint:typer,extract-expression,resolve-reflect-eval"` in [`ExpressionCompilerBridge.scala`](https://github.com/iusildra/scala-debug-adapter/blob/main/modules/expression-compiler/src/main/scala-3/dotty/tools/dotc/ExpressionCompilerBridge.scala)
129
+
130
+
### Runtime evaluator
131
+
132
+
#### Idea
133
+
134
+
The runtime evaluator is a fully reflection-based evaluation mode designed for simple expressions (without implicits, generics, etc.) written in the debug console. It is less precise than the expression compiler (that can evaluate any kind of expression), but it is much faster (since it does not need to compile). Also, because it is reflection-based, we can access runtime types and private members, which is not possible with the expression compiler. For instance:
135
+
136
+
```scala
137
+
classA { privatevalx=64 }
138
+
classBextendsA { valy=42 }
139
+
140
+
vala:A=newA
141
+
valb:A=newB
142
+
143
+
// a.x returns 64
144
+
// b.y returns 42
145
+
```
146
+
147
+
#### Constraints
148
+
149
+
Because it is less precise than the expression compiler, it might fail while evaluating an expression (and placing the program in an invalid state), or evaluate it incorrectly (bad overload resolution for instance). To overcome this, a validation step is performed before any evaluation and an order of priority has been defined to choose between the expression compiler and the runtime evaluator:
The evaluator is implemented in a 3-steps process:
156
+
157
+
- parse the expression with [scalameta](https://scalameta.org/) to get the tree representing the expression
158
+
- validation: traverse the tree and recursively check that the expression and its sub-expressions can be evaluated at runtime (type-checking, polymorphism overloads resolution, type resolution, etc.). Transform the AST from scalameta to a new AST containing all the information needed for evaluation (see [RuntimeTree.scala](https://github.com/scalacenter/scala-debug-adapter/blob/main/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimeTree.scala))
159
+
- evaluation: consume the new AST and evaluate the expression
160
+
161
+
The validation AST is separated in 2 main parts:
162
+
163
+
-`RuntimeEvaluableTree`: these nodes are valid expressions by themselves (e.g. a literal, a method call, a module, etc.)
164
+
-`RuntimeValidationTree`: theses nodes are not a valid expression by themselves (e.g. a class name), but they can be contained within an evaluable node (e.g. static member access)
165
+
166
+
The validation is modeled with a [Validation](https://github.com/scalacenter/scala-debug-adapter/blob/main/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/Validation.scala) monad:
167
+
168
+
-`Valid`: the expression is valid
169
+
-`Recoverable`: the expression is not valid, but we might be able to recover from it (e.g. when validating `foo`, we might not find a local variable `foo`, but we might find a field, a method, etc. that we can use instead)
170
+
-`CompilerRecoverable` when information at runtime is not enough to validate the information (e.g. overloads ambiguity at runtime), the expression compiler might be able to validate it
171
+
-`Fatal` for errors that cannot be recovered (e.g. parsing error, unexpected exception, etc.). *Abort the whole evaluation process*
172
+
173
+
#### Pre evaluation
174
+
175
+
Pre-evaluation is a validation mode that first validate an expression, and evaluate it does not have side effects / fail (only a few nodes can be pre-evaluated). It is used to access the runtime type of an expression to get more information about it. ([RuntimePreEvaluationValidator.scala](https://github.com/scalacenter/scala-debug-adapter/blob/main/modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/RuntimePreEvaluationValidator.scala))
176
+
177
+
### tests
111
178
112
179
The [`tests`](https://github.com/scalacenter/scala-debug-adapter/tree/main/modules/tests/src/test/scala/ch/epfl/scala/debugadapter) module contains the tests of the Scala Debug Server.
113
180
It uses the [TestingDebugClient](https://github.com/scalacenter/scala-debug-adapter/blob/main/modules/tests/src/main/scala/ch/epfl/scala/debugadapter/testfmk/TestingDebugClient.scala), a minimal debug client that is used to communicate with the debug server via a socket.
114
181
115
-
# References
182
+
### Show logs
183
+
184
+
To print the logs during the tests, you must change the logger in `DebugTestSuite#getDebugServer` and `DebugTestSuite#startDebugServer` from `NoopLogger` to `PrintLogger`
0 commit comments