Bug 562056 - [1.8] Cannot evaluate expression in xtext generated lambda scope
Summary: [1.8] Cannot evaluate expression in xtext generated lambda scope
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Debug (show other bugs)
Version: 4.16   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 4.16 M3   Edit
Assignee: Simeon Andreev CLA
QA Contact:
URL:
Whiteboard: 4.16 M2
Keywords: regression
Depends on:
Blocks: 560626
  Show dependency tree
 
Reported: 2020-04-13 05:20 EDT by Simeon Andreev CLA
Modified: 2020-05-19 07:04 EDT (History)
5 users (show)

See Also:


Attachments
Screenshot from debugging xtext project. (295.46 KB, image/png)
2020-04-13 10:21 EDT, Simeon Andreev CLA
no flags Details
Screenshot from debugging Eclipse + xtext product. (371.00 KB, image/png)
2020-04-13 10:21 EDT, Simeon Andreev CLA
no flags Details
Cannot evaluate in member CompatibilityPart.objectSetHandler initialized with a lambda. (311.17 KB, image/png)
2020-04-16 10:22 EDT, Simeon Andreev CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Simeon Andreev CLA 2020-04-13 05:20:00 EDT
We observe a regression from the fix for bug 560392 in our product. In particular, we cannot evaluate the expression "return 7 + 5;" in xtext generated Java code, during one of our automatic regression tests.

We observe a variable "arg$1" in the lambda scope, from more or less code as follows:

	    public void execute(final Interface it) {
	        // LINE INFO 16
	        final Runnable _function_1 = () -> {
	        // LINE INFO 17
	        it.println("Yeah!");
	        // LINE INFO 18
	        it.add(1); // the evaluation is done at a breakpoint here
	        };
	        // LINE INFO 19
	        final Runnable _function_2 = () -> {
	        // LINE INFO 20
	        it.println("OMG!");
	        // LINE INFO 21
	        it.add(2);
	        };
	    }

The variable name "1" is computed from "arg$1" during the new code in ASTEvaluationEngine.getCompiledExpression(String, IJavaStackFrame), which leads to a compile problem in the compilation unit:

Pb(235) Syntax error on token "1", invalid VariableDeclaratorId

From that point on the evaluation snippet doesn't work (I assume any snippet won't evaluate).

So far we have no reproduction snippet unfortunately. When I use the snippet from above in a minimal Java project, the variable is called "val$it" and the fix for bug 560392 works as intended.

We are looking into the problem.
Comment 1 Simeon Andreev CLA 2020-04-13 05:22:09 EDT
Sarika, any idea why we would have a variable named "arg$1" and not "val$it" while in the lambda for "_functional_1" (see the snippet in the description)?
Comment 2 Simeon Andreev CLA 2020-04-13 08:01:40 EDT
I see that in JDIStackFrame.LambdaASTVisitor.visit(LambdaExpression), the following check prevents the update of the lambda variable name to the name expected by the fix in bug 560392:

// check if the lineNo fall in lambda region, it can either be single or multiline lambda body.
if (lineNo < cu.getLineNumber(lambdaExpression.getStartPosition())
        || lineNo > cu.getLineNumber(lambdaExpression.getStartPosition() + lambdaExpression.getLength())) {
    return true;
}

lineNo is equal to the line in the xtext DSL file, but the compilation unit is the source file generated by xtext, where the line numbers are different. So the if expression evaluates to true, and the renaming of the lambda variables is skipped. This breaks the assumptions in the new code from bug 560392.
Comment 3 Simeon Andreev CLA 2020-04-13 08:10:55 EDT
Christian, any suggestions here? IMO this is one of the following:

* JDIStackFrame should report a line number from the generated source.
* The compilation unit should report lines that match the xtext DSL.

Though no idea what might break due to either of those.

Or is some adapter call missing in JDIStackFrame.LambdaASTVisitor.visit(LambdaExpression), that makes the xtext magic work as expected?
Comment 4 Gayan Perera CLA 2020-04-13 08:16:43 EDT
@Simeon

- Can you add example snippet of generated java source code which cause this issue ?
- Also can you reproduce this issue with pure java project ?
- Does your xtext project use maven for dependency management ?
Comment 5 Simeon Andreev CLA 2020-04-13 08:26:05 EDT
> - Can you add example snippet of generated java source code which cause this
> issue ?
Christian, can you provide a minimal example? Debugging with a breakpoint inside a generated lambda should suffice. I tried the arithmetic xtext example, but have no idea how to actually run the code in the .xtend source.

> - Also can you reproduce this issue with pure java project ?
No.

