Merge remote-tracking branch 'origin/master' into BETA_JAVA14

Change-Id: Iddd0c6592c93f6b3b5cc058971bc3bf95912f89a
diff --git a/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceConsoleTest.java b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceConsoleTest.java
index 924dcda..6e3a667 100644
--- a/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceConsoleTest.java
+++ b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceConsoleTest.java
@@ -175,6 +175,17 @@
 		assertArrayEquals("Expected no hyperlinks for invalid type name", new Position[0], positions);
 	}
 
+	public void testBug489365_unicodeMatch() throws Exception {
+		consoleDocumentWithText("at com.example.Fran\u00E7ais.de\u0301butant(Fran\u00E7ais.java:101)\n" // "Latin Small Letter C with Cedilla"
+				+ "at com.example.Franc\u0327ais.de\u0301butant(Franc\u0327ais.java:101)\n" // "Latin Small Letter C" + "Combining Cedilla"
+				+ "at Exc\u00E4ption.main(Exc\u00E4ption.java:4)\n" // "Latin Small Letter A with Diaeresis"
+				+ "at Exca\u0308ption.main(Exca\u0308ption.java:4)"); // "Latin Small Letter A" + "Combining Diaeresis"
+
+		String[] matchTexts = linkTextsAtPositions(34, 88, 126, 163);
+		assertArrayEquals(allLinks(), new String[] { "Fran\u00E7ais.java:101", "Franc\u0327ais.java:101",
+				"Exc\u00E4ption.java:4", "Exca\u0308ption.java:4" }, matchTexts);
+	}
+
 	/**
 	 * Test save/restore of stack trace console content on console close/reactivation.
 	 */
diff --git a/org.eclipse.jdt.debug.tests/java8/EvalTest18.java b/org.eclipse.jdt.debug.tests/java8/EvalTest18.java
index 02e72ba..b7c82cb 100644
--- a/org.eclipse.jdt.debug.tests/java8/EvalTest18.java
+++ b/org.eclipse.jdt.debug.tests/java8/EvalTest18.java
@@ -19,5 +19,17 @@
 	public static void main(String[] args) {
 		List<String> strings = Arrays.asList("One", "Two", "Three");
 		System.out.println("Count of strings in stream from array =" + strings.stream().count());
+		nextMain();
 	}
+	
+	public static void nextMain() {
+		int someInt = 22; List<Integer> l = Arrays.asList(1,2,3,4);
+		
+		System.out.println("Just some int = " + someInt);
+		
+		for (int i : l ) {
+			System.out.println("i = " + i);
+		}
+	}
+	
 }
\ No newline at end of file
diff --git a/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java b/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java
new file mode 100644
index 0000000..1a35ce4
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Jesper Steen Møller and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Jesper Steen Møller - initial API and implementation
+ *******************************************************************************/
+
+import java.util.Objects;
+import java.util.function.Function;
+
+public class FunctionalCaptureTest18 {
+	
+	FunctionalCaptureTest18(int field) {
+		publicField = field;
+	}
+	
+	public static void main(String[] args) {
+		new FunctionalCaptureTest18(1).runAssertions(1);
+	}
+
+	static <I, O> O assertFunctionalExpression(Function<I, O> functional, I input, O expected) {
+		O result = functional.apply(input);
+		if (!Objects.equals(result, expected)) {
+			throw new RuntimeException("Expected " + expected + ", got " + result);
+		}
+		return result;
+	}
+
+	@Override
+	public int hashCode() {
+		return 1992;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj || obj == null)
+			return false;
+		return getClass() == obj.getClass();
+	}
+
+	final public int publicField;
+	private int privateField = 2;
+
+	public static int publicStaticField = 3;
+	private static int privateStaticField = 4;
+
+	public Integer publicMethod() {
+		return 5;
+	}
+	private Integer privateMethod() {
+		return 6;
+	}
+	public int publicArgMethod(int i) {
+		return i + 6;
+	}
+	private int privateArgMethod(int i) {
+		return i + 7;
+	}
+	public static int publicStaticMethod(int i) {
+		return i - 8;
+	}
+	private static int privateStaticMethod(int i) {
+		return i - 9;
+	}
+	
+	public void runAssertions(int parameter /* = 1 */) {
+		int localConstI = -3;
+		System.out.println("Go!");
+		/* CHECK EXPRESSIONS BELOW */
+		/* Nothing captured */
+		assertFunctionalExpression(n -> -3, 42, -3);
+		assertFunctionalExpression(n -> n / 2 - 7, 10, -2);
+
+		/* Capture locals */
+		/* But not yet on project's types */
+		assertFunctionalExpression(n -> n + localConstI, 2, -1);
+		assertFunctionalExpression(n -> n - parameter, parameter, 0);
+
+		/* Capture instance fields */
+		/* 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 non-public fields */
+		assertFunctionalExpression(n -> n - publicStaticField, 6, 3);
+		assertFunctionalExpression(n -> n - privateStaticField, 8, 4);/* SKIP */
+
+		/* Evaluate unbound method references */
+		/* 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 this-references */
+		assertFunctionalExpression(this::publicArgMethod, 1, 7);
+		assertFunctionalExpression(this::privateArgMethod, 1, 8);/* SKIP */
+
+		/* Evaluate static method references */
+		assertFunctionalExpression(Integer::valueOf, "16", 16);
+		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 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");
+		assertFunctionalExpression(FunctionalCaptureTest18::new, 42, new FunctionalCaptureTest18(42));
+
+		/* END OF TESTS */
+		System.out.println("OK");
+	}
+}
diff --git a/org.eclipse.jdt.debug.tests/testprograms/ErrorRecurrence.java b/org.eclipse.jdt.debug.tests/testprograms/ErrorRecurrence.java
new file mode 100644
index 0000000..916db7c
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/testprograms/ErrorRecurrence.java
@@ -0,0 +1,28 @@
+
+public class ErrorRecurrence {
+
+	public static void main(String[] args) {
+		m0(); // L5
+	}
+	static void m0() {
+		try {
+			m1();
+		} finally {
+			System.out.println("finally");
+		} // L12
+	}
+
+	static void m1() {
+		try {
+			m2();
+		} catch (Error e) {
+			System.out.println("caught");
+			throw e; // L20
+		}
+	}
+
+	static void m2() {
+		System.out.println("before throw");
+		throw new Error(); // L26
+	}
+}
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 9a3e826..9aa6ac1 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
@@ -169,6 +169,7 @@
 import org.osgi.service.prefs.BackingStoreException;
 
 import com.sun.jdi.InternalException;
+import com.sun.jdi.InvocationException;
 
 import junit.framework.TestCase;
 
@@ -204,7 +205,8 @@
 			"org.eclipse.debug.tests.targets.HcrClass5", "org.eclipse.debug.tests.targets.HcrClass6", "org.eclipse.debug.tests.targets.HcrClass7", "org.eclipse.debug.tests.targets.HcrClass8",
 			"org.eclipse.debug.tests.targets.HcrClass9", "TestContributedStepFilterClass", "TerminateAll_01", "TerminateAll_02", "StepResult1",
 			"StepResult2", "StepResult3", "StepUncaught", "TriggerPoint_01", "BulkThreadCreationTest", "MethodExitAndException",
-			"Bug534319earlyStart", "Bug534319lateStart", "Bug534319singleThread", "Bug534319startBetwen", "MethodCall", "Bug538303", "Bug540243", "OutSync", "OutSync2", "ConsoleOutputUmlaut" };
+			"Bug534319earlyStart", "Bug534319lateStart", "Bug534319singleThread", "Bug534319startBetwen", "MethodCall", "Bug538303", "Bug540243",
+			"OutSync", "OutSync2", "ConsoleOutputUmlaut", "ErrorRecurrence" };
 
 	/**
 	 * the default timeout
@@ -456,6 +458,7 @@
 	        if (!loaded18) {
 	        	jp = createProject(ONE_EIGHT_PROJECT_NAME, JavaProjectHelper.TEST_1_8_SRC_DIR.toString(), JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false);
 	    		cfgs.add(createLaunchConfiguration(jp, "EvalTest18"));
+	    		cfgs.add(createLaunchConfiguration(jp, "FunctionalCaptureTest18"));
 	    		cfgs.add(createLaunchConfiguration(jp, "EvalTestIntf18"));
 				cfgs.add(createLaunchConfiguration(jp, "EvalIntfSuperDefault"));
 				cfgs.add(createLaunchConfiguration(jp, "DebugHoverTest18"));
@@ -2845,10 +2848,10 @@
 			}
 			IEvaluationResult result = listener.getResult();
 			assertNotNull("The evaluation should have result: ", result);
-			assertNull("The evaluation should not have exception : " + result.getException(), result.getException());
+			assertNull("Evaluation of '" + snippet + "' should not have exception : " + findCause(result.getException()), result.getException());
 
 			String firstError = result.hasErrors() ? result.getErrorMessages()[0] : "";
-			assertFalse("The evaluation should not have errors : " + firstError, result.hasErrors());
+			assertFalse("The evaluation of '\" + snippet + \"'  should not have errors : " + firstError, result.hasErrors());
 			return listener.getResult().getValue();
 		}
 		finally {
@@ -2856,6 +2859,17 @@
 		}
 	}
 
+	private static Object findCause(DebugException problem) {
+		if (problem == null) {
+			return null;
+		}
+		Throwable cause = problem.getCause();
+		if (cause instanceof InvocationException) {
+			return ((InvocationException)cause).exception().toString();
+		}
+		return cause;
+	}
+
 	/**
 	 * @return true if the UI event loop should be proicessed during wait operations on UI thread
 	 */
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
index 8ed5ac9..8b73956 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -39,6 +39,7 @@
 import org.eclipse.jdt.debug.tests.breakpoints.PatternBreakpointTests;
 import org.eclipse.jdt.debug.tests.breakpoints.PreLaunchBreakpointTest;
 import org.eclipse.jdt.debug.tests.breakpoints.RunToLineTests;
+import org.eclipse.jdt.debug.tests.breakpoints.SpecialExceptionBreakpointTests;
 import org.eclipse.jdt.debug.tests.breakpoints.SuspendVMBreakpointsTests;
 import org.eclipse.jdt.debug.tests.breakpoints.TargetPatternBreakpointTests;
 import org.eclipse.jdt.debug.tests.breakpoints.TestToggleBreakpointsTarget;
@@ -343,6 +344,7 @@
 		addTest(new TestSuite(BreakpointWorkingSetTests.class));
 		addTest(new TestSuite(MethodBreakpointTests.class));
 		addTest(new TestSuite(ExceptionBreakpointTests.class));
+		addTest(new TestSuite(SpecialExceptionBreakpointTests.class));
 		addTest(new TestSuite(WatchpointTests.class));
 		addTest(new TestSuite(PatternBreakpointTests.class));
 		addTest(new TestSuite(TargetPatternBreakpointTests.class));
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/SpecialExceptionBreakpointTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/SpecialExceptionBreakpointTests.java
new file mode 100644
index 0000000..dfdcc5a
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/breakpoints/SpecialExceptionBreakpointTests.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2020 GK Software SE and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Stephan Herrmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.debug.tests.breakpoints;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
+import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint.SuspendOnRecurrenceStrategy;
+import org.eclipse.jdt.debug.core.IJavaStackFrame;
+import org.eclipse.jdt.debug.core.IJavaThread;
+import org.eclipse.jdt.debug.core.JDIDebugModel;
+import org.eclipse.jdt.debug.tests.AbstractDebugTest;
+import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
+import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants;
+import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
+import org.eclipse.jdt.internal.debug.ui.JavaDebugOptionsManager;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+public class SpecialExceptionBreakpointTests extends AbstractDebugTest {
+
+	private boolean fDefaultSuspendOnUncaught;
+	private boolean fDefaultSuspendOnCompilationErrors;
+	private SuspendOnRecurrenceStrategy fDefaultRecurrenceStrategy;
+
+	public SpecialExceptionBreakpointTests(String name) {
+		super(name);
+	}
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		IPreferenceStore uiPrefStore = JDIDebugUIPlugin.getDefault().getPreferenceStore();
+		fDefaultSuspendOnUncaught = uiPrefStore.getBoolean(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS);
+		fDefaultSuspendOnCompilationErrors = uiPrefStore.getBoolean(IJDIPreferencesConstants.PREF_SUSPEND_ON_COMPILATION_ERRORS);
+		IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(JDIDebugPlugin.getUniqueIdentifier());
+		String val = prefs.get(JDIDebugModel.PREF_SUSPEND_ON_RECURRENCE_STRATEGY, SuspendOnRecurrenceStrategy.RECURRENCE_UNCONFIGURED.name());
+		fDefaultRecurrenceStrategy = SuspendOnRecurrenceStrategy.valueOf(val);
+	}
+
+	@Override
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		setPreferences(fDefaultSuspendOnUncaught, fDefaultSuspendOnCompilationErrors, fDefaultRecurrenceStrategy);
+	}
+
+	void setPreferences(boolean suspUncaught, boolean suspCompErr, SuspendOnRecurrenceStrategy recurr) {
+		IPreferenceStore uiPrefStore = JDIDebugUIPlugin.getDefault().getPreferenceStore();
+		uiPrefStore.setValue(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS, suspUncaught);
+		uiPrefStore.setValue(IJDIPreferencesConstants.PREF_SUSPEND_ON_COMPILATION_ERRORS, suspCompErr);
+		IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(JDIDebugPlugin.getUniqueIdentifier());
+		prefs.put(JDIDebugModel.PREF_SUSPEND_ON_RECURRENCE_STRATEGY, recurr.name());
+	}
+
+	public void testCaughtException_compErr() throws Exception {
+		caughtException(true);
+	}
+	public void testCaughtException() throws Exception {
+		caughtException(false);
+	}
+	void caughtException(boolean suspendOnCompErr) throws Exception {
+		setPreferences(true, suspendOnCompErr, SuspendOnRecurrenceStrategy.SUSPEND_ALWAYS);
+
+		String typeName = "ErrorRecurrence";
+		IJavaExceptionBreakpoint ex = createExceptionBreakpoint("java.lang.Error", true, true);
+
+		IJavaThread thread = null;
+		try {
+			thread = launchToBreakpoint(typeName);
+			assertNotNull("1. Breakpoint not hit within timeout period", thread);
+			assertEquals("Line of 1. suspend", 26, thread.getTopStackFrame().getLineNumber());
+			assertExceptionBreakpointHit(thread, ex);
+
+			thread = resume(thread);
+			assertExceptionBreakpointHit(thread, ex);
+			assertNotNull("2. Breakpoint not hit within timeout period", thread);
+			assertEquals("Line of 2. suspend", 20, thread.getTopStackFrame().getLineNumber());
+
+			thread = resume(thread);
+			assertExceptionBreakpointHit(thread, ex);
+			assertNotNull("3. Breakpoint not hit within timeout period", thread);
+			assertEquals("Line of 3. suspend", 12, thread.getTopStackFrame().getLineNumber());
+
+			resumeAndExit(thread);
+			ex.delete();
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}
+	}
+
+	public void testCaughtExceptionSkip_uncaught_compErr() throws Exception {
+		caughtExceptionSkip_uncaught(true);
+	}
+	public void testCaughtExceptionSkip_uncaught() throws Exception {
+		caughtExceptionSkip_uncaught(false);
+	}
+	void caughtExceptionSkip_uncaught(boolean suspendOnCompErr) throws Exception {
+		setPreferences(true, suspendOnCompErr, SuspendOnRecurrenceStrategy.SKIP_RECURRENCES);
+
+		String typeName = "ErrorRecurrence";
+		IJavaExceptionBreakpoint ex = createExceptionBreakpoint("java.lang.Error", true, true);
+
+		IJavaThread thread = null;
+		try {
+			thread = launchToBreakpoint(typeName);
+			assertNotNull("1. Breakpoint not hit within timeout period", thread);
+			assertEquals("Line of 1. suspend", 26, thread.getTopStackFrame().getLineNumber());
+			assertExceptionBreakpointHit(thread, ex);
+
+			// L20 skipped recurrence
+
+			// L12: uncaught:
+			thread = resume(thread);
+			assertExceptionBreakpointHit(thread, ex);
+			assertNotNull("2. Breakpoint not hit within timeout period", thread);
+			assertEquals("Line of 2. suspend", 12, thread.getTopStackFrame().getLineNumber());
+
+			resumeAndExit(thread);
+			ex.delete();
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}
+	}
+
+	public void testCaughtExceptionSkip_compErr() throws Exception {
+		caughtExceptionSkip(true);
+	}
+	public void testCaughtExceptionSkip() throws Exception {
+		caughtExceptionSkip(false);
+	}
+	void caughtExceptionSkip(boolean suspendOnCompErr) throws Exception {
+		setPreferences(false, suspendOnCompErr, SuspendOnRecurrenceStrategy.SKIP_RECURRENCES);
+
+		String typeName = "ErrorRecurrence";
+		IJavaExceptionBreakpoint ex = createExceptionBreakpoint("java.lang.Error", true, true);
+
+		IJavaThread thread = null;
+		try {
+			thread = launchToBreakpoint(typeName);
+			assertNotNull("1. Breakpoint not hit within timeout period", thread);
+			assertEquals("Line of 1. suspend", 26, thread.getTopStackFrame().getLineNumber());
+			assertExceptionBreakpointHit(thread, ex);
+
+			// L20 skipped recurrence
+
+			// L12 suspend on uncaught disabled
+			resumeAndExit(thread);
+			ex.delete();
+		} finally {
+			terminateAndRemove(thread);
+			removeAllBreakpoints();
+		}
+	}
+
+	private void assertExceptionBreakpointHit(IJavaThread thread, IJavaExceptionBreakpoint ex) throws DebugException {
+		IMarker problem = JavaDebugOptionsManager.getDefault().getProblem((IJavaStackFrame) thread.getTopStackFrame());
+		if (problem != null) {
+			fail("unexpected problem marker "+problem);
+		}
+		IBreakpoint hit = getBreakpoint(thread);
+		assertNotNull("suspended, but not by breakpoint", hit);
+		assertEquals("suspended, but not by expected exception", ex.getExceptionTypeName(), ((IJavaExceptionBreakpoint) hit).getExceptionTypeName());
+	}
+}
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/Java8Tests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/Java8Tests.java
index 9c14ddd..9353f40 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/Java8Tests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/Java8Tests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 Jesper S. Møller and others.
+ * Copyright (c) 2014, 2020 Jesper S. Møller and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -17,6 +17,7 @@
 package org.eclipse.jdt.debug.tests.eval;
 
 import org.eclipse.debug.core.model.IValue;
