Bug 525860 - [PY4J] Accurate return values

Extended code to try to be more accurate with return values.

Change-Id: Ifd43348f7f406ab05a9f8f449f0df80b753fe80d
Signed-off-by: Martin Kloesch <martin.kloesch@gmail.com>
diff --git a/plugins/org.eclipse.ease.lang.python.py4j/pysrc/ease_py4j_main.py b/plugins/org.eclipse.ease.lang.python.py4j/pysrc/ease_py4j_main.py
index 3094fd0..bb66b2f 100644
--- a/plugins/org.eclipse.ease.lang.python.py4j/pysrc/ease_py4j_main.py
+++ b/plugins/org.eclipse.ease.lang.python.py4j/pysrc/ease_py4j_main.py
@@ -1,4 +1,4 @@
-#################################################################################
+###############################################################################
 # Copyright (c) 2016 Kichwa Coders and others.
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License v1.0
@@ -7,18 +7,31 @@
 #
 # Contributors:
 #     Jonah Graham (Kichwa Coders) - initial API and implementation
-#################################################################################
+###############################################################################
 
 import code
 import os
 import py4j
 from py4j.clientserver import ClientServer, JavaParameters, PythonParameters
-from py4j.java_collections import MapConverter
-from py4j.java_gateway import JavaObject
+from py4j.java_collections import MapConverter, ListConverter, SetConverter
+from py4j.java_gateway import JavaObject, JavaClass
 from py4j.protocol import Py4JJavaError, get_command_part
 import sys
 import threading
 import __main__
+import ast
+try:
+    from six import integer_types
+    from six import string_types
+except ImportError:
+    import sys
+    if sys.version_info.major == 2:
+        integer_types = (int, long)
+        string_types = (basestring, )
+    else:
+        integer_types = (int,)
+        string_types = (str,)
+
 
 # To ease some debugging of the py4j engine itself it is useful to turn logging on,
 # uncomment the following lines for one way to do that
@@ -26,6 +39,73 @@
 # logger = logging.getLogger("py4j")
 # logger.setLevel(logging.DEBUG)
 # logger.addHandler(logging.StreamHandler())