> - Does your xtext project use maven for dependency management ?
No.
Comment 6 Gayan Perera CLA 2020-04-13 08:32:40 EDT
@Simeon Does xtext generate source in java or in xtend ? i'm bit confused about the problem. Is the actual problem lies in xtend source debug execution or java debug execution ?
Comment 7 Andrey Loskutov CLA 2020-04-13 08:52:52 EDT
(In reply to Simeon Andreev from comment #3)
> Christian, any suggestions here? IMO this is one of the following:
> 
> * JDIStackFrame should report a line number from the generated source.
> * The compilation unit should report lines that match the xtext DSL.

Without looking into the code, I would expect we generate class files out of xtext DSL, so most likely something with line numbers is messed up in our bytecode generation for lambdas.
Comment 8 Simeon Andreev CLA 2020-04-13 09:01:14 EDT
(In reply to Gayan Perera from comment #6)
> @Simeon Does xtext generate source in java or in xtend ? i'm bit confused
> about the problem. Is the actual problem lies in xtend source debug
> execution or java debug execution ?

As far as I can tell, the problem occurs as follows:

1. Bug 561542 ensures lambda scope variables have human readable names.
2. Bug 560392 uses those names to allow evaluations, using outer scope variables inside the lambda scope.
3.1. xtext generates Java code from some DSL code (xtext, xtend, or something custom as is in our case). The user then seemingly debugs their DSL code, stepping through it. While JDT mostly works on the generated Java code and knows nothing about the DSL code.
3.2. As a result, the fix for bug 561542 is not working (see 1.).
3.3. With the fix for bug 560392 (see 2.), there is now a compile error during the evaluation. In particular, "arg$1" results in a variable name "1" which is invalid. So no evaluation works in the lambda scope, including evaluations that worked prior to the fix for bug 560392.

I.e. xtext provides "wrong" JDIStackFrame line information (I'm yet to check how exactly) but until the fix for bug 560392 that "bothered no one".

A workaround could be:

diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
index fa250018f..8153d896c 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
@@ -331,7 +331,7 @@ public class ASTEvaluationEngine implements IAstEvaluationEngine {
                                if (variable instanceof IJavaVariable && !isLambdaOrImplicitVariable(variable)) {
                                        IJavaVariable javaVariable = (IJavaVariable) variable;
                                        final boolean lambdaField = LambdaUtils.isLambdaField(variable);
-                                       String variableName = (lambdaField) ? variable.getName().substring(ANONYMOUS_VAR_PREFIX.length()) : variable.getName();
+                                       String variableName = (lambdaField && variable.getName().startsWith(ANONYMOUS_VAR_PREFIX)) ? variable.getName().substring(ANONYMOUS_VAR_PREFIX.length()) : variable.getName();
                                        if (variableName != null && (!variableName.contains("$") || lambdaField)) { //$NON-NLS-1$
                                                if (!isLocalType(javaVariable.getSignature()) && !names.contains(variableName)) {
                                                        locals[numLocals] = javaVariable;

No idea about an actual fix. Or whether we want a fix or a workaround.

(In reply to Andrey Loskutov from comment #7)
> Without looking into the code, I would expect we generate class files out of
> xtext DSL, so most likely something with line numbers is messed up in our
> bytecode generation for lambdas.

No idea about this. The line number in JDIStackFrame comes from JDIStackFrame.fLocation, which is the DSL file in this case.
Comment 9 Gayan Perera CLA 2020-04-13 09:01:36 EDT
I think also the mentioned two bugs in JDT,

1 fix fixed the problem we had with mixing variables which were not in lambda context.
2 fix Improved the fix to support multi line lambda statements.

I don't see a direct connection with this bug and those two bugs provided that you bytecode is generated as @Andrey mentioned or if you Transpiling xtend to java and then compiling into JVM bytecode and expect that the generated lambdas are synched with xtend code.
Comment 10 Gayan Perera CLA 2020-04-13 09:07:36 EDT
Please ignore my previous comment since i was referring to the wrong bug
Comment 11 Gayan Perera CLA 2020-04-13 09:12:05 EDT
arg$1 variable name is wrong, you will not be able to evaluate a variable name like that with or without bug560392. One situation where the arg$1 variables are described at https://bugs.eclipse.org/bugs/show_bug.cgi?id=560392#c41. Please see if this is the problem you are having. If you get the proper variable names the evaluation should work.
Comment 12 Eclipse Genie CLA 2020-04-13 09:15:01 EDT
New Gerrit change created: https://git.eclipse.org/r/160832
Comment 13 Christian Dietrich CLA 2020-04-13 09:27:05 EDT
@sebastian. you might be of better help than me here.
please also check the gerrit
Comment 14 Gayan Perera CLA 2020-04-13 09:30:31 EDT
Why is this difference in lamnda outer scope variable names in Java -> JVM bytecode and xText -> JVM bytecode. Isn't the problem is in xText -> JVM bytecode where it generate the outerscope variable names different ?
Comment 15 Christian Dietrich CLA 2020-04-13 09:32:00 EDT
Xtext generates Java.
JDT generates bytecode.
JDT uses ASM to add line information to bytecode
=> maybe an asm problem?
Comment 16 Christian Dietrich CLA 2020-04-13 09:32:32 EDT
Correction:

Xtext uses asm to add the Line infos to JDT generatd bytecode
Comment 17 Gayan Perera CLA 2020-04-13 09:33:46 EDT
Yes i think we should try to investigate that, because otherwise JDT will need to handle this kind of edge scenarios for all other JVM languages like groovy as well.
Comment 18 Gayan Perera CLA 2020-04-13 09:35:24 EDT
(In reply to Christian Dietrich from comment #16)
> Correction:
> 
> Xtext uses asm to add the Line infos to JDT generatd bytecode

Well then how the variable names are different ? As you say if the java source was compiled using the javac to jvm bytecode, we should get the same val$it as the variable name right ?. The fix seems to handle a situation where we have outer scope variables without the val$ prefix.
Comment 19 Gayan Perera CLA 2020-04-13 09:47:43 EDT
(In reply to Simeon Andreev from comment #2)
> I see that in JDIStackFrame.LambdaASTVisitor.visit(LambdaExpression), the
> following check prevents the update of the lambda variable name to the name
> expected by the fix in bug 560392:
> 
> // check if the lineNo fall in lambda region, it can either be single or
> multiline lambda body.
> if (lineNo < cu.getLineNumber(lambdaExpression.getStartPosition())
>         || lineNo > cu.getLineNumber(lambdaExpression.getStartPosition() +
> lambdaExpression.getLength())) {
>     return true;
> }
> 
> lineNo is equal to the line in the xtext DSL file, but the compilation unit
> is the source file generated by xtext, where the line numbers are different.
> So the if expression evaluates to true, and the renaming of the lambda
> variables is skipped. This breaks the assumptions in the new code from bug
> 560392.

Well if this is a issue, isn't that xtext code generation has a problem. IMO i would except this as a bug if you can reproduce it with a plain java project.
Comment 20 Christian Dietrich CLA 2020-04-13 09:49:04 EDT
@simeon. which are the wrong names?
Comment 21 Christian Dietrich CLA 2020-04-13 09:52:12 EDT
@simeon: can you reproduce this with pure xtend?
Comment 22 Gayan Perera CLA 2020-04-13 09:54:49 EDT
@christian: i would also like if we can see if this is reproducible from pure java project.

What is the compiler you are using to compile code into bytecode ?
Comment 23 Christian Dietrich CLA 2020-04-13 09:56:40 EDT
i dont understand

(1) xtext creates a java file
(2) eclipse jdt translates this into a .class file
(3) Xtext uses asm to (re)write that class file with additional line information
Comment 24 Gayan Perera CLA 2020-04-13 10:03:21 EDT
@christian
What if we debug the generated source files as a javaproject ? can you reproduce this issue ?
Comment 25 Simeon Andreev CLA 2020-04-13 10:21:10 EDT
Created attachment 282424 [details]
Screenshot from debugging xtext project.
Comment 26 Simeon Andreev CLA 2020-04-13 10:21:59 EDT
Created attachment 282425 [details]
Screenshot from debugging Eclipse + xtext product.

// class version 52.0 (52)
// access flags 0x21
public class common/ExampleMainFlowData extends ate/ext/prog/javaflow/ReplaceableFlowAlgorithm {

  // compiled from: ExampleMain.flow

  @Late/ext/prog/javaflow/OriginInformation;(sourceURI="Test/src/common/ExampleMain.flow", fragment="//@testFlow")

  @Late/ext/prog/javaflow/SmartestGenerated;() // invisible

  @Lxoc/dta/internal/annotations/SuppressFBWarnings;() // invisible
  // access flags 0x19
  public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup

  // access flags 0x9
  public static Z isInSyncWithFile

  // access flags 0x8
  static <clinit>()V
    ICONST_1
    PUTSTATIC common/ExampleMainFlowData.isInSyncWithFile : Z
    RETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x1
  public <init>(Late/ext/prog/javaflow/IAlgorithmFactory;)V
   L0
    ALOAD 0
    ALOAD 1
    INVOKESPECIAL ate/ext/prog/javaflow/ReplaceableFlowAlgorithm.<init>(Late/ext/prog/javaflow/IAlgorithmFactory;)V
    RETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    LOCALVARIABLE algorithmFactoryPool Late/ext/prog/javaflow/IAlgorithmFactory; L0 L1 1
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  public isInSyncWithFile()Z
   L0
    GETSTATIC common/ExampleMainFlowData.isInSyncWithFile : Z
    IRETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x4
  protected initializeParameters(Late/ext/prog/javaflow/IFlow;)V
   L0
    RETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    LOCALVARIABLE initializedFlow Late/ext/prog/javaflow/IFlow; L0 L1 1
    MAXSTACK = 0
    MAXLOCALS = 2

  // access flags 0x1
  public initializeTestSuites(Late/ext/prog/javaflow/IFlow;Ljava/util/Map;)V
   L0
    ALOAD 0
    ALOAD 1
    ALOAD 2
    LDC "Functional"
    LDC "common.ExampleMain____FunctionalSuiteData"
    INVOKEVIRTUAL common/ExampleMainFlowData.initializeTestSuite(Late/ext/prog/javaflow/IFlow;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;)V
    RETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    LOCALVARIABLE initializedFlow Late/ext/prog/javaflow/IFlow; L0 L1 1
    LOCALVARIABLE currentSuites Ljava/util/Map; L0 L1 2
    MAXSTACK = 5
    MAXLOCALS = 3

  // access flags 0x1
  public initializeSubflows(Late/ext/prog/javaflow/IFlow;Ljava/util/Map;)V
   L0
    RETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    LOCALVARIABLE initializedFlow Late/ext/prog/javaflow/IFlow; L0 L1 1
    LOCALVARIABLE currentSubflows Ljava/util/Map; L0 L1 2
    MAXSTACK = 0
    MAXLOCALS = 3

  // access flags 0x1
  public initializeCharacterizations(Late/ext/prog/javaflow/IFlow;Ljava/util/Map;)V
   L0
    RETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    LOCALVARIABLE initializedFlow Late/ext/prog/javaflow/IFlow; L0 L1 1
    LOCALVARIABLE currentChars Ljava/util/Map; L0 L1 2
    MAXSTACK = 0
    MAXLOCALS = 3

  // access flags 0x1
  public executeTestflow(Late/ext/prog/javaflow/IFlow;)V
   L0
    LINENUMBER 14 L0
    ALOAD 1
    LDC "Functional"
    INVOKEINTERFACE ate/ext/prog/javaflow/IFlow.getSuite(Ljava/lang/String;)Late/ext/prog/javaflow/ISuite;
    INVOKEINTERFACE ate/ext/prog/javaflow/ISuite.execute()V
   L1
    LINENUMBER 16 L1
    ALOAD 1
    INVOKEDYNAMIC run(Late/ext/prog/javaflow/IFlow;)Ljava/lang/Runnable; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()V, 
      // handle kind 0x6 : INVOKESTATIC
      common/ExampleMainFlowData.lambda$0(Late/ext/prog/javaflow/IFlow;)V, 
      ()V
    ]
    ASTORE 2
   L2
    LINENUMBER 19 L2
    ALOAD 1
    INVOKEDYNAMIC run(Late/ext/prog/javaflow/IFlow;)Ljava/lang/Runnable; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()V, 
      // handle kind 0x6 : INVOKESTATIC
      common/ExampleMainFlowData.lambda$1(Late/ext/prog/javaflow/IFlow;)V, 
      ()V
    ]
    ASTORE 3
   L3
    LINENUMBER 16 L3
    ALOAD 1
    LDC "Functional"
    INVOKEINTERFACE ate/ext/prog/javaflow/IFlow.getSuite(Ljava/lang/String;)Late/ext/prog/javaflow/ISuite;
    LDC "pass"
    LDC Lxoc/dta/datatypes/MultiSiteBoolean;.class
    INVOKEINTERFACE ate/ext/prog/javaflow/ISuite.getParameter(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
    CHECKCAST xoc/dta/datatypes/MultiSiteBoolean
    ALOAD 2
    ALOAD 3
    INVOKESTATIC ate/ext/prog/javaflow/MultiSiteIf.run(Lxoc/dta/datatypes/MultiSiteBoolean;Ljava/lang/Runnable;Ljava/lang/Runnable;)V
   L4
    LINENUMBER 16 L4
    ALOAD 1
    INVOKEINTERFACE ate/ext/prog/javaflow/IFlow.hasNoActiveSites()Z
    IFEQ L5
    RETURN
   L5
   FRAME APPEND [java/lang/Runnable java/lang/Runnable]
    RETURN
   L6
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L6 0
    LOCALVARIABLE it Late/ext/prog/javaflow/IFlow; L0 L6 1
    LOCALVARIABLE _function_1 Ljava/lang/Runnable; L2 L6 2
    LOCALVARIABLE _function_2 Ljava/lang/Runnable; L3 L6 3
    MAXSTACK = 3
    MAXLOCALS = 4

  // access flags 0x1
  public dependsOnTPVariable(Ljava/lang/String;)Z
   L0
    ICONST_0
    IRETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    LOCALVARIABLE tpVariable Ljava/lang/String; L0 L1 1
    MAXSTACK = 1
    MAXLOCALS = 2

  // access flags 0x1
  public getSetupHash()Ljava/lang/String;
   L0
    LDC "19ef21fb"
    ARETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  public debugHook(Late/ext/prog/javaflow/IFlow;)V
   L0
    LINENUMBER 13 L0
    RETURN
   L1
    LOCALVARIABLE this Lcommon/ExampleMainFlowData; L0 L1 0
    LOCALVARIABLE it Late/ext/prog/javaflow/IFlow; L0 L1 1
    MAXSTACK = 0
    MAXLOCALS = 2

  // access flags 0x100A
  private static synthetic lambda$0(Late/ext/prog/javaflow/IFlow;)V
   L0
    LINENUMBER 17 L0
    ALOAD 0
    LDC "Yeah!"
    INVOKEINTERFACE ate/ext/prog/javaflow/IFlow.println(Ljava/lang/Object;)V
   L1
    LINENUMBER 18 L1
    ALOAD 0
    ICONST_1
    INVOKEINTERFACE ate/ext/prog/javaflow/IFlow.addBin(I)V
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x100A
  private static synthetic lambda$1(Late/ext/prog/javaflow/IFlow;)V
   L0
    LINENUMBER 20 L0
    ALOAD 0
    LDC "OMG!"
    INVOKEINTERFACE ate/ext/prog/javaflow/IFlow.println(Ljava/lang/Object;)V
   L1
    LINENUMBER 21 L1
    ALOAD 0
    ICONST_2
    INVOKEINTERFACE ate/ext/prog/javaflow/IFlow.addBin(I)V
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 1
}
Comment 27 Gayan Perera CLA 2020-04-13 10:25:58 EDT
@Simeon this not the source code right, this is bytecode (i'm not a expert on ByteCode stuff). So the flow file directly get generated into a class file ? or does it get traspilled into .java source and then compiled to a .class file.
Comment 28 Christian Dietrich CLA 2020-04-13 10:28:24 EDT
@Gayan

nope. the bytecode is created by jdt
we just use ASM to wrote JSR-45 information to the class files afterwards
Comment 29 Christian Dietrich CLA 2020-04-13 10:34:41 EDT
i dont know if this ibuild is anywhere. in my WS (using 4.16 i-builds) i am getting val$it
Comment 30 Gayan Perera CLA 2020-04-13 10:35:12 EDT
@christian @simeon I'm not a expert in JDT inner workings, but from my understanding while working in the JDT area i can say the following.

If we take the java file that you have in your screenshot and try to debug it, i think you should be able to do it if you have all the required dependencies. Do we see a problem in trying to debug addBin(l) method ?

from your screenshots it seems the JDT logic is correct at JDIStackFrame when looking at your generated code. I think your sourcemapping might have a issue, because for the addBin(l) in your flow DSL which is line 18 should translate to like 51 in the java source code to get the correct variable information from the JDT.

So isn't the problem in this line mapping ?
Comment 31 Christian Dietrich CLA 2020-04-13 10:37:39 EDT
@Gayan

the line numbers relate to the flow file
(See JSR-45)
Comment 32 Simeon Andreev CLA 2020-04-13 10:39:57 EDT
Let me discuss this with Andrey tomorrow.
Comment 33 Simeon Andreev CLA 2020-04-13 10:42:12 EDT
Let me discuss this with Andrey tomorrow.
Comment 34 Gayan Perera CLA 2020-04-13 10:44:42 EDT
@Simeon good, and also its good to get some input from @Sarika on this since she know both of those bugs well in and out.
Comment 35 Gayan Perera CLA 2020-04-13 10:46:22 EDT
@christian

Seems like you are rewrting the the orginal java source line numbers with flow file line numbers ? did i got you right ?
Comment 36 Sebastian Zarnekow CLA 2020-04-13 10:57:29 EDT
Jumping into the discussion, I try to add a little bit of background information about debugging Xtext DSLs.

As Christian already mentioned, Xtext is used to generate Java source code.
That code is compiled by JDT in the context of an IJavaProject with the settings that apply to the regular compilation of all the source code in that Java project. E.g. if you untick some of the options about the classfile generation, the compiled code will not have information about parameter names, local var names etc.
After the class file was written by JDT, Xtext uses ASM to read the classfile and add more debug information.
Depending on the configuration of the project, Xtext will either add an aditional stratum to the classfile, that points to the original DLS file(s) that were used to create the Java source code (JSR 45, same technology that is used to debug JSP files), or alternatively the regular Java source information will be replaced by the DSL source line numbers. If the second option is chosen, there additional configuration flags, e.g. to hide some local vars that are synthesised during the Java source code generation, but variable names like 'it' will not be changed.

Things to watch out for when tracking this issue down:
Is the project configured correctly, e.g. do we see Java local vars when debugging plain Java code? Are parameter names kept in the class file correctly?
Does the JSR45 support in JDT work with lambda expressions?
Do the line numbers in the stratum match the real line numbers in Java and the DSL file?
Does debugging / evaluating expression work, if you chose 'Show source -> Java'  in the debugger on the DSL stack frame?
Comment 37 Gayan Perera CLA 2020-04-13 11:07:53 EDT
@Sebastian thanks good insight into this. I have some 3 years back experience working on xtext dsl and working with xtend to writing the code generator for my dsl.

Well i think the problem here is when we ask for CompilationUnit here in JDIStackFrame.visit method, we get line number information for java source code, not for the DSL source code which is represented by lineNo. So i think root cause for this issue is in here. We should be able to either convert the lineNo to javasource code line number which is 51 or we should be able to get the cu.getLineNumber for given offset from the DSL file perspective.

Therefore i think we should not try to fix this in ASTEvaluationEngine, rather we should try to fix the problems which doesn't provide the variable names as shown in the screenshots.

What do you guys think ?
Comment 38 Simeon Andreev CLA 2020-04-13 11:12:52 EDT
OK, once again, hopefully this time the complete information.

1. We use xtext to define a DSL. The user writes DSL.
2. xtext generates Java code from the DSL, in the bug case it generates a lambda expression.
3. We compile the Java code with Eclipse, to byte code.
4. xtext adjusts the byte code ("without telling Eclipse"), to add line information that matches the DSL.
5. During debugging, JDT asks the JVM for line numbers and "sees" the line numbers  set by xtext in step 4..
6.1. The code that fixes bug 561542 uses the adjusted line numbers (step 4.) and an ASTParser on the generated Java code (step 2.).
6.2. The lambda expression in the generated Java code does not match (line-wise) the DSL code. So the fix for bug 561542 does nothing.
6.3. So we have "arg$1" and so on, instead of "val$it" and so on, as lambda variables on the stack frame.
7.1. The fix for bug 560392 assumes step 6. went well and removes the "arg$" prefix, "thinking" it removes the "val$" prefix.
7.2. The remaining variable name is "1", which is illegal.
7.3. Any evaluation within the lambda expression (generated in step 2.) results in an error, since the evaluation scope contains compile problems.

With https://git.eclipse.org/r/#/c/160832/, the fix for bug 560392 is applied only if the fix for bug 561542 "worked". If not, the synthetic variable names are used, which prevents using outer scope (visible) variables to be used in expressions (but only for the very specific conditions, xtext + generated lambda).

We don't know how to fix xtext here, we are only adjusting ASTEvaluationEngine to not produce a broken context for evaluations (so that evaluations that worked before the fix for bug 560392 continue to work).
Comment 39 Sebastian Zarnekow CLA 2020-04-13 11:18:46 EDT
(In reply to Simeon Andreev from comment #38)
> hopefully this time the complete information.

Is (4) custom logic or Xtext default debugging information enhancement. If it is the latter, do you use JSR45 capabilities, or do you use the mode that replaces the Java line information.
Comment 40 Sebastian Zarnekow CLA 2020-04-13 11:19:16 EDT
(In reply to Simeon Andreev from comment #38)
> hopefully this time the complete information.

Is (4) custom logic or Xtext default debugging information enhancement. If it is the latter, do you use JSR45 capabilities, or do you use the mode that replaces the Java line information.
Comment 41 Christian Dietrich CLA 2020-04-13 11:22:53 EDT
@Sebastian.

we use TraceAsPrimarySourceInstaller
thus this should be reproducible with Xtend
(when configured respectively)
as well
Comment 42 Gayan Perera CLA 2020-04-13 11:27:44 EDT
(In reply to Simeon Andreev from comment #38)
> With https://git.eclipse.org/r/#/c/160832/, the fix for bug 560392 is
> applied only if the fix for bug 561542 "worked". If not, the synthetic
> variable names are used, which prevents using outer scope (visible)
> variables to be used in expressions (but only for the very specific
> conditions, xtext + generated lambda).

I see your point now @Simeon, sorry if i was a PITA upto now :(. The problem is the fix in bug560392 assumed that lambda variables are always resolved. You fix might solve future issues for other jvm languages as well.
 
> We don't know how to fix xtext here, we are only adjusting
> ASTEvaluationEngine to not produce a broken context for evaluations (so that
> evaluations that worked before the fix for bug 560392 continue to work).

Can you see if you get both the line numbers here at org.eclipse.jdt.internal.debug.core.model.JDIStackFrame.setLambdaVariableNames(IJavaValue, ObjectReference)

			try {
				allLineLocations = getUnderlyingMethod().allLineLocations();
				int lineNo = allLineLocations.get(0).lineNumber();
				cu.accept(new LambdaASTVisitor(false, underlyingThisObject, getUnderlyingMethod().isStatic(), cu, lineNo));
			} catch (AbsentInformationException e) {
				e.printStackTrace();
			}

Because we only get the first element.
Comment 43 Gayan Perera CLA 2020-04-15 03:13:21 EDT
@Simeon have you check about my previous comment ? By the way do you keep both line number information dsl and java both ?
Comment 44 Christian Dietrich CLA 2020-04-15 03:31:03 EDT
As Sebastian said:
In Xtend:

you can decide which mode you want to have
- SMAP (whichb basically keeps the Java Lines and adds additional information about source)
- replacing the java lines with dsl lines

- in our DSL we do the second
Comment 45 Gayan Perera CLA 2020-04-15 04:00:44 EDT
@Christian should we try to keep both for this kind of resolutions ? Because there might be a need to read some variables in inspections which lambda fields in compiled lambda classes
Comment 46 Christian Dietrich CLA 2020-04-15 04:10:38 EDT
if i understand JSR-45 correct both are valid ways and should be supported
Comment 47 Simeon Andreev CLA 2020-04-15 04:59:06 EDT
(In reply to Gayan Perera from comment #42)
> [..]
> Can you see if you get both the line numbers here at
> org.eclipse.jdt.internal.debug.core.model.JDIStackFrame.
> setLambdaVariableNames(IJavaValue, ObjectReference)
> 
> 			try {
> 				allLineLocations = getUnderlyingMethod().allLineLocations();
> 				int lineNo = allLineLocations.get(0).lineNumber();
> 				cu.accept(new LambdaASTVisitor(false, underlyingThisObject,
> getUnderlyingMethod().isStatic(), cu, lineNo));
> 			} catch (AbsentInformationException e) {
> 				e.printStackTrace();
> 			}
> 
> Because we only get the first element.

Here are the locations I see when debugging:

[sourcename: ExampleMain.flow, line: 17, method: lambda$0, sourcename: ExampleMain.flow, line: 18, method: lambda$0]
Comment 48 Gayan Perera CLA 2020-04-15 06:50:30 EDT
@Sarika if we add multiple line info in class level is there a way we can retrieve that here and read the java line numbers in this kind of a situation to match against the AST ?
Comment 49 Andrey Loskutov CLA 2020-04-15 08:04:01 EDT
(In reply to Gayan Perera from comment #48)
> @Sarika if we add multiple line info in class level is there a way we can
> retrieve that here and read the java line numbers in this kind of a
> situation to match against the AST ?

Not sure what do you mean by that, but as proposed by Sebastian we simply shouldn't try to resolve anything from Java code if the class file says it belongs to another language.
Comment 50 Gayan Perera CLA 2020-04-15 08:15:52 EDT
(In reply to Andrey Loskutov from comment #49)
> (In reply to Gayan Perera from comment #48)
> > @Sarika if we add multiple line info in class level is there a way we can
> > retrieve that here and read the java line numbers in this kind of a
> > situation to match against the AST ?
> 
> Not sure what do you mean by that, but as proposed by Sebastian we simply
> shouldn't try to resolve anything from Java code if the class file says it
> belongs to another language.

My suggestion was whether we can get both line numbers that is java file and flow file. So that we can choose the correct line cordinate to resolve the variables. Otherwise we might missed some variables info for the flow file debug seasion isn’t it ?
Comment 51 Sarika Sinha CLA 2020-04-15 09:44:55 EDT
Class file Line number is maintained by JDT Core, I don't think we can maintain xtext line number as well.
I agree, that we should check for java file and then only use the line number.
Comment 52 Christian Dietrich CLA 2020-04-15 09:46:26 EDT
@simeon.

as said before. we can if we use the smap mode
(this is the default)

so if you create a xtend file with lamba this is basically done
Comment 53 Sarika Sinha CLA 2020-04-15 09:46:54 EDT
(In reply to Simeon Andreev from comment #1)
> Sarika, any idea why we would have a variable named "arg$1" and not "val$it"
> while in the lambda for "_functional_1" (see the snippet in the description)?

arg was used as it was the argument to the lambda method.
Comment 54 Gayan Perera CLA 2020-04-15 09:50:55 EDT
(In reply to Sarika Sinha from comment #51)
> Class file Line number is maintained by JDT Core, I don't think we can
> maintain xtext line number as well.
> I agree, that we should check for java file and then only use the line
> number.

Then ideally the jdt debugger should not be used to debugging other languages right ? Because here we are just using because it just works. 

I think we should have way of using something like source maps to map flow file line number to java line number as a extension so that a jvm language can write such extension 

What do you all think ?
Comment 55 Andrey Loskutov CLA 2020-04-15 10:06:54 EDT
(In reply to Gayan Perera from comment #54)
> (In reply to Sarika Sinha from comment #51)
> > Class file Line number is maintained by JDT Core, I don't think we can
> > maintain xtext line number as well.
> > I agree, that we should check for java file and then only use the line
> > number.
> 
> Then ideally the jdt debugger should not be used to debugging other
> languages right ? Because here we are just using because it just works. 

No, debugger can be used but it should not blindly assume the *source* corresponding to the loaded type is always written in *Java* language. We have various languages running on JVM, all using common *class* format, and as you can see on the screenshot, we can perfectly debug such non-Java languages with Java debugger.

I will push updated patch in a moment.

Note: the loaded class says unfortunately that it is from "Java" stratum, but fortunately the source name is clearly not Java: ExampleMain.flow :-)
Comment 56 Gayan Perera CLA 2020-04-15 10:39:14 EDT
> 
> Note: the loaded class says unfortunately that it is from "Java" stratum,
> but fortunately the source name is clearly not Java: ExampleMain.flow :-)

May be something to fix in xtext ASM modifications ?
Comment 57 Gayan Perera CLA 2020-04-15 10:56:56 EDT
> No, debugger can be used but it should not blindly assume the *source*
> corresponding to the loaded type is always written in *Java* language. We
> have various languages running on JVM, all using common *class* format, and
> as you can see on the screenshot, we can perfectly debug such non-Java
> languages with Java debugger.
>
So that means we will not try resolved lambda variables if the source is not java is it ?
Comment 58 Andrey Loskutov CLA 2020-04-15 11:34:55 EDT
(In reply to Gayan Perera from comment #57)
> So that means we will not try resolved lambda variables if the source is not
> java is it ?

Please check new patch set https://git.eclipse.org/r/#/c/160832/3.
Comment 59 Gayan Perera CLA 2020-04-15 11:40:26 EDT
@Andrey
Looks good on handling the issue at hand, but if we don't resolve the variable names, can those variables be inspected in variables view, on inspections in debugshell when debugging a DSL file ?

@Simeon, @Christian Don't you have such requirments,
Comment 60 Christian Dietrich CLA 2020-04-15 11:46:20 EDT
in general there is the requirement to resolve
Comment 61 Gayan Perera CLA 2020-04-15 11:47:43 EDT
I should have hold my horses :) The answer to my question is in your commit message, So if i understand correctly, XText can provide a JDIStackFrame implementation for flow files right @Andrey ?
Comment 62 Andrey Loskutov CLA 2020-04-15 11:55:30 EDT
(In reply to Gayan Perera from comment #59)
> @Andrey
> Looks good on handling the issue at hand, but if we don't resolve the
> variable names, can those variables be inspected in variables view, on
> inspections in debugshell when debugging a DSL file ?

Sure, they can, but instead of "val$hello" you will see "arg$1" (same as provided by class files). 

Please note: in *Java* debugger we have no clues about captured lambda variable names if the names aren't coming from *Java* source code. Resolving that info is beyond of JDT capabilities.
Comment 63 Simeon Andreev CLA 2020-04-16 10:19:50 EDT
This bug has more impact than I originally assumed. I cannot evaluate expressions in the stack frame from bug 562220:

java.lang.NullPointerException
	at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.lambda$1(CompatibilityPart.java:122)

When I try inspect "reference.getPage()" there is an error "evaluations must contain a valid expression".

In the Error Log I see:

eclipse.buildId=4.16.0.I20200409-0200
java.version=1.8.0_252-ea
java.vendor=Oracle Corporation
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=en_US
Command-line arguments:  -os linux -ws gtk -arch x86_64 -data /home/sandreev/development_workspaces/contributor_workspace/ws/

org.eclipse.debug.core
Warning
Thu Apr 16 16:18:07 CEST 2020
Compile error during code evaluation: Syntax error, insert "... VariableDeclaratorId" to complete FormalParameter

eclipse.buildId=4.16.0.I20200409-0200
java.version=1.8.0_252-ea
java.vendor=Oracle Corporation
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=en_US
Command-line arguments:  -os linux -ws gtk -arch x86_64 -data /home/sandreev/development_workspaces/contributor_workspace/ws/

org.eclipse.debug.core
Warning
Thu Apr 16 16:18:07 CEST 2020
Compile error during code evaluation: Syntax error on token "1", invalid VariableDeclaratorId

This is a pure Java example, unless I'm mistaken. The lambda in question is set on: org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.objectSetHandler

I assume the fix for bug 561542 doesn't work in this case.
Comment 64 Simeon Andreev CLA 2020-04-16 10:22:18 EDT
Created attachment 282476 [details]
Cannot evaluate in member CompatibilityPart.objectSetHandler initialized with a lambda.
Comment 65 Sarika Sinha CLA 2020-04-17 07:59:44 EDT
(In reply to Sarika Sinha from comment #51)
> Class file Line number is maintained by JDT Core, I don't think we can
> maintain xtext line number as well.
> I agree, that we should check for java file and then only use the line
> number.

Line number info can also be obtained from editor, vertical rule information. To understand the JSR 45 debugger support usage, checkout /org.eclipse.jdt.ui.examples.javafamily
Comment 66 Andrey Loskutov CLA 2020-04-17 10:14:35 EDT
(In reply to Sarika Sinha from comment #65)
> (In reply to Sarika Sinha from comment #51)
> > Class file Line number is maintained by JDT Core, I don't think we can
> > maintain xtext line number as well.
> > I agree, that we should check for java file and then only use the line
> > number.
> 
> Line number info can also be obtained from editor, vertical rule
> information. To understand the JSR 45 debugger support usage, checkout
> /org.eclipse.jdt.ui.examples.javafamily

Sarika, we have no bandwidth (and serious interest in JSR 45 support in general) to work on that. In general our DSL based debugging works just fine with the current JDT (we find time to time some bugs here and there, but nothing related to the current problem with JSR 45).

I've added real word test case to the patch that shows the regression from bug 560392 that can be reproduced with plain Java code. The patch fixes this regression and also as a side effect fixes our DSL related regression. I honestly can't spend more time on that, and would like to merge it. Would you accept that?
Comment 67 Sarika Sinha CLA 2020-04-20 02:11:28 EDT
(In reply to Andrey Loskutov from comment #66)
> (In reply to Sarika Sinha from comment #65)
> > (In reply to Sarika Sinha from comment #51)
> > > Class file Line number is maintained by JDT Core, I don't think we can
> > > maintain xtext line number as well.
> > > I agree, that we should check for java file and then only use the line
> > > number.
> > 
> > Line number info can also be obtained from editor, vertical rule
> > information. To understand the JSR 45 debugger support usage, checkout
> > /org.eclipse.jdt.ui.examples.javafamily
> 
> Sarika, we have no bandwidth (and serious interest in JSR 45 support in
> general) to work on that. In general our DSL based debugging works just fine
> with the current JDT (we find time to time some bugs here and there, but
> nothing related to the current problem with JSR 45).
> 
> I've added real word test case to the patch that shows the regression from
> bug 560392 that can be reproduced with plain Java code. The patch fixes this
> regression and also as a side effect fixes our DSL related regression. I
> honestly can't spend more time on that, and would like to merge it. Would
> you accept that?

Yes, I am fine with the current change. Gerrit having issue, so could not put +2 there.

As Gayan had asked I just wanted to put the information so that it can be used to understand the code base.
Comment 68 Andrey Loskutov CLA 2020-04-20 02:21:59 EDT
(In reply to Sarika Sinha from comment #67)
> Yes, I am fine with the current change. Gerrit having issue, so could not
> put +2 there.

Thanks, I see +2 but I can't merge. Do we have a bug for Gerrit issues?
Comment 69 Andrey Loskutov CLA 2020-04-20 03:23:04 EDT
(In reply to Andrey Loskutov from comment #68)
> (In reply to Sarika Sinha from comment #67)
> > Yes, I am fine with the current change. Gerrit having issue, so could not
> > put +2 there.
> 
> Thanks, I see +2 but I can't merge. Do we have a bug for Gerrit issues?

I was able to merge, git commit is https://git.eclipse.org/c/jdt/eclipse.jdt.debug.git/commit/?id=ac9edad720070520bfb97887e88b15145efef9bb .

Gerrit bug is bug 562301.
Comment 70 Andrey Loskutov CLA 2020-04-26 13:53:05 EDT
Verified patch in our product: our tests are green again.