Bug 448473: Support functional expressions in Modular projects

Also: Provide access to non-system classes, so you can use static
fields, static method references, expression method references (like
obj::someMethod) as long as they don't reference "this", creation
references (MyClass::new) and type method references (MyType::myMethod
or MyType::myStaticMethod).

Access to public fields and methods of enclosing instances

Improved error handling for certain edge cases.

Change-Id: Ibbbfa6c81b1a9763be6b76f4dac7860a55d7c1b6
diff --git a/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java b/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java
index 32ef5ca..1a35ce4 100644
--- a/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java
+++ b/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java
@@ -84,43 +84,47 @@
 		assertFunctionalExpression(n -> n - parameter, parameter, 0);
 
 		/* Capture instance fields */
-		/* But not yet on project's types */
-		assertFunctionalExpression(n -> n - publicField, 2, 1);/* SKIP */
+		/* But not yet on enclosing instance's instance fields */
+		assertFunctionalExpression(n -> n - publicField, 2, 1);
+		assertFunctionalExpression(n -> n - this.publicField, 2, 1);
 		assertFunctionalExpression(n -> n - privateField, 4, 2);/* SKIP */
+		assertFunctionalExpression(n -> n - this.privateField, 4, 2);/* SKIP */
 		
 		/* Capture static fields */
-		/* But not yet on project's types */
-		assertFunctionalExpression(n -> n - publicStaticField, 6, 3);/* SKIP */
+		/* But not yet on non-public fields */
+		assertFunctionalExpression(n -> n - publicStaticField, 6, 3);
 		assertFunctionalExpression(n -> n - privateStaticField, 8, 4);/* SKIP */
 
 		/* Evaluate unbound method references */
-		assertFunctionalExpression(FunctionalCaptureTest18::publicMethod, this, 5);/* SKIP */
+		/* But not yet on project's non-public methods */
+		assertFunctionalExpression(FunctionalCaptureTest18::publicMethod, this, 5);
 		assertFunctionalExpression(FunctionalCaptureTest18::privateMethod, this, 6);/* SKIP */
 
 		/* Evaluate instance method references */
 		assertFunctionalExpression("Hello, "::concat, "World", "Hello, World");
-		/* But not yet on project's types */
-		assertFunctionalExpression(this::publicArgMethod, 1, 7);/* SKIP */
+		/* But not yet this-references */
+		assertFunctionalExpression(this::publicArgMethod, 1, 7);
 		assertFunctionalExpression(this::privateArgMethod, 1, 8);/* SKIP */
 
 		/* Evaluate static method references */
 		assertFunctionalExpression(Integer::valueOf, "16", 16);
-		/* But not yet on project's types */
-		assertFunctionalExpression(FunctionalCaptureTest18::publicStaticMethod, 17, 9);/* SKIP */
+		assertFunctionalExpression(FunctionalCaptureTest18::publicStaticMethod, 17, 9);
+		/* But not yet on project's non-public methods */
 		assertFunctionalExpression(FunctionalCaptureTest18::privateStaticMethod, 19, 10);/* SKIP */
 
 		/* Capture methods */
 		assertFunctionalExpression(s -> Integer.valueOf(s, 16), "0B", 11);
-		/* But not yet on project's types */
-		assertFunctionalExpression(obj -> obj.publicMethod() + 7, this, 12);/* SKIP */
-		assertFunctionalExpression(obj -> this.privateMethod() + 7, this, 13);/* SKIP */
-		assertFunctionalExpression(obj -> obj.publicMethod() + 9, this, 14);/* SKIP */
+		/* But not yet directlt on the instance */
+		assertFunctionalExpression(obj -> obj.publicMethod() + 7, this, 12);
+		assertFunctionalExpression(obj -> this.publicMethod() + 8, this, 13);
+		assertFunctionalExpression(obj -> publicMethod() + 8, this, 13);
+		assertFunctionalExpression(obj -> obj.privateMethod() + 8, this, 14);/* SKIP */
 		assertFunctionalExpression(obj -> this.privateMethod() + 9, this, 15);/* SKIP */