+def convert_value(value, gw):
+    '''
+    Tries to convert the given value using different strategies.
+
+    Used because default py4j converters have some issues with e.g.
+    nested collections.
+
+    :param value:    Value to be converted.
+    :param gw:       py4j gateway necessary for converters.
+    '''
+    # None will be mapped to Java null
+    if value is None:
+        return value
+
+    # Integers can be send directly
+    if isinstance(value, integer_types):
+        return value
+
+    # Strings can be send directly
+    if isinstance(value, string_types):
+        return value
+
+    # Recursively check collections
+    if isinstance(value, dict):
+        return MapConverter().convert({
+            convert_value(k, gw): convert_value(v, gw)
+            for k, v in value.items()
+        }, gw)
+    if isinstance(value, list):
+        return ListConverter().convert([
+            convert_value(v, gw) for v in value
+        ], gw)
+    if isinstance(value, set):
+        return SetConverter().convert({
+            convert_value(v, gw) for v in value
+        }, gw)
+
+    # Try registered converters
+    for converter in gw.converters:
+        if converter.can_convert(value):
+            try:
+                return converter.convert(value, gw)
+            except Exception:
+                # TODO: Actually find out what this might throw
+                pass
+
+    # Issues with marshalling Java class objects
+    if isinstance(value, JavaClass):
+        return repr(value)
+
+    # Check if we have a Java object
+    if isinstance(value, py4j.java_gateway.JavaObject):
+        return value
+
+    # Check if we implement a Java interface
+    if hasattr(value, 'Java') and hasattr(getattr(value, 'Java'), 'implements'):
+        if hasattr(value, '_get_obj_id'):
+            if callable(value._get_obj_id):
+                return value
+        else:
+            # Issue with invalid implementations
+            if hasattr(value, 'toString'):
+                return value
+
+    # Last resort use string representation
+    return repr(value)
+
 
 class EaseInteractiveConsole(code.InteractiveConsole):
     '''
@@ -35,15 +115,42 @@
     def __init__(self, engine, *args, **kwargs):
         code.InteractiveConsole.__init__(self, *args, **kwargs)
         self.engine = engine
+
     def write(self, data):
         # Python 3 write the whole error in one write, Python 2 does multiple writes
         if self.engine.except_data is None:
             self.engine.except_data = [data]
         else:
             self.engine.except_data.append(data)
+
     def runcode(self, code):
         try:
-            exec(code, self.locals)
+            # If we have compiled code we cannot use AST
+            if isinstance(code, string_types):
+                # Parse code input
+                tree = ast.parse(code)
+
+                # Check if we have multiline statement
+                if len(tree.body) > 1:
+                    module = ast.Module(tree.body[:-1])
+                    compiled = compile(module, '<...>', 'exec')
+                    exec(compiled, self.locals)
+
+                # Check if at least one line given
+                if len(tree.body):
+                    if isinstance(tree.body[-1], ast.Expr):
+                        # Only expressions can be evaluated
+                        expression = ast.Expression(tree.body[-1].value)
+                        compiled = compile(expression, '<...>', 'eval')
+                        result = eval(compiled, self.locals)
+                        return result, False
+                    else:
+                        module = ast.Module([tree.body[-1]])
+                        compiled = compile(module, '<...>', 'exec')
+                        exec(compiled, self.locals)
+            else:
+                exec(code, self.locals)
+
         except SystemExit:
             raise
         except Py4JJavaError as e:
@@ -54,52 +161,48 @@
             else:
                 # No java exception here, fallback to normal case
                 self.showtraceback()
-        except:
+        except Exception:
             # create information that will end up in a
             # ScriptExecutionException
             self.showtraceback()
 
+
+# Sentinel object for no result (different than None)
+NO_RESULT = object()
 class InteractiveReturn(object):
     '''
     Instance of Java's IInteractiveReturn.
     This class encapsulates the return state from the
     ScriptEngineExecute.executeInteractive() method
     '''
-    def __init__(self, gateway_client, display_data=None, except_data=None):
+    def __init__(self, gateway_client, display_data=None, except_data=None, result=NO_RESULT):
         self.gateway_client = gateway_client
         self.display_data = display_data
         self.except_data = except_data
+        self.result = result
+
     def getException(self):
         data = self.except_data
         if data is None:
             return None
         if isinstance(data, JavaObject):
-            return data;
-        return "".join(data)
-    def getResult(self):
-        data = self.display_data
-
-        try:
-            # test if py4j understands this type
-            get_command_part(data, dict())
-            # py4j knows how to convert this to Java,
-            # just return it as is
             return data
-        except:
-            pass
+        return "".join(data)
 
-        # try registered converters
-        for converter in self.gateway_client.converters:
-            if converter.can_convert(data):
-                return converter.convert(data, self.gateway_client)
+    def getResult(self):
+        # Check if we did not receive result
+        if self.result is NO_RESULT:
+            data = self.display_data
+        else:
+            data = self.result
 
-        # data cannot be represented in Java, return a string representation
-        # instead
-        return repr(data)
+        # Use conversion just to be sure
+        return convert_value(data, self.gateway_client)
 
     class Java:
         implements = ['org.eclipse.ease.lang.python.py4j.internal.IInteractiveReturn']
 
+
 class ScriptEngineExecute(object):
     '''
     Instance of Java's IPythonSideEngine.
@@ -135,7 +238,16 @@
     def executeCommon(self, code_text, code_exec):
         self.display_data = None
         self.except_data = None
