Bug 562695 - [1.8] Cannot evaluate lambda expressions which include methods inside a private inner class
Summary: [1.8] Cannot evaluate lambda expressions which include methods inside a priva...
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Debug (show other bugs)
Version: 4.15   Edit
Hardware: PC Linux
: P3 normal with 2 votes (vote)
Target Milestone: ---   Edit
Assignee: JDT-Debug-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on:
Blocks:
 
Reported: 2020-05-01 10:43 EDT by Gayan Perera CLA
Modified: 2022-11-25 17:27 EST (History)
5 users (show)

See Also:


Attachments
Stacktrace (8.43 KB, text/rtf)
2020-11-04 00:16 EST, Sarika Sinha CLA
no flags Details
breakpoint (2.89 KB, application/octet-stream)
2020-11-10 14:02 EST, Gayan Perera CLA
no flags Details
temp code patch (3.58 KB, application/octet-stream)
2020-11-12 11:17 EST, Gayan Perera CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Gayan Perera CLA 2020-05-01 10:43:51 EDT
Code Example:

package workbench;

import java.util.Arrays;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.google.common.base.Predicates;

public class ScopeTest {
	
	public static void main(String[] args) {
		(new ScopeTest()).exec();
	}

	private void exec() {
		(new Runner()).run();
	}
	
	class Runner {

		public void run() {
			Arrays.asList("a", "b","c").stream().collect(Collectors.toList());
		}

		private boolean test() {
			return false;
		}
	}
}

Add a debug breakpoint at line "Arrays.asList("a","b","c").stream().collect(Collectors.toList());" and run the program. One the breakpoint is hit, evaluate the following expression in the debugshell.

Arrays.asList("a", "b", "ac").stream().filter(v -> this.test()).collect(java.util.stream.Collectors.toList())

The evaluation will fail with the following error message