+import org.eclipse.jdt.core.ICompilationUnit;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
 import org.eclipse.jdt.debug.core.IJavaThread;
@@ -129,4 +130,115 @@
 			terminateAndRemove(thread);
 		}
 	}
+
+	/**
+	 * Evaluates a snippet containing a lambda
+	 *
+	 * @throws Exception
+	 */
+	public void testEvalLambda() throws Exception {
+		IJavaThread thread = null;
+		try {
+			String type = "EvalTest18";
+			IJavaLineBreakpoint bp = createLineBreakpoint(28, type);
+			assertNotNull("should have created breakpoint", bp);
+			thread = launchToBreakpoint(type);
+			assertNotNull("The program did not suspend", thread);
+			String snippet = "l.stream().filter(i -> i > 2).count()";
+			IValue result = doEval(thread, snippet);
+			assertEquals("2", result.getValueString());
+		} finally {
+			removeAllBreakpoints();
+			terminateAndRemove(thread);
+		}
+	}
+
+	/**
+	 * Evaluates a snippet containing a lambda referencing a variable in a loop
+	 *
+	 * @throws Exception
+	 */
+	public void testEvalLambdaInLoop() throws Exception {
+		IJavaThread thread = null;
+		try {
+			String type = "EvalTest18";
+			IJavaLineBreakpoint bp = createLineBreakpoint(31, type);
+			assertNotNull("should have created breakpoint", bp);
+			thread = launchToBreakpoint(type);
+			assertNotNull("The program did not suspend", thread);
+			String snippet = "l.stream().filter(j -> j > i+1).count()";
+			IValue result = doEval(thread, snippet);
+			assertEquals("2", result.getValueString());
+		} finally {
+			removeAllBreakpoints();
+			terminateAndRemove(thread);
+		}
+	}
+
+	/**
+	 * Evaluates a snippet containing a method reference
+	 *
+	 * @throws Exception
+	 */
+	public void testEvalMethodReference() throws Exception {
+		IJavaThread thread = null;
+		try {
+			String type = "EvalTest18";
+			IJavaLineBreakpoint bp = createLineBreakpoint(28, type);
+			assertNotNull("should have created breakpoint", bp);
+			thread = launchToBreakpoint(type);
+			assertNotNull("The program did not suspend", thread);
+			String snippet = "l.stream().mapToInt(Integer::bitCount).sum()";
+			IValue result = doEval(thread, snippet);
+			assertEquals("5", result.getValueString());
+		} finally {
+			removeAllBreakpoints();
+			terminateAndRemove(thread);
+		}
+	}
+
+	/**
+	 * Evaluates a snippet containing a method reference
+	 *
+	 * @throws Exception
+	 */
+	public void testContextEvaluations() throws Exception {
+		IJavaThread thread = null;
+		try {
+			String type = "FunctionalCaptureTest18";
+			ICompilationUnit cu = getType(type).getCompilationUnit();
+			String[] lines = new String(cu.getBuffer().getCharacters()).split("\n");
+
+			int i = 0;
+			for (; i < lines.length; ++i) {
+				if (lines[i].contains("/* CHECK EXPRESSIONS BELOW */")) break;
+			}
+			assertTrue("Missing source marker", i < lines.length);
+			IJavaLineBreakpoint bp = createLineBreakpoint(i, type);
+			assertNotNull("should have created breakpoint", bp);
+			thread = launchToBreakpoint(type);
+			assertNotNull("The program did not suspend", thread);
+			
+			for (; i < lines.length; ++i) {
+				String line = lines[i];
+				
+				if (line.contains("/* END OF TESTS */")) break; 
+				if (line.trim().startsWith("/") || line.trim().isEmpty()) continue; // Comment, just skip it
+				if (line.contains("/* SKIP */")) continue;
+				
+				int lastSemicolon = line.lastIndexOf(';');
+				assertTrue(lastSemicolon > 1);
+				String snippet = line.substring(0,  lastSemicolon).trim();
+				//System.out.println("*******************: " + snippet);
+				IValue result = doEval(thread, snippet);
+				assertNotNull(result);
+				//System.out.println(">>>>>>>>>>>>>>>>>>>: " + result.getReferenceTypeName());
+			}
+			
+		} finally {
+			removeAllBreakpoints();
+			terminateAndRemove(thread);
+		}
+	}
+
 }
diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml
index 0a44a67..287975f 100644
--- a/org.eclipse.jdt.debug.ui/plugin.xml
+++ b/org.eclipse.jdt.debug.ui/plugin.xml
@@ -3406,6 +3406,7 @@
             class="org.eclipse.jdt.internal.debug.ui.console.JavaConsoleTracker"
             regex="\(\w*${java_extensions_regex}\S*\)"
             qualifier="${java_extensions_regex}"
+            flags="UNICODE_CHARACTER_CLASS"
             id="org.eclipse.jdt.debug.ui.JavaConsoleTracker">
          <enablement>
             <or>
@@ -3432,6 +3433,7 @@
             class="org.eclipse.jdt.internal.debug.ui.console.JavaExceptionConsoleTracker"
             regex="\w[^\(\s]+Exception[\s|:]"
             qualifier="Exception[\s|:]"
