Bug549952 - Add support to evaluate expressions with anonymous classes

Change-Id: I94afc9ffa33f2f3e108671f547f75afc358005db
Signed-off-by: gayanper <gayanper@gmail.com>
diff --git a/org.eclipse.jdt.debug.tests/java8/AnonymousEvaluator.java b/org.eclipse.jdt.debug.tests/java8/AnonymousEvaluator.java
new file mode 100644
index 0000000..0441a38
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/java8/AnonymousEvaluator.java
@@ -0,0 +1,20 @@
+import java.util.function.IntSupplier;
+
+public class AnonymousEvaluator {
+	public static void main(String[] args) {
+		int calc = calculate(new IntSup());
+	}
+
+	public static int calculate(IntSupplier supplier) {
+		return supplier.getAsInt() * 2;
+	}
+
+	private static class IntSup implements IntSupplier {
+
+		@Override
+		public int getAsInt() {
+			return 10 * 20;
+		}
+	}
+
+}
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
index 45a356c..bcce41a 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
@@ -476,6 +476,7 @@
 				cfgs.add(createLaunchConfiguration(jp, "Bug561715"));
 				cfgs.add(createLaunchConfiguration(jp, "Bug562056"));
 				cfgs.add(createLaunchConfiguration(jp, "RemoteEvaluator"));
+				cfgs.add(createLaunchConfiguration(jp, "AnonymousEvaluator"));
 	    		loaded18 = true;
 	    		waitForBuild();
 	        }
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/RemoteEvaluatorTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/RemoteEvaluatorTests.java
index 4c9c93f..cfbf895 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/RemoteEvaluatorTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/RemoteEvaluatorTests.java
@@ -60,6 +60,34 @@
 		assertEquals("count is not 0", "0", value.getValueString());
 	}
 
+	public void testEvaluate_AnonymousClassInstance_WithSemicolon() throws Exception {
+		debugWithBreakpoint("AnonymousEvaluator", 6);
+		IValue value = evaluate("calculate(new IntSupplier() {\n" + 
+				"\n" + 
+				"			@Override\n" + 
+				"			public int getAsInt() {\n" + 
+				"				return 100 * 20;\n" + 
+				"			}\n" + 
+				"		});");
+
+		assertNotNull("result is null", value);
+		assertEquals("count is not 4000", "4000", value.getValueString());
+	}
+
+	public void testEvaluate_AnonymousClassInstance_WithoutSemicolon() throws Exception {
+		debugWithBreakpoint("AnonymousEvaluator", 6);
+		IValue value = evaluate("calculate(new IntSupplier() {\n" + 
+				"\n" + 
+				"			@Override\n" + 
+				"			public int getAsInt() {\n" + 
+				"				return 100 * 20;\n" + 
+				"			}\n" + 
+				"		})");
+
+		assertNotNull("result is null", value);
+		assertEquals("count is not 4000", "4000", value.getValueString());
+	}
+
 	@Override
 	protected IJavaProject getProjectContext() {
 		return get18Project();
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java
index 34d5aa8..e6a77df 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java
@@ -82,6 +82,11 @@
 		acceptFunctionalExpression(node, expectedResult);
 	}
 
+	public void acceptAnonymousClass(ClassInstanceCreation node, ITypeBinding expectedResult) {
+		acceptFunctionalExpression(node, expectedResult);
+	}
+
+
 	private void acceptFunctionalExpression(Expression node, ITypeBinding expectedResult) {
 		FunctionalEvalVisitor visitor = new FunctionalEvalVisitor();
 		node.accept(visitor);
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTInstructionCompiler.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTInstructionCompiler.java
index 7cf7a2b..5d20aa0 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTInstructionCompiler.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTInstructionCompiler.java
@@ -1548,8 +1548,6 @@
 		if (!isActive()) {
 			return true;
 		}
-		setHasError(true);
-		addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Anonymous_type_declaration_cannot_be_used_in_an_evaluation_expression_2);
 		return false;
 	}
 
@@ -2008,9 +2006,22 @@
 			return true;
 		}
 
+		String rewrittenLocalType = null;
 		if (node.getAnonymousClassDeclaration() != null) {
-			setHasError(true);
-			addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Anonymous_type_declaration_cannot_be_used_in_an_evaluation_expression_7);
+			try {
+				RemoteEvaluatorBuilder builder = makeBuilder(node);
+				builder.acceptAnonymousClass(node, node.getType().resolveBinding());
+				RemoteEvaluator remoteEvaluator = builder.build();
+				push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
+
+				push(new PushType(getTypeName(node.getType().resolveBinding())));
+				storeInstruction();
+				return false;
+
+			} catch (JavaModelException | DebugException e) {
+				addErrorMessage(e.getMessage());
+				setHasError(true);
+			}
 		}
 
 		IMethodBinding methodBinding = node.resolveConstructorBinding();
@@ -2026,10 +2037,7 @@
 		boolean isInstanceMemberType = typeBinding.isMember()
 				&& !Modifier.isStatic(typeBinding.getModifiers());
 