Error Message: Missing code implementation in the compiler
Comment 1 Gayan Perera CLA 2020-07-04 05:01:11 EDT
With Eclipse 4.17 i get "type not visible", the reason is the remote code snippet doesn't get compiled due to inner class is a package private. Any suggestion to solve this ?
Comment 2 Gayan Perera CLA 2020-07-06 04:17:15 EDT
Any suggestions how to handle this inner class thing ? Seems like IJ is handling this. So probably we should be able to fix this as well.
Comment 3 Gayan Perera CLA 2020-07-24 07:18:06 EDT
@Sarika any suggestions or can you point to someone who can give some hints.
Comment 4 Jesper Moller CLA 2020-07-24 08:37:31 EDT
(In reply to Gayan Perera from comment #3)
> @Sarika any suggestions or can you point to someone who can give some hints.

To allow this, you’d need to override the behaviour in the snippet compiler.
Comment 5 Gayan Perera CLA 2020-09-13 16:10:47 EDT
(In reply to Jesper Moller from comment #4)
> (In reply to Gayan Perera from comment #3)
> > @Sarika any suggestions or can you point to someone who can give some hints.
> 
> To allow this, you’d need to override the behaviour in the snippet compiler.

do you suggest that we inject the methods and variables in private class into the snippet ? 

Or should we extract a dummy class in the snippet level which has the same signatures and variable so that the snippet gets compiled ?

But not sure if that will work with the debugger engine when the evaluations happens even though these kind of things works in javascript world.
Comment 6 Sarika Sinha CLA 2020-09-17 07:00:38 EDT
(In reply to Gayan Perera from comment #5)
> (In reply to Jesper Moller from comment #4)
> > (In reply to Gayan Perera from comment #3)
> > > @Sarika any suggestions or can you point to someone who can give some hints.
> > 
> > To allow this, you’d need to override the behaviour in the snippet compiler.
> 
> do you suggest that we inject the methods and variables in private class
> into the snippet ? 
> 
> Or should we extract a dummy class in the snippet level which has the same
> signatures and variable so that the snippet gets compiled ?
> 
> But not sure if that will work with the debugger engine when the evaluations
> happens even though these kind of things works in javascript world.

Even in debugger dummy classes are inserted now also to get it compiled by JDT Core. So you can try extracting a dummy class.
Comment 7 Gayan Perera CLA 2020-09-17 08:59:52 EDT
(In reply to Sarika Sinha from comment #6)
> (In reply to Gayan Perera from comment #5)
> > (In reply to Jesper Moller from comment #4)
> > > (In reply to Gayan Perera from comment #3)
> > > > @Sarika any suggestions or can you point to someone who can give some hints.
> > > 
> > > To allow this, you’d need to override the behaviour in the snippet compiler.
> > 
> > do you suggest that we inject the methods and variables in private class
> > into the snippet ? 
> > 
> > Or should we extract a dummy class in the snippet level which has the same
> > signatures and variable so that the snippet gets compiled ?
> > 
> > But not sure if that will work with the debugger engine when the evaluations
> > happens even though these kind of things works in javascript world.
> 
> Even in debugger dummy classes are inserted now also to get it compiled by
> JDT Core. So you can try extracting a dummy class.

If I understand you correctly you are suggesting to extract a Dummy interface with same method signature and use it in snippet to get it compiled by JDT right ?
Comment 8 Jesper Moller CLA 2020-09-17 16:50:06 EDT
(In reply to Gayan Perera from comment #7)
> If I understand you correctly you are suggesting to extract a Dummy
> interface with same method signature and use it in snippet to get it
> compiled by JDT right ?

Yes, something like that, but with a twist.

You see, for added confusion, there are TWO compilations in effect:

The normal JDT Debug snippet compiler recreates the name environment around a snippet by creating the relevant source around it, and inserting a run method for the snippet. This is handled in SourceBasedSourceGenerator (or BinaryBasedSourceGenerator, depending on the origin of the source under the cursor).
This produces a source with empty surroundings and the requested snippet, which is then compiled into an AST and translated by ASTInstructionCompiler into an interpreter which can be executed against a JDI Target. This does not introduce new code into the debuggee, and so has no permissions problems.
This method uses a "normal" JDT Parser to make the AST, in org.eclipse.jdt.internal.debug.eval.ast.engine.ASTEvaluationEngine.parseCompilationUnit(char[], String, IJavaProject, Map<String, String>)

Alongside this, there is the JDT Core "Evaluator" functionality, which is used in some of the other JDT evaluations. JDT Core is used to produce bytecode for a specified snippet, but uses a bunch of specialized Parser and (internal) AST notes, to deal with the scope and context challenges. The central class for this sorcery is org.eclipse.jdt.internal.eval.CodeSnippetParser.

The special handling of the JDT Debug snippet compiler for lambdas use the JDT Evaluator, but since the evaluator uses tricks to deal with the context (instead of recreating the source around the snippet as JDT Debug does). That's why you get these problems. So, to work around the issue in the case, you'll have to review all the classes in org.eclipse.jdt.internal.eval. It's tricky, to say the least.
Comment 9 Gayan Perera CLA 2020-10-28 15:58:47 EDT
I'm giving up on this issue, i tried for two days the find a way forward but out of luck :(. if someone can tell me what needs to be done i would be happy to help out, compilers and parsers is not my strong points.
Comment 10 Sarika Sinha CLA 2020-11-04 00:16:02 EST
Created attachment 284663 [details]
Stacktrace

Attached is the stacktraces where output of "canBeSeenBy" needs to be always true for debug to be able to procced with finding a solution to this.

@Manoj!
Can you look into this and see if somehow Debug can indicate this to Core and core can accordingly ignore "canBeSeenBy".
Comment 11 Gayan Perera CLA 2020-11-06 05:03:09 EST
@Manoj any updates from you on this issue ?
Comment 12 Gayan Perera CLA 2020-11-09 04:50:28 EST
Any updates from anyone @Manoj and @Sarika ?
Comment 13 Manoj N Palat CLA 2020-11-09 22:00:27 EST
(In reply to Sarika Sinha from comment #10)
> Created attachment 284663 [details]
> Stacktrace
> 
> Attached is the stacktraces where output of "canBeSeenBy" needs to be always
> true for debug to be able to procced with finding a solution to this.
> 
> @Manoj!
> Can you look into this and see if somehow Debug can indicate this to Core
> and core can accordingly ignore "canBeSeenBy".

@Sarika: Just a quick check - Is the ST on master or 4.17? How do you reproduce the ST which you have added?

From comment 0 and comment 1 I understand that the observed error messages are different - 

Following the test and the steps given by Gayan,On master, I could reproduce the error Gayan mentioned with an ST : ProblemReporter.attemptToReturnNonVoidExpression(ReturnStatement, TypeBinding) line: 1218	
ReturnStatement.resolve(BlockScope) line: 324	
MethodDeclaration(AbstractMethodDeclaration).resolveStatements() line: 661	
MethodDeclaration.resolveStatements() line: 362	
MethodDeclaration(AbstractMethodDeclaration).resolve(ClassScope) line: 570	
...

which looks different from the ST which you posted.


Further, I do a get an ST similar to what you shared once I put a breakpoint at: 
1432: if (!currentType.canBeSeenBy(this))
with ST
ClassScope(Scope).findField(TypeBinding, char[], InvocationSite, boolean, boolean) line: 1432	
ClassScope(Scope).findField(TypeBinding, char[], InvocationSite, boolean) line: 1382	
MethodScope(Scope).getBinding(char[], int, InvocationSite, boolean) line: 2119	
Argument.bind(MethodScope, TypeBinding, boolean) line: 122	
LambdaExpression.resolveType(BlockScope, boolean) line: 385	
..
However, I don't see canBeSeen() returning false for any of the fields.
If that is the case, the api which you have asked would not make a difference.

maybe I am missing something here in trying to reproduce the exact ST, can you please clarify? [please note that I have taken the latest code on master]
Comment 14 Gayan Perera CLA 2020-11-10 01:20:51 EST
@Manoj i think the error messages should be different. Because there was fixes i did my self in this area i think. 

On the master i now get 3 errors which says ScopeTest.Runner cannot be accessed or something. If you want i can test on latest master and post the error messages. 

The canBeSeen methods fail when compiling the CodeSnippet for RemoteEvaluator which compiles the lambda expression and push it to debugger.
Comment 15 Gayan Perera CLA 2020-11-10 14:01:46 EST
I checked with latest master on both jdt.core and jdt.debug, seems like i don't have local changes. I have some breakpoint which hacks some variables, i have attached the breakpoint to try out.

You need to evaluate the expression 

Arrays.asList("a", "b", "ac").stream().filter(v -> this.test()).collect(java.util.stream.Collectors.toList())

In the debug shell.
Comment 16 Gayan Perera CLA 2020-11-10 14:02:11 EST
Created attachment 284723 [details]
breakpoint
Comment 17 Sarika Sinha CLA 2020-11-12 00:11:32 EST
Code from Comment 0, is not going through that stacktrace. I tried the following code:

import java.lang.Thread.State;
import java.util.Arrays;
import java.util.stream.Collectors;

public class Application {

	public static void main(String[] args) {
		(new Application()).runInner();
	}

	private void runInner() {
		(new InnerPrivate()).print();
	}

	private void foo(State state) {
	}

	class InnerPrivate {

		public void print() {
			Arrays.asList("a", "b","c").stream().collect(Collectors.counting());
		}

		private boolean test() {
			return false;
		}
	}

}

And this goes through canbeSeen and RemoteEvaluatorBuilder. canBeseen methods return true but then it fails with 
	Evaluation failed. Reason(s):
		[Missing code implementation in the compiler]

With the following stack.


Thread [ModalContext] (Suspended (breakpoint at line 6894 in ProblemReporter))	
	owns: Object  (id=15712)	
	ProblemReporter.needImplementation(ASTNode) line: 6894	
	TypeAnnotationCodeStream(CodeStream).generateOuterAccess(Object[], ASTNode, Binding, Scope) line: 2546	
	TypeAnnotationCodeStream(StackMapFrameCodeStream).generateOuterAccess(Object[], ASTNode, Binding, Scope) line: 285	
	CodeSnippetSingleNameReference.generateCode(BlockScope, CodeStream, boolean) line: 299	
	CodeSnippetMessageSend.generateCode(BlockScope, CodeStream, boolean) line: 113	
	LambdaExpression.generateCode(ClassFile) line: 1293	
	LambdaExpression.generateCode(ClassScope, ClassFile) line: 1238	
	CodeSnippetClassFile(ClassFile).addSpecialMethods(TypeDeclaration) line: 1124	
	CodeSnippetTypeDeclaration.generateCode(ClassFile) line: 79	
	CodeSnippetTypeDeclaration(TypeDeclaration).generateCode(CompilationUnitScope) line: 812	
	CompilationUnitDeclaration.generateCode() line: 408	
	CodeSnippetCompiler(Compiler).process(CompilationUnitDeclaration, int) line: 913	
	CodeSnippetCompiler(Compiler).processCompiledUnits(int, boolean) line: 575	
	CodeSnippetCompiler(Compiler).compile(ICompilationUnit[], boolean) line: 475	
	CodeSnippetCompiler(Compiler).compile(ICompilationUnit[]) line: 426	
	CodeSnippetEvaluator(Evaluator).getClasses() line: 135	
	EvaluationContext.evaluate(char[], char[][], char[][], int[], char[], boolean, boolean, INameEnvironment, Map<String,String>, IRequestor, IProblemFactory) line: 305	
	EvaluationContextWrapper.evaluateCodeSnippet(String, String[], String[], int[], IType, boolean, boolean, ICodeSnippetRequestor, IProgressMonitor) line: 234	
	RemoteEvaluatorBuilder.build() line: 121	
	ASTInstructionCompiler.visit(LambdaExpression) line: 2955	
	LambdaExpression.accept0(ASTVisitor) line: 195	
	LambdaExpression(ASTNode).accept(ASTVisitor) line: 3012	
	ASTInstructionCompiler.pushMethodArguments(IMethodBinding, List<Expression>) line: 3156	
	ASTInstructionCompiler.visit(MethodInvocation) line: 3089	
	MethodInvocation.accept0(ASTVisitor) line: 220	
	MethodInvocation(ASTNode).accept(ASTVisitor) line: 3012	
	ASTInstructionCompiler.visit(MethodInvocation) line: 3084	
	MethodInvocation.accept0(ASTVisitor) line: 220	
	MethodInvocation(ASTNode).accept(ASTVisitor) line: 3012	
	ReturnStatement(ASTNode).acceptChild(ASTVisitor, ASTNode) line: 3060	
	ReturnStatement.accept0(ASTVisitor) line: 128	
	ReturnStatement(ASTNode).accept(ASTVisitor) line: 3012	
	Block(ASTNode).acceptChildren(ASTVisitor, ASTNode$NodeList) line: 3083	
	Block.accept0(ASTVisitor) line: 128	
	Block(ASTNode).accept(ASTVisitor) line: 3012	
	MethodDeclaration(ASTNode).acceptChild(ASTVisitor, ASTNode) line: 3060	
	MethodDeclaration.accept0(ASTVisitor) line: 698	
	MethodDeclaration(ASTNode).accept(ASTVisitor) line: 3012	
	TypeDeclaration(ASTNode).acceptChildren(ASTVisitor, ASTNode$NodeList) line: 3083	
	TypeDeclaration.accept0(ASTVisitor) line: 526	
	TypeDeclaration(ASTNode).accept(ASTVisitor) line: 3012	
	TypeDeclaration(ASTNode).acceptChildren(ASTVisitor, ASTNode$NodeList) line: 3083	
	TypeDeclaration.accept0(ASTVisitor) line: 526	
	TypeDeclaration(ASTNode).accept(ASTVisitor) line: 3012	
	CompilationUnit(ASTNode).acceptChildren(ASTVisitor, ASTNode$NodeList) line: 3083	
	CompilationUnit.accept0(ASTVisitor) line: 258	
	CompilationUnit(ASTNode).accept(ASTVisitor) line: 3012	
	ASTEvaluationEngine.createExpressionFromAST(String, EvaluationSourceGenerator, CompilationUnit) line: 654	
	ASTEvaluationEngine.getCompiledExpression(String, IJavaStackFrame) line: 394	
	ASTEvaluationEngine.evaluate(String, IJavaStackFrame, IEvaluationListener, int, boolean) line: 149	
	EvaluateAction$1.run(IProgressMonitor) line: 261	
	ModalContext$ModalContextThread.run() line: 122
Comment 18 Gayan Perera CLA 2020-11-12 01:18:18 EST
@Sarika did you try adding the conditional breakpoints with the new code ? That should resolve the error message you are getting.
Comment 19 Sarika Sinha CLA 2020-11-12 03:41:29 EST
(In reply to Gayan Perera from comment #18)
> @Sarika did you try adding the conditional breakpoints with the new code ?
> That should resolve the error message you are getting.

I have the conditional breakpoint.
It doesn't hit the RemoteEvaluator class itself.
Comment 20 Gayan Perera CLA 2020-11-12 04:16:03 EST
What is the expression you are evaluating?
Comment 21 Sarika Sinha CLA 2020-11-12 05:26:32 EST
(In reply to Gayan Perera from comment #20)
> What is the expression you are evaluating?

same as:
Arrays.asList("a", "b", "ac").stream().filter(v -> this.test()).collect(java.util.stream.Collectors.toList());
Comment 22 Gayan Perera CLA 2020-11-12 06:05:11 EST
Thats strange. I check if something extra happens in my workspace.
Comment 23 Gayan Perera CLA 2020-11-12 11:17:36 EST
Created attachment 284747 [details]
temp code patch

@Sarika please try with the patch that i have done in jdt.core in my workspace. This was created using git diff
Comment 24 Sarika Sinha CLA 2020-11-17 07:55:45 EST
(In reply to Gayan Perera from comment #23)
> Created attachment 284747 [details]
> temp code patch
> 
> @Sarika please try with the patch that i have done in jdt.core in my
> workspace. This was created using git diff

I tried this and now I don't get NPE. I see that 
org.eclipse.jdt.internal.compiler.lookup.Scope.getConstructor0(ReferenceBinding, TypeBinding[], InvocationSite)

calls 

org.eclipse.jdt.internal.compiler.lookup.MethodBinding.canBeSeenBy(InvocationSite, Scope)

and fails at return invocationType.fPackage == this.declaringClass.fPackage;

as invocationType.fPackage = package tt

and this.declaringClass.fPackage = package org.eclipse.jdt.internal.eval.target
Comment 25 Gayan Perera CLA 2020-11-18 05:09:41 EST
@Sarika do you mean that now its working with patches and debug point based modifications ? Try the new example i posted, because the debug point hacks are based on my new code which has Application class.
Comment 26 Sarika Sinha CLA 2020-11-21 14:07:40 EST
(In reply to Gayan Perera from comment #25)
> @Sarika do you mean that now its working with patches and debug point based
> modifications ? Try the new example i posted, because the debug point hacks
> are based on my new code which has Application class.

Busy with RC1 activities. Will try after that.
Comment 27 Sarika Sinha CLA 2020-11-25 01:34:25 EST
(In reply to Gayan Perera from comment #25)
> @Sarika do you mean that now its working with patches and debug point based
> modifications ? Try the new example i posted, because the debug point hacks
> are based on my new code which has Application class.

Yes with JDT Core patch and the "Application" class evaluation works.
Comment 28 Sarika Sinha CLA 2020-11-30 06:48:59 EST
@Gayan,
So can you add the temporary JDT Core patch and then JDT Debug patch solution?

Manoj will look at the JDT Core and improve it but we can proceed with Debug thing for the problem described in Comment 1 ?
Comment 29 Eclipse Genie CLA 2022-11-25 17:27:56 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.