+            flags="UNICODE_CHARACTER_CLASS"
             id="org.eclipse.jdt.debug.ui.JavaExceptionConsoleTracker">
          <enablement>
             <or>
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugOptionsManager.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugOptionsManager.java
index 63c1ff9..d1fe3b7 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugOptionsManager.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugOptionsManager.java
@@ -583,6 +583,15 @@
 	@Override
 	public int breakpointHit(IJavaThread thread, IJavaBreakpoint breakpoint) {
 		if (thread instanceof JDIThread && breakpoint instanceof IJavaExceptionBreakpoint) {
+			try {
+				String[] breakpointListeners = breakpoint.getBreakpointListeners();
+				if (breakpointListeners.length == 1
+						&& SuspendOnCompilationErrorListener.ID_COMPILATION_ERROR_LISTENER.equals(breakpointListeners[0])) {
+					return DONT_CARE; // not a user breakpoint
+				}
+			} catch (CoreException e1) {
+				// continue
+			}
 			if (shouldSkipSubsequentOccurrence((JDIThread) thread, (IJavaExceptionBreakpoint) breakpoint)) {
 				return DONT_SUSPEND;
 			}
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 f077d87..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
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -28,7 +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_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 e11fa21..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
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2000, 2007 IBM Corporation and others.
+# Copyright (c) 2000, 2020 IBM Corporation and others.
 #
 # This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License 2.0
@@ -22,3 +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_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/EvaluationResult.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationResult.java
index 87dd2fa..581b138 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationResult.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationResult.java
@@ -14,7 +14,6 @@
 package org.eclipse.jdt.internal.debug.eval;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 
 import org.eclipse.debug.core.DebugException;
@@ -113,8 +112,8 @@
 	public Message[] getErrors() {
 		Message[] messages = new Message[fErrors.size()];
 		int i = 0;
-		for (Iterator<String> iter = fErrors.iterator(); iter.hasNext();) {
-			messages[i++] = new Message(iter.next(), -1);
+		for (String errMsg : fErrors) {
+			messages[i++] = new Message(errMsg, -1);
 		}
 		return messages;
 	}
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
new file mode 100644
index 0000000..2fa84cf
--- /dev/null
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java
@@ -0,0 +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 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
new file mode 100644
index 0000000..c19fab8
--- /dev/null
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java
@@ -0,0 +1,364 @@
+/*******************************************************************************
+
+ * Copyright (c) 2019, 2020 Jesper Steen Møller and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Jesper Steen Møller - initial API and implementation
+ *     IBM Corporation - Bug 448473 - [1.8][debug] Cannot use lambda expressions in breakpoint properties and display/expression view
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.eval;
+
+import static org.eclipse.jdt.core.eval.ICodeSnippetRequestor.LOCAL_VAR_PREFIX;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IVariable;
+import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
+import org.eclipse.jdt.debug.core.IJavaArray;
+import org.eclipse.jdt.debug.core.IJavaArrayType;
+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.IJavaObject;
+import org.eclipse.jdt.debug.core.IJavaReferenceType;
+import org.eclipse.jdt.debug.core.IJavaThread;
+import org.eclipse.jdt.debug.core.IJavaType;
+import org.eclipse.jdt.debug.core.IJavaValue;
+import org.eclipse.jdt.debug.core.IJavaVariable;
+import org.eclipse.jdt.debug.core.JDIDebugModel;
+import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
+import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
+import org.eclipse.jdt.internal.debug.core.model.JDIValue;
+
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.ObjectReference;
+
+/**
+ * An evaluation engine that deploys class files to a debuggee by using Unsafe through the JDWP.
+ */
+
+public class RemoteEvaluator {
+
+	private final LinkedHashMap<String, byte[]> classFiles;
+
+	private final String codeSnippetClassName;
+
+	private final List<String> variableNames;
+
+	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 variableNames
+	 * @param enclosingTypeName
+	 */
+	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;
+	}
+
+	private IJavaClassObject loadTheClasses(IJavaThread theThread) throws DebugException {
+
+		if (loadedClass != null) {
+			return loadedClass;
+		}
+		JDIDebugTarget debugTarget = ((JDIDebugTarget) theThread.getDebugTarget());
+		IJavaClassObject theMainClass = null;
+		IJavaObject classloader = null;
+
+		IJavaReferenceType surroundingClass = findType(this.enclosingTypeName, debugTarget);
+		classloader = surroundingClass.getClassLoaderObject();
+
+		for (Map.Entry<String, byte[]> entry : classFiles.entrySet()) {
+			String className = entry.getKey();
+
+			IJavaReferenceType existingClass = tryLoadType(className, debugTarget);
+			if (existingClass != null) {
+				if (codeSnippetClassName.equals(className)) {
+					theMainClass = existingClass.getClassObject();
+				}
+			} else {
+				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), // offset
+						debugTarget.newValue(entry.getValue().length), // length
+						debugTarget.nullValue() // protection domain
+				};
+
+				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;
+				}
+			}
+		}
+		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) {
+			// unable to load the class
+			throw new DebugException(
+					new Status(
+							IStatus.ERROR,
+							JDIDebugModel.getPluginIdentifier(),
+							DebugException.REQUEST_FAILED,
+							EvaluationMessages.RemoteEvaluationEngine_Evaluation_failed___unable_to_find_injected_class,
+							null));
+		}
+		return theClass;
+	}
+
+	private IJavaReferenceType tryLoadType(String typeName, IJavaDebugTarget debugTarget) throws DebugException {
+		IJavaReferenceType clazz = null;
+		IJavaType[] types = debugTarget.getJavaTypes(typeName);
+		if (types != null && types.length > 0) {
+			clazz = (IJavaReferenceType) types[0];
+		}
+		return clazz;
+	}
+
+	/**
+	 * Initializes the value of instance variables in the 'code snippet object' that are used as place-holders for free variables and 'this' in the
+	 * current stack frame.
+	 *
+	 * @param object
+	 *            instance of code snippet class that will be run
+	 * @param boundValues
+	 *            popped values which should be injected into the code snippet object.
+	 * @exception DebugException
+	 *                if an exception is thrown accessing the given object
+	 */
+	protected void initializeFreeVars(IJavaObject object, IJavaValue boundValues[]) throws DebugException {
+		if (boundValues.length != this.variableNames.size()) {
+			throw new DebugException(new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(), DebugException.REQUEST_FAILED, EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__4, null));
+		}
+
+		for (int i = 0; i < boundValues.length; ++i) {
+			IJavaVariable field = object.getField(new String(LOCAL_VAR_PREFIX) + this.variableNames.get(i), false);
+			if (field != null) {
+				IJavaValue bound = boundValues[i];
+				field.setValue(bound);
+			} else {
+				// System.out.print(Arrays.asList(((IJavaReferenceType) object.getJavaType()).getAllFieldNames()));
+				throw new DebugException(new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(), DebugException.REQUEST_FAILED, EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__4, null));
+			}
+		}
+	}
+
+	/**
+	 * Constructs and returns a new instance of the specified class on the
+	 * target VM.
+	 *
+	 * @param className
+	 *            fully qualified class name
+	 * @return a new instance on the target, as an <code>IJavaValue</code>
+	 * @exception DebugException
+	 *                if creation fails
+	 */
+	protected IJavaObject newInstance(IJavaThread theThread) throws DebugException {
+		IJavaDebugTarget debugTarget = ((IJavaDebugTarget) theThread.getDebugTarget());
+
+		IJavaObject object = null;
+		IJavaClassObject clazz = loadTheClasses(theThread);
+		if (clazz == null) {
+			// The class is not loaded on the target VM.
+			// Force the load of the class.
+			IJavaType[] types = debugTarget.getJavaTypes("java.lang.Class"); //$NON-NLS-1$
+			IJavaClassType classClass = null;
+			if (types != null && types.length > 0) {
+				classClass = (IJavaClassType) types[0];
+			}
+			if (classClass == null) {
+				// unable to load the class
+				throw new DebugException(
+						new Status(
+								IStatus.ERROR,
+								JDIDebugModel.getPluginIdentifier(),
+								DebugException.REQUEST_FAILED,
+								EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_instantiate_code_snippet_class__11,
+								null));
+			}
+			IJavaValue[] args = new IJavaValue[] { debugTarget.newValue(
+					this.codeSnippetClassName) };
+			IJavaObject classObject = (IJavaObject) classClass
+					.sendMessage(
+							"forName", "(Ljava/lang/String;)Ljava/lang/Class;", args, theThread); //$NON-NLS-2$ //$NON-NLS-1$
+			object = (IJavaObject) classObject
+					.sendMessage(
+							"newInstance", "()Ljava/lang/Object;", null, theThread, false); //$NON-NLS-2$ //$NON-NLS-1$
+		} else {
+			object = (IJavaObject) clazz.sendMessage("newInstance", "()Ljava/lang/Object;", null, theThread, false); //$NON-NLS-2$ //$NON-NLS-1$
+			// object = clazz.newInstance("<init>", null, theThread); //$NON-NLS-1$
+		}
+		return object;
+	}
+
+	/**
+	 * Interprets and returns the result of the running the snippet class file.
+	 * The type of the result is described by an instance of
+	 * <code>java.lang.Class</code>. The value is interpreted based on the
+	 * result type.
+	 * <p>
+	 * Objects as well as primitive data types (boolean, int, etc.), have class
+	 * objects, which are created by the VM. If the class object represents a
+	 * primitive data type, then the associated value is stored in an instance
+	 * of its "object" class. For example, when the result type is the class
+	 * object for <code>int</code>, the result object is an instance of
+	 * <code>java.lang.Integer</code>, and the actual <code>int</code> is stored
+	 * in the </code>intValue()</code>. When the result type is the class object
+	 * for <code>java.lang.Integer</code> the result object is an instance of
+	 * <code>java.lang.Integer</code>, to be interpreted as a
+	 * <code>java.lang.Integer</code>.
+	 * </p>
+	 *
+	 * @param resultType
+	 *            the class of the result
+	 * @param resultValue
+	 *            the value of the result, to be interpreted based on
+	 *            resultType
+	 * @return the result of running the code snippet class file
+	 */
+	protected IJavaValue convertResult(IJavaDebugTarget debugTarget, IJavaClassObject resultType,
+			IJavaValue result) throws DebugException {
+		if (resultType == null) {
+			// there was an exception or compilation problem - no result
+			return null;
+		}
+
+		// check the type of the result - if a primitive type, convert it
+		String sig = resultType.getInstanceType().getSignature();
+		if (sig.equals("V") || sig.equals("Lvoid;")) { //$NON-NLS-2$ //$NON-NLS-1$
+			// void
+			return debugTarget.voidValue();
+		}
+
+		if (result.getJavaType() == null) {
+			// null result
+			return result;
+		}
+
+		if (sig.length() == 1) {
+			// primitive type - find the instance variable with the
+			// signature of the result type we are looking for
+			IVariable[] vars = result.getVariables();
+			IJavaVariable var = null;
+			for (IVariable var2 : vars) {
+				IJavaVariable jv = (IJavaVariable) var2;
+				if (!jv.isStatic() && jv.getSignature().equals(sig)) {
+					var = jv;
+					break;
+				}
+			}
+			if (var != null) {
+				return (IJavaValue) var.getValue();
+			}
+		} else {
+			// an object
+			return result;
+		}
+		throw new DebugException(
+				new Status(
+						IStatus.ERROR,
+						JDIDebugModel.getPluginIdentifier(),
+						DebugException.REQUEST_FAILED,
+						EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___internal_error_retreiving_result__17,
+						null));
+	}
+
+	/**
+	 * Returns the name of the code snippet to instantiate to run the current
+	 * evaluation.
+	 *
+	 * @return the name of the deployed code snippet to instantiate and run
+	 */
+	protected String getCodeSnippetClassName() {
+		return codeSnippetClassName;
+	}
+
+	public IJavaValue evaluate(IJavaThread theThread, IJavaValue[] args) throws DebugException {
+		IJavaObject codeSnippetInstance = null;
+		IJavaDebugTarget debugTarget = ((IJavaDebugTarget) theThread.getDebugTarget());
+		try {
+			codeSnippetInstance = newInstance(theThread);
+			initializeFreeVars(codeSnippetInstance, args);
+			codeSnippetInstance.sendMessage(ICodeSnippetRequestor.RUN_METHOD, "()V", null, theThread, false); //$NON-NLS-1$
+
+			// now retrieve the description of the result
+			IVariable[] fields = codeSnippetInstance.getVariables();
+			IJavaVariable resultValue = null;
+			IJavaVariable resultType = null;
+			for (IVariable field : fields) {
+				if (field.getName().equals(ICodeSnippetRequestor.RESULT_TYPE_FIELD)) {
+					resultType = (IJavaVariable) field;
+				}
+				if (field.getName().equals(ICodeSnippetRequestor.RESULT_VALUE_FIELD)) {
+					resultValue = (IJavaVariable) field;
+				}
+			}
+			IJavaValue result = convertResult(debugTarget, (IJavaClassObject) resultType.getValue(), (IJavaValue) resultValue.getValue());
+			return result;
+		} catch (DebugException e) {
+			Throwable underlyingException = e.getStatus().getException();
+			if (underlyingException instanceof InvocationException) {
+				ObjectReference theException = ((InvocationException) underlyingException).exception();
+				if (theException != null) {
+					try {
+						try {
+							IJavaObject v = (IJavaObject) JDIValue.createValue((JDIDebugTarget) debugTarget, theException);
+							v.sendMessage("printStackTrace", "()V", null, theThread, false); //$NON-NLS-2$ //$NON-NLS-1$
+						} catch (DebugException de) {
+							JDIDebugPlugin.log(de);
+						}
+					} catch (RuntimeException re) {
+						JDIDebugPlugin.log(re);
+					}
+				}
+			}
+			throw e;
+		}
+	}
+
+	public int getVariableCount() {
+		return this.variableNames.size();
+	}
+
+	public String getVariableName(int i) {
+		return this.variableNames.get(i);
+	}
+}
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
new file mode 100644
index 0000000..03a05fa
--- /dev/null
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java
@@ -0,0 +1,2098 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Jesper Steen Møller and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Jesper Steen Møller - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.eval;
+
+import static org.eclipse.jdt.core.eval.ICodeSnippetRequestor.LOCAL_VAR_PREFIX;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+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.
+ */
+
+@SuppressWarnings("rawtypes")
+public class RemoteEvaluatorBuilder {
+
+	private IJavaProject javaProject;
+	private ExpressionBinder binder;
+	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
+	 */
+	private LinkedHashMap<String, byte[]> classFiles = new LinkedHashMap<>();
+
+	/**
+	 * The name of the code snippet class to instantiate
+	 */
+	private String codeSnippetClassName = null;
+	private String snippet = null;
+	private final ITypeBinding enclosingClass;
+
+	public RemoteEvaluatorBuilder(IJavaProject javaProject, ExpressionBinder binder, ITypeBinding enclosingClass, boolean isStatic, boolean isConstructor) {
+		this.javaProject = javaProject;
+		this.binder = binder;
+		this.enclosingClass = enclosingClass;
+		this.enclosingTypeName = enclosingClass.getQualifiedName();
+		this.packageName = enclosingClass.getPackage().getName();
+		this.isStatic = isStatic;
+		this.isConstructor = isConstructor;
+	}
+
+	public void acceptLambda(LambdaExpression lambda, ITypeBinding expectedResult) {
+		acceptFunctionalExpression(lambda, expectedResult);
+	}
+
+	public void acceptMethodReference(MethodReference node, ITypeBinding expectedResult) {
+		acceptFunctionalExpression(node, expectedResult);
+	}
+
+	private void acceptFunctionalExpression(Expression node, ITypeBinding expectedResult) {
+		FunctionalEvalVisitor visitor = new FunctionalEvalVisitor();
+		node.accept(visitor);
+		String castExpression = "(" + expectedResult.getQualifiedName() + ")"; //$NON-NLS-1$//$NON-NLS-2$
+		this.snippet = castExpression + "(" + visitor.buffer.toString() + ")"; //$NON-NLS-1$//$NON-NLS-2$
+	}
+
+	public String getSnippet() {
+		return snippet;
+	}
+
+	private static Object EVALUATE_CODE_SNIPPET_LOCK = new Object();
+
+	public RemoteEvaluator build() throws JavaModelException, DebugException {
+
+		List<String> boundVariableNames = getVariableNames();
+		List<String> boundVariableTypeNames = getVariableTypeNames();
+
+		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) {
+			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) {
+					if (problemMarker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) >= IMarker.SEVERITY_ERROR) {
+						errors.add(problemMarker.toString());
+					}
+				}
+
+				@Override
+				public boolean acceptClassFiles(byte[][] classFileBytes, String[][] classFileCompoundNames, String mainCodeSnippetClassName) {
+					for (int i = 0; i < classFileCompoundNames.length; ++i) {
+						String className = makeClassName(classFileCompoundNames[i]);
+						classFiles.put(className, classFileBytes[i]);
+					}
+					if (mainCodeSnippetClassName != null) {
+						setCodeSnippetClassName(mainCodeSnippetClassName);
+					}
+					return true;
+				}
+			}, null);
+		}
+
+		if (!errors.isEmpty()) {
+			throw new DebugException(new Status(IStatus.ERROR, JDIDebugPlugin.getUniqueIdentifier(), errors.toString()));
+		}
+
+		return new RemoteEvaluator(classFiles, codeSnippetClassName, getVariableNames(), enclosingType.getFullyQualifiedName('$'));
+	}
+
+	private void setCodeSnippetClassName(String codeSnippetClassName) {
+		this.codeSnippetClassName = codeSnippetClassName;
+	}
+
+	private static String makeClassName(String[] names) {
+		StringBuilder sb = new StringBuilder();
+		for (int j = 0; j < names.length; j++) {
+			if (j > 0) {
+				sb.append('/');
+			}
+			sb.append(names[j]);
+		}
+		return sb.toString();
+	}
+
+	public List<String> getVariableTypeNames() {
+		return Collections.unmodifiableList(argumentTypeNames);
+	}
+
+	public List<String> getVariableNames() {
+		return Collections.unmodifiableList(argumentNames);
+	}
+
+	public String allocateNewVariable(ITypeBinding binding, String hint) {
+		String varName = hint + "$" + argumentNames.size(); //$NON-NLS-1$
+		argumentNames.add(varName);
+		argumentTypeNames.add(binding.getQualifiedName());
+		return varName;
+	}
+
+	public IJavaProject getJavaProject() {
+		return javaProject;
+	}
+
+	public String getEnclosingTypeName() {
+		return enclosingTypeName;
+	}
+
+	public boolean isStatic() {
+		return isStatic;
+	}
+
+	/**
+	 * Internal synonym for {@link MethodDeclaration#getReturnType()}. Use to alleviate deprecation warnings.
+	 *
+	 * @deprecated
+	 * @since 3.4
+	 */
+	@Deprecated
+	private static Type getReturnType(MethodDeclaration node) {
+		return node.getReturnType();
+	}
+
+	/**
+	 * Internal synonym for {@link TypeDeclaration#getSuperclass()}. Use to alleviate deprecation warnings.
+	 *
+	 * @deprecated
+	 * @since 3.4
+	 */
+	@Deprecated
+	private static Name getSuperclass(TypeDeclaration node) {
+		return node.getSuperclass();
+	}
+
+	/**
+	 * Internal synonym for {@link TypeDeclarationStatement#getTypeDeclaration()}. Use to alleviate deprecation warnings.
+	 *
+	 * @deprecated
+	 * @since 3.4
+	 */
+	@Deprecated
+	private static TypeDeclaration getTypeDeclaration(TypeDeclarationStatement node) {
+		return node.getTypeDeclaration();
+	}
+
+	/**
+	 * Internal synonym for {@link MethodDeclaration#thrownExceptions()}. Use to alleviate deprecation warnings.
+	 *
+	 * @deprecated
+	 * @since 3.10
+	 */
+	@Deprecated
+	private static List<?> thrownExceptions(MethodDeclaration node) {
+		return node.thrownExceptions();
+	}
+
+	private class FunctionalEvalVisitor extends ASTVisitor {
+
+		/**
+		 * The string buffer into which the serialized representation of the AST is written.
+		 */
+		protected StringBuilder buffer = new StringBuilder();
+
+		private int indent = 2;
+
+		private Map<IBinding, String> localBindings = new HashMap<>();
+
+		public FunctionalEvalVisitor() {
+		}
+
+		boolean isLocalBinding(IBinding binding) {
+			return localBindings.containsKey(binding);
+		}
+
+		void addLocalBinding(IBinding binding, String name) {
+			localBindings.put(binding, name);
+		}
+
+		void printIndent() {
+			for (int i = 0; i < indent; i++) {
+				buffer.append("  ");//$NON-NLS-1$
+			}
+		}
+
+		/**
+		 * Internal synonym for {@link AST#JLS2}. Use to alleviate deprecation warnings.
+		 *
+		 * @deprecated
+		 */
+		@Deprecated
+		private static final int JLS2 = AST.JLS2;
+
+		/**
+		 * Internal synonym for {@link AST#JLS3}. Use to alleviate deprecation warnings.
+		 *
+		 * @deprecated
+		 */
+		@Deprecated
+		private static final int JLS3 = AST.JLS3;
+
+		/**
+		 * Internal synonym for {@link AST#JLS4}. Use to alleviate deprecation warnings.
+		 *
+		 * @deprecated
+		 */
+		@Deprecated
+		private static final int JLS4 = AST.JLS4;
+
+		/**
+		 * Internal synonym for {@link AST#JLS8}. Use to alleviate deprecation warnings.
+		 *
+		 * @deprecated
+		 */
+		@Deprecated
+		private static final int JLS8 = AST.JLS8;
+
+		/**
+		 * Internal synonym for {@link AST#JLS9}. Use to alleviate deprecation warnings.
+		 *
+		 * @deprecated
+		 */
+		@Deprecated
+		private static final int JLS9 = AST.JLS9;
+
+		/**
+		 * Internal synonym for {@link ClassInstanceCreation#getName()}. Use to alleviate deprecation warnings.
+		 *
+		 * @deprecated
+		 */
+		@Deprecated
+		private Name getName(ClassInstanceCreation node) {
+			return node.getName();
+		}
+
+		/**
+		 * Appends the text representation of the given modifier flags, followed by a single space. Used for JLS2 modifiers.
+		 *
+		 * @param modifiers
+		 *            the modifier flags
+		 */
+		void printModifiers(int modifiers) {
+			if (Modifier.isPublic(modifiers)) {
+				buffer.append("public ");//$NON-NLS-1$
+			}
+			if (Modifier.isProtected(modifiers)) {
+				buffer.append("protected ");//$NON-NLS-1$
+			}
+			if (Modifier.isPrivate(modifiers)) {
+				buffer.append("private ");//$NON-NLS-1$
+			}
+			if (Modifier.isStatic(modifiers)) {
+				buffer.append("static ");//$NON-NLS-1$
+			}
+			if (Modifier.isAbstract(modifiers)) {
+				buffer.append("abstract ");//$NON-NLS-1$
+			}
+			if (Modifier.isFinal(modifiers)) {
+				buffer.append("final ");//$NON-NLS-1$
+			}
+			if (Modifier.isSynchronized(modifiers)) {
+				buffer.append("synchronized ");//$NON-NLS-1$
+			}
+			if (Modifier.isVolatile(modifiers)) {
+				buffer.append("volatile ");//$NON-NLS-1$
+			}
+			if (Modifier.isNative(modifiers)) {
+				buffer.append("native ");//$NON-NLS-1$
+			}
+			if (Modifier.isStrictfp(modifiers)) {
+				buffer.append("strictfp ");//$NON-NLS-1$
+			}
+			if (Modifier.isTransient(modifiers)) {
+				buffer.append("transient ");//$NON-NLS-1$
+			}
+		}
+
+		/**
+		 * Appends the text representation of the given modifier flags, followed by a single space. Used for 3.0 modifiers and annotations.
+		 *
+		 * @param ext
+		 *            the list of modifier and annotation nodes (element type: <code>IExtendedModifiers</code>)
+		 */
+		void printModifiers(List ext) {
+			for (Iterator it = ext.iterator(); it.hasNext();) {
+				ASTNode p = (ASTNode) it.next();
+				p.accept(this);
+				buffer.append(" ");//$NON-NLS-1$
+			}
+		}
+
+		private void printTypes(List<Type> types, String prefix) {
+			if (types.size() > 0) {
+				buffer.append(" " + prefix + " ");//$NON-NLS-1$ //$NON-NLS-2$
+				Type type = types.get(0);
+				type.accept(this);
+				for (int i = 1, l = types.size(); i < l; ++i) {
+					buffer.append(","); //$NON-NLS-1$
+					type = types.get(0);
+					type.accept(this);
+				}
+			}
+		}
+
+		/**
+		 * reference node helper function that is common to all the difference reference nodes.
+		 *
+		 * @param typeArguments
+		 *            list of type arguments
+		 */
+		private void visitReferenceTypeArguments(List typeArguments) {
+			buffer.append("::");//$NON-NLS-1$
+			if (!typeArguments.isEmpty()) {
+				buffer.append('<');
+				for (Iterator it = typeArguments.iterator(); it.hasNext();) {
+					Type t = (Type) it.next();
+					t.accept(this);
+					if (it.hasNext()) {
+						buffer.append(',');
+					}
+				}
+				buffer.append('>');
+			}
+		}
+
+		private void visitTypeAnnotations(AnnotatableType node) {
+			if (node.getAST().apiLevel() >= JLS8) {
+				visitAnnotationsList(node.annotations());
+			}
+		}
+
+		private void visitAnnotationsList(List annotations) {
+			for (Iterator it = annotations.iterator(); it.hasNext();) {
+				Annotation annotation = (Annotation) it.next();
+				annotation.accept(this);
+				buffer.append(' ');
+			}
+		}
+
+		/**
+		 * Internal synonym for {@link TypeDeclaration#superInterfaces()}. Use to alleviate deprecation warnings.
+		 *
+		 * @deprecated
+		 * @since 3.4
+		 */
+		@Deprecated
+		private List superInterfaces(TypeDeclaration node) {
+			return node.superInterfaces();
+		}
+
+		@Override
+		public boolean visit(AnnotationTypeDeclaration node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			printIndent();
+			printModifiers(node.modifiers());
+			buffer.append("@interface ");//$NON-NLS-1$
+			node.getName().accept(this);
+			buffer.append(" {");//$NON-NLS-1$
+			for (Iterator it = node.bodyDeclarations().iterator(); it.hasNext();) {
+				BodyDeclaration d = (BodyDeclaration) it.next();
+				d.accept(this);
+			}
+			buffer.append("}\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(AnnotationTypeMemberDeclaration node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			printIndent();
+			printModifiers(node.modifiers());
+			node.getType().accept(this);
+			buffer.append(" ");//$NON-NLS-1$
+			node.getName().accept(this);
+			buffer.append("()");//$NON-NLS-1$
+			if (node.getDefault() != null) {
+				buffer.append(" default ");//$NON-NLS-1$
+				node.getDefault().accept(this);
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(AnonymousClassDeclaration node) {
+			buffer.append("{\n");//$NON-NLS-1$
+			indent++;
+			for (Iterator it = node.bodyDeclarations().iterator(); it.hasNext();) {
+				BodyDeclaration b = (BodyDeclaration) it.next();
+				b.accept(this);
+			}
+			indent--;
+			printIndent();
+			buffer.append("}\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ArrayAccess node) {
+			node.getArray().accept(this);
+			buffer.append("[");//$NON-NLS-1$
+			node.getIndex().accept(this);
+			buffer.append("]");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ArrayCreation node) {
+			buffer.append("new ");//$NON-NLS-1$
+			ArrayType at = node.getType();
+			int dims = at.getDimensions();
+			Type elementType = at.getElementType();
+			elementType.accept(this);
+			for (Iterator it = node.dimensions().iterator(); it.hasNext();) {
+				buffer.append("[");//$NON-NLS-1$
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				buffer.append("]");//$NON-NLS-1$
+				dims--;
+			}
+			// add empty "[]" for each extra array dimension
+			for (int i = 0; i < dims; i++) {
+				buffer.append("[]");//$NON-NLS-1$
+			}
+			if (node.getInitializer() != null) {
+				node.getInitializer().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(ArrayInitializer node) {
+			buffer.append("{");//$NON-NLS-1$
+			for (Iterator it = node.expressions().iterator(); it.hasNext();) {
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append("}");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ArrayType node) {
+			if (node.getAST().apiLevel() < JLS8) {
+				visitComponentType(node);
+				buffer.append("[]");//$NON-NLS-1$
+			} else {
+				node.getElementType().accept(this);
+				List dimensions = node.dimensions();
+				int size = dimensions.size();
+				for (int i = 0; i < size; i++) {
+					Dimension aDimension = (Dimension) dimensions.get(i);
+					aDimension.accept(this);
+				}
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(AssertStatement node) {
+			printIndent();
+			buffer.append("assert ");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			if (node.getMessage() != null) {
+				buffer.append(" : ");//$NON-NLS-1$
+				node.getMessage().accept(this);
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(Assignment node) {
+			node.getLeftHandSide().accept(this);
+			buffer.append(node.getOperator().toString());
+			node.getRightHandSide().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(Block node) {
+			buffer.append("{\n");//$NON-NLS-1$
+			indent++;
+			for (Iterator it = node.statements().iterator(); it.hasNext();) {
+				Statement s = (Statement) it.next();
+				s.accept(this);
+			}
+			indent--;
+			printIndent();
+			buffer.append("}\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(BlockComment node) {
+			printIndent();
+			buffer.append("/* */");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(BooleanLiteral node) {
+			if (node.booleanValue() == true) {
+				buffer.append("true");//$NON-NLS-1$
+			} else {
+				buffer.append("false");//$NON-NLS-1$
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(BreakStatement node) {
+			printIndent();
+			buffer.append("break");//$NON-NLS-1$
+			if (node.getLabel() != null) {
+				buffer.append(" ");//$NON-NLS-1$
+				node.getLabel().accept(this);
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(CastExpression node) {
+			//buffer.append("(");//$NON-NLS-1$
+			node.getType().accept(this);
+			//buffer.append(")");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(CatchClause node) {
+			buffer.append("catch (");//$NON-NLS-1$
+			node.getException().accept(this);
+			buffer.append(") ");//$NON-NLS-1$
+			node.getBody().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(CharacterLiteral node) {
+			buffer.append(node.getEscapedValue());
+			return false;
+		}
+
+		@Override
+		public boolean visit(ClassInstanceCreation node) {
+			if (node.getExpression() != null) {
+				node.getExpression().accept(this);
+				buffer.append(".");//$NON-NLS-1$
+			}
+			buffer.append("new ");//$NON-NLS-1$
+			if (node.getAST().apiLevel() == JLS2) {
+				getName(node).accept(this);
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (!node.typeArguments().isEmpty()) {
+					buffer.append("<");//$NON-NLS-1$
+					for (Iterator it = node.typeArguments().iterator(); it.hasNext();) {
+						Type t = (Type) it.next();
+						t.accept(this);
+						if (it.hasNext()) {
+							buffer.append(",");//$NON-NLS-1$
+						}
+					}
+					buffer.append(">");//$NON-NLS-1$
+				}
+				node.getType().accept(this);
+			}
+			buffer.append("(");//$NON-NLS-1$
+			for (Iterator it = node.arguments().iterator(); it.hasNext();) {
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(")");//$NON-NLS-1$
+			if (node.getAnonymousClassDeclaration() != null) {
+				node.getAnonymousClassDeclaration().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(CompilationUnit node) {
+			if (node.getAST().apiLevel() >= JLS9) {
+				if (node.getModule() != null) {
+					node.getModule().accept(this);
+				}
+			}
+			if (node.getPackage() != null) {
+				node.getPackage().accept(this);
+			}
+			for (Iterator it = node.imports().iterator(); it.hasNext();) {
+				ImportDeclaration d = (ImportDeclaration) it.next();
+				d.accept(this);
+			}
+			for (Iterator it = node.types().iterator(); it.hasNext();) {
+				AbstractTypeDeclaration d = (AbstractTypeDeclaration) it.next();
+				d.accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(ConditionalExpression node) {
+			node.getExpression().accept(this);
+			buffer.append(" ? ");//$NON-NLS-1$
+			node.getThenExpression().accept(this);
+			buffer.append(" : ");//$NON-NLS-1$
+			node.getElseExpression().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(ConstructorInvocation node) {
+			printIndent();
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (!node.typeArguments().isEmpty()) {
+					buffer.append("<");//$NON-NLS-1$
+					for (Iterator it = node.typeArguments().iterator(); it.hasNext();) {
+						Type t = (Type) it.next();
+						t.accept(this);
+						if (it.hasNext()) {
+							buffer.append(",");//$NON-NLS-1$
+						}
+					}
+					buffer.append(">");//$NON-NLS-1$
+				}
+			}
+			buffer.append("this(");//$NON-NLS-1$
+			for (Iterator it = node.arguments().iterator(); it.hasNext();) {
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(");\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ContinueStatement node) {
+			printIndent();
+			buffer.append("continue");//$NON-NLS-1$
+			if (node.getLabel() != null) {
+				buffer.append(" ");//$NON-NLS-1$
+				node.getLabel().accept(this);
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(CreationReference node) {
+			node.getType().accept(this);
+			visitReferenceTypeArguments(node.typeArguments());
+			buffer.append("new");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(Dimension node) {
+			List annotations = node.annotations();
+			if (annotations.size() > 0) {
+				buffer.append(' ');
+			}
+			visitAnnotationsList(annotations);
+			buffer.append("[]"); //$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(DoStatement node) {
+			printIndent();
+			buffer.append("do ");//$NON-NLS-1$
+			node.getBody().accept(this);
+			buffer.append(" while (");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			buffer.append(");\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(EmptyStatement node) {
+			printIndent();
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(EnhancedForStatement node) {
+			printIndent();
+			buffer.append("for (");//$NON-NLS-1$
+			node.getParameter().accept(this);
+			buffer.append(" : ");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			buffer.append(") ");//$NON-NLS-1$
+			node.getBody().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(EnumConstantDeclaration node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			printIndent();
+			printModifiers(node.modifiers());
+			node.getName().accept(this);
+			if (!node.arguments().isEmpty()) {
+				buffer.append("(");//$NON-NLS-1$
+				for (Iterator it = node.arguments().iterator(); it.hasNext();) {
+					Expression e = (Expression) it.next();
+					e.accept(this);
+					if (it.hasNext()) {
+						buffer.append(",");//$NON-NLS-1$
+					}
+				}
+				buffer.append(")");//$NON-NLS-1$
+			}
+			if (node.getAnonymousClassDeclaration() != null) {
+				node.getAnonymousClassDeclaration().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(EnumDeclaration node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			printIndent();
+			printModifiers(node.modifiers());
+			buffer.append("enum ");//$NON-NLS-1$
+			node.getName().accept(this);
+			buffer.append(" ");//$NON-NLS-1$
+			if (!node.superInterfaceTypes().isEmpty()) {
+				buffer.append("implements ");//$NON-NLS-1$
+				for (Iterator it = node.superInterfaceTypes().iterator(); it.hasNext();) {
+					Type t = (Type) it.next();
+					t.accept(this);
+					if (it.hasNext()) {
+						buffer.append(", ");//$NON-NLS-1$
+					}
+				}
+				buffer.append(" ");//$NON-NLS-1$
+			}
+			buffer.append("{");//$NON-NLS-1$
+			for (Iterator it = node.enumConstants().iterator(); it.hasNext();) {
+				EnumConstantDeclaration d = (EnumConstantDeclaration) it.next();
+				d.accept(this);
+				// enum constant declarations do not include punctuation
+				if (it.hasNext()) {
+					// enum constant declarations are separated by commas
+					buffer.append(", ");//$NON-NLS-1$
+				}
+			}
+			if (!node.bodyDeclarations().isEmpty()) {
+				buffer.append("; ");//$NON-NLS-1$
+				for (Iterator it = node.bodyDeclarations().iterator(); it.hasNext();) {
+					BodyDeclaration d = (BodyDeclaration) it.next();
+					d.accept(this);
+					// other body declarations include trailing punctuation
+				}
+			}
+			buffer.append("}\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ExportsDirective node) {
+			return visit(node, "exports"); //$NON-NLS-1$
+		}
+
+		@Override
+		public boolean visit(ExpressionMethodReference node) {
+			node.getExpression().accept(this);
+			visitReferenceTypeArguments(node.typeArguments());
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(ExpressionStatement node) {
+			printIndent();
+			node.getExpression().accept(this);
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@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);
+			return false;
+		}
+
+		@Override
+		public boolean visit(FieldDeclaration node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			printIndent();
+			if (node.getAST().apiLevel() == JLS2) {
+				printModifiers(node.getModifiers());
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				printModifiers(node.modifiers());
+			}
+			node.getType().accept(this);
+			buffer.append(" ");//$NON-NLS-1$
+			for (Iterator it = node.fragments().iterator(); it.hasNext();) {
+				VariableDeclarationFragment f = (VariableDeclarationFragment) it.next();
+				f.accept(this);
+				if (it.hasNext()) {
+					buffer.append(", ");//$NON-NLS-1$
+				}
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ForStatement node) {
+			printIndent();
+			buffer.append("for (");//$NON-NLS-1$
+			for (Iterator it = node.initializers().iterator(); it.hasNext();) {
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(", ");//$NON-NLS-1$
+				}
+			}
+			buffer.append("; ");//$NON-NLS-1$
+			if (node.getExpression() != null) {
+				node.getExpression().accept(this);
+			}
+			buffer.append("; ");//$NON-NLS-1$
+			for (Iterator it = node.updaters().iterator(); it.hasNext();) {
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(", ");//$NON-NLS-1$
+				}
+			}
+			buffer.append(") ");//$NON-NLS-1$
+			node.getBody().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(IfStatement node) {
+			printIndent();
+			buffer.append("if (");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			buffer.append(") ");//$NON-NLS-1$
+			node.getThenStatement().accept(this);
+			if (node.getElseStatement() != null) {
+				buffer.append(" else ");//$NON-NLS-1$
+				node.getElseStatement().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(ImportDeclaration node) {
+			printIndent();
+			buffer.append("import ");//$NON-NLS-1$
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (node.isStatic()) {
+					buffer.append("static ");//$NON-NLS-1$
+				}
+			}
+			node.getName().accept(this);
+			if (node.isOnDemand()) {
+				buffer.append(".*");//$NON-NLS-1$
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(InfixExpression node) {
+			node.getLeftOperand().accept(this);
+			buffer.append(' '); // for cases like x= i - -1; or x= i++ + ++i;
+			buffer.append(node.getOperator().toString());
+			buffer.append(' ');
+			node.getRightOperand().accept(this);
+			final List extendedOperands = node.extendedOperands();
+			if (extendedOperands.size() != 0) {
+				buffer.append(' ');
+				for (Iterator it = extendedOperands.iterator(); it.hasNext();) {
+					buffer.append(node.getOperator().toString()).append(' ');
+					Expression e = (Expression) it.next();
+					e.accept(this);
+				}
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(Initializer node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			if (node.getAST().apiLevel() == JLS2) {
+				printModifiers(node.getModifiers());
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				printModifiers(node.modifiers());
+			}
+			node.getBody().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(InstanceofExpression node) {
+			node.getLeftOperand().accept(this);
+			buffer.append(" instanceof ");//$NON-NLS-1$
+			node.getRightOperand().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(IntersectionType node) {
+			for (Iterator it = node.types().iterator(); it.hasNext();) {
+				Type t = (Type) it.next();
+				t.accept(this);
+				if (it.hasNext()) {
+					buffer.append(" & "); //$NON-NLS-1$
+				}
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(Javadoc node) {
+			printIndent();
+			buffer.append("/** ");//$NON-NLS-1$
+			for (Iterator it = node.tags().iterator(); it.hasNext();) {
+				ASTNode e = (ASTNode) it.next();
+				e.accept(this);
+			}
+			buffer.append("\n */\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(LabeledStatement node) {
+			printIndent();
+			node.getLabel().accept(this);
+			buffer.append(": ");//$NON-NLS-1$
+			node.getBody().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(LambdaExpression node) {
+			boolean hasParentheses = node.hasParentheses();
+			if (hasParentheses) {
+				buffer.append('(');
+			}
+			for (Iterator it = node.parameters().iterator(); it.hasNext();) {
+				VariableDeclaration v = (VariableDeclaration) it.next();
+				v.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			if (hasParentheses) {
+				buffer.append(')');
+			}
+			buffer.append(" -> "); //$NON-NLS-1$
+			node.getBody().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(LineComment node) {
+			buffer.append("//\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(MarkerAnnotation node) {
+			buffer.append("@");//$NON-NLS-1$
+			node.getTypeName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(MemberRef node) {
+			if (node.getQualifier() != null) {
+				node.getQualifier().accept(this);
+			}
+			buffer.append("#");//$NON-NLS-1$
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(MemberValuePair node) {
+			node.getName().accept(this);
+			buffer.append("=");//$NON-NLS-1$
+			node.getValue().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(MethodDeclaration node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			printIndent();
+			if (node.getAST().apiLevel() == JLS2) {
+				printModifiers(node.getModifiers());
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				printModifiers(node.modifiers());
+				if (!node.typeParameters().isEmpty()) {
+					buffer.append("<");//$NON-NLS-1$
+					for (Iterator it = node.typeParameters().iterator(); it.hasNext();) {
+						TypeParameter t = (TypeParameter) it.next();
+						t.accept(this);
+						if (it.hasNext()) {
+							buffer.append(",");//$NON-NLS-1$
+						}
+					}
+					buffer.append(">");//$NON-NLS-1$
+				}
+			}
+			if (!node.isConstructor()) {
+				if (node.getAST().apiLevel() == JLS2) {
+					getReturnType(node).accept(this);
+				} else {
+					if (node.getReturnType2() != null) {
+						node.getReturnType2().accept(this);
+					} else {
+						// methods really ought to have a return type
+						buffer.append("void");//$NON-NLS-1$
+					}
+				}
+				buffer.append(" ");//$NON-NLS-1$
+			}
+			node.getName().accept(this);
+			buffer.append("(");//$NON-NLS-1$
+			if (node.getAST().apiLevel() >= JLS8) {
+				Type receiverType = node.getReceiverType();
+				if (receiverType != null) {
+					receiverType.accept(this);
+					buffer.append(' ');
+					SimpleName qualifier = node.getReceiverQualifier();
+					if (qualifier != null) {
+						qualifier.accept(this);
+						buffer.append('.');
+					}
+					buffer.append("this"); //$NON-NLS-1$
+					if (node.parameters().size() > 0) {
+						buffer.append(',');
+					}
+				}
+			}
+			for (Iterator it = node.parameters().iterator(); it.hasNext();) {
+				SingleVariableDeclaration v = (SingleVariableDeclaration) it.next();
+				v.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(")");//$NON-NLS-1$
+			int size = node.getExtraDimensions();
+			if (node.getAST().apiLevel() >= JLS8) {
+				List dimensions = node.extraDimensions();
+				for (int i = 0; i < size; i++) {
+					visit((Dimension) dimensions.get(i));
+				}
+			} else {
+				for (int i = 0; i < size; i++) {
+					buffer.append("[]"); //$NON-NLS-1$
+				}
+			}
+			if (node.getAST().apiLevel() < JLS8) {
+				if (!thrownExceptions(node).isEmpty()) {
+					buffer.append(" throws ");//$NON-NLS-1$
+					for (Iterator it = thrownExceptions(node).iterator(); it.hasNext();) {
+						Name n = (Name) it.next();
+						n.accept(this);
+						if (it.hasNext()) {
+							buffer.append(", ");//$NON-NLS-1$
+						}
+					}
+					buffer.append(" ");//$NON-NLS-1$
+				}
+			} else {
+				if (!node.thrownExceptionTypes().isEmpty()) {
+					buffer.append(" throws ");//$NON-NLS-1$
+					for (Iterator it = node.thrownExceptionTypes().iterator(); it.hasNext();) {
+						Type n = (Type) it.next();
+						n.accept(this);
+						if (it.hasNext()) {
+							buffer.append(", ");//$NON-NLS-1$
+						}
+					}
+					buffer.append(" ");//$NON-NLS-1$
+				}
+			}
+			if (node.getBody() == null) {
+				buffer.append(";\n");//$NON-NLS-1$
+			} else {
+				node.getBody().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(MethodInvocation node) {
+			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()) {
+					buffer.append("<");//$NON-NLS-1$
+					for (Iterator it = node.typeArguments().iterator(); it.hasNext();) {
+						Type t = (Type) it.next();
+						t.accept(this);
+						if (it.hasNext()) {
+							buffer.append(",");//$NON-NLS-1$
+						}
+					}
+					buffer.append(">");//$NON-NLS-1$
+				}
+			}
+			node.getName().accept(this);
+			buffer.append("(");//$NON-NLS-1$
+			for (Iterator it = node.arguments().iterator(); it.hasNext();) {
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(")");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(MethodRef node) {
+			if (node.getQualifier() != null) {
+				node.getQualifier().accept(this);
+			}
+			buffer.append("#");//$NON-NLS-1$
+			node.getName().accept(this);
+			buffer.append("(");//$NON-NLS-1$
+			for (Iterator it = node.parameters().iterator(); it.hasNext();) {
+				MethodRefParameter e = (MethodRefParameter) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(")");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(MethodRefParameter node) {
+			node.getType().accept(this);
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (node.isVarargs()) {
+					buffer.append("...");//$NON-NLS-1$
+				}
+			}
+			if (node.getName() != null) {
+				buffer.append(" ");//$NON-NLS-1$
+				node.getName().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(Modifier node) {
+			buffer.append(node.getKeyword().toString());
+			return false;
+		}
+
+		@Override
+		public boolean visit(ModuleDeclaration node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			printModifiers(node.annotations());
+			if (node.isOpen()) {
+				buffer.append("open "); //$NON-NLS-1$
+			}
+			buffer.append("module"); //$NON-NLS-1$
+			buffer.append(" "); //$NON-NLS-1$
+			node.getName().accept(this);
+			buffer.append(" {\n"); //$NON-NLS-1$
+			indent++;
+			for (ModuleDirective stmt : (List<ModuleDirective>) node.moduleStatements()) {
+				stmt.accept(this);
+			}
+			indent--;
+			buffer.append("}"); //$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		/*
+		 * @see ASTVisitor#visit(ModuleModifier)
+		 *
+		 * @since 3.14
+		 */
+		public boolean visit(ModuleModifier node) {
+			buffer.append(node.getKeyword().toString());
+			return false;
+		}
+
+		private boolean visit(ModulePackageAccess node, String keyword) {
+			printIndent();
+			buffer.append(keyword);
+			buffer.append(" ");//$NON-NLS-1$
+			node.getName().accept(this);
+			printTypes(node.modules(), "to"); //$NON-NLS-1$
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(NameQualifiedType node) {
+			node.getQualifier().accept(this);
+			buffer.append('.');
+			visitTypeAnnotations(node);
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(NormalAnnotation node) {
+			buffer.append("@");//$NON-NLS-1$
+			node.getTypeName().accept(this);
+			buffer.append("(");//$NON-NLS-1$
+			for (Iterator it = node.values().iterator(); it.hasNext();) {
+				MemberValuePair p = (MemberValuePair) it.next();
+				p.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(")");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(NullLiteral node) {
+			buffer.append("null");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(NumberLiteral node) {
+			buffer.append(node.getToken());
+			return false;
+		}
+
+		@Override
+		public boolean visit(OpensDirective node) {
+			return visit(node, "opens"); //$NON-NLS-1$
+		}
+
+		@Override
+		public boolean visit(PackageDeclaration node) {
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (node.getJavadoc() != null) {
+					node.getJavadoc().accept(this);
+				}
+				for (Iterator it = node.annotations().iterator(); it.hasNext();) {
+					Annotation p = (Annotation) it.next();
+					p.accept(this);
+					buffer.append(" ");//$NON-NLS-1$
+				}
+			}
+			printIndent();
+			buffer.append("package ");//$NON-NLS-1$
+			node.getName().accept(this);
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ParameterizedType node) {
+			node.getType().accept(this);
+			buffer.append("<");//$NON-NLS-1$
+			for (Iterator it = node.typeArguments().iterator(); it.hasNext();) {
+				Type t = (Type) it.next();
+				t.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(">");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ParenthesizedExpression node) {
+			buffer.append("(");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			buffer.append(")");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(PostfixExpression node) {
+			node.getOperand().accept(this);
+			buffer.append(node.getOperator().toString());
+			return false;
+		}
+
+		@Override
+		public boolean visit(PrefixExpression node) {
+			buffer.append(node.getOperator().toString());
+			node.getOperand().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(PrimitiveType node) {
+			visitTypeAnnotations(node);
+			buffer.append(node.getPrimitiveTypeCode().toString());
+			return false;
+		}
+
+		@Override
+		public boolean visit(ProvidesDirective node) {
+			printIndent();
+			buffer.append("provides");//$NON-NLS-1$
+			buffer.append(" ");//$NON-NLS-1$
+			node.getName().accept(this);
+			printTypes(node.implementations(), "with"); //$NON-NLS-1$
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(QualifiedName node) {
+			node.getQualifier().accept(this);
+			buffer.append(".");//$NON-NLS-1$
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(QualifiedType node) {
+			node.getQualifier().accept(this);
+			buffer.append(".");//$NON-NLS-1$
+			visitTypeAnnotations(node);
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(RequiresDirective node) {
+			printIndent();
+			buffer.append("requires");//$NON-NLS-1$
+			buffer.append(" ");//$NON-NLS-1$
+			printModifiers(node.modifiers());
+			node.getName().accept(this);
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(ReturnStatement node) {
+			printIndent();
+			buffer.append("return");//$NON-NLS-1$
+			if (node.getExpression() != null) {
+				buffer.append(" ");//$NON-NLS-1$
+				node.getExpression().accept(this);
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(SimpleName node) {
+			IBinding binding = node.resolveBinding();
+			if (!isLocalBinding(binding)) {
+				if (binding instanceof IVariableBinding) {
+					IVariableBinding vb = ((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(newVarName);
+					}
+					return false;
+				}
+			}
+
+			buffer.append(node.getIdentifier());
+			return false;
+		}
+
+		@Override
+		public boolean visit(SimpleType node) {
+			visitTypeAnnotations(node);
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(SingleMemberAnnotation node) {
+			buffer.append("@");//$NON-NLS-1$
+			node.getTypeName().accept(this);
+			buffer.append("(");//$NON-NLS-1$
+			node.getValue().accept(this);
+			buffer.append(")");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(SingleVariableDeclaration node) {
+			printIndent();
+			if (node.getAST().apiLevel() == JLS2) {
+				printModifiers(node.getModifiers());
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				printModifiers(node.modifiers());
+			}
+			node.getType().accept(this);
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (node.isVarargs()) {
+					if (node.getAST().apiLevel() >= JLS8) {
+						List annotations = node.varargsAnnotations();
+						if (annotations.size() > 0) {
+							buffer.append(' ');
+						}
+						visitAnnotationsList(annotations);
+					}
+					buffer.append("...");//$NON-NLS-1$
+				}
+			}
+			buffer.append(" ");//$NON-NLS-1$
+			node.getName().accept(this);
+			int size = node.getExtraDimensions();
+			if (node.getAST().apiLevel() >= JLS8) {
+				List dimensions = node.extraDimensions();
+				for (int i = 0; i < size; i++) {
+					visit((Dimension) dimensions.get(i));
+				}
+			} else {
+				for (int i = 0; i < size; i++) {
+					buffer.append("[]"); //$NON-NLS-1$
+				}
+			}
+			if (node.getInitializer() != null) {
+				buffer.append("=");//$NON-NLS-1$
+				node.getInitializer().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(StringLiteral node) {
+			buffer.append(node.getEscapedValue());
+			return false;
+		}
+
+		@Override
+		public boolean visit(SuperConstructorInvocation node) {
+			printIndent();
+			if (node.getExpression() != null) {
+				node.getExpression().accept(this);
+				buffer.append(".");//$NON-NLS-1$
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (!node.typeArguments().isEmpty()) {
+					buffer.append("<");//$NON-NLS-1$
+					for (Iterator it = node.typeArguments().iterator(); it.hasNext();) {
+						Type t = (Type) it.next();
+						t.accept(this);
+						if (it.hasNext()) {
+							buffer.append(",");//$NON-NLS-1$
+						}
+					}
+					buffer.append(">");//$NON-NLS-1$
+				}
+			}
+			buffer.append("super(");//$NON-NLS-1$
+			for (Iterator it = node.arguments().iterator(); it.hasNext();) {
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(");\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(SuperFieldAccess node) {
+			if (node.getQualifier() != null) {
+				node.getQualifier().accept(this);
+				buffer.append(".");//$NON-NLS-1$
+			}
+			buffer.append("super.");//$NON-NLS-1$
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(SuperMethodInvocation node) {
+			if (node.getQualifier() != null) {
+				node.getQualifier().accept(this);
+				buffer.append(".");//$NON-NLS-1$
+			}
+			buffer.append("super.");//$NON-NLS-1$
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (!node.typeArguments().isEmpty()) {
+					buffer.append("<");//$NON-NLS-1$
+					for (Iterator it = node.typeArguments().iterator(); it.hasNext();) {
+						Type t = (Type) it.next();
+						t.accept(this);
+						if (it.hasNext()) {
+							buffer.append(",");//$NON-NLS-1$
+						}
+					}
+					buffer.append(">");//$NON-NLS-1$
+				}
+			}
+			node.getName().accept(this);
+			buffer.append("(");//$NON-NLS-1$
+			for (Iterator it = node.arguments().iterator(); it.hasNext();) {
+				Expression e = (Expression) it.next();
+				e.accept(this);
+				if (it.hasNext()) {
+					buffer.append(",");//$NON-NLS-1$
+				}
+			}
+			buffer.append(")");//$NON-NLS-1$
+			return false;
+		}
+
+		/*
+		 * @see ASTVisitor#visit(SuperMethodReference)
+		 *
+		 * @since 3.10
+		 */
+		@Override
+		public boolean visit(SuperMethodReference node) {
+			if (node.getQualifier() != null) {
+				node.getQualifier().accept(this);
+				buffer.append('.');
+			}
+			buffer.append("super");//$NON-NLS-1$
+			visitReferenceTypeArguments(node.typeArguments());
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(SwitchCase node) {
+			if ((node.getAST().isPreviewEnabled())) {
+				if (node.isDefault()) {
+					buffer.append("default");//$NON-NLS-1$
+					buffer.append(node.isSwitchLabeledRule() ? " ->" : ":");//$NON-NLS-1$ //$NON-NLS-2$
+				} else {
+					buffer.append("case ");//$NON-NLS-1$
+					for (Iterator it = node.expressions().iterator(); it.hasNext();) {
+						Expression t = (Expression) it.next();
+						t.accept(this);
+						buffer.append(it.hasNext() ? ", " : //$NON-NLS-1$
+								node.isSwitchLabeledRule() ? " ->" : ":");//$NON-NLS-1$ //$NON-NLS-2$
+					}
+				}
+			} else {
+				if (node.isDefault()) {
+					buffer.append("default :\n");//$NON-NLS-1$
+				} else {
+					buffer.append("case ");//$NON-NLS-1$
+					getSwitchExpression(node).accept(this);
+					buffer.append(":\n");//$NON-NLS-1$
+				}
+			}
+			indent++; // decremented in visit(SwitchStatement)
+			return false;
+		}
+
+		/**
+		 * @deprecated
+		 */
+		@Deprecated
+		private Expression getSwitchExpression(SwitchCase node) {
+			return node.getExpression();
+		}
+
+		private void visitSwitchNode(ASTNode node) {
+			buffer.append("switch (");//$NON-NLS-1$
+			if (node instanceof SwitchExpression) {
+				((SwitchExpression) node).getExpression().accept(this);
+			} else if (node instanceof SwitchStatement) {
+				((SwitchStatement) node).getExpression().accept(this);
+			}
+			buffer.append(") ");//$NON-NLS-1$
+			buffer.append("{\n");//$NON-NLS-1$
+			indent++;
+			if (node instanceof SwitchExpression) {
+				for (Iterator it = ((SwitchExpression) node).statements().iterator(); it.hasNext();) {
+					Statement s = (Statement) it.next();
+					s.accept(this);
+					indent--; // incremented in visit(SwitchCase)
+				}
+			} else if (node instanceof SwitchStatement) {
+				for (Iterator it = ((SwitchStatement) node).statements().iterator(); it.hasNext();) {
+					Statement s = (Statement) it.next();
+					s.accept(this);
+					indent--; // incremented in visit(SwitchCase)
+				}
+			}
+			indent--;
+			printIndent();
+			buffer.append("}\n");//$NON-NLS-1$
+
+		}
+
+		@Override
+		public boolean visit(SwitchExpression node) {
+			visitSwitchNode(node);
+			return false;
+		}
+
+		@Override
+		public boolean visit(SwitchStatement node) {
+			visitSwitchNode(node);
+			return false;
+		}
+
+		@Override
+		public boolean visit(SynchronizedStatement node) {
+			buffer.append("synchronized (");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			buffer.append(") ");//$NON-NLS-1$
+			node.getBody().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(TagElement node) {
+			return false;
+		}
+
+		@Override
+		public boolean visit(TextBlock node) {
+			buffer.append(node.getEscapedValue());
+			return false;
+		}
+
+		@Override
+		public boolean visit(TextElement node) {
+			buffer.append(node.getText());
+			return false;
+		}
+
+		@Override
+		public boolean visit(ThisExpression node) {
+			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;
+		}
+
+		@Override
+		public boolean visit(ThrowStatement node) {
+			printIndent();
+			buffer.append("throw ");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(TryStatement node) {
+			printIndent();
+			buffer.append("try ");//$NON-NLS-1$
+			if (node.getAST().apiLevel() >= JLS4) {
+				List resources = node.resources();
+				if (!resources.isEmpty()) {
+					buffer.append('(');
+					for (Iterator it = resources.iterator(); it.hasNext();) {
+						Expression variable = (Expression) it.next();
+						variable.accept(this);
+						if (it.hasNext()) {
+							buffer.append(';');
+						}
+					}
+					buffer.append(')');
+				}
+			}
+			node.getBody().accept(this);
+			buffer.append(" ");//$NON-NLS-1$
+			for (Iterator it = node.catchClauses().iterator(); it.hasNext();) {
+				CatchClause cc = (CatchClause) it.next();
+				cc.accept(this);
+			}
+			if (node.getFinally() != null) {
+				buffer.append(" finally ");//$NON-NLS-1$
+				node.getFinally().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(TypeDeclaration node) {
+			if (node.getJavadoc() != null) {
+				node.getJavadoc().accept(this);
+			}
+			if (node.getAST().apiLevel() == JLS2) {
+				printModifiers(node.getModifiers());
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				printModifiers(node.modifiers());
+			}
+			buffer.append(node.isInterface() ? "interface " : "class ");//$NON-NLS-2$//$NON-NLS-1$
+			node.getName().accept(this);
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (!node.typeParameters().isEmpty()) {
+					buffer.append("<");//$NON-NLS-1$
+					for (Iterator it = node.typeParameters().iterator(); it.hasNext();) {
+						TypeParameter t = (TypeParameter) it.next();
+						t.accept(this);
+						if (it.hasNext()) {
+							buffer.append(",");//$NON-NLS-1$
+						}
+					}
+					buffer.append(">");//$NON-NLS-1$
+				}
+			}
+			buffer.append(" ");//$NON-NLS-1$
+			if (node.getAST().apiLevel() == JLS2) {
+				if (getSuperclass(node) != null) {
+					buffer.append("extends ");//$NON-NLS-1$
+					getSuperclass(node).accept(this);
+					buffer.append(" ");//$NON-NLS-1$
+				}
+				if (!superInterfaces(node).isEmpty()) {
+					buffer.append(node.isInterface() ? "extends " : "implements ");//$NON-NLS-2$//$NON-NLS-1$
+					for (Iterator it = superInterfaces(node).iterator(); it.hasNext();) {
+						Name n = (Name) it.next();
+						n.accept(this);
+						if (it.hasNext()) {
+							buffer.append(", ");//$NON-NLS-1$
+						}
+					}
+					buffer.append(" ");//$NON-NLS-1$
+				}
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				if (node.getSuperclassType() != null) {
+					buffer.append("extends ");//$NON-NLS-1$
+					node.getSuperclassType().accept(this);
+					buffer.append(" ");//$NON-NLS-1$
+				}
+				if (!node.superInterfaceTypes().isEmpty()) {
+					buffer.append(node.isInterface() ? "extends " : "implements ");//$NON-NLS-2$//$NON-NLS-1$
+					for (Iterator it = node.superInterfaceTypes().iterator(); it.hasNext();) {
+						Type t = (Type) it.next();
+						t.accept(this);
+						if (it.hasNext()) {
+							buffer.append(", ");//$NON-NLS-1$
+						}
+					}
+					buffer.append(" ");//$NON-NLS-1$
+				}
+			}
+			buffer.append("{\n");//$NON-NLS-1$
+			indent++;
+			for (Iterator it = node.bodyDeclarations().iterator(); it.hasNext();) {
+				BodyDeclaration d = (BodyDeclaration) it.next();
+				d.accept(this);
+			}
+			indent--;
+			printIndent();
+			buffer.append("}\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(TypeDeclarationStatement node) {
+			if (node.getAST().apiLevel() == JLS2) {
+				getTypeDeclaration(node).accept(this);
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				node.getDeclaration().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(TypeLiteral node) {
+			node.getType().accept(this);
+			buffer.append(".class");//$NON-NLS-1$
+			return false;
+		}
+
+		/*
+		 * @see ASTVisitor#visit(TypeMethodReference)
+		 *
+		 * @since 3.10
+		 */
+		@Override
+		public boolean visit(TypeMethodReference node) {
+			node.getType().accept(this);
+			visitReferenceTypeArguments(node.typeArguments());
+			node.getName().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(TypeParameter node) {
+			if (node.getAST().apiLevel() >= JLS8) {
+				printModifiers(node.modifiers());
+			}
+			node.getName().accept(this);
+			if (!node.typeBounds().isEmpty()) {
+				buffer.append(" extends ");//$NON-NLS-1$
+				for (Iterator it = node.typeBounds().iterator(); it.hasNext();) {
+					Type t = (Type) it.next();
+					t.accept(this);
+					if (it.hasNext()) {
+						buffer.append(" & ");//$NON-NLS-1$
+					}
+				}
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(UnionType node) {
+			for (Iterator it = node.types().iterator(); it.hasNext();) {
+				Type t = (Type) it.next();
+				t.accept(this);
+				if (it.hasNext()) {
+					buffer.append('|');
+				}
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(UsesDirective node) {
+			printIndent();
+			buffer.append("uses");//$NON-NLS-1$
+			buffer.append(" ");//$NON-NLS-1$
+			node.getName().accept(this);
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(VariableDeclarationExpression node) {
+			if (node.getAST().apiLevel() == JLS2) {
+				printModifiers(node.getModifiers());
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				printModifiers(node.modifiers());
+			}
+			node.getType().accept(this);
+			buffer.append(" ");//$NON-NLS-1$
+			for (Iterator it = node.fragments().iterator(); it.hasNext();) {
+				VariableDeclarationFragment f = (VariableDeclarationFragment) it.next();
+				f.accept(this);
+				if (it.hasNext()) {
+					buffer.append(", ");//$NON-NLS-1$
+				}
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(VariableDeclarationFragment node) {
+			addLocalBinding(node.resolveBinding(), node.getName().getIdentifier());
+
+			buffer.append(node.getName().getIdentifier());
+			int size = node.getExtraDimensions();
+			if (node.getAST().apiLevel() >= JLS8) {
+				List dimensions = node.extraDimensions();
+				for (int i = 0; i < size; i++) {
+					visit((Dimension) dimensions.get(i));
+				}
+			} else {
+				for (int i = 0; i < size; i++) {
+					buffer.append("[]");//$NON-NLS-1$
+				}
+			}
+			if (node.getInitializer() != null) {
+				buffer.append("=");//$NON-NLS-1$
+				node.getInitializer().accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(VariableDeclarationStatement node) {
+			printIndent();
+			if (node.getAST().apiLevel() == JLS2) {
+				printModifiers(node.getModifiers());
+			}
+			if (node.getAST().apiLevel() >= JLS3) {
+				printModifiers(node.modifiers());
+			}
+			node.getType().accept(this);
+			buffer.append(" ");//$NON-NLS-1$
+			for (Iterator it = node.fragments().iterator(); it.hasNext();) {
+				VariableDeclarationFragment f = (VariableDeclarationFragment) it.next();
+				f.accept(this);
+				if (it.hasNext()) {
+					buffer.append(", ");//$NON-NLS-1$
+				}
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		@Override
+		public boolean visit(WhileStatement node) {
+			printIndent();
+			buffer.append("while (");//$NON-NLS-1$
+			node.getExpression().accept(this);
+			buffer.append(") ");//$NON-NLS-1$
+			node.getBody().accept(this);
+			return false;
+		}
+
+		@Override
+		public boolean visit(WildcardType node) {
+			visitTypeAnnotations(node);
+			buffer.append("?");//$NON-NLS-1$
+			Type bound = node.getBound();
+			if (bound != null) {
+				if (node.isUpperBound()) {
+					buffer.append(" extends ");//$NON-NLS-1$
+				} else {
+					buffer.append(" super ");//$NON-NLS-1$
+				}
+				bound.accept(this);
+			}
+			return false;
+		}
+
+		@Override
+		public boolean visit(YieldStatement node) {
+			if ((node.getAST().isPreviewEnabled()) && node.isImplicit() && node.getExpression() == null) {
+				return false;
+			}
+			printIndent();
+			buffer.append("yield"); //$NON-NLS-1$
+			if (node.getExpression() != null) {
+				buffer.append(" ");//$NON-NLS-1$
+				node.getExpression().accept(this);
+			}
+			buffer.append(";\n");//$NON-NLS-1$
+			return false;
+		}
+
+		/**
+		 * @deprecated
+		 */
+		@Deprecated
+		private void visitComponentType(ArrayType node) {
+			node.getComponentType().accept(this);
+		}
+	}
+
+}
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 c54ca84..617191f 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
@@ -345,8 +345,7 @@
 				}
 			}
 			// Adding outer class variables to inner class scope
-			for (int i = 0; i < innerClassFields.length; i++) {
-				IVariable var = innerClassFields[i];
+			for (IVariable var : innerClassFields) {
 				if (var instanceof IJavaVariable && var.getName().startsWith(ANONYMOUS_VAR_PREFIX)) {
 					String name = var.getName().substring(ANONYMOUS_VAR_PREFIX.length());
 					if (!names.contains(name)) {
@@ -629,7 +628,7 @@
 		}
 
 		ASTInstructionCompiler visitor = new ASTInstructionCompiler(
-				mapper.getSnippetStart(), snippet);
+				mapper.getSnippetStart(), snippet, getJavaProject());
 		unit.accept(visitor);
 
 		return visitor.getInstructions();
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 6941a01..762a4dd 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
@@ -26,11 +26,17 @@
 
 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;
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.core.dom.*;
 import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
+import org.eclipse.jdt.internal.debug.eval.ExpressionBinder;
+import org.eclipse.jdt.internal.debug.eval.RemoteEvaluator;
+import org.eclipse.jdt.internal.debug.eval.RemoteEvaluatorBuilder;
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.AndAssignmentOperator;
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.AndOperator;
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.ArrayAllocation;
@@ -90,6 +96,7 @@
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushType;
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.RemainderAssignmentOperator;
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.RemainderOperator;
+import org.eclipse.jdt.internal.debug.eval.ast.instructions.RemoteOperator;
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.ReturnInstruction;
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.RightShiftAssignmentOperator;
 import org.eclipse.jdt.internal.debug.eval.ast.instructions.RightShiftOperator;
@@ -156,14 +163,17 @@
 	// internal index used to create unique variable name
 	private int fUniqueIdIndex = 0;
 
+	private IJavaProject fJavaProject;
+
 	/**
 	 * Create a new AST instruction compiler
 	 */
-	public ASTInstructionCompiler(int startPosition, String snippet) {
+	public ASTInstructionCompiler(int startPosition, String snippet, IJavaProject javaProject) {
 		fStartPosition = startPosition;
 		fInstructions = new InstructionSequence(snippet);
 		fStack = new Stack<>();
 		fCompleteInstructions = new ArrayList<>();
+		fJavaProject = javaProject;
 	}
 
 	/**
@@ -226,6 +236,7 @@
 				((CompoundInstruction) instruction).setEnd(fCounter);
 			}
 			fInstructions.add(instruction);
+			//System.out.println("Added: " + instruction.toString()); //$NON-NLS-1$
 			verbose("Add " + instruction.toString()); //$NON-NLS-1$
 		}
 	}
@@ -2154,8 +2165,16 @@
 		if (!isActive()) {
 			return true;
 		}
-		setHasError(true);
-		addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression);
+		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 | DebugException e) {
+			addErrorMessage(e.getMessage());
+			setHasError(true);
+		}
 		return false;
 	}
 
@@ -2354,8 +2373,62 @@
 		if (!isActive()) {
 			return true;
 		}
-		setHasError(true);
-		addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression);
+
+		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 | DebugException e) {
+			addErrorMessage(e.getMessage());
+			setHasError(true);
+		}
+
+		return false;
+	}
+
+	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();
+				push(new PushLocalVariable(variableId));
+				storeInstruction();
+			}
+
+			@Override
+			public void bindThis(ITypeBinding typeBinding, String asVariableName) {
+				push(new PushThis(getEnclosingLevel(node, typeBinding)));
+				storeInstruction();
+			}
+
+		}, getEnclosingClass(node), isStaticContext(node), false);
+		return builder;
+	}
+
+	private ITypeBinding getEnclosingClass(ASTNode node) {
+		while (node != null) {
+			if (node instanceof MethodDeclaration) {
+				return ((MethodDeclaration) node).resolveBinding().getDeclaringClass();
+			}
+			if (node instanceof TypeDeclaration) {
+				return ((TypeDeclaration) node).resolveBinding();
+			}
+			node = node.getParent();
+		}
+		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;
 	}
 
@@ -2865,10 +2938,20 @@
 		if (!isActive()) {
 			return true;
 		}
-		setHasError(true);
-		addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Lambda_expressions_cannot_be_used_in_an_evaluation_expression);
+
+		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 | DebugException e) {
+			addErrorMessage(e.getMessage());
+			setHasError(true);
+		}
 		return false;
 	}
+
 	/*
 	 * (non-Javadoc)
 	 *
@@ -4064,17 +4147,15 @@
 			storeInstruction(); // jump
 		}
 
-		for (Iterator<slot> iter = jumpsStatements.iterator(); iter.hasNext();) {
-			currentslot = iter.next();
-			for (Iterator<ConditionalJump> iterator = currentslot.jumps.iterator(); iterator.hasNext();) {
-				ConditionalJump condJump = iterator.next();
+		for (slot slot : jumpsStatements) {
+			for (ConditionalJump condJump : slot.jumps) {
 				condJump.setOffset((fCounter - fInstructions.indexOf(condJump)) - 1);
 			}
 			if (currentslot.stmts != null) {
 				push(new Pop(0));
 				storeInstruction(); // pop
-				for (Iterator<Statement> iterator = currentslot.stmts.iterator(); iterator.hasNext();) {
-					iterator.next().accept(this);
+				for (Statement statement : currentslot.stmts) {
+					statement.accept(this);
 				}
 			}
 		}
@@ -4084,8 +4165,8 @@
 			jumpDefault.setOffset((fCounter - fInstructions.indexOf(jumpDefault)) - 1);
 			push(new Pop(0));
 			storeInstruction(); // pop
-			for (Iterator<Statement> iterator = statementsDefault.iterator(); iterator.hasNext();) {
-				iterator.next().accept(this);
+			for (Statement statement : statementsDefault) {
+				statement.accept(this);
 			}
 		} else if(jumpEnd != null){
 			jumpEnd.setOffset((fCounter - fInstructions.indexOf(jumpEnd)) - 1);
@@ -4251,8 +4332,18 @@
 		if (!isActive()) {
 			return true;
 		}
-		setHasError(true);
-		addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression);
+
+		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 | 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/BinaryBasedSourceGenerator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/BinaryBasedSourceGenerator.java
index 2a17e34..a91e07d 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/BinaryBasedSourceGenerator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/BinaryBasedSourceGenerator.java
@@ -152,9 +152,7 @@
 
 		Field thisField = null;
 
-		List<Field> fields = referenceType.visibleFields();
-		for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext();) {
-			Field field = iterator.next();
+		for (Field field : referenceType.visibleFields()) {
 			if (field.name().startsWith("this$")) { //$NON-NLS-1$
 				thisField = field;
 				break;
@@ -330,39 +328,33 @@
 			source.append(buffer);
 		}
 
-		List<Field> fields = referenceType.fields();
-		for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext();) {
-			Field field = iterator.next();
+		for (Field field : referenceType.fields()) {
 			if (!field.name().startsWith("this$")) { //$NON-NLS-1$
 				source.append(buildFieldDeclaration(field));
 			}
 		}
 
-		List<Method> methods = referenceType.methods();
-		for (Iterator<Method> iterator = methods.iterator(); iterator.hasNext();) {
-			Method method = iterator.next();
+		for (Method method : referenceType.methods()) {
 			if (!method.isConstructor() && !method.isStaticInitializer()
-					&& !method.isBridge()) {
+				&& !method.isBridge()) {
 				source.append(buildMethodDeclaration(method));
 			}
 		}
 
 		List<ReferenceType> nestedTypes = referenceType.nestedTypes();
 		if (nestedTypeName == null) {
-			for (Iterator<ReferenceType> iterator = nestedTypes.iterator(); iterator.hasNext();) {
-				ReferenceType nestedType = iterator.next();
+			for (ReferenceType nestedType : nestedTypes) {
 				if (isADirectInnerType(typeName, nestedType.name())) {
 					source.append(buildTypeDeclaration(nestedType, null, null,
-							true));
+						true));
 				}
 			}
 		} else {
-			for (Iterator<ReferenceType> iterator = nestedTypes.iterator(); iterator.hasNext();) {
-				ReferenceType nestedType = iterator.next();
+			for (ReferenceType nestedType : nestedTypes) {
 				if (!nestedTypeName.equals(nestedType.name())
-						&& isADirectInnerType(typeName, nestedType.name())) {
+					&& isADirectInnerType(typeName, nestedType.name())) {
 					source.append(buildTypeDeclaration(nestedType, null, null,
-							true));
+						true));
 				}
 			}
 		}
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
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 0685c85..3146f70 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
@@ -14,7 +14,6 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.eval.ast.engine;
 
-import java.util.Iterator;
 import java.util.Map;
 
 import org.eclipse.core.runtime.CoreException;
@@ -315,8 +314,7 @@
 	 */
 	public static Map<String, String> getCompilerOptions(IJavaProject project) {
 		Map<String, String> options = project.getOptions(true);
-		for (Iterator<String> iter = options.keySet().iterator(); iter.hasNext();) {
-			String key = iter.next();
+		for (String key : options.keySet()) {
 			String value = options.get(key);
 			if (JavaCore.ERROR.equals(value) || JavaCore.WARNING.equals(value) || JavaCore.INFO.equals(value)) {
 				options.put(key, JavaCore.IGNORE);
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/Interpreter.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/Interpreter.java
index d9d1460..51377b8 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/Interpreter.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/Interpreter.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -121,6 +121,10 @@
 			value.disableCollection();
 			fPermStorage.add(value);
 		} catch (CoreException e) {
+			// don't worry about GC if the VM has terminated
+			if ((e.getStatus().getException() instanceof VMDisconnectedException)) {
+				return;
+			}
 			JDIDebugPlugin.log(e);
 		}
 	}
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
index cffaf52..4b89662 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
@@ -427,8 +427,7 @@
 			fSnippetStartPosition += source.length();
 			source.append(buffer.toString());
 		}
-		for (Iterator<BodyDeclaration> iterator = list.iterator(); iterator.hasNext();) {
-			BodyDeclaration bodyDeclaration = iterator.next();
+		for (BodyDeclaration bodyDeclaration : list) {
 			if (bodyDeclaration instanceof FieldDeclaration) {
 				source.append(buildFieldDeclaration((FieldDeclaration) bodyDeclaration));
 			} else if (bodyDeclaration instanceof MethodDeclaration) {
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionSequence.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionSequence.java
index a80f7a0..3de2379 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionSequence.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionSequence.java
@@ -14,7 +14,6 @@
 package org.eclipse.jdt.internal.debug.eval.ast.instructions;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 
 import org.eclipse.core.runtime.CoreException;
@@ -79,8 +78,8 @@
 	public Message[] getErrors() {
 		Message[] messages = new Message[fErrors.size()];
 		int i = 0;
-		for (Iterator<String> iter = fErrors.iterator(); iter.hasNext();) {
-			messages[i++] = new Message(iter.next(), -1);
+		for (String errorMsg : fErrors) {
+			messages[i++] = new Message(errorMsg, -1);
 		}
 		return messages;
 	}
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionsEvaluationMessages.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionsEvaluationMessages.java
index b97de5f..9d92c16 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionsEvaluationMessages.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionsEvaluationMessages.java
@@ -29,6 +29,7 @@
 	public static String AssignmentOperator_operator_1;
 	public static String Cast_ClassCastException__Cannot_cast__0__as__1___1;
 	public static String Cast_cast_3;
+	public static String Run_Remote_1;
 	public static String ConditionalJump_conditional_jump_1;
 	public static String Constructor_constructor__1;
 	public static String LocalVariableCreation_create_local_variable__0___1___1;
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionsEvaluationMessages.properties b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionsEvaluationMessages.properties
index ed26ba7..15f1260 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionsEvaluationMessages.properties
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/InstructionsEvaluationMessages.properties
@@ -24,6 +24,7 @@
 Cast_cast_3=cast
 ConditionalJump_conditional_jump_1=conditional jump
 Constructor_constructor__1=constructor
+Run_Remote_1=run remote code
 LocalVariableCreation_create_local_variable__0___1___1=Create local variable {1} {0}
 DivideAssignmentOperator_operator_1='/=\' operator
 DivideOperator_Divide_by_zero_1=Divide by zero
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/RemoteOperator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/RemoteOperator.java
new file mode 100644
index 0000000..c47f065
--- /dev/null
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/RemoteOperator.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2020, Jesper Steen Møller and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Jesper Steen Møller - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.eval.ast.instructions;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.debug.core.IJavaValue;
+import org.eclipse.jdt.internal.debug.eval.RemoteEvaluator;
+
+/**
+ * Invokes a method on a object in a class injected into the debug target. The arguments are on the stack in reverse order, followed by the type.
+ * Pushes the result onto the stack
+ */
+public class RemoteOperator extends CompoundInstruction {
+
+	private RemoteEvaluator fEvaluator;
+	private String fSignature;
+
+	public RemoteOperator(String body, int start, RemoteEvaluator evaluator) {
+		super(start);
+		fSignature = body;
+		fEvaluator = evaluator;
+	}
+
+	@Override
+	public void execute() throws CoreException {
+		int variableCount = fEvaluator.getVariableCount();
+		IJavaValue[] args = new IJavaValue[variableCount];
+		// args are in reverse order
+		for (int i = variableCount - 1; i >= 0; i--) {
+			args[i] = popValue();
+		}
+		IJavaValue result = fEvaluator.evaluate(this.getContext().getThread(), args);
+		push(result);
+	}
+
+	@Override
+	public String toString() {
+		return InstructionsEvaluationMessages.Run_Remote_1
+				+ fSignature;
+	}
+
+}
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/RuntimeSignature.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/RuntimeSignature.java
index 02e5833..59d1493 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/RuntimeSignature.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/instructions/RuntimeSignature.java
@@ -247,8 +247,8 @@
 
 			// parameter names
 			if(parameterNames != null) {
-				for (int i = 0; i < parameterNames.length; i++) {
-					resultLength += parameterNames[i].length + 1;
+				for (char[] parameterName : parameterNames) {
+					resultLength += parameterName.length + 1;
 					// parameter name + space
 				}
 			}
diff --git a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/MethodImpl.java b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/MethodImpl.java
index 90ead5b..835cb9e 100644
--- a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/MethodImpl.java
+++ b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/MethodImpl.java
@@ -274,8 +274,9 @@
 		Iterator<LocalVariable> iter = variables().iterator();
 		while (iter.hasNext()) {
 			LocalVariable var = iter.next();
-			if (var.isArgument())
+			if (var.isArgument()) {
 				result.add(var);
+			}
 		}
 		fArguments = result;
 		return fArguments;
@@ -290,10 +291,9 @@
 		if (fArgumentTypeNames != null) {
 			return fArgumentTypeNames;
 		}
-		List<String> argumentTypeSignatures = argumentTypeSignatures();
 		List<String> result = new ArrayList<>();
-		for (Iterator<String> iter = argumentTypeSignatures.iterator(); iter.hasNext();) {
-			result.add(TypeImpl.signatureToName(iter.next()));
+		for (String signature : argumentTypeSignatures()) {
+			result.add(TypeImpl.signatureToName(signature));
 		}
 
 		fArgumentTypeNames = result;
@@ -394,15 +394,17 @@
 	 */
 	@Override
 	public int compareTo(Method method) {
-		if (method == null || !method.getClass().equals(this.getClass()))
+		if (method == null || !method.getClass().equals(this.getClass())) {
 			throw new ClassCastException(
 					JDIMessages.MethodImpl_Can__t_compare_method_to_given_object_6);
+		}
 
 		// See if declaring types are the same, if not return comparison between
 		// declaring types.
 		Method type2 = method;
-		if (!declaringType().equals(type2.declaringType()))
+		if (!declaringType().equals(type2.declaringType())) {
 			return declaringType().compareTo(type2.declaringType());
+		}
 
 		// Return comparison of position within declaring type.
 		int index1 = declaringType().methods().indexOf(this);
@@ -699,8 +701,9 @@
 		// See Location.
 		ReferenceTypeImpl referenceType = ReferenceTypeImpl.readWithTypeTag(
 				target, in);
-		if (referenceType == null)
+		if (referenceType == null) {
 			return null;
+		}
 
 		JdwpMethodID ID = new JdwpMethodID(vmImpl);
 		if (target.fVerboseWriter != null) {
@@ -877,16 +880,15 @@
 	 */
 	protected List<Location> javaStratumLocationsOfLines(List<Integer> javaLines)	throws AbsentInformationException {
 		Set<Long> tmpLocations = new TreeSet<>();
-		for (Iterator<Integer> iter = javaLines.iterator(); iter.hasNext();) {
-			Integer key = iter.next();
+		for (Integer key : javaLines) {
 			List<Long> indexes = javaStratumLineToCodeIndexes(key.intValue());
 			if (indexes != null) {
 				tmpLocations.addAll(indexes);
 			}
 		}
 		List<Location> locations = new ArrayList<>();
-		for (Iterator<Long> iter = tmpLocations.iterator(); iter.hasNext();) {
-			long index = iter.next().longValue();
+		for (Long location : tmpLocations) {
+			long index = location.longValue();
 			int position = Arrays.binarySearch(fCodeIndexTable, index);
 			if(position < 0) {
 				//https://bugs.eclipse.org/bugs/show_bug.cgi?id=388172
diff --git a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ReferenceTypeImpl.java b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ReferenceTypeImpl.java
index 77ac963..7d2b1ee 100644
--- a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ReferenceTypeImpl.java
+++ b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ReferenceTypeImpl.java
@@ -158,8 +158,7 @@
 			List<Integer> list = new ArrayList<>();
 			List<int[]> outputLines = fLineInfo.get(new Integer(lineNumber));
 			if (outputLines != null) {
-				for (Iterator<int[]> iter = outputLines.iterator(); iter.hasNext();) {
-					int[] info = iter.next();
+				for (int[] info : outputLines) {
 					int outputLineNumber = info[0];
 					int length = info[1];
 					if (length == 0) {
@@ -282,8 +281,7 @@
 				throws AbsentInformationException {
 			FileInfo fileInfo = null;
 			// get the FileInfo object
-			for (Iterator<FileInfo> iter = fFileInfos.iterator(); iter.hasNext();) {
-				FileInfo element = iter.next();
+			for (FileInfo element : fFileInfos) {
 				if (element.fFileId == lineFileId) {
 					fileInfo = element;
 				}
@@ -333,8 +331,7 @@
 		 *            the source name to search.
 		 */
 		public FileInfo getFileInfo(String sourceName) {
-			for (Iterator<FileInfo> iter = fFileInfos.iterator(); iter.hasNext();) {
-				FileInfo fileInfo = iter.next();
+			for (FileInfo fileInfo : fFileInfos) {
 				if (fileInfo.fFileName.equals(sourceName)) {
 					return fileInfo;
 				}
@@ -583,8 +580,8 @@
 		List<Method> visibleMethods = new ArrayList<>();
 
 		// The methods of its own (own methods() command).
-		for (Iterator<Method> iter = methods().iterator(); iter.hasNext();) {
-			MethodImpl method = (MethodImpl) iter.next();
+		for (Method m : methods()) {
+			MethodImpl method = (MethodImpl) m;
 			namesAndSignatures.add(method.name() + method.signature());
 			visibleMethods.add(method);
 		}
@@ -1542,8 +1539,8 @@
 				throw new AbsentInformationException(
 						JDIMessages.ReferenceTypeImpl_30);
 			}
-			for (Iterator<FileInfo> iter = stratum.fFileInfos.iterator(); iter.hasNext();) {
-				list.add(iter.next().fFileName);
+			for (FileInfo fileInfo : stratum.fFileInfos) {
+				list.add(fileInfo.fFileName);
 			}
 			return list;
 		}
@@ -1564,8 +1561,7 @@
 		Stratum stratum = getStratum(stratumId);
 		if (stratum != null) {
 			// return the source paths defined for this stratum in the SMAP.
-			for (Iterator<FileInfo> iter = stratum.fFileInfos.iterator(); iter.hasNext();) {
-				FileInfo fileInfo = iter.next();
+			for (FileInfo fileInfo : stratum.fFileInfos) {
 				String path = fileInfo.fAbsoluteFileName;
 				if (path == null) {
 					path = getPath(fileInfo.fFileName);
@@ -1906,8 +1902,7 @@
 				fileId = lineInfos.get(0)[0];
 			}
 		}
-		for (Iterator<FileInfo> iter = stratum.fFileInfos.iterator(); iter.hasNext();) {
-			FileInfo fileInfo = iter.next();
+		for (FileInfo fileInfo : stratum.fFileInfos) {
 			if (fileInfo.fFileId == fileId) {
 				return fileInfo;
 			}
@@ -2088,8 +2083,7 @@
 				for (int i = 0, length = javaStratumLineNumberTable.length; i < length; i++) {
 					List<int[]> lineInfos = stratum.getInputLineInfos(javaStratumLineNumberTable[i]);
 					if (lineInfos != null) {
-						for (Iterator<int[]> iter = lineInfos.iterator(); iter.hasNext();) {
-							int[] lineInfo = iter.next();
+						for (int[] lineInfo : lineInfos) {
 							if (lineInfo[0] == fileId) {
 								if (!lineInfo.equals(lineInfoTable[lastIndex])) {
 									lineInfoTable[i] = lineInfo;
diff --git a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ValueImpl.java b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ValueImpl.java
index fc2137f..daadc80 100644
--- a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ValueImpl.java
+++ b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ValueImpl.java
@@ -140,8 +140,9 @@
 			throws IOException {
 		JdwpObjectID nullID = new JdwpObjectID(target.virtualMachineImpl());
 		nullID.write(out);
-		if (target.fVerboseWriter != null)
+		if (target.fVerboseWriter != null) {
 			target.fVerboseWriter.println("objectReference", nullID.value()); //$NON-NLS-1$
+		}
 	}
 
 	/**
@@ -269,9 +270,8 @@
 						return;
 					}
 				} else {
-					List<InterfaceType> interfaces = ((ClassType) valueType).allInterfaces();
-					for (Iterator<InterfaceType> iter = interfaces.iterator(); iter.hasNext();) {
-						if (checkInterfaceType(iter.next(),
+					for (InterfaceType interfaceType : ((ClassType) valueType).allInterfaces()) {
+						if (checkInterfaceType(interfaceType,
 								(InterfaceType) type)) {
 							return;
 						}
@@ -291,9 +291,8 @@
 		if (valueType.equals(type)) {
 			return true;
 		}
-		List<InterfaceType> superInterfaces = valueType.superinterfaces();
-		for (Iterator<InterfaceType> iter = superInterfaces.iterator(); iter.hasNext();) {
-			if (checkInterfaceType(iter.next(), type)) {
+		for (InterfaceType interfaceType : valueType.superinterfaces()) {
+			if (checkInterfaceType(interfaceType, type)) {
 				return true;
 			}
 		}
diff --git a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/request/EventRequestImpl.java b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/request/EventRequestImpl.java
index 20a606e..347c2fc 100644
--- a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/request/EventRequestImpl.java
+++ b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/request/EventRequestImpl.java
@@ -613,9 +613,7 @@
 		// communicating with SUN's VM.
 		// It seems to expect them 'the wrong way around'.
 		if (fThreadStepFilters != null) {
-			for (int i = 0; i < fThreadStepFilters.size(); i++) {
-				ThreadStepFilter filter = fThreadStepFilters
-						.get(i);
+			for (ThreadStepFilter filter : fThreadStepFilters) {
 				writeByte(MODIF_KIND_STEP,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
 				filter.fThread.write(this, outData);
@@ -626,17 +624,15 @@
 			}
 		}
 		if (fFieldFilters != null) {
-			for (int i = 0; i < fFieldFilters.size(); i++) {
+			for (FieldImpl field : fFieldFilters) {
 				writeByte(MODIF_KIND_FIELDONLY,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-				fFieldFilters.get(i).writeWithReferenceType(this,
+				field.writeWithReferenceType(this,
 						outData);
 			}
 		}
 		if (fExceptionFilters != null) {
-			for (int i = 0; i < fExceptionFilters.size(); i++) {
-				ExceptionFilter filter = fExceptionFilters
-						.get(i);
+			for (ExceptionFilter filter : fExceptionFilters) {
 				writeByte(MODIF_KIND_EXCEPTIONONLY,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
 				if (filter.fException != null)
@@ -649,66 +645,66 @@
 			}
 		}
 		if (fLocationFilters != null) {
-			for (int i = 0; i < fLocationFilters.size(); i++) {
+			for (LocationImpl locationFilter : fLocationFilters) {
 				writeByte(MODIF_KIND_LOCATIONONLY,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-				fLocationFilters.get(i).write(this, outData);
+				locationFilter.write(this, outData);
 			}
 		}
 		if (fClassExclusionFilters != null) {
-			for (int i = 0; i < fClassExclusionFilters.size(); i++) {
+			for (String classExclusionFilter : fClassExclusionFilters) {
 				writeByte(MODIF_KIND_CLASSEXCLUDE,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-				writeString(fClassExclusionFilters.get(i),
+				writeString(classExclusionFilter,
 						"class excl. filter", outData); //$NON-NLS-1$
 			}
 		}
 		if (fClassFilters != null) {
-			for (int i = 0; i < fClassFilters.size(); i++) {
+			for (String classFilter : fClassFilters) {
 				writeByte(MODIF_KIND_CLASSMATCH,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-				writeString(fClassFilters.get(i),
+				writeString(classFilter,
 						"class filter", outData); //$NON-NLS-1$
 			}
 		}
 		if (fClassFilterRefs != null) {
-			for (int i = 0; i < fClassFilterRefs.size(); i++) {
+			for (ReferenceType classFilterRef : fClassFilterRefs) {
 				writeByte(MODIF_KIND_CLASSONLY,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-				((ReferenceTypeImpl) fClassFilterRefs.get(i)).write(this,
+				((ReferenceTypeImpl) classFilterRef).write(this,
 						outData);
 			}
 		}
 		if (fThreadFilters != null) {
-			for (int i = 0; i < fThreadFilters.size(); i++) {
+			for (ThreadReference threadFilter : fThreadFilters) {
 				writeByte(MODIF_KIND_THREADONLY,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-				((ThreadReferenceImpl) fThreadFilters.get(i)).write(this,
+				((ThreadReferenceImpl) threadFilter).write(this,
 						outData);
 			}
 		}
 		if (fCountFilters != null) {
-			for (int i = 0; i < fCountFilters.size(); i++) {
+			for (Integer countFilter : fCountFilters) {
 				writeByte(MODIF_KIND_COUNT,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-				writeInt(fCountFilters.get(i).intValue(),
+				writeInt(countFilter.intValue(),
 						"count filter", outData); //$NON-NLS-1$
 			}
 		}
 		if (fInstanceFilters != null) {
-			for (int i = 0; i < fInstanceFilters.size(); i++) {
+			for (ObjectReference instanceFilter : fInstanceFilters) {
 				writeByte(MODIF_KIND_INSTANCE,
 						"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-				((ObjectReferenceImpl) fInstanceFilters.get(i)).write(this,
+				((ObjectReferenceImpl) instanceFilter).write(this,
 						outData);
 			}
 		}
 		if (fSourceNameFilters != null) {
 			if (supportsSourceNameFilters()) {
-				for (int i = 0; i < fSourceNameFilters.size(); i++) {
+				for (String sourceNameFilter : fSourceNameFilters) {
 					writeByte(MODIF_KIND_SOURCE_NAME_FILTER,
 							"modifier", modifierKindMap(), outData); //$NON-NLS-1$
-					writeString(fSourceNameFilters.get(i),
+					writeString(sourceNameFilter,
 							"modifier", outData); //$NON-NLS-1$
 				}
 			}
@@ -732,13 +728,11 @@
 		if (fStepSizeMap != null)
 			return;
 
-		java.lang.reflect.Field[] fields = EventRequestImpl.class
-				.getDeclaredFields();
 		fStepSizeMap = new HashMap<>();
 		fStepDepthMap = new HashMap<>();
 		fSuspendPolicyMap = new HashMap<>();
 		fModifierKindMap = new HashMap<>();
-		for (Field field : fields) {
+		for (Field field : EventRequestImpl.class.getDeclaredFields()) {
 			if ((field.getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0
 					|| (field.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0
 					|| (field.getModifiers() & java.lang.reflect.Modifier.FINAL) == 0)
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/JDIDebugModel.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/JDIDebugModel.java
index d60d947..97aadd6 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/JDIDebugModel.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/debug/core/JDIDebugModel.java
@@ -847,15 +847,14 @@
 		String markerType = JavaLineBreakpoint.getMarkerType();
 		IBreakpointManager manager = DebugPlugin.getDefault()
 				.getBreakpointManager();
-		IBreakpoint[] breakpoints = manager.getBreakpoints(modelId);
-		for (int i = 0; i < breakpoints.length; i++) {
-			if (!(breakpoints[i] instanceof IJavaLineBreakpoint)) {
+		for (IBreakpoint bp : manager.getBreakpoints(modelId)) {
+			if (!(bp instanceof IJavaLineBreakpoint)) {
 				continue;
 			}
-			IJavaLineBreakpoint breakpoint = (IJavaLineBreakpoint) breakpoints[i];
+			IJavaLineBreakpoint breakpoint = (IJavaLineBreakpoint) bp;
 			IMarker marker = breakpoint.getMarker();
 			if (marker != null && marker.exists()
-					&& marker.getType().equals(markerType)) {
+				&& marker.getType().equals(markerType)) {
 				String breakpointTypeName = breakpoint.getTypeName();
 				if (JavaDebugUtils.typeNamesEqual(breakpointTypeName, typeName) || (breakpointTypeName != null && breakpointTypeName.startsWith(typeName + '$'))) {
 					if (breakpoint.getLineNumber() == lineNumber) {
@@ -893,19 +892,18 @@
 		String markerType = JavaLineBreakpoint.getMarkerType();
 		IBreakpointManager manager = DebugPlugin.getDefault()
 				.getBreakpointManager();
-		IBreakpoint[] breakpoints = manager.getBreakpoints(modelId);
-		for (int i = 0; i < breakpoints.length; i++) {
-			if (!(breakpoints[i] instanceof IJavaLineBreakpoint)) {
+		for (IBreakpoint bp : manager.getBreakpoints(modelId)) {
+			if (!(bp instanceof IJavaLineBreakpoint)) {
 				continue;
 			}
-			IJavaLineBreakpoint breakpoint = (IJavaLineBreakpoint) breakpoints[i];
+			IJavaLineBreakpoint breakpoint = (IJavaLineBreakpoint) bp;
 			IMarker marker = breakpoint.getMarker();
 			if (marker != null && marker.exists()
-					&& marker.getType().equals(markerType)) {
+				&& marker.getType().equals(markerType)) {
 				String breakpointTypeName = breakpoint.getTypeName();
 				if ((JavaDebugUtils.typeNamesEqual(breakpointTypeName, typeName) || (breakpointTypeName != null && breakpointTypeName.startsWith(typeName + '$')))
-						&& breakpoint.getLineNumber() == lineNumber
-						&& resource.equals(marker.getResource())) {
+					&& breakpoint.getLineNumber() == lineNumber
+					&& resource.equals(marker.getResource())) {
 					return breakpoint;
 				}
 			}
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaStratumLineBreakpoint.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaStratumLineBreakpoint.java
index 729248d..d03882f 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaStratumLineBreakpoint.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/JavaStratumLineBreakpoint.java
@@ -247,8 +247,8 @@
 	}
 
 	private boolean containsMatch(List<String> strings, String key) {
-		for (Iterator<String> iter = strings.iterator(); iter.hasNext();) {
-			if (iter.next().equals(key)) {
+		for (String string : strings) {
+			if (string.equals(key)) {
 				return true;
 			}
 		}
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/logicalstructures/JavaLogicalStructures.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/logicalstructures/JavaLogicalStructures.java
index 6dc8765..5687af9 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/logicalstructures/JavaLogicalStructures.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/logicalstructures/JavaLogicalStructures.java
@@ -122,9 +122,8 @@
 	 */
 	private static void addAllLogicalStructures(
 			List<JavaLogicalStructure> pluginContributedJavaLogicalStructures) {
-		for (Iterator<JavaLogicalStructure> iter = pluginContributedJavaLogicalStructures.iterator(); iter
-				.hasNext();) {
-			addLogicalStructure(iter.next());
+		for (JavaLogicalStructure logicalStructure : pluginContributedJavaLogicalStructures) {
+			addLogicalStructure(logicalStructure);
 		}
 	}
 
@@ -208,17 +207,14 @@
 	 */
 	public static void saveUserDefinedJavaLogicalStructures() {
 		StringBuilder logicalStructuresString = new StringBuilder();
-		for (Iterator<JavaLogicalStructure> iter = fUserDefinedJavaLogicalStructures.iterator(); iter
-				.hasNext();) {
-			JavaLogicalStructure logicalStructure = iter
-					.next();
+		for (JavaLogicalStructure logicalStructure : fUserDefinedJavaLogicalStructures) {
 			logicalStructuresString.append(
-					logicalStructure.getQualifiedTypeName()).append('\0');
+				logicalStructure.getQualifiedTypeName()).append('\0');
 			logicalStructuresString.append(logicalStructure.getDescription())
-					.append('\0');
+				.append('\0');
 			logicalStructuresString.append(
-					logicalStructure.isSubtypes() ? IS_SUBTYPE_TRUE
-							: IS_SUBTYPE_FALSE).append('\0');
+				logicalStructure.isSubtypes() ? IS_SUBTYPE_TRUE
+					: IS_SUBTYPE_FALSE).append('\0');
 			String value = logicalStructure.getValue();
 			if (value != null) {
 				logicalStructuresString.append(value);
@@ -249,13 +245,11 @@
 		JavaLogicalStructure[] logicalStructures = new JavaLogicalStructure[fPluginContributedJavaLogicalStructures
 				.size() + fUserDefinedJavaLogicalStructures.size()];
 		int i = 0;
-		for (Iterator<JavaLogicalStructure> iter = fPluginContributedJavaLogicalStructures.iterator(); iter
-				.hasNext();) {
-			logicalStructures[i++] = iter.next();
+		for (JavaLogicalStructure logicalStructure : fPluginContributedJavaLogicalStructures) {
+			logicalStructures[i++] = logicalStructure;
 		}
-		for (Iterator<JavaLogicalStructure> iter = fUserDefinedJavaLogicalStructures.iterator(); iter
-				.hasNext();) {
-			logicalStructures[i++] = iter.next();
+		for (JavaLogicalStructure logicalStructure : fUserDefinedJavaLogicalStructures) {
+			logicalStructures[i++] = logicalStructure;
 		}
 		return logicalStructures;
 	}
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIObjectValue.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIObjectValue.java
index 307c462..15b0b91 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIObjectValue.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIObjectValue.java
@@ -453,10 +453,9 @@
 	public IJavaThread[] getWaitingThreads() throws DebugException {
 		List<JDIThread> waiting = new ArrayList<>();
 		try {
-			List<ThreadReference> threads = getUnderlyingObject().waitingThreads();
 			JDIDebugTarget debugTarget = (JDIDebugTarget) getDebugTarget();
-			for (Iterator<ThreadReference> iter = threads.iterator(); iter.hasNext();) {
-				JDIThread jdiThread = debugTarget.findThread(iter.next());
+			for (ThreadReference threadReference : getUnderlyingObject().waitingThreads()) {
+				JDIThread jdiThread = debugTarget.findThread(threadReference);
 				if (jdiThread != null) {
 					waiting.add(jdiThread);
 				}
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
index ece2b06..4301a39 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java
@@ -3029,9 +3029,8 @@
 					return true;
 				}
 				if(!orig) {
-					IStepFilter[] contributedFilters = DebugPlugin.getStepFilters(JDIDebugPlugin.getUniqueIdentifier());
-					for (int i = 0; i < contributedFilters.length; i++) {
-						if (contributedFilters[i].isFiltered(method)) {
+					for (IStepFilter contributedFilter : DebugPlugin.getStepFilters(JDIDebugPlugin.getUniqueIdentifier())) {
+						if (contributedFilter.isFiltered(method)) {
 							return true;
 						}
 					}
diff --git a/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleBuilder.java b/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleBuilder.java
index 9f98ba2..415f0a0 100644
--- a/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleBuilder.java
+++ b/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleBuilder.java
@@ -39,7 +39,7 @@
 
 public class BundleBuilder implements BundleAttributes {
 
-	private List<Process> fProcesses= new ArrayList<Process>();
+	private List<Process> fProcesses= new ArrayList<>();
 	private BundleDescription fBundleDescription;
 
 
diff --git a/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleDescription.java b/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleDescription.java
index 1dc2b80..fa7ddd1 100644
--- a/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleDescription.java
+++ b/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleDescription.java
@@ -42,14 +42,14 @@
 	private static final String ICON= "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Resources/GenericApp.icns"; //$NON-NLS-1$
 	private static  Set<String> RUN_MODE;
 	{
-		RUN_MODE = new HashSet<String>();
+		RUN_MODE = new HashSet<>();
 		RUN_MODE.add(ILaunchManager.RUN_MODE);
 	}
 
 	private ListenerList<IPropertyChangeListener> fListeners= new ListenerList<>();
 	private Properties fProperties= new Properties();
-	private List<ResourceInfo> fClassPath= new ArrayList<ResourceInfo>();
-	private List<ResourceInfo> fResources= new ArrayList<ResourceInfo>();
+	private List<ResourceInfo> fClassPath= new ArrayList<>();
+	private List<ResourceInfo> fResources= new ArrayList<>();
 	Properties fProperties2= new Properties();
 
 
diff --git a/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleWizardPage1.java b/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleWizardPage1.java
index c655071..cc2138a 100644
--- a/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleWizardPage1.java
+++ b/org.eclipse.jdt.launching.ui.macosx/src/org/eclipse/jdt/internal/ui/macbundler/BundleWizardPage1.java
@@ -197,7 +197,7 @@
 	// private stuff
 
 	private void collectLaunchConfigs() {
-		ArrayList<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>();
+		ArrayList<ILaunchConfiguration> configs = new ArrayList<>();
 		ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
 		ILaunchConfigurationType type = manager.getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
 		try {
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/environments/ExecutionEnvironment.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/environments/ExecutionEnvironment.java
index c9f6844..1b05fa8 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/environments/ExecutionEnvironment.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/environments/ExecutionEnvironment.java
@@ -478,6 +478,13 @@
 			}
 
 		}
+		// For below Java 9 EE, compiler release option should be disabled by default
+		String property = profile.getProperty(JavaCore.COMPILER_COMPLIANCE);
+		if (property != null) {
+			if (JavaCore.compareJavaVersions(property, JavaCore.VERSION_9) < 0) {
+				profile.setProperty(JavaCore.COMPILER_RELEASE, JavaCore.DISABLED);
+			}
+		}
 		return profile;
 	}