-		if (isALocalType(typeBinding)) {
-			setHasError(true);
-			addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Constructor_of_a_local_type_cannot_be_used_in_an_evaluation_expression_8);
-		}
+		boolean isLocalType = isALocalType(typeBinding);
 
 		if (containsALocalType(methodBinding)) {
 			setHasError(true);
@@ -2057,12 +2065,18 @@
 			paramCount++;
 		}
 
-		String signature = getMethodSignature(methodBinding,
-				enclosingTypeSignature).replace('.', '/');
+		String signature = null;
+		if(isLocalType) {
+			String methodSignature = getMethodSignature(methodBinding, null);
+			signature = methodSignature.replace(methodBinding.getReturnType().getQualifiedName(), rewrittenLocalType).replace('.', '/');
+		} else {
+			signature = getMethodSignature(methodBinding,
+					enclosingTypeSignature).replace('.', '/');
+		}
 
 		push(new Constructor(signature, paramCount, fCounter));
 
-		push(new PushType(getTypeName(typeBinding)));
+		push(new PushType(isLocalType ? rewrittenLocalType : getTypeName(typeBinding)));
 		storeInstruction();
 
 		if (isInstanceMemberType) {
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.java
index 138371d..ae75890 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.java
@@ -19,7 +19,6 @@
 public class EvaluationEngineMessages extends NLS {
 	private static final String BUNDLE_NAME = "org.eclipse.jdt.internal.debug.eval.ast.engine.EvaluationEngineMessages";//$NON-NLS-1$
 
-	public static String ASTInstructionCompiler_Anonymous_type_declaration_cannot_be_used_in_an_evaluation_expression_2;
 	public static String ASTInstructionCompiler_Assert_statement_cannot_be_used_in_an_evaluation_expression_3;
 	public static String ASTInstructionCompiler_Unrecognized_assignment_operator____4;
 
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.properties b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.properties
index 125c9ce..61f40cd 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.properties
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.properties
@@ -13,7 +13,6 @@
 # Jesper Steen Moller - 427089: [1.8] Change value in Variables view with lambda or method reference
 ###############################################################################
 
-ASTInstructionCompiler_Anonymous_type_declaration_cannot_be_used_in_an_evaluation_expression_2=Anonymous type declaration cannot be used in an evaluation expression
 ASTInstructionCompiler_Assert_statement_cannot_be_used_in_an_evaluation_expression_3=Assert statement cannot be used in an evaluation expression
 ASTInstructionCompiler_Unrecognized_assignment_operator____4=Unrecognized assignment operator :
 
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
index 3146f70..c733adf 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
@@ -112,7 +112,8 @@
 		int i ;
 		for (i=0; i < chars.length; i++) {
 			// copy everything before the last statement or if whitespace
-			if (i<= semicolonIndex || Character.isWhitespace(chars[i]) || chars[i] == '}'){
+			if (i<= semicolonIndex || Character.isWhitespace(chars[i]) || chars[i] == '}' 
+					|| (i > 0 && (chars[i - 1] == '}' && (chars[i] == ')' || chars[i] == ',')))) {
 				wordBuffer.append(codeSnippet.charAt(i));
 			}
 			else
@@ -131,7 +132,7 @@
 				if (needsReturn(lastSentence))
 					wordBuffer.append(returnString);
 			}
-		} else if (chars[chars.length -1] !='}' && ( i+7 > chars.length || (i + 7 <= chars.length && !codeSnippet.substring(i, i+7).equals(returnString)))){
+		} else if (chars[chars.length -1] !='}' && ( (i+7 > chars.length && lastSentence.length() > 1) || (i + 7 <= chars.length && !codeSnippet.substring(i, i+7).equals(returnString)))){
 			// add return if last statement does not have return
 			if (needsReturn(lastSentence))
 				wordBuffer.append(returnString);
@@ -149,6 +150,11 @@
 			if ( j != -1 && (j!=k))
 				wordBuffer.append(';');
 		}
+		
+		if(lastSentence.length() <= 1 && needsReturn(wordBuffer.toString())) {
+			wordBuffer.insert(0, returnString).append(' ');
+		}
+		
 		return wordBuffer.toString();
 	}
 
@@ -225,8 +231,20 @@
 				else if ( (count == 3 || count == 1 ) && token == ITerminalSymbols.TokenNameIdentifier ){
 					 return false;
 				}
-				else {
-					return true;
+				else if (count == 0 && token == ITerminalSymbols.TokenNamereturn) {
+					return false;
+				} else {
+					// ignore and continue the tokens specified here, handling following types of statements
+					// {return i > 0;}, {/**/};{return true;}
+					switch (token) {
+						case ITerminalSymbols.TokenNameLBRACE:
+						case ITerminalSymbols.TokenNameRBRACE:
+						case ITerminalSymbols.TokenNameSEMICOLON:
+							token = scanner.getNextToken();
+							break;
+						default:
+							return true;
+					}
 				}
 
 			}