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
The scala-debug-adapter is the debugger of Scala programs in Metals.
Problem
Some Scala constructs generate methods in the bytecode that do not contain any part of the source code. For example we have the getter methods, the mixin forwarders, the bridges.
When the compiler generates those methods, it fills their source lines tables with the line of the class or field definition. That's because there is nothing else to go since there is no corresponding source code of the method.
As a consequence the JVM debugger steps into those generated methods showing the class or field definition.
This is:
confusing: the user does not need to know about those generated methods
useless: the user does not need to step into those methods since there is none of their code inside
Live example (mixin forwarder):
Instead, the user expects those intermediate steps to be skipped.
Identified patterns
In these examples, when the user steps into 1=> the debugger goes to 2=>.
public java.lang.Object foo();
descriptor: ()Ljava/lang/Object;
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #17 // Method foo:()Ljava/lang/String;
4: areturn
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lexample/D;
It is flagged ACC_BRIDGE. Its line number table contains line 7 which is the definition of class D.
In this case we can skip the bridge method on the fact that is flagged ACC_BRIDGE. That should be fine because all ACC_BRIGE methods should be skipped, even the java ones.
Solution 2: The compiler does not set line numbers in those methods
We change the compiler so that it does not set line numbers in those methods.
We should check, in the JVM specification, that it is valid to have a method with an empty source line table.
Also we should test that the JVM can skip those methods and step into the next method call.
And we should sync with JetBrains team to not break their debugger.
Solution 3: The compiler set some attribute in the class file.
It is easier for the compiler to know which method should be skipped and which one should not be skipped.
So if the Solution 2 is not viable, the compiler can still add some attribute in the class file that stores a list of methods that should be skipped.
Expectations
Even if solutions 2 and 3 are simpler, we expect solution 1 to be implemented so that we can support current versions of the Scala compiler. Solution 2 and 3 are not expected.
Stepping into and out should behave consistently.
Most of the described patterns should be implemented successfully.
Scala 2.12, 2.13 and 3 should be supported.
Each implemented patterns should be tested in the latest versions of Scala 2.12, 2.13 and 3.
Introduction
The scala-debug-adapter is the debugger of Scala programs in Metals.
Problem
Some Scala constructs generate methods in the bytecode that do not contain any part of the source code. For example we have the getter methods, the mixin forwarders, the bridges.
When the compiler generates those methods, it fills their source lines tables with the line of the class or field definition. That's because there is nothing else to go since there is no corresponding source code of the method.
As a consequence the JVM debugger steps into those generated methods showing the class or field definition.
This is:
Live example (mixin forwarder):
![step-into-mixin-forwarder](https://user-images.githubusercontent.com/13123162/136216767-68f762a5-fddd-4365-a44d-fdccfb8e82dd.gif)
Instead, the user expects those intermediate steps to be skipped.
Identified patterns
In these examples, when the user steps into
1=>
the debugger goes to2=>
.Getter
The getter method in the class file of
Foo
is:It is not flagged as
ACC_SYNTHETIC
. Its line number table contains line 4 where the fieldfoo
is defined.How to detect that a method is a getter?
Setter
The setter method in the class file of
Foo
is:It is not flagged as
ACC_SYNTHETIC
. Its line number table contains line 4 where the fieldfoo
is defined.How to detect that a method is a setter?
<field>_$eq
where<field>
is a field of the classMixin forwarder
The mixin forwarder in the class file of
B
is:Again it is not flagged as
ACC_SYNTHETIC
. Its line table contains line7
which is the definition of classB
.How to detect that a method is a mixin forwarder?
<methodname>$
in a interface x that have the same signature but takes an instance of x as first parameter.Bridge method
The bridge in the class file of
D
is:It is flagged
ACC_BRIDGE
. Its line number table contains line 7 which is the definition ofclass D
.In this case we can skip the bridge method on the fact that is flagged
ACC_BRIDGE
. That should be fine because allACC_BRIGE
methods should be skipped, even the java ones.Implementation
Solution 1: Create custom
StepFilters
Currently the
StepFilters
in https://github.com/scalacenter/java-debug contains:and it inherits from
ClassFilters
This is not what we need because we do not want:
But we can adapt this class with something more customizable:
Then create one custom filter for each identified patterns (in https://github.com/scalacenter/scala-debug-adapter).
The heuristic to identify such patterns can be taken from or inspired by the intellij-scala smartStepInto classes.
Solution 2: The compiler does not set line numbers in those methods
We change the compiler so that it does not set line numbers in those methods.
We should check, in the JVM specification, that it is valid to have a method with an empty source line table.
Also we should test that the JVM can skip those methods and step into the next method call.
And we should sync with JetBrains team to not break their debugger.
Solution 3: The compiler set some attribute in the class file.
It is easier for the compiler to know which method should be skipped and which one should not be skipped.
So if the Solution 2 is not viable, the compiler can still add some attribute in the class file that stores a list of methods that should be skipped.
Expectations
Going further
loadClass
when stepping into constructor #126The text was updated successfully, but these errors were encountered: