changeset 3062:15bdc18525ff

8134254: JShell API/tool: REPL for Java into JDK9 Summary: Adding the implementation of the jshell (read-eval-print-loop) tool. Reviewed-by: briangoetz, mcimadamore, psandoz, forax Contributed-by: robert.field@oracle.com, bitterfoxc@gmail.com, jan.lahoda@oracle.com
author jlahoda
date Mon, 19 Oct 2015 19:15:16 +0200
parents 777c5a760a84
children 161940723360
files make/build.properties make/build.xml make/gensrc/Gensrc-jdk.jshell.gmk make/intellij/langtools.iml make/intellij/workspace.xml make/launcher.sh-template make/netbeans/langtools/build.xml make/netbeans/langtools/nbproject/project.xml make/tools/anttasks/DumpClassesTask.java make/tools/anttasks/SelectToolTask.java src/jdk.jshell/share/classes/jdk/internal/jshell/debug/InternalDebugControl.java src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteAgent.java src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteClassLoader.java src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteCodes.java src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteResolutionException.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/EditPad.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/EditingHistory.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ExternalEditor.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/IOContext.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/StopDetectingInputStream.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/version.properties-template src/jdk.jshell/share/classes/jdk/jshell/ClassTracker.java src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java src/jdk.jshell/share/classes/jdk/jshell/Diag.java src/jdk.jshell/share/classes/jdk/jshell/DiagList.java src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java src/jdk.jshell/share/classes/jdk/jshell/Eval.java src/jdk.jshell/share/classes/jdk/jshell/EvalException.java src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java src/jdk.jshell/share/classes/jdk/jshell/GeneralWrap.java src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java src/jdk.jshell/share/classes/jdk/jshell/JDIConnection.java src/jdk.jshell/share/classes/jdk/jshell/JDIEnv.java src/jdk.jshell/share/classes/jdk/jshell/JDIEventHandler.java src/jdk.jshell/share/classes/jdk/jshell/JDINotConnectedException.java src/jdk.jshell/share/classes/jdk/jshell/JShell.java src/jdk.jshell/share/classes/jdk/jshell/Key.java src/jdk.jshell/share/classes/jdk/jshell/KeyMap.java src/jdk.jshell/share/classes/jdk/jshell/MaskCommentsAndModifiers.java src/jdk.jshell/share/classes/jdk/jshell/MemoryFileManager.java src/jdk.jshell/share/classes/jdk/jshell/MethodSnippet.java src/jdk.jshell/share/classes/jdk/jshell/OuterWrap.java src/jdk.jshell/share/classes/jdk/jshell/PersistentSnippet.java src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java src/jdk.jshell/share/classes/jdk/jshell/ReplResolve.java src/jdk.jshell/share/classes/jdk/jshell/Snippet.java src/jdk.jshell/share/classes/jdk/jshell/SnippetEvent.java src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java src/jdk.jshell/share/classes/jdk/jshell/StatementSnippet.java src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java src/jdk.jshell/share/classes/jdk/jshell/TreeDependencyScanner.java src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java src/jdk.jshell/share/classes/jdk/jshell/TypeDeclSnippet.java src/jdk.jshell/share/classes/jdk/jshell/TypePrinter.java src/jdk.jshell/share/classes/jdk/jshell/Unit.java src/jdk.jshell/share/classes/jdk/jshell/UnresolvedReferenceException.java src/jdk.jshell/share/classes/jdk/jshell/Util.java src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java src/jdk.jshell/share/classes/jdk/jshell/Wrap.java src/jdk.jshell/share/classes/jdk/jshell/package-info.java test/jdk/jshell/AnalysisTest.java test/jdk/jshell/ClassMembersTest.java test/jdk/jshell/ClassPathTest.java test/jdk/jshell/ClassesTest.java test/jdk/jshell/CommandCompletionTest.java test/jdk/jshell/Compiler.java test/jdk/jshell/CompletenessStressTest.java test/jdk/jshell/CompletenessTest.java test/jdk/jshell/CompletionSuggestionTest.java test/jdk/jshell/CustomEditor.java test/jdk/jshell/DropTest.java test/jdk/jshell/EditorPadTest.java test/jdk/jshell/EditorTestBase.java test/jdk/jshell/EmptyTest.java test/jdk/jshell/ErrorTranslationTest.java test/jdk/jshell/ExceptionsTest.java test/jdk/jshell/ExpectedDiagnostic.java test/jdk/jshell/ExternalEditorTest.java test/jdk/jshell/HistoryTest.java test/jdk/jshell/IOTest.java test/jdk/jshell/IdGeneratorTest.java test/jdk/jshell/IgnoreTest.java test/jdk/jshell/IllegalArgumentExceptionTest.java test/jdk/jshell/ImportTest.java test/jdk/jshell/JShellStateClosedTest.java test/jdk/jshell/KullaCompletenessStressTest.java test/jdk/jshell/KullaTesting.java test/jdk/jshell/MethodsTest.java test/jdk/jshell/ModifiersTest.java test/jdk/jshell/NullTest.java test/jdk/jshell/RejectedFailedTest.java test/jdk/jshell/ReplToolTesting.java test/jdk/jshell/ReplaceTest.java test/jdk/jshell/ShutdownTest.java test/jdk/jshell/SimpleRegressionTest.java test/jdk/jshell/SnippetStatusListenerTest.java test/jdk/jshell/SnippetTest.java test/jdk/jshell/StartOptionTest.java test/jdk/jshell/StopExecutionTest.java test/jdk/jshell/TestingInputStream.java test/jdk/jshell/ToolBasicTest.java test/jdk/jshell/TypeNameTest.java test/jdk/jshell/VariablesTest.java
diffstat 110 files changed, 24672 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/make/build.properties	Mon Oct 19 12:41:45 2015 +0200
+++ b/make/build.properties	Mon Oct 19 19:15:16 2015 +0200
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -47,11 +47,21 @@
 boot.javac.target = 8
 
 #configuration of submodules (share by both the bootstrap and normal compilation):
-langtools.modules=java.compiler:jdk.compiler:jdk.jdeps:jdk.javadoc
+langtools.modules=java.compiler:jdk.compiler:jdk.jdeps:jdk.javadoc:jdk.jshell:jdk.internal.le:jdk.jdi
 java.compiler.dependencies=
 jdk.compiler.dependencies=java.compiler
 jdk.javadoc.dependencies=java.compiler:jdk.compiler
 jdk.jdeps.dependencies=java.compiler:jdk.compiler
+jdk.internal.le.dependencies=
+jdk.jdi.dependencies=
+jdk.jshell.dependencies=java.compiler:jdk.internal.le:jdk.compiler:jdk.jdi
+
+tool.javac.main.class=com.sun.tools.javac.Main
+tool.javadoc.main.class=com.sun.tools.javadoc.Main
+tool.javap.main.class=com.sun.tools.javap.Main
+tool.javah.main.class=com.sun.tools.javah.Main
+tool.sjavac.main.class=com.sun.tools.sjavac.Main
+tool.jshell.main.class=jdk.internal.jshell.tool.JShellTool
 
 javac.resource.includes = \
         com/sun/tools/javac/resources/compiler.properties
--- a/make/build.xml	Mon Oct 19 12:41:45 2015 +0200
+++ b/make/build.xml	Mon Oct 19 19:15:16 2015 +0200
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 
  This code is free software; you can redistribute it and/or modify it
@@ -172,6 +172,7 @@
         <build-tool name="javap"/>
         <build-tool name="javah"/>
         <build-tool name="sjavac"/>
+        <build-tool name="jshell"/>
     </target>
 
     <target name="build-all-classes" depends="-def-build-all-module-classes,build-bootstrap-javac-classes">
@@ -464,6 +465,9 @@
                 <build-module-jar module.name="jdk.compiler" compilation.kind="@{compilation.kind}" />
                 <build-module-jar module.name="jdk.javadoc" compilation.kind="@{compilation.kind}" />
                 <build-module-jar module.name="jdk.jdeps" compilation.kind="@{compilation.kind}" />
+                <build-module-jar module.name="jdk.internal.le" compilation.kind="@{compilation.kind}" />
+                <build-module-jar module.name="jdk.jdi" compilation.kind="@{compilation.kind}" />
+                <build-module-jar module.name="jdk.jshell" compilation.kind="@{compilation.kind}" />
             </sequential>
         </macrodef>
     </target>
@@ -502,11 +506,12 @@
             <attribute name="compilation.kind" default=""/>
             <attribute name="bin.dir" default="${@{compilation.kind}dist.bin.dir}"/>
             <attribute name="java" default="${launcher.java}"/>
+            <attribute name="main.class" default="${tool.@{name}.main.class}"/>
             <sequential>
                 <mkdir dir="@{bin.dir}"/>
                 <copy file="${make.dir}/launcher.sh-template" tofile="@{bin.dir}/@{name}">
                     <filterset begintoken="#" endtoken="#">
-                        <filter token="PROGRAM" value="@{name}"/>
+                        <filter token="PROGRAM" value="@{main.class}"/>
                         <filter token="TARGET_JAVA" value="@{java}"/>
                         <filter token="PS" value="${path.separator}"/>
                     </filterset>
@@ -529,11 +534,17 @@
                                       compilation.kind="@{compilation.kind}" />
                 <build-module-classes module.name="jdk.jdeps"
                                       compilation.kind="@{compilation.kind}" />
+                <copy-module-classes  module.name="jdk.internal.le"
+                                      compilation.kind="@{compilation.kind}" />
+                <copy-module-classes  module.name="jdk.jdi"
+                                      compilation.kind="@{compilation.kind}" />
+                <build-module-classes module.name="jdk.jshell"
+                                      compilation.kind="@{compilation.kind}" />
             </sequential>
         </macrodef>
     </target>
 
-    <target name="-def-build-module-classes" depends="-def-pcompile,-def-pparse">
+    <target name="-def-build-module-classes" depends="-def-pcompile,-def-pparse,-def-cdumper">
         <macrodef name="build-module-classes">
             <attribute name="module.name"/>
             <attribute name="compilation.kind" default=""/>
@@ -646,6 +657,18 @@
                 </copy>
             </sequential>
         </macrodef>
+        <macrodef name="copy-module-classes">
+            <attribute name="module.name"/>
+            <attribute name="compilation.kind" default=""/>
+            <attribute name="build.dir" default="${@{compilation.kind}build.dir}"/>
+            <attribute name="classes.dir" default="@{build.dir}/@{module.name}/classes"/>
+            <attribute name="java.home" default="${boot.java.home}"/>
+            <sequential>
+                <property name="classes.origin.dir" location="${target.java.home}/../../jdk/modules/@{module.name}"/>
+                <mkdir dir="@{classes.dir}"/>
+                <dumpclasses moduleName="@{module.name}" destDir="@{classes.dir}" />
+            </sequential>
+        </macrodef>
     </target>
 
     <target name="-def-pparse">
@@ -670,6 +693,25 @@
                  classpath="${build.toolclasses.dir}/"/>
     </target>
 
+    <target name="-def-cdumper">
+        <mkdir dir="${build.toolclasses.dir}"/>
+        <javac fork="true"
+               source="${boot.javac.source}"
+               target="${boot.javac.target}"
+               executable="${boot.java.home}/bin/javac"
+               srcdir="${make.tools.dir}"
+               includes="anttasks/DumpClass*"
+               destdir="${build.toolclasses.dir}/"
+               classpath="${ant.core.lib}"
+               bootclasspath="${boot.java.home}/jre/lib/rt.jar"
+               includeantruntime="false">
+            <compilerarg line="${javac.lint.opts}"/>
+        </javac>
+        <taskdef name="dumpclasses"
+                 classname="anttasks.DumpClassesTask"
+                 classpath="${build.toolclasses.dir}/:${target.java.home}/jrt-fs.jar"/>
+    </target>
+
     <target name="-do-depend" if="do.depend">
         <depend srcdir="${src.dir}:${gensrc.dir}" destdir="${classes.dir}" classpath="${classpath}"
                 cache="${depcache.dir}"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/gensrc/Gensrc-jdk.jshell.gmk	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+include GensrcCommon.gmk
+
+$(eval $(call SetupVersionProperties,JSHELL_VERSION, \
+    jdk/internal/jshell/tool/resources/version.properties))
+
+$(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, \
+    $(JSHELL_VERSION) $(JAVAH_VERSION)))
+
+all: $(COMPILE_PROPERTIES)
--- a/make/intellij/langtools.iml	Mon Oct 19 12:41:45 2015 +0200
+++ b/make/intellij/langtools.iml	Mon Oct 19 19:15:16 2015 +0200
@@ -13,6 +13,7 @@
       <sourceFolder url="file://$MODULE_DIR$/build/bootstrap/jdk.compiler/gensrc" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/build/bootstrap/jdk.javadoc/gensrc" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/build/bootstrap/jdk.jdeps/gensrc" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/jdk.jshell/share/classes" isTestSource="false" />
     </content>
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="inheritedJdk" />
--- a/make/intellij/workspace.xml	Mon Oct 19 12:41:45 2015 +0200
+++ b/make/intellij/workspace.xml	Mon Oct 19 19:15:16 2015 +0200
@@ -103,6 +103,24 @@
         <option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
       </method>
     </configuration>
+    <configuration default="false" name="jshell" type="Application" factoryName="Application">
+      <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+      <option name="MAIN_CLASS_NAME" value="jdk.internal.jshell.tool.JShellTool" />
+      <option name="VM_PARAMETERS" value="-Xbootclasspath/p:build/jdk.internal.le/classes:build/jdk.jdi/classes:build/jdk.jshell/classes:build/java.compiler/classes:build/jdk.compiler/classes:build/jdk.javadoc/classes:build/jdk.jdeps/classes" />
+      <option name="PROGRAM_PARAMETERS" value="" />
+      <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" value="$PROJECT_DIR$/../../dev/build/linux-x86_64-normal-server-release/images/jre" />
+      <option name="ENABLE_SWING_INSPECTOR" value="false" />
+      <option name="ENV_VARIABLES" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <module name="langtools" />
+      <envs />
+      <method>
+        <option name="Make" enabled="false" />
+        <option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
+      </method>
+    </configuration>
     <!-- bootstrap javac -->
     <configuration default="false" name="javac (bootstrap)" type="Application" factoryName="Application">
       <option name="MAIN_CLASS_NAME" value="com.sun.tools.javac.Main" />
--- a/make/launcher.sh-template	Mon Oct 19 12:41:45 2015 +0200
+++ b/make/launcher.sh-template	Mon Oct 19 19:15:16 2015 +0200
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -71,4 +71,4 @@
 unset DUALCASE
 
 IFS=$nl
-"#TARGET_JAVA#" "${bcp:+-Xbootclasspath/p:"$bcp"}" ${ea} ${javaOpts} com.sun.tools.#PROGRAM#.Main ${toolOpts}
+"#TARGET_JAVA#" "${bcp:+-Xbootclasspath/p:"$bcp"}" ${ea} ${javaOpts} #PROGRAM# ${toolOpts}
--- a/make/netbeans/langtools/build.xml	Mon Oct 19 12:41:45 2015 +0200
+++ b/make/netbeans/langtools/build.xml	Mon Oct 19 19:15:16 2015 +0200
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
@@ -81,11 +81,21 @@
         the user.
     -->
 
-    <target name="run" depends="-check-target.java.home,-build-classes,-def-run,-get-tool-and-args,-setup-bootclasspath"
+    <target name="run" depends="-check-target.java.home,-build-classes,-def-run,-get-tool-and-args,-setup-bootclasspath,-def-resolve-main-class"
             description="run tool">
         <echo level="info" message="${with_bootclasspath}"/>
         <echo level="info" message="Run ${use_bootstrap}${langtools.tool.name} with args ${langtools.tool.args}"/>
-        <run bcp="${with_bootclasspath}" mainclass="com.sun.tools.${langtools.tool.name}.Main" args="${langtools.tool.args}"/>
+        <resolve-main-class tool.name="${langtools.tool.name}" />
+        <run bcp="${with_bootclasspath}" mainclass="${langtools.main.class}" args="${langtools.tool.args}"/>
+    </target>
+
+    <target name="-def-resolve-main-class">
+        <macrodef name="resolve-main-class">
+          <attribute name="tool.name"/>
+          <sequential>
+            <property name="langtools.main.class" value="${tool.@{tool.name}.main.class}"/>
+          </sequential>
+        </macrodef>
     </target>
 
     <target name="-build-classes" depends="-get-tool-if-set,-build-classes-bootstrap-javac,-build-classes-all" />
@@ -159,10 +169,11 @@
 
     <!-- Debug tool in NetBeans. -->
 
-    <target name="debug" depends="-check-target.java.home,-def-run,-def-start-debugger,-get-tool-and-args,-setup-bootclasspath,-build-classes" if="netbeans.home">
+    <target name="debug" depends="-check-target.java.home,-def-run,-def-start-debugger,-get-tool-and-args,-setup-bootclasspath,-build-classes,-def-resolve-main-class" if="netbeans.home">
         <echo level="info" message="Debug ${use_bootstrap}${langtools.tool.name} with args ${langtools.tool.args}"/>
         <start-debugger/>
-        <run bcp="${with_bootclasspath}" mainclass="com.sun.tools.${langtools.tool.name}.Main" args="${langtools.tool.args}" jpda.jvmargs="${jpda.jvmargs}"/>
+        <resolve-main-class tool.name="${langtools.tool.name}" />
+        <run bcp="${with_bootclasspath}" mainclass="${langtools.main.class}" args="${langtools.tool.args}" jpda.jvmargs="${jpda.jvmargs}"/>
     </target>
 
     <!-- Debug a selected class . -->
--- a/make/netbeans/langtools/nbproject/project.xml	Mon Oct 19 12:41:45 2015 +0200
+++ b/make/netbeans/langtools/nbproject/project.xml	Mon Oct 19 19:15:16 2015 +0200
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
@@ -76,6 +76,11 @@
                     <type>java</type>
                     <location>${root}/src/jdk.javadoc/share/classes</location>
                 </source-folder>
+                <source-folder>
+                    <label>Source files - jdk.jshell</label>
+                    <type>java</type>
+                    <location>${root}/src/jdk.jshell/share/classes</location>
+                </source-folder>
                 <build-file>
                     <location>${root}/build/classes</location>
                 </build-file>
@@ -152,6 +157,19 @@
                         </arity>
                     </context>
                 </action>
+                <action name="compile.single">
+                    <target>compile-single</target>
+                    <property name="module.name">jdk.jshell</property>
+                    <context>
+                        <property>includes</property>
+                        <folder>${root}/src/jdk.jshell/share/classes</folder>
+                        <pattern>\.java$</pattern>
+                        <format>relative-path</format>
+                        <arity>
+                            <separated-files>,</separated-files>
+                        </arity>
+                    </context>
+                </action>
                 <action name="run">
                     <target>run</target>
                 </action>
@@ -215,6 +233,18 @@
                         </arity>
                     </context>
                 </action>