+		assertFunctionalExpression(obj -> privateMethod() + 9, this, 15);/* SKIP */
 
 		/* Constructor references */
 		assertFunctionalExpression(String::new, new char[] { 'a','b','c' }, "abc");
-		/* But not yet on project's types */
-		assertFunctionalExpression(FunctionalCaptureTest18::new, 42, new FunctionalCaptureTest18(42));/* SKIP */
+		assertFunctionalExpression(FunctionalCaptureTest18::new, 42, new FunctionalCaptureTest18(42));
 
 		/* END OF TESTS */
 		System.out.println("OK");
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.java
index b7b034b..bee3ae2 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.java
@@ -28,8 +28,8 @@
 	public static String LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__6;
 	public static String LocalEvaluationEngine_Evaluation_failed___unable_to_initialize___this___context__5;
 	public static String LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__4;
-	public static String RemoteEvaluationEngine_Evaluation_failed___unable_to_load_in_modular_project;
-
+	public static String RemoteEvaluationEngine_Evaluation_failed___unable_to_instantiate_snippet_class;
+	public static String RemoteEvaluationEngine_Evaluation_failed___unable_to_find_injected_class;
 	static {
 		// load message values from bundle file
 		NLS.initializeMessages(BUNDLE_NAME, EvaluationMessages.class);
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.properties b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.properties
index d9cac7c..b970e07 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.properties
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.properties
@@ -22,4 +22,5 @@
 LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__6=Evaluation failed - unable to initialize local variables.
 LocalEvaluationEngine_Evaluation_failed___unable_to_initialize___this___context__5=Evaluation failed - unable to initialize \'this\' context.
 LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__4=Evaluation failed - unable to initialize local variables.
-RemoteEvaluationEngine_Evaluation_failed___unable_to_load_in_modular_project=Evaluation failed - lambda expression evaluation is not yet supported in modular projects.
\ No newline at end of file
+RemoteEvaluationEngine_Evaluation_failed___unable_to_instantiate_snippet_class=Evaluation failed - generated class could not be instantiated.
+RemoteEvaluationEngine_Evaluation_failed___unable_to_find_injected_class=Evaluation failed - unable to find injected class
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java
index af86d6d..2fa84cf 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java
@@ -1,8 +1,10 @@
 package org.eclipse.jdt.internal.debug.eval;
 
+import org.eclipse.jdt.core.dom.ITypeBinding;
 import org.eclipse.jdt.core.dom.IVariableBinding;
 
 public interface ExpressionBinder {
 	// Record and register the binding specified
-	void bind(IVariableBinding bind, String asVariableName);
+	void bind(IVariableBinding variableToBind, String asVariableName);
+	void bindThis(ITypeBinding thisForType, String asVariableName);
 }
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java
index 9d6de6e..c19fab8 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java
@@ -15,9 +15,8 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.eval;
 
-import static org.eclipse.jdt.internal.eval.EvaluationConstants.LOCAL_VAR_PREFIX;
+import static org.eclipse.jdt.core.eval.ICodeSnippetRequestor.LOCAL_VAR_PREFIX;
 
-import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -32,7 +31,6 @@
 import org.eclipse.jdt.debug.core.IJavaClassObject;
 import org.eclipse.jdt.debug.core.IJavaClassType;
 import org.eclipse.jdt.debug.core.IJavaDebugTarget;
-import org.eclipse.jdt.debug.core.IJavaFieldVariable;
 import org.eclipse.jdt.debug.core.IJavaObject;
 import org.eclipse.jdt.debug.core.IJavaReferenceType;
 import org.eclipse.jdt.debug.core.IJavaThread;
@@ -61,16 +59,20 @@
 
 	private IJavaClassObject loadedClass = null;
 
+	private String enclosingTypeName;
+
 	/**
 	 * Constructs a new evaluation engine for the given VM in the context of the specified project. Class files required for the evaluation will be
 	 * deployed to the specified directory (which must be on the class path of the VM in order for evaluation to work).
 	 *
+	 * @param classFiles
 	 * @param codeSnippetClassName
-	 * @param classFiles2
 	 * @param variableNames
+	 * @param enclosingTypeName
 	 */
-	public RemoteEvaluator(LinkedHashMap<String, byte[]> classFiles, String codeSnippetClassName, List<String> variableNames) {
+	public RemoteEvaluator(LinkedHashMap<String, byte[]> classFiles, String codeSnippetClassName, List<String> variableNames, String enclosingTypeName) {
 		this.classFiles = classFiles;
+		this.enclosingTypeName = enclosingTypeName;
 		this.codeSnippetClassName = codeSnippetClassName.replace('.', '/');
 		this.variableNames = variableNames;
 	}
@@ -80,32 +82,12 @@
 		if (loadedClass != null) {
 			return loadedClass;
 		}
-
 		JDIDebugTarget debugTarget = ((JDIDebugTarget) theThread.getDebugTarget());
-		IJavaClassType unsafeClass;
-		try {
-			unsafeClass = (IJavaClassType) findType("sun.misc.Unsafe", debugTarget); //$NON-NLS-1$
-		} catch (DebugException ex) {
-			throw new DebugException(new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(), DebugException.REQUEST_FAILED, EvaluationMessages.RemoteEvaluationEngine_Evaluation_failed___unable_to_load_in_modular_project,
-					null));
-		}
-
-		// IJavaValue[] getDeclaredFieldArgs = new IJavaValue[] { getDebugTarget().newValue("theUnsafe") }; //$NON-NLS-1$
-		IJavaFieldVariable theField = unsafeClass.getField("theUnsafe"); //$NON-NLS-1$
-		IJavaObject theUnsafe = (IJavaObject) theField.getValue();
-
-		// IJavaValue[] setAccessibleArgs = new IJavaValue[] { getDebugTarget().newValue(true) };
-		// theField.sendMessage(
-		// "setAccessible", "(Z)V", setAccessibleArgs, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
-
-		// IJavaValue[] getArgs = new IJavaValue[] { getDebugTarget().newValue(null) };
-		// IJavaObject theUnsafe = (IJavaObject) theField
-		// .sendMessage(
-		// "get", "()Ljava/lang/Object;", getArgs, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
-
 		IJavaClassObject theMainClass = null;
+		IJavaObject classloader = null;
 
-		IJavaReferenceType byteArrayType = findType("byte[]", debugTarget);//$NON-NLS-1$
+		IJavaReferenceType surroundingClass = findType(this.enclosingTypeName, debugTarget);
+		classloader = surroundingClass.getClassLoaderObject();
 
 		for (Map.Entry<String, byte[]> entry : classFiles.entrySet()) {
 			String className = entry.getKey();
@@ -116,21 +98,16 @@
 					theMainClass = existingClass.getClassObject();
 				}
 			} else {
-				byte[] classBytes = entry.getValue();
-				IJavaArray byteArray = ((IJavaArrayType) byteArrayType).newInstance(classBytes.length);
-
-				IJavaValue[] debugClassBytes = new IJavaValue[classBytes.length];
-				for (int ix = 0; ix < classBytes.length; ++ix) {
-					debugClassBytes[ix] = ((JDIDebugTarget) theThread.getDebugTarget()).newValue(classBytes[ix]);
-				}
-				byteArray.setValues(debugClassBytes);
-				IJavaValue[] defineClassArgs = new IJavaValue[] {
-						debugTarget.newValue(className),
+				IJavaArray byteArray = createClassBytes(theThread, debugTarget, entry);
+				IJavaValue[] defineClassArgs = new IJavaValue[] { // args for defineClass
+						debugTarget.newValue(className.replaceAll("/", ".")), // class name //$NON-NLS-1$ //$NON-NLS-2$
 						byteArray, // classBytes,
-						debugTarget.newValue(0), debugTarget.newValue(classBytes.length), debugTarget.nullValue(), // classloader
+						debugTarget.newValue(0), // offset
+						debugTarget.newValue(entry.getValue().length), // length
 						debugTarget.nullValue() // protection domain
 				};
-				IJavaClassObject theClass = (IJavaClassObject) theUnsafe.sendMessage("defineClass", "(Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;", defineClassArgs, theThread, false); //$NON-NLS-1$//$NON-NLS-2$
+
+				IJavaClassObject theClass = (IJavaClassObject) classloader.sendMessage("defineClass", "(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;", defineClassArgs, theThread, false); //$NON-NLS-1$//$NON-NLS-2$
 				if (codeSnippetClassName.equals(className)) {
 					theMainClass = theClass;
 				}
@@ -139,6 +116,19 @@
 		return theMainClass;
 	}
 
+	private IJavaArray createClassBytes(IJavaThread theThread, JDIDebugTarget debugTarget, Map.Entry<String, byte[]> entry) throws DebugException {
+		IJavaReferenceType byteArrayType = findType("byte[]", debugTarget);//$NON-NLS-1$
+		byte[] classBytes = entry.getValue();
+		IJavaArray byteArray = ((IJavaArrayType) byteArrayType).newInstance(classBytes.length);
+
+		IJavaValue[] debugClassBytes = new IJavaValue[classBytes.length];
+		for (int ix = 0; ix < classBytes.length; ++ix) {
+			debugClassBytes[ix] = ((JDIDebugTarget) theThread.getDebugTarget()).newValue(classBytes[ix]);
+		}
+		byteArray.setValues(debugClassBytes);
+		return byteArray;
+	}
+
 	private IJavaReferenceType findType(String typeName, IJavaDebugTarget debugTarget) throws DebugException {
 		IJavaReferenceType theClass = tryLoadType(typeName, debugTarget);
 		if (theClass == null) {
@@ -148,7 +138,7 @@
 							IStatus.ERROR,
 							JDIDebugModel.getPluginIdentifier(),
 							DebugException.REQUEST_FAILED,
-							EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_instantiate_code_snippet_class__11,
+							EvaluationMessages.RemoteEvaluationEngine_Evaluation_failed___unable_to_find_injected_class,
 							null));
 		}
 		return theClass;