-        needMore = code_exec(code_text)
+        execution_result = code_exec(code_text)
+
+        # Check if we received a tuple, meaning that we are in script mode 
+        if isinstance(execution_result, tuple) and len(execution_result) == 2:
+            result, needMore = execution_result
+        else:
+            # Set result to NO_RESULT to distinguish from None result
+            result = NO_RESULT
+            needMore = execution_result
+
         if needMore:
             # TODO, need to handle this with prompts, this message
             # is a workaround
@@ -145,7 +257,7 @@
             except_data = self.except_data
             self.display_data = None
             self.except_data = None
-            return InteractiveReturn(self.gateway._gateway_client, display_data=display_data, except_data=except_data)
+            return InteractiveReturn(self.gateway._gateway_client, display_data=display_data, except_data=except_data, result=result)
 
     def executeScript(self, code_text, filename=None):
         # TODO: Handle filename
@@ -155,15 +267,17 @@
         return self.executeCommon(code_text, self.interp.push)
 
     def internalGetVariable(self, name):
-        return repr(self.locals.get(name))
+        return convert_value(self.locals.get(name), self.gateway._gateway_client)
 
     def internalGetVariables(self):
-        locals_repr = dict()
-        for k, v in self.locals.items():
-            if not k.startswith("__"):
-                locals_repr[k] = repr(v)
-        converted = MapConverter().convert(locals_repr, self.gateway._gateway_client)
-        return converted
+        # Filter out data we do not want
+        filtered = {
+            k: convert_value(v, self.gateway._gateway_client)
+            for k, v in self.locals.items()
+            if not k.startswith('__')
+        }
+
+        return MapConverter().convert(filtered, self.gateway._gateway_client)
 
     def internalHasVariable(self, name):
         return name in self.locals
@@ -227,5 +341,6 @@
     gateway.shutdown_callback_server()
     gateway.shutdown()
 
+
 if __name__ == '__main__':
     main(sys.argv)
diff --git a/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ModeTestBase.java b/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ModeTestBase.java
new file mode 100644
index 0000000..ef1749f
--- /dev/null
+++ b/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ModeTestBase.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kloesch and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Kloesch - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.ease.lang.python.py4j;
+
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.ease.IScriptable;
+import org.eclipse.ease.ScriptResult;
+import org.junit.Test;
+
+/**
+ * @author Kloesch
+ *
+ */
+public abstract class ModeTestBase extends Py4JEngineTestBase {
+	abstract protected ScriptResult executeCode(String code) throws Exception;
+
+	abstract protected void executeCode(String code, Object result) throws Exception;
+
+	@Test
+	public void pythonInteger() throws Exception {
+		executeCode("40 + 2", 42);
+	}
+
+	@Test
+	public void pythonString() throws Exception {
+		executeCode("'42'", "42");
+	}
+
+	@Test
+	public void javaInteger() throws Exception {
+		executeCode("java.lang.Integer(42)", 42);
+	}
+
+	@Test
+	public void javaString() throws Exception {
+		executeCode("java.lang.String('42')", "42");
+	}
+
+	@Test
+	public void pythonList() throws Exception {
+		executeCode("[1,2,3]", Arrays.asList(1, 2, 3));
+	}
+
+	@Test
+	public void pythonSet() throws Exception {
+		final Set<Object> javaSet = new HashSet<>();
+		javaSet.add(new Integer(1));
+		javaSet.add(new Integer(2));
+		javaSet.add(new Integer(3));
+		executeCode("{1,2,2,3}", javaSet);
+	}
+
+	@Test
+	public void pythonDict() throws Exception {
+		final Map<String, Object> javaMap = new HashMap<>();
+		javaMap.put("a", "b");
+		executeCode("{'a': 'b'}", javaMap);
+	}
+
+	@Test
+	public void createJavaType() throws Exception {
+		executeCode("java.io.File('/')", new java.io.File("/"));
+	}
+
+	@Test
+	public void createEclipseClass() throws Exception {
+		executeCode("org.eclipse.core.runtime.Path('/')", new org.eclipse.core.runtime.Path("/"));
+	}
+
+	@Test
+	public void createPythonObject() throws Exception {
+		final ScriptResult result = executeCode("object()", false);
+		assertNotNull(result);
+		assertNull(result.getException());
+		assertNotNull(result.getResult());
+		assertTrue(result.getResult() instanceof String);
+		assertThat((String) result.getResult(), startsWith("<object object at "));
+	}
+
+	@Test
+	public void implementJavaInterface() throws Exception {
+		final StringBuilder sb = new StringBuilder();
+		sb.append("class Foo:\n");
+		sb.append("\tdef getSourceCode(self):\n");
+		sb.append("\t\treturn None\n");
+		sb.append("\tdef toString(self):\n");
+		sb.append("\t\treturn java.lang.String(repr(self))\n");
+		sb.append("\tclass Java:\n");
+		sb.append("\t\timplements = ['org.eclipse.ease.IScriptable']\n");
+		executeCode(sb.toString(), false);
+		final ScriptResult result = executeCode("Foo()");
+		assertNotNull(result);
+		assertNull(result.getException());
+		assertNotNull(result.getResult());
+		assertTrue(result.getResult() instanceof IScriptable);
+	}
+
+	@Test
+	public void testMultiple() throws Exception {
+		javaInteger();
+		javaString();
+		createJavaType();
+		createEclipseClass();
+		javaInteger();
+		javaString();
+	}
+}
diff --git a/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/Py4JEngineTest.java b/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/Py4JEngineTest.java
index 027d139..906707b 100644
--- a/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/Py4JEngineTest.java
+++ b/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/Py4JEngineTest.java
@@ -24,17 +24,22 @@
 		push("def a(i):", true);
 		push(" return i + 1", true);
 		push("", false);