+                <action name="run.single">
+                    <target>run-single</target>
+                    <context>
+                        <property>run.classname</property>
+                        <folder>${root}/src/jdk.jshell/share/classes</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
                 <!--
  Note: NetBeans does not appear to support context menu items
  on shell scripts :-(
@@ -285,6 +315,18 @@
                         </arity>
                     </context>
                 </action>
+                <action name="debug.single">
+                    <target>debug-single</target>
+                    <context>
+                        <property>debug.classname</property>
+                        <folder>${root}/src/jdk.jshell/share/classes</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
                 <!--
  Note: NetBeans does not appear to support context menu items
  on shell scripts :-(
@@ -353,6 +395,19 @@
                         </arity>
                     </context>
                 </action>
+                <action name="debug.fix">
+                    <target>debug-fix</target>
+                    <property name="module.name">jdk.jshell</property>
+                    <context>
+                        <property>class</property>
+                        <folder>${root}/src/jdk.jshell/share/classes</folder>
+                        <pattern>\.java$</pattern>
+                        <format>relative-path-noext</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
                 <action name="javadoc">
                     <target>javadoc</target>
                 </action>
@@ -390,6 +445,10 @@
                         <location>${root}/src/jdk.javadoc/share/classes</location>
                     </source-folder>
                     <source-folder style="tree">
+                        <label>Source files - jdk.jshell</label>
+                        <location>${root}/src/jdk.jshell/share/classes</location>
+                    </source-folder>
+                    <source-folder style="tree">
                         <label>Test files</label>
                         <location>${root}/test</location>
                     </source-folder>
@@ -456,6 +515,15 @@
                 <built-to>${root}/build/jdk.javadoc/classes</built-to>
                 <source-level>1.8</source-level>
             </compilation-unit>
+            <compilation-unit>
+                <package-root>${root}/src/jdk.jshell/share/classes</package-root>
+                <package-root>${root}/build/bootstrap/jdk.jshell/gensrc</package-root>
+                <package-root>${root}/../jdk/src/jdk.internal.le/share/classes</package-root>
+                <package-root>${root}/../jdk/src/jdk.jdi/share/classes</package-root>
+                <classpath mode="compile">${root}/build/java.compiler/classes:${root}/build/jdk.compiler/classes:${root}/build/jdk.internal.le/aux:${root}/build/jdk.jdi/aux:${root}/build/jdk.internal.le/classes:${root}/build/jdk.jdi/classes</classpath>
+                <built-to>${root}/build/jdk.jshell/classes</built-to>
+                <source-level>1.8</source-level>
+            </compilation-unit>
         </java-data>
     </configuration>
 </project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/tools/anttasks/DumpClassesTask.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package anttasks;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Collections;
+import java.util.stream.Stream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+public class DumpClassesTask extends Task {
+
+    private String moduleName;
+    private File dir;
+
+    public void setModuleName(String moduleName) {
+        this.moduleName = moduleName;
+    }
+
+    public void setDestDir(File dir) {
+        this.dir = dir;
+    }
+
+    @Override
+    public void execute() {
+        try (FileSystem fs = FileSystems.newFileSystem(new URI("jrt:/"), Collections.emptyMap(), DumpClassesTask.class.getClassLoader())) {
+            Path source = fs.getPath("modules", moduleName);
+            Path target = dir.toPath();
+
+            try (Stream<Path> content = Files.walk(source)) {
+                content.filter(Files :: isRegularFile)
+                       .forEach(p -> {
+                    try {
+                        Path targetFile = target.resolve(source.relativize(p).toString());
+                        if (!Files.exists(targetFile) || Files.getLastModifiedTime(targetFile).compareTo(Files.getLastModifiedTime(source)) < 0) {
+                            Files.createDirectories(targetFile.getParent());
+                            Files.copy(p, targetFile, StandardCopyOption.REPLACE_EXISTING);
+                        }
+                    } catch (IOException ex) {
+                        throw new UncheckedIOException(ex);
+                    }
+                });
+            }
+        } catch (URISyntaxException | IOException | UncheckedIOException ex) {
+            throw new BuildException(ex);
+        }
+    }
+}
--- a/make/tools/anttasks/SelectToolTask.java	Mon Oct 19 12:41:45 2015 +0200
+++ b/make/tools/anttasks/SelectToolTask.java	Mon Oct 19 19:15:16 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -88,7 +88,8 @@
         },
         JAVADOC("javadoc"),
         JAVAH("javah"),
-        JAVAP("javap");
+        JAVAP("javap"),
+        JSHELL("jshell");
 
         String toolName;
         boolean bootstrap;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/debug/InternalDebugControl.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.debug;
+
+import java.util.HashMap;
+import java.util.Map;
+import jdk.jshell.JShell;
+
+/**
+ * Used to externally control output messages for debugging the implementation
+ * of the JShell API.  This is NOT a supported interface,
+ * @author Robert Field
+ */
+public class InternalDebugControl {
+    public static final int DBG_GEN   = 0b0000001;
+    public static final int DBG_FMGR  = 0b0000010;
+    public static final int DBG_COMPA = 0b0000100;
+    public static final int DBG_DEP   = 0b0001000;
+    public static final int DBG_EVNT  = 0b0010000;
+
+    private static Map<JShell, Integer> debugMap = null;
+
+    public static void setDebugFlags(JShell state, int flags) {
+        if (debugMap == null) {
+            debugMap = new HashMap<>();
+        }
+        debugMap.put(state, flags);
+    }
+
+    public static boolean debugEnabled(JShell state, int flag) {
+        if (debugMap == null) {
+            return false;
+        }
+        Integer flags = debugMap.get(state);
+        if (flags == null) {
+            return false;
+        }
+        return (flags & flag) != 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteAgent.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.remote;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.Socket;
+
+import java.util.ArrayList;
+import java.util.List;
+import static jdk.internal.jshell.remote.RemoteCodes.*;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * The remote agent runs in the execution process (separate from the main JShell
+ * process.  This agent loads code over a socket from the main JShell process,
+ * executes the code, and other misc,
+ * @author Robert Field
+ */
+class RemoteAgent {
+
+    private final RemoteClassLoader loader = new RemoteClassLoader();
+    private final Map<String, Class<?>> klasses = new TreeMap<>();
+
+    public static void main(String[] args) throws Exception {
+        String loopBack = null;
+        Socket socket = new Socket(loopBack, Integer.parseInt(args[0]));
+        (new RemoteAgent()).commandLoop(socket);
+    }
+
+    void commandLoop(Socket socket) throws IOException {
+        // in before out -- so we don't hang the controlling process
+        ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
+        ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
+        while (true) {
+            int cmd = in.readInt();
+            switch (cmd) {
+                case CMD_EXIT:
+                    // Terminate this process
+                    return;
+                case CMD_LOAD:
+                    // Load a generated class file over the wire
+                    try {
+                        int count = in.readInt();
+                        List<String> names = new ArrayList<>(count);
+                        for (int i = 0; i < count; ++i) {
+                            String name = in.readUTF();
+                            byte[] kb = (byte[]) in.readObject();
+                            loader.delare(name, kb);
+                            names.add(name);
+                        }
+                        for (String name : names) {
+                            Class<?> klass = loader.loadClass(name);
+                            klasses.put(name, klass);
+                            // Get class loaded to the point of, at least, preparation
+                            klass.getDeclaredMethods();
+                        }
+                        out.writeInt(RESULT_SUCCESS);
+                        out.flush();
+                    } catch (IOException | ClassNotFoundException | ClassCastException ex) {
+                        debug("*** Load failure: %s\n", ex);
+                        out.writeInt(RESULT_FAIL);
+                        out.writeUTF(ex.toString());
+                        out.flush();
+                    }
+                    break;
+                case CMD_INVOKE: {
+                    // Invoke executable entry point in loaded code
+                    String name = in.readUTF();
+                    Class<?> klass = klasses.get(name);
+                    if (klass == null) {
+                        debug("*** Invoke failure: no such class loaded %s\n", name);
+                        out.writeInt(RESULT_FAIL);
+                        out.writeUTF("no such class loaded: " + name);
+                        out.flush();
+                        break;
+                    }
+                    Method doitMethod;
+                    try {
+                        doitMethod = klass.getDeclaredMethod(DOIT_METHOD_NAME, new Class<?>[0]);
+                        doitMethod.setAccessible(true);
+                        Object res;
+                        try {
+                            clientCodeEnter();
+                            res = doitMethod.invoke(null, new Object[0]);
+                        } catch (InvocationTargetException ex) {
+                            if (ex.getCause() instanceof StopExecutionException) {
+                                expectingStop = false;
+                                throw (StopExecutionException) ex.getCause();
+                            }
+                            throw ex;
+                        } catch (StopExecutionException ex) {
+                            expectingStop = false;
+                            throw ex;
+                        } finally {
+                            clientCodeLeave();
+                        }
+                        out.writeInt(RESULT_SUCCESS);
+                        out.writeUTF(valueString(res));
+                        out.flush();
+                    } catch (InvocationTargetException ex) {
+                        Throwable cause = ex.getCause();
+                        StackTraceElement[] elems = cause.getStackTrace();
+                        if (cause instanceof RemoteResolutionException) {
+                            out.writeInt(RESULT_CORRALLED);
+                            out.writeInt(((RemoteResolutionException) cause).id);
+                        } else {
+                            out.writeInt(RESULT_EXCEPTION);
+                            out.writeUTF(cause.getClass().getName());
+                            out.writeUTF(cause.getMessage() == null ? "<none>" : cause.getMessage());
+                        }
+                        out.writeInt(elems.length);
+                        for (StackTraceElement ste : elems) {
+                            out.writeUTF(ste.getClassName());
+                            out.writeUTF(ste.getMethodName());
+                            out.writeUTF(ste.getFileName() == null ? "<none>" : ste.getFileName());
+                            out.writeInt(ste.getLineNumber());
+                        }
+                        out.flush();
+                    } catch (NoSuchMethodException | IllegalAccessException ex) {
+                        debug("*** Invoke failure: %s -- %s\n", ex, ex.getCause());
+                        out.writeInt(RESULT_FAIL);
+                        out.writeUTF(ex.toString());
+                        out.flush();
+                    } catch (StopExecutionException ex) {
+                        try {
+                            out.writeInt(RESULT_KILLED);
+                            out.flush();
+                        } catch (IOException err) {
+                            debug("*** Error writing killed result: %s -- %s\n", ex, ex.getCause());
+                        }
+                    }
+                    System.out.flush();
+                    break;
+                }
+                case CMD_VARVALUE: {
+                    // Retrieve a variable value
+                    String classname = in.readUTF();
+                    String varname = in.readUTF();
+                    Class<?> klass = klasses.get(classname);
+                    if (klass == null) {
+                        debug("*** Var value failure: no such class loaded %s\n", classname);
+                        out.writeInt(RESULT_FAIL);
+                        out.writeUTF("no such class loaded: " + classname);
+                        out.flush();
+                        break;
+                    }
+                    try {
+                        Field var = klass.getDeclaredField(varname);
+                        var.setAccessible(true);
+                        Object res = var.get(null);
+                        out.writeInt(RESULT_SUCCESS);
+                        out.writeUTF(valueString(res));
+                        out.flush();
+                    } catch (Exception ex) {
+                        debug("*** Var value failure: no such field %s.%s\n", classname, varname);
+                        out.writeInt(RESULT_FAIL);
+                        out.writeUTF("no such field loaded: " + varname + " in class: " + classname);
+                        out.flush();
+                    }
+                    break;
+                }
+                case CMD_CLASSPATH: {
+                    // Append to the claspath
+                    String cp = in.readUTF();
+                    for (String path : cp.split(File.pathSeparator)) {
+                        loader.addURL(new File(path).toURI().toURL());
+                    }
+                    out.writeInt(RESULT_SUCCESS);
+                    out.flush();
+                    break;
+                }
+                default:
+                    debug("*** Bad command code: %d\n", cmd);
+                    break;
+            }
+        }
+    }
+
+    // These three variables are used by the main JShell process in interrupting
+    // the running process.  Access is via JDI, so the reference is not visible
+    // to code inspection.
+    private boolean inClientCode; // Queried by the main process
+    private boolean expectingStop; // Set by the main process
+
+    // thrown by the main process via JDI:
+    private final StopExecutionException stopException = new StopExecutionException();
+
+    @SuppressWarnings("serial")             // serialVersionUID intentionally omitted
+    private class StopExecutionException extends ThreadDeath {
+        @Override public synchronized Throwable fillInStackTrace() {
+            return this;
+        }
+    }
+
+    void clientCodeEnter() {
+        expectingStop = false;
+        inClientCode = true;
+    }
+
+    void clientCodeLeave() {
+        inClientCode = false;
+        while (expectingStop) {
+            try {
+                Thread.sleep(0);
+            } catch (InterruptedException ex) {
+                debug("*** Sleep interrupted while waiting for stop exception: %s\n", ex);
+            }
+        }
+    }
+
+    private void debug(String format, Object... args) {
+        System.err.printf("REMOTE: "+format, args);
+    }
+
+    static String valueString(Object value) {
+        if (value == null) {
+            return "null";
+        } else if (value instanceof String) {
+            return "\"" + expunge((String)value) + "\"";
+        } else if (value instanceof Character) {
+            return "'" + value + "'";
+        } else {
+            return expunge(value.toString());
+        }
+    }
+
+    static String expunge(String s) {
+        StringBuilder sb = new StringBuilder();
+        for (String comp : prefixPattern.split(s)) {
+            sb.append(comp);
+        }
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteClassLoader.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.remote;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Class loader wrapper which caches class files by name until requested.
+ * @author Robert Field
+ */
+class RemoteClassLoader extends URLClassLoader {
+
+    private final Map<String, byte[]> classObjects = new TreeMap<>();
+
+    RemoteClassLoader() {
+        super(new URL[0]);
+    }
+
+    void delare(String name, byte[] bytes) {
+        classObjects.put(name, bytes);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        byte[] b = classObjects.get(name);
+        if (b == null) {
+            return super.findClass(name);
+        }
+        return super.defineClass(name, b, 0, b.length, (CodeSource) null);
+    }
+
+    @Override
+    public void addURL(URL url) {
+        super.addURL(url);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteCodes.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package jdk.internal.jshell.remote;
+
+import java.util.regex.Pattern;
+
+/**
+ * Communication constants shared between the main process and the remote
+ * execution process
+ * @author Robert Field
+ */
+public class RemoteCodes {
+    // Command codes
+    public static final int CMD_EXIT       = 0;
+    public static final int CMD_LOAD       = 1;
+    public static final int CMD_INVOKE     = 3;
+    public static final int CMD_CLASSPATH  = 4;
+    public static final int CMD_VARVALUE   = 5;
+
+    // Return result codes
+    public static final int RESULT_SUCCESS   = 100;
+    public static final int RESULT_FAIL      = 101;
+    public static final int RESULT_EXCEPTION = 102;
+    public static final int RESULT_CORRALLED = 103;
+    public static final int RESULT_KILLED    = 104;
+
+    public static final String DOIT_METHOD_NAME = "do_it$";
+    public static final String replClass = "\\$REPL(?<num>\\d+)[A-Z]*";
+    public static final Pattern prefixPattern = Pattern.compile("(REPL\\.)?" + replClass + "[\\$\\.]?");
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteResolutionException.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.remote;
+
+/**
+ * The exception thrown on the remote side upon executing a
+ * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
+ * user method. This exception is not seen by the end user nor through the API.
+ * @author Robert Field
+ */
+@SuppressWarnings("serial")             // serialVersionUID intentionally omitted
+public class RemoteResolutionException extends RuntimeException {
+
+    final int id;
+
+    /**
+     * The throw of this exception is generated into the body of a
+     * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
+     * method.
+     * @param id An internal identifier of the specific method
+     */
+    public RemoteResolutionException(int id) {
+        super("RemoteResolutionException");
+        this.id = id;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.tool;
+
+import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
+import jdk.jshell.SourceCodeAnalysis.Suggestion;
+
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import jdk.internal.jline.NoInterruptUnixTerminal;
+import jdk.internal.jline.Terminal;
+import jdk.internal.jline.TerminalFactory;
+import jdk.internal.jline.WindowsTerminal;
+import jdk.internal.jline.console.ConsoleReader;
+import jdk.internal.jline.console.KeyMap;
+import jdk.internal.jline.console.UserInterruptException;
+import jdk.internal.jline.console.completer.Completer;
+import jdk.internal.jshell.tool.StopDetectingInputStream.State;
+
+class ConsoleIOContext extends IOContext {
+
+    final JShellTool repl;
+    final StopDetectingInputStream input;
+    final ConsoleReader in;
+    final EditingHistory history;
+
+    String prefix = "";
+
+    ConsoleIOContext(JShellTool repl, InputStream cmdin, PrintStream cmdout) throws Exception {
+        this.repl = repl;
+        this.input = new StopDetectingInputStream(() -> repl.state.stop(), ex -> repl.hard("Error on input: %s", ex));
+        Terminal term;
+        if (System.getProperty("os.name").toLowerCase(Locale.US).contains(TerminalFactory.WINDOWS)) {
+            term = new JShellWindowsTerminal(input);
+        } else {
+            term = new JShellUnixTerminal(input);
+        }
+        term.init();
+        in = new ConsoleReader(cmdin, cmdout, term);
+        in.setExpandEvents(false);
+        in.setHandleUserInterrupt(true);
+        in.setHistory(history = new EditingHistory(JShellTool.PREFS) {
+            @Override protected CompletionInfo analyzeCompletion(String input) {
+                return repl.analysis.analyzeCompletion(input);
+            }
+        });
+        in.setBellEnabled(true);
+        in.addCompleter(new Completer() {
+            private String lastTest;
+            private int lastCursor;
+            private boolean allowSmart = false;
+            @Override public int complete(String test, int cursor, List<CharSequence> result) {
+                int[] anchor = new int[] {-1};
+                List<Suggestion> suggestions;
+                if (prefix.isEmpty() && test.trim().startsWith("/")) {
+                    suggestions = repl.commandCompletionSuggestions(test, cursor, anchor);
+                } else {
+                    int prefixLength = prefix.length();
+                    suggestions = repl.analysis.completionSuggestions(prefix + test, cursor + prefixLength, anchor);
+                    anchor[0] -= prefixLength;
+                }
+                if (!Objects.equals(lastTest, test) || lastCursor != cursor)
+                    allowSmart = true;
+
+                boolean smart = allowSmart &&
+                                suggestions.stream()
+                                           .anyMatch(s -> s.isSmart);
+
+                lastTest = test;
+                lastCursor = cursor;
+                allowSmart = !allowSmart;
+
+                suggestions.stream()
+                           .filter(s -> !smart || s.isSmart)
+                           .map(s -> s.continuation)
+                           .forEach(result::add);
+
+                boolean onlySmart = suggestions.stream()
+                                               .allMatch(s -> s.isSmart);
+
+                if (smart && !onlySmart) {
+                    Optional<String> prefix =
+                            suggestions.stream()
+                                       .map(s -> s.continuation)
+                                       .reduce(ConsoleIOContext::commonPrefix);
+
+                    String prefixStr = prefix.orElse("").substring(cursor - anchor[0]);
+                    try {
+                        in.putString(prefixStr);
+                        cursor += prefixStr.length();
+                    } catch (IOException ex) {
+                        throw new IllegalStateException(ex);
+                    }
+                    result.add("<press tab to see more>");
+                    return cursor; //anchor should not be used.
+                }
+
+                if (result.isEmpty()) {
+                    try {
+                        //provide "empty completion" feedback
+                        //XXX: this only works correctly when there is only one Completer:
+                        in.beep();
+                    } catch (IOException ex) {
+                        throw new UncheckedIOException(ex);
+                    }
+                }
+
+                return anchor[0];
+            }
+        });
+        bind(DOCUMENTATION_SHORTCUT, (ActionListener) evt -> documentation(repl));
+        bind(CTRL_UP, (ActionListener) evt -> moveHistoryToSnippet(((EditingHistory) in.getHistory())::previousSnippet));
+        bind(CTRL_DOWN, (ActionListener) evt -> moveHistoryToSnippet(((EditingHistory) in.getHistory())::nextSnippet));
+    }
+
+    @Override
+    public String readLine(String prompt, String prefix) throws IOException, InputInterruptedException {
+        this.prefix = prefix;
+        try {
+            return in.readLine(prompt);
+        } catch (UserInterruptException ex) {
+            throw (InputInterruptedException) new InputInterruptedException().initCause(ex);
+        }
+    }
+
+    @Override
+    public boolean interactiveOutput() {
+        return true;
+    }
+
+    @Override
+    public Iterable<String> currentSessionHistory() {
+        return history.currentSessionEntries();
+    }
+
+    @Override
+    public void close() throws IOException {
+        history.save();
+        in.shutdown();
+        try {
+            in.getTerminal().restore();
+        } catch (Exception ex) {
+            throw new IOException(ex);
+        }
+    }
+
+    private void moveHistoryToSnippet(Supplier<Boolean> action) {
+        if (!action.get()) {
+            try {
+                in.beep();
+            } catch (IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+        } else {
+            try {
+                //could use:
+                //in.resetPromptLine(in.getPrompt(), in.getHistory().current().toString(), -1);
+                //but that would mean more re-writing on the screen, (and prints an additional
+                //empty line), so using setBuffer directly:
+                Method setBuffer = in.getClass().getDeclaredMethod("setBuffer", String.class);
+
+                setBuffer.setAccessible(true);
+                setBuffer.invoke(in, in.getHistory().current().toString());
+                in.flush();
+            } catch (ReflectiveOperationException | IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+
+    private void bind(String shortcut, Object action) {
+        KeyMap km = in.getKeys();
+        for (int i = 0; i < shortcut.length(); i++) {
+            Object value = km.getBound(Character.toString(shortcut.charAt(i)));
+            if (value instanceof KeyMap) {
+                km = (KeyMap) value;
+            } else {
+                km.bind(shortcut.substring(i), action);
+            }
+        }
+    }
+
+    private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB
+    private static final String CTRL_UP = "\033\133\061\073\065\101"; //Ctrl-UP
+    private static final String CTRL_DOWN = "\033\133\061\073\065\102"; //Ctrl-DOWN
+
+    private void documentation(JShellTool repl) {
+        String buffer = in.getCursorBuffer().buffer.toString();
+        int cursor = in.getCursorBuffer().cursor;
+        String doc;
+        if (prefix.isEmpty() && buffer.trim().startsWith("/")) {
+            doc = repl.commandDocumentation(buffer, cursor);
+        } else {
+            doc = repl.analysis.documentation(prefix + buffer, cursor + prefix.length());
+        }
+
+        try {
+            if (doc != null) {
+                in.println();
+                in.println(doc);
+                in.redrawLine();
+                in.flush();
+            } else {
+                in.beep();
+            }
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    private static String commonPrefix(String str1, String str2) {
+        for (int i = 0; i < str2.length(); i++) {
+            if (!str1.startsWith(str2.substring(0, i + 1))) {
+                return str2.substring(0, i);
+            }
+        }
+
+        return str2;
+    }
+
+    @Override
+    public boolean terminalEditorRunning() {
+        Terminal terminal = in.getTerminal();
+        if (terminal instanceof JShellUnixTerminal)
+            return ((JShellUnixTerminal) terminal).isRaw();
+        return false;
+    }
+
+    @Override
+    public void suspend() {
+        try {
+            in.getTerminal().restore();
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public void resume() {
+        try {
+            in.getTerminal().init();
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    public void beforeUserCode() {
+        input.setState(State.BUFFER);
+    }
+
+    public void afterUserCode() {
+        input.setState(State.WAIT);
+    }
+
+    @Override
+    public void replaceLastHistoryEntry(String source) {
+        history.fullHistoryReplace(source);
+    }
+
+    private static final class JShellUnixTerminal extends NoInterruptUnixTerminal {
+
+        private final StopDetectingInputStream input;
+
+        public JShellUnixTerminal(StopDetectingInputStream input) throws Exception {
+            this.input = input;
+        }
+
+        public boolean isRaw() {
+            try {
+                return getSettings().get("-a").contains("-icanon");
+            } catch (IOException | InterruptedException ex) {
+                return false;
+            }
+        }
+
+        @Override
+        public InputStream wrapInIfNeeded(InputStream in) throws IOException {
+            return input.setInputStream(super.wrapInIfNeeded(in));
+        }
+
+        @Override
+        public void disableInterruptCharacter() {
+        }
+
+        @Override
+        public void enableInterruptCharacter() {
+        }
+
+    }
+
+    private static final class JShellWindowsTerminal extends WindowsTerminal {
+
+        private final StopDetectingInputStream input;
+
+        public JShellWindowsTerminal(StopDetectingInputStream input) throws Exception {
+            this.input = input;
+        }
+
+        @Override
+        public void init() throws Exception {
+            super.init();
+            setAnsiSupported(false);
+        }
+
+        @Override
+        public InputStream wrapInIfNeeded(InputStream in) throws IOException {
+            return input.setInputStream(super.wrapInIfNeeded(in));
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/EditPad.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.tool;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+
+/**
+ * A minimal Swing editor as a fallback when the user does not specify an
+ * external editor.
+ */
+@SuppressWarnings("serial")             // serialVersionUID intentionally omitted
+public class EditPad extends JFrame implements Runnable {
+    private final Consumer<String> errorHandler; // For possible future error handling
+    private final String initialText;
+    private final CountDownLatch closeLock;
+    private final Consumer<String> saveHandler;
+
+    EditPad(Consumer<String> errorHandler, String initialText,
+            CountDownLatch closeLock, Consumer<String> saveHandler) {
+        super("JShell Edit Pad");
+        this.errorHandler = errorHandler;
+        this.initialText = initialText;
+        this.closeLock = closeLock;
+        this.saveHandler = saveHandler;
+    }
+
+    @Override
+    public void run() {
+        addWindowListener(new WindowAdapter() {
+            @Override
+            public void windowClosing(WindowEvent e) {
+                EditPad.this.dispose();
+                closeLock.countDown();
+            }
+        });
+        setLocationRelativeTo(null);
+        setLayout(new BorderLayout());
+        JTextArea textArea = new JTextArea(initialText);
+        add(new JScrollPane(textArea), BorderLayout.CENTER);
+        add(buttons(textArea), BorderLayout.SOUTH);
+
+        setSize(800, 600);
+        setVisible(true);
+    }
+
+    private JPanel buttons(JTextArea textArea) {
+        FlowLayout flow = new FlowLayout();
+        flow.setHgap(35);
+        JPanel buttons = new JPanel(flow);
+        JButton cancel = new JButton("Cancel");
+        cancel.setMnemonic(KeyEvent.VK_C);
+        JButton accept = new JButton("Accept");
+        accept.setMnemonic(KeyEvent.VK_A);
+        JButton exit = new JButton("Exit");
+        exit.setMnemonic(KeyEvent.VK_X);
+        buttons.add(cancel);
+        buttons.add(accept);
+        buttons.add(exit);
+
+        cancel.addActionListener(e -> {
+            close();
+        });
+        accept.addActionListener(e -> {
+            saveHandler.accept(textArea.getText());
+        });
+        exit.addActionListener(e -> {
+            saveHandler.accept(textArea.getText());
+            close();
+        });
+
+        return buttons;
+    }
+
+    private void close() {
+        setVisible(false);
+        dispose();
+        closeLock.countDown();
+    }
+
+    public static void edit(Consumer<String> errorHandler, String initialText,
+            Consumer<String> saveHandler) {
+        CountDownLatch closeLock = new CountDownLatch(1);
+        SwingUtilities.invokeLater(
+                new EditPad(errorHandler, initialText, closeLock, saveHandler));
+        do {
+            try {
+                closeLock.await();
+                break;
+            } catch (InterruptedException ex) {
+                // ignore and loop
+            }
+        } while (true);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/EditingHistory.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jshell.tool;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import jdk.internal.jline.console.history.History;
+import jdk.internal.jline.console.history.History.Entry;
+import jdk.internal.jline.console.history.MemoryHistory;
+import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
+
+/*Public for tests (HistoryTest).
+ */
+public abstract class EditingHistory implements History {
+
+    private final Preferences prefs;
+    private final History fullHistory;
+    private History currentDelegate;
+
+    protected EditingHistory(Preferences prefs) {
+        this.prefs = prefs;
+        this.fullHistory = new MemoryHistory();
+        this.currentDelegate = fullHistory;
+        load();
+    }
+
+    @Override
+    public int size() {
+        return currentDelegate.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return currentDelegate.isEmpty();
+    }
+
+    @Override
+    public int index() {
+        return currentDelegate.index();
+    }
+
+    @Override
+    public void clear() {
+        if (currentDelegate != fullHistory)
+            throw new IllegalStateException("narrowed");
+        currentDelegate.clear();
+    }
+
+    @Override
+    public CharSequence get(int index) {
+        return currentDelegate.get(index);
+    }
+
+    @Override
+    public void add(CharSequence line) {
+        NarrowingHistoryLine currentLine = null;
+        int origIndex = fullHistory.index();
+        int fullSize;
+        try {
+            fullHistory.moveToEnd();
+            fullSize = fullHistory.index();
+            if (currentDelegate == fullHistory) {
+                if (origIndex < fullHistory.index()) {
+                    for (Entry entry : fullHistory) {
+                        if (!(entry.value() instanceof NarrowingHistoryLine))
+                            continue;
+                        int[] cluster = ((NarrowingHistoryLine) entry.value()).span;
+                        if (cluster[0] == origIndex && cluster[1] > cluster[0]) {
+                            currentDelegate = new MemoryHistory();
+                            for (int i = cluster[0]; i <= cluster[1]; i++) {
+                                currentDelegate.add(fullHistory.get(i));
+                            }
+                        }
+                    }
+                }
+            }
+            fullHistory.moveToEnd();
+            while (fullHistory.previous()) {
+                CharSequence c = fullHistory.current();
+                if (c instanceof NarrowingHistoryLine) {
+                    currentLine = (NarrowingHistoryLine) c;
+                    break;
+                }
+            }
+        } finally {
+            fullHistory.moveTo(origIndex);
+        }
+        if (currentLine == null || currentLine.span[1] != (-1)) {
+            line = currentLine = new NarrowingHistoryLine(line, fullSize);
+        }
+        StringBuilder complete = new StringBuilder();
+        for (int i = currentLine.span[0]; i < fullSize; i++) {
+            complete.append(fullHistory.get(i));
+        }
+        complete.append(line);
+        if (analyzeCompletion(complete.toString()).completeness.isComplete) {
+            currentLine.span[1] = fullSize; //TODO: +1?
+            currentDelegate = fullHistory;
+        }
+        fullHistory.add(line);
+    }
+
+    protected abstract CompletionInfo analyzeCompletion(String input);
+
+    @Override
+    public void set(int index, CharSequence item) {
+        if (currentDelegate != fullHistory)
+            throw new IllegalStateException("narrowed");
+        currentDelegate.set(index, item);
+    }
+
+    @Override
+    public CharSequence remove(int i) {
+        if (currentDelegate != fullHistory)
+            throw new IllegalStateException("narrowed");
+        return currentDelegate.remove(i);
+    }
+
+    @Override
+    public CharSequence removeFirst() {
+        if (currentDelegate != fullHistory)
+            throw new IllegalStateException("narrowed");
+        return currentDelegate.removeFirst();
+    }
+
+    @Override
+    public CharSequence removeLast() {
+        if (currentDelegate != fullHistory)
+            throw new IllegalStateException("narrowed");
+        return currentDelegate.removeLast();
+    }
+
+    @Override
+    public void replace(CharSequence item) {
+        if (currentDelegate != fullHistory)
+            throw new IllegalStateException("narrowed");
+        currentDelegate.replace(item);
+    }
+
+    @Override
+    public ListIterator<Entry> entries(int index) {
+        return currentDelegate.entries(index);
+    }
+
+    @Override
+    public ListIterator<Entry> entries() {
+        return currentDelegate.entries();
+    }
+
+    @Override
+    public Iterator<Entry> iterator() {
+        return currentDelegate.iterator();
+    }
+
+    @Override
+    public CharSequence current() {
+        return currentDelegate.current();
+    }
+
+    @Override
+    public boolean previous() {
+        return currentDelegate.previous();
+    }
+
+    @Override
+    public boolean next() {
+        return currentDelegate.next();
+    }
+
+    @Override
+    public boolean moveToFirst() {
+        return currentDelegate.moveToFirst();
+    }
+
+    @Override
+    public boolean moveToLast() {
+        return currentDelegate.moveToLast();
+    }
+
+    @Override
+    public boolean moveTo(int index) {
+        return currentDelegate.moveTo(index);
+    }
+
+    @Override
+    public void moveToEnd() {
+        currentDelegate.moveToEnd();
+    }
+
+    public boolean previousSnippet() {
+        for (int i = index() - 1; i >= 0; i--) {
+            if (get(i) instanceof NarrowingHistoryLine) {
+                moveTo(i);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public boolean nextSnippet() {
+        for (int i = index() + 1; i < size(); i++) {
+            if (get(i) instanceof NarrowingHistoryLine) {
+                moveTo(i);
+                return true;
+            }
+        }
+
+        if (index() < size()) {
+            moveToEnd();
+            return true;
+        }
+
+        return false;
+    }
+
+    private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_";
+    private static final String HISTORY_SNIPPET_START = "HISTORY_SNIPPET";
+
+    public final void load() {
+        try {
+            Set<Integer> snippetsStart = new HashSet<>();
+            for (String start : prefs.get(HISTORY_SNIPPET_START, "").split(";")) {
+                if (!start.isEmpty())
+                    snippetsStart.add(Integer.parseInt(start));
+            }
+            List<String> keys = Stream.of(prefs.keys()).sorted().collect(Collectors.toList());
+            NarrowingHistoryLine currentHistoryLine = null;
+            int currentLine = 0;
+            for (String key : keys) {
+                if (!key.startsWith(HISTORY_LINE_PREFIX))
+                    continue;
+                CharSequence line = prefs.get(key, "");
+                if (snippetsStart.contains(currentLine)) {
+                    class PersistentNarrowingHistoryLine extends NarrowingHistoryLine implements PersistentEntryMarker {
+                        public PersistentNarrowingHistoryLine(CharSequence delegate, int start) {
+                            super(delegate, start);
+                        }
+                    }
+                    line = currentHistoryLine = new PersistentNarrowingHistoryLine(line, currentLine);
+                } else {
+                    class PersistentLine implements CharSequence, PersistentEntryMarker {
+                        private final CharSequence delegate;
+                        public PersistentLine(CharSequence delegate) {
+                            this.delegate = delegate;
+                        }
+                        @Override public int length() {
+                            return delegate.length();
+                        }
+                        @Override public char charAt(int index) {
+                            return delegate.charAt(index);
+                        }
+                        @Override public CharSequence subSequence(int start, int end) {
+                            return delegate.subSequence(start, end);
+                        }
+                        @Override public String toString() {
+                            return delegate.toString();
+                        }
+                    }
+                    line = new PersistentLine(line);
+                }
+                if (currentHistoryLine != null)
+                    currentHistoryLine.span[1] = currentLine;
+                currentLine++;
+                fullHistory.add(line);
+            }
+            currentLine = 0;
+        } catch (BackingStoreException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    public void save() {
+        try {
+            for (String key : prefs.keys()) {
+                if (key.startsWith(HISTORY_LINE_PREFIX))
+                    prefs.remove(key);
+            }
+            Iterator<Entry> entries = fullHistory.iterator();
+            if (entries.hasNext()) {
+                int len = (int) Math.ceil(Math.log10(fullHistory.size()+1));
+                String format = HISTORY_LINE_PREFIX + "%0" + len + "d";
+                StringBuilder snippetStarts = new StringBuilder();
+                String snippetStartDelimiter = "";
+                while (entries.hasNext()) {
+                    Entry entry = entries.next();
+                    prefs.put(String.format(format, entry.index()), entry.value().toString());
+                    if (entry.value() instanceof NarrowingHistoryLine) {
+                        snippetStarts.append(snippetStartDelimiter);
+                        snippetStarts.append(entry.index());
+                        snippetStartDelimiter = ";";
+                    }
+                }
+                prefs.put(HISTORY_SNIPPET_START, snippetStarts.toString());
+            }
+        } catch (BackingStoreException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    public List<String> currentSessionEntries() {
+        List<String> result = new ArrayList<>();
+
+        for (Entry e : fullHistory) {
+            if (!(e.value() instanceof PersistentEntryMarker)) {
+                result.add(e.value().toString());
+            }
+        }
+
+        return result;
+    }
+
+    void fullHistoryReplace(String source) {
+        fullHistory.replace(source);
+    }
+
+    private class NarrowingHistoryLine implements CharSequence {
+        private final CharSequence delegate;
+        private final int[] span;
+
+        public NarrowingHistoryLine(CharSequence delegate, int start) {
+            this.delegate = delegate;
+            this.span = new int[] {start, -1};
+        }
+
+        @Override
+        public int length() {
+            return delegate.length();
+        }
+
+        @Override
+        public char charAt(int index) {
+            return delegate.charAt(index);
+        }
+
+        @Override
+        public CharSequence subSequence(int start, int end) {
+            return delegate.subSequence(start, end);
+        }
+
+        @Override
+        public String toString() {
+            return delegate.toString();
+        }
+
+    }
+
+    private interface PersistentEntryMarker {}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ExternalEditor.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.tool;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+
+/**
+ * Wrapper for controlling an external editor.
+ */
+public class ExternalEditor {
+    private final Consumer<String> errorHandler;
+    private final Consumer<String> saveHandler;
+    private final IOContext input;
+
+    private WatchService watcher;
+    private Thread watchedThread;
+    private Path dir;
+    private Path tmpfile;
+
+    ExternalEditor(Consumer<String> errorHandler, Consumer<String> saveHandler, IOContext input) {
+        this.errorHandler = errorHandler;
+        this.saveHandler = saveHandler;
+        this.input = input;
+    }
+
+    private void edit(String cmd, String initialText) {
+        try {
+            setupWatch(initialText);
+            launch(cmd);
+        } catch (IOException ex) {
+            errorHandler.accept(ex.getMessage());
+        }
+    }
+
+    /**
+     * Creates a WatchService and registers the given directory
+     */
+    private void setupWatch(String initialText) throws IOException {
+        this.watcher = FileSystems.getDefault().newWatchService();
+        this.dir = Files.createTempDirectory("REPL");
+        this.tmpfile = Files.createTempFile(dir, null, ".repl");
+        Files.write(tmpfile, initialText.getBytes(Charset.forName("UTF-8")));
+        dir.register(watcher,
+                ENTRY_CREATE,
+                ENTRY_DELETE,
+                ENTRY_MODIFY);
+        watchedThread = new Thread(() -> {
+            for (;;) {
+                WatchKey key;
+                try {
+                    key = watcher.take();
+                } catch (ClosedWatchServiceException ex) {
+                    break;
+                } catch (InterruptedException ex) {
+                    continue; // tolerate an intrupt
+                }
+
+                if (!key.pollEvents().isEmpty()) {
+                    if (!input.terminalEditorRunning()) {
+                        saveFile();
+                    }
+                }
+
+                boolean valid = key.reset();
+                if (!valid) {
+                    errorHandler.accept("Invalid key");
+                    break;
+                }
+            }
+        });
+        watchedThread.start();
+    }
+
+    private void launch(String cmd) throws IOException {
+        ProcessBuilder pb = new ProcessBuilder(cmd, tmpfile.toString());
+        pb = pb.inheritIO();
+
+        try {
+            input.suspend();
+            Process process = pb.start();
+            process.waitFor();
+        } catch (IOException ex) {
+            errorHandler.accept("process IO failure: " + ex.getMessage());
+        } catch (InterruptedException ex) {
+            errorHandler.accept("process interrupt: " + ex.getMessage());
+        } finally {
+            try {
+                watcher.close();
+                watchedThread.join(); //so that saveFile() is finished.
+                saveFile();
+            } catch (InterruptedException ex) {
+                errorHandler.accept("process interrupt: " + ex.getMessage());
+            } finally {
+                input.resume();
+            }
+        }
+    }
+
+    private void saveFile() {
+        try {
+            saveHandler.accept(Files.lines(tmpfile).collect(Collectors.joining("\n", "", "\n")));
+        } catch (IOException ex) {
+            errorHandler.accept("Failure in read edit file: " + ex.getMessage());
+        }
+    }
+
+    static void edit(String cmd, Consumer<String> errorHandler, String initialText,
+            Consumer<String> saveHandler, IOContext input) {
+        ExternalEditor ed = new ExternalEditor(errorHandler,  saveHandler, input);
+        ed.edit(cmd, initialText);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/IOContext.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.tool;
+
+import java.io.IOException;
+
+/**
+ * Interface for defining user interaction with the shell.
+ * @author Robert Field
+ */
+abstract class IOContext implements AutoCloseable {
+
+    @Override
+    public abstract void close() throws IOException;
+
+    public abstract String readLine(String prompt, String prefix) throws IOException, InputInterruptedException;
+
+    public abstract boolean interactiveOutput();
+
+    public abstract Iterable<String> currentSessionHistory();
+
+    public abstract  boolean terminalEditorRunning();
+
+    public abstract void suspend();
+
+    public abstract void resume();
+
+    public abstract void beforeUserCode();
+
+    public abstract void afterUserCode();
+
+    public abstract void replaceLastHistoryEntry(String source);
+
+    class InputInterruptedException extends Exception {
+        private static final long serialVersionUID = 1L;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,1804 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jshell.tool;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+import java.nio.file.AccessDeniedException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.prefs.Preferences;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import jdk.internal.jshell.debug.InternalDebugControl;
+import jdk.internal.jshell.tool.IOContext.InputInterruptedException;
+import jdk.jshell.Diag;
+import jdk.jshell.EvalException;
+import jdk.jshell.JShell;
+import jdk.jshell.Snippet;
+import jdk.jshell.DeclarationSnippet;
+import jdk.jshell.TypeDeclSnippet;
+import jdk.jshell.MethodSnippet;
+import jdk.jshell.PersistentSnippet;
+import jdk.jshell.VarSnippet;
+import jdk.jshell.ExpressionSnippet;
+import jdk.jshell.Snippet.Status;
+import jdk.jshell.SourceCodeAnalysis;
+import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
+import jdk.jshell.SourceCodeAnalysis.Suggestion;
+import jdk.jshell.SnippetEvent;
+import jdk.jshell.UnresolvedReferenceException;
+import jdk.jshell.Snippet.SubKind;
+import jdk.jshell.JShell.Subscription;
+
+import static java.nio.file.StandardOpenOption.CREATE;
+import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
+import static java.nio.file.StandardOpenOption.WRITE;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import static java.util.stream.Collectors.toList;
+
+/**
+ * Command line REPL tool for Java using the JShell API.
+ * @author Robert Field
+ */
+public class JShellTool {
+
+    private static final Pattern LINEBREAK = Pattern.compile("\\R");
+    private static final Pattern HISTORY_ALL_FILENAME = Pattern.compile(
+            "((?<cmd>(all|history))(\\z|\\p{javaWhitespace}+))?(?<filename>.*)");
+
+    final InputStream cmdin;
+    final PrintStream cmdout;
+    final PrintStream cmderr;
+    final PrintStream console;
+    final InputStream userin;
+    final PrintStream userout;
+    final PrintStream usererr;
+
+    /**
+     * The constructor for the tool (used by tool launch via main and by test
+     * harnesses to capture ins and outs.
+     * @param cmdin command line input -- snippets and commands
+     * @param cmdout command line output, feedback including errors
+     * @param cmderr start-up errors and debugging info
+     * @param console console control interaction
+     * @param userin code execution input (not yet functional)
+     * @param userout code execution output  -- System.out.printf("hi")
+     * @param usererr code execution error stream  -- System.err.printf("Oops")
+     */
+    public JShellTool(InputStream cmdin, PrintStream cmdout, PrintStream cmderr,
+            PrintStream console,
+            InputStream userin, PrintStream userout, PrintStream usererr) {
+        this.cmdin = cmdin;
+        this.cmdout = cmdout;
+        this.cmderr = cmderr;
+        this.console = console;
+        this.userin = userin;
+        this.userout = userout;
+        this.usererr = usererr;
+    }
+
+    private IOContext input = null;
+    private boolean regenerateOnDeath = true;
+    private boolean live = false;
+
+    SourceCodeAnalysis analysis;
+    JShell state = null;
+    Subscription shutdownSubscription = null;
+
+    private boolean debug = false;
+    private boolean displayPrompt = true;
+    public boolean testPrompt = false;
+    private Feedback feedback = Feedback.Default;
+    private String cmdlineClasspath = null;
+    private String cmdlineStartup = null;
+    private String editor = null;
+
+    static final Preferences PREFS = Preferences.userRoot().node("tool/REPL");
+
+    static final String STARTUP_KEY = "STARTUP";
+
+    static final String DEFAULT_STARTUP =
+            "\n" +
+            "import java.util.*;\n" +
+            "import java.io.*;\n" +
+            "import java.math.*;\n" +
+            "import java.net.*;\n" +
+            "import java.util.concurrent.*;\n" +
+            "import java.util.prefs.*;\n" +
+            "import java.util.regex.*;\n" +
+            "void printf(String format, Object... args) { System.out.printf(format, args); }\n";
+
+    // Tool id (tid) mapping
+    NameSpace mainNamespace;
+    NameSpace startNamespace;
+    NameSpace errorNamespace;
+    NameSpace currentNameSpace;
+    Map<Snippet,SnippetInfo> mapSnippet;
+
+    void debug(String format, Object... args) {
+        if (debug) {
+            cmderr.printf(format + "\n", args);
+        }
+    }
+
+    /**
+     * For more verbose feedback modes
+     * @param format printf format
+     * @param args printf args
+     */
+    void fluff(String format, Object... args) {
+        if (feedback() != Feedback.Off && feedback() != Feedback.Concise) {
+            hard(format, args);
+        }
+    }
+
+    /**
+     * For concise feedback mode only
+     * @param format printf format
+     * @param args printf args
+     */
+    void concise(String format, Object... args) {
+        if (feedback() == Feedback.Concise) {
+            hard(format, args);
+        }
+    }
+
+    /**
+     * For all feedback modes -- must show
+     * @param format printf format
+     * @param args printf args
+     */
+    void hard(String format, Object... args) {
+        cmdout.printf("|  " + format + "\n", args);
+    }
+
+    /**
+     * Trim whitespace off end of string
+     * @param s
+     * @return
+     */
+    static String trimEnd(String s) {
+        int last = s.length() - 1;
+        int i = last;
+        while (i >= 0 && Character.isWhitespace(s.charAt(i))) {
+            --i;
+        }
+        if (i != last) {
+            return s.substring(0, i + 1);
+        } else {
+            return s;
+        }
+    }
+
+    /**
+     * Normal start entry point
+     * @param args
+     * @throws Exception
+     */
+    public static void main(String[] args) throws Exception {
+        new JShellTool(System.in, System.out, System.err, System.out,
+                 new ByteArrayInputStream(new byte[0]), System.out, System.err)
+                .start(args);
+    }
+
+    public void start(String[] args) throws Exception {
+        List<String> loadList = processCommandArgs(args);
+        if (loadList == null) {
+            // Abort
+            return;
+        }
+        try (IOContext in = new ConsoleIOContext(this, cmdin, console)) {
+            start(in, loadList);
+        }
+    }
+
+    private void start(IOContext in, List<String> loadList) {
+        resetState(); // Initialize
+
+        for (String loadFile : loadList) {
+            cmdOpen(loadFile);
+        }
+
+        if (regenerateOnDeath) {
+            fluff("Welcome to JShell -- Version %s", version());
+            fluff("Type /help for help");
+        }
+
+        try {
+            while (regenerateOnDeath) {
+                if (!live) {
+                    resetState();
+                }
+                run(in);
+            }
+        } finally {
+            closeState();
+        }
+    }
+
+    /**
+     * Process the command line arguments.
+     * Set options.
+     * @param args the command line arguments
+     * @return the list of files to be loaded
+     */
+    private List<String> processCommandArgs(String[] args) {
+        List<String> loadList = new ArrayList<>();
+        Iterator<String> ai = Arrays.asList(args).iterator();
+        while (ai.hasNext()) {
+            String arg = ai.next();
+            if (arg.startsWith("-")) {
+                switch (arg) {
+                    case "-classpath":
+                    case "-cp":
+                        if (cmdlineClasspath != null) {
+                            cmderr.printf("Conflicting -classpath option.\n");
+                            return null;
+                        }
+                        if (ai.hasNext()) {
+                            cmdlineClasspath = ai.next();
+                        } else {
+                            cmderr.printf("Argument to -classpath missing.\n");
+                            return null;
+                        }
+                        break;
+                    case "-help":
+                        printUsage();
+                        return null;
+                    case "-version":
+                        cmdout.printf("jshell %s\n", version());
+                        return null;
+                    case "-fullversion":
+                        cmdout.printf("jshell %s\n", fullVersion());
+                        return null;
+                    case "-startup":
+                        if (cmdlineStartup != null) {
+                            cmderr.printf("Conflicting -startup or -nostartup option.\n");
+                            return null;
+                        }
+                        if (ai.hasNext()) {
+                            String filename = ai.next();
+                            try {
+                                byte[] encoded = Files.readAllBytes(Paths.get(filename));
+                                cmdlineStartup = new String(encoded);
+                            } catch (AccessDeniedException e) {
+                                hard("File '%s' for start-up is not accessible.", filename);
+                            } catch (NoSuchFileException e) {
+                                hard("File '%s' for start-up is not found.", filename);
+                            } catch (Exception e) {
+                                hard("Exception while reading start-up file: %s", e);
+                            }
+                        } else {
+                            cmderr.printf("Argument to -startup missing.\n");
+                            return null;
+                        }
+                        break;
+                    case "-nostartup":
+                        if (cmdlineStartup != null && !cmdlineStartup.isEmpty()) {
+                            cmderr.printf("Conflicting -startup option.\n");
+                            return null;
+                        }
+                        cmdlineStartup = "";
+                        break;
+                    default:
+                        cmderr.printf("Unknown option: %s\n", arg);
+                        printUsage();
+                        return null;
+                }
+            } else {
+                loadList.add(arg);
+            }
+        }
+        return loadList;
+    }
+
+    private void printUsage() {
+        cmdout.printf("Usage:   jshell <options> <load files>\n");
+        cmdout.printf("where possible options include:\n");
+        cmdout.printf("  -classpath <path>          Specify where to find user class files\n");
+        cmdout.printf("  -cp <path>                 Specify where to find user class files\n");
+        cmdout.printf("  -startup <file>            One run replacement for the start-up definitions\n");
+        cmdout.printf("  -nostartup                 Do not run the start-up definitions\n");
+        cmdout.printf("  -help                      Print a synopsis of standard options\n");
+        cmdout.printf("  -version                   Version information\n");
+    }
+
+    private void resetState() {
+        closeState();
+
+        // Initialize tool id mapping
+        mainNamespace = new NameSpace("main", "");
+        startNamespace = new NameSpace("start", "s");
+        errorNamespace = new NameSpace("error", "e");
+        mapSnippet = new LinkedHashMap<>();
+        currentNameSpace = startNamespace;
+
+        state = JShell.builder()
+                .in(userin)
+                .out(userout)
+                .err(usererr)
+                .tempVariableNameGenerator(()-> "$" + currentNameSpace.tidNext())
+                .idGenerator((sn, i) -> (currentNameSpace == startNamespace || state.status(sn).isActive)
+                        ? currentNameSpace.tid(sn)
+                        : errorNamespace.tid(sn))
+                .build();
+        analysis = state.sourceCodeAnalysis();
+        shutdownSubscription = state.onShutdown((JShell deadState) -> {
+            if (deadState == state) {
+                hard("State engine terminated.  See /history");
+                live = false;
+            }
+        });
+        live = true;
+
+        if (cmdlineClasspath != null) {
+            state.addToClasspath(cmdlineClasspath);
+        }
+
+
+        String start;
+        if (cmdlineStartup == null) {
+            start = PREFS.get(STARTUP_KEY, "<nada>");
+            if (start.equals("<nada>")) {
+                start = DEFAULT_STARTUP;
+                PREFS.put(STARTUP_KEY, DEFAULT_STARTUP);
+            }
+        } else {
+            start = cmdlineStartup;
+        }
+        try (IOContext suin = new FileScannerIOContext(new StringReader(start))) {
+            run(suin);
+        } catch (Exception ex) {
+            hard("Unexpected exception reading start-up: %s\n", ex);
+        }
+        currentNameSpace = mainNamespace;
+    }
+
+    private void closeState() {
+        live = false;
+        JShell oldState = state;
+        if (oldState != null) {
+            oldState.unsubscribe(shutdownSubscription); // No notification
+            oldState.close();
+        }
+    }
+
+    /**
+     * Main loop
+     * @param in the line input/editing context
+     */
+    private void run(IOContext in) {
+        IOContext oldInput = input;
+        input = in;
+        try {
+            String incomplete = "";
+            while (live) {
+                String prompt;
+                if (in.interactiveOutput() && displayPrompt) {
+                    prompt = testPrompt
+                                    ? incomplete.isEmpty()
+                                            ? "\u0005" //ENQ
+                                            : "\u0006" //ACK
+                                    : incomplete.isEmpty()
+                                            ? feedback() == Feedback.Concise
+                                                    ? "-> "
+                                                    : "\n-> "
+                                            : ">> "
+                    ;
+                } else {
+                    prompt = "";
+                }
+                String raw;
+                try {
+                    raw = in.readLine(prompt, incomplete);
+                } catch (InputInterruptedException ex) {
+                    //input interrupted - clearing current state
+                    incomplete = "";
+                    continue;
+                }
+                if (raw == null) {
+                    //EOF
+                    if (in.interactiveOutput()) {
+                        // End after user ctrl-D
+                        regenerateOnDeath = false;
+                    }
+                    break;
+                }
+                String trimmed = trimEnd(raw);
+                if (!trimmed.isEmpty()) {
+                    String line = incomplete + trimmed;
+
+                    // No commands in the middle of unprocessed source
+                    if (incomplete.isEmpty() && line.startsWith("/") && !line.startsWith("//") && !line.startsWith("/*")) {
+                        processCommand(line.trim());
+                    } else {
+                        incomplete = processSourceCatchingReset(line);
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            hard("Unexpected exception: %s\n", ex);
+        } finally {
+            input = oldInput;
+        }
+    }
+
+    private String processSourceCatchingReset(String src) {
+        try {
+            input.beforeUserCode();
+            return processSource(src);
+        } catch (IllegalStateException ex) {
+            hard("Resetting...");
+            live = false; // Make double sure
+            return "";
+        } finally {
+            input.afterUserCode();
+        }
+    }
+
+    private void processCommand(String cmd) {
+        try {
+            //handle "/[number]"
+            cmdUseHistoryEntry(Integer.parseInt(cmd.substring(1)));
+            return ;
+        } catch (NumberFormatException ex) {
+            //ignore
+        }
+        String arg = "";
+        int idx = cmd.indexOf(' ');
+        if (idx > 0) {
+            arg = cmd.substring(idx + 1).trim();
+            cmd = cmd.substring(0, idx);
+        }
+        Command command = commands.get(cmd);
+        if (command == null || command.kind == CommandKind.HELP_ONLY) {
+            hard("No such command: %s", cmd);
+            fluff("Type /help for help.");
+        } else {
+            command.run.accept(arg);
+        }
+    }
+
+    private static Path toPathResolvingUserHome(String pathString) {
+        if (pathString.replace(File.separatorChar, '/').startsWith("~/"))
+            return Paths.get(System.getProperty("user.home"), pathString.substring(2));
+        else
+            return Paths.get(pathString);
+    }
+
+    static final class Command {
+        public final String[] aliases;
+        public final String params;
+        public final String description;
+        public final Consumer<String> run;
+        public final CompletionProvider completions;
+        public final CommandKind kind;
+
+        public Command(String command, String alias, String params, String description, Consumer<String> run, CompletionProvider completions) {
+            this(command, alias, params, description, run, completions, CommandKind.NORMAL);
+        }
+
+        public Command(String command, String alias, String params, String description, Consumer<String> run, CompletionProvider completions, CommandKind kind) {
+            this.aliases = alias != null ? new String[] {command, alias} : new String[] {command};
+            this.params = params;
+            this.description = description;
+            this.run = run;
+            this.completions = completions;
+            this.kind = kind;
+        }
+
+    }
+
+    interface CompletionProvider {
+        List<Suggestion> completionSuggestions(String input, int cursor, int[] anchor);
+    }
+
+    enum CommandKind {
+        NORMAL,
+        HIDDEN,
+        HELP_ONLY;
+    }
+
+    static final class FixedCompletionProvider implements CompletionProvider {
+
+        private final String[] alternatives;
+
+        public FixedCompletionProvider(String... alternatives) {
+            this.alternatives = alternatives;
+        }
+
+        @Override
+        public List<Suggestion> completionSuggestions(String input, int cursor, int[] anchor) {
+            List<Suggestion> result = new ArrayList<>();
+
+            for (String alternative : alternatives) {
+                if (alternative.startsWith(input)) {
+                    result.add(new Suggestion(alternative, false));
+                }
+            }
+
+            anchor[0] = 0;
+
+            return result;
+        }
+
+    }
+
+    private static final CompletionProvider EMPTY_COMPLETION_PROVIDER = new FixedCompletionProvider();
+    private static final CompletionProvider FILE_COMPLETION_PROVIDER = fileCompletions(p -> true);
+    private final Map<String, Command> commands = new LinkedHashMap<>();
+    private void registerCommand(Command cmd) {
+        for (String str : cmd.aliases) {
+            commands.put(str, cmd);
+        }
+    }
+    private static CompletionProvider fileCompletions(Predicate<Path> accept) {
+        return (code, cursor, anchor) -> {
+            int lastSlash = code.lastIndexOf('/');
+            String path = code.substring(0, lastSlash + 1);
+            String prefix = lastSlash != (-1) ? code.substring(lastSlash + 1) : code;
+            Path current = toPathResolvingUserHome(path);
+            List<Suggestion> result = new ArrayList<>();
+            try (Stream<Path> dir = Files.list(current)) {
+                dir.filter(f -> accept.test(f) && f.getFileName().toString().startsWith(prefix))
+                   .map(f -> new Suggestion(f.getFileName() + (Files.isDirectory(f) ? "/" : ""), false))
+                   .forEach(result::add);
+            } catch (IOException ex) {
+                //ignore...
+            }
+            if (path.isEmpty()) {
+                StreamSupport.stream(FileSystems.getDefault().getRootDirectories().spliterator(), false)
+                             .filter(root -> accept.test(root) && root.toString().startsWith(prefix))
+                             .map(root -> new Suggestion(root.toString(), false))
+                             .forEach(result::add);
+            }
+            anchor[0] = path.length();
+            return result;
+        };
+    }
+
+    private static CompletionProvider classPathCompletion() {
+        return fileCompletions(p -> Files.isDirectory(p) ||
+                                    p.getFileName().toString().endsWith(".zip") ||
+                                    p.getFileName().toString().endsWith(".jar"));
+    }
+
+    private CompletionProvider editCompletion() {
+        return (prefix, cursor, anchor) -> {
+            anchor[0] = 0;
+            return state.snippets()
+                        .stream()
+                        .flatMap(k -> (k instanceof DeclarationSnippet)
+                                ? Stream.of(String.valueOf(k.id()), ((DeclarationSnippet) k).name())
+                                : Stream.of(String.valueOf(k.id())))
+                        .filter(k -> k.startsWith(prefix))
+                        .map(k -> new Suggestion(k, false))
+                        .collect(Collectors.toList());
+        };
+    }
+
+    private static CompletionProvider saveCompletion() {
+        CompletionProvider keyCompletion = new FixedCompletionProvider("all ", "history ");
+        return (code, cursor, anchor) -> {
+            List<Suggestion> result = new ArrayList<>();
+            int space = code.indexOf(' ');
+            if (space == (-1)) {
+                result.addAll(keyCompletion.completionSuggestions(code, cursor, anchor));
+            }
+            result.addAll(FILE_COMPLETION_PROVIDER.completionSuggestions(code.substring(space + 1), cursor - space - 1, anchor));
+            anchor[0] += space + 1;
+            return result;
+        };
+    }
+
+    // Table of commands -- with command forms, argument kinds, help message, implementation, ...
+
+    {
+        registerCommand(new Command("/list", "/l", "[all]", "list the source you have typed",
+                                    arg -> cmdList(arg),
+                                    new FixedCompletionProvider("all")));
+        registerCommand(new Command("/seteditor", null, "<executable>", "set the external editor command to use",
+                                    arg -> cmdSetEditor(arg),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/edit", "/e", "<name or id>", "edit a source entry referenced by name or id",
+                                    arg -> cmdEdit(arg),
+                                    editCompletion()));
+        registerCommand(new Command("/drop", "/d", "<name or id>", "delete a source entry referenced by name or id",
+                                    arg -> cmdDrop(arg),
+                                    editCompletion()));
+        registerCommand(new Command("/save", "/s", "[all|history] <file>", "save the source you have typed",
+                                    arg -> cmdSave(arg),
+                                    saveCompletion()));
+        registerCommand(new Command("/open", "/o", "<file>", "open a file as source input",
+                                    arg -> cmdOpen(arg),
+                                    FILE_COMPLETION_PROVIDER));
+        registerCommand(new Command("/vars", "/v", null, "list the declared variables and their values",
+                                    arg -> cmdVars(),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/methods", "/m", null, "list the declared methods and their signatures",
+                                    arg -> cmdMethods(),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/classes", "/c", null, "list the declared classes",
+                                    arg -> cmdClasses(),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/exit", "/x", null, "exit the REPL",
+                                    arg -> cmdExit(),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/reset", "/r", null, "reset everything in the REPL",
+                                    arg -> cmdReset(),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/feedback", "/f", "<level>", "feedback information: off, concise, normal, verbose, default, or ?",
+                                    arg -> cmdFeedback(arg),
+                                    new FixedCompletionProvider("off", "concise", "normal", "verbose", "default", "?")));
+        registerCommand(new Command("/prompt", "/p", null, "toggle display of a prompt",
+                                    arg -> cmdPrompt(),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/classpath", "/cp", "<path>", "add a path to the classpath",
+                                    arg -> cmdClasspath(arg),
+                                    classPathCompletion()));
+        registerCommand(new Command("/history", "/h", null, "history of what you have typed",
+                                    arg -> cmdHistory(),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/setstart", null, "<file>", "read file and set as the new start-up definitions",
+                                    arg -> cmdSetStart(arg),
+                                    FILE_COMPLETION_PROVIDER));
+        registerCommand(new Command("/savestart", null, "<file>", "save the default start-up definitions to the file",
+                                    arg -> cmdSaveStart(arg),
+                                    FILE_COMPLETION_PROVIDER));
+        registerCommand(new Command("/debug", "/db", "", "toggle debugging of the REPL",
+                                    arg -> cmdDebug(arg),
+                                    EMPTY_COMPLETION_PROVIDER,
+                                    CommandKind.HIDDEN));
+        registerCommand(new Command("/help", "/?", "", "this help message",
+                                    arg -> cmdHelp(),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/!", null, "", "re-run last snippet",
+                                    arg -> cmdUseHistoryEntry(-1),
+                                    EMPTY_COMPLETION_PROVIDER));
+        registerCommand(new Command("/<n>", null, "", "re-run n-th snippet",
+                                    arg -> { throw new IllegalStateException(); },
+                                    EMPTY_COMPLETION_PROVIDER,
+                                    CommandKind.HELP_ONLY));
+        registerCommand(new Command("/-<n>", null, "", "re-run n-th previous snippet",
+                                    arg -> { throw new IllegalStateException(); },
+                                    EMPTY_COMPLETION_PROVIDER,
+                                    CommandKind.HELP_ONLY));
+    }
+
+    public List<Suggestion> commandCompletionSuggestions(String code, int cursor, int[] anchor) {
+        String prefix = code.substring(0, cursor);
+        int space = prefix.indexOf(' ');
+        Stream<Suggestion> result;
+
+        if (space == (-1)) {
+            result = commands.values()
+                             .stream()
+                             .distinct()
+                             .filter(cmd -> cmd.kind != CommandKind.HIDDEN && cmd.kind != CommandKind.HELP_ONLY)
+                             .map(cmd -> cmd.aliases[0])
+                             .filter(key -> key.startsWith(prefix))
+                             .map(key -> new Suggestion(key + " ", false));
+            anchor[0] = 0;
+        } else {
+            String arg = prefix.substring(space + 1);
+            String cmd = prefix.substring(0, space);
+            Command command = commands.get(cmd);
+            if (command != null) {
+                result = command.completions.completionSuggestions(arg, cursor - space, anchor).stream();
+                anchor[0] += space + 1;
+            } else {
+                result = Stream.empty();
+            }
+        }
+
+        return result.sorted((s1, s2) -> s1.continuation.compareTo(s2.continuation))
+                     .collect(Collectors.toList());
+    }
+
+    public String commandDocumentation(String code, int cursor) {
+        code = code.substring(0, cursor);
+        int space = code.indexOf(' ');
+
+        if (space != (-1)) {
+            String cmd = code.substring(0, space);
+            Command command = commands.get(cmd);
+            if (command != null) {
+                return command.description;
+            }
+        }
+
+        return null;
+    }
+
+    // --- Command implementations ---
+
+    void cmdSetEditor(String arg) {
+        if (arg.isEmpty()) {
+            hard("/seteditor requires a path argument");
+        } else {
+            editor = arg;
+            fluff("Editor set to: %s", arg);
+        }
+    }
+
+    void cmdClasspath(String arg) {
+        if (arg.isEmpty()) {
+            hard("/classpath requires a path argument");
+        } else {
+            state.addToClasspath(toPathResolvingUserHome(arg).toString());
+            fluff("Path %s added to classpath", arg);
+        }
+    }
+
+    void cmdDebug(String arg) {
+        if (arg.isEmpty()) {
+            debug = !debug;
+            InternalDebugControl.setDebugFlags(state, debug ? InternalDebugControl.DBG_GEN : 0);
+            fluff("Debugging %s", debug ? "on" : "off");
+        } else {
+            int flags = 0;
+            for (char ch : arg.toCharArray()) {
+                switch (ch) {
+                    case '0':
+                        flags = 0;
+                        debug = false;
+                        fluff("Debugging off");
+                        break;
+                    case 'r':
+                        debug = true;
+                        fluff("REPL tool debugging on");
+                        break;
+                    case 'g':
+                        flags |= InternalDebugControl.DBG_GEN;
+                        fluff("General debugging on");
+                        break;
+                    case 'f':
+                        flags |= InternalDebugControl.DBG_FMGR;
+                        fluff("File manager debugging on");
+                        break;
+                    case 'c':
+                        flags |= InternalDebugControl.DBG_COMPA;
+                        fluff("Completion analysis debugging on");
+                        break;
+                    case 'd':
+                        flags |= InternalDebugControl.DBG_DEP;
+                        fluff("Dependency debugging on");
+                        break;
+                    case 'e':
+                        flags |= InternalDebugControl.DBG_EVNT;
+                        fluff("Event debugging on");
+                        break;
+                    default:
+                        hard("Unknown debugging option: %c", ch);
+                        fluff("Use: 0 r g f c d");
+                        break;
+                }
+            }
+            InternalDebugControl.setDebugFlags(state, flags);
+        }
+    }
+
+    private void cmdExit() {
+        regenerateOnDeath = false;
+        live = false;
+        fluff("Goodbye\n");
+    }
+
+    private void cmdFeedback(String arg) {
+        switch (arg) {
+            case "":
+            case "d":
+            case "default":
+                feedback = Feedback.Default;
+                break;
+            case "o":
+            case "off":
+                feedback = Feedback.Off;
+                break;
+            case "c":
+            case "concise":
+                feedback = Feedback.Concise;
+                break;
+            case "n":
+            case "normal":
+                feedback = Feedback.Normal;
+                break;
+            case "v":
+            case "verbose":
+                feedback = Feedback.Verbose;
+                break;
+            default:
+                hard("Follow /feedback with of the following:");
+                hard("  off       (errors and critical output only)");
+                hard("  concise");
+                hard("  normal");
+                hard("  verbose");
+                hard("  default");
+                hard("You may also use just the first letter, for example: /f c");
+                hard("In interactive mode 'default' is the same as 'normal', from a file it is the same as 'off'");
+                return;
+        }
+        fluff("Feedback mode: %s", feedback.name().toLowerCase());
+    }
+
+    void cmdHelp() {
+        int synopsisLen = 0;
+        Map<String, String> synopsis2Description = new LinkedHashMap<>();
+        for (Command cmd : new LinkedHashSet<>(commands.values())) {
+            if (cmd.kind == CommandKind.HIDDEN)
+                continue;
+            StringBuilder synopsis = new StringBuilder();
+            if (cmd.aliases.length > 1) {
+                synopsis.append(String.format("%-3s or ", cmd.aliases[1]));
+            } else {
+                synopsis.append("       ");
+            }
+            synopsis.append(cmd.aliases[0]);
+            if (cmd.params != null)
+                synopsis.append(" ").append(cmd.params);
+            synopsis2Description.put(synopsis.toString(), cmd.description);
+            synopsisLen = Math.max(synopsisLen, synopsis.length());
+        }
+        cmdout.println("Type a Java language expression, statement, or declaration.");
+        cmdout.println("Or type one of the following commands:\n");
+        for (Entry<String, String> e : synopsis2Description.entrySet()) {
+            cmdout.print(String.format("%-" + synopsisLen + "s", e.getKey()));
+            cmdout.print(" -- ");
+            cmdout.println(e.getValue());
+        }
+        cmdout.println();
+        cmdout.println("Supported shortcuts include:");
+        cmdout.println("<tab>       -- show possible completions for the current text");
+        cmdout.println("Shift-<tab> -- for current method or constructor invocation, show a synopsis of the method/constructor");
+    }
+
+    private void cmdHistory() {
+        cmdout.println();
+        for (String s : input.currentSessionHistory()) {
+            // No number prefix, confusing with snippet ids
+            cmdout.printf("%s\n", s);
+        }
+    }
+
+    /**
+     * Convert a user argument to a list of snippets referenced by that
+     * argument (or lack of argument).
+     * @param arg The user's argument to the command
+     * @return a list of referenced snippets
+     */
+    private List<Snippet> argToSnippets(String arg) {
+        List<Snippet> snippets = new ArrayList<>();
+        if (arg.isEmpty()) {
+            // Default is all user snippets
+            for (Snippet sn : state.snippets()) {
+                if (notInStartUp(sn)) {
+                    snippets.add(sn);
+                }
+            }
+        } else {
+            // Look for all declarations with matching names
+            for (Snippet key : state.snippets()) {
+                switch (key.kind()) {
+                    case METHOD:
+                    case VAR:
+                    case TYPE_DECL:
+                        if (((DeclarationSnippet) key).name().equals(arg)) {
+                            snippets.add(key);
+                        }
+                        break;
+                }
+            }
+            // If no declarations found, look for an id of this name
+            if (snippets.isEmpty()) {
+                for (Snippet sn : state.snippets()) {
+                    if (sn.id().equals(arg)) {
+                        snippets.add(sn);
+                        break;
+                    }
+                }
+            }
+            // If still no matches found, give an error
+            if (snippets.isEmpty()) {
+                hard("No definition or id named %s found.  See /classes /methods /vars or /list", arg);
+                return null;
+            }
+        }
+        return snippets;
+    }
+
+    private void cmdDrop(String arg) {
+        if (arg.isEmpty()) {
+            hard("In the /drop argument, please specify an import, variable, method, or class to drop.");
+            hard("Specify by id or name. Use /list to see ids. Use /reset to reset all state.");
+            return;
+        }
+        List<Snippet> snippetSet = argToSnippets(arg);
+        if (snippetSet == null) {
+            return;
+        }
+        snippetSet = snippetSet.stream()
+                .filter(sn -> state.status(sn).isActive)
+                .collect(toList());
+        snippetSet.removeIf(sn -> !(sn instanceof PersistentSnippet));
+        if (snippetSet.isEmpty()) {
+            hard("The argument did not specify an import, variable, method, or class to drop.");
+            return;
+        }
+        if (snippetSet.size() > 1) {
+            hard("The argument references more than one import, variable, method, or class.");
+            hard("Try again with one of the ids below:");
+            for (Snippet sn : snippetSet) {
+                cmdout.printf("%4s : %s\n", sn.id(), sn.source().replace("\n", "\n       "));
+            }
+            return;
+        }
+        PersistentSnippet psn = (PersistentSnippet) snippetSet.iterator().next();
+        state.drop(psn).forEach(this::handleEvent);
+    }
+
+    private void cmdEdit(String arg) {
+        List<Snippet> snippetSet = argToSnippets(arg);
+        if (snippetSet == null) {
+            return;
+        }
+        Set<String> srcSet = new LinkedHashSet<>();
+        for (Snippet key : snippetSet) {
+            String src = key.source();
+            switch (key.subKind()) {
+                case VAR_VALUE_SUBKIND:
+                    break;
+                case ASSIGNMENT_SUBKIND:
+                case OTHER_EXPRESSION_SUBKIND:
+                case TEMP_VAR_EXPRESSION_SUBKIND:
+                    if (!src.endsWith(";")) {
+                        src = src + ";";
+                    }
+                    srcSet.add(src);
+                    break;
+                default:
+                    srcSet.add(src);
+                    break;
+            }
+        }
+        StringBuilder sb = new StringBuilder();
+        for (String s : srcSet) {
+            sb.append(s);
+            sb.append('\n');
+        }
+        String src = sb.toString();
+        Consumer<String> saveHandler = new SaveHandler(src, srcSet);
+        Consumer<String> errorHandler = s -> hard("Edit Error: %s", s);
+        if (editor == null) {
+            EditPad.edit(errorHandler, src, saveHandler);
+        } else {
+            ExternalEditor.edit(editor, errorHandler, src, saveHandler, input);
+        }
+    }
+    //where
+    // receives editor requests to save
+    private class SaveHandler implements Consumer<String> {
+
+        String src;
+        Set<String> currSrcs;
+
+        SaveHandler(String src, Set<String> ss) {
+            this.src = src;
+            this.currSrcs = ss;
+        }
+
+        @Override
+        public void accept(String s) {
+            if (!s.equals(src)) { // quick check first
+                src = s;
+                try {
+                    Set<String> nextSrcs = new LinkedHashSet<>();
+                    boolean failed = false;
+                    while (true) {
+                        CompletionInfo an = analysis.analyzeCompletion(s);
+                        if (!an.completeness.isComplete) {
+                            break;
+                        }
+                        String tsrc = trimNewlines(an.source);
+                        if (!failed && !currSrcs.contains(tsrc)) {
+                            failed = processCompleteSource(tsrc);
+                        }
+                        nextSrcs.add(tsrc);
+                        if (an.remaining.isEmpty()) {
+                            break;
+                        }
+                        s = an.remaining;
+                    }
+                    currSrcs = nextSrcs;
+                } catch (IllegalStateException ex) {
+                    hard("Resetting...");
+                    resetState();
+                    currSrcs = new LinkedHashSet<>(); // re-process everything
+                }
+            }
+        }
+
+        private String trimNewlines(String s) {
+            int b = 0;
+            while (b < s.length() && s.charAt(b) == '\n') {
+                ++b;
+            }
+            int e = s.length() -1;
+            while (e >= 0 && s.charAt(e) == '\n') {
+                --e;
+            }
+            return s.substring(b, e + 1);
+        }
+    }
+
+    private void cmdList(String arg) {
+        boolean all = false;
+        switch (arg) {
+            case "all":
+                all = true;
+                break;
+            case "history":
+                cmdHistory();
+                return;
+            case "":
+                break;
+            default:
+                hard("Invalid /list argument: %s", arg);
+                return;
+        }
+        boolean hasOutput = false;
+        for (Snippet sn : state.snippets()) {
+            if (all || (notInStartUp(sn) && state.status(sn).isActive)) {
+                if (!hasOutput) {
+                    cmdout.println();
+                    hasOutput = true;
+                }
+                cmdout.printf("%4s : %s\n", sn.id(), sn.source().replace("\n", "\n       "));
+
+            }
+        }
+    }
+
+    private void cmdOpen(String filename) {
+        if (filename.isEmpty()) {
+            hard("The /open command requires a filename argument.");
+        } else {
+            try {
+                run(new FileScannerIOContext(toPathResolvingUserHome(filename).toString()));
+            } catch (FileNotFoundException e) {
+                hard("File '%s' is not found: %s", filename, e.getMessage());
+            } catch (Exception e) {
+                hard("Exception while reading file: %s", e);
+            }
+        }
+    }
+
+    private void cmdPrompt() {
+        displayPrompt = !displayPrompt;
+        fluff("Prompt will %sdisplay. Use /prompt to toggle.", displayPrompt ? "" : "NOT ");
+        concise("Prompt: %s", displayPrompt ? "on" : "off");
+    }
+
+    private void cmdReset() {
+        live = false;
+        fluff("Resetting state.");
+    }
+
+    private void cmdSave(String arg_filename) {
+        Matcher mat = HISTORY_ALL_FILENAME.matcher(arg_filename);
+        if (!mat.find()) {
+            hard("Malformed argument to the /save command: %s", arg_filename);
+            return;
+        }
+        boolean useHistory = false;
+        boolean saveAll = false;
+        String cmd = mat.group("cmd");
+        if (cmd != null) switch (cmd) {
+            case "all":
+                saveAll = true;
+                break;
+            case "history":
+                useHistory = true;
+                break;
+        }
+        String filename = mat.group("filename");
+        if (filename == null ||filename.isEmpty()) {
+            hard("The /save command requires a filename argument.");
+            return;
+        }
+        try (BufferedWriter writer = Files.newBufferedWriter(toPathResolvingUserHome(filename),
+                Charset.defaultCharset(),
+                CREATE, TRUNCATE_EXISTING, WRITE)) {
+            if (useHistory) {
+                for (String s : input.currentSessionHistory()) {
+                    writer.write(s);
+                    writer.write("\n");
+                }
+            } else {
+                for (Snippet sn : state.snippets()) {
+                    if (saveAll || notInStartUp(sn)) {
+                        writer.write(sn.source());
+                        writer.write("\n");
+                    }
+                }
+            }
+        } catch (FileNotFoundException e) {
+            hard("File '%s' for save is not accessible: %s", filename, e.getMessage());
+        } catch (Exception e) {
+            hard("Exception while saving: %s", e);
+        }
+    }
+
+    private void cmdSetStart(String filename) {
+        if (filename.isEmpty()) {
+            hard("The /setstart command requires a filename argument.");
+        } else {
+            try {
+                byte[] encoded = Files.readAllBytes(toPathResolvingUserHome(filename));
+                String init = new String(encoded);
+                PREFS.put(STARTUP_KEY, init);
+            } catch (AccessDeniedException e) {
+                hard("File '%s' for /setstart is not accessible.", filename);
+            } catch (NoSuchFileException e) {
+                hard("File '%s' for /setstart is not found.", filename);
+            } catch (Exception e) {
+                hard("Exception while reading start set file: %s", e);
+            }
+        }
+    }
+
+    private void cmdSaveStart(String filename) {
+        if (filename.isEmpty()) {
+            hard("The /savestart command requires a filename argument.");
+        } else {
+            try {
+                Files.write(toPathResolvingUserHome(filename), DEFAULT_STARTUP.getBytes());
+            } catch (AccessDeniedException e) {
+                hard("File '%s' for /savestart is not accessible.", filename);
+            } catch (NoSuchFileException e) {
+                hard("File '%s' for /savestart cannot be located.", filename);
+            } catch (Exception e) {
+                hard("Exception while saving default startup file: %s", e);
+            }
+        }
+    }
+
+    private void cmdVars() {
+        for (VarSnippet vk : state.variables()) {
+            String val = state.status(vk) == Status.VALID
+                    ? state.varValue(vk)
+                    : "(not-active)";
+            hard("  %s %s = %s", vk.typeName(), vk.name(), val);
+        }
+    }
+
+    private void cmdMethods() {
+        for (MethodSnippet mk : state.methods()) {
+            hard("  %s %s", mk.name(), mk.signature());
+        }
+    }
+
+    private void cmdClasses() {
+        for (TypeDeclSnippet ck : state.types()) {
+            String kind;
+            switch (ck.subKind()) {
+                case INTERFACE_SUBKIND:
+                    kind = "interface";
+                    break;
+                case CLASS_SUBKIND:
+                    kind = "class";
+                    break;
+                case ENUM_SUBKIND:
+                    kind = "enum";
+                    break;
+                case ANNOTATION_TYPE_SUBKIND:
+                    kind = "@interface";
+                    break;
+                default:
+                    assert false : "Wrong kind" + ck.subKind();
+                    kind = "class";
+                    break;
+            }
+            hard("  %s %s", kind, ck.name());
+        }
+    }
+
+    private void cmdUseHistoryEntry(int index) {
+        List<Snippet> keys = state.snippets();
+        if (index < 0)
+            index += keys.size();
+        else
+            index--;
+        if (index >= 0 && index < keys.size()) {
+            String source = keys.get(index).source();
+            cmdout.printf("%s\n", source);
+            input.replaceLastHistoryEntry(source);
+            processSourceCatchingReset(source);
+        } else {
+            hard("Cannot find snippet %d", index + 1);
+        }
+    }
+
+    /**
+     * Filter diagnostics for only errors (no warnings, ...)
+     * @param diagnostics input list
+     * @return filtered list
+     */
+    List<Diag> errorsOnly(List<Diag> diagnostics) {
+        return diagnostics.stream()
+                .filter(d -> d.isError())
+                .collect(toList());
+    }
+
+    void printDiagnostics(String source, List<Diag> diagnostics, boolean embed) {
+        String padding = embed? "    " : "";
+        for (Diag diag : diagnostics) {
+            //assert diag.getSource().equals(source);
+
+            if (!embed) {
+                if (diag.isError()) {
+                    hard("Error:");
+                } else {
+                    hard("Warning:");
+                }
+            }
+
+            for (String line : diag.getMessage(null).split("\\r?\\n")) {
+                if (!line.trim().startsWith("location:")) {
+                    hard("%s%s", padding, line);
+                }
+            }
+
+            int pstart = (int) diag.getStartPosition();
+            int pend = (int) diag.getEndPosition();
+            Matcher m = LINEBREAK.matcher(source);
+            int pstartl = 0;
+            int pendl = -2;
+            while (m.find(pstartl)) {
+                pendl = m.start();
+                if (pendl >= pstart) {
+                    break;
+                } else {
+                    pstartl = m.end();
+                }
+            }
+            if (pendl < pstart) {
+                pendl = source.length();
+            }
+            fluff("%s%s", padding, source.substring(pstartl, pendl));
+
+            StringBuilder sb = new StringBuilder();
+            int start = pstart - pstartl;
+            for (int i = 0; i < start; ++i) {
+                sb.append(' ');
+            }
+            sb.append('^');
+            boolean multiline = pend > pendl;
+            int end = (multiline ? pendl : pend) - pstartl - 1;
+            if (end > start) {
+                for (int i = start + 1; i < end; ++i) {
+                    sb.append('-');
+                }
+                if (multiline) {
+                    sb.append("-...");
+                } else {
+                    sb.append('^');
+                }
+            }
+            fluff("%s%s", padding, sb.toString());
+
+            debug("printDiagnostics start-pos = %d ==> %d -- wrap = %s", diag.getStartPosition(), start, this);
+            debug("Code: %s", diag.getCode());
+            debug("Pos: %d (%d - %d)", diag.getPosition(),
+                    diag.getStartPosition(), diag.getEndPosition());
+        }
+    }
+
+    private String processSource(String srcInput) throws IllegalStateException {
+        while (true) {
+            CompletionInfo an = analysis.analyzeCompletion(srcInput);
+            if (!an.completeness.isComplete) {
+                return an.remaining;
+            }
+            boolean failed = processCompleteSource(an.source);
+            if (failed || an.remaining.isEmpty()) {
+                return "";
+            }
+            srcInput = an.remaining;
+        }
+    }
+    //where
+    private boolean processCompleteSource(String source) throws IllegalStateException {
+        debug("Compiling: %s", source);
+        boolean failed = false;
+        List<SnippetEvent> events = state.eval(source);
+        for (SnippetEvent e : events) {
+            failed |= handleEvent(e);
+        }
+        return failed;
+    }
+
+    private boolean handleEvent(SnippetEvent ste) {
+        Snippet sn = ste.snippet();
+        if (sn == null) {
+            debug("Event with null key: %s", ste);
+            return false;
+        }
+        List<Diag> diagnostics = state.diagnostics(sn);
+        String source = sn.source();
+        if (ste.causeSnippet() == null) {
+            // main event
+            printDiagnostics(source, diagnostics, false);
+            if (ste.status().isActive) {
+                if (ste.exception() != null) {
+                    if (ste.exception() instanceof EvalException) {
+                        printEvalException((EvalException) ste.exception());
+                        return true;
+                    } else if (ste.exception() instanceof UnresolvedReferenceException) {
+                        printUnresolved((UnresolvedReferenceException) ste.exception());
+                    } else {
+                        hard("Unexpected execution exception: %s", ste.exception());
+                        return true;
+                    }
+                } else {
+                    displayDeclarationAndValue(ste, false, ste.value());
+                }
+            } else if (ste.status() == Status.REJECTED) {
+                if (diagnostics.isEmpty()) {
+                    hard("Failed.");
+                }
+                return true;
+            }
+        } else if (ste.status() == Status.REJECTED) {
+            //TODO -- I don't believe updates can cause failures any more
+            hard("Caused failure of dependent %s --", ((DeclarationSnippet) sn).name());
+            printDiagnostics(source, diagnostics, true);
+        } else {
+            // Update
+            SubKind subkind = sn.subKind();
+            if (sn instanceof DeclarationSnippet
+                    && (feedback() == Feedback.Verbose
+                    || ste.status() == Status.OVERWRITTEN
+                    || subkind == SubKind.VAR_DECLARATION_SUBKIND
+                    || subkind == SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND)) {
+                // Under the conditions, display update information
+                displayDeclarationAndValue(ste, true, null);
+                List<Diag> other = errorsOnly(diagnostics);
+                if (other.size() > 0) {
+                    printDiagnostics(source, other, true);
+                }
+            }
+        }
+        return false;
+    }
+
+    @SuppressWarnings("fallthrough")
+    private void displayDeclarationAndValue(SnippetEvent ste, boolean update, String value) {
+        Snippet key = ste.snippet();
+        String declared;
+        Status status = ste.status();
+        switch (status) {
+            case VALID:
+            case RECOVERABLE_DEFINED:
+            case RECOVERABLE_NOT_DEFINED:
+                if (ste.previousStatus().isActive) {
+                    declared = ste.isSignatureChange()
+                        ? "Replaced"
+                        : "Modified";
+                } else {
+                    declared = "Added";
+                }
+                break;
+            case OVERWRITTEN:
+                declared = "Overwrote";
+                break;
+            case DROPPED:
+                declared = "Dropped";
+                break;
+            case REJECTED:
+                declared = "Rejected";
+                break;
+            case NONEXISTENT:
+            default:
+                // Should not occur
+                declared = ste.previousStatus().toString() + "=>" + status.toString();
+        }
+        if (update) {
+            declared = "  Update " + declared.toLowerCase();
+        }
+        String however;
+        if (key instanceof DeclarationSnippet && (status == Status.RECOVERABLE_DEFINED || status == Status.RECOVERABLE_NOT_DEFINED)) {
+            String cannotUntil = (status == Status.RECOVERABLE_NOT_DEFINED)
+                    ? " cannot be referenced until"
+                    : " cannot be invoked until";
+            however = (update? " which" : ", however, it") + cannotUntil + unresolved((DeclarationSnippet) key);
+        } else {
+            however = "";
+        }
+        switch (key.subKind()) {
+            case CLASS_SUBKIND:
+                fluff("%s class %s%s", declared, ((TypeDeclSnippet) key).name(), however);
+                break;
+            case INTERFACE_SUBKIND:
+                fluff("%s interface %s%s", declared, ((TypeDeclSnippet) key).name(), however);
+                break;
+            case ENUM_SUBKIND:
+                fluff("%s enum %s%s", declared, ((TypeDeclSnippet) key).name(), however);
+                break;
+            case ANNOTATION_TYPE_SUBKIND:
+                fluff("%s annotation interface %s%s", declared, ((TypeDeclSnippet) key).name(), however);
+                break;
+            case METHOD_SUBKIND:
+                fluff("%s method %s(%s)%s", declared, ((MethodSnippet) key).name(),
+                        ((MethodSnippet) key).parameterTypes(), however);
+                break;
+            case VAR_DECLARATION_SUBKIND:
+                if (!update) {
+                    VarSnippet vk = (VarSnippet) key;
+                    if (status == Status.RECOVERABLE_NOT_DEFINED) {
+                        fluff("%s variable %s%s", declared, vk.name(), however);
+                    } else {
+                        fluff("%s variable %s of type %s%s", declared, vk.name(), vk.typeName(), however);
+                    }
+                    break;
+                }
+            // Fall through
+            case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: {
+                VarSnippet vk = (VarSnippet) key;
+                if (status == Status.RECOVERABLE_NOT_DEFINED) {
+                    if (!update) {
+                        fluff("%s variable %s%s", declared, vk.name(), however);
+                        break;
+                    }
+                } else if (update) {
+                    if (ste.isSignatureChange()) {
+                        hard("%s variable %s, reset to null", declared, vk.name());
+                    }
+                } else {
+                    fluff("%s variable %s of type %s with initial value %s",
+                            declared, vk.name(), vk.typeName(), value);
+                    concise("%s : %s", vk.name(), value);
+                }
+                break;
+            }
+            case TEMP_VAR_EXPRESSION_SUBKIND: {
+                VarSnippet vk = (VarSnippet) key;
+                if (update) {
+                    hard("%s temporary variable %s, reset to null", declared, vk.name());
+                 } else {
+                    fluff("Expression value is: %s", (value));
+                    fluff("  assigned to temporary variable %s of type %s", vk.name(), vk.typeName());
+                    concise("%s : %s", vk.name(), value);
+                }
+                break;
+            }
+            case OTHER_EXPRESSION_SUBKIND:
+                fluff("Expression value is: %s", (value));
+                break;
+            case VAR_VALUE_SUBKIND: {
+                ExpressionSnippet ek = (ExpressionSnippet) key;
+                fluff("Variable %s of type %s has value %s", ek.name(), ek.typeName(), (value));
+                concise("%s : %s", ek.name(), value);
+                break;
+            }
+            case ASSIGNMENT_SUBKIND: {
+                ExpressionSnippet ek = (ExpressionSnippet) key;
+                fluff("Variable %s has been assigned the value %s", ek.name(), (value));
+                concise("%s : %s", ek.name(), value);
+                break;
+            }
+        }
+    }
+    //where
+    void printStackTrace(StackTraceElement[] stes) {
+        for (StackTraceElement ste : stes) {
+            StringBuilder sb = new StringBuilder();
+            String cn = ste.getClassName();
+            if (!cn.isEmpty()) {
+                int dot = cn.lastIndexOf('.');
+                if (dot > 0) {
+                    sb.append(cn.substring(dot + 1));
+                } else {
+                    sb.append(cn);
+                }
+                sb.append(".");
+            }
+            if (!ste.getMethodName().isEmpty()) {
+                sb.append(ste.getMethodName());
+                sb.append(" ");
+            }
+            String fileName = ste.getFileName();
+            int lineNumber = ste.getLineNumber();
+            String loc = ste.isNativeMethod()
+                    ? "Native Method"
+                    : fileName == null
+                            ? "Unknown Source"
+                            : lineNumber >= 0
+                                    ? fileName + ":" + lineNumber
+                                    : fileName;
+            hard("      at %s(%s)", sb, loc);
+
+        }
+    }
+    //where
+    void printUnresolved(UnresolvedReferenceException ex) {
+        MethodSnippet corralled =  ex.getMethodSnippet();
+        List<Diag> otherErrors = errorsOnly(state.diagnostics(corralled));
+        StringBuilder sb = new StringBuilder();
+        if (otherErrors.size() > 0) {
+            if (state.unresolvedDependencies(corralled).size() > 0) {
+                sb.append(" and");
+            }
+            if (otherErrors.size() == 1) {
+                sb.append(" this error is addressed --");
+            } else {
+                sb.append(" these errors are addressed --");
+            }
+        } else {
+            sb.append(".");
+        }
+
+        hard("Attempted to call %s which cannot be invoked until%s", corralled.name(),
+                unresolved(corralled), sb.toString());
+        if (otherErrors.size() > 0) {
+            printDiagnostics(corralled.source(), otherErrors, true);
+        }
+    }
+    //where
+    void printEvalException(EvalException ex) {
+        if (ex.getMessage() == null) {
+            hard("%s thrown", ex.getExceptionClassName());
+        } else {
+            hard("%s thrown: %s", ex.getExceptionClassName(), ex.getMessage());
+        }
+        printStackTrace(ex.getStackTrace());
+    }
+    //where
+    String unresolved(DeclarationSnippet key) {
+        List<String> unr = state.unresolvedDependencies(key);
+        StringBuilder sb = new StringBuilder();
+        int fromLast = unr.size();
+        if (fromLast > 0) {
+            sb.append(" ");
+        }
+        for (String u : unr) {
+            --fromLast;
+            sb.append(u);
+            if (fromLast == 0) {
+                // No suffix
+            } else if (fromLast == 1) {
+                sb.append(", and ");
+            } else {
+                sb.append(", ");
+            }
+        }
+        switch (unr.size()) {
+            case 0:
+                break;
+            case 1:
+                sb.append(" is declared");
+                break;
+            default:
+                sb.append(" are declared");
+                break;
+        }
+        return sb.toString();
+    }
+
+    enum Feedback {
+        Default,
+        Off,
+        Concise,
+        Normal,
+        Verbose
+    }
+
+    Feedback feedback() {
+        if (feedback == Feedback.Default) {
+            return input == null || input.interactiveOutput() ? Feedback.Normal : Feedback.Off;
+        }
+        return feedback;
+    }
+
+    boolean notInStartUp(Snippet sn) {
+        return mapSnippet.get(sn).space != startNamespace;
+    }
+
+    /** The current version number as a string.
+     */
+    static String version() {
+        return version("release");  // mm.nn.oo[-milestone]
+    }
+
+    /** The current full version number as a string.
+     */
+    static String fullVersion() {
+        return version("full"); // mm.mm.oo[-milestone]-build
+    }
+
+    private static final String versionRBName = "jdk.internal.jshell.tool.resources.version";
+    private static ResourceBundle versionRB;
+
+    private static String version(String key) {
+        if (versionRB == null) {
+            try {
+                versionRB = ResourceBundle.getBundle(versionRBName);
+            } catch (MissingResourceException e) {
+                return "(version info not available)";
+            }
+        }
+        try {
+            return versionRB.getString(key);
+        }
+        catch (MissingResourceException e) {
+            return "(version info not available)";
+        }
+    }
+
+    class NameSpace {
+        final String spaceName;
+        final String prefix;
+        private int nextNum;
+
+        NameSpace(String spaceName, String prefix) {
+            this.spaceName = spaceName;
+            this.prefix = prefix;
+            this.nextNum = 1;
+        }
+
+        String tid(Snippet sn) {
+            String tid = prefix + nextNum++;
+            mapSnippet.put(sn, new SnippetInfo(sn, this, tid));
+            return tid;
+        }
+
+        String tidNext() {
+            return prefix + nextNum;
+        }
+    }
+
+    static class SnippetInfo {
+        final Snippet snippet;
+        final NameSpace space;
+        final String tid;
+
+        SnippetInfo(Snippet snippet, NameSpace space, String tid) {
+            this.snippet = snippet;
+            this.space = space;
+            this.tid = tid;
+        }
+    }
+}
+
+class ScannerIOContext extends IOContext {
+
+    private final Scanner scannerIn;
+    private final PrintStream pStream;
+
+    public ScannerIOContext(Scanner scannerIn, PrintStream pStream) {
+        this.scannerIn = scannerIn;
+        this.pStream = pStream;
+    }
+
+    @Override
+    public String readLine(String prompt, String prefix) {
+        if (pStream != null && prompt != null) {
+            pStream.print(prompt);
+        }
+        if (scannerIn.hasNextLine()) {
+            return scannerIn.nextLine();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean interactiveOutput() {
+        return true;
+    }
+
+    @Override
+    public Iterable<String> currentSessionHistory() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public void close() {
+        scannerIn.close();
+    }
+
+    @Override
+    public boolean terminalEditorRunning() {
+        return false;
+    }
+
+    @Override
+    public void suspend() {
+    }
+
+    @Override
+    public void resume() {
+    }
+
+    @Override
+    public void beforeUserCode() {
+    }
+
+    @Override
+    public void afterUserCode() {
+    }
+
+    @Override
+    public void replaceLastHistoryEntry(String source) {
+    }
+}
+
+class FileScannerIOContext extends ScannerIOContext {
+
+    public FileScannerIOContext(String fn) throws FileNotFoundException {
+        this(new FileReader(fn));
+    }
+
+    public FileScannerIOContext(Reader rdr) throws FileNotFoundException {
+        super(new Scanner(rdr), null);
+    }
+
+    @Override
+    public boolean interactiveOutput() {
+        return false;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/StopDetectingInputStream.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jshell.tool;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.function.Consumer;
+
+public final class StopDetectingInputStream extends InputStream {
+    public static final int INITIAL_SIZE = 128;
+
+    private final Runnable stop;
+    private final Consumer<Exception> errorHandler;
+
+    private boolean initialized;
+    private int[] buffer = new int[INITIAL_SIZE];
+    private int start;
+    private int end;
+    private State state = State.WAIT;
+
+    public StopDetectingInputStream(Runnable stop, Consumer<Exception> errorHandler) {
+        this.stop = stop;
+        this.errorHandler = errorHandler;
+    }
+
+    public synchronized InputStream setInputStream(InputStream input) {
+        if (initialized)
+            throw new IllegalStateException("Already initialized.");
+        initialized = true;
+
+        Thread reader = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    int read;
+                    while (true) {
+                        //to support external terminal editors, the "cmdin.read" must not run when
+                        //an external editor is running. At the same time, it needs to run while the
+                        //user's code is running (so Ctrl-C is detected). Hence waiting here until
+                        //there is a confirmed need for input.
+                        waitInputNeeded();
+                        if ((read = input.read()) == (-1)) {
+                            break;
+                        }
+                        if (read == 3 && state == StopDetectingInputStream.State.BUFFER) {
+                            stop.run();
+                        } else {
+                            write(read);
+                        }
+                    }
+                } catch (IOException ex) {
+                    errorHandler.accept(ex);
+                } finally {
+                    state = StopDetectingInputStream.State.CLOSED;
+                }
+            }
+        };
+        reader.setDaemon(true);
+        reader.start();
+
+        return this;
+    }
+
+    @Override
+    public synchronized int read() {
+        while (start == end) {
+            if (state == State.CLOSED) {
+                return -1;
+            }
+            if (state == State.WAIT) {
+                state = State.READ;
+            }
+            notifyAll();
+            try {
+                wait();
+            } catch (InterruptedException ex) {
+                //ignore
+            }
+        }
+        try {
+            return buffer[start];
+        } finally {
+            start = (start + 1) % buffer.length;
+        }
+    }
+
+    public synchronized void write(int b) {
+        if (state != State.BUFFER) {
+            state = State.WAIT;
+        }
+        int newEnd = (end + 1) % buffer.length;
+        if (newEnd == start) {
+            //overflow:
+            int[] newBuffer = new int[buffer.length * 2];
+            int rightPart = (end > start ? end : buffer.length) - start;
+            int leftPart = end > start ? 0 : start - 1;
+            System.arraycopy(buffer, start, newBuffer, 0, rightPart);
+            System.arraycopy(buffer, 0, newBuffer, rightPart, leftPart);
+            buffer = newBuffer;
+            start = 0;
+            end = rightPart + leftPart;
+            newEnd = end + 1;
+        }
+        buffer[end] = b;
+        end = newEnd;
+        notifyAll();
+    }
+
+    public synchronized void setState(State state) {
+        this.state = state;
+        notifyAll();
+    }
+
+    private synchronized void waitInputNeeded() {
+        while (state == State.WAIT) {
+            try {
+                wait();
+            } catch (InterruptedException ex) {
+                //ignore
+            }
+        }
+    }
+
+    public enum State {
+        /* No reading from the input should happen. This is the default state. The StopDetectingInput
+         * must be in this state when an external editor is being run, so that the external editor
+         * can read from the input.
+         */
+        WAIT,
+        /* A single input character should be read. Reading from the StopDetectingInput will move it
+         * into this state, and the state will be automatically changed back to WAIT when a single
+         * input character is obtained. This state is typically used while the user is editing the
+         * input line.
+         */
+        READ,
+        /* Continuously read from the input. Forward Ctrl-C ((int) 3) to the "stop" Runnable, buffer
+         * all other input. This state is typically used while the user's code is running, to provide
+         * the ability to detect Ctrl-C in order to stop the execution.
+         */
+        BUFFER,
+        /* The input is closed.
+        */
+        CLOSED
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/version.properties-template	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,28 @@
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+jdk=$(JDK_VERSION)
+full=$(FULL_VERSION)
+release=$(RELEASE)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ClassTracker.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import com.sun.jdi.ReferenceType;
+
+/**
+ * Tracks the state of a class through compilation and loading in the remote
+ * environment.
+ *
+ * @author Robert Field
+ */
+class ClassTracker {
+
+    private final JShell state;
+    private final HashMap<String, ClassInfo> map;
+
+    ClassTracker(JShell state) {
+        this.state = state;
+        this.map = new HashMap<>();
+    }
+
+    class ClassInfo {
+
+        private final String className;
+        private byte[] bytes;
+        private byte[] loadedBytes;
+        private ReferenceType rt;
+
+        private ClassInfo(String className) {
+            this.className = className;
+        }
+
+        String getClassName() {
+            return className;
+        }
+
+        byte[] getBytes() {
+            return bytes;
+        }
+
+        void setBytes(byte[] bytes) {
+            this.bytes = bytes;
+        }
+
+        void setLoaded() {
+            loadedBytes = bytes;
+        }
+
+        boolean isLoaded() {
+            return Arrays.equals(loadedBytes, bytes);
+        }
+
+        ReferenceType getReferenceTypeOrNull() {
+            if (rt == null) {
+                rt = state.executionControl().nameToRef(className);
+            }
+            return rt;
+        }
+    }
+
+    ClassInfo classInfo(String className, byte[] bytes) {
+        ClassInfo ci = map.computeIfAbsent(className, k -> new ClassInfo(k));
+        ci.setBytes(bytes);
+        return ci;
+    }
+
+    ClassInfo get(String className) {
+        return map.get(className);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,885 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import com.sun.tools.javac.code.Source;
+import com.sun.tools.javac.parser.Scanner;
+import com.sun.tools.javac.parser.ScannerFactory;
+import com.sun.tools.javac.parser.Tokens.Token;
+import com.sun.tools.javac.parser.Tokens.TokenKind;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.JCDiagnostic;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+import com.sun.tools.javac.util.Log;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.EnumMap;
+import java.util.Iterator;
+import jdk.jshell.SourceCodeAnalysis.Completeness;
+import com.sun.source.tree.Tree;
+import static jdk.jshell.CompletenessAnalyzer.TK.*;
+import jdk.jshell.TaskFactory.ParseTask;
+import java.util.List;
+
+/**
+ * Low level scanner to determine completeness of input.
+ * @author Robert Field
+ */
+class CompletenessAnalyzer {
+
+    private final ScannerFactory scannerFactory;
+    private final JShell proc;
+
+    private static Completeness error() {
+        return Completeness.UNKNOWN;  // For breakpointing
+    }
+
+    static class CaInfo {
+
+        CaInfo(Completeness status, int unitEndPos) {
+            this.status = status;
+            this.unitEndPos = unitEndPos;
+        }
+        final int unitEndPos;
+        final Completeness status;
+    }
+
+    CompletenessAnalyzer(JShell proc) {
+        this.proc = proc;
+        Context context = new Context();
+        Log log = CaLog.createLog(context);
+        context.put(Log.class, log);
+        context.put(Source.class, Source.JDK1_9);
+        scannerFactory = ScannerFactory.instance(context);
+    }
+
+    CaInfo scan(String s) {
+        try {
+            Scanner scanner = scannerFactory.newScanner(s, false);
+            Matched in = new Matched(scanner);
+            Parser parser = new Parser(in, proc, s);
+            Completeness stat = parser.parseUnit();
+            int endPos = stat == Completeness.UNKNOWN
+                    ? s.length()
+                    : in.prevCT.endPos;
+            return new CaInfo(stat, endPos);
+        } catch (SyntaxException ex) {
+            return new CaInfo(error(), s.length());
+        }
+    }
+
+    @SuppressWarnings("serial")             // serialVersionUID intentionally omitted
+    private static class SyntaxException extends RuntimeException {
+    }
+
+    private static void die() {
+        throw new SyntaxException();
+    }
+
+    /**
+     * Subclass of Log used by compiler API to die on error and ignore
+     * other messages
+     */
+    private static class CaLog extends Log {
+
+        private static CaLog createLog(Context context) {
+            PrintWriter pw = new PrintWriter(new StringWriter());
+            CaLog log = new CaLog(context, pw, pw, pw);
+            context.put(outKey, pw);
+            context.put(logKey, log);
+            return log;
+        }
+
+        private CaLog(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) {
+            super(context, errWriter, warnWriter, noticeWriter);
+        }
+
+        @Override
+        public void error(String key, Object... args) {
+            die();
+        }
+
+        @Override
+        public void error(DiagnosticPosition pos, String key, Object... args) {
+            die();
+        }
+
+        @Override
+        public void error(DiagnosticFlag flag, DiagnosticPosition pos, String key, Object... args) {
+            die();
+        }
+
+        @Override
+        public void error(int pos, String key, Object... args) {
+            die();
+        }
+
+        @Override
+        public void error(DiagnosticFlag flag, int pos, String key, Object... args) {
+            die();
+        }
+
+        @Override
+        public void report(JCDiagnostic diagnostic) {
+            // Ignore
+        }
+    }
+
+    // Location position kinds -- a token is ...
+    private static final int XEXPR         = 0b1;                       // OK in expression (not first)
+    private static final int XDECL         = 0b10;                      // OK in declaration (not first)
+    private static final int XSTMT         = 0b100;                     // OK in statement framework (not first)
+    private static final int XEXPR1o       = 0b1000;                    // OK first in expression
+    private static final int XDECL1o       = 0b10000;                   // OK first in declaration
+    private static final int XSTMT1o       = 0b100000;                  // OK first or only in statement framework
+    private static final int XEXPR1        = XEXPR1o | XEXPR;           // OK in expression (anywhere)
+    private static final int XDECL1        = XDECL1o | XDECL;           // OK in declaration (anywhere)
+    private static final int XSTMT1        = XSTMT1o | XSTMT;           // OK in statement framework (anywhere)
+    private static final int XANY1         = XEXPR1o | XDECL1o | XSTMT1o;  // Mask: first in statement, declaration, or expression
+    private static final int XTERM         = 0b100000000;               // Can terminate (last before EOF)
+    private static final int XSTART        = 0b1000000000;              // Boundary, must be XTERM before
+    private static final int XERRO         = 0b10000000000;             // Is an error
+
+    /**
+     * An extension of the compiler's TokenKind which adds our combined/processed
+     * kinds. Also associates each TK with a union of acceptable kinds of code
+     * position it can occupy.  For example: IDENTIFER is XEXPR1|XDECL1|XTERM,
+     * meaning it can occur in expressions or declarations (but not in the
+     * framework of a statement and that can be the final (terminating) token
+     * in a snippet.
+     * <P>
+     * There must be a TK defined for each compiler TokenKind, an exception
+     * will
+     * be thrown if a TokenKind is defined and a corresponding TK is not. Add a
+     * new TK in the appropriate category. If it is like an existing category
+     * (e.g. a new modifier or type this may be all that is needed.  If it
+     * is bracketing or modifies the acceptable positions of other tokens,
+     * please closely examine the needed changes to this scanner.
+     */
+    static enum TK {
+
+        // Special
+        EOF(TokenKind.EOF, 0),  //
+        ERROR(TokenKind.ERROR, XERRO),  //
+        IDENTIFIER(TokenKind.IDENTIFIER, XEXPR1|XDECL1|XTERM),  //
+        UNDERSCORE(TokenKind.UNDERSCORE, XERRO),  //  _
+        CLASS(TokenKind.CLASS, XEXPR|XDECL1|XTERM),  //  class decl and .class
+        MONKEYS_AT(TokenKind.MONKEYS_AT, XEXPR|XDECL1),  //  @
+        IMPORT(TokenKind.IMPORT, XDECL1|XSTART),  //  import -- consider declaration
+        SEMI(TokenKind.SEMI, XSTMT1|XTERM|XSTART),  //  ;
+
+        // Shouldn't see -- error
+        PACKAGE(TokenKind.PACKAGE, XERRO),  //  package
+        CONST(TokenKind.CONST, XERRO),  //  reserved keyword -- const
+        GOTO(TokenKind.GOTO, XERRO),  //  reserved keyword -- goto
+        CUSTOM(TokenKind.CUSTOM, XERRO),  // No uses
+
+        // Declarations
+        ENUM(TokenKind.ENUM, XDECL1),  //  enum
+        IMPLEMENTS(TokenKind.IMPLEMENTS, XDECL),  //  implements
+        INTERFACE(TokenKind.INTERFACE, XDECL1),  //  interface
+        THROWS(TokenKind.THROWS, XDECL),  //  throws
+
+        // Primarive type names
+        BOOLEAN(TokenKind.BOOLEAN, XEXPR|XDECL1),  //  boolean
+        BYTE(TokenKind.BYTE, XEXPR|XDECL1),  //  byte
+        CHAR(TokenKind.CHAR, XEXPR|XDECL1),  //  char
+        DOUBLE(TokenKind.DOUBLE, XEXPR|XDECL1),  //  double
+        FLOAT(TokenKind.FLOAT, XEXPR|XDECL1),  //  float
+        INT(TokenKind.INT, XEXPR|XDECL1),  //  int
+        LONG(TokenKind.LONG, XEXPR|XDECL1),  //  long
+        SHORT(TokenKind.SHORT, XEXPR|XDECL1),  //  short
+        VOID(TokenKind.VOID, XEXPR|XDECL1),  //  void
+
+        // Modifiers keywords
+        ABSTRACT(TokenKind.ABSTRACT, XDECL1),  //  abstract
+        FINAL(TokenKind.FINAL, XDECL1),  //  final
+        NATIVE(TokenKind.NATIVE, XDECL1),  //  native
+        STATIC(TokenKind.STATIC, XDECL1),  //  static
+        STRICTFP(TokenKind.STRICTFP, XDECL1),  //  strictfp
+        PRIVATE(TokenKind.PRIVATE, XDECL1),  //  private
+        PROTECTED(TokenKind.PROTECTED, XDECL1),  //  protected
+        PUBLIC(TokenKind.PUBLIC, XDECL1),  //  public
+        TRANSIENT(TokenKind.TRANSIENT, XDECL1),  //  transient
+        VOLATILE(TokenKind.VOLATILE, XDECL1),  //  volatile
+
+        // Declarations and type parameters (thus expressions)
+        EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL),  //  extends
+        COMMA(TokenKind.COMMA, XEXPR|XDECL|XSTART),  //  ,
+        AMP(TokenKind.AMP, XEXPR|XDECL),  //  &
+        GT(TokenKind.GT, XEXPR|XDECL),  //  >
+        LT(TokenKind.LT, XEXPR|XDECL1),  //  <
+        LTLT(TokenKind.LTLT, XEXPR|XDECL1),  //  <<
+        GTGT(TokenKind.GTGT, XEXPR|XDECL),  //  >>
+        GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL),  //  >>>
+        QUES(TokenKind.QUES, XEXPR|XDECL),  //  ?
+        DOT(TokenKind.DOT, XEXPR|XDECL),  //  .
+        STAR(TokenKind.STAR, XEXPR|XDECL|XTERM),  //  * -- import foo.* //TODO handle these case separately, XTERM
+
+        // Statement keywords
+        ASSERT(TokenKind.ASSERT, XSTMT1|XSTART),  //  assert
+        BREAK(TokenKind.BREAK, XSTMT1|XTERM|XSTART),  //  break
+        CATCH(TokenKind.CATCH, XSTMT1|XSTART),  //  catch
+        CONTINUE(TokenKind.CONTINUE, XSTMT1|XTERM|XSTART),  //  continue
+        DO(TokenKind.DO, XSTMT1|XSTART),  //  do
+        ELSE(TokenKind.ELSE, XSTMT1|XTERM|XSTART),  //  else
+        FINALLY(TokenKind.FINALLY, XSTMT1|XSTART),  //  finally
+        FOR(TokenKind.FOR, XSTMT1|XSTART),  //  for
+        IF(TokenKind.IF, XSTMT1|XSTART),  //  if
+        RETURN(TokenKind.RETURN, XSTMT1|XTERM|XSTART),  //  return
+        SWITCH(TokenKind.SWITCH, XSTMT1|XSTART),  //  switch
+        SYNCHRONIZED(TokenKind.SYNCHRONIZED, XSTMT1|XDECL),  //  synchronized
+        THROW(TokenKind.THROW, XSTMT1|XSTART),  //  throw
+        TRY(TokenKind.TRY, XSTMT1|XSTART),  //  try
+        WHILE(TokenKind.WHILE, XSTMT1|XSTART),  //  while
+
+        // Statement keywords that we shouldn't see -- inside braces
+        CASE(TokenKind.CASE, XSTMT|XSTART),  //  case
+        DEFAULT(TokenKind.DEFAULT, XSTMT|XSTART),  //  default method, default case -- neither we should see
+
+        // Expressions (can terminate)
+        INTLITERAL(TokenKind.INTLITERAL, XEXPR1|XTERM),  //
+        LONGLITERAL(TokenKind.LONGLITERAL, XEXPR1|XTERM),  //
+        FLOATLITERAL(TokenKind.FLOATLITERAL, XEXPR1|XTERM),  //
+        DOUBLELITERAL(TokenKind.DOUBLELITERAL, XEXPR1|XTERM),  //
+        CHARLITERAL(TokenKind.CHARLITERAL, XEXPR1|XTERM),  //
+        STRINGLITERAL(TokenKind.STRINGLITERAL, XEXPR1|XTERM),  //
+        TRUE(TokenKind.TRUE, XEXPR1|XTERM),  //  true
+        FALSE(TokenKind.FALSE, XEXPR1|XTERM),  //  false
+        NULL(TokenKind.NULL, XEXPR1|XTERM),  //  null
+        THIS(TokenKind.THIS, XEXPR1|XTERM),  //  this  -- shouldn't see
+
+        // Expressions maybe terminate  //TODO handle these case separately
+        PLUSPLUS(TokenKind.PLUSPLUS, XEXPR1|XTERM),  //  ++
+        SUBSUB(TokenKind.SUBSUB, XEXPR1|XTERM),  //  --
+
+        // Expressions cannot terminate
+        INSTANCEOF(TokenKind.INSTANCEOF, XEXPR),  //  instanceof
+        NEW(TokenKind.NEW, XEXPR1),  //  new
+        SUPER(TokenKind.SUPER, XEXPR1|XDECL),  //  super -- shouldn't see as rec. But in type parameters
+        ARROW(TokenKind.ARROW, XEXPR),  //  ->
+        COLCOL(TokenKind.COLCOL, XEXPR),  //  ::
+        LPAREN(TokenKind.LPAREN, XEXPR),  //  (
+        RPAREN(TokenKind.RPAREN, XEXPR),  //  )
+        LBRACE(TokenKind.LBRACE, XEXPR),  //  {
+        RBRACE(TokenKind.RBRACE, XEXPR),  //  }
+        LBRACKET(TokenKind.LBRACKET, XEXPR),  //  [
+        RBRACKET(TokenKind.RBRACKET, XEXPR),  //  ]
+        ELLIPSIS(TokenKind.ELLIPSIS, XEXPR),  //  ...
+        EQ(TokenKind.EQ, XEXPR),  //  =
+        BANG(TokenKind.BANG, XEXPR1),  //  !
+        TILDE(TokenKind.TILDE, XEXPR1),  //  ~
+        COLON(TokenKind.COLON, XEXPR|XTERM),  //  :
+        EQEQ(TokenKind.EQEQ, XEXPR),  //  ==
+        LTEQ(TokenKind.LTEQ, XEXPR),  //  <=
+        GTEQ(TokenKind.GTEQ, XEXPR),  //  >=
+        BANGEQ(TokenKind.BANGEQ, XEXPR),  //  !=
+        AMPAMP(TokenKind.AMPAMP, XEXPR),  //  &&
+        BARBAR(TokenKind.BARBAR, XEXPR),  //  ||
+        PLUS(TokenKind.PLUS, XEXPR1),  //  +
+        SUB(TokenKind.SUB, XEXPR1),  //  -
+        SLASH(TokenKind.SLASH, XEXPR),  //  /
+        BAR(TokenKind.BAR, XEXPR),  //  |
+        CARET(TokenKind.CARET, XEXPR),  //  ^
+        PERCENT(TokenKind.PERCENT, XEXPR),  //  %
+        PLUSEQ(TokenKind.PLUSEQ, XEXPR),  //  +=
+        SUBEQ(TokenKind.SUBEQ, XEXPR),  //  -=
+        STAREQ(TokenKind.STAREQ, XEXPR),  //  *=
+        SLASHEQ(TokenKind.SLASHEQ, XEXPR),  //  /=
+        AMPEQ(TokenKind.AMPEQ, XEXPR),  //  &=
+        BAREQ(TokenKind.BAREQ, XEXPR),  //  |=
+        CARETEQ(TokenKind.CARETEQ, XEXPR),  //  ^=
+        PERCENTEQ(TokenKind.PERCENTEQ, XEXPR),  //  %=
+        LTLTEQ(TokenKind.LTLTEQ, XEXPR),  //  <<=
+        GTGTEQ(TokenKind.GTGTEQ, XEXPR),  //  >>=
+        GTGTGTEQ(TokenKind.GTGTGTEQ, XEXPR),  //  >>>=
+
+        // combined/processed kinds
+        UNMATCHED(XERRO),
+        PARENS(XEXPR1|XDECL|XSTMT|XTERM),
+        BRACKETS(XEXPR|XDECL|XTERM),
+        BRACES(XSTMT1|XEXPR|XTERM);
+
+        static final EnumMap<TokenKind,TK> tokenKindToTKMap = new EnumMap<>(TokenKind.class);
+
+        final TokenKind tokenKind;
+        final int belongs;
+
+        TK(int b) {
+            this.tokenKind = null;
+            this.belongs = b;
+        }
+
+        TK(TokenKind tokenKind, int b) {
+            this.tokenKind = tokenKind;
+            this.belongs = b;
+        }
+
+        private static TK tokenKindToTK(TokenKind kind) {
+            TK tk = tokenKindToTKMap.get(kind);
+            if (tk == null) {
+                System.err.printf("No corresponding %s for %s: %s\n",
+                        TK.class.getCanonicalName(),
+                        TokenKind.class.getCanonicalName(),
+                        kind);
+                throw new InternalError("No corresponding TK for TokenKind: " + kind);
+            }
+            return tk;
+        }
+
+        boolean isOkToTerminate() {
+            return (belongs & XTERM) != 0;
+        }
+
+        boolean isExpression() {
+            return (belongs & XEXPR) != 0;
+        }
+
+        boolean isDeclaration() {
+            return (belongs & XDECL) != 0;
+        }
+
+        boolean isError() {
+            return (belongs & XERRO) != 0;
+        }
+
+        boolean isStart() {
+            return (belongs & XSTART) != 0;
+        }
+
+        /**
+         * After construction, check that all compiler TokenKind values have
+         * corresponding TK values.
+         */
+        static {
+            for (TK tk : TK.values()) {
+                if (tk.tokenKind != null) {
+                    tokenKindToTKMap.put(tk.tokenKind, tk);
+                }
+            }
+            for (TokenKind kind : TokenKind.values()) {
+                tokenKindToTK(kind); // assure they can be retrieved without error
+            }
+        }
+    }
+
+    /**
+     * A completeness scanner token.
+     */
+    private static class CT {
+
+        /** The token kind */
+        public final TK kind;
+
+        /** The end position of this token */
+        public final int endPos;
+
+        /** The error message **/
+        public final String message;
+
+        private CT(TK tk, Token tok, String msg) {
+            this.kind = tk;
+            this.endPos = tok.endPos;
+            this.message = msg;
+            //throw new InternalError(msg); /* for debugging */
+        }
+
+        private CT(TK tk, Token tok) {
+            this.kind = tk;
+            this.endPos = tok.endPos;
+            this.message = null;
+        }
+
+        private CT(TK tk, int endPos) {
+            this.kind = tk;
+            this.endPos = endPos;
+            this.message = null;
+        }
+    }
+
+    /**
+     * Look for matching tokens (like parens) and other special cases, like "new"
+     */
+    private static class Matched implements Iterator<CT> {
+
+        private final Scanner scanner;
+        private Token current;
+        private CT prevCT;
+        private CT currentCT;
+        private final Deque<Token> stack = new ArrayDeque<>();
+
+        Matched(Scanner scanner) {
+            this.scanner = scanner;
+            advance();
+            prevCT = currentCT = new CT(SEMI, 0); // So is valid for testing
+        }
+
+        @Override
+        public boolean hasNext() {
+            return currentCT.kind != EOF;
+        }
+
+        private Token advance() {
+            Token prev = current;
+            scanner.nextToken();
+            current = scanner.token();
+            return prev;
+        }
+
+        @Override
+        public CT next() {
+            prevCT = currentCT;
+            currentCT = nextCT();
+            return currentCT;
+        }
+
+        private CT match(TK tk, TokenKind open) {
+            Token tok = advance();
+            db("match desired-tk=%s, open=%s, seen-tok=%s", tk, open, tok.kind);
+            if (stack.isEmpty()) {
+                return new CT(ERROR, tok, "Encountered '" + tok + "' with no opening '" + open + "'");
+            }
+            Token p = stack.pop();
+            if (p.kind != open) {
+                return new CT(ERROR, tok, "No match for '" + p + "' instead encountered '" + tok + "'");
+            }
+            return new CT(tk, tok);
+        }
+
+        private void db(String format, Object ... args) {
+//            System.err.printf(format, args);
+//            System.err.printf(" -- stack(");
+//            if (stack.isEmpty()) {
+//
+//            } else {
+//                for (Token tok : stack) {
+//                    System.err.printf("%s ", tok.kind);
+//                }
+//            }
+//            System.err.printf(") current=%s / currentCT=%s\n", current.kind, currentCT.kind);
+        }
+
+        /**
+         * @return the next scanner token
+         */
+        private CT nextCT() {
+            // TODO Annotations?
+            TK prevTK = currentCT.kind;
+            while (true) {
+                db("nextCT");
+                CT ct;
+                switch (current.kind) {
+                    case EOF:
+                        db("eof");
+                        if (stack.isEmpty()) {
+                            ct = new CT(EOF, current);
+                        } else {
+                            TokenKind unmatched = stack.pop().kind;
+                            stack.clear(); // So we will get EOF next time
+                            ct = new CT(UNMATCHED, current, "Unmatched " + unmatched);
+                        }
+                        break;
+                    case LPAREN:
+                    case LBRACE:
+                    case LBRACKET:
+                        stack.push(advance());
+                        prevTK = SEMI; // new start
+                        continue;
+                    case RPAREN:
+                        ct = match(PARENS, TokenKind.LPAREN);
+                        break;
+                    case RBRACE:
+                        ct = match(BRACES, TokenKind.LBRACE);
+                        break;
+                    case RBRACKET:
+                        ct = match(BRACKETS, TokenKind.LBRACKET);
+                        break;
+                    default:
+                        ct = new CT(TK.tokenKindToTK(current.kind), advance());
+                        break;
+                }
+                if (ct.kind.isStart() && !prevTK.isOkToTerminate()) {
+                    return new CT(ERROR, current, "No '" + prevTK + "' before '" + ct.kind + "'");
+                }
+                if (stack.isEmpty() || ct.kind.isError()) {
+                    return ct;
+                }
+                prevTK = ct.kind;
+            }
+        }
+    }
+
+    /**
+     * Fuzzy parser based on token kinds
+     */
+    private static class Parser {
+
+        final Matched in;
+        CT token;
+        Completeness checkResult;
+
+        final JShell proc;
+        final String scannedInput;
+
+
+
+        Parser(Matched in, JShell proc, String scannedInput) {
+            this.in = in;
+            nextToken();
+
+            this.proc = proc;
+            this.scannedInput = scannedInput;
+        }
+
+        final void nextToken() {
+            in.next();
+            token = in.currentCT;
+        }
+
+        boolean shouldAbort(TK tk) {
+            if (token.kind == tk) {
+                nextToken();
+                return false;
+            }
+            switch (token.kind) {
+                case EOF:
+                    checkResult = ((tk == SEMI) && in.prevCT.kind.isOkToTerminate())
+                            ? Completeness.COMPLETE_WITH_SEMI
+                            : Completeness.DEFINITELY_INCOMPLETE;
+                    return true;
+                case UNMATCHED:
+                    checkResult = Completeness.DEFINITELY_INCOMPLETE;
+                    return true;
+                default:
+                    checkResult = error();
+                    return true;
+
+            }
+        }
+
+        Completeness lastly(TK tk) {
+            if (shouldAbort(tk))  return checkResult;
+            return Completeness.COMPLETE;
+        }
+
+        Completeness optionalFinalSemi() {
+            if (!shouldAbort(SEMI)) return Completeness.COMPLETE;
+            if (checkResult == Completeness.COMPLETE_WITH_SEMI) return Completeness.COMPLETE;
+            return checkResult;
+        }
+
+        boolean shouldAbort(Completeness flags) {
+            checkResult = flags;
+            return flags != Completeness.COMPLETE;
+        }
+
+        public Completeness parseUnit() {
+            //System.err.printf("%s:  belongs %o  XANY1 %o\n", token.kind, token.kind.belongs, token.kind.belongs & XANY1);
+            switch (token.kind.belongs & XANY1) {
+                case XEXPR1o:
+                    return parseExpressionOptionalSemi();
+                case XSTMT1o: {
+                    Completeness stat = parseSimpleStatement();
+                    return stat==null? error() : stat;
+                }
+                case XDECL1o:
+                    return parseDeclaration();
+                case XSTMT1o | XDECL1o:
+                case XEXPR1o | XDECL1o:
+                    return disambiguateDeclarationVsExpression();
+                case 0:
+                    if ((token.kind.belongs & XERRO) != 0) {
+                        return parseExpressionStatement(); // Let this gen the status
+                    }
+                    return error();
+                default:
+                    throw new InternalError("Case not covered " + token.kind.belongs + " in " + token.kind);
+            }
+        }
+
+        public Completeness parseDeclaration() {
+            boolean isImport = token.kind == IMPORT;
+            while (token.kind.isDeclaration()) {
+                nextToken();
+            }
+            switch (token.kind) {
+                case EQ:
+                    // Check for array initializer
+                    nextToken();
+                    if (token.kind == BRACES) {
+                        nextToken();
+                        return lastly(SEMI);
+                    }
+                    return parseExpressionStatement();
+                case BRACES:
+                case SEMI:
+                    nextToken();
+                    return Completeness.COMPLETE;
+                case UNMATCHED:
+                    nextToken();
+                    return Completeness.DEFINITELY_INCOMPLETE;
+                case EOF:
+                    switch (in.prevCT.kind) {
+                        case BRACES:
+                        case SEMI:
+                            return Completeness.COMPLETE;
+                        case IDENTIFIER:
+                        case BRACKETS:
+                            return Completeness.COMPLETE_WITH_SEMI;
+                        case STAR:
+                            if (isImport) {
+                                return Completeness.COMPLETE_WITH_SEMI;
+                            } else {
+                                return Completeness.DEFINITELY_INCOMPLETE;
+                            }
+                        default:
+                            return Completeness.DEFINITELY_INCOMPLETE;
+                    }
+                default:
+                    return error();
+            }
+        }
+
+        public Completeness disambiguateDeclarationVsExpression() {
+            // String folding messes up position information.
+            ParseTask pt = proc.taskFactory.new ParseTask(scannedInput);
+            List<? extends Tree> units = pt.units();
+            if (units.isEmpty()) {
+                return error();
+            }
+            Tree unitTree = units.get(0);
+            switch (unitTree.getKind()) {
+                case EXPRESSION_STATEMENT:
+                    return parseExpressionOptionalSemi();
+                case LABELED_STATEMENT:
+                    if (shouldAbort(IDENTIFIER))  return checkResult;
+                    if (shouldAbort(COLON))  return checkResult;
+                    return parseStatement();
+                case VARIABLE:
+                case IMPORT:
+                case CLASS:
+                case ENUM:
+                case ANNOTATION_TYPE:
+                case INTERFACE:
+                case METHOD:
+                    return parseDeclaration();
+                default:
+                    return error();
+            }
+        }
+
+//        public Status parseExpressionOrDeclaration() {
+//            if (token.kind == IDENTIFIER) {
+//                nextToken();
+//                switch (token.kind) {
+//                    case IDENTIFIER:
+//                        return parseDeclaration();
+//                }
+//            }
+//            while (token.kind.isExpressionOrDeclaration()) {
+//                if (!token.kind.isExpression()) {
+//                    return parseDeclaration();
+//                }
+//                if (!token.kind.isDeclaration()) {
+//                    // Expression not declaration
+//                    if (token.kind == EQ) {
+//                        // Check for array initializer
+//                        nextToken();
+//                        if (token.kind == BRACES) {
+//                            nextToken();
+//                            return lastly(SEMI);
+//                        }
+//                    }
+//                    return parseExpressionStatement();
+//                }
+//                nextToken();
+//            }
+//            switch (token.kind) {
+//                case BRACES:
+//                case SEMI:
+//                    nextToken();
+//                    return Status.COMPLETE;
+//                case UNMATCHED:
+//                    nextToken();
+//                    return Status.DEFINITELY_INCOMPLETE;
+//                case EOF:
+//                    if (in.prevCT.kind.isOkToTerminate()) {
+//                        return Status.COMPLETE_WITH_SEMI;
+//                    } else {
+//                        return Status.DEFINITELY_INCOMPLETE;
+//                    }
+//                default:
+//                    return error();
+//            }
+//        }
+
+        public Completeness parseExpressionStatement() {
+            if (shouldAbort(parseExpression()))  return checkResult;
+            return lastly(SEMI);
+        }
+
+        public Completeness parseExpressionOptionalSemi() {
+            if (shouldAbort(parseExpression())) return checkResult;
+            return optionalFinalSemi();
+        }
+
+        public Completeness parseExpression() {
+            while (token.kind.isExpression())
+                nextToken();
+            return Completeness.COMPLETE;
+        }
+
+        public Completeness parseStatement() {
+            Completeness stat = parseSimpleStatement();
+            if (stat == null) {
+                return parseExpressionStatement();
+            }
+            return stat;
+        }
+
+        /**
+         * Statement = Block | IF ParExpression Statement [ELSE Statement] | FOR
+         * "(" ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement | FOR
+         * "(" FormalParameter : Expression ")" Statement | WHILE ParExpression
+         * Statement | DO Statement WHILE ParExpression ";" | TRY Block (
+         * Catches | [Catches] FinallyPart ) | TRY "(" ResourceSpecification
+         * ";"opt ")" Block [Catches] [FinallyPart] | SWITCH ParExpression "{"
+         * SwitchBlockStatementGroups "}" | SYNCHRONIZED ParExpression Block |
+         * RETURN [Expression] ";" | THROW Expression ";" | BREAK [Ident] ";" |
+         * CONTINUE [Ident] ";" | ASSERT Expression [ ":" Expression ] ";" | ";"
+         */
+        public Completeness parseSimpleStatement() {
+            switch (token.kind) {
+                case BRACES:
+                    return lastly(BRACES);
+                case IF: {
+                    nextToken();
+                    if (shouldAbort(PARENS))  return checkResult;
+                    Completeness thenpart = parseStatement();
+                    if (shouldAbort(thenpart)) return thenpart;
+                    if (token.kind == ELSE) {
+                        nextToken();
+                        return parseStatement();
+                    }
+                    return thenpart;
+
+                }
+                case FOR: {
+                    nextToken();
+                    if (shouldAbort(PARENS))  return checkResult;
+                    if (shouldAbort(parseStatement()))  return checkResult;
+                    return Completeness.COMPLETE;
+                }
+                case WHILE: {
+                    nextToken();
+                    if (shouldAbort(PARENS))  return error();
+                    return parseStatement();
+                }
+                case DO: {
+                    nextToken();
+                    switch (parseStatement()) {
+                        case DEFINITELY_INCOMPLETE:
+                        case CONSIDERED_INCOMPLETE:
+                        case COMPLETE_WITH_SEMI:
+                            return Completeness.DEFINITELY_INCOMPLETE;
+                        case UNKNOWN:
+                            return error();
+                        case COMPLETE:
+                            break;
+                    }
+                    if (shouldAbort(WHILE))  return checkResult;
+                    if (shouldAbort(PARENS)) return checkResult;
+                    return lastly(SEMI);
+                }
+                case TRY: {
+                    boolean hasResources = false;
+                    nextToken();
+                    if (token.kind == PARENS) {
+                        nextToken();
+                        hasResources = true;
+                    }
+                    if (shouldAbort(BRACES))  return checkResult;
+                    if (token.kind == CATCH || token.kind == FINALLY) {
+                        while (token.kind == CATCH) {
+                            if (shouldAbort(CATCH))  return checkResult;
+                            if (shouldAbort(PARENS)) return checkResult;
+                            if (shouldAbort(BRACES)) return checkResult;
+                        }
+                        if (token.kind == FINALLY) {
+                            if (shouldAbort(FINALLY))  return checkResult;
+                            if (shouldAbort(BRACES)) return checkResult;
+                        }
+                    } else if (!hasResources) {
+                        if (token.kind == EOF) {
+                            return Completeness.DEFINITELY_INCOMPLETE;
+                        } else {
+                            return error();
+                        }
+                    }
+                    return Completeness.COMPLETE;
+                }
+                case SWITCH: {
+                    nextToken();
+                    if (shouldAbort(PARENS))  return checkResult;
+                    return lastly(BRACES);
+                }
+                case SYNCHRONIZED: {
+                    nextToken();
+                    if (shouldAbort(PARENS))  return checkResult;
+                    return lastly(BRACES);
+                }
+                case THROW: {
+                    nextToken();
+                    if (shouldAbort(parseExpression()))  return checkResult;
+                    return lastly(SEMI);
+                }
+                case SEMI:
+                    return lastly(SEMI);
+                case ASSERT:
+                    nextToken();
+                    // Crude expression parsing just happily eats the optional colon
+                    return parseExpressionStatement();
+                case RETURN:
+                case BREAK:
+                case CONTINUE:
+                    nextToken();
+                    return parseExpressionStatement();
+                // What are these doing here?
+                case ELSE:
+                case FINALLY:
+                case CATCH:
+                    return error();
+                case EOF:
+                    return Completeness.CONSIDERED_INCOMPLETE;
+                default:
+                    return null;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jshell;
+
+import java.util.Collection;
+import jdk.jshell.Key.DeclarationKey;
+
+/**
+ * Grouping for all declaration Snippets: variable declarations
+ * ({@link jdk.jshell.VarSnippet}), method declarations
+ * ({@link jdk.jshell.MethodSnippet}), and type declarations
+ * ({@link jdk.jshell.TypeDeclSnippet}).
+ * <p>
+ * Declaration snippets are unique in that they can be active
+ *  with unresolved references:
+ * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED} or
+ * {@link jdk.jshell.Snippet.Status#RECOVERABLE_NOT_DEFINED RECOVERABLE_NOT_DEFINED}.
+ * Unresolved references can be queried with
+ * {@link jdk.jshell.JShell#unresolvedDependencies(jdk.jshell.DeclarationSnippet)
+ * JShell.unresolvedDependencies(DeclarationSnippet)}.
+ * <p>
+ * <code>DeclarationSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ */
+public abstract class DeclarationSnippet extends PersistentSnippet {
+
+    private final Wrap corralled;
+    private final Collection<String> declareReferences;
+    private final Collection<String> bodyReferences;
+
+    DeclarationSnippet(DeclarationKey key, String userSource, Wrap guts,
+            String unitName, SubKind subkind, Wrap corralled,
+            Collection<String> declareReferences,
+            Collection<String> bodyReferences) {
+        super(key, userSource, guts, unitName, subkind);
+        this.corralled = corralled;
+        this.declareReferences = declareReferences;
+        this.bodyReferences = bodyReferences;
+    }
+
+    /**** internal access ****/
+
+    /**
+     * @return the corralled guts
+     */
+    @Override
+    Wrap corralled() {
+        return corralled;
+    }
+
+    @Override
+    Collection<String> declareReferences() {
+        return declareReferences;
+    }
+
+    @Override
+    Collection<String> bodyReferences() {
+        return bodyReferences;
+    }
+
+    @Override
+    String importLine(JShell state) {
+        return "import static " + state.maps.classFullName(this) + "." + name() + ";\n";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Diag.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import java.util.Locale;
+import javax.tools.Diagnostic;
+
+/**
+ * Diagnostic information for a Snippet.
+ * @see jdk.jshell.JShell#diagnostics(jdk.jshell.Snippet)
+ */
+public abstract class Diag {
+    // Simplified view on compiler Diagnostic.
+
+    /**
+     * Used to signal that no position is available.
+     */
+    public final static long NOPOS = Diagnostic.NOPOS;
+
+    /**
+     * Is this diagnostic and error (as opposed to a warning or note)
+     * @return true if this diagnostic is an error
+     */
+    public abstract boolean isError();
+
+    /**
+     * Returns a character offset from the beginning of the source object
+     * associated with this diagnostic that indicates the location of
+     * the problem.  In addition, the following must be true:
+     *
+     * <p>{@code getStartPostion() <= getPosition()}
+     * <p>{@code getPosition() <= getEndPosition()}
+     *
+     * @return character offset from beginning of source; {@link
+     * #NOPOS} if {@link #getSource()} would return {@code null} or if
+     * no location is suitable
+     */
+    public abstract long getPosition();
+
+    /**
+     * Returns the character offset from the beginning of the file
+     * associated with this diagnostic that indicates the start of the
+     * problem.
+     *
+     * @return offset from beginning of file; {@link #NOPOS} if and
+     * only if {@link #getPosition()} returns {@link #NOPOS}
+     */
+    public abstract long getStartPosition();
+
+    /**
+     * Returns the character offset from the beginning of the file
+     * associated with this diagnostic that indicates the end of the
+     * problem.
+     *
+     * @return offset from beginning of file; {@link #NOPOS} if and
+     * only if {@link #getPosition()} returns {@link #NOPOS}
+     */
+    public abstract long getEndPosition();
+
+    /**
+     * Returns a diagnostic code indicating the type of diagnostic.  The
+     * code is implementation-dependent and might be {@code null}.
+     *
+     * @return a diagnostic code
+     */
+    public abstract String getCode();
+
+    /**
+     * Returns a localized message for the given locale.  The actual
+     * message is implementation-dependent.  If the locale is {@code
+     * null} use the default locale.
+     *
+     * @param locale a locale; might be {@code null}
+     * @return a localized message
+     */
+    public abstract String getMessage(Locale locale);
+
+    // *** Internal support ***
+
+    /**
+     * Internal: If this is from a compile, extract the compilation Unit.
+     * Otherwise null.
+     */
+    abstract Unit unitOrNull();
+
+    /**
+     * This is an unreachable-statement error
+     */
+    boolean isUnreachableError() {
+        return getCode().equals("compiler.err.unreachable.stmt");
+    }
+
+    /**
+     * This is a not-a-statement error
+     */
+    boolean isNotAStatementError() {
+        return getCode().equals("compiler.err.not.stmt");
+    }
+
+    /**
+     * This is a resolution error.
+     */
+    boolean isResolutionError() {
+        //TODO: try javac RESOLVE_ERROR flag
+        return getCode().startsWith("compiler.err.cant.resolve");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/DiagList.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+/**
+ * List of diagnostics, with convenient operations.
+ *
+ * @author Robert Field
+ */
+@SuppressWarnings("serial")             // serialVersionUID intentionally omitted
+final class DiagList extends ArrayList<Diag> {
+
+    private int cntNotStmt = 0;
+    private int cntUnreach = 0;
+    private int cntResolve = 0;
+    private int cntOther = 0;
+
+    DiagList() {
+        super();
+    }
+
+    DiagList(Diag d) {
+        super();
+        add(d);
+    }
+
+    DiagList(Collection<? extends Diag> c) {
+        super();
+        addAll(c);
+    }
+
+    private void tally(Diag d) {
+        if (d.isError()) {
+            if (d.isUnreachableError()) {
+                ++cntUnreach;
+            } else if (d.isNotAStatementError()) {
+                ++cntNotStmt;
+            } else if (d.isResolutionError()) {
+                ++cntResolve;
+            } else {
+                ++cntOther;
+            }
+        }
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends Diag> c) {
+        return c.stream().filter(d -> add(d)).count() > 0;
+    }
+
+    @Override
+    public Diag set(int index, Diag element) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void add(int index, Diag element) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean add(Diag d) {
+        boolean added = super.add(d);
+        if (added) {
+            tally(d);
+        }
+        return added;
+    }
+
+    @Override
+    public boolean addAll(int index, Collection<? extends Diag> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    DiagList ofUnit(Unit u) {
+        return this.stream()
+                .filter(d -> d.unitOrNull() == u)
+                .collect(Collectors.toCollection(() -> new DiagList()));
+    }
+
+    boolean hasErrors() {
+        return (cntNotStmt + cntResolve + cntUnreach + cntOther) > 0;
+    }
+
+    boolean hasResolutionErrorsAndNoOthers() {
+        return cntResolve > 0 && (cntNotStmt + cntUnreach + cntOther) == 0;
+    }
+
+    boolean hasUnreachableError() {
+        return cntUnreach > 0;
+    }
+
+    boolean hasNotStatement() {
+        return cntNotStmt > 0;
+    }
+
+    boolean hasOtherThanNotStatementErrors() {
+        return (cntResolve + cntUnreach + cntOther) > 0;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jshell;
+
+import jdk.jshell.Key.ErroneousKey;
+
+/**
+ * A snippet of code that is not valid Java programming language code, and for
+ * which the kind of snippet could not be determined.
+ * The Kind is {@link jdk.jshell.Snippet.Kind#ERRONEOUS ERRONEOUS}.
+ * <p>
+ * <code>ErroneousSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ */
+public class ErroneousSnippet extends Snippet {
+
+    ErroneousSnippet(ErroneousKey key, String userSource, Wrap guts, SubKind subkind) {
+        super(key, userSource, guts, null, subkind);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jshell;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Modifier;
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ModifiersTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.Pretty;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import jdk.jshell.ClassTracker.ClassInfo;
+import jdk.jshell.Key.ErroneousKey;
+import jdk.jshell.Key.MethodKey;
+import jdk.jshell.Snippet.SubKind;
+import jdk.jshell.TaskFactory.AnalyzeTask;
+import jdk.jshell.TaskFactory.BaseTask;
+import jdk.jshell.TaskFactory.CompileTask;
+import jdk.jshell.TaskFactory.ParseTask;
+import jdk.jshell.TreeDissector.ExpressionInfo;
+import jdk.jshell.Wrap.Range;
+import jdk.jshell.Snippet.Status;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
+import static jdk.jshell.Util.*;
+import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
+import static jdk.internal.jshell.remote.RemoteCodes.prefixPattern;
+import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
+import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
+import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND;
+import static jdk.jshell.Snippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND;
+
+/**
+ * The Evaluation Engine. Source internal analysis, wrapping control,
+ * compilation, declaration. redefinition, replacement, and execution.
+ *
+ * @author Robert Field
+ */
+class Eval {
+
+    private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\p{javaWhitespace}+(?<static>static\\p{javaWhitespace}+)?(?<fullname>[\\p{L}\\p{N}_\\$\\.]+\\.(?<name>[\\p{L}\\p{N}_\\$]+|\\*))");
+
+    private int varNumber = 0;
+
+    private final JShell state;
+
+    Eval(JShell state) {
+        this.state = state;
+    }
+
+    List<SnippetEvent> eval(String userSource) throws IllegalStateException {
+        String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared());
+        if (compileSource.length() == 0) {
+            return Collections.emptyList();
+        }
+        // String folding messes up position information.
+        ParseTask pt = state.taskFactory.new ParseTask(compileSource);
+        if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
+            return compileFailResult(pt, userSource);
+        }
+
+        List<? extends Tree> units = pt.units();
+        if (units.isEmpty()) {
+            return compileFailResult(pt, userSource);
+        }
+        // Erase illegal modifiers
+        compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
+        Tree unitTree = units.get(0);
+        state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
+        switch (unitTree.getKind()) {
+            case IMPORT:
+                return processImport(userSource, compileSource);
+            case VARIABLE:
+                return processVariables(userSource, units, compileSource, pt);
+            case EXPRESSION_STATEMENT:
+                return processExpression(userSource, compileSource);
+            case CLASS:
+                return processClass(userSource, unitTree, compileSource, SubKind.CLASS_SUBKIND, pt);
+            case ENUM:
+                return processClass(userSource, unitTree, compileSource, SubKind.ENUM_SUBKIND, pt);
+            case ANNOTATION_TYPE:
+                return processClass(userSource, unitTree, compileSource, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
+            case INTERFACE:
+                return processClass(userSource, unitTree, compileSource, SubKind.INTERFACE_SUBKIND, pt);
+            case METHOD:
+                return processMethod(userSource, unitTree, compileSource, pt);
+            default:
+                return processStatement(userSource, compileSource);
+        }
+    }
+
+    private List<SnippetEvent> processImport(String userSource, String compileSource) {
+        Wrap guts = Wrap.importWrap(compileSource);
+        Matcher mat = IMPORT_PATTERN.matcher(compileSource);
+        String fullname;
+        String name;
+        boolean isStatic;
+        if (mat.find()) {
+            isStatic = mat.group("static") != null;
+            name = mat.group("name");
+            fullname = mat.group("fullname");
+        } else {
+            // bad import -- fake it
+            isStatic = compileSource.contains("static");
+            name = fullname = compileSource;
+        }
+        String fullkey = (isStatic ? "static-" : "") + fullname;
+        boolean isStar = name.equals("*");
+        String keyName = isStar
+                ? fullname
+                : name;
+        SubKind snippetKind = isStar
+                ? (isStatic ? STATIC_IMPORT_ON_DEMAND_SUBKIND : TYPE_IMPORT_ON_DEMAND_SUBKIND)
+                : (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND);
+        Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind),
+                userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar);
+        return declare(snip);
+    }
+
+    private static class EvalPretty extends Pretty {
+
+        private final Writer out;
+
+        public EvalPretty(Writer writer, boolean bln) {
+            super(writer, bln);
+            this.out = writer;
+        }
+
+        /**
+         * Print string, DO NOT replacing all non-ascii character with unicode
+         * escapes.
+         */
+        @Override
+        public void print(Object o) throws IOException {
+            out.write(o.toString());
+        }
+
+        static String prettyExpr(JCTree tree, boolean bln) {
+            StringWriter out = new StringWriter();
+            try {
+                new EvalPretty(out, bln).printExpr(tree);
+            } catch (IOException e) {
+                throw new AssertionError(e);
+            }
+            return out.toString();
+        }
+    }
+
+    private List<SnippetEvent> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
+        List<SnippetEvent> allEvents = new ArrayList<>();
+        TreeDissector dis = new TreeDissector(pt);
+        for (Tree unitTree : units) {
+            VariableTree vt = (VariableTree) unitTree;
+            String name = vt.getName().toString();
+            String typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
+            Tree baseType = vt.getType();
+            TreeDependencyScanner tds = new TreeDependencyScanner();
+            tds.scan(baseType); // Not dependent on initializer
+            StringBuilder sbBrackets = new StringBuilder();
+            while (baseType instanceof ArrayTypeTree) {
+                //TODO handle annotations too
+                baseType = ((ArrayTypeTree) baseType).getType();
+                sbBrackets.append("[]");
+            }
+            Range rtype = dis.treeToRange(baseType);
+            Range runit = dis.treeToRange(vt);
+            runit = new Range(runit.begin, runit.end - 1);
+            ExpressionTree it = vt.getInitializer();
+            Range rinit = null;
+            int nameMax = runit.end - 1;
+            SubKind subkind;
+            if (it != null) {
+                subkind = SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND;
+                rinit = dis.treeToRange(it);
+                nameMax = rinit.begin - 1;
+            } else {
+                subkind = SubKind.VAR_DECLARATION_SUBKIND;
+            }
+            int nameStart = compileSource.lastIndexOf(name, nameMax);
+            if (nameStart < 0) {
+                throw new AssertionError("Name '" + name + "' not found");
+            }
+            int nameEnd = nameStart + name.length();
+            Range rname = new Range(nameStart, nameEnd);
+            Wrap guts = Wrap.varWrap(compileSource, rtype, sbBrackets.toString(), rname, rinit);
+            Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
+                    name, subkind, typeName,
+                    tds.declareReferences());
+            DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
+            List<SnippetEvent> res1 = declare(snip, modDiag);
+            allEvents.addAll(res1);
+        }
+
+        return allEvents;
+    }
+
+    private List<SnippetEvent> processExpression(String userSource, String compileSource) {
+        String name = null;
+        ExpressionInfo ei = typeOfExpression(compileSource);
+        ExpressionTree assignVar;
+        Wrap guts;
+        Snippet snip;
+        if (ei != null && ei.isNonVoid) {
+            String typeName = ei.typeName;
+            SubKind subkind;
+            if (ei.tree instanceof IdentifierTree) {
+                IdentifierTree id = (IdentifierTree) ei.tree;
+                name = id.getName().toString();
+                subkind = SubKind.VAR_VALUE_SUBKIND;
+
+            } else if (ei.tree instanceof AssignmentTree
+                    && (assignVar = ((AssignmentTree) ei.tree).getVariable()) instanceof IdentifierTree) {
+                name = assignVar.toString();
+                subkind = SubKind.ASSIGNMENT_SUBKIND;
+            } else {
+                subkind = SubKind.OTHER_EXPRESSION_SUBKIND;
+            }
+            if (shouldGenTempVar(subkind)) {
+                if (state.tempVariableNameGenerator != null) {
+                    name = state.tempVariableNameGenerator.get();
+                }
+                while (name == null || state.keyMap.doesVariableNameExist(name)) {
+                    name = "$" + ++varNumber;
+                }
+                guts = Wrap.tempVarWrap(compileSource, typeName, name);
+                Collection<String> declareReferences = null; //TODO
+                snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
+                        name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences);
+            } else {
+                guts = Wrap.methodReturnWrap(compileSource);
+                snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
+                        name, subkind);
+            }
+        } else {
+            guts = Wrap.methodWrap(compileSource);
+            if (ei == null) {
+                // We got no type info, check for not a statement by trying
+                AnalyzeTask at = trialCompile(guts);
+                if (at.getDiagnostics().hasNotStatement()) {
+                    guts = Wrap.methodReturnWrap(compileSource);
+                    at = trialCompile(guts);
+                }
+                if (at.hasErrors()) {
+                    return compileFailResult(at, userSource);
+                }
+            }
+            snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
+        }
+        return declare(snip);
+    }
+
+    private List<SnippetEvent> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) {
+        TreeDependencyScanner tds = new TreeDependencyScanner();
+        tds.scan(unitTree);
+
+        TreeDissector dis = new TreeDissector(pt);
+
+        ClassTree klassTree = (ClassTree) unitTree;
+        String name = klassTree.getSimpleName().toString();
+        Wrap guts = Wrap.classMemberWrap(compileSource);
+        Wrap corralled = null; //TODO
+        Snippet snip = new TypeDeclSnippet(state.keyMap.keyForClass(name), userSource, guts,
+                name, snippetKind,
+                corralled, tds.declareReferences(), tds.bodyReferences());
+        DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
+        return declare(snip, modDiag);
+    }
+
+    private List<SnippetEvent> processStatement(String userSource, String compileSource) {
+        Wrap guts = Wrap.methodWrap(compileSource);
+        // Check for unreachable by trying
+        AnalyzeTask at = trialCompile(guts);
+        if (at.hasErrors()) {
+            if (at.getDiagnostics().hasUnreachableError()) {
+                guts = Wrap.methodUnreachableSemiWrap(compileSource);
+                at = trialCompile(guts);
+                if (at.hasErrors()) {
+                    if (at.getDiagnostics().hasUnreachableError()) {
+                        // Without ending semicolon
+                        guts = Wrap.methodUnreachableWrap(compileSource);
+                        at = trialCompile(guts);
+                    }
+                    if (at.hasErrors()) {
+                        return compileFailResult(at, userSource);
+                    }
+                }
+            } else {
+                return compileFailResult(at, userSource);
+            }
+        }
+        Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
+        return declare(snip);
+    }
+
+    private OuterWrap wrapInClass(String className, Set<Key> except, String userSource, Wrap guts, Collection<Snippet> plus) {
+        String imports = state.maps.packageAndImportsExcept(except, plus);
+        return OuterWrap.wrapInClass(state.maps.packageName(), className, imports, userSource, guts);
+    }
+
+    OuterWrap wrapInClass(Snippet snip, Set<Key> except, Wrap guts, Collection<Snippet> plus) {
+        return wrapInClass(snip.className(), except, snip.source(), guts, plus);
+    }
+
+    private AnalyzeTask trialCompile(Wrap guts) {
+        OuterWrap outer = wrapInClass(REPL_DOESNOTMATTER_CLASS_NAME,
+                Collections.emptySet(), "", guts, null);
+        return state.taskFactory.new AnalyzeTask(outer);
+    }
+
+    private List<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
+        TreeDependencyScanner tds = new TreeDependencyScanner();
+        tds.scan(unitTree);
+
+        MethodTree mt = (MethodTree) unitTree;
+        TreeDissector dis = new TreeDissector(pt);
+        DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
+        if (modDiag.hasErrors()) {
+            return compileFailResult(modDiag, userSource);
+        }
+        String unitName = mt.getName().toString();
+        Wrap guts = Wrap.classMemberWrap(compileSource);
+
+        Range modRange = dis.treeToRange(mt.getModifiers());
+        Range tpRange = dis.treeListToRange(mt.getTypeParameters());
+        Range typeRange = dis.treeToRange(mt.getReturnType());
+        String name = mt.getName().toString();
+        Range paramRange = dis.treeListToRange(mt.getParameters());
+        Range throwsRange = dis.treeListToRange(mt.getThrows());
+
+        String parameterTypes
+                = mt.getParameters()
+                .stream()
+                .map(param -> dis.treeToRange(param.getType()).part(compileSource))
+                .collect(Collectors.joining(","));
+        String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource);
+
+        MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
+        // rewrap with correct Key index
+        Wrap corralled = Wrap.corralledMethod(compileSource,
+                modRange, tpRange, typeRange, name, paramRange, throwsRange, key.index());
+        Snippet snip = new MethodSnippet(key, userSource, guts,
+                unitName, signature,
+                corralled, tds.declareReferences(), tds.bodyReferences());
+        return declare(snip, modDiag);
+    }
+
+    /**
+     * The snippet has failed, return with the rejected event
+     *
+     * @param xt the task from which to extract the failure diagnostics
+     * @param userSource the incoming bad user source
+     * @return a rejected snippet event
+     */
+    private List<SnippetEvent> compileFailResult(BaseTask xt, String userSource) {
+        return compileFailResult(xt.getDiagnostics(), userSource);
+    }
+
+    /**
+     * The snippet has failed, return with the rejected event
+     *
+     * @param diags the failure diagnostics
+     * @param userSource the incoming bad user source
+     * @return a rejected snippet event
+     */
+    private List<SnippetEvent> compileFailResult(DiagList diags, String userSource) {
+        ErroneousKey key = state.keyMap.keyForErroneous();
+        Snippet snip = new ErroneousSnippet(key, userSource, null, SubKind.UNKNOWN_SUBKIND);
+        snip.setFailed(diags);
+        state.maps.installSnippet(snip);
+        return Collections.singletonList(new SnippetEvent(
+                snip, Status.NONEXISTENT, Status.REJECTED,
+                false, null, null, null)
+        );
+    }
+
+    private ExpressionInfo typeOfExpression(String expression) {
+        Wrap guts = Wrap.methodReturnWrap(expression);
+        TaskFactory.AnalyzeTask at = trialCompile(guts);
+        if (!at.hasErrors() && at.cuTree() != null) {
+            return new TreeDissector(at)
+                    .typeOfReturnStatement(at.messages(), state.maps::fullClassNameAndPackageToClass);
+        }
+        return null;
+    }
+
+    /**
+     * Should a temp var wrap the expression. TODO make this user configurable.
+     *
+     * @param snippetKind
+     * @return
+     */
+    private boolean shouldGenTempVar(SubKind snippetKind) {
+        return snippetKind == SubKind.OTHER_EXPRESSION_SUBKIND;
+    }
+
+    List<SnippetEvent> drop(Snippet si) {
+        Unit c = new Unit(state, si);
+
+        Set<Unit> ins = c.dependents().collect(toSet());
+        Set<Unit> outs = compileAndLoad(ins);
+
+        return events(c, outs, null, null);
+    }
+
+    private List<SnippetEvent> declare(Snippet si) {
+        return declare(si, new DiagList());
+    }
+
+    private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) {
+        Unit c = new Unit(state, si, null, generatedDiagnostics);
+
+        // Ignores duplicates
+        //TODO: remove, modify, or move to edit
+        if (c.isRedundant()) {
+            return Collections.emptyList();
+        }
+
+        Set<Unit> ins = new LinkedHashSet<>();
+        ins.add(c);
+        Set<Unit> outs = compileAndLoad(ins);
+
+        if (!si.status().isDefined
+                && si.diagnostics().isEmpty()
+                && si.unresolved().isEmpty()) {
+            // did not succeed, but no record of it, extract from others
+            si.setDiagnostics(outs.stream()
+                    .flatMap(u -> u.snippet().diagnostics().stream())
+                    .collect(Collectors.toCollection(DiagList::new)));
+        }
+
+        // If appropriate, execute the snippet
+        String value = null;
+        Exception exception = null;
+        if (si.isExecutable() && si.status().isDefined) {
+            try {
+                value = state.executionControl().commandInvoke(state.maps.classFullName(si));
+                value = si.subKind().hasValue()
+                        ? expunge(value)
+                        : "";
+            } catch (EvalException ex) {
+                exception = translateExecutionException(ex);
+            } catch (UnresolvedReferenceException ex) {
+                exception = ex;
+            }
+        }
+        return events(c, outs, value, exception);
+    }
+
+    private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, Exception exception) {
+        List<SnippetEvent> events = new ArrayList<>();
+        events.add(c.event(value, exception));
+        events.addAll(outs.stream()
+                .filter(u -> u != c)
+                .map(u -> u.event(null, null))
+                .collect(Collectors.toList()));
+        events.addAll(outs.stream()
+                .flatMap(u -> u.secondaryEvents().stream())
+                .collect(Collectors.toList()));
+        //System.err.printf("Events: %s\n", events);
+        return events;
+    }
+
+    private Set<Unit> compileAndLoad(Set<Unit> ins) {
+        if (ins.isEmpty()) {
+            return ins;
+        }
+        Set<Unit> replaced = new LinkedHashSet<>();
+        while (true) {
+            state.debug(DBG_GEN, "compileAndLoad  %s\n", ins);
+
+            ins.stream().forEach(u -> u.initialize(ins));
+            AnalyzeTask at = state.taskFactory.new AnalyzeTask(ins);
+            ins.stream().forEach(u -> u.setDiagnostics(at));
+            // corral any Snippets that need it
+            if (ins.stream().filter(u -> u.corralIfNeeded(ins)).count() > 0) {
+                // if any were corralled, re-analyze everything
+                AnalyzeTask cat = state.taskFactory.new AnalyzeTask(ins);
+                ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
+            }
+            ins.stream().forEach(u -> u.setStatus());
+            // compile and load the legit snippets
+            boolean success;
+            while (true) {
+                List<Unit> legit = ins.stream()
+                        .filter(u -> u.isDefined())
+                        .collect(toList());
+                state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n",
+                        ins, legit);
+                if (legit.isEmpty()) {
+                    // no class files can be generated
+                    success = true;
+                } else {
+                    // re-wrap with legit imports
+                    legit.stream().forEach(u -> u.setWrap(ins, legit));
+
+                    // generate class files for those capable
+                    CompileTask ct = state.taskFactory.new CompileTask(legit);
+                    if (!ct.compile()) {
+                        // oy! compile failed because of recursive new unresolved
+                        if (legit.stream()
+                                .filter(u -> u.smashingErrorDiagnostics(ct))
+                                .count() > 0) {
+                            // try again, with the erroreous removed
+                            continue;
+                        } else {
+                            state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
+                                    legit);
+                        }
+                    }
+
+                    // load all new classes
+                    load(legit.stream()
+                            .flatMap(u -> u.classesToLoad(ct.classInfoList(u)))
+                            .collect(toList()));
+                    // attempt to redefine the remaining classes
+                    List<Unit> toReplace = legit.stream()
+                            .filter(u -> !u.doRedefines())
+                            .collect(toList());
+
+                    // prevent alternating redefine/replace cyclic dependency
+                    // loop by replacing all that have been replaced
+                    if (!toReplace.isEmpty()) {
+                        replaced.addAll(toReplace);
+                        replaced.stream().forEach(u -> u.markForReplacement());
+                    }
+
+                    success = toReplace.isEmpty();
+                }
+                break;
+            }
+
+            // add any new dependencies to the working set
+            List<Unit> newDependencies = ins.stream()
+                    .flatMap(u -> u.effectedDependents())
+                    .collect(toList());
+            state.debug(DBG_GEN, "compileAndLoad %s -- deps: %s  success: %s\n",
+                    ins, newDependencies, success);
+            if (!ins.addAll(newDependencies) && success) {
+                // all classes that could not be directly loaded (because they
+                // are new) have been redefined, and no new dependnencies were
+                // identified
+                ins.stream().forEach(u -> u.finish());
+                return ins;
+            }
+        }
+    }
+
+    private void load(List<ClassInfo> cil) {
+        if (!cil.isEmpty()) {
+            state.executionControl().commandLoad(cil);
+        }
+    }
+
+    private EvalException translateExecutionException(EvalException ex) {
+        StackTraceElement[] raw = ex.getStackTrace();
+        int last = raw.length;
+        do {
+            if (last == 0) {
+                last = raw.length - 1;
+                break;
+            }
+        } while (!isWrap(raw[--last]));
+        StackTraceElement[] elems = new StackTraceElement[last + 1];
+        for (int i = 0; i <= last; ++i) {
+            StackTraceElement r = raw[i];
+            String rawKlass = r.getClassName();
+            Matcher matcher = prefixPattern.matcher(rawKlass);
+            String num;
+            if (matcher.find() && (num = matcher.group("num")) != null) {
+                int end = matcher.end();
+                if (rawKlass.charAt(end - 1) == '$') {
+                    --end;
+                }
+                int id = Integer.parseInt(num);
+                Snippet si = state.maps.getSnippet(id);
+                String klass = expunge(rawKlass);
+                String method = r.getMethodName().equals(DOIT_METHOD_NAME) ? "" : r.getMethodName();
+                String file = "#" + id;
+                int line = si.outerWrap().wrapLineToSnippetLine(r.getLineNumber() - 1) + 1;
+                elems[i] = new StackTraceElement(klass, method, file, line);
+            } else if (r.getFileName().equals("<none>")) {
+                elems[i] = new StackTraceElement(r.getClassName(), r.getMethodName(), null, r.getLineNumber());
+            } else {
+                elems[i] = r;
+            }
+        }
+        String msg = ex.getMessage();
+        if (msg.equals("<none>")) {
+            msg = null;
+        }
+        return new EvalException(msg, ex.getExceptionClassName(), elems);
+    }
+
+    private boolean isWrap(StackTraceElement ste) {
+        return prefixPattern.matcher(ste.getClassName()).find();
+    }
+
+    private DiagList modifierDiagnostics(ModifiersTree modtree,
+            final TreeDissector dis, boolean isAbstractProhibited) {
+
+        class ModifierDiagnostic extends Diag {
+
+            final boolean fatal;
+            final String message;
+
+            ModifierDiagnostic(List<Modifier> list, boolean fatal) {
+                this.fatal = fatal;
+                StringBuilder sb = new StringBuilder();
+                sb.append((list.size() > 1) ? "Modifiers " : "Modifier ");
+                for (Modifier mod : list) {
+                    sb.append("'");
+                    sb.append(mod.toString());
+                    sb.append("' ");
+                }
+                sb.append("not permitted in top-level declarations");
+                if (!fatal) {
+                    sb.append(", ignored");
+                }
+                this.message = sb.toString();
+            }
+
+            @Override
+            public boolean isError() {
+                return fatal;
+            }
+
+            @Override
+            public long getPosition() {
+                return dis.getStartPosition(modtree);
+            }
+
+            @Override
+            public long getStartPosition() {
+                return dis.getStartPosition(modtree);
+            }
+
+            @Override
+            public long getEndPosition() {
+                return dis.getEndPosition(modtree);
+            }
+
+            @Override
+            public String getCode() {
+                return fatal
+                        ? "jdk.eval.error.illegal.modifiers"
+                        : "jdk.eval.warn.illegal.modifiers";
+            }
+
+            @Override
+            public String getMessage(Locale locale) {
+                return message;
+            }
+
+            @Override
+            Unit unitOrNull() {
+                return null;
+            }
+        }
+
+        List<Modifier> list = new ArrayList<>();
+        boolean fatal = false;
+        for (Modifier mod : modtree.getFlags()) {
+            switch (mod) {
+                case SYNCHRONIZED:
+                case NATIVE:
+                    list.add(mod);
+                    fatal = true;
+                    break;
+                case ABSTRACT:
+                    if (isAbstractProhibited) {
+                        list.add(mod);
+                        fatal = true;
+                    }
+                    break;
+                case PUBLIC:
+                case PROTECTED:
+                case PRIVATE:
+                case STATIC:
+                case FINAL:
+                    list.add(mod);
+                    break;
+            }
+        }
+        return list.isEmpty()
+                ? new DiagList()
+                : new DiagList(new ModifierDiagnostic(list, fatal));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/EvalException.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+/**
+ * Wraps an exception thrown in the remotely executing client.
+ * An instance of <code>EvalException</code> can be returned in the
+ * {@link jdk.jshell.SnippetEvent#exception()} query.
+ * The name of the exception thrown is available from
+ * {@link jdk.jshell.EvalException#getExceptionClassName()}.
+ * Message and stack can be queried by methods on <code>Exception</code>.
+ * <p>
+ * Note that in stack trace frames representing JShell Snippets,
+ * <code>StackTraceElement.getFileName()</code> will return "#" followed by
+ * the Snippet id and for snippets without a method name (for example an
+ * expression) <code>StackTraceElement.getMethodName()</code> will be the
+ * empty string.
+ */
+@SuppressWarnings("serial")             // serialVersionUID intentionally omitted
+public class EvalException extends Exception {
+    private final String exceptionClass;
+
+    EvalException(String message, String exceptionClass, StackTraceElement[] stackElements) {
+        super(message);
+        this.exceptionClass = exceptionClass;
+        this.setStackTrace(stackElements);
+    }
+
+    /**
+     * Returns the name of the Throwable subclass which was thrown in the
+     * executing client. Note this class may not be loaded in the controlling
+     * process.
+     * See
+     * {@link java.lang.Class#getName() Class.getName()} for the format of the string.
+     * @return the name of the exception class as a String
+     */
+    public String getExceptionClassName() {
+        return exceptionClass;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import static jdk.internal.jshell.remote.RemoteCodes.*;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import com.sun.jdi.*;
+import java.io.EOFException;
+import java.util.List;
+import java.util.Map;
+import jdk.jshell.ClassTracker.ClassInfo;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
+
+/**
+ * Controls the remote execution environment.
+ *
+ * @author Robert Field
+ */
+class ExecutionControl {
+
+    private final JDIEnv env;
+    private final SnippetMaps maps;
+    private JDIEventHandler handler;
+    private Socket socket;
+    private ObjectInputStream in;
+    private ObjectOutputStream out;
+    private final JShell proc;
+
+    ExecutionControl(JDIEnv env, SnippetMaps maps, JShell proc) {
+        this.env = env;
+        this.maps = maps;
+        this.proc = proc;
+    }
+
+    void launch() throws IOException {
+        try (ServerSocket listener = new ServerSocket(0)) {
+            // timeout after 60 seconds
+            listener.setSoTimeout(60000);
+            int port = listener.getLocalPort();
+            jdiGo(port);
+            socket = listener.accept();
+            // out before in -- match remote creation so we don't hang
+            out = new ObjectOutputStream(socket.getOutputStream());
+            in = new ObjectInputStream(socket.getInputStream());
+        }
+    }
+
+    void commandExit() {
+        try {
+            if (out != null) {
+                out.writeInt(CMD_EXIT);
+                out.flush();
+            }
+            JDIConnection c = env.connection();
+            if (c != null) {
+                c.disposeVM();
+            }
+        } catch (IOException ex) {
+            proc.debug(DBG_GEN, "Exception on JDI exit: %s\n", ex);
+        }
+    }
+
+
+    boolean commandLoad(List<ClassInfo> cil) {
+        try {
+            out.writeInt(CMD_LOAD);
+            out.writeInt(cil.size());
+            for (ClassInfo ci : cil) {
+                out.writeUTF(ci.getClassName());
+                out.writeObject(ci.getBytes());
+            }
+            out.flush();
+            return readAndReportResult();
+        } catch (IOException ex) {
+            proc.debug(DBG_GEN, "IOException on remote load operation: %s\n", ex);
+            return false;
+        }
+    }
+
+    String commandInvoke(String classname) throws EvalException, UnresolvedReferenceException {
+        try {
+            synchronized (STOP_LOCK) {
+                userCodeRunning = true;
+            }
+            out.writeInt(CMD_INVOKE);
+            out.writeUTF(classname);
+            out.flush();
+            if (readAndReportExecutionResult()) {
+                String result = in.readUTF();
+                return result;
+            }
+        } catch (EOFException ex) {
+            env.shutdown();
+        } catch (IOException | ClassNotFoundException ex) {
+            proc.debug(DBG_GEN, "Exception on remote invoke: %s\n", ex);
+            return "Execution failure: " + ex.getMessage();
+        } finally {
+            synchronized (STOP_LOCK) {
+                userCodeRunning = false;
+            }
+        }
+        return "";
+    }
+
+    String commandVarValue(String classname, String varname) {
+        try {
+            out.writeInt(CMD_VARVALUE);
+            out.writeUTF(classname);
+            out.writeUTF(varname);
+            out.flush();
+            if (readAndReportResult()) {
+                String result = in.readUTF();
+                return result;
+            }
+        } catch (EOFException ex) {
+            env.shutdown();
+        } catch (IOException ex) {
+            proc.debug(DBG_GEN, "Exception on remote var value: %s\n", ex);
+            return "Execution failure: " + ex.getMessage();
+        }
+        return "";
+    }
+
+    boolean commandAddToClasspath(String cp) {
+        try {
+            out.writeInt(CMD_CLASSPATH);
+            out.writeUTF(cp);
+            out.flush();
+            return readAndReportResult();
+        } catch (IOException ex) {
+            throw new InternalError("Classpath addition failed: " + cp, ex);
+        }
+    }
+
+    boolean commandRedefine(Map<ReferenceType, byte[]> mp) {
+        try {
+            env.vm().redefineClasses(mp);
+            return true;
+        } catch (UnsupportedOperationException ex) {
+            return false;
+        } catch (Exception ex) {
+            proc.debug(DBG_GEN, "Exception on JDI redefine: %s\n", ex);
+            return false;
+        }
+    }
+
+    ReferenceType nameToRef(String name) {
+        List<ReferenceType> rtl = env.vm().classesByName(name);
+        if (rtl.size() != 1) {
+            return null;
+        }
+        return rtl.get(0);
+    }
+
+    private boolean readAndReportResult() throws IOException {
+        int ok = in.readInt();
+        switch (ok) {
+            case RESULT_SUCCESS:
+                return true;
+            case RESULT_FAIL: {
+                String ex = in.readUTF();
+                proc.debug(DBG_GEN, "Exception on remote operation: %s\n", ex);
+                return false;
+            }
+            default: {
+                proc.debug(DBG_GEN, "Bad remote result code: %s\n", ok);
+                return false;
+            }
+        }
+    }
+
+    private boolean readAndReportExecutionResult() throws IOException, ClassNotFoundException, EvalException, UnresolvedReferenceException {
+        int ok = in.readInt();
+        switch (ok) {
+            case RESULT_SUCCESS:
+                return true;
+            case RESULT_FAIL: {
+                String ex = in.readUTF();
+                proc.debug(DBG_GEN, "Exception on remote operation: %s\n", ex);
+                return false;
+            }
+            case RESULT_EXCEPTION: {
+                String exceptionClassName = in.readUTF();
+                String message = in.readUTF();
+                StackTraceElement[] elems = readStackTrace();
+                EvalException ee = new EvalException(message, exceptionClassName, elems);
+                throw ee;
+            }
+            case RESULT_CORRALLED: {
+                int id = in.readInt();
+                StackTraceElement[] elems = readStackTrace();
+                Snippet si = maps.getSnippet(id);
+                throw new UnresolvedReferenceException((MethodSnippet) si, elems);
+            }
+            case RESULT_KILLED: {
+                proc.out.println("Killed.");
+                return false;
+            }
+            default: {
+                proc.debug(DBG_GEN, "Bad remote result code: %s\n", ok);
+                return false;
+            }
+        }
+    }
+
+    private StackTraceElement[] readStackTrace() throws IOException {
+        int elemCount = in.readInt();
+        StackTraceElement[] elems = new StackTraceElement[elemCount];
+        for (int i = 0; i < elemCount; ++i) {
+            String className = in.readUTF();
+            String methodName = in.readUTF();
+            String fileName = in.readUTF();
+            int line = in.readInt();
+            elems[i] = new StackTraceElement(className, methodName, fileName, line);
+        }
+        return elems;
+    }
+
+    private void jdiGo(int port) {
+        //MessageOutput.textResources = ResourceBundle.getBundle("impl.TTYResources",
+        //        Locale.getDefault());
+
+        String connect = "com.sun.jdi.CommandLineLaunch:";
+        String cmdLine = "jdk.internal.jshell.remote.RemoteAgent";
+        String classPath = System.getProperty("java.class.path");
+        String bootclassPath = System.getProperty("sun.boot.class.path");
+        String javaArgs = "-classpath " + classPath + " -Xbootclasspath:" + bootclassPath;
+
+        String connectSpec = connect + "main=" + cmdLine + " " + port + ",options=" + javaArgs + ",";
+        boolean launchImmediately = true;
+        int traceFlags = 0;// VirtualMachine.TRACE_SENDS | VirtualMachine.TRACE_EVENTS;
+
+        env.init(connectSpec, launchImmediately, traceFlags);
+
+        if (env.connection().isOpen() && env.vm().canBeModified()) {
+            /*
+             * Connection opened on startup. Start event handler
+             * immediately, telling it (through arg 2) to stop on the
+             * VM start event.
+             */
+            handler = new JDIEventHandler(env);
+        }
+    }
+
+    private final Object STOP_LOCK = new Object();
+    private boolean userCodeRunning = false;
+
+    void commandStop() {
+        synchronized (STOP_LOCK) {
+            if (!userCodeRunning)
+                return ;
+
+            VirtualMachine vm = handler.env.vm();
+            vm.suspend();
+            try {
+                OUTER: for (ThreadReference thread : vm.allThreads()) {
+                    // could also tag the thread (e.g. using name), to find it easier
+                    for (StackFrame frame : thread.frames()) {
+                        String remoteAgentName = "jdk.internal.jshell.remote.RemoteAgent";
+                        if (remoteAgentName.equals(frame.location().declaringType().name()) &&
+                            "commandLoop".equals(frame.location().method().name())) {
+                            ObjectReference thiz = frame.thisObject();
+                            if (((BooleanValue) thiz.getValue(thiz.referenceType().fieldByName("inClientCode"))).value()) {
+                                thiz.setValue(thiz.referenceType().fieldByName("expectingStop"), vm.mirrorOf(true));
+                                ObjectReference stopInstance = (ObjectReference) thiz.getValue(thiz.referenceType().fieldByName("stopException"));
+
+                                vm.resume();
+                                proc.debug(DBG_GEN, "Attempting to stop the client code...\n");
+                                thread.stop(stopInstance);
+                                thiz.setValue(thiz.referenceType().fieldByName("expectingStop"), vm.mirrorOf(false));
+                            }
+
+                            break OUTER;
+                        }
+                    }
+                }
+            } catch (ClassNotLoadedException | IncompatibleThreadStateException | InvalidTypeException  ex) {
+                proc.debug(DBG_GEN, "Exception on remote stop: %s\n", ex);
+            } finally {
+                vm.resume();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jshell;
+
+import jdk.jshell.Key.ExpressionKey;
+
+/**
+ * Snippet for an assignment or variable-value expression.
+ * The Kind is {@link jdk.jshell.Snippet.Kind#EXPRESSION}.
+ * <p>
+ * <code>ExpressionSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ * @jls 15: Expression.
+ */
+public class ExpressionSnippet extends Snippet {
+
+    ExpressionSnippet(ExpressionKey key, String userSource, Wrap guts, String name, SubKind subkind) {
+        super(key, userSource, guts, name, subkind);
+    }
+
+    /**
+     * Variable name which is the value of the expression. Since the expression
+     * is either just a variable identifier or it is an assignment
+     * to a variable, there is always a variable which is the subject of the
+     * expression. All other forms of expression become temporary variables
+     * which are instead referenced by a {@link VarSnippet}.
+     * @return the name of the variable which is the subject of the expression.
+     */
+    @Override
+    public String name() {
+        return key().name();
+    }
+
+    /**
+     * Type of the expression
+     * @return String representation of the type of the expression.
+     */
+    public String typeName() {
+        return key().typeName();
+    }
+
+    /**** internal access ****/
+
+    @Override
+    ExpressionKey key() {
+        return (ExpressionKey) super.key();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/GeneralWrap.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+/**
+ * Common interface for all wrappings of snippet source to Java source.
+ *
+ * @author Robert Field
+ */
+interface GeneralWrap {
+
+    String wrapped();
+
+    int snippetIndexToWrapIndex(int sni);
+
+    int wrapIndexToSnippetIndex(int wi);
+
+    default int wrapIndexToSnippetIndex(long wi) {
+        return wrapIndexToSnippetIndex((int) wi);
+    }
+
+    int firstSnippetIndex();
+
+    int lastSnippetIndex();
+
+    int snippetLineToWrapLine(int snline);
+
+    int wrapLineToSnippetLine(int wline);
+
+    int firstSnippetLine();
+
+    int lastSnippetLine();
+
+    default String debugPos(long lpos) {
+        int pos = (int) lpos;
+        int len = wrapped().length();
+        return wrapped().substring(Math.max(0, pos - 10), Math.max(0, Math.min(len, pos)))
+                + "###"
+                + wrapped().substring(Math.max(0, Math.min(len, pos)), Math.max(0, Math.min(len, pos + 10)));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jshell;
+
+import jdk.jshell.Key.ImportKey;
+
+/**
+ * Snippet for an import declaration.
+ * The Kind is {@link jdk.jshell.Snippet.Kind#IMPORT}.
+ * <p>
+ * <code>ImportSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ * @jls 8.3: importDeclaration.
+ */
+public class ImportSnippet extends PersistentSnippet {
+
+    final String fullname;
+    final String fullkey;
+    final boolean isStatic;
+    final boolean isStar;
+
+    ImportSnippet(ImportKey key, String userSource, Wrap guts,
+            String fullname, String name, SubKind subkind, String fullkey,
+            boolean isStatic, boolean isStar) {
+        super(key, userSource, guts, name, subkind);
+        this.fullname = fullname;
+        this.fullkey = fullkey;
+        this.isStatic = isStatic;
+        this.isStar = isStar;
+    }
+
+    /**
+     * The identifying name of the import. For on-demand imports
+     * ({@link jdk.jshell.Snippet.SubKind#TYPE_IMPORT_ON_DEMAND_SUBKIND} or
+     * ({@link jdk.jshell.Snippet.SubKind#STATIC_IMPORT_ON_DEMAND_SUBKIND})
+     * that is the full specifier including any
+     * qualifiers and the asterisks. For single imports
+     * ({@link jdk.jshell.Snippet.SubKind#SINGLE_TYPE_IMPORT_SUBKIND} or
+     * ({@link jdk.jshell.Snippet.SubKind#SINGLE_STATIC_IMPORT_SUBKIND}),
+     * it is the imported name. That is, the unqualified name.
+     * @return the name of the import.
+     */
+    @Override
+    public String name() {
+        return key().name();
+    }
+
+    /**** internal access ****/
+
+    @Override
+    ImportKey key() {
+        return (ImportKey) super.key();
+    }
+
+    boolean isStatic() {
+        return isStatic;
+    }
+
+    @Override
+    String importLine(JShell state) {
+        return source();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/JDIConnection.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This source code is provided to illustrate the usage of a given feature
+ * or technique and has been deliberately simplified. Additional steps
+ * required for a production-quality application, such as security checks,
+ * input validation and proper error handling, might not be present in
+ * this sample code.
+ */
+
+
+package jdk.jshell;
+
+import com.sun.jdi.*;
+import com.sun.jdi.connect.*;
+
+import java.util.*;
+import java.util.regex.*;
+import java.io.*;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
+
+/**
+ * Connection to a Java Debug Interface VirtualMachine instance.
+ * Adapted from jdb VMConnection. Message handling, exception handling, and I/O
+ * redirection changed.  Interface to JShell added.
+ */
+class JDIConnection {
+
+    private VirtualMachine vm;
+    private Process process = null;
+    private int outputCompleteCount = 0;
+
+    private final JShell proc;
+    private final JDIEnv env;
+    private final Connector connector;
+    private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs;
+    private final int traceFlags;
+
+    synchronized void notifyOutputComplete() {
+        outputCompleteCount++;
+        notifyAll();
+    }
+
+    synchronized void waitOutputComplete() {
+        // Wait for stderr and stdout
+        if (process != null) {
+            while (outputCompleteCount < 2) {
+                try {wait();} catch (InterruptedException e) {}
+            }
+        }
+    }
+
+    private Connector findConnector(String name) {
+        for (Connector cntor :
+                 Bootstrap.virtualMachineManager().allConnectors()) {
+            if (cntor.name().equals(name)) {
+                return cntor;
+            }
+        }
+        return null;
+    }
+
+    private Map <String, com.sun.jdi.connect.Connector.Argument> parseConnectorArgs(Connector connector, String argString) {
+        Map<String, com.sun.jdi.connect.Connector.Argument> arguments = connector.defaultArguments();
+
+        /*
+         * We are parsing strings of the form:
+         *    name1=value1,[name2=value2,...]
+         * However, the value1...valuen substrings may contain
+         * embedded comma(s), so make provision for quoting inside
+         * the value substrings. (Bug ID 4285874)
+         */
+        String regexPattern =
+            "(quote=[^,]+,)|" +           // special case for quote=.,
+            "(\\w+=)" +                   // name=
+            "(((\"[^\"]*\")|" +           //   ( "l , ue"
+            "('[^']*')|" +                //     'l , ue'
+            "([^,'\"]+))+,)";             //     v a l u e )+ ,
+        Pattern p = Pattern.compile(regexPattern);
+        Matcher m = p.matcher(argString);
+        while (m.find()) {
+            int startPosition = m.start();
+            int endPosition = m.end();
+            if (startPosition > 0) {
+                /*
+                 * It is an error if parsing skips over any part of argString.
+                 */
+                throw new IllegalArgumentException("Illegal connector argument" +
+                                          argString);
+            }
+
+            String token = argString.substring(startPosition, endPosition);
+            int index = token.indexOf('=');
+            String name = token.substring(0, index);
+            String value = token.substring(index + 1,
+                                           token.length() - 1); // Remove comma delimiter
+
+            /*
+             * for values enclosed in quotes (single and/or double quotes)
+             * strip off enclosing quote chars
+             * needed for quote enclosed delimited substrings
+             */
+            if (name.equals("options")) {
+                StringBuilder sb = new StringBuilder();
+                for (String s : splitStringAtNonEnclosedWhiteSpace(value)) {
+                    while (isEnclosed(s, "\"") || isEnclosed(s, "'")) {
+                        s = s.substring(1, s.length() - 1);
+                    }
+                    sb.append(s);
+                    sb.append(" ");
+                }
+                value = sb.toString();
+            }
+
+            Connector.Argument argument = arguments.get(name);
+            if (argument == null) {
+                throw new IllegalArgumentException("Argument is not defined for connector:" +
+                                          name + " -- " + connector.name());
+            }
+            argument.setValue(value);
+
+            argString = argString.substring(endPosition); // Remove what was just parsed...
+            m = p.matcher(argString);                     //    and parse again on what is left.
+        }
+        if ((! argString.equals(",")) && (argString.length() > 0)) {
+            /*
+             * It is an error if any part of argString is left over,
+             * unless it was empty to begin with.
+             */
+            throw new IllegalArgumentException("Illegal connector argument" + argString);
+        }
+        return arguments;
+    }
+
+    private static boolean isEnclosed(String value, String enclosingChar) {
+        if (value.indexOf(enclosingChar) == 0) {
+            int lastIndex = value.lastIndexOf(enclosingChar);
+            if (lastIndex > 0 && lastIndex  == value.length() - 1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static List<String> splitStringAtNonEnclosedWhiteSpace(String value) throws IllegalArgumentException {
+        List<String> al = new ArrayList<>();
+        char[] arr;
+        int startPosition = 0;
+        int endPosition;
+        final char SPACE = ' ';
+        final char DOUBLEQ = '"';
+        final char SINGLEQ = '\'';
+
+        /*
+         * An "open" or "active" enclosing state is where
+         * the first valid start quote qualifier is found,
+         * and there is a search in progress for the
+         * relevant end matching quote
+         *
+         * enclosingTargetChar set to SPACE
+         * is used to signal a non open enclosing state
+         */
+        char enclosingTargetChar = SPACE;
+
+        if (value == null) {
+            throw new IllegalArgumentException("value string is null");
+        }
+
+        // split parameter string into individual chars
+        arr = value.toCharArray();
+
+        for (int i = 0; i < arr.length; i++) {
+            switch (arr[i]) {
+                case SPACE: {
+                    // do nothing for spaces
+                    // unless last in array
+                    if (isLastChar(arr, i)) {
+                        endPosition = i;
+                        // break for substring creation
+                        break;
+                    }
+                    continue;
+                }
+                case DOUBLEQ:
+                case SINGLEQ: {
+                    if (enclosingTargetChar == arr[i]) {
+                        // potential match to close open enclosing
+                        if (isNextCharWhitespace(arr, i)) {
+                            // if peek next is whitespace
+                            // then enclosing is a valid substring
+                            endPosition = i;
+                            // reset enclosing target char
+                            enclosingTargetChar = SPACE;
+                            // break for substring creation
+                            break;
+                        }
+                    }
+                    if (enclosingTargetChar == SPACE) {
+                        // no open enclosing state
+                        // handle as normal char
+                        if (isPreviousCharWhitespace(arr, i)) {
+                            startPosition = i;
+                            // peek forward for end candidates
+                            if (value.indexOf(arr[i], i + 1) >= 0) {
+                                // set open enclosing state by
+                                // setting up the target char
+                                enclosingTargetChar = arr[i];
+                            } else {
+                                // no more target chars left to match
+                                // end enclosing, handle as normal char
+                                if (isNextCharWhitespace(arr, i)) {
+                                    endPosition = i;
+                                    // break for substring creation
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    continue;
+                }
+                default: {
+                    // normal non-space, non-" and non-' chars
+                    if (enclosingTargetChar == SPACE) {
+                        // no open enclosing state
+                        if (isPreviousCharWhitespace(arr, i)) {
+                            // start of space delim substring
+                            startPosition = i;
+                        }
+                        if (isNextCharWhitespace(arr, i)) {
+                            // end of space delim substring
+                            endPosition = i;
+                            // break for substring creation
+                            break;
+                        }
+                    }
+                    continue;
+                }
+            }
+
+            // break's end up here
+            if (startPosition > endPosition) {
+                throw new IllegalArgumentException("Illegal option values");
+            }
+
+            // extract substring and add to List<String>
+            al.add(value.substring(startPosition, ++endPosition));
+
+            // set new start position
+            i = startPosition = endPosition;
+
+        } // for loop
+
+        return al;
+    }
+
+    static private boolean isPreviousCharWhitespace(char[] arr, int curr_pos) {
+        return isCharWhitespace(arr, curr_pos - 1);
+    }
+
+    static private boolean isNextCharWhitespace(char[] arr, int curr_pos) {
+        return isCharWhitespace(arr, curr_pos + 1);
+    }
+
+    static private boolean isCharWhitespace(char[] arr, int pos) {
+        if (pos < 0 || pos >= arr.length) {
+            // outside arraybounds is considered an implicit space
+            return true;
+        }
+        return (arr[pos] == ' ');
+    }
+
+    static private boolean isLastChar(char[] arr, int pos) {
+        return (pos + 1 == arr.length);
+    }
+
+    JDIConnection(JDIEnv env, String connectSpec, int traceFlags, JShell proc) {
+        this.env = env;
+        this.proc = proc;
+        String nameString;
+        String argString;
+        int index = connectSpec.indexOf(':');
+        if (index == -1) {
+            nameString = connectSpec;
+            argString = "";
+        } else {
+            nameString = connectSpec.substring(0, index);
+            argString = connectSpec.substring(index + 1);
+        }
+
+        connector = findConnector(nameString);
+        if (connector == null) {
+            throw new IllegalArgumentException("No connector named: " + nameString);
+        }
+
+        connectorArgs = parseConnectorArgs(connector, argString);
+        this.traceFlags = traceFlags;
+    }
+
+    synchronized VirtualMachine open() {
+        if (connector instanceof LaunchingConnector) {
+            vm = launchTarget();
+        } else if (connector instanceof AttachingConnector) {
+            vm = attachTarget();
+        } else if (connector instanceof ListeningConnector) {
+            vm = listenTarget();
+        } else {
+            throw new InternalError("Invalid connect type");
+        }
+        vm.setDebugTraceMode(traceFlags);
+        // Uncomment here and below to enable event requests
+        // installEventRequests(vm);
+
+        return vm;
+    }
+
+    boolean setConnectorArg(String name, String value) {
+        /*
+         * Too late if the connection already made
+         */
+        if (vm != null) {
+            return false;
+        }
+
+        Connector.Argument argument = connectorArgs.get(name);
+        if (argument == null) {
+            return false;
+        }
+        argument.setValue(value);
+        return true;
+    }
+
+    String connectorArg(String name) {
+        Connector.Argument argument = connectorArgs.get(name);
+        if (argument == null) {
+            return "";
+        }
+        return argument.value();
+    }
+
+    public synchronized VirtualMachine vm() {
+        if (vm == null) {
+            throw new JDINotConnectedException();
+        } else {
+            return vm;
+        }
+    }
+
+    boolean isOpen() {
+        return (vm != null);
+    }
+
+    boolean isLaunch() {
+        return (connector instanceof LaunchingConnector);
+    }
+
+    public void disposeVM() {
+        try {
+            if (vm != null) {
+                vm.dispose(); // This could NPE, so it is caught below
+                vm = null;
+            }
+        } catch (VMDisconnectedException | NullPointerException ex) {
+            // Ignore if already closed
+        } finally {
+            if (process != null) {
+                process.destroy();
+                process = null;
+            }
+            waitOutputComplete();
+        }
+    }
+
+/*** Preserved for possible future support of event requests
+
+    private void installEventRequests(VirtualMachine vm) {
+        if (vm.canBeModified()){
+            setEventRequests(vm);
+            resolveEventRequests();
+        }
+    }
+
+    private void setEventRequests(VirtualMachine vm) {
+        EventRequestManager erm = vm.eventRequestManager();
+
+        // Normally, we want all uncaught exceptions.  We request them
+        // via the same mechanism as Commands.commandCatchException()
+        // so the user can ignore them later if they are not
+        // interested.
+        // FIXME: this works but generates spurious messages on stdout
+        //        during startup:
+        //          Set uncaught java.lang.Throwable
+        //          Set deferred uncaught java.lang.Throwable
+        Commands evaluator = new Commands();
+        evaluator.commandCatchException
+            (new StringTokenizer("uncaught java.lang.Throwable"));
+
+        ThreadStartRequest tsr = erm.createThreadStartRequest();
+        tsr.enable();
+        ThreadDeathRequest tdr = erm.createThreadDeathRequest();
+        tdr.enable();
+    }
+
+    private void resolveEventRequests() {
+        Env.specList.resolveAll();
+    }
+***/
+
+    private void dumpStream(InputStream inStream, final PrintStream pStream) throws IOException {
+        BufferedReader in =
+            new BufferedReader(new InputStreamReader(inStream));
+        int i;
+        try {
+            while ((i = in.read()) != -1) {
+                pStream.print((char) i);
+            }
+        } catch (IOException ex) {
+            String s = ex.getMessage();
+            if (!s.startsWith("Bad file number")) {
+                throw ex;
+            }
+            // else we got a Bad file number IOException which just means
+            // that the debuggee has gone away.  We'll just treat it the
+            // same as if we got an EOF.
+        }
+    }
+
+    /**
+     *  Create a Thread that will retrieve and display any output.
+     *  Needs to be high priority, else debugger may exit before
+     *  it can be displayed.
+     */
+    private void displayRemoteOutput(final InputStream inStream, final PrintStream pStream) {
+        Thread thr = new Thread("output reader") {
+            @Override
+            public void run() {
+                try {
+                    dumpStream(inStream, pStream);
+                } catch (IOException ex) {
+                    proc.debug(ex, "Failed reading output");
+                    env.shutdown();
+                } finally {
+                    notifyOutputComplete();
+                }
+            }
+        };
+        thr.setPriority(Thread.MAX_PRIORITY-1);
+        thr.start();
+    }
+
+    /**
+     *  Create a Thread that will ship all input to remote.
+     *  Does it need be high priority?
+     */
+    private void readRemoteInput(final OutputStream outStream, final InputStream inputStream) {
+        Thread thr = new Thread("input reader") {
+            @Override
+            public void run() {
+                try {
+                    byte[] buf = new byte[256];
+                    int cnt;
+                    while ((cnt = inputStream.read(buf)) != -1) {
+                        outStream.write(buf, 0, cnt);
+                        outStream.flush();
+                    }
+                } catch (IOException ex) {
+                    proc.debug(ex, "Failed reading output");
+                    env.shutdown();
+                }
+            }
+        };
+        thr.setPriority(Thread.MAX_PRIORITY-1);
+        thr.start();
+    }
+
+    /* launch child target vm */
+    private VirtualMachine launchTarget() {
+        LaunchingConnector launcher = (LaunchingConnector)connector;
+        try {
+            VirtualMachine new_vm = launcher.launch(connectorArgs);
+            process = new_vm.process();
+            displayRemoteOutput(process.getErrorStream(), proc.err);
+            displayRemoteOutput(process.getInputStream(), proc.out);
+            readRemoteInput(process.getOutputStream(), proc.in);
+            return new_vm;
+        } catch (Exception ex) {
+            reportLaunchFail(ex, "launch");
+        }
+        return null;
+    }
+
+    /* JShell currently uses only launch, preserved for futures: */
+    /* attach to running target vm */
+    private VirtualMachine attachTarget() {
+        AttachingConnector attacher = (AttachingConnector)connector;
+        try {
+            return attacher.attach(connectorArgs);
+        } catch (Exception ex) {
+            reportLaunchFail(ex, "attach");
+        }
+        return null;
+    }
+
+    /* JShell currently uses only launch, preserved for futures: */
+    /* listen for connection from target vm */
+    private VirtualMachine listenTarget() {
+        ListeningConnector listener = (ListeningConnector)connector;
+        try {
+            String retAddress = listener.startListening(connectorArgs);
+            proc.debug(DBG_GEN, "Listening at address: " + retAddress);
+            vm = listener.accept(connectorArgs);
+            listener.stopListening(connectorArgs);
+            return vm;
+        } catch (Exception ex) {
+            reportLaunchFail(ex, "listen");
+        }
+        return null;
+    }
+
+    private void reportLaunchFail(Exception ex, String context) {
+        throw new InternalError("Failed remote " + context + ": " + connector +
+                " -- " + connectorArgs, ex);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/JDIEnv.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import com.sun.jdi.*;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
+
+/**
+ * Representation of a Java Debug Interface environment
+ * Select methods extracted from jdb Env; shutdown() adapted to JShell shutdown.
+ */
+class JDIEnv {
+
+    private JDIConnection connection;
+    private final JShell state;
+
+    JDIEnv(JShell state) {
+        this.state = state;
+    }
+
+    void init(String connectSpec, boolean openNow, int flags) {
+        connection = new JDIConnection(this, connectSpec, flags, state);
+        if (!connection.isLaunch() || openNow) {
+            connection.open();
+        }
+    }
+
+    JDIConnection connection() {
+        return connection;
+    }
+
+    VirtualMachine vm() {
+        return connection.vm();
+    }
+
+    void shutdown() {
+        if (connection != null) {
+            try {
+                connection.disposeVM();
+            } catch (VMDisconnectedException e) {
+                // Shutting down after the VM has gone away. This is
+                // not an error, and we just ignore it.
+            } catch (Throwable e) {
+                state.debug(DBG_GEN, null, "disposeVM threw: " + e);
+            }
+        }
+        if (state != null) { // If state has been set-up
+            try {
+                state.closeDown();
+            } catch (Throwable e) {
+                state.debug(DBG_GEN, null, "state().closeDown() threw: " + e);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/JDIEventHandler.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1998, 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+
+/**
+ * Handler of Java Debug Interface events.
+ * Adapted from jdb EventHandler; Handling of events not used by JShell stubbed out.
+ */
+class JDIEventHandler implements Runnable {
+
+    Thread thread;
+    volatile boolean connected = true;
+    boolean completed = false;
+    String shutdownMessageKey;
+    final JDIEnv env;
+
+    JDIEventHandler(JDIEnv env) {
+        this.env = env;
+        this.thread = new Thread(this, "event-handler");
+        this.thread.start();
+    }
+
+    synchronized void shutdown() {
+        connected = false;  // force run() loop termination
+        thread.interrupt();
+        while (!completed) {
+            try {wait();} catch (InterruptedException exc) {}
+        }
+    }
+
+    @Override
+    public void run() {
+        EventQueue queue = env.vm().eventQueue();
+        while (connected) {
+            try {
+                EventSet eventSet = queue.remove();
+                boolean resumeStoppedApp = false;
+                EventIterator it = eventSet.eventIterator();
+                while (it.hasNext()) {
+                    resumeStoppedApp |= handleEvent(it.nextEvent());
+                }
+
+                if (resumeStoppedApp) {
+                    eventSet.resume();
+                }
+            } catch (InterruptedException exc) {
+                // Do nothing. Any changes will be seen at top of loop.
+            } catch (VMDisconnectedException discExc) {
+                handleDisconnectedException();
+                break;
+            }
+        }
+        synchronized (this) {
+            completed = true;
+            notifyAll();
+        }
+    }
+
+    private boolean handleEvent(Event event) {
+        if (event instanceof ExceptionEvent) {
+            exceptionEvent(event);
+        } else if (event instanceof WatchpointEvent) {
+            fieldWatchEvent(event);
+        } else if (event instanceof MethodEntryEvent) {
+            methodEntryEvent(event);
+        } else if (event instanceof MethodExitEvent) {
+            methodExitEvent(event);
+        } else if (event instanceof ClassPrepareEvent) {
+            classPrepareEvent(event);
+        } else if (event instanceof ThreadStartEvent) {
+            threadStartEvent(event);
+        } else if (event instanceof ThreadDeathEvent) {
+            threadDeathEvent(event);
+        } else if (event instanceof VMStartEvent) {
+            vmStartEvent(event);
+            return true;
+        } else {
+            handleExitEvent(event);
+        }
+        return true;
+    }
+
+    private boolean vmDied = false;
+
+    private void handleExitEvent(Event event) {
+        if (event instanceof VMDeathEvent) {
+            vmDied = true;
+            shutdownMessageKey = "The application exited";
+        } else if (event instanceof VMDisconnectEvent) {
+            connected = false;
+            if (!vmDied) {
+                shutdownMessageKey = "The application has been disconnected";
+            }
+        } else {
+            throw new InternalError("Unexpected event type: " +
+                    event.getClass());
+        }
+        env.shutdown();
+    }
+
+    synchronized void handleDisconnectedException() {
+        /*
+         * A VMDisconnectedException has happened while dealing with
+         * another event. We need to flush the event queue, dealing only
+         * with exit events (VMDeath, VMDisconnect) so that we terminate
+         * correctly.
+         */
+        EventQueue queue = env.vm().eventQueue();
+        while (connected) {
+            try {
+                EventSet eventSet = queue.remove();
+                EventIterator iter = eventSet.eventIterator();
+                while (iter.hasNext()) {
+                    handleExitEvent(iter.next());
+                }
+            } catch (InterruptedException exc) {
+                // ignore
+            } catch (InternalError exc) {
+                // ignore
+            }
+        }
+    }
+
+    private void vmStartEvent(Event event)  {
+        VMStartEvent se = (VMStartEvent)event;
+    }
+
+    private void methodEntryEvent(Event event)  {
+        MethodEntryEvent me = (MethodEntryEvent)event;
+    }
+
+    private void methodExitEvent(Event event)  {
+        MethodExitEvent me = (MethodExitEvent)event;
+    }
+
+    private void fieldWatchEvent(Event event)  {
+        WatchpointEvent fwe = (WatchpointEvent)event;
+    }
+
+    private void classPrepareEvent(Event event)  {
+        ClassPrepareEvent cle = (ClassPrepareEvent)event;
+    }
+
+    private void exceptionEvent(Event event) {
+        ExceptionEvent ee = (ExceptionEvent)event;
+    }
+
+    private void threadDeathEvent(Event event) {
+        ThreadDeathEvent tee = (ThreadDeathEvent)event;
+    }
+
+    private void threadStartEvent(Event event) {
+        ThreadStartEvent tse = (ThreadStartEvent)event;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/JDINotConnectedException.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+/**
+ * Internal exception when Java Debug Interface VirtualMacine is not connected.
+ * Copy of jdb VMNotConnectedException.
+ */
+class JDINotConnectedException extends RuntimeException {
+
+    private static final long serialVersionUID = -7433430494903950165L;
+
+    public JDINotConnectedException() {
+        super();
+    }
+
+    public JDINotConnectedException(String s) {
+        super(s);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+
+import java.util.function.Supplier;
+import jdk.internal.jshell.debug.InternalDebugControl;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_EVNT;
+import static jdk.jshell.Util.expunge;
+import jdk.jshell.Snippet.Status;
+
+/**
+ * The JShell evaluation state engine.  This is the central class in the JShell
+ * API.  A <code>JShell</code> instance holds the evolving compilation and
+ * execution state.  The state is changed with the instance methods
+ * {@link jdk.jshell.JShell#eval(java.lang.String) eval(String)},
+ * {@link jdk.jshell.JShell#drop(jdk.jshell.PersistentSnippet) drop(PersistentSnippet)} and
+ * {@link jdk.jshell.JShell#addToClasspath(java.lang.String) addToClasspath(String)}.
+ * The majority of methods query the state.
+ * A <code>JShell</code> instance also allows registering for events with
+ * {@link jdk.jshell.JShell#onSnippetEvent(java.util.function.Consumer) onSnippetEvent(Consumer)}
+ * and {@link jdk.jshell.JShell#onShutdown(java.util.function.Consumer) onShutdown(Consumer)}, which
+ * are unregistered with
+ * {@link jdk.jshell.JShell#unsubscribe(jdk.jshell.JShell.Subscription) unsubscribe(Subscription)}.
+ * Access to the source analysis utilities is via
+ * {@link jdk.jshell.JShell#sourceCodeAnalysis()}.
+ * When complete the instance should be closed to free resources --
+ * {@link jdk.jshell.JShell#close()}.
+ * <p>
+ * An instance of <code>JShell</code> is created with
+ * <code>JShell.create()</code>.
+ * <p>
+ * This class is not thread safe, except as noted, all access should be through
+ * a single thread.
+ * @see jdk.jshell
+ * @author Robert Field
+ */
+public class JShell implements AutoCloseable {
+
+    final SnippetMaps maps;
+    final KeyMap keyMap;
+    final TaskFactory taskFactory;
+    final InputStream in;
+    final PrintStream out;
+    final PrintStream err;
+    final Supplier<String> tempVariableNameGenerator;
+    final BiFunction<Snippet, Integer, String> idGenerator;
+
+    private int nextKeyIndex = 1;
+
+    final Eval eval;
+    final ClassTracker classTracker;
+    private final Map<Subscription, Consumer<JShell>> shutdownListeners = new HashMap<>();
+    private final Map<Subscription, Consumer<SnippetEvent>> keyStatusListeners = new HashMap<>();
+    private boolean closed = false;
+
+
+    private ExecutionControl executionControl = null;
+    private SourceCodeAnalysis sourceCodeAnalysis = null;
+
+
+    JShell(Builder b) {
+        this.in = b.in;
+        this.out = b.out;
+        this.err = b.err;
+        this.tempVariableNameGenerator = b.tempVariableNameGenerator;
+        this.idGenerator = b.idGenerator;
+
+        this.maps = new SnippetMaps(this);
+        maps.setPackageName("REPL");
+        this.keyMap = new KeyMap(this);
+        this.taskFactory = new TaskFactory(this);
+        this.eval = new Eval(this);
+        this.classTracker = new ClassTracker(this);
+    }
+
+    /**
+     * Builder for <code>JShell</code> instances.
+     * Create custom instances of <code>JShell</code> by using the setter
+     * methods on this class.  After zero or more of these, use the
+     * {@link #build()} method to create a <code>JShell</code> instance.
+     * These can all be chained. For example, setting the remote output and
+     * error streams:
+     * <pre>
+     * <code>
+     *     JShell myShell =
+     *       JShell.builder()
+     *         .out(myOutStream)
+     *         .err(myErrStream)
+     *         .build(); </code> </pre>
+     * If no special set-up is needed, just use
+     * <code>JShell.builder().build()</code> or the short-cut equivalent
+     * <code>JShell.create()</code>.
+     */
+    public static class Builder {
+
+        InputStream in = new ByteArrayInputStream(new byte[0]);
+        PrintStream out = System.out;
+        PrintStream err = System.err;
+        Supplier<String> tempVariableNameGenerator = null;
+        BiFunction<Snippet, Integer, String> idGenerator = null;
+
+        Builder() { }
+
+        /**
+         * Input for the running evaluation (it's <code>System.in</code>). Note:
+         * applications that use <code>System.in</code> for snippet or other
+         * user input cannot use <code>System.in</code> as the input stream for
+         * the remote process.
+         * <p>
+         * The default, if this is not set, is to provide an empty input stream
+         * -- <code>new ByteArrayInputStream(new byte[0])</code>.
+         *
+         * @param in the <code>InputStream</code> to be channelled to
+         * <code>System.in</code> in the remote execution process.
+         * @return the <code>Builder</code> instance (for use in chained
+         * initialization).
+         */
+        public Builder in(InputStream in) {
+            this.in = in;
+            return this;
+        }
+
+        /**
+         * Output for the running evaluation (it's <code>System.out</code>).
+         * The controlling process and
+         * the remote process can share <code>System.out</code>.
+         * <p>
+         * The default, if this is not set, is <code>System.out</code>.
+         *
+         * @param out the <code>PrintStream</code> to be channelled to
+         * <code>System.out</code> in the remote execution process.
+         * @return the <code>Builder</code> instance (for use in chained
+         * initialization).
+         */
+        public Builder out(PrintStream out) {
+            this.out = out;
+            return this;
+        }
+
+        /**
+         * Error output for the running evaluation (it's
+         * <code>System.err</code>). The controlling process and the remote
+         * process can share <code>System.err</code>.
+         * <p>
+         * The default, if this is not set, is <code>System.err</code>.
+         *
+         * @param err the <code>PrintStream</code> to be channelled to
+         * <code>System.err</code> in the remote execution process.
+         * @return the <code>Builder</code> instance (for use in chained
+         * initialization).
+         */
+        public Builder err(PrintStream err) {
+            this.err = err;
+            return this;
+        }
+
+        /**
+         * Set a generator of temp variable names for
+         * {@link jdk.jshell.VarSnippet} of
+         * {@link jdk.jshell.Snippet.SubKind#TEMP_VAR_EXPRESSION_SUBKIND}.
+         * <p>
+         * Do not use this method unless you have explicit need for it.
+         * <p>
+         * The generator will be used for newly created VarSnippet
+         * instances. The name of a variable is queried with
+         * {@link jdk.jshell.VarSnippet#name()}.
+         * <p>
+         * The callback is sent during the processing of the snippet, the
+         * JShell state is not stable. No calls whatsoever on the
+         * <code>JShell</code> instance may be made from the callback.
+         * <p>
+         * The generated name must be unique within active snippets.
+         * <p>
+         * The default behavior (if this is not set or <code>generator</code>
+         * is null) is to generate the name as a sequential number with a
+         * prefixing dollar sign ("$").
+         *
+         * @param generator the <code>Supplier</code> to generate the temporary
+         * variable name string or <code>null</code>.
+         * @return the <code>Builder</code> instance (for use in chained
+         * initialization).
+         */
+        public Builder tempVariableNameGenerator(Supplier<String> generator) {
+            this.tempVariableNameGenerator = generator;
+            return this;
+        }
+
+        /**
+         * Set the generator of identifying names for Snippets.
+         * <p>
+         * Do not use this method unless you have explicit need for it.
+         * <p>
+         * The generator will be used for newly created Snippet instances. The
+         * identifying name (id) is accessed with
+         * {@link jdk.jshell.Snippet#id()} and can be seen in the
+         * <code>StackTraceElement.getFileName()</code> for a
+         * {@link jdk.jshell.EvalException} and
+         * {@link jdk.jshell.UnresolvedReferenceException}.
+         * <p>
+         * The inputs to the generator are the {@link jdk.jshell.Snippet} and an
+         * integer. The integer will be the same for two Snippets which would
+         * overwrite one-another, but otherwise is unique.
+         * <p>
+         * The callback is sent during the processing of the snippet and the
+         * Snippet and the state as a whole are not stable. No calls to change
+         * system state (including Snippet state) should be made. Queries of
+         * Snippet may be made except to {@link jdk.jshell.Snippet#id()}. No
+         * calls on the <code>JShell</code> instance may be made from the
+         * callback, except to
+         * {@link #status(jdk.jshell.Snippet) status(Snippet)}.
+         * <p>
+         * The default behavior (if this is not set or <code>generator</code>
+         * is null) is to generate the id as the integer converted to a string.
+         *
+         * @param generator the <code>BiFunction</code> to generate the id
+         * string or <code>null</code>.
+         * @return the <code>Builder</code> instance (for use in chained
+         * initialization).
+         */
+        public Builder idGenerator(BiFunction<Snippet, Integer, String> generator) {
+            this.idGenerator = generator;
+            return this;
+        }
+
+        /**
+         * Build a JShell state engine. This is the entry-point to all JShell
+         * functionality. This creates a remote process for execution. It is
+         * thus important to close the returned instance.
+         *
+         * @return the state engine.
+         */
+        public JShell build() {
+            return new JShell(this);
+        }
+    }
+
+    // --- public API ---
+
+    /**
+     * Create a new JShell state engine.
+     * That is, create an instance of <code>JShell</code>.
+     * <p>
+     * Equivalent to {@link JShell#builder() JShell.builder()}{@link JShell.Builder#build() .build()}.
+     * @return an instance of <code>JShell</code>.
+     */
+    public static JShell create() {
+        return builder().build();
+    }
+
+    /**
+     * Factory method for <code>JShell.Builder</code> which, in-turn, is used
+     * for creating instances of <code>JShell</code>.
+     * Create a default instance of <code>JShell</code> with
+     * <code>JShell.builder().build()</code>. For more construction options
+     * see {@link jdk.jshell.JShell.Builder}.
+     * @return an instance of <code>Builder</code>.
+     * @see jdk.jshell.JShell.Builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Access to source code analysis functionality.
+     * An instance of <code>JShell</code> will always return the same
+     * <code>SourceCodeAnalysis</code> instance from
+     * <code>sourceCodeAnalysis()</code>.
+     * @return an instance of {@link SourceCodeAnalysis SourceCodeAnalysis}
+     * which can be used for source analysis such as completion detection and
+     * completion suggestions.
+     */
+    public SourceCodeAnalysis sourceCodeAnalysis() {
+        if (sourceCodeAnalysis == null) {
+            sourceCodeAnalysis = new SourceCodeAnalysisImpl(this);
+        }
+        return sourceCodeAnalysis;
+    }
+
+    /**
+     * Evaluate the input String, including definition and/or execution, if
+     * applicable. The input is checked for errors, unless the errors can be
+     * deferred (as is the case with some unresolvedDependencies references),
+     * errors will abort evaluation. The input should be
+     * exactly one complete snippet of source code, that is, one expression,
+     * statement, variable declaration, method declaration, class declaration,
+     * or import.
+     * To break arbitrary input into individual complete snippets, use
+     * {@link SourceCodeAnalysis#analyzeCompletion(String)}.
+     * <p>
+     * For imports, the import is added.  Classes, interfaces. methods,
+     * and variables are defined.  The initializer of variables, statements,
+     * and expressions are executed.
+     * The modifiers public, protected, private, static, and final are not
+     * allowed on op-level declarations and are ignored with a warning.
+     * Synchronized, native, abstract, and default top-level methods are not
+     * allowed and are errors.
+     * If a previous definition of a declaration is overwritten then there will
+     * be an event showing its status changed to OVERWRITTEN, this will not
+     * occur for dropped, rejected, or already overwritten declarations.
+     * <p>
+     * The execution environment is out of process.  If the evaluated code
+     * causes the execution environment to terminate, this <code>JShell</code>
+     * instance will be closed but the calling process and VM remain valid.
+     * @param input The input String to evaluate
+     * @return the list of events directly or indirectly caused by this evaluation.
+     * @throws IllegalStateException if this <code>JShell</code> instance is closed.
+     * @see SourceCodeAnalysis#analyzeCompletion(String)
+     * @see JShell#onShutdown(java.util.function.Consumer)
+     */
+    public List<SnippetEvent> eval(String input) throws IllegalStateException {
+        checkIfAlive();
+        List<SnippetEvent> events = eval.eval(input);
+        events.forEach(this::notifyKeyStatusEvent);
+        return Collections.unmodifiableList(events);
+    }
+
+    /**
+     * Remove a declaration from the state.
+     * @param snippet The snippet to remove
+     * @return The list of events from updating declarations dependent on the
+     * dropped snippet.
+     * @throws IllegalStateException if this <code>JShell</code> instance is closed.
+     * @throws IllegalArgumentException if the snippet is not associated with
+     * this <code>JShell</code> instance.
+     */
+    public List<SnippetEvent> drop(PersistentSnippet snippet) throws IllegalStateException {
+        checkIfAlive();
+        checkValidSnippet(snippet);
+        List<SnippetEvent> events = eval.drop(snippet);
+        events.forEach(this::notifyKeyStatusEvent);
+        return Collections.unmodifiableList(events);
+    }
+
+    /**
+     * The specified path is added to the end of the classpath used in eval().
+     * Note that the unnamed package is not accessible from the package in which
+     * {@link JShell#eval()} code is placed.
+     * @param path the path to add to the classpath.
+     */
+    public void addToClasspath(String path) {
+        taskFactory.addToClasspath(path);  // Compiler
+        executionControl().commandAddToClasspath(path);       // Runtime
+    }
+
+    /**
+     * Attempt to stop currently running evaluation. When called while
+     * the {@link #eval(java.lang.String) } method is running and the
+     * user's code being executed, an attempt will be made to stop user's code.
+     * Note that typically this method needs to be called from a different thread
+     * than the one running the {@code eval} method.
+     * <p>
+     * If the {@link #eval(java.lang.String) } method is not running, does nothing.
+     * <p>
+     * The attempt to stop the user's code may fail in some case, which may include
+     * when the execution is blocked on an I/O operation, or when the user's code is
+     * catching the {@link ThreadDeath} exception.
+     */
+    public void stop() {
+        if (executionControl != null)
+            executionControl.commandStop();
+    }
+
+    /**
+     * Close this state engine. Frees resources. Should be called when this
+     * state engine is no longer needed.
+     */
+    @Override
+    public void close() {
+        if (!closed) {
+            closeDown();
+            executionControl().commandExit();
+        }
+    }
+
+    /**
+     * Return all snippets.
+     * @return the snippets for all current snippets in id order.
+     * @throws IllegalStateException if this JShell instance is closed.
+     */
+    public List<Snippet> snippets() throws IllegalStateException {
+        checkIfAlive();
+        return Collections.unmodifiableList(maps.snippetList());
+    }
+
+    /**
+     * Returns the active variable snippets.
+     * This convenience method is equivalent to <code>snippets()</code> filtered for
+     * {@link jdk.jshell.Snippet.Status#isActive status(snippet).isActive}
+     * <code>&amp;&amp; snippet.kind() == Kind.VARIABLE</code>
+     * and cast to <code>VarSnippet</code>.
+     * @return the active declared variables.
+     * @throws IllegalStateException if this JShell instance is closed.
+     */
+    public List<VarSnippet> variables() throws IllegalStateException {
+        return snippets().stream()
+                     .filter(sn -> status(sn).isActive && sn.kind() == Snippet.Kind.VAR)
+                     .map(sn -> (VarSnippet) sn)
+                     .collect(collectingAndThen(toList(), Collections::unmodifiableList));
+    }
+
+    /**
+     * Returns the active method snippets.
+     * This convenience method is equivalent to <code>snippets()</code> filtered for
+     * {@link jdk.jshell.Snippet.Status#isActive status(snippet).isActive}
+     * <code>&amp;&amp; snippet.kind() == Kind.METHOD</code>
+     * and cast to MethodSnippet.
+     * @return the active declared methods.
+     * @throws IllegalStateException if this JShell instance is closed.
+     */
+    public List<MethodSnippet> methods() throws IllegalStateException {
+        return snippets().stream()
+                     .filter(sn -> status(sn).isActive && sn.kind() == Snippet.Kind.METHOD)
+                     .map(sn -> (MethodSnippet)sn)
+                     .collect(collectingAndThen(toList(), Collections::unmodifiableList));
+    }
+
+    /**
+     * Returns the active type declaration (class, interface, annotation type, and enum) snippets.
+     * This convenience method is equivalent to <code>snippets()</code> filtered for
+     * {@link jdk.jshell.Snippet.Status#isActive status(snippet).isActive}
+     * <code>&amp;&amp; snippet.kind() == Kind.TYPE_DECL</code>
+     * and cast to TypeDeclSnippet.
+     * @return the active declared type declarations.
+     * @throws IllegalStateException if this JShell instance is closed.
+     */
+    public List<TypeDeclSnippet> types() throws IllegalStateException {
+        return snippets().stream()
+                .filter(sn -> status(sn).isActive && sn.kind() == Snippet.Kind.TYPE_DECL)
+                .map(sn -> (TypeDeclSnippet) sn)
+                .collect(collectingAndThen(toList(), Collections::unmodifiableList));
+    }
+
+    /**
+     * Return the status of the snippet.
+     * This is updated either because of an explicit <code>eval()</code> call or
+     * an automatic update triggered by a dependency.
+     * @param snippet the <code>Snippet</code> to look up
+     * @return the status corresponding to this snippet
+     * @throws IllegalStateException if this <code>JShell</code> instance is closed.
+     * @throws IllegalArgumentException if the snippet is not associated with
+     * this <code>JShell</code> instance.
+     */
+    public Status status(Snippet snippet) {
+        return checkValidSnippet(snippet).status();
+    }
+
+    /**
+     * Return the diagnostics of the most recent evaluation of the snippet.
+     * The evaluation can either because of an explicit <code>eval()</code> call or
+     * an automatic update triggered by a dependency.
+     * @param snippet the <code>Snippet</code> to look up
+     * @return the diagnostics corresponding to this snippet.  This does not
+     * include unresolvedDependencies references reported in <code>unresolvedDependencies()</code>.
+     * @throws IllegalStateException if this <code>JShell</code> instance is closed.
+     * @throws IllegalArgumentException if the snippet is not associated with
+     * this <code>JShell</code> instance.
+     */
+    public List<Diag> diagnostics(Snippet snippet) {
+        return Collections.unmodifiableList(checkValidSnippet(snippet).diagnostics());
+    }
+
+    /**
+     * For {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED} or
+     * {@link jdk.jshell.Snippet.Status#RECOVERABLE_NOT_DEFINED RECOVERABLE_NOT_DEFINED}
+     * declarations, the names of current unresolved dependencies for
+     * the snippet.
+     * The returned value of this method, for a given method may change when an
+     * <code>eval()</code> or <code>drop()</code> of another snippet causes
+     * an update of a dependency.
+     * @param snippet the declaration <code>Snippet</code> to look up
+     * @return the list of symbol names that are currently unresolvedDependencies.
+     * @throws IllegalStateException if this <code>JShell</code> instance is closed.
+     * @throws IllegalArgumentException if the snippet is not associated with
+     * this <code>JShell</code> instance.
+     */
+    public List<String> unresolvedDependencies(DeclarationSnippet snippet) {
+        return Collections.unmodifiableList(checkValidSnippet(snippet).unresolved());
+    }
+
+    /**
+     * Get the current value of a variable.
+     * @param snippet the variable Snippet whose value is queried.
+     * @return the current value of the variable referenced by snippet.
+     * @throws IllegalStateException if this <code>JShell</code> instance is closed.
+     * @throws IllegalArgumentException if the snippet is not associated with
+     * this <code>JShell</code> instance.
+     * @throws IllegalArgumentException if the variable's status is anything but
+     * {@link jdk.jshell.Snippet.Status#VALID}.
+     */
+    public String varValue(VarSnippet snippet) throws IllegalStateException {
+        checkIfAlive();
+        checkValidSnippet(snippet);
+        if (snippet.status() != Status.VALID) {
+            throw new IllegalArgumentException("Snippet parameter of varValue() '" +
+                    snippet + "' must be VALID, it is: " + snippet.status());
+        }
+        String value = executionControl().commandVarValue(maps.classFullName(snippet), snippet.name());
+        return expunge(value);
+    }
+
+    /**
+     * Register a callback to be called when the Status of a snippet changes.
+     * Each call adds a new subscription.
+     * @param listener Action to perform when the Status changes.
+     * @return A token which can be used to {@linkplain JShell#unsubscribe unsubscribe} this subscription.
+     * @throws IllegalStateException if this <code>JShell</code> instance is closed.
+     */
+    public Subscription onSnippetEvent(Consumer<SnippetEvent> listener)
+            throws IllegalStateException {
+        return onX(keyStatusListeners, listener);
+    }
+
+    /**
+     * Register a callback to be called when this JShell instance terminates.
+     * This occurs either because the client process has ended (e.g. called System.exit(0))
+     * or the connection has been shutdown, as by close().
+     * Each call adds a new subscription.
+     * @param listener Action to perform when the state terminates.
+     * @return A token which can be used to {@linkplain JShell#unsubscribe unsubscribe} this subscription.
+     * @throws IllegalStateException if this JShell instance is closed
+     */
+    public Subscription onShutdown(Consumer<JShell> listener)
+            throws IllegalStateException {
+        return onX(shutdownListeners, listener);
+    }
+
+    /**
+     * Cancel a callback subscription.
+     * @param token The token corresponding to the subscription to be unsubscribed.
+     */
+    public void unsubscribe(Subscription token) {
+        synchronized (this) {
+            token.remover.accept(token);
+        }
+    }
+
+    /**
+     * Subscription is a token for referring to subscriptions so they can
+     * be {@linkplain JShell#unsubscribe unsubscribed}.
+     */
+    public class Subscription {
+
+        Consumer<Subscription> remover;
+
+        Subscription(Consumer<Subscription> remover) {
+            this.remover = remover;
+        }
+    }
+
+    // --- private / package-private implementation support ---
+
+    ExecutionControl executionControl() {
+        if (executionControl == null) {
+            this.executionControl = new ExecutionControl(new JDIEnv(this), maps, this);
+            try {
+                executionControl.launch();
+            } catch (IOException ex) {
+                throw new InternalError("Launching JDI execution engine threw: " + ex.getMessage(), ex);
+            }
+        }
+        return executionControl;
+    }
+
+    void debug(int flags, String format, Object... args) {
+        if (InternalDebugControl.debugEnabled(this, flags)) {
+            err.printf(format, args);
+        }
+    }
+
+    void debug(Exception ex, String where) {
+        if (InternalDebugControl.debugEnabled(this, 0xFFFFFFFF)) {
+            err.printf("Fatal error: %s: %s\n", where, ex.getMessage());
+            ex.printStackTrace(err);
+        }
+    }
+
+    /**
+     * Generate the next key index, indicating a unique snippet signature.
+     * @return the next key index
+     */
+    int nextKeyIndex() {
+        return nextKeyIndex++;
+    }
+
+    private synchronized <T> Subscription onX(Map<Subscription, Consumer<T>> map, Consumer<T> listener)
+            throws IllegalStateException {
+        Objects.requireNonNull(listener);
+        checkIfAlive();
+        Subscription token = new Subscription(map::remove);
+        map.put(token, listener);
+        return token;
+    }
+
+    private synchronized void notifyKeyStatusEvent(SnippetEvent event) {
+        keyStatusListeners.values().forEach(l -> l.accept(event));
+    }
+
+    private synchronized void notifyShutdownEvent(JShell state) {
+        shutdownListeners.values().forEach(l -> l.accept(state));
+    }
+
+    void closeDown() {
+        if (!closed) {
+            // Send only once
+            closed = true;
+            notifyShutdownEvent(this);
+        }
+    }
+
+    /**
+     * Check if this JShell has been closed
+     * @throws IllegalStateException if it is closed
+     */
+    private void checkIfAlive()  throws IllegalStateException {
+        if (closed) {
+            throw new IllegalStateException("JShell (" + this + ") has been closed.");
+        }
+    }
+
+    /**
+     * Check a Snippet parameter coming from the API user
+     * @param sn the Snippet to check
+     * @throws NullPointerException if Snippet parameter is null
+     * @throws IllegalArgumentException if Snippet is not from this JShell
+     * @return the input Snippet (for chained calls)
+     */
+    private Snippet checkValidSnippet(Snippet sn) {
+        if (sn == null) {
+            throw new NullPointerException("Snippet must not be null");
+        } else {
+            if (sn.key().state() != this) {
+                throw new IllegalArgumentException("Snippet not from this JShell");
+            }
+            return sn;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Key.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import jdk.jshell.Snippet.Kind;
+import jdk.jshell.Snippet.SubKind;
+
+/**
+ * The Key is unique for a given signature.  Used internal to the implementation
+ * to group signature matched Snippets.  Snippet kinds without a signature
+ * (for example expressions) each have their own UniqueKey.
+ * @author Robert Field
+ */
+abstract class Key {
+
+    /**
+     * The unique index for this Key
+     */
+    private final int index;
+
+    /**
+     * The JShell corresponding to this Key
+     */
+    private final JShell state;
+
+    Key(JShell state) {
+        this.index = state.nextKeyIndex();
+        this.state = state;
+    }
+
+    /**
+     * The unique numeric identifier for the snippet.  Snippets which replace or
+     * redefine existing snippets take on the same key index as the replaced or
+     * redefined snippet.
+     * The index values are monotonically increasing integers.
+     * @return the Key index
+     */
+    int index() { return index; }
+
+    /**
+     * The kind for the key.  Indicates the subinterface of Key.
+     * @return the Kind of the Key
+     */
+    abstract Kind kind();
+
+    /**
+     * For foreign key testing.
+     */
+    JShell state() { return state; }
+
+    /**
+     * Grouping for snippets which persist and influence future code.
+     * They are keyed off at least the name.  They may be Modified/Replaced
+     * with new input and can be dropped (JShell#drop).
+     */
+    static abstract class PersistentKey extends Key {
+
+        private final String name;
+
+        PersistentKey(JShell state, String name) {
+            super(state);
+            this.name = name;
+        }
+        /**
+         * Name of the snippet.
+         *
+         * @return the name of the snippet.
+         */
+        String name() { return name; }
+    }
+
+    /**
+     * Grouping for snippets which reference declarations
+     */
+    static abstract class DeclarationKey extends PersistentKey {
+
+        DeclarationKey(JShell state, String name) {
+            super(state, name);
+        }
+    }
+
+    /**
+     * Key for a class definition Keys of TYPE_DECL. Keyed off class name.
+     */
+    static class TypeDeclKey extends DeclarationKey {
+
+        TypeDeclKey(JShell state, String name) {
+            super(state, name);
+        }
+
+        @Override
+        Kind kind() { return Kind.TYPE_DECL; }
+
+        @Override
+        public String toString() { return "ClassKey(" + name() + ")#" + index(); }
+    }
+
+    /**
+     * Key for a method definition Keys of METHOD. Keyed off method name and
+     * parameter types.
+     */
+    static class MethodKey extends DeclarationKey {
+
+        private final String parameterTypes;
+
+        MethodKey(JShell state, String name, String parameterTypes) {
+            super(state, name);
+            this.parameterTypes = parameterTypes;
+        }
+
+        @Override
+        Kind kind() { return Kind.METHOD; }
+
+        /**
+         * The parameter types of the method. Because of overloading of methods,
+         * the parameter types are part of the key.
+         *
+         * @return a String representation of the parameter types of the method.
+         */
+        String parameterTypes() { return parameterTypes; }
+
+
+        @Override
+        public String toString() { return "MethodKey(" + name() +
+                "(" + parameterTypes() + "))#" + index(); }
+    }
+
+    /**
+     * Key for a variable definition Keys of VARIABLE. Keyed off variable
+     * name.
+     */
+    static class VarKey extends DeclarationKey {
+
+        VarKey(JShell state, String name) {
+            super(state, name);
+        }
+
+        @Override
+        public Kind kind() { return Kind.VAR; }
+
+        @Override
+        public String toString() { return "VariableKey(" + name() + ")#" + index(); }
+    }
+
+    /**
+     * Key for an import. Keys of IMPORT. Keyed off import text and whether
+     * import is static.
+     */
+    static class ImportKey extends PersistentKey {
+
+        private final SubKind snippetKind;
+
+        ImportKey(JShell state, String name,SubKind snippetKind) {
+            super(state, name);
+            this.snippetKind = snippetKind;
+        }
+
+        @Override
+        public Kind kind() { return Kind.IMPORT; }
+
+        /**
+         * Which kind of import.
+         *
+         * @return the appropriate SubKind.
+         */
+        SubKind snippetKind() {
+            return snippetKind;
+        }
+
+
+        @Override
+        public String toString() { return "ImportKey(" + name() + "," +
+                snippetKind + ")#" + index(); }
+    }
+
+    /**
+     * Grouping for snippets which are the key for only one input -- even if the
+     * exactly same entry is made again. The referenced snippets are thus
+     * unmodifiable.
+     */
+    static abstract class UniqueKey extends Key {
+
+        UniqueKey(JShell state) {
+            super(state);
+        }
+    }
+
+    /**
+     * Key for a statement snippet. Keys of STATEMENT. Uniquely keyed, see
+     * UniqueKey.
+     */
+    static class StatementKey extends UniqueKey {
+
+        StatementKey(JShell state) {
+            super(state);
+        }
+
+
+        @Override
+        public Kind kind() {
+            return Kind.STATEMENT;
+        }
+
+        @Override
+        public String toString() { return "StatementKey#" + index(); }
+    }
+
+    /**
+     * Key for an expression. Keys of EXPRESSION. Uniquely keyed, see
+     * UniqueKey.
+     */
+    static class ExpressionKey extends UniqueKey {
+
+        private final String name;
+        private final String typeName;
+
+        ExpressionKey(JShell state, String name, String typeName) {
+            super(state);
+            this.name = name;
+            this.typeName = typeName;
+        }
+
+        @Override
+        public Kind kind() { return Kind.EXPRESSION; }
+
+
+        /**
+         * Variable name which is the value of the expression. Since the
+         * expression is either just an identifier which is a variable or it is
+         * an assignment to a variable, there is always a variable which is the
+         * subject of the expression. All other expression snippets become
+         * temporary variables referenced by a VariableKey.
+         *
+         * @return the name of the variable which is the subject of the
+         * expression.
+         */
+        String name() { return name; }
+
+        /**
+         * Type of the expression
+         *
+         * @return String representation of the type of the expression.
+         */
+        String typeName() { return typeName; }
+
+
+        @Override
+        public String toString() { return "ExpressionKey(" + name() + ")#" + index(); }
+    }
+
+    /**
+     * Key for an erroneous snippet. Keys of ERRONEOUS. Uniquely keyed, see
+     * UniqueKey.
+     */
+    static class ErroneousKey extends UniqueKey {
+
+        ErroneousKey(JShell state) {
+            super(state);
+        }
+
+        @Override
+        Kind kind() { return Kind.ERRONEOUS; }
+
+        @Override
+        public String toString() { return "ErroneousKey#" + index(); }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/KeyMap.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Stream;
+import jdk.jshell.Key.TypeDeclKey;
+import jdk.jshell.Key.ErroneousKey;
+import jdk.jshell.Key.ExpressionKey;
+import jdk.jshell.Key.ImportKey;
+import jdk.jshell.Key.MethodKey;
+import jdk.jshell.Key.StatementKey;
+import jdk.jshell.Key.VarKey;
+import jdk.jshell.Snippet.SubKind;
+import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
+import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
+
+/**
+ * Maps signature to Key by Kind.
+ * @author Robert Field
+ */
+class KeyMap {
+    private final JShell state;
+
+    KeyMap(JShell state) {
+        this.state = state;
+    }
+
+    private final Map<String, TypeDeclKey> classMap = new LinkedHashMap<>();
+    private final Map<String, MethodKey> methodMap = new LinkedHashMap<>();
+    private final Map<String, VarKey> varMap = new LinkedHashMap<>();
+    private final Map<String, ImportKey> importMap = new LinkedHashMap<>();
+
+    TypeDeclKey keyForClass(String name) {
+        return classMap.computeIfAbsent(name, k -> new TypeDeclKey(state, name));
+    }
+
+    MethodKey keyForMethod(String name, String parameterTypes) {
+        return methodMap.computeIfAbsent(name + ":" + parameterTypes,
+                                         k -> new MethodKey(state, name, parameterTypes));
+    }
+
+    VarKey keyForVariable(String name) {
+        return varMap.computeIfAbsent(name, k -> new VarKey(state, name));
+    }
+
+    ImportKey keyForImport(String name, SubKind snippetKind) {
+        return importMap.computeIfAbsent(name + ":"
+                + ((snippetKind == SINGLE_TYPE_IMPORT_SUBKIND || snippetKind == SINGLE_STATIC_IMPORT_SUBKIND)
+                        ? "single"
+                        : snippetKind.toString()),
+                k -> new ImportKey(state, name, snippetKind));
+    }
+
+    StatementKey keyForStatement() {
+        return new StatementKey(state);
+    }
+
+    ExpressionKey keyForExpression(String name, String type) {
+        return new ExpressionKey(state, name, type);
+    }
+
+    ErroneousKey keyForErroneous() {
+        return new ErroneousKey(state);
+    }
+
+    boolean doesVariableNameExist(String name) {
+        return varMap.get(name) != null;
+    }
+
+    Stream<ImportKey> importKeys() {
+        return importMap.values().stream();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/MaskCommentsAndModifiers.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jshell;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Within a String, mask code comments and ignored modifiers (within context).
+ *
+ * @author Robert Field
+ */
+class MaskCommentsAndModifiers {
+
+    private final static Set<String> IGNORED_MODIFERS =
+            Stream.of( "public", "protected", "private", "static", "final" )
+                    .collect( Collectors.toSet() );
+
+    private final StringBuilder sbCleared = new StringBuilder();
+    private final StringBuilder sbMask = new StringBuilder();
+    private final String str;
+    private final int length;
+    private final boolean maskModifiers;
+    private int next = 0;
+    private boolean wasMasked = false;
+    private boolean inside = false;
+
+    @SuppressWarnings("empty-statement")
+    public MaskCommentsAndModifiers(String s, boolean maskModifiers) {
+        this.str = s;
+        this.length = s.length();
+        this.maskModifiers = maskModifiers;
+        do { } while (next());
+    }
+
+    public String cleared() {
+        return sbCleared.toString();
+    }
+
+    public String mask() {
+        return sbMask.toString();
+    }
+
+    public boolean wasMasked() {
+        return wasMasked;
+    }
+
+    /**
+     * Read the next character
+     */
+    private int read() {
+        if (next >= length) {
+            return -1;
+        }
+        return str.charAt(next++);
+    }
+
+    private void write(StringBuilder sb, int ch) {
+        sb.append((char)ch);
+    }
+
+    private void write(int ch) {
+        write(sbCleared, ch);
+        write(sbMask, Character.isWhitespace(ch) ? ch : ' ');
+    }
+
+    private void writeMask(int ch) {
+        wasMasked = true;
+        write(sbMask, ch);
+        write(sbCleared, Character.isWhitespace(ch) ? ch : ' ');
+    }
+
+    private void write(CharSequence s) {
+        for (int cp : s.chars().toArray()) {
+            write(cp);
+        }
+    }
+
+    private void writeMask(CharSequence s) {
+        for (int cp : s.chars().toArray()) {
+            writeMask(cp);
+        }
+    }
+
+    private boolean next() {
+        return next(read());
+    }
+
+    private boolean next(int c) {
+        if (c < 0) {
+            return false;
+        }
+
+        if (c == '\'' || c == '"') {
+            inside = true;
+            write(c);
+            int match = c;
+            c = read();
+            while (c != match) {
+                if (c < 0) {
+                    return false;
+                }
+                if (c == '\n' || c == '\r') {
+                    write(c);
+                    return true;
+                }
+                if (c == '\\') {
+                    write(c);
+                    c = read();
+                }
+                write(c);
+                c = read();
+            }
+            write(c);
+            return true;
+        }
+
+        if (c == '/') {
+            c = read();
+            if (c == '*') {
+                writeMask('/');
+                writeMask(c);
+                int prevc = 0;
+                while ((c = read()) != '/' || prevc != '*') {
+                    if (c < 0) {
+                        return false;
+                    }
+                    writeMask(c);
+                    prevc = c;
+                }
+                writeMask(c);
+                return true;
+            } else if (c == '/') {
+                writeMask('/');
+                writeMask(c);
+                while ((c = read()) != '\n' && c != '\r') {
+                    if (c < 0) {
+                        return false;
+                    }
+                    writeMask(c);
+                }
+                writeMask(c);
+                return true;
+            } else {
+                inside = true;
+                write('/');
+                // read character falls through
+            }
+        }
+
+        if (Character.isJavaIdentifierStart(c)) {
+            if (maskModifiers && !inside) {
+                StringBuilder sb = new StringBuilder();
+                do {
+                    write(sb, c);
+                    c = read();
+                } while (Character.isJavaIdentifierPart(c));
+                String id = sb.toString();
+                if (IGNORED_MODIFERS.contains(id)) {
+                    writeMask(sb);
+                } else {
+                    write(sb);
+                    if (id.equals("import")) {
+                        inside = true;
+                    }
+                }
+                return next(c); // recurse to handle left-over character
+            }
+        } else if (!Character.isWhitespace(c)) {
+            inside = true;
+        }
+
+        if (c < 0) {
+            return false;
+        }
+        write(c);
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jshell/share/classes/jdk/jshell/MemoryFileManager.java	Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.TreeMap;
+import javax.tools.JavaFileObject.Kind;
+import static javax.tools.StandardLocation.CLASS_PATH;
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+
+import com.sun.tools.javac.util.DefinedBy;
+import com.sun.tools.javac.util.DefinedBy.Api;
+
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_FMGR;
+
+/**
+ * File manager for the compiler API.  Reads from memory (Strings) and writes
+ * class files to memory (cached OutputMemoryJavaFileObject).
+ *
+ * @author Robert Field
+ */
+class MemoryFileManager implements JavaFileManager {
+
+    private final StandardJavaFileManager stdFileManager;
+
+    private final Map<String, OutputMemoryJavaFileObject> classObjects = new TreeMap<>();
+
+    private ClassFileCreationListener classListener = null;
+
+    private final ClassLoader loader = new REPLClassLoader();
+
+    private final JShell proc;
+
+    // Upcoming Jigsaw
+    private Method inferModuleNameMethod = null;
+    private Method listModuleLocationsMethod = null;
+
+    static abstract class MemoryJavaFileObject extends SimpleJavaFileObject {
+
+        public MemoryJavaFileObject(String name, JavaFileObject.Kind kind) {
+            super(URI.create("string:///" + name.replace('.', '/')
+                    + kind.extension), kind);
+        }
+    }
+
+    class SourceMemoryJavaFileObject extends MemoryJavaFileObject {
+        private final String src;
+        private final Object origin;
+
+        SourceMemoryJavaFileObject(Object origin, String className, String code) {
+            super(className, JavaFileObject.Kind.SOURCE);
+            this.origin = origin;
+            this.src = code;
+        }
+
+        public Object getOrigin() {
+            return origin;
+        }
+
+        @Override @DefinedBy(Api.COMPILER)
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return src;
+        }
+    }
+
+    static class OutputMemoryJavaFileObject extends MemoryJavaFileObject {
+
+        /**
+         * Byte code created by the compiler will be stored in this
+         * ByteArrayOutputStream.
+         */
+        private ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        private byte[] bytes = null;
+
+        private final String className;
+
+        public OutputMemoryJavaFileObject(String name, JavaFileObject.Kind kind) {
+            super(name, kind);
+            this.className = name;
+        }
+
+        public byte[] getBytes() {
+            if (bytes == null) {
+                bytes = bos.toByteArray();
+                bos = null;
+            }
+            return bytes;
+        }
+
+        public void dump() {
+            try {
+                Path dumpDir = FileSystems.getDefault().getPath("dump");
+                if (Files.notExists(dumpDir)) {
+                    Files.createDirectory(dumpDir);
+                }
+                Path file = FileSystems.getDefault().getPath("dump", getName() + ".class");
+                Files.write(file, getBytes());
+            } catch (IOException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        @Override @DefinedBy(Api.COMPILER)
+        public String getName() {
+            return className;
+        }
+
+        /**
+         * Will provide the compiler with an output stream that leads to our
+         * byte array.
+         */
+        @Override @DefinedBy(Api.COMPILER)
+        public OutputStream openOutputStream() throws IOException {
+            return bos;
+        }
+
+        @Override @DefinedBy(Api.COMPILER)
+        public InputStream openInputStream() throws IOException {
+            return new ByteArrayInputStream(getBytes());
+        }
+    }
+
+    // For restoring process-local execution support
+    class REPLClassLoader extends ClassLoader {
+
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            OutputMemoryJavaFileObject fo = classObjects.get(name);
+            proc.debug(DBG_FMGR, "findClass %s = %s\n", name, fo);
+            if (fo == null) {
+                throw new ClassNotFoundException("Not ours");
+            }
+            byte[] b = fo.getBytes();
+            return super.defineClass(name, b, 0, b.length, null);
+        }
+    }
+
+    public MemoryFileManager(StandardJavaFileManager standardManager, JShell proc) {
+        this.stdFileManager = standardManager;
+        this.proc = proc;
+    }
+
+    private Collection<OutputMemoryJavaFileObject> generatedClasses() {
+        return classObjects.values();
+    }
+
+    // For debugging dumps
+    public void dumpClasses() {
+        for (OutputMemoryJavaFileObject co : generatedClasses()) {
+            co.dump();
+        }
+    }
+
+    // For restoring process-local execution support
+    public Class<?> findGeneratedClass(String genClassFullName) throws ClassNotFoundException {
+        for (OutputMemoryJavaFileObject co : generatedClasses()) {
+            if (co.className.equals(genClassFullName)) {
+                Class<?> klass = loadClass(co.className);
+                proc.debug(DBG_FMGR, "Loaded %s\n", klass);
+                return klass;
+            }
+        }
+        return null;
+    }
+
+    // For restoring process-local execution support
+    public byte[] findGeneratedBytes(String genClassFullName) throws ClassNotFoundException {
+        for (OutputMemoryJavaFileObject co : generatedClasses()) {
+            if (co.className.equals(genClassFullName)) {
+                return co.getBytes();
+            }
+        }
+        return null;
+    }
+
+    // For restoring process-local execution support
+    public Class<?> loadClass(String name) throws ClassNotFoundException {
+        return getClassLoader(null).loadClass(name);
+    }
+
+    public JavaFileObject createSourceFileObject(Object origin, String name, String code) {
+        return new SourceMemoryJavaFileObject(origin, name, code);
+    }
+
+    // Make compatible with Jigsaw
+    public String inferModuleName(Location location) {
+        try {
+            if (inferModuleNameMethod == null) {
+                inferModuleNameMethod = JavaFileManager.class.getDeclaredMethod("inferModuleName", Location.class);
+            }
+            @SuppressWarnings("unchecked")
+            String result = (String) inferModuleNameMethod.invoke(stdFileManager, location);
+            return result;
+        } catch (NoSuchMethodException | SecurityException ex) {
+            throw new InternalError("Cannot lookup JavaFileManager method", ex);
+        } catch (IllegalAccessException |
+                IllegalArgumentException |
+                InvocationTargetException ex) {
+            throw new InternalError("Cannot invoke JavaFileManager method", ex);
+        }
+    }
+
+    // Make compatible with Jigsaw
+    public Iterable<Set<Location>> listModuleLocations(Location location) throws IOException {
+        try {
+            if (listModuleLocationsMethod == null) {
+                listModuleLocationsMethod = JavaFileManager.class.getDeclaredMethod("listModuleLocations", Location.class);
+            }
+            @SuppressWarnings("unchecked")
+            Iterable<Set<Location>> result = (Iterable<Set<Location>>) listModuleLocationsMethod.invoke(stdFileManager, location);
+            return result;
+        } catch (NoSuchMethodException | SecurityException ex) {
+            throw new InternalError("Cannot lookup JavaFileManager method", ex);
+        } catch (IllegalAccessException |
+                IllegalArgumentException |
+                InvocationTargetException ex) {
+            throw new InternalError("Cannot invoke JavaFileManager method", ex);
+        }
+    }
+
+
+    /**
+     * Returns a class loader for loading plug-ins from the given location. For
+     * example, to load annotation processors, a compiler will request a class
+     * loader for the {@link
+     * StandardLocation#ANNOTATION_PROCESSOR_PATH
+     * ANNOTATION_PROCESSOR_PATH} location.
+     *
+     * @param location a location
+     * @return a class loader for the given location; or {@code null}
+     * if loading plug-ins from the given location is disabled or if
+     * the location is not known
+     * @throws SecurityException if a class loader can not be created
+     * in the current security context
+     * @throws IllegalStateException if {@link #close} has been called
+     * and this file manager cannot be reopened
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public ClassLoader getClassLoader(JavaFileManager.Location location) {
+        proc.debug(DBG_FMGR, "getClassLoader: location\n", location);
+        return loader;
+    }
+
+    /**
+     * Lists all file objects matching the given criteria in the given
+     * location.  List file objects in "subpackages" if recurse is
+     * true.
+     *
+     * <p>Note: even if the given location is unknown to this file
+     * manager, it may not return {@code null}.  Also, an unknown
+     * location may not cause an exception.
+     *
+     * @param location     a location
+     * @param packageName  a package name
+     * @param kinds        return objects only of these kinds
+     * @param recurse      if true include "subpackages"
+     * @return an Iterable of file objects matching the given criteria
+     * @throws IOException if an I/O error occurred, or if {@link
+     * #close} has been called and this file manager cannot be
+     * reopened
+     * @throws IllegalStateException if {@link #close} has been called
+     * and this file manager cannot be reopened
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public Iterable<JavaFileObject> list(JavaFileManager.Location location,
+            String packageName,
+            Set<JavaFileObject.Kind> kinds,
+            boolean recurse)
+            throws IOException {
+        Iterable<JavaFileObject> stdList = stdFileManager.list(location, packageName, kinds, recurse);
+        if (location==CLASS_PATH && packageName.equals("REPL")) {
+            // if the desired list is for our JShell package, lazily iterate over
+            // first the standard list then any generated classes.
+            return () -> new Iterator<JavaFileObject>() {
+                boolean stdDone = false;
+                Iterator<? extends JavaFileObject> it;
+
+                @Override
+                public boolean hasNext() {
+                    if (it == null) {
+                        it = stdList.iterator();
+                    }
+                    if (it.hasNext()) {
+                        return true;
+                    }
+                    if (stdDone) {
+                        return false;
+                    } else {
+                        stdDone = true;
+                        it = generatedClasses().iterator();
+                        return it.hasNext();
+                    }
+                }
+
+                @Override
+                public JavaFileObject next() {
+                    if (!hasNext()) {
+                        throw new NoSuchElementException();
+                    }
+                    return it.next();
+                }
+
+            };
+        } else {
+            return stdList;
+        }
+    }
+
+    /**
+     * Infers a binary name of a file object based on a location.  The
+     * binary name returned might not be a valid binary name according to
+     * <cite>The Java&trade {        throw new UnsupportedOperationException("Not supported yet.");  } Language Specification</cite>.
+     *
+     * @param location a location
+     * @param file a file object
+     * @return a binary name or {@code null} the file object is not
+     * found in the given location
+     * @throws IllegalStateException if {@link #close} has been called
+     * and this file manager cannot be reopened
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) {
+        if (file instanceof OutputMemoryJavaFileObject) {
+            OutputMemoryJavaFileObject ofo = (OutputMemoryJavaFileObject) file;
+            proc.debug(DBG_FMGR, "inferBinaryName %s => %s\n", file, ofo.getName());
+            return ofo.getName();
+        } else {
+            return stdFileManager.inferBinaryName(location, file);
+        }
+    }
+