@@ -312,61 +302,6 @@
 	}
 
 	/**
-	 * Returns a copy of the type name with '$' replaced by '.', or returns
-	 * <code>null</code> if the given type name refers to an anonymous inner
-	 * class.
-	 *
-	 * @param typeName
-	 *            a fully qualified type name
-	 * @return a copy of the type name with '$' replaced by '.', or returns
-	 *         <code>null</code> if the given type name refers to an anonymous
-	 *         inner class.
-	 */
-	protected String getTranslatedTypeName(String typeName) {
-		int index = typeName.lastIndexOf('$');
-		if (index == -1) {
-			return typeName;
-		}
-		if (index + 1 > typeName.length()) {
-			// invalid name
-			return typeName;
-		}
-		String last = typeName.substring(index + 1);
-		try {
-			Integer.parseInt(last);
-			return null;
-		} catch (NumberFormatException e) {
-			return typeName.replace('$', '.');
-		}
-	}
-
-	/**
-	 * Returns an array of simple type names that are part of the given type's
-	 * qualified name. For example, if the given name is <code>x.y.A$B</code>,
-	 * an array with <code>["A", "B"]</code> is returned.
-	 *
-	 * @param typeName
-	 *            fully qualified type name
-	 * @return array of nested type names
-	 */
-	protected String[] getNestedTypeNames(String typeName) {
-		int index = typeName.lastIndexOf('.');
-		if (index >= 0) {
-			typeName = typeName.substring(index + 1);
-		}
-		index = typeName.indexOf('$');
-		ArrayList<String> list = new ArrayList<>(1);
-		while (index >= 0) {
-			list.add(typeName.substring(0, index));
-			typeName = typeName.substring(index + 1);
-			index = typeName.indexOf('$');
-		}
-		list.add(typeName);
-		return list.toArray(new String[list.size()]);
-	}
-
-	/**
-	/**
 	 * Returns the name of the code snippet to instantiate to run the current
 	 * evaluation.
 	 *
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 acc9761..03a05fa 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
@@ -13,7 +13,7 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.eval;
 
-import static org.eclipse.jdt.internal.eval.EvaluationConstants.LOCAL_VAR_PREFIX;
+import static org.eclipse.jdt.core.eval.ICodeSnippetRequestor.LOCAL_VAR_PREFIX;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -24,11 +24,17 @@
 import java.util.Map;
 
 import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.dom.*;
 import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
+import org.eclipse.jdt.core.eval.IEvaluationContext;
+import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
+import org.eclipse.jdt.internal.debug.eval.ast.engine.EvaluationEngineMessages;
 
 /**
  * A builder for a reuseable expression evaluator against a runnng VM.
@@ -39,11 +45,12 @@
 
 	private IJavaProject javaProject;
 	private ExpressionBinder binder;
-	private String enclosingTypeName;
-	private boolean isStatic;
-	private boolean isConstructor;
-	private List<String> argumentNames = new ArrayList<>();
-	private List<String> argumentTypeNames = new ArrayList<>();
+	private final String enclosingTypeName;
+	private final String packageName;
+	private final boolean isStatic;
+	private final boolean isConstructor;
+	private final List<String> argumentNames = new ArrayList<>();
+	private final List<String> argumentTypeNames = new ArrayList<>();
 
 	/**
 	 * The names and bytecodes of the code snippet class to instantiate
@@ -55,11 +62,14 @@
 	 */
 	private String codeSnippetClassName = null;
 	private String snippet = null;