+
 		// define b function script mode
 		assertResultIsNone(executeCode("def b(i):\n\treturn i + 2", false));
 
 		// try to use each function from the other mode
-		assertEquals("11", printExpression("a(10)"));
-		assertEquals("12", printExpression("b(10)"));
+		final ScriptResult resultScript_a = executeCode("a(20)", false);
+		assertNull(resultScript_a.getException());
+		assertEquals(21, resultScript_a.getResult());
+		final ScriptResult resultScript_b = executeCode("b(10)", false);
+		assertNull(resultScript_b.getException());
+		assertEquals(12, resultScript_b.getResult());
 
-		ScriptResult result21 = executeCode("a(20)", true);
+		final ScriptResult result21 = executeCode("a(20)", true);
 		assertNull(result21.getException());
 		assertEquals(21, result21.getResult());
-		ScriptResult result22 = executeCode("b(20)", true);
+		final ScriptResult result22 = executeCode("b(20)", true);
 		assertNull(result22.getException());
 		assertEquals(22, result22.getResult());
 	}
diff --git a/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ScriptModeEngineTest.java b/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ScriptModeEngineTest.java
index f3ad338..b32a213 100644
--- a/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ScriptModeEngineTest.java
+++ b/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ScriptModeEngineTest.java
@@ -13,8 +13,8 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.startsWith;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 
@@ -23,61 +23,26 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
-public class ScriptModeEngineTest extends Py4JEngineTestBase {
+public class ScriptModeEngineTest extends ModeTestBase {
 
+	@Override
 	protected ScriptResult executeCode(String code) throws Exception {
 		return super.executeCode(code, false);
 	}
 
-	@Test
-	public void pythonInteger() throws Exception {
-		assertEquals("42", printExpression("40 + 2"));
-	}
-
-	@Test
-	public void pythonString() throws Exception {
-		assertEquals("42", printExpression("'42'"));
-	}
-
-	@Test
-	public void javaInteger() throws Exception {
-		assertEquals("42", printExpression("java.lang.Integer(42)"));
-	}
-
-	@Test
-	public void javaString() throws Exception {
-		assertEquals("42", printExpression("java.lang.String('42')"));
-	}
-
-	@Test
-	public void createJavaType() throws Exception {
-		assertEquals(new java.io.File("/").toString(), printExpression("java.io.File('/')"));
-	}
-
-	@Test
-	public void createEclipseClass() throws Exception {
-		assertEquals(new org.eclipse.core.runtime.Path("/").toString(), printExpression("org.eclipse.core.runtime.Path('/')"));
-	}
-
-	@Test
-	public void createPythonObject() throws Exception {
-		assertThat(printExpression("object()"), startsWith("<object object at "));
-	}
-
-	@Test
-	public void testMultiple() throws Exception {
-		javaInteger();
-		javaString();
-		createJavaType();
-		createEclipseClass();
-		javaInteger();
-		javaString();
+	@Override
+	protected void executeCode(String code, Object target) throws Exception {
+		final ScriptResult result = super.executeCode(code, false);
+		assertNotNull(result);
+		assertNull(result.getException());
+		assertNotNull(result.getResult());
+		assertEquals(target, result.getResult());
 	}
 
 	@Test
 	@Ignore("Disabled until Bug 493677 is resolved")
 	public void callExit() throws Exception {
-		ScriptResult result = executeCode("print_('this should be output', False)\nexit()\nprint_('this should not appear')");
+		final ScriptResult result = executeCode("print_('this should be output', False)\nexit()\nprint_('this should not appear')");
 		assertResultIsNone(result);
 		assertEquals("this should be output", fOutputStream.getAndClearOutput());
 	}
@@ -89,7 +54,7 @@
 
 	@Test
 	public void incompleteStatement() throws Exception {
-		ScriptResult result = executeCode("def a():");
+		final ScriptResult result = executeCode("def a():");
 		assertThat(result.getException(), instanceOf(ScriptExecutionException.class));
 		assertThat(fErrorStream.getAndClearOutput(), containsString("SyntaxError"));
 		assertNull(result.getResult());
@@ -109,8 +74,7 @@
 
 	@Test
 	public void multiLineStatement() throws Exception {
-		assertResultIsNone(executeCode("def a():\n\treturn 42"));
-		assertEquals("42", printExpression("a()"));
+		executeCode("def a():\n\treturn 42\na()", 42);
 	}
 
 	@Test
diff --git a/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ShellModeEngineTest.java b/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ShellModeEngineTest.java
index 5d6c0e6..91d078f 100644
--- a/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ShellModeEngineTest.java
+++ b/tests/org.eclipse.ease.lang.python.py4j.test/src/org/eclipse/ease/lang/python/py4j/ShellModeEngineTest.java
@@ -13,112 +13,36 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.startsWith;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
 
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
 import org.eclipse.ease.ScriptExecutionException;
 import org.eclipse.ease.ScriptResult;
 import org.junit.Ignore;
 import org.junit.Test;
 
-public class ShellModeEngineTest extends Py4JEngineTestBase {
+public class ShellModeEngineTest extends ModeTestBase {
 
+	@Override
 	protected ScriptResult executeCode(String code) throws Exception {
 		return super.executeCode(code, true);
 	}
 
-	@Test
-	public void simpleScriptCode() throws Exception {
-		ScriptResult result = executeCode("40 + 2");
+	@Override
+	protected void executeCode(String code, Object target) throws Exception {
+		final ScriptResult result = super.executeCode(code, true);
+		assertNotNull(result);
 		assertNull(result.getException());
-		assertEquals(42, result.getResult());
-	}
-
-	@Test
-	public void createAutoConvertedJavaInteger() throws Exception {
-		ScriptResult result = executeCode("java.lang.Integer(42)");
-		assertNull(result.getException());
-		assertEquals(42, result.getResult());
-	}
-
-	@Test
-	public void createAutoConvertedJavaString() throws Exception {
-		ScriptResult result = executeCode("java.lang.String('42')");
-		assertNull(result.getException());
-		assertEquals("42", result.getResult());
-	}
-
-	@Test
-	public void createList() throws Exception {
-		ScriptResult result = executeCode("[1, 2, 3]");
-		assertNull(result.getException());
-		assertEquals(Arrays.asList(1, 2, 3), result.getResult());
-	}
-
-	@Test
-	public void createMap() throws Exception {
-		ScriptResult result = executeCode("dict(a=1, b=2, c=3)");
-		assertNull(result.getException());
-		Map<Object, Object> map = new HashMap<>();
-		map.put("a", 1);
-		map.put("b", 2);
-		map.put("c", 3);
-		assertEquals(map, result.getResult());
-	}
-
-	@Test
-	public void createSet() throws Exception {
-		ScriptResult result = executeCode("set([1, 2, 3])");
-		assertNull(result.getException());
-		Set<Object> set = new HashSet<>();
-		set.add(1);
-		set.add(2);
-		set.add(3);
-		assertEquals(set, result.getResult());
-	}
-
-	@Test
-	public void createJavaType() throws Exception {
-		ScriptResult result = executeCode("java.io.File('/')");
-		assertNull(result.getException());
-		assertEquals(new java.io.File("/"), result.getResult());
-	}
-
-	@Test
-	public void createEclipseClass() throws Exception {
-		ScriptResult result = executeCode("org.eclipse.core.runtime.Path('/')");
-		assertNull(result.getException());
-		assertEquals(new org.eclipse.core.runtime.Path("/"), result.getResult());
-	}
-
-	@Test
-	public void createPythonObject() throws Exception {
-		ScriptResult result = executeCode("object()");
-		assertNull(result.getException());
-		assertThat(result.getResult(), instanceOf(String.class));
-		assertThat((String) result.getResult(), startsWith("<object object at "));
-	}
-
-	@Test
-	public void testMultiple() throws Exception {
-		simpleScriptCode();
-		createJavaType();
-		createEclipseClass();
-		simpleScriptCode();
+		assertNotNull(result.getResult());
+		assertEquals(target, result.getResult());
 	}
 
 	@Test
 	public void callModuleCode() throws Exception {
-		ScriptResult result = executeCode("exit(\"done\")");
+		final ScriptResult result = executeCode("exit(\"done\")");
 		assertNull(result.getException());
 		assertEquals("done", result.getResult());
 	}
@@ -130,7 +54,7 @@
 
 	@Test
 	public void getScriptEngine() throws Exception {
-		ScriptResult result = executeCode("getScriptEngine()");
+		final ScriptResult result = executeCode("getScriptEngine()");
 		assertNull(result.getException());
 		assertSame(fEngine, result.getResult());
 	}
@@ -172,15 +96,16 @@
 	public void multiLineStatement() throws Exception {
 		push("def a():", true);
 		push("    return 42", true);
-		assertResultIsNone(push("", false));
-		ScriptResult result = push("a()", false);
+		push("", false);
+		// assertResultIsNone(push("", false));final
+		final ScriptResult result = push("a()", false);
 		assertNull(result.getException());
 		assertEquals(42, result.getResult());
 	}
 
 	@Test
 	public void invalidSyntax() throws Exception {
-		ScriptResult result = executeCode("1++");
+		final ScriptResult result = executeCode("1++");
 		assertThat(result.getException(), instanceOf(ScriptExecutionException.class));
 		assertThat(fErrorStream.getAndClearOutput(), containsString("SyntaxError"));
 		assertNull(result.getResult());
@@ -188,7 +113,7 @@
 
 	@Test
 	public void runtimeError() throws Exception {
-		ScriptResult result = executeCode("x");
+		final ScriptResult result = executeCode("x");
 		assertThat(result.getException(), instanceOf(ScriptExecutionException.class));
 		assertThat(fErrorStream.getAndClearOutput(), containsString("NameError"));
 		assertNull(result.getResult());