+	private final ITypeBinding enclosingClass;
 
-	public RemoteEvaluatorBuilder(IJavaProject javaProject, ExpressionBinder binder, String enclosingTypeName, boolean isStatic, boolean isConstructor) {
+	public RemoteEvaluatorBuilder(IJavaProject javaProject, ExpressionBinder binder, ITypeBinding enclosingClass, boolean isStatic, boolean isConstructor) {
 		this.javaProject = javaProject;
 		this.binder = binder;
-		this.enclosingTypeName = enclosingTypeName;
+		this.enclosingClass = enclosingClass;
+		this.enclosingTypeName = enclosingClass.getQualifiedName();
+		this.packageName = enclosingClass.getPackage().getName();
 		this.isStatic = isStatic;
 		this.isConstructor = isConstructor;
 	}
@@ -85,7 +95,7 @@
 
 	private static Object EVALUATE_CODE_SNIPPET_LOCK = new Object();
 
-	public RemoteEvaluator build() throws JavaModelException {
+	public RemoteEvaluator build() throws JavaModelException, DebugException {
 
 		List<String> boundVariableNames = getVariableNames();
 		List<String> boundVariableTypeNames = getVariableTypeNames();
@@ -93,12 +103,23 @@
 		List<String> errors = new ArrayList<>();
 		IType enclosingType = this.javaProject.findType(enclosingTypeName);
 
+		if (enclosingType == null) {
+			throw new DebugException(new Status(IStatus.ERROR, JDIDebugPlugin.getUniqueIdentifier(), EvaluationEngineMessages.ASTInstructionCompiler_Functional_expressions_cannot_be_evaluated_inside_local_and_or_anonymous_classes));
+		}
+
 		synchronized (EVALUATE_CODE_SNIPPET_LOCK) {
-			this.javaProject.newEvaluationContext().evaluateCodeSnippet(this.snippet, boundVariableTypeNames.toArray(new String[boundVariableNames.size()]), boundVariableNames.toArray(new String[boundVariableNames.size()]), new int[boundVariableNames.size()], enclosingType, isStatic, isConstructor, new ICodeSnippetRequestor() {
+			IEvaluationContext context = this.javaProject.newEvaluationContext();
+			if (!packageName.startsWith("java.")) { //$NON-NLS-1$
+				context.setPackageName(this.packageName);
+			}
+			// System.out.println(this.snippet);
+			context.evaluateCodeSnippet(this.snippet, boundVariableTypeNames.toArray(new String[boundVariableNames.size()]), boundVariableNames.toArray(new String[boundVariableNames.size()]), new int[boundVariableNames.size()], enclosingType, isStatic, isConstructor, new ICodeSnippetRequestor() {
 
 				@Override
 				public void acceptProblem(IMarker problemMarker, String fragmentSource, int fragmentKind) {
-					errors.add(problemMarker.toString());
+					if (problemMarker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) >= IMarker.SEVERITY_ERROR) {
+						errors.add(problemMarker.toString());
+					}
 				}
 
 				@Override
@@ -116,12 +137,10 @@
 		}
 
 		if (!errors.isEmpty()) {
-			//LE//$NON-NLS-1$
-			//LE//$NON-NLS-1$
-			throw new RuntimeException(errors.toString());
+			throw new DebugException(new Status(IStatus.ERROR, JDIDebugPlugin.getUniqueIdentifier(), errors.toString()));
 		}
 
-		return new RemoteEvaluator(classFiles, codeSnippetClassName, getVariableNames());
+		return new RemoteEvaluator(classFiles, codeSnippetClassName, getVariableNames(), enclosingType.getFullyQualifiedName('$'));
 	}
 
 	private void setCodeSnippetClassName(String codeSnippetClassName) {
@@ -147,10 +166,10 @@
 		return Collections.unmodifiableList(argumentNames);
 	}
 
-	public String allocateNewVariable(IVariableBinding binding) {
-		String varName = "bound$" + argumentNames.size(); //$NON-NLS-1$
+	public String allocateNewVariable(ITypeBinding binding, String hint) {
+		String varName = hint + "$" + argumentNames.size(); //$NON-NLS-1$
 		argumentNames.add(varName);
-		argumentTypeNames.add(binding.getType().getQualifiedName());
+		argumentTypeNames.add(binding.getQualifiedName());
 		return varName;
 	}
 
@@ -217,19 +236,6 @@
 		 */
 		protected StringBuilder buffer = new StringBuilder();
 
-		/**
-		 * Returns the string accumulated in the visit.
-		 *
-		 * @return the serialized
-		 */
-		// public String getResult() {
-		// return buffer.toString();
-		// }
-
-		// public Map<String, ITypeBinding> getCaptures() {
-		// return Collections.emptyMap();
-		// }
-
 		private int indent = 2;
 
 		private Map<IBinding, String> localBindings = new HashMap<>();
@@ -597,9 +603,9 @@
 
 		@Override
 		public boolean visit(CastExpression node) {
-			buffer.append("(");//$NON-NLS-1$
+			//buffer.append("(");//$NON-NLS-1$
 			node.getType().accept(this);
-			buffer.append(")");//$NON-NLS-1$
+			//buffer.append(")");//$NON-NLS-1$
 			node.getExpression().accept(this);
 			return false;
 		}
@@ -869,6 +875,14 @@
 
 		@Override
 		public boolean visit(FieldAccess node) {
+			/* TODO: Make tricks here when we access fields where we have non-public access */
+			ITypeBinding instanceType = node.getExpression().resolveTypeBinding();
+			if (instanceType.isAssignmentCompatible(RemoteEvaluatorBuilder.this.enclosingClass)) {
+				node.getExpression().accept(this);
+				buffer.append(".");//$NON-NLS-1$ */
+				buffer.append(node.getName().getIdentifier());
+				return false;
+			}
 			node.getExpression().accept(this);
 			buffer.append(".");//$NON-NLS-1$
 			node.getName().accept(this);
@@ -1197,6 +1211,12 @@
 			if (node.getExpression() != null) {
 				node.getExpression().accept(this);
 				buffer.append(".");//$NON-NLS-1$
+			} else {
+				String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable(node.resolveMethodBinding().getDeclaringClass(), "this"); //$NON-NLS-1$
+				binder.bindThis(RemoteEvaluatorBuilder.this.enclosingClass, newVarName);
+				// buffer.append("this."); //$NON-NLS-1$
+				buffer.append(newVarName);
+				buffer.append(".");//$NON-NLS-1$
 			}
 			if (node.getAST().apiLevel() >= JLS3) {
 				if (!node.typeArguments().isEmpty()) {
@@ -1469,18 +1489,31 @@
 			if (!isLocalBinding(binding)) {
 				if (binding instanceof IVariableBinding) {
 					IVariableBinding vb = ((IVariableBinding) binding);
-					Object constant = vb.getConstantValue();
-					if (constant != null) {
-						buffer.append(constant);
-						return false;
-					}
-					if (!vb.isField()) {
-						String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable((IVariableBinding) binding);
+					// For future optimization: Check for duplicates, so same value is only bound once
+					if (vb.isField()) {
+						if (Modifier.isStatic(vb.getModifiers())) {
+							ITypeBinding declaringClass = vb.getDeclaringClass();
+							buffer.append(declaringClass.getQualifiedName());
+							buffer.append("."); //$NON-NLS-1$
+							buffer.append(node.getIdentifier());
+
+						} else {
+							// TODO: Fix this to use same method as visit(FieldAccess)
+							ITypeBinding declaringClass = vb.getDeclaringClass();
+							String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable(declaringClass, "this"); //$NON-NLS-1$
+							binder.bindThis(declaringClass, newVarName);
+							// buffer.append("this."); //$NON-NLS-1$
+							buffer.append(newVarName);
+							buffer.append("."); //$NON-NLS-1$
+							buffer.append(node.getIdentifier());
+						}
+					} else {
+						String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable(vb.getType(), node.getIdentifier());
 						binder.bind((IVariableBinding) binding, newVarName);
-						buffer.append("this."); //$NON-NLS-1$
+						// buffer.append("this."); //$NON-NLS-1$
 						buffer.append(newVarName);
-						return false;
 					}
+					return false;
 				}
 			}
 
@@ -1751,11 +1784,12 @@
 
 		@Override
 		public boolean visit(ThisExpression node) {
-			if (node.getQualifier() != null) {
-				node.getQualifier().accept(this);
-				buffer.append(".");//$NON-NLS-1$
-			}
-			buffer.append("this");//$NON-NLS-1$
+			ITypeBinding thisType = node.resolveTypeBinding();
+
+			String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable(thisType, "this"); //$NON-NLS-1$
+			binder.bindThis(thisType, newVarName);
+			// buffer.append("this."); //$NON-NLS-1$
+			buffer.append(newVarName);
 			return false;
 		}
 
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 e332112..d41c6b2 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
@@ -23,6 +23,7 @@
 
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
 import org.eclipse.jdt.core.Flags;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaModelException;
@@ -2161,14 +2162,15 @@
 		if (!isActive()) {
 			return true;
 		}
-		RemoteEvaluatorBuilder builder = makeBuilder(node);
-		builder.acceptMethodReference(node, node.resolveTypeBinding());
 		try {
+			RemoteEvaluatorBuilder builder = makeBuilder(node);
+			builder.acceptMethodReference(node, node.resolveTypeBinding());
 			RemoteEvaluator remoteEvaluator = builder.build();
 			push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
 			storeInstruction();
-		} catch (JavaModelException e) {
+		} catch (JavaModelException | DebugException e) {
 			addErrorMessage(e.getMessage());
+			setHasError(true);
 		}
 		return false;
 	}
@@ -2369,39 +2371,36 @@
 			return true;
 		}
 
-		RemoteEvaluatorBuilder builder = makeBuilder(node);
-		builder.acceptMethodReference(node, node.resolveTypeBinding());
 		try {
+			RemoteEvaluatorBuilder builder = makeBuilder(node);
+			builder.acceptMethodReference(node, node.resolveTypeBinding());
 			RemoteEvaluator remoteEvaluator = builder.build();
 			push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
 			storeInstruction();
-		} catch (JavaModelException e) {
+		} catch (JavaModelException | DebugException e) {
 			addErrorMessage(e.getMessage());
+			setHasError(true);
 		}
 
 		return false;
 	}
 
-	private RemoteEvaluatorBuilder makeBuilder(ASTNode node) {
+	private RemoteEvaluatorBuilder makeBuilder(ASTNode node) throws DebugException {
 		RemoteEvaluatorBuilder builder = new RemoteEvaluatorBuilder(fJavaProject, new ExpressionBinder() {
 			@Override
 			public void bind(IVariableBinding variableBinding, String asVariableName) {
 				String variableId = variableBinding.getName();
-				ITypeBinding declaringTypeBinding = variableBinding.getDeclaringClass();
-				if (variableBinding.isField()) {
-					if (Modifier.isStatic(variableBinding.getModifiers())) {
-						push(new PushStaticFieldVariable(variableId, getTypeName(declaringTypeBinding), fCounter));
-					} else {
-						push(new PushFieldVariable(variableId, getTypeSignature(declaringTypeBinding), fCounter));
-						push(new PushThis(getEnclosingLevel(node, declaringTypeBinding)));
-						storeInstruction();
-					}
-				} else {
-					push(new PushLocalVariable(variableId));
-				}
+				push(new PushLocalVariable(variableId));
 				storeInstruction();
 			}
-		}, getEnclosingClass(node).getQualifiedName(), false, false);
+
+			@Override
+			public void bindThis(ITypeBinding typeBinding, String asVariableName) {
+				push(new PushThis(getEnclosingLevel(node, typeBinding)));
+				storeInstruction();
+			}
+
+		}, getEnclosingClass(node), isStaticContext(node), false);
 		return builder;
 	}
 
@@ -2418,6 +2417,18 @@
 		return null;
 	}
 
+	private boolean isStaticContext(ASTNode node) {
+		while (node != null) {
+			if (node instanceof MethodDeclaration) {
+				return Modifier.isStatic(((MethodDeclaration) node).getModifiers());
+			} else if (node instanceof Initializer) {
+				return Modifier.isStatic(((Initializer) node).getModifiers());
+			}
+			node = node.getParent();
+		}
+		return false;
+	}
+
 	/**
 	 * @see ASTVisitor#visit(ExpressionStatement)
 	 */
@@ -2925,14 +2936,15 @@
 			return true;
 		}
 
-		RemoteEvaluatorBuilder builder = makeBuilder(node);
-		builder.acceptLambda(node, node.resolveTypeBinding());
 		try {
+			RemoteEvaluatorBuilder builder = makeBuilder(node);
+			builder.acceptLambda(node, node.resolveTypeBinding());
 			RemoteEvaluator remoteEvaluator = builder.build();
 			push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
 			storeInstruction();
-		} catch (JavaModelException e) {
+		} catch (JavaModelException | DebugException e) {
 			addErrorMessage(e.getMessage());
+			setHasError(true);
 		}
 		return false;
 	}
@@ -4318,14 +4330,15 @@
 			return true;
 		}
 
-		RemoteEvaluatorBuilder builder = makeBuilder(node);
-		builder.acceptMethodReference(node, node.resolveTypeBinding());
 		try {
+			RemoteEvaluatorBuilder builder = makeBuilder(node);
+			builder.acceptMethodReference(node, node.resolveTypeBinding());
 			RemoteEvaluator remoteEvaluator = builder.build();
 			push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
 			storeInstruction();
-		} catch (JavaModelException e) {
+		} catch (JavaModelException | DebugException e) {
 			addErrorMessage(e.getMessage());
+			setHasError(true);
 		}
 
 		return false;
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 42d9044..138371d 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
@@ -62,6 +62,7 @@
 	public static String ASTInstructionCompiler_Lambda_expressions_cannot_be_used_in_an_evaluation_expression;
 	public static String ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression;
 	public static String ASTInstructionCompiler_Switch_expressions_cannot_be_used_in_an_evaluation_expression;
+	public static String ASTInstructionCompiler_Functional_expressions_cannot_be_evaluated_inside_local_and_or_anonymous_classes;
 
 	static {
 		// load message values from bundle file
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 5f19282..125c9ce 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
@@ -57,4 +57,5 @@
 
 ASTInstructionCompiler_Lambda_expressions_cannot_be_used_in_an_evaluation_expression=Lambda expressions cannot be used in an evaluation expression
 ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression=Reference expressions cannot be used in an evaluation expression
-ASTInstructionCompiler_Switch_expressions_cannot_be_used_in_an_evaluation_expression=Switch expressions cannot be used in an evaluation expression
\ No newline at end of file
+ASTInstructionCompiler_Switch_expressions_cannot_be_used_in_an_evaluation_expression=Switch expressions cannot be used in an evaluation expression
+ASTInstructionCompiler_Functional_expressions_cannot_be_evaluated_inside_local_and_or_anonymous_classes=Functional expressions cannot be evaluated inside local and or anonymous classes
\ No newline at end of file