Merge
authorlana
Wed, 21 Oct 2015 18:40:01 -0700
changeset 33365 b25c5559727e
parent 33025 16b4968f9bb8 (current diff)
parent 33364 542040bb5990 (diff)
child 33366 a113e08cc061
Merge
--- a/langtools/make/build.properties	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/make/build.properties	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/build.xml	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/make/build.xml	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/gensrc/Gensrc-jdk.jshell.gmk	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/intellij/langtools.iml	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/make/intellij/langtools.iml	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/intellij/workspace.xml	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/make/intellij/workspace.xml	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/launcher.sh-template	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/make/launcher.sh-template	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/netbeans/langtools/build.xml	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/make/netbeans/langtools/build.xml	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/netbeans/langtools/nbproject/project.xml	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/make/netbeans/langtools/nbproject/project.xml	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/tools/anttasks/DumpClassesTask.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/make/tools/anttasks/SelectToolTask.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/make/tools/anttasks/SelectToolTask.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/source/doctree/DocCommentTree.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/source/doctree/DocCommentTree.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -25,6 +25,7 @@
 
 package com.sun.source.doctree;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -44,6 +45,20 @@
     List<? extends DocTree> getFirstSentence();
 
     /**
+     * Returns the entire body of a documentation comment, appearing
+     * before any block tags, including the first sentence.
+     * @return body of a documentation comment first sentence inclusive
+     *
+     * @since 1.9
+     */
+    default List<? extends DocTree> getFullBody() {
+        ArrayList<DocTree> bodyList = new ArrayList<>();
+        bodyList.addAll(getFirstSentence());
+        bodyList.addAll(getBody());
+        return bodyList;
+    }
+
+    /**
      * Returns the body of a documentation comment,
      * appearing after the first sentence, and before any block tags.
      * @return the body of a documentation comment
--- a/langtools/src/jdk.compiler/share/classes/com/sun/source/util/DocTrees.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/source/util/DocTrees.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -25,12 +25,15 @@
 
 package com.sun.source.util;
 
+import java.util.List;
+
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
 import javax.tools.JavaCompiler.CompilationTask;
 
 import com.sun.source.doctree.DocCommentTree;
-import javax.tools.Diagnostic;
+import com.sun.source.doctree.DocTree;
 
 /**
  * Provides access to syntax trees for doc comments.
@@ -78,6 +81,17 @@
     public abstract Element getElement(DocTreePath path);
 
     /**
+     * Returns the list of {@link DocTree} representing the first sentence of
+     * a comment.
+     *
+     * @param list the DocTree list to interrogate
+     * @return the first sentence
+     *
+     * @since 1.9
+     */
+    public abstract List<DocTree> getFirstSentence(List<? extends DocTree> list);
+
+    /**
      * Returns a utility object for accessing the source positions
      * of documentation tree nodes.
      * @return the utility object
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/doclint/HtmlTag.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/doclint/HtmlTag.java	Wed Oct 21 18:40:01 2015 -0700
@@ -25,18 +25,17 @@
 
 package com.sun.tools.doclint;
 
-import java.util.Set;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
-
+import java.util.Set;
 import javax.lang.model.element.Name;
 
-import static com.sun.tools.doclint.HtmlTag.Attr.*;
+import com.sun.tools.javac.util.StringUtils;
 
-import com.sun.tools.javac.util.StringUtils;
+import static com.sun.tools.doclint.HtmlTag.Attr.*;
 
 /**
  * Enum representing HTML tags.
@@ -646,15 +645,14 @@
         return map;
     }
 
-    private static final Map<String,HtmlTag> index = new HashMap<>();
+    private static final Map<String, HtmlTag> index = new HashMap<>();
     static {
         for (HtmlTag t: values()) {
             index.put(t.getText(), t);
         }
     }
 
-    static HtmlTag get(Name tagName) {
+    public static HtmlTag get(Name tagName) {
         return index.get(StringUtils.toLowerCase(tagName.toString()));
     }
-
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java	Wed Oct 21 18:40:01 2015 -0700
@@ -25,7 +25,6 @@
 
 package com.sun.tools.javac.api;
 
-import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -86,9 +85,17 @@
 import com.sun.tools.javac.tree.DCTree.DCParam;
 import com.sun.tools.javac.tree.DCTree.DCReference;
 import com.sun.tools.javac.tree.DCTree.DCText;
+import com.sun.tools.javac.tree.DocTreeMaker;
 import com.sun.tools.javac.tree.EndPosTable;
 import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.*;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCCatch;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
 import com.sun.tools.javac.tree.TreeCopier;
 import com.sun.tools.javac.tree.TreeInfo;
 import com.sun.tools.javac.tree.TreeMaker;
@@ -106,6 +113,7 @@
 import com.sun.tools.javac.util.Names;
 import com.sun.tools.javac.util.Pair;
 import com.sun.tools.javac.util.Position;
+
 import static com.sun.tools.javac.code.Kinds.Kind.*;
 import static com.sun.tools.javac.code.TypeTag.*;
 
@@ -132,6 +140,7 @@
     private JavacTaskImpl javacTaskImpl;
     private Names names;
     private Types types;
+    private DocTreeMaker doctreeMaker;
 
     // called reflectively from Trees.instance(CompilationTask task)
     public static JavacTrees instance(JavaCompiler.CompilationTask task) {
@@ -173,6 +182,7 @@
         memberEnter = MemberEnter.instance(context);
         names = Names.instance(context);
         types = Types.instance(context);
+        doctreeMaker = DocTreeMaker.instance(context);
 
         JavacTask t = context.get(JavacTask.class);
         if (t instanceof JavacTaskImpl)
@@ -259,7 +269,7 @@
 
         tree.accept(new DocTreeScanner<Void, Void>() {
             @Override @DefinedBy(Api.COMPILER_TREE)
- public Void scan(DocTree node, Void p) {
+            public Void scan(DocTree node, Void p) {
                 if (node != null) last[0] = node;
                 return null;
             }
@@ -356,6 +366,11 @@
         return null;
     }
 
+    @Override @DefinedBy(Api.COMPILER_TREE)
+    public java.util.List<DocTree> getFirstSentence(java.util.List<? extends DocTree> list) {
+        return doctreeMaker.getFirstSentence(list);
+    }
+
     private Symbol attributeDocReference(TreePath path, DCReference ref) {
         Env<AttrContext> env = getAttrContext(path);
 
@@ -763,7 +778,6 @@
             javacTaskImpl.enter(null);
         }
 
-
         JCCompilationUnit unit = (JCCompilationUnit) path.getCompilationUnit();
         Copier copier = createCopier(treeMaker.forToplevel(unit));
 
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1597,6 +1597,10 @@
             }
         }
 
+        public boolean isLambdaMethod() {
+            return (flags() & LAMBDA_METHOD) == LAMBDA_METHOD;
+        }
+
         /** The implementation of this (abstract) symbol in class origin;
          *  null if none exists. Synthetic methods are not considered
          *  as possible implementations.
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java	Wed Oct 21 18:40:01 2015 -0700
@@ -263,6 +263,8 @@
                 attr.memberReferenceQualifierResult(tree));
         JCMemberReference mref2 = new TreeCopier<Void>(attr.make).copy(tree);
         mref2.expr = exprTree;
+        Symbol lhsSym = TreeInfo.symbol(exprTree);
+        localEnv.info.selectSuper = lhsSym != null && lhsSym.name == lhsSym.name.table.names._super;
         Symbol res =
                 attr.rs.getMemberReference(tree, localEnv, mref2,
                         exprTree.type, tree.name);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Wed Oct 21 18:40:01 2015 -0700
@@ -2817,8 +2817,10 @@
                 //omitted as we don't know at this stage as to whether this is a
                 //raw selector (because of inference)
                 chk.validate(that.expr, env, false);
+            } else {
+                Symbol lhsSym = TreeInfo.symbol(that.expr);
+                localEnv.info.selectSuper = lhsSym != null && lhsSym.name == names._super;
             }
-
             //attrib type-arguments
             List<Type> typeargtypes = List.nil();
             if (that.typeargs != null) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1107,8 +1107,10 @@
             endAttr(alenIdx);
             acount++;
         }
-        if (options.isSet(PARAMETERS))
-            acount += writeMethodParametersAttr(m);
+        if (options.isSet(PARAMETERS)) {
+            if (!m.isLambdaMethod()) // Per JDK-8138729, do not emit parameters table for lambda bodies.
+                acount += writeMethodParametersAttr(m);
+        }
         acount += writeMemberAttrs(m);
         acount += writeParameterAttrs(m);
         endAttrs(acountIdx, acount);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java	Wed Oct 21 18:40:01 2015 -0700
@@ -26,12 +26,8 @@
 package com.sun.tools.javac.parser;
 
 import java.text.BreakIterator;
-import java.util.Arrays;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 
 import com.sun.source.doctree.AttributeTree.ValueKind;
 import com.sun.tools.javac.parser.DocCommentParser.TagParser.Kind;
@@ -40,12 +36,10 @@
 import com.sun.tools.javac.tree.DCTree;
 import com.sun.tools.javac.tree.DCTree.DCAttribute;
 import com.sun.tools.javac.tree.DCTree.DCDocComment;
-import com.sun.tools.javac.tree.DCTree.DCEndElement;
 import com.sun.tools.javac.tree.DCTree.DCEndPosTree;
 import com.sun.tools.javac.tree.DCTree.DCErroneous;
 import com.sun.tools.javac.tree.DCTree.DCIdentifier;
 import com.sun.tools.javac.tree.DCTree.DCReference;
-import com.sun.tools.javac.tree.DCTree.DCStartElement;
 import com.sun.tools.javac.tree.DCTree.DCText;
 import com.sun.tools.javac.tree.DocTreeMaker;
 import com.sun.tools.javac.tree.JCTree;
@@ -55,9 +49,8 @@
 import com.sun.tools.javac.util.Log;
 import com.sun.tools.javac.util.Name;
 import com.sun.tools.javac.util.Names;
-import com.sun.tools.javac.util.Options;
 import com.sun.tools.javac.util.Position;
-import com.sun.tools.javac.util.StringUtils;
+
 import static com.sun.tools.javac.util.LayoutCharacters.*;
 
 /**
@@ -100,24 +93,20 @@
 
     Map<Name, TagParser> tagParsers;
 
-    DocCommentParser(ParserFactory fac, DiagnosticSource diagSource, Comment comment) {
+    public DocCommentParser(ParserFactory fac, DiagnosticSource diagSource, Comment comment) {
         this.fac = fac;
         this.diagSource = diagSource;
         this.comment = comment;
         names = fac.names;
         m = fac.docTreeMaker;
-
-        Locale locale = (fac.locale == null) ? Locale.getDefault() : fac.locale;
-
-        Options options = fac.options;
-        boolean useBreakIterator = options.isSet("breakIterator");
-        if (useBreakIterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage()))
-            sentenceBreaker = BreakIterator.getSentenceInstance(locale);
-
         initTagParsers();
     }
 
-    DCDocComment parse() {
+    public DocCommentParser(ParserFactory fac) {
+        this(fac, null, null);
+    }
+
+    public DCDocComment parse() {
         String c = comment.getText();
         buf = new char[c.length() + 1];
         c.getChars(0, c.length(), buf, 0);
@@ -128,54 +117,11 @@
 
         List<DCTree> body = blockContent();
         List<DCTree> tags = blockTags();
+        int pos = !body.isEmpty()
+                ? body.head.pos
+                : !tags.isEmpty() ? tags.head.pos : Position.NOPOS;
 
-        // split body into first sentence and body
-        ListBuffer<DCTree> fs = new ListBuffer<>();
-        loop:
-        for (; body.nonEmpty(); body = body.tail) {
-            DCTree t = body.head;
-            switch (t.getKind()) {
-                case TEXT:
-                    String s = ((DCText) t).getBody();
-                    int i = getSentenceBreak(s);
-                    if (i > 0) {
-                        int i0 = i;
-                        while (i0 > 0 && isWhitespace(s.charAt(i0 - 1)))
-                            i0--;
-                        fs.add(m.at(t.pos).Text(s.substring(0, i0)));
-                        int i1 = i;
-                        while (i1 < s.length() && isWhitespace(s.charAt(i1)))
-                            i1++;
-                        body = body.tail;
-                        if (i1 < s.length())
-                            body = body.prepend(m.at(t.pos + i1).Text(s.substring(i1)));
-                        break loop;
-                    } else if (body.tail.nonEmpty()) {
-                        if (isSentenceBreak(body.tail.head)) {
-                            int i0 = s.length() - 1;
-                            while (i0 > 0 && isWhitespace(s.charAt(i0)))
-                                i0--;
-                            fs.add(m.at(t.pos).Text(s.substring(0, i0 + 1)));
-                            body = body.tail;
-                            break loop;
-                        }
-                    }
-                    break;
-
-                case START_ELEMENT:
-                case END_ELEMENT:
-                    if (isSentenceBreak(t))
-                        break loop;
-                    break;
-            }
-            fs.add(t);
-        }
-
-        @SuppressWarnings("unchecked")
-        DCTree first = getFirst(fs.toList(), body, tags);
-        int pos = (first == null) ? Position.NOPOS : first.pos;
-
-        DCDocComment dc = m.at(pos).DocComment(comment, fs.toList(), body, tags);
+        DCDocComment dc = m.at(pos).DocComment(comment, body, tags);
         return dc;
     }
 
@@ -331,23 +277,28 @@
             nextChar();
             if (isIdentifierStart(ch)) {
                 Name name = readTagName();
-                skipWhitespace();
+                TagParser tp = tagParsers.get(name);
 
-                TagParser tp = tagParsers.get(name);
                 if (tp == null) {
-                    DCTree text = inlineText();
+                    skipWhitespace();
+                    DCTree text = inlineText(WhitespaceRetentionPolicy.REMOVE_ALL);
                     if (text != null) {
                         nextChar();
                         return m.at(p).UnknownInlineTag(name, List.of(text)).setEndPos(bp);
                     }
-                } else if (tp.getKind() == TagParser.Kind.INLINE) {
-                    DCEndPosTree<?> tree = (DCEndPosTree<?>) tp.parse(p);
-                    if (tree != null) {
-                        return tree.setEndPos(bp);
+                } else {
+                    if (!tp.retainWhiteSpace) {
+                        skipWhitespace();
                     }
-                } else {
-                    inlineText(); // skip content
-                    nextChar();
+                    if (tp.getKind() == TagParser.Kind.INLINE) {
+                        DCEndPosTree<?> tree = (DCEndPosTree<?>) tp.parse(p);
+                        if (tree != null) {
+                            return tree.setEndPos(bp);
+                        }
+                    } else { // handle block tags (ex: @see) in inline content
+                        inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip content
+                        nextChar();
+                    }
                 }
             }
             return erroneous("dc.no.tag.name", p);
@@ -356,13 +307,32 @@
         }
     }
 
+    private static enum WhitespaceRetentionPolicy {
+        RETAIN_ALL,
+        REMOVE_FIRST_SPACE,
+        REMOVE_ALL
+    }
+
     /**
      * Read plain text content of an inline tag.
      * Matching pairs of { } are skipped; the text is terminated by the first
      * unmatched }. It is an error if the beginning of the next tag is detected.
      */
-    protected DCTree inlineText() throws ParseException {
-        skipWhitespace();
+    private DCTree inlineText(WhitespaceRetentionPolicy whitespacePolicy) throws ParseException {
+        switch (whitespacePolicy) {
+            case REMOVE_ALL:
+                skipWhitespace();
+                break;
+            case REMOVE_FIRST_SPACE:
+                if (ch == ' ')
+                    nextChar();
+                break;
+            case RETAIN_ALL:
+            default:
+                // do nothing
+                break;
+
+        }
         int pos = bp;
         int depth = 1;
 
@@ -742,7 +712,8 @@
                 }
                 if (ch == '>') {
                     nextChar();
-                    return m.at(p).StartElement(name, attrs, selfClosing).setEndPos(bp);
+                    DCTree dctree = m.at(p).StartElement(name, attrs, selfClosing).setEndPos(bp);
+                    return dctree;
                 }
             }
         } else if (ch == '/') {
@@ -884,15 +855,6 @@
         return m.at(pos).Erroneous(newString(pos, i + 1), diagSource, code);
     }
 
-    @SuppressWarnings("unchecked")
-    <T> T getFirst(List<T>... lists) {
-        for (List<T> list: lists) {
-            if (list.nonEmpty())
-                return list.head;
-        }
-        return null;
-    }
-
     protected boolean isIdentifierStart(char ch) {
         return Character.isUnicodeIdentifierStart(ch);
     }
@@ -916,8 +878,11 @@
     protected Name readTagName() {
         int start = bp;
         nextChar();
-        while (bp < buflen && (Character.isUnicodeIdentifierPart(ch) || ch == '.'))
+        while (bp < buflen
+                && (Character.isUnicodeIdentifierPart(ch) || ch == '.'
+                || ch == '-' || ch == ':')) {
             nextChar();
+        }
         return names.fromChars(buf, start, bp - start);
     }
 
@@ -960,59 +925,9 @@
     }
 
     protected void skipWhitespace() {
-        while (isWhitespace(ch))
+        while (isWhitespace(ch)) {
             nextChar();
-    }
-
-    protected int getSentenceBreak(String s) {
-        if (sentenceBreaker != null) {
-            sentenceBreaker.setText(s);
-            int i = sentenceBreaker.next();
-            return (i == s.length()) ? -1 : i;
         }
-
-        // scan for period followed by whitespace
-        boolean period = false;
-        for (int i = 0; i < s.length(); i++) {
-            switch (s.charAt(i)) {
-                case '.':
-                    period = true;
-                    break;
-
-                case ' ':
-                case '\f':
-                case '\n':
-                case '\r':
-                case '\t':
-                    if (period)
-                        return i;
-                    break;
-
-                default:
-                    period = false;
-                    break;
-            }
-        }
-        return -1;
-    }
-
-
-    Set<String> htmlBlockTags = new HashSet<>(Arrays.asList(
-                    "h1", "h2", "h3", "h4", "h5", "h6", "p", "pre"));
-
-    protected boolean isSentenceBreak(Name n) {
-        return htmlBlockTags.contains(StringUtils.toLowerCase(n.toString()));
-    }
-
-    protected boolean isSentenceBreak(DCTree t) {
-        switch (t.getKind()) {
-            case START_ELEMENT:
-                return isSentenceBreak(((DCStartElement) t).getName());
-
-            case END_ELEMENT:
-                return isSentenceBreak(((DCEndElement) t).getName());
-        }
-        return false;
     }
 
     /**
@@ -1026,12 +941,21 @@
     static abstract class TagParser {
         enum Kind { INLINE, BLOCK }
 
-        Kind kind;
-        DCTree.Kind treeKind;
+        final Kind kind;
+        final DCTree.Kind treeKind;
+        final boolean retainWhiteSpace;
+
 
         TagParser(Kind k, DCTree.Kind tk) {
             kind = k;
             treeKind = tk;
+            retainWhiteSpace = false;
+        }
+
+        TagParser(Kind k, DCTree.Kind tk, boolean retainWhiteSpace) {
+            kind = k;
+            treeKind = tk;
+            this.retainWhiteSpace = retainWhiteSpace;
         }
 
         Kind getKind() {
@@ -1059,9 +983,9 @@
             },
 
             // {@code text}
-            new TagParser(Kind.INLINE, DCTree.Kind.CODE) {
+            new TagParser(Kind.INLINE, DCTree.Kind.CODE, true) {
                 public DCTree parse(int pos) throws ParseException {
-                    DCTree text = inlineText();
+                    DCTree text = inlineText(WhitespaceRetentionPolicy.REMOVE_FIRST_SPACE);
                     nextChar();
                     return m.at(pos).Code((DCText) text);
                 }
@@ -1082,7 +1006,7 @@
                         nextChar();
                         return m.at(pos).DocRoot();
                     }
-                    inlineText(); // skip unexpected content
+                    inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip unexpected content
                     nextChar();
                     throw new ParseException("dc.unexpected.content");
                 }
@@ -1105,7 +1029,7 @@
                         nextChar();
                         return m.at(pos).InheritDoc();
                     }
-                    inlineText(); // skip unexpected content
+                    inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip unexpected content
                     nextChar();
                     throw new ParseException("dc.unexpected.content");
                 }
@@ -1130,9 +1054,9 @@
             },
 
             // {@literal text}
-            new TagParser(Kind.INLINE, DCTree.Kind.LITERAL) {
+            new TagParser(Kind.INLINE, DCTree.Kind.LITERAL, true) {
                 public DCTree parse(int pos) throws ParseException {
-                    DCTree text = inlineText();
+                    DCTree text = inlineText(WhitespaceRetentionPolicy.REMOVE_FIRST_SPACE);
                     nextChar();
                     return m.at(pos).Literal((DCText) text);
                 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Oct 21 18:40:01 2015 -0700
@@ -973,7 +973,7 @@
          */
         protected JCExpression foldStrings(JCExpression tree) {
             if (!allowStringFolding)
-                return null;
+                return tree;
             ListBuffer<JCExpression> opStack = new ListBuffer<>();
             ListBuffer<JCLiteral> litBuf = new ListBuffer<>();
             boolean needsFolding = false;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -25,7 +25,6 @@
 
 package com.sun.tools.javac.tree;
 
-
 import javax.tools.Diagnostic;
 
 import com.sun.source.doctree.*;
@@ -39,8 +38,10 @@
 import com.sun.tools.javac.util.List;
 import com.sun.tools.javac.util.Name;
 import com.sun.tools.javac.util.Position;
+
 import java.io.IOException;
 import java.io.StringWriter;
+
 import javax.tools.JavaFileObject;
 
 /**
@@ -104,14 +105,19 @@
     public static class DCDocComment extends DCTree implements DocCommentTree {
         public final Comment comment; // required for the implicit source pos table
 
+        public final List<DCTree> fullBody;
         public final List<DCTree> firstSentence;
         public final List<DCTree> body;
         public final List<DCTree> tags;
 
         public DCDocComment(Comment comment,
-                List<DCTree> firstSentence, List<DCTree> body, List<DCTree> tags) {
+                            List<DCTree> fullBody,
+                            List<DCTree> firstSentence,
+                            List<DCTree> body,
+                            List<DCTree> tags) {
             this.comment = comment;
             this.firstSentence = firstSentence;
+            this.fullBody = fullBody;
             this.body = body;
             this.tags = tags;
         }
@@ -132,6 +138,11 @@
         }
 
         @DefinedBy(Api.COMPILER_TREE)
+        public List<? extends DocTree> getFullBody() {
+            return fullBody;
+        }
+
+        @DefinedBy(Api.COMPILER_TREE)
         public List<? extends DocTree> getBody() {
             return body;
         }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocPretty.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -25,15 +25,15 @@
 
 package com.sun.tools.javac.tree;
 
+import java.io.IOException;
 import java.io.Writer;
+import java.util.List;
 
 import com.sun.source.doctree.*;
 import com.sun.source.doctree.AttributeTree.ValueKind;
 import com.sun.tools.javac.util.Convert;
 import com.sun.tools.javac.util.DefinedBy;
 import com.sun.tools.javac.util.DefinedBy.Api;
-import java.io.IOException;
-import java.util.List;
 
 /**
  * Prints out a doc comment tree.
@@ -201,14 +201,10 @@
     @DefinedBy(Api.COMPILER_TREE)
     public Void visitDocComment(DocCommentTree node, Void p) {
         try {
-            List<? extends DocTree> fs = node.getFirstSentence();
-            List<? extends DocTree> b = node.getBody();
+            List<? extends DocTree> b = node.getFullBody();
             List<? extends DocTree> t = node.getBlockTags();
-            print(fs);
-            if (!fs.isEmpty() && !b.isEmpty())
-                print(" ");
             print(b);
-            if ((!fs.isEmpty() || !b.isEmpty()) && !t.isEmpty())
+            if (!b.isEmpty() && !t.isEmpty())
                 print("\n");
             print(t, "\n");
         } catch (IOException e) {
@@ -308,7 +304,10 @@
         try {
             print("{");
             printTagName(node);
-            print(" ");
+            String body = node.getBody().getBody();
+            if (!body.isEmpty() && !Character.isWhitespace(body.charAt(0))) {
+                print(" ");
+            }
             print(node.getBody());
             print("}");
         } catch (IOException e) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -25,19 +25,62 @@
 
 package com.sun.tools.javac.tree;
 
+import java.text.BreakIterator;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.ListIterator;
+import java.util.Locale;
+
 import com.sun.source.doctree.AttributeTree.ValueKind;
+import com.sun.source.doctree.DocTree;
 import com.sun.source.doctree.DocTree.Kind;
+import com.sun.source.doctree.EndElementTree;
+import com.sun.source.doctree.StartElementTree;
+import com.sun.tools.doclint.HtmlTag;
 
 import com.sun.tools.javac.parser.Tokens.Comment;
-import com.sun.tools.javac.tree.DCTree.*;
+import com.sun.tools.javac.tree.DCTree.DCAttribute;
+import com.sun.tools.javac.tree.DCTree.DCAuthor;
+import com.sun.tools.javac.tree.DCTree.DCComment;
+import com.sun.tools.javac.tree.DCTree.DCDeprecated;
+import com.sun.tools.javac.tree.DCTree.DCDocComment;
+import com.sun.tools.javac.tree.DCTree.DCDocRoot;
+import com.sun.tools.javac.tree.DCTree.DCEndElement;
+import com.sun.tools.javac.tree.DCTree.DCEntity;
+import com.sun.tools.javac.tree.DCTree.DCErroneous;
+import com.sun.tools.javac.tree.DCTree.DCIdentifier;
+import com.sun.tools.javac.tree.DCTree.DCInheritDoc;
+import com.sun.tools.javac.tree.DCTree.DCLink;
+import com.sun.tools.javac.tree.DCTree.DCLiteral;
+import com.sun.tools.javac.tree.DCTree.DCParam;
+import com.sun.tools.javac.tree.DCTree.DCReference;
+import com.sun.tools.javac.tree.DCTree.DCReturn;
+import com.sun.tools.javac.tree.DCTree.DCSee;
+import com.sun.tools.javac.tree.DCTree.DCSerial;
+import com.sun.tools.javac.tree.DCTree.DCSerialData;
+import com.sun.tools.javac.tree.DCTree.DCSerialField;
+import com.sun.tools.javac.tree.DCTree.DCSince;
+import com.sun.tools.javac.tree.DCTree.DCStartElement;
+import com.sun.tools.javac.tree.DCTree.DCText;
+import com.sun.tools.javac.tree.DCTree.DCThrows;
+import com.sun.tools.javac.tree.DCTree.DCUnknownBlockTag;
+import com.sun.tools.javac.tree.DCTree.DCUnknownInlineTag;
+import com.sun.tools.javac.tree.DCTree.DCValue;
+import com.sun.tools.javac.tree.DCTree.DCVersion;
 import com.sun.tools.javac.util.Context;
 import com.sun.tools.javac.util.DiagnosticSource;
 import com.sun.tools.javac.util.JCDiagnostic;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
 import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Options;
+import com.sun.tools.javac.util.Pair;
 import com.sun.tools.javac.util.Position;
 
+import static com.sun.tools.doclint.HtmlTag.*;
+
 /**
  *
  *  <p><b>This is NOT part of any supported API.
@@ -50,6 +93,12 @@
     /** The context key for the tree factory. */
     protected static final Context.Key<DocTreeMaker> treeMakerKey = new Context.Key<>();
 
+    // A subset of block tags, which acts as sentence breakers, appearing
+    // anywhere but the zero'th position in the first sentence.
+    final EnumSet<HtmlTag> sentenceBreakTags;
+
+    private final BreakIterator sentenceBreaker;
+
     /** Get the TreeMaker instance. */
     public static DocTreeMaker instance(Context context) {
         DocTreeMaker instance = context.get(treeMakerKey);
@@ -71,6 +120,15 @@
         context.put(treeMakerKey, this);
         diags = JCDiagnostic.Factory.instance(context);
         this.pos = Position.NOPOS;
+        sentenceBreakTags = EnumSet.of(H1, H2, H3, H4, H5, H6, PRE, P);
+        Locale locale = (context.get(Locale.class) != null)
+                ? context.get(Locale.class)
+                : Locale.getDefault();
+        Options options = Options.instance(context);
+        boolean useBreakIterator = options.isSet("breakiterator");
+        sentenceBreaker = (useBreakIterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage()))
+                ? BreakIterator.getSentenceInstance(locale)
+                : null;
     }
 
     /** Reassign current position.
@@ -117,9 +175,11 @@
         return tree;
     }
 
-    public DCDocComment DocComment(Comment comment, List<DCTree> firstSentence, List<DCTree> body, List<DCTree> tags) {
-        DCDocComment tree = new DCDocComment(comment, firstSentence, body, tags);
-        tree.pos = pos;
+    public DCDocComment DocComment(Comment comment, List<DCTree> fullBody, List<DCTree> tags) {
+        final int savepos = pos;
+        Pair<List<DCTree>, List<DCTree>> pair = splitBody(fullBody);
+        DCDocComment tree = new DCDocComment(comment, fullBody, pair.fst, pair.snd, tags);
+        this.pos = tree.pos = savepos;
         return tree;
     }
 
@@ -273,4 +333,155 @@
         tree.pos = pos;
         return tree;
     }
+
+    public java.util.List<DocTree> getFirstSentence(java.util.List<? extends DocTree> list) {
+        Pair<List<DCTree>, List<DCTree>> pair = splitBody(list);
+        return new ArrayList<>(pair.fst);
+    }
+
+    /*
+     * Breaks up the body tags into the first sentence and its successors.
+     * The first sentence is determined with the presence of a period, block tag,
+     * or a sentence break, as returned by the BreakIterator. Trailing
+     * whitespaces are trimmed.
+     */
+    Pair<List<DCTree>, List<DCTree>> splitBody(Collection<? extends DocTree> list) {
+        ListBuffer<DCTree> body = new ListBuffer<>();
+        // split body into first sentence and body
+        ListBuffer<DCTree> fs = new ListBuffer<>();
+        if (list.isEmpty()) {
+            return new Pair<>(fs.toList(), body.toList());
+        }
+        boolean foundFirstSentence = false;
+        ArrayList<DocTree> alist = new ArrayList<>(list);
+        ListIterator<DocTree> itr = alist.listIterator();
+        while (itr.hasNext()) {
+            boolean isFirst = itr.previousIndex() == -1;
+            DocTree dt = itr.next();
+            int spos = ((DCTree)dt).pos;
+            if (foundFirstSentence) {
+                body.add((DCTree) dt);
+                continue;
+            }
+            switch (dt.getKind()) {
+                case TEXT:
+                    DCText tt = (DCText)dt;
+                    String s = tt.getBody();
+                    int sbreak = getSentenceBreak(s);
+                    if (sbreak > 0) {
+                        s = removeTrailingWhitespace(s.substring(0, sbreak));
+                        DCText text = this.at(spos).Text(s);
+                        fs.add(text);
+                        foundFirstSentence = true;
+                        int nwPos = skipWhiteSpace(tt.getBody(), sbreak);
+                        if (nwPos > 0) {
+                            DCText text2 = this.at(spos + nwPos).Text(tt.getBody().substring(nwPos));
+                            body.add(text2);
+                        }
+                        continue;
+                    } else if (itr.hasNext()) {
+                        // if the next doctree is a break, remove trailing spaces
+                        DocTree next = itr.next();
+                        boolean sbrk = isSentenceBreak(next, false);
+                        if (sbrk) {
+                            s = removeTrailingWhitespace(s);
+                            DCText text = this.at(spos).Text(s);
+                            fs.add(text);
+                            body.add((DCTree)next);
+                            foundFirstSentence = true;
+                            continue;
+                        }
+                        // reset to previous for further processing
+                        itr.previous();
+                    }
+                    break;
+                default:
+                    if (isSentenceBreak(dt, isFirst)) {
+                        body.add((DCTree)dt);
+                        foundFirstSentence = true;
+                        continue;
+                    }
+            }
+            fs.add((DCTree)dt);
+        }
+        return new Pair<>(fs.toList(), body.toList());
+    }
+
+    /*
+     * Computes the first sentence break.
+     */
+    int defaultSentenceBreak(String s) {
+        // scan for period followed by whitespace
+        int period = -1;
+        for (int i = 0; i < s.length(); i++) {
+            switch (s.charAt(i)) {
+                case '.':
+                    period = i;
+                    break;
+
+                case ' ':
+                case '\f':
+                case '\n':
+                case '\r':
+                case '\t':
+                    if (period >= 0) {
+                        return i;
+                    }
+                    break;
+
+                default:
+                    period = -1;
+                    break;
+            }
+        }
+        return -1;
+    }
+
+    int getSentenceBreak(String s) {
+        if (sentenceBreaker == null) {
+            return defaultSentenceBreak(s);
+        }
+        sentenceBreaker.setText(s);
+        return sentenceBreaker.first();
+    }
+
+    boolean isSentenceBreak(javax.lang.model.element.Name tagName) {
+        return sentenceBreakTags.contains(get(tagName));
+    }
+
+    boolean isSentenceBreak(DocTree dt, boolean isFirstDocTree) {
+        switch (dt.getKind()) {
+            case START_ELEMENT:
+                    StartElementTree set = (StartElementTree)dt;
+                    return !isFirstDocTree && ((DCTree) dt).pos > 1 && isSentenceBreak(set.getName());
+            case END_ELEMENT:
+                    EndElementTree eet = (EndElementTree)dt;
+                    return !isFirstDocTree && ((DCTree) dt).pos > 1 && isSentenceBreak(eet.getName());
+            default:
+                return false;
+        }
+    }
+
+    /*
+     * Returns the position of the the first non-white space
+     */
+    int skipWhiteSpace(String s, int start) {
+        for (int i = start; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (!Character.isWhitespace(c)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    String removeTrailingWhitespace(String s) {
+        for (int i = s.length() - 1 ; i > 0 ; i--) {
+            char ch = s.charAt(i);
+            if (!Character.isWhitespace(ch)) {
+                return s.substring(0, i + 1);
+            }
+        }
+        return s;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/debug/InternalDebugControl.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteAgent.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteClassLoader.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteCodes.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteResolutionException.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/EditPad.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/EditingHistory.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ExternalEditor.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/IOContext.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/StopDetectingInputStream.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/version.properties-template	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/ClassTracker.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/Diag.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/DiagList.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/EvalException.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/GeneralWrap.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/JDIConnection.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/JDIEnv.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/JDIEventHandler.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/JDINotConnectedException.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/Key.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/KeyMap.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/MaskCommentsAndModifiers.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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/langtools/src/jdk.jshell/share/classes/jdk/jshell/MemoryFileManager.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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);
+        }
+    }
+
+    /**
+     * Compares two file objects and return true if they represent the
+     * same underlying object.
+     *
+     * @param a a file object
+     * @param b a file object
+     * @return true if the given file objects represent the same
+     * underlying object
+     *
+     * @throws IllegalArgumentException if either of the arguments
+     * were created with another file manager and this file manager
+     * does not support foreign file objects
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public boolean isSameFile(FileObject a, FileObject b) {
+        return stdFileManager.isSameFile(b, b);
+    }
+
+    /**
+     * Determines if the given option is supported and if so, the
+     * number of arguments the option takes.
+     *
+     * @param option an option
+     * @return the number of arguments the given option takes or -1 if
+     * the option is not supported
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public int isSupportedOption(String option) {
+        proc.debug(DBG_FMGR, "isSupportedOption: %s\n", option);
+        return stdFileManager.isSupportedOption(option);
+    }
+
+    /**
+     * Handles one option.  If {@code current} is an option to this
+     * file manager it will consume any arguments to that option from
+     * {@code remaining} and return true, otherwise return false.
+     *
+     * @param current current option
+     * @param remaining remaining options
+     * @return true if this option was handled by this file manager,
+     * false otherwise
+     * @throws IllegalArgumentException if this option to this file
+     * manager is used incorrectly
+     * @throws IllegalStateException if {@link #close} has been called
+     * and this file manager cannot be reopened
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public boolean handleOption(String current, Iterator<String> remaining) {
+        proc.debug(DBG_FMGR, "handleOption: current: %s\n", current +
+                ", remaining: " + remaining);
+        return stdFileManager.handleOption(current, remaining);
+    }
+
+    /**
+     * Determines if a location is known to this file manager.
+     *
+     * @param location a location
+     * @return true if the location is known
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public boolean hasLocation(JavaFileManager.Location location) {
+        proc.debug(DBG_FMGR, "hasLocation: location: %s\n", location);
+        return stdFileManager.hasLocation(location);
+    }
+
+    interface ClassFileCreationListener {
+        void newClassFile(OutputMemoryJavaFileObject jfo, JavaFileManager.Location location,
+                String className, Kind kind, FileObject sibling);
+    }
+
+    void registerClassFileCreationListener(ClassFileCreationListener listen) {
+        this.classListener = listen;
+    }
+
+    /**
+     * Returns a {@linkplain JavaFileObject file object} for input
+     * representing the specified class of the specified kind in the
+     * given location.
+     *
+     * @param location a location
+     * @param className the name of a class
+     * @param kind the kind of file, must be one of {@link
+     * JavaFileObject.Kind#SOURCE SOURCE} or {@link
+     * JavaFileObject.Kind#CLASS CLASS}
+     * @return a file object, might return {@code null} if the
+     * file does not exist
+     * @throws IllegalArgumentException if the location is not known
+     * to this file manager and the file manager does not support
+     * unknown locations, or if the kind is not valid
+     * @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 JavaFileObject getJavaFileForInput(JavaFileManager.Location location,
+            String className,
+            JavaFileObject.Kind kind)
+            throws IOException {
+        return stdFileManager.getJavaFileForInput(location, className, kind);
+    }
+
+    /**
+     * Returns a {@linkplain JavaFileObject file object} for output
+     * representing the specified class of the specified kind in the
+     * given location.
+     *
+     * <p>Optionally, this file manager might consider the sibling as
+     * a hint for where to place the output.  The exact semantics of
+     * this hint is unspecified.  The JDK compiler, javac, for
+     * example, will place class files in the same directories as
+     * originating source files unless a class file output directory
+     * is provided.  To facilitate this behavior, javac might provide
+     * the originating source file as sibling when calling this
+     * method.
+     *
+     * @param location a location
+     * @param className the name of a class
+     * @param kind the kind of file, must be one of {@link
+     * JavaFileObject.Kind#SOURCE SOURCE} or {@link
+     * JavaFileObject.Kind#CLASS CLASS}
+     * @param sibling a file object to be used as hint for placement;
+     * might be {@code null}
+     * @return a file object for output
+     * @throws IllegalArgumentException if sibling is not known to
+     * this file manager, or if the location is not known to this file
+     * manager and the file manager does not support unknown
+     * locations, or if the kind is not valid
+     * @throws IOException if an I/O error occurred, or if {@link
+     * #close} has been called and this file manager cannot be
+     * reopened
+     * @throws IllegalStateException {@link #close} has been called
+     * and this file manager cannot be reopened
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location,
+            String className, Kind kind, FileObject sibling) throws IOException {
+
+        OutputMemoryJavaFileObject fo;
+        fo = new OutputMemoryJavaFileObject(className, kind);
+        classObjects.put(className, fo);
+        proc.debug(DBG_FMGR, "Set out file: %s = %s\n", className, fo);
+        if (classListener != null) {
+            classListener.newClassFile(fo, location, className, kind, sibling);
+        }
+        return fo;
+    }
+
+    /**
+     * Returns a {@linkplain FileObject file object} for input
+     * representing the specified <a href="JavaFileManager.html#relative_name">relative
+     * name</a> in the specified package in the given location.
+     *
+     * <p>If the returned object represents a {@linkplain
+     * JavaFileObject.Kind#SOURCE source} or {@linkplain
+     * JavaFileObject.Kind#CLASS class} file, it must be an instance
+     * of {@link JavaFileObject}.
+     *
+     * <p>Informally, the file object returned by this method is
+     * located in the concatenation of the location, package name, and
+     * relative name.  For example, to locate the properties file
+     * "resources/compiler.properties" in the package
+     * "com.sun.tools.javac" in the {@linkplain
+     * StandardLocation#SOURCE_PATH SOURCE_PATH} location, this method
+     * might be called like so:
+     *
+     * <pre>getFileForInput(SOURCE_PATH, "com.sun.tools.javac", "resources/compiler.properties");</pre>
+     *
+     * <p>If the call was executed on Windows, with SOURCE_PATH set to
+     * <code>"C:\Documents&nbsp;and&nbsp;Settings\UncleBob\src\share\classes"</code>,
+     * a valid result would be a file object representing the file
+     * <code>"C:\Documents&nbsp;and&nbsp;Settings\UncleBob\src\share\classes\com\sun\tools\javac\resources\compiler.properties"</code>.
+     *
+     * @param location a location
+     * @param packageName a package name
+     * @param relativeName a relative name
+     * @return a file object, might return {@code null} if the file
+     * does not exist
+     * @throws IllegalArgumentException if the location is not known
+     * to this file manager and the file manager does not support
+     * unknown locations, or if {@code relativeName} is not valid
+     * @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 FileObject getFileForInput(JavaFileManager.Location location,
+            String packageName,
+            String relativeName)
+            throws IOException {
+        proc.debug(DBG_FMGR, "getFileForInput location=%s packageName=%s\n", location, packageName);
+        return stdFileManager.getFileForInput(location, packageName, relativeName);
+    }
+
+    /**
+     * Returns a {@linkplain FileObject file object} for output
+     * representing the specified <a href="JavaFileManager.html#relative_name">relative
+     * name</a> in the specified package in the given location.
+     *
+     * <p>Optionally, this file manager might consider the sibling as
+     * a hint for where to place the output.  The exact semantics of
+     * this hint is unspecified.  The JDK compiler, javac, for
+     * example, will place class files in the same directories as
+     * originating source files unless a class file output directory
+     * is provided.  To facilitate this behavior, javac might provide
+     * the originating source file as sibling when calling this
+     * method.
+     *
+     * <p>If the returned object represents a {@linkplain
+     * JavaFileObject.Kind#SOURCE source} or {@linkplain
+     * JavaFileObject.Kind#CLASS class} file, it must be an instance
+     * of {@link JavaFileObject}.
+     *
+     * <p>Informally, the file object returned by this method is
+     * located in the concatenation of the location, package name, and
+     * relative name or next to the sibling argument.  See {@link
+     * #getFileForInput getFileForInput} for an example.
+     *
+     * @param location a location
+     * @param packageName a package name
+     * @param relativeName a relative name
+     * @param sibling a file object to be used as hint for placement;
+     * might be {@code null}
+     * @return a file object
+     * @throws IllegalArgumentException if sibling is not known to
+     * this file manager, or if the location is not known to this file
+     * manager and the file manager does not support unknown
+     * locations, or if {@code relativeName} is not valid
+     * @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 FileObject getFileForOutput(JavaFileManager.Location location,
+            String packageName,
+            String relativeName,
+            FileObject sibling)
+            throws IOException {
+        throw new UnsupportedOperationException("getFileForOutput: location: " + location +
+                ", packageName: " + packageName +
+                ", relativeName: " + relativeName +
+                ", sibling: " + sibling);
+    }
+
+    /**
+     * Flushes any resources opened for output by this file manager
+     * directly or indirectly.  Flushing a closed file manager has no
+     * effect.
+     *
+     * @throws IOException if an I/O error occurred
+     * @see #close
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public void flush() throws IOException {
+        // Nothing to flush
+    }
+
+    /**
+     * Releases any resources opened by this file manager directly or
+     * indirectly.  This might render this file manager useless and
+     * the effect of subsequent calls to methods on this object or any
+     * objects obtained through this object is undefined unless
+     * explicitly allowed.  However, closing a file manager which has
+     * already been closed has no effect.
+     *
+     * @throws IOException if an I/O error occurred
+     * @see #flush
+     */
+    @Override @DefinedBy(Api.COMPILER)
+    public void close() throws IOException {
+        // Nothing to close
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/MethodSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,94 @@
+/*
+ * 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.MethodKey;
+
+/**
+ * Snippet for a method definition.
+ * The Kind is {@link jdk.jshell.Snippet.Kind#METHOD}.
+ * <p>
+ * <code>MethodSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ * @jls 8.4: MethodDeclaration.
+ */
+public class MethodSnippet extends DeclarationSnippet {
+
+    final String signature;
+    private String qualifiedParamaterTypes;
+
+    MethodSnippet(MethodKey key, String userSource, Wrap guts,
+            String name, String signature, Wrap corralled,
+            Collection<String> declareReferences, Collection<String> bodyReferences) {
+        super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled, declareReferences, bodyReferences);
+        this.signature = signature;
+    }
+
+    /**
+     * A String representation of the parameter types of the method.
+     * @return a comma separated list of user entered parameter types for the
+     * method.
+     */
+    public String parameterTypes() {
+        return key().parameterTypes();
+    }
+
+    /**
+     * The full type signature of the method, including return type.
+     * @return A String representation of the parameter and return types
+     */
+    public String signature() {
+        return signature;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("MethodSnippet:");
+        sb.append(name());
+        sb.append('/');
+        sb.append(signature());
+        sb.append('-');
+        sb.append(source());
+        return sb.toString();
+    }
+
+    /**** internal access ****/
+
+    @Override
+    MethodKey key() {
+        return (MethodKey) super.key();
+    }
+
+    String qualifiedParameterTypes() {
+        return qualifiedParamaterTypes;
+    }
+
+    void setQualifiedParamaterTypes(String sig) {
+        qualifiedParamaterTypes = sig;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/OuterWrap.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,189 @@
+/*
+ * 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.Wrap.CompoundWrap;
+import static jdk.jshell.Util.*;
+import java.util.Locale;
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+import jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject;
+
+/**
+ *
+ * @author Robert Field
+ */
+final class OuterWrap implements GeneralWrap {
+
+    private final String packageName;
+    private final String className;
+    private final String userSource;
+    private final GeneralWrap w;
+    private final Wrap guts;
+
+    public static OuterWrap wrapInClass(String packageName, String className,
+             String imports, String userSource, Wrap guts) {
+        GeneralWrap kw = new CompoundWrap(
+                imports
+                + "class " + className + " {\n",
+                guts,
+                "}\n");
+        return new OuterWrap(packageName, className, userSource, kw, guts);
+    }
+
+    public static OuterWrap wrapImport(String userSource, Wrap guts) {
+        return new OuterWrap("", "", userSource, guts, guts);
+    }
+
+    private OuterWrap(String packageName, String className, String userSource,
+            GeneralWrap w, Wrap guts) {
+        this.packageName = packageName;
+        this.className = className;
+        this.userSource = userSource;
+        this.w = w;
+        this.guts = guts;
+    }
+
+    @Override
+    public final String wrapped() {
+        return w.wrapped();
+    }
+
+    @Override
+    public int snippetIndexToWrapIndex(int ui) {
+        return w.snippetIndexToWrapIndex(ui);
+    }
+
+    @Override
+    public int wrapIndexToSnippetIndex(int si) {
+        return w.wrapIndexToSnippetIndex(si);
+    }
+
+    @Override
+    public int firstSnippetIndex() {
+        return w.firstSnippetIndex();
+    }
+
+    @Override
+    public int lastSnippetIndex() {
+        return w.lastSnippetIndex();
+    }
+
+    @Override
+    public int snippetLineToWrapLine(int snline) {
+        return w.snippetLineToWrapLine(snline);
+    }
+
+    @Override
+    public int wrapLineToSnippetLine(int wline) {
+        return w.wrapLineToSnippetLine(wline);
+    }
+
+    @Override
+    public int firstSnippetLine() {
+        return w.firstSnippetLine();
+    }
+
+    @Override
+    public int lastSnippetLine() {
+        return w.lastSnippetLine();
+    }
+
+    public String className() {
+        return className;
+    }
+
+    public String classFullName() {
+        return packageName + "." + className;
+    }
+
+    public String getUserSource() {
+        return userSource;
+    }
+
+    Wrap guts() {
+        return guts;
+    }
+
+    Diag wrapDiag(Diagnostic<? extends JavaFileObject> d) {
+        return new WrappedDiagnostic(d);
+    }
+
+    class WrappedDiagnostic extends Diag {
+
+        private final Diagnostic<? extends JavaFileObject> diag;
+
+        WrappedDiagnostic(Diagnostic<? extends JavaFileObject> diag) {
+            this.diag = diag;
+        }
+
+        @Override
+        public boolean isError() {
+            return diag.getKind() == Diagnostic.Kind.ERROR;
+        }
+
+        @Override
+        public long getPosition() {
+            return wrapIndexToSnippetIndex(diag.getPosition());
+        }
+
+        @Override
+        public long getStartPosition() {
+            return wrapIndexToSnippetIndex(diag.getStartPosition());
+        }
+
+        @Override
+        public long getEndPosition() {
+            return wrapIndexToSnippetIndex(diag.getEndPosition());
+        }
+
+        @Override
+        public String getCode() {
+            return diag.getCode();
+        }
+
+        @Override
+        public String getMessage(Locale locale) {
+            return expunge(diag.getMessage(locale));
+        }
+
+        @Override
+        Unit unitOrNull() {
+            JavaFileObject fo = diag.getSource();
+            if (fo instanceof SourceMemoryJavaFileObject) {
+                SourceMemoryJavaFileObject sfo = (SourceMemoryJavaFileObject) fo;
+                if (sfo.getOrigin() instanceof Unit) {
+                    return (Unit) sfo.getOrigin();
+                }
+            }
+            return null;
+        }
+
+    @Override
+    public String toString() {
+        return "WrappedDiagnostic(" + getMessage(null) + ":" + getPosition() + ")";
+    }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/PersistentSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+/**
+ * Grouping for Snippets which persist and influence future code.
+ * A persistent snippet can be
+ * {@linkplain jdk.jshell.Snippet.Status#OVERWRITTEN overwritten)}
+ * with new input and can be dropped {@link JShell#drop}.
+ * <p>
+ * <code>PersistentSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ */
+public abstract class PersistentSnippet extends Snippet {
+
+    PersistentSnippet(Key key, String userSource, Wrap guts, String unitName, SubKind subkind) {
+        super(key, userSource, guts, unitName, subkind);
+    }
+
+    /**
+     * Name of the Snippet.
+     *
+     * @return the name of the snippet.
+     */
+    @Override
+    public String name() {
+        return unitName;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,272 @@
+/*
+ * 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 com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.parser.JavacParser;
+import com.sun.tools.javac.parser.ParserFactory;
+import com.sun.tools.javac.parser.Tokens.Comment;
+import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
+import com.sun.tools.javac.parser.Tokens.Token;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.CLASS;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.COLON;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.ENUM;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.EOF;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.INTERFACE;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.LPAREN;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.MONKEYS_AT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.PACKAGE;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.SEMI;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.VOID;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.Tag;
+import static com.sun.tools.javac.tree.JCTree.Tag.IDENT;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Position;
+
+/**
+ * This is a subclass of JavacParser which overrides one method with a modified
+ * verson of that method designed to allow parsing of one "snippet" of Java
+ * code without the surrounding context of class, method, etc.
+ * Accepts an expression, a statement, an import, or the declaration of a
+ * method, variable, or type (class, interface, ...).
+ */
+class ReplParser extends JavacParser {
+
+    public ReplParser(ParserFactory fac,
+            com.sun.tools.javac.parser.Lexer S,
+            boolean keepDocComments,
+            boolean keepLineMap,
+            boolean keepEndPositions) {
+        super(fac, S, keepDocComments, keepLineMap, keepEndPositions);
+    }
+
+    /**
+     * As faithful a clone of the overridden method as possible while still
+     * achieving the goal of allowing the parse of a stand-alone snippet.
+     * As a result, some variables are assigned and never used, tests are
+     * always true, loops don't, etc.  This is to allow easy transition as the
+     * underlying method changes.
+     * @return a snippet wrapped in a compilation unit
+     */
+    @Override
+    public JCCompilationUnit parseCompilationUnit() {
+        Token firstToken = token;
+        JCModifiers mods = null;
+        boolean seenImport = false;
+        boolean seenPackage = false;
+        ListBuffer<JCTree> defs = new ListBuffer<>();
+        if (token.kind == MONKEYS_AT) {
+            mods = modifiersOpt();
+        }
+
+        if (token.kind == PACKAGE) {
+            int packagePos = token.pos;
+            List<JCAnnotation> annotations = List.nil();
+            seenPackage = true;
+            if (mods != null) {
+                checkNoMods(mods.flags);
+                annotations = mods.annotations;
+                mods = null;
+            }
+            nextToken();
+            JCExpression pid = qualident(false);
+            accept(SEMI);
+            JCPackageDecl pd = F.at(packagePos).PackageDecl(annotations, pid);
+            attach(pd, firstToken.comment(CommentStyle.JAVADOC));
+            storeEnd(pd, token.pos);
+            defs.append(pd);
+        }
+
+        boolean firstTypeDecl = true;
+        while (token.kind != EOF) {
+            if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) {
+                // error recovery
+                skip(true, false, false, false);
+                if (token.kind == EOF) {
+                    break;
+                }
+            }
+            if (mods == null && token.kind == IMPORT) {
+                seenImport = true;
+                defs.append(importDeclaration());
+            } else {
+                Comment docComment = token.comment(CommentStyle.JAVADOC);
+                if (firstTypeDecl && !seenImport && !seenPackage) {
+                    docComment = firstToken.comment(CommentStyle.JAVADOC);
+                }
+                List<? extends JCTree> udefs = replUnit(mods, docComment);
+               // if (def instanceof JCExpressionStatement)
+                //     def = ((JCExpressionStatement)def).expr;
+                for (JCTree def : udefs) {
+                    defs.append(def);
+                }
+                mods = null;
+                firstTypeDecl = false;
+            }
+            break;  // Remove to process more than one snippet
+        }
+        List<JCTree> rdefs = defs.toList();
+        class ReplUnit extends JCCompilationUnit {
+
+            public ReplUnit(List<JCTree> defs) {
+                super(defs);
+            }
+        }
+        JCCompilationUnit toplevel = new ReplUnit(rdefs);
+        if (rdefs.isEmpty()) {
+            storeEnd(toplevel, S.prevToken().endPos);
+        }
+        toplevel.lineMap = S.getLineMap();
+        this.endPosTable.setParser(null); // remove reference to parser
+        toplevel.endPositions = this.endPosTable;
+        return toplevel;
+    }
+
+    @SuppressWarnings("fallthrough")
+     List<? extends JCTree> replUnit(JCModifiers pmods, Comment dc) {
+        switch (token.kind) {
+            case EOF:
+                return List.nil();
+            case RBRACE:
+            case CASE:
+            case DEFAULT:
+                // These are illegal, fall through to handle as illegal statement
+            case LBRACE:
+            case IF:
+            case FOR:
+            case WHILE:
+            case DO:
+            case TRY:
+            case SWITCH:
+            case RETURN:
+            case THROW:
+            case BREAK:
+            case CONTINUE:
+            case SEMI:
+            case ELSE:
+            case FINALLY:
+            case CATCH:
+            case ASSERT:
+                return List.<JCTree>of(parseStatement());
+            case SYNCHRONIZED:
+                if (peekToken(LPAREN)) {
+                    return List.<JCTree>of(parseStatement());
+                }
+                //fall-through
+            default:
+                JCModifiers mods = modifiersOpt(pmods);
+                if (token.kind == CLASS
+                        || token.kind == INTERFACE
+                        || token.kind == ENUM) {
+                    return List.<JCTree>of(classOrInterfaceOrEnumDeclaration(mods, dc));
+                } else {
+                    int pos = token.pos;
+                    List<JCTypeParameter> typarams = typeParametersOpt();
+                    // if there are type parameters but no modifiers, save the start
+                    // position of the method in the modifiers.
+                    if (typarams.nonEmpty() && mods.pos == Position.NOPOS) {
+                        mods.pos = pos;
+                        storeEnd(mods, pos);
+                    }
+                    List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION);
+
+                    if (annosAfterParams.nonEmpty()) {
+                        checkAnnotationsAfterTypeParams(annosAfterParams.head.pos);
+                        mods.annotations = mods.annotations.appendList(annosAfterParams);
+                        if (mods.pos == Position.NOPOS) {
+                            mods.pos = mods.annotations.head.pos;
+                        }
+                    }
+
+                    Token prevToken = token;
+                    pos = token.pos;
+                    JCExpression t;
+                    boolean isVoid = token.kind == VOID;
+                    if (isVoid) {
+                        t = to(F.at(pos).TypeIdent(TypeTag.VOID));
+                        nextToken();
+                    } else {
+                        // return type of method, declared type of variable, or an expression
+                        t = term(EXPR | TYPE);
+                    }
+                    if (token.kind == COLON && t.hasTag(IDENT)) {
+                        // labelled statement
+                        nextToken();
+                        JCStatement stat = parseStatement();
+                        return List.<JCTree>of(F.at(pos).Labelled(prevToken.name(), stat));
+                    } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.accepts(token.kind)) {
+                        // we have "Type Ident", so we can assume it is variable or method declaration
+                        pos = token.pos;
+                        Name name = ident();
+                        if (token.kind == LPAREN) {
+                        // method declaration
+                            //mods.flags |= Flags.STATIC;
+                            return List.of(methodDeclaratorRest(
+                                    pos, mods, t, name, typarams,
+                                    false, isVoid, dc));
+                        } else if (!isVoid && typarams.isEmpty()) {
+                        // variable declaration
+                            //mods.flags |= Flags.STATIC;
+                            List<JCTree> defs
+                                    = variableDeclaratorsRest(pos, mods, t, name, false, dc,
+                                            new ListBuffer<JCTree>()).toList();
+                            accept(SEMI);
+                            storeEnd(defs.last(), S.prevToken().endPos);
+                            return defs;
+                        } else {
+                            // malformed declaration, return error
+                            pos = token.pos;
+                            List<JCTree> err = isVoid
+                                    ? List.<JCTree>of(toP(F.at(pos).MethodDef(mods, name, t, typarams,
+                                                            List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null)))
+                                    : null;
+                            return List.<JCTree>of(syntaxError(token.pos, err, "expected", LPAREN));
+                        }
+                    } else if (!typarams.isEmpty()) {
+                        // type parameters on non-variable non-method -- error
+                        return List.<JCTree>of(syntaxError(token.pos, "illegal.start.of.type"));
+                    } else {
+                        // expression-statement or expression to evaluate
+                        JCExpressionStatement expr = toP(F.at(pos).Exec(t));
+                        return List.<JCTree>of(expr);
+                    }
+
+                }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,58 @@
+/*
+ * 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 com.sun.tools.javac.parser.JavacParser;
+import com.sun.tools.javac.parser.ParserFactory;
+import com.sun.tools.javac.parser.ScannerFactory;
+import com.sun.tools.javac.util.Context;
+
+/**
+ *
+ * @author Robert Field
+ */
+class ReplParserFactory extends ParserFactory {
+
+    public static ParserFactory instance(Context context) {
+        ParserFactory instance = context.get(parserFactoryKey);
+        if (instance == null) {
+            instance = new ReplParserFactory(context);
+        }
+        return instance;
+    }
+
+    private final ScannerFactory scannerFactory;
+
+    protected ReplParserFactory(Context context) {
+        super(context);
+        this.scannerFactory = ScannerFactory.instance(context);
+    }
+
+    @Override
+    public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) {
+        com.sun.tools.javac.parser.Lexer lexer = scannerFactory.newScanner(input, keepDocComments);
+        return new ReplParser(this, lexer, keepDocComments, keepLineMap, keepEndPos);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ReplResolve.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,45 @@
+/*
+ * 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.comp.AttrContext;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.comp.Resolve;
+import com.sun.tools.javac.util.Context;
+
+/**
+ * Just need access to isStatic
+ */
+class ReplResolve extends Resolve {
+
+    ReplResolve(Context context) {
+        super(context);
+    }
+
+    public static boolean isStatic(Env<AttrContext> env) {
+        return Resolve.isStatic(env);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,681 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+import static jdk.jshell.Util.REPL_CLASS_PREFIX;
+import static jdk.jshell.Util.asLetters;
+
+/**
+ * A Snippet represents a snippet of Java source code as passed to
+ * {@link jdk.jshell.JShell#eval}.  It is associated only with the
+ * {@link jdk.jshell.JShell JShell} instance that created it.
+ * An instance of Snippet (including its subclasses) is immutable: an access to
+ * any of its methods will always return the same result.
+ * For information about the current state of the snippet within the JShell
+ * state engine, query <code>JShell</code> passing the Snippet.
+ * <p>
+ * Because it is immutable, <code>Snippet</code> (and subclasses) is thread-safe.
+ * @author Robert Field
+ * @see jdk.jshell.JShell#status
+ */
+public abstract class Snippet {
+
+    /**
+     * Describes the general kind of snippet.
+     * The <code>Kind</code> is an immutable property of a Snippet.
+     * It is accessed with {@link jdk.jshell.Snippet#kind()}.
+     * The <code>Kind</code> can be used to determine which
+     * subclass of Snippet it is. For example,
+     * {@link jdk.jshell.JShell#eval eval("int three() { return 3; }")} will
+     * return a snippet creation event.  The <code>Kind</code> of that Snippet
+     * will be <code>METHOD</code>, from which you know that the subclass
+     * of <code>Snippet</code> is <code>MethodSnippet</code> and it can be
+     * cast as such.
+     */
+    public enum Kind {
+        /**
+         * An import declaration: <code>import</code> ...
+         * The snippet is an instance of {@link jdk.jshell.ImportSnippet}.
+         * An import can be a single type import
+         * ({@link jdk.jshell.Snippet.SubKind#SINGLE_TYPE_IMPORT_SUBKIND}),
+         * a static single import
+         * ({@link jdk.jshell.Snippet.SubKind#SINGLE_STATIC_IMPORT_SUBKIND}),
+         * an on-demand type import
+         * ({@link jdk.jshell.Snippet.SubKind#TYPE_IMPORT_ON_DEMAND_SUBKIND}),
+         * or a static on-demand type import
+         * ({@link jdk.jshell.Snippet.SubKind#SINGLE_STATIC_IMPORT_SUBKIND}) --
+         * use {@link jdk.jshell.Snippet#subKind()} to distinguish.
+         * @jls 8.3: importDeclaration.
+         */
+        IMPORT(true),
+
+        /**
+         * A type declaration.
+         * Which includes: NormalClassDeclaration, EnumDeclaration,
+         * NormalInterfaceDeclaration, and AnnotationTypeDeclaration.
+         * The snippet is an instance of {@link jdk.jshell.TypeDeclSnippet}.
+         * A type declaration may be an interface
+         * {@link jdk.jshell.Snippet.SubKind#INTERFACE_SUBKIND},
+         * classes {@link jdk.jshell.Snippet.SubKind#CLASS_SUBKIND}, enums, and
+         * annotation interfaces -- see {@link jdk.jshell.Snippet.SubKind} to
+         * differentiate.
+         * @jls 7.6: TypeDeclaration.
+         */
+        TYPE_DECL(true),
+
+        /**
+         * A method declaration.
+         * The snippet is an instance of {@link jdk.jshell.MethodSnippet}.
+         * @jls 8.4: MethodDeclaration.
+         */
+        METHOD(true),
+
+        /**
+         * One variable declaration.
+         * Corresponding to one <i>VariableDeclarator</i>.
+         * The snippet is an instance of {@link jdk.jshell.VarSnippet}.
+         * The variable may be with or without initializer, or be a temporary
+         * variable representing an expression -- see
+         * {@link jdk.jshell.Snippet.SubKind}to differentiate.
+         * @jls 8.3: FieldDeclaration.
+         */
+        VAR(true),
+
+        /**
+         * An expression, with or without side-effects.
+         * The snippet is an instance of {@link jdk.jshell.ExpressionSnippet}.
+         * The expression is currently either a simple named reference to a
+         * variable ({@link jdk.jshell.Snippet.SubKind#VAR_VALUE_SUBKIND}) or an
+         * assignment (both of which have natural referencing
+         * names) -- see {@link jdk.jshell.Snippet.SubKind} to differentiate.
+         * @jls 15: Expression.
+         */
+        EXPRESSION(false),
+
+        /**
+         * A statement.
+         * The snippet is an instance of {@link jdk.jshell.StatementSnippet}.
+         * @jls 14.5: Statement.
+         */
+        STATEMENT(false),
+
+        /**
+         * A syntactically incorrect input for which the specific
+         * kind could not be determined.
+         * The snippet is an instance of {@link jdk.jshell.ErroneousSnippet}.
+         */
+        ERRONEOUS(false);
+
+        /**
+         * True if this kind of snippet adds a declaration or declarations
+         * which are visible to subsequent evaluations.
+         */
+        public final boolean isPersistent;
+
+        Kind(boolean isPersistent) {
+            this.isPersistent = isPersistent;
+        }
+    }
+
+    /**
+     * The detailed variety of a snippet.  This is a sub-classification of the
+     * Kind.  The Kind of a SubKind is accessible with
+     * {@link jdk.jshell.Snippet.SubKind#kind}.
+     */
+    public enum SubKind {
+
+        /**
+         * Single-Type-Import Declaration.
+         * An import declaration of a single type.
+         * @jls 7.5.1 SingleTypeImportDeclaration.
+         */
+        SINGLE_TYPE_IMPORT_SUBKIND(Kind.IMPORT),
+
+        /**
+         * Type-Import-on-Demand Declaration.
+         * A non-static "star" import.
+         * @jls 7.5.2. TypeImportOnDemandDeclaration.
+         */
+        TYPE_IMPORT_ON_DEMAND_SUBKIND(Kind.IMPORT),
+
+        /**
+         * Single-Static-Import Declaration.
+         * An import of a static member.
+         * @jls 7.5.3 Single-Static-Import.
+         */
+        SINGLE_STATIC_IMPORT_SUBKIND(Kind.IMPORT),
+
+        /**
+         * Static-Import-on-Demand Declaration.
+         * A static "star" import of all static members of a named type.
+         * @jls 7.5.4. Static-Import-on-Demand Static "star" import.
+         */
+        STATIC_IMPORT_ON_DEMAND_SUBKIND(Kind.IMPORT),
+
+        /**
+         * A class declaration.
+         * A <code>SubKind</code> of {@link Kind#TYPE_DECL}.
+         * @jls 8.1. NormalClassDeclaration.
+         */
+        CLASS_SUBKIND(Kind.TYPE_DECL),
+
+        /**
+         * An interface declaration.
+         * A <code>SubKind</code> of {@link Kind#TYPE_DECL}.
+         * @jls 9.1. NormalInterfaceDeclaration.
+         */
+        INTERFACE_SUBKIND(Kind.TYPE_DECL),
+
+        /**
+         * An enum declaration.
+         * A <code>SubKind</code> of {@link Kind#TYPE_DECL}.
+         * @jls 8.9. EnumDeclaration.
+         */
+        ENUM_SUBKIND(Kind.TYPE_DECL),
+
+        /**
+         * An annotation interface declaration. A <code>SubKind</code> of
+         * {@link Kind#TYPE_DECL}.
+         * @jls 9.6. AnnotationTypeDeclaration.
+         */
+        ANNOTATION_TYPE_SUBKIND(Kind.TYPE_DECL),
+
+        /**
+         * A method. The only <code>SubKind</code> for {@link Kind#METHOD}.
+         * @jls 8.4. MethodDeclaration.
+         */
+        METHOD_SUBKIND(Kind.METHOD),
+
+        /**
+         * A variable declaration without initializer.
+         * A <code>SubKind</code> of {@link Kind#VAR}.
+         * @jls 8.3. VariableDeclarator without VariableInitializer in
+         * FieldDeclaration.
+         */
+        VAR_DECLARATION_SUBKIND(Kind.VAR),
+
+        /**
+         * A variable declaration with an initializer expression. A
+         * <code>SubKind</code> of {@link Kind#VAR}.
+         * @jls 8.3. VariableDeclarator with VariableInitializer in
+         * FieldDeclaration.
+         */
+        VAR_DECLARATION_WITH_INITIALIZER_SUBKIND(Kind.VAR, true, true),
+
+        /**
+         * An expression whose value has been stored in a temporary variable. A
+         * <code>SubKind</code> of {@link Kind#VAR}.
+         * @jls 15. Primary.
+         */
+        TEMP_VAR_EXPRESSION_SUBKIND(Kind.VAR, true, true),
+
+        /**
+         * A simple variable reference expression. A <code>SubKind</code> of
+         * {@link Kind#EXPRESSION}.
+         * @jls 15.11. Field Access as 3.8. Identifier.
+         */
+        VAR_VALUE_SUBKIND(Kind.EXPRESSION, true, true),
+
+        /**
+         * An assignment expression. A <code>SubKind</code> of
+         * {@link Kind#EXPRESSION}.
+         * @jls 15.26. Assignment.
+         */
+        ASSIGNMENT_SUBKIND(Kind.EXPRESSION, true, true),
+
+        /**
+         * An expression which has not been wrapped in a temporary variable
+         * (reserved). A <code>SubKind</code> of {@link Kind#EXPRESSION}.
+         */
+        OTHER_EXPRESSION_SUBKIND(Kind.EXPRESSION, true, true),
+
+        /**
+         * A statement. The only <code>SubKind</code> for {@link Kind#STATEMENT}.
+         * @jls 14.5. Statement.
+         */
+        STATEMENT_SUBKIND(Kind.STATEMENT, true, false),
+
+        /**
+         * An unknown snippet. The only <code>SubKind</code> for
+         * {@link Kind#ERRONEOUS}.
+         */
+        UNKNOWN_SUBKIND(Kind.ERRONEOUS, false, false);
+
+        private final boolean isExecutable;
+        private final boolean hasValue;
+        private final Kind kind;
+
+        SubKind(Kind kind) {
+            this.kind = kind;
+            this.isExecutable = false;
+            this.hasValue = false;
+        }
+
+        SubKind(Kind kind, boolean isExecutable, boolean hasValue) {
+            this.kind = kind;
+            this.isExecutable = isExecutable;
+            this.hasValue = hasValue;
+        }
+
+        /**
+         * Is this <code>SubKind</code> executable?
+         *
+         * @return true if this <code>SubKind</code> can be executed.
+         */
+        public boolean isExecutable() {
+            return isExecutable;
+        }
+
+        /**
+         * Is this <code>SubKind</code> executable and is non-<code>void</code>.
+         *
+         * @return true if this <code>SubKind</code> has a value.
+         */
+        public boolean hasValue() {
+            return hasValue;
+        }
+
+        /**
+         * The {@link Snippet.Kind} that corresponds to this <code>SubKind</code>.
+         *
+         * @return the fixed <code>Kind</code> for this <code>SubKind</code>
+         */
+        public Kind kind() {
+            return kind;
+        }
+    }
+
+    /**
+     * Describes the current state of a Snippet.
+     * This is a dynamic property of a Snippet within the JShell state --
+     * thus is retrieved with a {@linkplain
+     * jdk.jshell.JShell#status(jdk.jshell.Snippet) query on <code>JShell</code>}.
+     * <p>
+     * The <code>Status</code> changes as the state changes.
+     * For example, creation of another snippet with
+     * {@link jdk.jshell.JShell#eval(java.lang.String) eval}
+     * may resolve dependencies of this Snippet (or invalidate those dependencies), or
+     * {@linkplain jdk.jshell.Snippet.Status#OVERWRITTEN overwrite}
+     * this Snippet changing its
+     * <code>Status</code>.
+     * <p>
+     * Important properties associated with <code>Status</code> are:
+     * {@link jdk.jshell.Snippet.Status#isDefined}, if it is visible to other
+     * existing and new snippets; and
+     * {@link jdk.jshell.Snippet.Status#isActive}, if, as the
+     * JShell state changes, the snippet will update, possibly
+     * changing <code>Status</code>.
+     * An executable Snippet can only be executed if it is in the the
+     * {@link jdk.jshell.Snippet.Status#VALID} <code>Status</code>.
+     * @see JShell#status(jdk.jshell.Snippet)
+     */
+    public enum Status {
+        /**
+         * The snippet is a valid snippet
+         * (in the context of current <code>JShell</code> state).
+         * Only snippets with <code>VALID</code>
+         * <code>Status</code> can be executed (though not all
+         * <code>VALID</code> snippets have executable code).
+         * If the snippet is a declaration or import, it is visible to other
+         * snippets ({@link Status#isDefined isDefined} <code> == true</code>).
+         * <p>
+         * The snippet will update as dependents change
+         * ({@link Status#isActive isActive} <code> == true</code>), its
+         * status could become <code>RECOVERABLE_DEFINED</code>, <code>RECOVERABLE_NOT_DEFINED</code>,
+         * <code>DROPPED</code>, or <code>OVERWRITTEN</code>.
+         */
+        VALID(true, true),
+
+        /**
+         * The snippet is a declaration snippet with potentially recoverable
+         * unresolved references or other issues in its body
+         * (in the context of current <code>JShell</code> state).
+         * Only a {@link jdk.jshell.DeclarationSnippet} can have this
+         * <code>Status</code>.
+         * The snippet has a valid signature and it is visible to other
+         * snippets ({@link Status#isDefined isDefined} <code> == true</code>)
+         * and thus can be referenced in existing or new snippets
+         * but the snippet cannot be executed.
+         * An {@link UnresolvedReferenceException} will be thrown on an attempt
+         * to execute it.
+         * <p>
+         * The snippet will update as dependents change
+         * ({@link Status#isActive isActive} <code> == true</code>), its
+         * status could become <code>VALID</code>, <code>RECOVERABLE_NOT_DEFINED</code>,
+         * <code>DROPPED</code>, or <code>OVERWRITTEN</code>.
+         * <p>
+         * Note: both <code>RECOVERABLE_DEFINED</code> and <code>RECOVERABLE_NOT_DEFINED</code>
+         * indicate potentially recoverable errors, they differ in that, for
+         * <code>RECOVERABLE_DEFINED</code>, the snippet is
+         * {@linkplain Status#isDefined defined}.
+         */
+        RECOVERABLE_DEFINED(true, true),
+
+        /**
+         * The snippet is a declaration snippet with potentially recoverable
+         * unresolved references or other issues
+         * (in the context of current <code>JShell</code> state).
+         * Only a {@link jdk.jshell.DeclarationSnippet} can have this
+         * <code>Status</code>.
+         * The snippet has an invalid signature or the implementation is
+         * otherwise unable to define it.
+         * The snippet it is not visible to other snippets
+         * ({@link Status#isDefined isDefined} <code> == false</code>)
+         * and thus cannot be referenced or executed.
+         * <p>
+         * The snippet will update as dependents change
+         * ({@link Status#isActive isActive} <code> == true</code>), its
+         * status could become <code>VALID</code>, <code>RECOVERABLE_DEFINED</code>,
+         * <code>DROPPED</code>, or <code>OVERWRITTEN</code>.
+         * <p>
+         * Note: both <code>RECOVERABLE_DEFINED</code> and <code>RECOVERABLE_NOT_DEFINED</code>
+         * indicate potentially recoverable errors, they differ in that, for
+         * <code>RECOVERABLE_DEFINED</code>, the snippet is
+         * {@linkplain Status#isDefined defined}.
+         */
+        RECOVERABLE_NOT_DEFINED(true, false),
+
+        /**
+         * The snippet is inactive because of an explicit call to
+         * the {@link JShell#drop(jdk.jshell.PersistentSnippet)}.
+         * Only a {@link jdk.jshell.PersistentSnippet} can have this
+         * <code>Status</code>.
+         * The snippet is not visible to other snippets
+         * ({@link Status#isDefined isDefined} <code> == false</code>)
+         * and thus cannot be referenced or executed.
+         * <p>
+         * The snippet will not update as dependents change
+         * ({@link Status#isActive isActive} <code> == false</code>), its
+         * <code>Status</code> will never change again.
+         */
+        DROPPED(false, false),
+
+        /**
+         * The snippet is inactive because it has been replaced by a new
+         * snippet.  This occurs when the new snippet added with
+         * {@link jdk.jshell.JShell#eval} matches a previous snippet.
+         * A <code>TypeDeclSnippet</code> will match another
+         * <code>TypeDeclSnippet</code> if the names match.
+         * For example <code>class X { }</code> will overwrite
+         * <code>class X { int ii; }</code> or
+         * <code>interface X { }</code>.
+         * A <code>MethodSnippet</code> will match another
+         * <code>MethodSnippet</code> if the names and parameter types
+         * match.
+         * For example <code>void m(int a) { }</code> will overwrite
+         * <code>int m(int a) { return a+a; }</code>.
+         * A <code>VarSnippet</code> will match another
+         * <code>VarSnippet</code> if the names match.
+         * For example <code>double z;</code> will overwrite
+         * <code>long z = 2L;</code>.
+         * Only a {@link jdk.jshell.PersistentSnippet} can have this
+         * <code>Status</code>.
+         * The snippet is not visible to other snippets
+         * ({@link Status#isDefined isDefined} <code> == false</code>)
+         * and thus cannot be referenced or executed.
+         * <p>
+         * The snippet will not update as dependents change
+         * ({@link Status#isActive isActive} <code> == false</code>), its
+         * <code>Status</code> will never change again.
+         */
+        OVERWRITTEN(false, false),
+
+        /**
+         * The snippet is inactive because it failed compilation on initial
+         * evaluation and it is not capable of becoming valid with further
+         * changes to the JShell state.
+         * The snippet is not visible to other snippets
+         * ({@link Status#isDefined isDefined} <code> == false</code>)
+         * and thus cannot be referenced or executed.
+         * <p>
+         * The snippet will not update as dependents change
+         * ({@link Status#isActive isActive} <code> == false</code>), its
+         * <code>Status</code> will never change again.
+         */
+        REJECTED(false, false),
+
+        /**
+         * The snippet is inactive because it does not yet exist.
+         * Used only in {@link SnippetEvent#previousStatus} for new
+         * snippets.
+         * {@link jdk.jshell.JShell#status(jdk.jshell.Snippet) JShell.status(Snippet)}
+         * will never return this <code>Status</code>.
+         * Vacuously, {@link Status#isDefined isDefined} and
+         * {@link Status#isActive isActive} are both defined <code>false</code>.
+         */
+        NONEXISTENT(false, false);
+
+        /**
+         * Is the Snippet active, that is, will the snippet
+         * be re-evaluated when a new
+         * {@link JShell#eval(java.lang.String) JShell.eval(String)} or
+         * {@link JShell#drop(jdk.jshell.PersistentSnippet)
+         * JShell.drop(PersistentSnippet)} that could change
+         * its status is invoked?  This is more broad than
+         * {@link Status#isDefined} since a Snippet which is
+         * {@link Status#RECOVERABLE_NOT_DEFINED}
+         * will be updated.
+         */
+        public final boolean isActive;
+
+        /**
+         * Is the snippet currently part of the defined state of the JShell?
+         * Is it visible to compilation of other snippets?
+         */
+        public final boolean isDefined;
+
+        Status(boolean isActive, boolean isDefined) {
+            this.isActive = isActive;
+            this.isDefined = isDefined;
+        }
+    }
+
+    private final Key key;
+    private final String source;
+    private final Wrap guts;
+    final String unitName;
+    private final SubKind subkind;
+
+    private int seq;
+    private String className;
+    private String id;
+    private OuterWrap outer;
+    private Status status;
+    private List<String> unresolved;
+    private DiagList diagnostics;
+
+    Snippet(Key key, String userSource, Wrap guts, String unitName, SubKind subkind) {
+        this.key = key;
+        this.source = userSource;
+        this.guts = guts;
+        this.unitName = unitName;
+        this.subkind = subkind;
+        this.status = Status.NONEXISTENT;
+        setSequenceNumber(0);
+    }
+
+    /**** public access ****/
+
+    /**
+     * The unique identifier for the snippet. No two active snippets will have
+     * the same id().
+     * @return the snippet id string.
+     */
+    public String id() {
+        return id;
+    }
+
+    /**
+     * The {@link jdk.jshell.Snippet.Kind} for the snippet.
+     * Indicates the subclass of Snippet.
+     * @return the Kind of the snippet
+     * @see Snippet.Kind
+     */
+    public Kind kind() {
+        return subkind.kind();
+    }
+
+    /**
+     * Return the {@link SubKind} of snippet.
+     * The SubKind is useful for feedback to users.
+     * @return the SubKind corresponding to this snippet
+     */
+    public SubKind subKind() {
+        return subkind;
+    }
+
+    /**
+     * Return the source code of the snippet.
+     * @return the source code corresponding to this snippet
+     */
+    public String source() {
+        return source;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Snippet:");
+        if (key() != null) {
+            sb.append(key().toString());
+        }
+        sb.append('-');
+        sb.append(source);
+        return sb.toString();
+    }
+
+    //**** internal access ****
+
+    String name() {
+        return unitName;
+    }
+
+    Key key() {
+        return key;
+    }
+
+    List<String> unresolved() {
+        return Collections.unmodifiableList(unresolved);
+    }
+
+    DiagList diagnostics() {
+        return diagnostics;
+    }
+
+    /**
+     * @return the corralled guts
+     */
+    Wrap corralled() {
+        return null;
+    }
+
+    Collection<String> declareReferences() {
+        return null;
+    }
+
+    Collection<String> bodyReferences() {
+        return null;
+    }
+
+    String importLine(JShell state) {
+        return "";
+    }
+
+    void setId(String id) {
+        this.id = id;
+    }
+
+    final void setSequenceNumber(int seq) {
+        this.seq = seq;
+        this.className = REPL_CLASS_PREFIX + key().index() + asLetters(seq);
+    }
+
+    void setOuterWrap(OuterWrap outer) {
+        this.outer = outer;
+    }
+
+    void setCompilationStatus(Status status, List<String> unresolved, DiagList diagnostics) {
+        this.status = status;
+        this.unresolved = unresolved;
+        this.diagnostics = diagnostics;
+    }
+
+    void setDiagnostics(DiagList diagnostics) {
+        this.diagnostics = diagnostics;
+    }
+
+    void setFailed(DiagList diagnostics) {
+        this.seq = -1;
+        this.outer = null;
+        this.status = Status.REJECTED;
+        this.unresolved = Collections.emptyList();
+        this.diagnostics = diagnostics;
+    }
+
+    void setDropped() {
+        this.status = Status.DROPPED;
+    }
+
+    void setOverwritten() {
+        this.status = Status.OVERWRITTEN;
+    }
+
+    Status status() {
+        return status;
+    }
+
+    String className() {
+        return className;
+    }
+
+    /**
+     * Top-level wrap
+     * @return
+     */
+    OuterWrap outerWrap() {
+        return outer;
+    }
+
+    /**
+     * Basically, class version for this Key.
+     * @return int
+     */
+    int sequenceNumber() {
+        return seq;
+    }
+
+    Wrap guts() {
+        return guts;
+    }
+
+    boolean isExecutable() {
+        return subkind.isExecutable();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SnippetEvent.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,152 @@
+/*
+ * 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.Status;
+
+/**
+ * A description of a change to a Snippet. These are generated by direct changes
+ * to state with {@link JShell#eval(java.lang.String) JShell.eval(String)} or
+ * {@link JShell#drop(jdk.jshell.PersistentSnippet) JShell.drop(PersistentSnippet)},
+ * or indirectly by these same methods as
+ * dependencies change or Snippets are overwritten. For direct changes, the
+ * {@link SnippetEvent#causeSnippet()} is <code>null</code>.
+ * <p>
+ * <code>SnippetEvent</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ * @author Robert Field
+ */
+public class SnippetEvent {
+
+    SnippetEvent(Snippet snippet, Status previousStatus, Status status,
+            boolean isSignatureChange, Snippet causeSnippet,
+            String value, Exception exception) {
+        this.snippet = snippet;
+        this.previousStatus = previousStatus;
+        this.status = status;
+        this.isSignatureChange = isSignatureChange;
+        this.causeSnippet = causeSnippet;
+        this.value = value;
+        this.exception = exception;
+    }
+
+    private final Snippet snippet;
+    private final Status previousStatus;
+    private final Status status;
+    private final boolean isSignatureChange;
+    private final Snippet causeSnippet;
+    private final String value;
+    private final Exception exception;
+
+    /**
+     * The Snippet which has changed
+     * @return the return the Snippet whose <code>Status</code> has changed.
+     */
+    public Snippet snippet() {
+        return snippet;
+    }
+
+    /**
+     * The status before the transition. If this event describes a Snippet
+     * creation return {@link Snippet.Status#NONEXISTENT NONEXISTENT}.
+     * @return the previousStatus
+     */
+    public Status previousStatus() {
+        return previousStatus;
+    }
+
+    /**
+     * The after status. Note: this may be the same as the previous status (not
+     * all changes cause a <code>Status</code> change.
+     * @return the status
+     */
+    public Status status() {
+        return status;
+    }
+
+    /**
+     * Has the signature changed? Coming in or out of definition
+     * (status.isDefined) is always a signature change. An overwritten Snippet
+     * {@link jdk.jshell.Snippet.Status#OVERWRITTEN (status == OVERWRITTEN)}
+     * is always <code>false</code> as responsibility for the
+     * definition has passed to the overwriting definition.
+     * @return <code>true</code> if the signature changed.
+     */
+    public boolean isSignatureChange() {
+        return isSignatureChange;
+    }
+
+    /**
+     * Either the snippet whose change caused this update or
+     * <code>null</code>. This returns <code>null</code> if this change is the
+     * creation of a new Snippet via
+     * {@link jdk.jshell.JShell#eval(java.lang.String) eval} or it is the
+     * explicit drop of a Snippet with
+     * {@link jdk.jshell.JShell#drop(jdk.jshell.PersistentSnippet) drop}.
+     *
+     * @return the Snippet which caused this change or <code>null</code> if
+     * directly caused by an API action.
+     */
+    public Snippet causeSnippet() {
+        return causeSnippet;
+    }
+
+    /**
+     * An instance of {@link jdk.jshell.UnresolvedReferenceException}, if an unresolved reference was
+     * encountered, or an instance of {@link jdk.jshell.EvalException} if an exception was thrown
+     * during execution, otherwise <code>null</code>.
+     * @return the exception or <code>null</code>.
+     */
+    public Exception exception() {
+        return exception;
+    }
+
+    /**
+     * The result value of successful run. The value is null if not executed
+     * or an exception was thrown.
+     * @return the value or <code>null</code>.
+     */
+    public String value() {
+        return value;
+    }
+
+    /**
+     * Return a string representation of the event
+     * @return a descriptive representation of the SnippetEvent
+     */
+    @Override
+    public String toString() {
+        return "SnippetEvent(snippet=" + snippet +
+                ",previousStatus=" + previousStatus +
+                ",status=" + status +
+                ",isSignatureChange=" + isSignatureChange +
+                ",causeSnippet" + causeSnippet +
+                (value == null? "" : "value=" + value) +
+                (exception == null? "" : "exception=" + exception) +
+                ")";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,200 @@
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.toList;
+import static jdk.internal.jshell.remote.RemoteCodes.prefixPattern;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_DEP;
+
+/**
+ * Maintain relationships between the significant entities: Snippets,
+ * internal snippet index, Keys, etc.
+ * @author Robert Field
+ */
+final class SnippetMaps {
+
+    private String packageName;
+    private final List<Snippet> keyIndexToSnippet = new ArrayList<>();
+    private final Set<Snippet> snippets = new LinkedHashSet<>();
+    private final Map<String, Set<Integer>> dependencies = new HashMap<>();
+    private final JShell state;
+
+    SnippetMaps(JShell proc) {
+        this.state = proc;
+    }
+
+    void installSnippet(Snippet sn) {
+        if (sn != null && snippets.add(sn)) {
+            if (sn.key() != null) {
+                sn.setId((state.idGenerator != null)
+                        ? state.idGenerator.apply(sn, sn.key().index())
+                        : "" + sn.key().index());
+                setSnippet(sn.key().index(), sn);
+            }
+        }
+    }
+
+    private void setSnippet(int ki, Snippet snip) {
+        while (ki >= keyIndexToSnippet.size()) {
+            keyIndexToSnippet.add(null);
+        }
+        keyIndexToSnippet.set(ki, snip);
+    }
+
+    Snippet getSnippet(Key key) {
+        return getSnippet(key.index());
+    }
+
+    Snippet getSnippet(int ki) {
+        if (ki >= keyIndexToSnippet.size()) {
+            return null;
+        }
+        return keyIndexToSnippet.get(ki);
+    }
+
+    List<Snippet> snippetList() {
+        return new ArrayList<>(snippets);
+    }
+
+    void setPackageName(String n) {
+        packageName = n;
+    }
+
+    String packageName() {
+        return packageName;
+    }
+
+    String classFullName(Snippet sn) {
+        return packageName + "." + sn.className();
+    }
+
+    String packageAndImportsExcept(Set<Key> except, Collection<Snippet> plus) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("package ").append(packageName()).append(";\n");
+        for (Snippet si : keyIndexToSnippet) {
+            if (si != null && si.status().isDefined && (except == null || !except.contains(si.key()))) {
+                sb.append(si.importLine(state));
+            }
+        }
+        if (plus != null) {
+            plus.stream()
+                    .forEach(psi -> sb.append(psi.importLine(state)));
+        }
+        return sb.toString();
+    }
+
+    List<Snippet> getDependents(Snippet snip) {
+        if (!snip.kind().isPersistent) {
+            return Collections.emptyList();
+        }
+        Set<Integer> depset;
+        if (snip.unitName.equals("*")) {
+            // star import
+            depset = new HashSet<>();
+            for (Set<Integer> as : dependencies.values()) {
+                depset.addAll(as);
+            }
+        } else {
+            depset = dependencies.get(snip.name());
+        }
+        if (depset == null) {
+            return Collections.emptyList();
+        }
+        List<Snippet> deps = new ArrayList<>();
+        for (Integer dss : depset) {
+            Snippet dep = getSnippet(dss);
+            if (dep != null) {
+                deps.add(dep);
+                state.debug(DBG_DEP, "Found dependency %s -> %s\n", snip.name(), dep.name());
+            }
+        }
+        return deps;
+    }
+
+    void mapDependencies(Snippet snip) {
+        addDependencies(snip.declareReferences(), snip);
+        addDependencies(snip.bodyReferences(),    snip);
+    }
+
+    private void addDependencies(Collection<String> refs, Snippet snip) {
+        if (refs == null) return;
+        for (String ref : refs) {
+            dependencies.computeIfAbsent(ref, k -> new HashSet<>())
+                        .add(snip.key().index());
+            state.debug(DBG_DEP, "Added dependency %s -> %s\n", ref, snip.name());
+        }
+    }
+
+    String fullClassNameAndPackageToClass(String full, String pkg) {
+        Matcher mat = prefixPattern.matcher(full);
+        if (mat.lookingAt()) {
+            return full.substring(mat.end());
+        }
+        state.debug(DBG_DEP, "SM %s %s\n", full, pkg);
+        List<String> klasses = importSnippets()
+                               .filter(isi -> !isi.isStar)
+                               .map(isi -> isi.fullname)
+                               .collect(toList());
+        for (String k : klasses) {
+            if (k.equals(full)) {
+                return full.substring(full.lastIndexOf(".")+1, full.length());
+            }
+        }
+        List<String> pkgs = importSnippets()
+                               .filter(isi -> isi.isStar)
+                               .map(isi -> isi.fullname.substring(0, isi.fullname.lastIndexOf(".")))
+                               .collect(toList());
+        pkgs.add(0, "java.lang");
+        for (String ipkg : pkgs) {
+            if (!ipkg.isEmpty() && ipkg.equals(pkg)) {
+                return full.substring(pkg.length() + 1);
+            }
+        }
+        return full;
+    }
+
+    /**
+     * Compute the set of imports to prepend to a snippet
+     * @return a stream of the import needed
+     */
+    private Stream<ImportSnippet> importSnippets() {
+        return state.keyMap.importKeys()
+                .map(key -> (ImportSnippet)getSnippet(key))
+                .filter(sn -> state.status(sn).isDefined);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,201 @@
+/*
+ * 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.List;
+
+/**
+ * Provides analysis utilities for source code input.
+ * Optional functionality that provides for a richer interactive experience.
+ * Includes completion analysis:
+ * Is the input a complete snippet of code?
+ * Do I need to prompt for more input?
+ * Would adding a semicolon make it complete?
+ * Is there more than one snippet?
+ * etc.
+ * Also includes completion suggestions, as might be used in tab-completion.
+ *
+ */
+public abstract class SourceCodeAnalysis {
+
+    /**
+     * Given an input string, find the first snippet of code (one statement,
+     * definition, import, or expression) and evaluate if it is complete.
+     * @param input the input source string
+     * @return a CompletionInfo instance with location and completeness info
+     */
+    public abstract CompletionInfo analyzeCompletion(String input);
+
+    /**
+     * Compute possible follow-ups for the given input.
+     * Uses information from the current <code>JShell</code> state, including
+     * type information, to filter the suggestions.
+     * @param input the user input, so far
+     * @param cursor the current position of the cursors in the given {@code input} text
+     * @param anchor outgoing parameter - when an option will be completed, the text between
+     *               the anchor and cursor will be deleted and replaced with the given option
+     * @return list of candidate continuations of the given input.
+     */
+    public abstract List<Suggestion> completionSuggestions(String input, int cursor, int[] anchor);
+
+    /**
+     * Compute a description/help string for the given user's input.
+     * @param input the snippet the user wrote so far
+     * @param cursor the current position of the cursors in the given {@code input} text
+     * @return description/help string for the given user's input
+     */
+    public abstract String documentation(String input, int cursor);
+
+    /**
+     * Internal only constructor
+     */
+    SourceCodeAnalysis() {}
+
+    /**
+     * The result of <code>analyzeCompletion(String input)</code>.
+     * Describes the completeness and position of the first snippet in the given input.
+     */
+    public static class CompletionInfo {
+
+        public CompletionInfo(Completeness completeness, int unitEndPos, String source, String remaining) {
+            this.completeness = completeness;
+            this.unitEndPos = unitEndPos;
+            this.source = source;
+            this.remaining = remaining;
+        }
+
+        /**
+         * The analyzed completeness of the input.
+         */
+        public final Completeness completeness;
+
+        /**
+         * The end of the first unit of source.
+         */
+        public final int unitEndPos;
+
+        /**
+         * Source code for the first unit of code input.  For example, first
+         * statement, or first method declaration.  Trailing semicolons will
+         * be added, as needed
+         */
+        public final String source;
+
+        /**
+         * Input remaining after the source
+         */
+        public final String remaining;
+    }
+
+    /**
+     * Describes the completeness of the given input.
+     */
+    public enum Completeness {
+        /**
+         * The input is a complete source snippet (declaration or statement) as is.
+         */
+        COMPLETE(true),
+
+        /**
+         * With this addition of a semicolon the input is a complete source snippet.
+         * This will only be returned when the end of input is encountered.
+         */
+        COMPLETE_WITH_SEMI(true),
+
+        /**
+         * There must be further source beyond the given input in order for it
+         * to be complete.  A semicolon would not complete it.
+         * This will only be returned when the end of input is encountered.
+         */
+        DEFINITELY_INCOMPLETE(false),
+
+        /**
+         * A statement with a trailing (non-terminated) empty statement.
+         * Though technically it would be a complete statement
+         * with the addition of a semicolon, it is rare
+         * that that assumption is the desired behavior.
+         * The input is considered incomplete.  Comments and white-space are
+         * still considered empty.
+         */
+        CONSIDERED_INCOMPLETE(false),
+
+
+        /**
+         * An empty input.
+         * The input is considered incomplete.  Comments and white-space are
+         * still considered empty.
+         */
+        EMPTY(false),
+
+        /**
+         * The completeness of the input could not be determined because it
+         * contains errors. Error detection is not a goal of completeness
+         * analysis, however errors interfered with determining its completeness.
+         * The input is considered complete because evaluating is the best
+         * mechanism to get error information.
+         */
+        UNKNOWN(true);
+
+        /**
+         * Is the first snippet of source complete. For example, "x=" is not
+         * complete, but "x=2" is complete, even though a subsequent line could
+         * make it "x=2+2". Already erroneous code is marked complete.
+         */
+        public final boolean isComplete;
+
+        Completeness(boolean isComplete) {
+            this.isComplete = isComplete;
+        }
+    }
+
+    /**
+     * A candidate for continuation of the given user's input.
+     */
+    public static class Suggestion {
+
+        /**
+         * Create a {@code Suggestion} instance.
+         * @param continuation a candidate continuation of the user's input
+         * @param isSmart is the candidate "smart"
+         */
+        public Suggestion(String continuation, boolean isSmart) {
+            this.continuation = continuation;
+            this.isSmart = isSmart;
+        }
+
+        /**
+         * The candidate continuation of the given user's input.
+         */
+        public final String continuation;
+
+        /**
+         * Is it an input continuation that matches the target type and is thus more
+         * likely to be the desired continuation. A smart continuation
+         * is preferred.
+         */
+        public final boolean isSmart;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,1000 @@
+/*
+ * 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 jdk.jshell.SourceCodeAnalysis.Completeness;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ErroneousTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.ImportTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Scope;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import com.sun.tools.javac.api.JavacScope;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol.CompletionFailure;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.ClassType;
+import com.sun.tools.javac.util.DefinedBy;
+import com.sun.tools.javac.util.DefinedBy.Api;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Names;
+import com.sun.tools.javac.util.Pair;
+import jdk.jshell.CompletenessAnalyzer.CaInfo;
+import jdk.jshell.TaskFactory.AnalyzeTask;
+import jdk.jshell.TaskFactory.ParseTask;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Predicate;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_COMPA;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toCollection;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.QualifiedNameable;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+
+import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
+
+/**
+ * The concrete implementation of SourceCodeAnalysis.
+ * @author Robert Field
+ */
+class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
+    private final JShell proc;
+    private final CompletenessAnalyzer ca;
+
+    SourceCodeAnalysisImpl(JShell proc) {
+        this.proc = proc;
+        this.ca = new CompletenessAnalyzer(proc);
+    }
+
+    @Override
+    public CompletionInfo analyzeCompletion(String srcInput) {
+        MaskCommentsAndModifiers mcm = new MaskCommentsAndModifiers(srcInput, false);
+        String cleared = mcm.cleared();
+        String trimmedInput = Util.trimEnd(cleared);
+        if (trimmedInput.isEmpty()) {
+            // Just comment or empty
+            return new CompletionInfo(Completeness.EMPTY, srcInput.length(), srcInput, "");
+        }
+        CaInfo info = ca.scan(trimmedInput);
+        Completeness status = info.status;
+        int unitEndPos = info.unitEndPos;
+        if (unitEndPos > srcInput.length()) {
+            unitEndPos = srcInput.length();
+        }
+        int nonCommentNonWhiteLength = trimmedInput.length();
+        String src = srcInput.substring(0, unitEndPos);
+        switch (status) {
+            case COMPLETE:
+                if (unitEndPos == nonCommentNonWhiteLength) {
+                    // The unit is the whole non-coment/white input plus semicolon
+                    String compileSource = src
+                            + mcm.mask().substring(nonCommentNonWhiteLength);
+                    proc.debug(DBG_COMPA, "Complete: %s\n", compileSource);
+                    proc.debug(DBG_COMPA, "   nothing remains.\n");
+                    return new CompletionInfo(status, unitEndPos, compileSource, "");
+                } else {
+                    String remain = srcInput.substring(unitEndPos);
+                    proc.debug(DBG_COMPA, "Complete: %s\n", src);
+                    proc.debug(DBG_COMPA, "          remaining: %s\n", remain);
+                    return new CompletionInfo(status, unitEndPos, src, remain);
+                }
+            case COMPLETE_WITH_SEMI:
+                // The unit is the whole non-coment/white input plus semicolon
+                String compileSource = src
+                        + ";"
+                        + mcm.mask().substring(nonCommentNonWhiteLength);
+                proc.debug(DBG_COMPA, "Complete with semi: %s\n", compileSource);
+                proc.debug(DBG_COMPA, "   nothing remains.\n");
+                return new CompletionInfo(status, unitEndPos, compileSource, "");
+            case DEFINITELY_INCOMPLETE:
+                proc.debug(DBG_COMPA, "Incomplete: %s\n", srcInput);
+                return new CompletionInfo(status, unitEndPos, null, srcInput + '\n');
+            case CONSIDERED_INCOMPLETE:
+                proc.debug(DBG_COMPA, "Considered incomplete: %s\n", srcInput);
+                return new CompletionInfo(status, unitEndPos, null, srcInput + '\n');
+            case EMPTY:
+                proc.debug(DBG_COMPA, "Detected empty: %s\n", srcInput);
+                return new CompletionInfo(status, unitEndPos, srcInput, "");
+            case UNKNOWN:
+                proc.debug(DBG_COMPA, "Detected error: %s\n", srcInput);
+                return new CompletionInfo(status, unitEndPos, srcInput, "");
+        }
+        throw new InternalError();
+    }
+
+    private OuterWrap wrapInClass(Wrap guts) {
+        String imports = proc.maps.packageAndImportsExcept(null, null);
+        return OuterWrap.wrapInClass(proc.maps.packageName(), REPL_DOESNOTMATTER_CLASS_NAME, imports, "", guts);
+    }
+
+    private Tree.Kind guessKind(String code) {
+        ParseTask pt = proc.taskFactory.new ParseTask(code);
+        List<? extends Tree> units = pt.units();
+        if (units.isEmpty()) {
+            return Tree.Kind.BLOCK;
+        }
+        Tree unitTree = units.get(0);
+        proc.debug(DBG_COMPA, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
+        return unitTree.getKind();
+    }
+
+    //TODO: would be better handled through a lexer:
+    private final Pattern JAVA_IDENTIFIER = Pattern.compile("\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*");
+
+    @Override
+    public List<Suggestion> completionSuggestions(String code, int cursor, int[] anchor) {
+        code = code.substring(0, cursor);
+        Matcher m = JAVA_IDENTIFIER.matcher(code);
+        String identifier = "";
+        while (m.find()) {
+            if (m.end() == code.length()) {
+                cursor = m.start();
+                code = code.substring(0, cursor);
+                identifier = m.group();
+            }
+        }
+        code = code.substring(0, cursor);
+        if (code.trim().isEmpty()) { //TODO: comment handling
+            code += ";";
+        }
+        OuterWrap codeWrap;
+        switch (guessKind(code)) {
+            case IMPORT:
+                codeWrap = OuterWrap.wrapImport(null, Wrap.importWrap(code + "any.any"));
+                break;
+            case METHOD:
+                codeWrap = wrapInClass(Wrap.classMemberWrap(code));
+                break;
+            default:
+                codeWrap = wrapInClass(Wrap.methodWrap(code));
+                break;
+        }
+        String requiredPrefix = identifier;
+        return computeSuggestions(codeWrap, cursor, anchor).stream()
+                .filter(s -> s.continuation.startsWith(requiredPrefix) && !s.continuation.equals(REPL_DOESNOTMATTER_CLASS_NAME))
+                .sorted(Comparator.comparing(s -> s.continuation))
+                .collect(collectingAndThen(toList(), Collections::unmodifiableList));
+    }
+
+    private List<Suggestion> computeSuggestions(OuterWrap code, int cursor, int[] anchor) {
+        AnalyzeTask at = proc.taskFactory.new AnalyzeTask(code);
+        SourcePositions sp = at.trees().getSourcePositions();
+        CompilationUnitTree topLevel = at.cuTree();
+        List<Suggestion> result = new ArrayList<>();
+        TreePath tp = pathFor(topLevel, sp, code.snippetIndexToWrapIndex(cursor));
+        if (tp != null) {
+            Scope scope = at.trees().getScope(tp);
+            Predicate<Element> accessibility = createAccessibilityFilter(at, tp);
+            Predicate<Element> smartTypeFilter;
+            Predicate<Element> smartFilter;
+            Iterable<TypeMirror> targetTypes = findTargetType(at, tp);
+            if (targetTypes != null) {
+                smartTypeFilter = el -> {
+                    TypeMirror resultOf = resultTypeOf(el);
+                    return Util.stream(targetTypes)
+                            .anyMatch(targetType -> at.getTypes().isAssignable(resultOf, targetType));
+                };
+
+                smartFilter = IS_CLASS.negate()
+                                      .and(IS_INTERFACE.negate())
+                                      .and(IS_PACKAGE.negate())
+                                      .and(smartTypeFilter);
+            } else {
+                smartFilter = TRUE;
+                smartTypeFilter = TRUE;
+            }
+            switch (tp.getLeaf().getKind()) {
+                case MEMBER_SELECT: {
+                    MemberSelectTree mst = (MemberSelectTree)tp.getLeaf();
+                    if (mst.getIdentifier().contentEquals("*"))
+                        break;
+                    TreePath exprPath = new TreePath(tp, mst.getExpression());
+                    TypeMirror site = at.trees().getTypeMirror(exprPath);
+                    boolean staticOnly = isStaticContext(at, exprPath);
+                    ImportTree it = findImport(tp);
+                    boolean isImport = it != null;
+
+                    List<? extends Element> members = membersOf(at, site, staticOnly && !isImport);
+                    Predicate<Element> filter = accessibility;
+                    Function<Boolean, String> paren = DEFAULT_PAREN;
+
+                    if (isNewClass(tp)) { // new xxx.|
+                        Predicate<Element> constructorFilter = accessibility.and(IS_CONSTRUCTOR)
+                            .and(el -> {
+                                if (el.getEnclosingElement().getEnclosingElement().getKind() == ElementKind.CLASS) {
+                                    return el.getEnclosingElement().getModifiers().contains(Modifier.STATIC);
+                                }
+                                return true;
+                            });
+                        addElements(membersOf(at, members), constructorFilter, smartFilter, result);
+
+                        filter = filter.and(IS_PACKAGE);
+                    } else if (isThrowsClause(tp)) {
+                        staticOnly = true;
+                        filter = filter.and(IS_PACKAGE.or(IS_CLASS).or(IS_INTERFACE));
+                        smartFilter = IS_PACKAGE.negate().and(smartTypeFilter);
+                    } else if (isImport) {
+                        paren = NO_PAREN;
+                        if (!it.isStatic()) {
+                            filter = filter.and(IS_PACKAGE.or(IS_CLASS).or(IS_INTERFACE));
+                        }
+                    } else {
+                        filter = filter.and(IS_CONSTRUCTOR.negate());
+                    }
+
+                    filter = filter.and(staticOnly ? STATIC_ONLY : INSTANCE_ONLY);
+
+                    addElements(members, filter, smartFilter, paren, result);
+                    break;
+                }
+                case IDENTIFIER:
+                    if (isNewClass(tp)) {
+                        Function<Element, Iterable<? extends Element>> listEnclosed =
+                                el -> el.getKind() == ElementKind.PACKAGE ? Collections.singletonList(el)
+                                                                          : el.getEnclosedElements();
+                        Predicate<Element> filter = accessibility.and(IS_CONSTRUCTOR.or(IS_PACKAGE));
+                        NewClassTree newClassTree = (NewClassTree)tp.getParentPath().getLeaf();
+                        ExpressionTree enclosingExpression = newClassTree.getEnclosingExpression();
+                        if (enclosingExpression != null) { // expr.new IDENT|
+                            TypeMirror site = at.trees().getTypeMirror(new TreePath(tp, enclosingExpression));
+                            filter = filter.and(el -> el.getEnclosingElement().getKind() == ElementKind.CLASS && !el.getEnclosingElement().getModifiers().contains(Modifier.STATIC));
+                            addElements(membersOf(at, membersOf(at, site, false)), filter, smartFilter, result);
+                        } else {
+                            addScopeElements(at, scope, listEnclosed, filter, smartFilter, result);
+                        }
+                        break;
+                    }
+                    if (isThrowsClause(tp)) {
+                        Predicate<Element> accept = accessibility.and(STATIC_ONLY)
+                                .and(IS_PACKAGE.or(IS_CLASS).or(IS_INTERFACE));
+                        addScopeElements(at, scope, IDENTITY, accept, IS_PACKAGE.negate().and(smartTypeFilter), result);
+                        break;
+                    }
+                    ImportTree it = findImport(tp);
+                    if (it != null) {
+                        addElements(membersOf(at, at.getElements().getPackageElement("").asType(), false), it.isStatic() ? STATIC_ONLY.and(accessibility) : accessibility, smartFilter, result);
+                    }
+                    break;
+                case ERRONEOUS:
+                case EMPTY_STATEMENT: {
+                    boolean staticOnly = ReplResolve.isStatic(((JavacScope)scope).getEnv());
+                    Predicate<Element> accept = accessibility.and(staticOnly ? STATIC_ONLY : TRUE);
+                    addScopeElements(at, scope, IDENTITY, accept, smartFilter, result);
+
+                    Tree parent = tp.getParentPath().getLeaf();
+                    switch (parent.getKind()) {
+                        case VARIABLE:
+                            accept = ((VariableTree)parent).getType() == tp.getLeaf() ?
+                                    IS_VOID.negate() :
+                                    TRUE;
+                            break;
+                        case PARAMETERIZED_TYPE: // TODO: JEP 218: Generics over Primitive Types
+                        case TYPE_PARAMETER:
+                        case CLASS:
+                        case INTERFACE:
+                        case ENUM:
+                            accept = FALSE;
+                            break;
+                        default:
+                            accept = TRUE;
+                            break;
+                    }
+                    addElements(primitivesOrVoid(at), accept, smartFilter, result);
+                    break;
+                }
+            }
+        }
+        anchor[0] = cursor;
+        return result;
+    }
+
+    private boolean isStaticContext(AnalyzeTask at, TreePath path) {
+        switch (path.getLeaf().getKind()) {
+            case ARRAY_TYPE:
+            case PRIMITIVE_TYPE:
+                return true;
+            default:
+                Element selectEl = at.trees().getElement(path);
+                return selectEl != null && (selectEl.getKind().isClass() || selectEl.getKind().isInterface() || selectEl.getKind() == ElementKind.TYPE_PARAMETER) && selectEl.asType().getKind() != TypeKind.ERROR;
+        }
+    }
+
+    private TreePath pathFor(CompilationUnitTree topLevel, SourcePositions sp, int pos) {
+        TreePath[] deepest = new TreePath[1];
+
+        new TreePathScanner<Void, Void>() {
+            @Override @DefinedBy(Api.COMPILER_TREE)
+            public Void scan(Tree tree, Void p) {
+                if (tree == null)
+                    return null;
+
+                long start = sp.getStartPosition(topLevel, tree);
+                long end = sp.getEndPosition(topLevel, tree);
+
+                if (start <= pos && pos <= end) {
+                    deepest[0] = new TreePath(getCurrentPath(), tree);
+                    return super.scan(tree, p);
+                }
+
+                return null;
+            }
+            @Override @DefinedBy(Api.COMPILER_TREE)
+            public Void visitErroneous(ErroneousTree node, Void p) {
+                return scan(node.getErrorTrees(), null);
+            }
+        }.scan(topLevel, null);
+
+        return deepest[0];
+    }
+
+    private boolean isNewClass(TreePath tp) {
+        return tp.getParentPath() != null &&
+               tp.getParentPath().getLeaf().getKind() == Kind.NEW_CLASS &&
+               ((NewClassTree) tp.getParentPath().getLeaf()).getIdentifier() == tp.getLeaf();
+    }
+
+    private boolean isThrowsClause(TreePath tp) {
+        Tree parent = tp.getParentPath().getLeaf();
+        return parent.getKind() == Kind.METHOD &&
+                ((MethodTree)parent).getThrows().contains(tp.getLeaf());
+    }
+
+    private ImportTree findImport(TreePath tp) {
+        while (tp != null && tp.getLeaf().getKind() != Kind.IMPORT) {
+            tp = tp.getParentPath();
+        }
+        return tp != null ? (ImportTree)tp.getLeaf() : null;
+    }
+
+    private Predicate<Element> createAccessibilityFilter(AnalyzeTask at, TreePath tp) {
+        Scope scope = at.trees().getScope(tp);
+        return el -> {
+            switch (el.getKind()) {
+                case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
+                    return at.trees().isAccessible(scope, (TypeElement) el);
+                case PACKAGE:
+                case EXCEPTION_PARAMETER: case PARAMETER: case LOCAL_VARIABLE: case RESOURCE_VARIABLE:
+                    return true;
+                default:
+                    TypeMirror type = el.getEnclosingElement().asType();
+                    if (type.getKind() == TypeKind.DECLARED)
+                        return at.trees().isAccessible(scope, el, (DeclaredType) type);
+                    else
+                        return true;
+            }
+        };
+    }
+
+    private final Predicate<Element> TRUE = el -> true;
+    private final Predicate<Element> FALSE = TRUE.negate();
+    private final Predicate<Element> IS_STATIC = el -> el.getModifiers().contains(Modifier.STATIC);
+    private final Predicate<Element> IS_CONSTRUCTOR = el -> el.getKind() == ElementKind.CONSTRUCTOR;
+    private final Predicate<Element> IS_METHOD = el -> el.getKind() == ElementKind.METHOD;
+    private final Predicate<Element> IS_PACKAGE = el -> el.getKind() == ElementKind.PACKAGE;
+    private final Predicate<Element> IS_CLASS = el -> el.getKind().isClass();
+    private final Predicate<Element> IS_INTERFACE = el -> el.getKind().isInterface();
+    private final Predicate<Element> IS_VOID = el -> el.asType().getKind() == TypeKind.VOID;
+    private final Predicate<Element> STATIC_ONLY = el -> {
+        ElementKind kind = el.getKind();
+        Element encl = el.getEnclosingElement();
+        ElementKind enclKind = encl != null ? encl.getKind() : ElementKind.OTHER;
+
+        return IS_STATIC.or(IS_PACKAGE).or(IS_CLASS).or(IS_INTERFACE).test(el) || IS_PACKAGE.test(encl) ||
+                (kind == ElementKind.TYPE_PARAMETER && !enclKind.isClass() && !enclKind.isInterface());
+    };
+    private final Predicate<Element> INSTANCE_ONLY = el -> {
+        Element encl = el.getEnclosingElement();
+
+        return IS_STATIC.or(IS_CLASS).or(IS_INTERFACE).negate().test(el) ||
+                IS_PACKAGE.test(encl);
+    };
+    private final Function<Element, Iterable<? extends Element>> IDENTITY = el -> Collections.singletonList(el);
+    private final Function<Boolean, String> DEFAULT_PAREN = hasParams -> hasParams ? "(" : "()";
+    private final Function<Boolean, String> NO_PAREN = hasParams -> "";
+
+    private void addElements(Iterable<? extends Element> elements, Predicate<Element> accept, Predicate<Element> smart, List<Suggestion> result) {
+        addElements(elements, accept, smart, DEFAULT_PAREN, result);
+    }
+    private void addElements(Iterable<? extends Element> elements, Predicate<Element> accept, Predicate<Element> smart, Function<Boolean, String> paren, List<Suggestion> result) {
+        Set<String> hasParams = Util.stream(elements)
+                .filter(accept)
+                .filter(IS_CONSTRUCTOR.or(IS_METHOD))
+                .filter(c -> !((ExecutableElement)c).getParameters().isEmpty())
+                .map(this::simpleName)
+                .collect(toSet());
+
+        for (Element c : elements) {
+            if (!accept.test(c))
+                continue;
+            String simpleName = simpleName(c);
+            if (c.getKind() == ElementKind.CONSTRUCTOR || c.getKind() == ElementKind.METHOD) {
+                simpleName += paren.apply(hasParams.contains(simpleName));
+            }
+            result.add(new Suggestion(simpleName, smart.test(c)));
+        }
+    }
+
+    private String simpleName(Element el) {
+        return el.getKind() == ElementKind.CONSTRUCTOR ? el.getEnclosingElement().getSimpleName().toString()
+                                                       : el.getSimpleName().toString();
+    }
+
+    private List<? extends Element> membersOf(AnalyzeTask at, TypeMirror site, boolean shouldGenerateDotClassItem) {
+        if (site  == null)
+            return Collections.emptyList();
+
+        switch (site.getKind()) {
+            case DECLARED: {
+                TypeElement element = (TypeElement) at.getTypes().asElement(site);
+                List<Element> result = new ArrayList<>();
+                result.addAll(at.getElements().getAllMembers(element));
+                if (shouldGenerateDotClassItem) {
+                    result.add(createDotClassSymbol(at, site));
+                }
+                result.removeIf(el -> el.getKind() == ElementKind.STATIC_INIT);
+                return result;
+            }
+            case ERROR: {
+                //try current qualified name as a package:
+                TypeElement typeElement = (TypeElement) at.getTypes().asElement(site);
+                Element enclosingElement = typeElement.getEnclosingElement();
+                String parentPackageName = enclosingElement instanceof QualifiedNameable ?
+                    ((QualifiedNameable)enclosingElement).getQualifiedName().toString() :
+                    "";
+                Set<PackageElement> packages = listPackages(at, parentPackageName);
+                return packages.stream()
+                               .filter(p -> p.getQualifiedName().equals(typeElement.getQualifiedName()))
+                               .findAny()
+                               .map(p -> membersOf(at, p.asType(), false))
+                               .orElse(Collections.emptyList());
+            }
+            case PACKAGE: {
+                String packageName = site.toString()/*XXX*/;
+                List<Element> result = new ArrayList<>();
+                result.addAll(getEnclosedElements(at.getElements().getPackageElement(packageName)));
+                result.addAll(listPackages(at, packageName));
+                return result;
+            }
+            case BOOLEAN: case BYTE: case SHORT: case CHAR:
+            case INT: case FLOAT: case LONG: case DOUBLE:
+            case VOID: {
+                return shouldGenerateDotClassItem ?
+                    Collections.singletonList(createDotClassSymbol(at, site)) :
+                    Collections.emptyList();
+            }
+            case ARRAY: {
+                List<Element> result = new ArrayList<>();
+                result.add(createArrayLengthSymbol(at, site));
+                if (shouldGenerateDotClassItem)
+                    result.add(createDotClassSymbol(at, site));
+                return result;
+            }
+            default:
+                return Collections.emptyList();
+        }
+    }
+
+    private List<? extends Element> membersOf(AnalyzeTask at, List<? extends Element> elements) {
+        return elements.stream()
+                .flatMap(e -> membersOf(at, e.asType(), true).stream())
+                .collect(toList());
+    }
+
+    private List<? extends Element> getEnclosedElements(PackageElement packageEl) {
+        if (packageEl == null) {
+            return Collections.emptyList();
+        }
+        //workaround for: JDK-8024687
+        while (true) {
+            try {
+                return packageEl.getEnclosedElements()
+                                .stream()
+                                .filter(el -> el.asType() != null)
+                                .filter(el -> el.asType().getKind() != TypeKind.ERROR)
+                                .collect(toList());
+            } catch (CompletionFailure cf) {
+                //ignore...
+            }
+        }
+    }
+
+    private List<? extends Element> primitivesOrVoid(AnalyzeTask at) {
+        Types types = at.getTypes();
+        return Stream.of(
+                TypeKind.BOOLEAN, TypeKind.BYTE, TypeKind.CHAR,
+                TypeKind.DOUBLE, TypeKind.FLOAT, TypeKind.INT,
+                TypeKind.LONG, TypeKind.SHORT, TypeKind.VOID)
+                .map(tk -> (Type)(tk == TypeKind.VOID ? types.getNoType(tk) : types.getPrimitiveType(tk)))
+                .map(Type::asElement)
+                .collect(toList());
+    }
+
+    private Set<PackageElement> listPackages(AnalyzeTask at, String enclosingPackage) {
+        Set<PackageElement> packs = new HashSet<>();
+        listPackages(at, StandardLocation.PLATFORM_CLASS_PATH, enclosingPackage, packs);
+        listPackages(at, StandardLocation.CLASS_PATH, enclosingPackage, packs);
+        listPackages(at, StandardLocation.SOURCE_PATH, enclosingPackage, packs);
+        return packs;
+    }
+
+    private void listPackages(AnalyzeTask at, Location loc, String currentPackage, Set<PackageElement> packs) {
+        try {
+            MemoryFileManager fm = proc.taskFactory.fileManager();
+            for (JavaFileObject file : fm.list(loc, currentPackage, fileKinds, true)) {
+                String binaryName = fm.inferBinaryName(loc, file);
+                if (!currentPackage.isEmpty() && !binaryName.startsWith(currentPackage + "."))
+                    continue;
+                int nextDot = binaryName.indexOf('.', !currentPackage.isEmpty() ? currentPackage.length() + 1 : 0);
+                if (nextDot == (-1))
+                    continue;
+                Elements elements = at.getElements();
+                PackageElement pack =
+                        elements.getPackageElement(binaryName.substring(0, nextDot));
+                if (pack == null) {
+                    //if no types in the package have ever been seen, the package will be unknown
+                    //try to load a type, and then try to recognize the package again:
+                    elements.getTypeElement(binaryName);
+                    pack = elements.getPackageElement(binaryName.substring(0, nextDot));
+                }
+                if (pack != null)
+                    packs.add(pack);
+            }
+        } catch (IOException ex) {
+            //TODO: should log?
+        }
+    }
+    //where:
+        private final Set<JavaFileObject.Kind> fileKinds = EnumSet.of(JavaFileObject.Kind.CLASS);
+
+    private Element createArrayLengthSymbol(AnalyzeTask at, TypeMirror site) {
+        Name length = Names.instance(at.getContext()).length;
+        Type intType = Symtab.instance(at.getContext()).intType;
+
+        return new VarSymbol(Flags.PUBLIC | Flags.FINAL, length, intType, ((Type) site).tsym);
+    }
+
+    private Element createDotClassSymbol(AnalyzeTask at, TypeMirror site) {
+        Name _class = Names.instance(at.getContext())._class;
+        Type classType = Symtab.instance(at.getContext()).classType;
+        Type erasedSite = (Type)at.getTypes().erasure(site);
+        classType = new ClassType(classType.getEnclosingType(), com.sun.tools.javac.util.List.of(erasedSite), classType.asElement());
+
+        return new VarSymbol(Flags.PUBLIC | Flags.STATIC | Flags.FINAL, _class, classType, erasedSite.tsym);
+    }
+
+    private Iterable<? extends Element> scopeContent(AnalyzeTask at, Scope scope, Function<Element, Iterable<? extends Element>> elementConvertor) {
+        Iterable<Scope> scopeIterable = () -> new Iterator<Scope>() {
+            private Scope currentScope = scope;
+            @Override
+            public boolean hasNext() {
+                return currentScope != null;
+            }
+            @Override
+            public Scope next() {
+                if (!hasNext())
+                    throw new NoSuchElementException();
+                try {
+                    return currentScope;
+                } finally {
+                    currentScope = currentScope.getEnclosingScope();
+                }
+            }
+        };
+        @SuppressWarnings("unchecked")
+        List<Element> result = Util.stream(scopeIterable)
+                             .flatMap(s -> Util.stream((Iterable<Element>)s.getLocalElements()))
+                             .flatMap(el -> Util.stream((Iterable<Element>)elementConvertor.apply(el)))
+                             .collect(toCollection(ArrayList :: new));
+        result.addAll(listPackages(at, ""));
+        return result;
+    }
+
+    @SuppressWarnings("fallthrough")
+    private Iterable<TypeMirror> findTargetType(AnalyzeTask at, TreePath forPath) {
+        if (forPath.getParentPath() == null)
+            return null;
+
+        Tree current = forPath.getLeaf();
+
+        switch (forPath.getParentPath().getLeaf().getKind()) {
+            case ASSIGNMENT: {
+                AssignmentTree tree = (AssignmentTree) forPath.getParentPath().getLeaf();
+                if (tree.getExpression() == current)
+                    return Collections.singletonList(at.trees().getTypeMirror(new TreePath(forPath.getParentPath(), tree.getVariable())));
+                break;
+            }
+            case VARIABLE: {
+                VariableTree tree = (VariableTree) forPath.getParentPath().getLeaf();
+                if (tree.getInitializer()== current)
+                    return Collections.singletonList(at.trees().getTypeMirror(forPath.getParentPath()));
+                break;
+            }
+            case ERRONEOUS:
+                return findTargetType(at, forPath.getParentPath());
+            case NEW_CLASS: {
+                NewClassTree nct = (NewClassTree) forPath.getParentPath().getLeaf();
+                List<TypeMirror> actuals = computeActualInvocationTypes(at, nct.getArguments(), forPath);
+
+                if (actuals != null) {
+                    Iterable<Pair<ExecutableElement, ExecutableType>> candidateConstructors = newClassCandidates(at, forPath.getParentPath());
+
+                    return computeSmartTypesForExecutableType(at, candidateConstructors, actuals);
+                } else {
+                    return findTargetType(at, forPath.getParentPath());
+                }
+            }
+            case METHOD:
+                if (!isThrowsClause(forPath)) {
+                    break;
+                }
+                // fall through
+            case THROW:
+                return Collections.singletonList(at.getElements().getTypeElement("java.lang.Throwable").asType());
+            case METHOD_INVOCATION: {
+                MethodInvocationTree mit = (MethodInvocationTree) forPath.getParentPath().getLeaf();
+                List<TypeMirror> actuals = computeActualInvocationTypes(at, mit.getArguments(), forPath);
+
+                if (actuals == null)
+                    return null;
+
+                Iterable<Pair<ExecutableElement, ExecutableType>> candidateMethods = methodCandidates(at, forPath.getParentPath());
+
+                return computeSmartTypesForExecutableType(at, candidateMethods, actuals);
+            }
+        }
+
+        return null;
+    }
+
+    private List<TypeMirror> computeActualInvocationTypes(AnalyzeTask at, List<? extends ExpressionTree> arguments, TreePath currentArgument) {
+        if (currentArgument == null)
+            return null;
+
+        int paramIndex = arguments.indexOf(currentArgument.getLeaf());
+
+        if (paramIndex == (-1))
+            return null;
+
+        List<TypeMirror> actuals = new ArrayList<>();
+
+        for (ExpressionTree arg : arguments.subList(0, paramIndex)) {
+            actuals.add(at.trees().getTypeMirror(new TreePath(currentArgument.getParentPath(), arg)));
+        }
+
+        return actuals;
+    }
+
+    private List<Pair<ExecutableElement, ExecutableType>> filterExecutableTypesByArguments(AnalyzeTask at, Iterable<Pair<ExecutableElement, ExecutableType>> candidateMethods, List<TypeMirror> precedingActualTypes) {
+        List<Pair<ExecutableElement, ExecutableType>> candidate = new ArrayList<>();
+        int paramIndex = precedingActualTypes.size();
+
+        OUTER:
+        for (Pair<ExecutableElement, ExecutableType> method : candidateMethods) {
+            boolean varargInvocation = paramIndex >= method.snd.getParameterTypes().size();
+
+            for (int i = 0; i < paramIndex; i++) {
+                TypeMirror actual = precedingActualTypes.get(i);
+
+                if (this.parameterType(method.fst, method.snd, i, !varargInvocation)
+                        .noneMatch(formal -> at.getTypes().isAssignable(actual, formal))) {
+                    continue OUTER;
+                }
+            }
+            candidate.add(method);
+        }
+
+        return candidate;
+    }
+
+    private Stream<TypeMirror> parameterType(ExecutableElement method, ExecutableType methodType, int paramIndex, boolean allowVarArgsArray) {
+        int paramCount = methodType.getParameterTypes().size();
+        if (paramIndex >= paramCount && !method.isVarArgs())
+            return Stream.empty();
+        if (paramIndex < paramCount - 1 || !method.isVarArgs())
+            return Stream.of(methodType.getParameterTypes().get(paramIndex));
+        TypeMirror varargType = methodType.getParameterTypes().get(paramCount - 1);
+        TypeMirror elemenType = ((ArrayType) varargType).getComponentType();
+        if (paramIndex >= paramCount || !allowVarArgsArray)
+            return Stream.of(elemenType);
+        return Stream.of(varargType, elemenType);
+    }
+
+    private List<TypeMirror> computeSmartTypesForExecutableType(AnalyzeTask at, Iterable<Pair<ExecutableElement, ExecutableType>> candidateMethods, List<TypeMirror> precedingActualTypes) {
+        List<TypeMirror> candidate = new ArrayList<>();
+        int paramIndex = precedingActualTypes.size();
+
+        this.filterExecutableTypesByArguments(at, candidateMethods, precedingActualTypes)
+            .stream()
+            .flatMap(method -> parameterType(method.fst, method.snd, paramIndex, true))
+            .forEach(candidate::add);
+
+        return candidate;
+    }
+
+
+    private TypeMirror resultTypeOf(Element el) {
+        //TODO: should reflect the type of site!
+        switch (el.getKind()) {
+            case METHOD:
+                return ((ExecutableElement) el).getReturnType();
+            case CONSTRUCTOR:
+            case INSTANCE_INIT: case STATIC_INIT: //TODO: should be filtered out
+                return el.getEnclosingElement().asType();
+            default:
+                return el.asType();
+        }
+    }
+
+    private void addScopeElements(AnalyzeTask at, Scope scope, Function<Element, Iterable<? extends Element>> elementConvertor, Predicate<Element> filter, Predicate<Element> smartFilter, List<Suggestion> result) {
+        addElements(scopeContent(at, scope, elementConvertor), filter, smartFilter, result);
+    }
+
+    private Iterable<Pair<ExecutableElement, ExecutableType>> methodCandidates(AnalyzeTask at, TreePath invocation) {
+        MethodInvocationTree mit = (MethodInvocationTree) invocation.getLeaf();
+        ExpressionTree select = mit.getMethodSelect();
+        List<Pair<ExecutableElement, ExecutableType>> result = new ArrayList<>();
+        Predicate<Element> accessibility = createAccessibilityFilter(at, invocation);
+
+        switch (select.getKind()) {
+            case MEMBER_SELECT:
+                MemberSelectTree mst = (MemberSelectTree) select;
+                TreePath tp = new TreePath(new TreePath(invocation, select), mst.getExpression());
+                TypeMirror site = at.trees().getTypeMirror(tp);
+
+                if (site == null || site.getKind() != TypeKind.DECLARED)
+                    break;
+
+                Element siteEl = at.getTypes().asElement(site);
+
+                if (siteEl == null)
+                    break;
+
+                if (isStaticContext(at, tp)) {
+                    accessibility = accessibility.and(STATIC_ONLY);
+                }
+
+                for (ExecutableElement ee : ElementFilter.methodsIn(membersOf(at, siteEl.asType(), false))) {
+                    if (ee.getSimpleName().contentEquals(mst.getIdentifier())) {
+                        if (accessibility.test(ee)) {
+                            result.add(Pair.of(ee, (ExecutableType) at.getTypes().asMemberOf((DeclaredType) site, ee)));
+                        }
+                    }
+                }
+                break;
+            case IDENTIFIER:
+                IdentifierTree it = (IdentifierTree) select;
+                for (ExecutableElement ee : ElementFilter.methodsIn(scopeContent(at, at.trees().getScope(invocation), IDENTITY))) {
+                    if (ee.getSimpleName().contentEquals(it.getName())) {
+                        if (accessibility.test(ee)) {
+                            result.add(Pair.of(ee, (ExecutableType) ee.asType())); //XXX: proper site
+                        }
+                    }
+                }
+                break;
+            default:
+                break;
+        }
+
+        return result;
+    }
+
+    private Iterable<Pair<ExecutableElement, ExecutableType>> newClassCandidates(AnalyzeTask at, TreePath newClassPath) {
+        NewClassTree nct = (NewClassTree) newClassPath.getLeaf();
+        Element type = at.trees().getElement(new TreePath(newClassPath.getParentPath(), nct.getIdentifier()));
+        TypeMirror targetType = at.trees().getTypeMirror(newClassPath);
+        if (targetType == null || targetType.getKind() != TypeKind.DECLARED) {
+            Iterable<TypeMirror> targetTypes = findTargetType(at, newClassPath);
+            if (targetTypes == null)
+                targetTypes = Collections.emptyList();
+            targetType =
+                    StreamSupport.stream(targetTypes.spliterator(), false)
+                                 .filter(t -> at.getTypes().asElement(t) == type)
+                                 .findAny()
+                                 .orElse(at.getTypes().erasure(type.asType()));
+        }
+        List<Pair<ExecutableElement, ExecutableType>> candidateConstructors = new ArrayList<>();
+        Predicate<Element> accessibility = createAccessibilityFilter(at, newClassPath);
+
+        if (targetType != null &&
+            targetType.getKind() == TypeKind.DECLARED &&
+            type != null &&
+            (type.getKind().isClass() || type.getKind().isInterface())) {
+            for (ExecutableElement constr : ElementFilter.constructorsIn(type.getEnclosedElements())) {
+                if (accessibility.test(constr)) {
+                    ExecutableType constrType =
+                            (ExecutableType) at.getTypes().asMemberOf((DeclaredType) targetType, constr);
+                    candidateConstructors.add(Pair.of(constr, constrType));
+                }
+            }
+        }
+
+        return candidateConstructors;
+    }
+
+    @Override
+    public String documentation(String code, int cursor) {
+        code = code.substring(0, cursor);
+        if (code.trim().isEmpty()) { //TODO: comment handling
+            code += ";";
+        }
+
+        if (guessKind(code) == Kind.IMPORT)
+            return null;
+
+        OuterWrap codeWrap = wrapInClass(Wrap.methodWrap(code));
+        AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap);
+        SourcePositions sp = at.trees().getSourcePositions();
+        CompilationUnitTree topLevel = at.cuTree();
+        TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
+
+        if (tp == null)
+            return null;
+
+        TreePath prevPath = null;
+        while (tp != null && tp.getLeaf().getKind() != Kind.METHOD_INVOCATION && tp.getLeaf().getKind() != Kind.NEW_CLASS) {
+            prevPath = tp;
+            tp = tp.getParentPath();
+        }
+
+        if (tp == null)
+            return null;
+
+        Iterable<Pair<ExecutableElement, ExecutableType>> candidates;
+        List<? extends ExpressionTree> arguments;
+
+        if (tp.getLeaf().getKind() == Kind.METHOD_INVOCATION) {
+            MethodInvocationTree mit = (MethodInvocationTree) tp.getLeaf();
+            candidates = methodCandidates(at, tp);
+            arguments = mit.getArguments();
+        } else {
+            NewClassTree nct = (NewClassTree) tp.getLeaf();
+            candidates = newClassCandidates(at, tp);
+            arguments = nct.getArguments();
+        }
+
+        if (!isEmptyArgumentsContext(arguments)) {
+            List<TypeMirror> actuals = computeActualInvocationTypes(at, arguments, prevPath);
+            List<TypeMirror> fullActuals = actuals != null ? actuals : Collections.emptyList();
+
+            candidates =
+                    this.filterExecutableTypesByArguments(at, candidates, fullActuals)
+                        .stream()
+                        .filter(method -> parameterType(method.fst, method.snd, fullActuals.size(), true).findAny().isPresent())
+                        .collect(Collectors.toList());
+        }
+
+        return Util.stream(candidates)
+                .map(method -> Util.expunge(element2String(method.fst)))
+                .collect(joining("\n"));
+    }
+
+    private boolean isEmptyArgumentsContext(List<? extends ExpressionTree> arguments) {
+        if (arguments.size() == 1) {
+            Tree firstArgument = arguments.get(0);
+            return firstArgument.getKind() == Kind.ERRONEOUS;
+        }
+        return false;
+    }
+
+    private String element2String(Element el) {
+        switch (el.getKind()) {
+            case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
+                return ((TypeElement) el).getQualifiedName().toString();
+            case FIELD:
+                return element2String(el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
+            case ENUM_CONSTANT:
+                return element2String(el.getEnclosingElement()) + "." + el.getSimpleName();
+            case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE:
+                return el.getSimpleName() + ":" + el.asType();
+            case CONSTRUCTOR: case METHOD:
+                StringBuilder header = new StringBuilder();
+                header.append(element2String(el.getEnclosingElement()));
+                if (el.getKind() == ElementKind.METHOD) {
+                    header.append(".");
+                    header.append(el.getSimpleName());
+                }
+                header.append("(");
+                String sep = "";
+                ExecutableElement method = (ExecutableElement) el;
+                for (Iterator<? extends VariableElement> i = method.getParameters().iterator(); i.hasNext();) {
+                    VariableElement p = i.next();
+                    header.append(sep);
+                    if (!i.hasNext() && method.isVarArgs()) {
+                        header.append(unwrapArrayType(p.asType()));
+                        header.append("...");
+
+                    } else {
+                        header.append(p.asType());
+                    }
+                    header.append(" ");
+                    header.append(p.getSimpleName());
+                    sep = ", ";
+                }
+                header.append(")");
+                return header.toString();
+           default:
+                return el.toString();
+        }
+    }
+    private TypeMirror unwrapArrayType(TypeMirror arrayType) {
+        if (arrayType.getKind() == TypeKind.ARRAY) {
+            return ((ArrayType)arrayType).getComponentType();
+        }
+        return arrayType;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/StatementSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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.StatementKey;
+
+/**
+ * Snippet for a statement.
+ * The Kind is {@link jdk.jshell.Snippet.Kind#STATEMENT}.
+ * <p>
+ * <code>StatementSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ * @jls 14.5: Statement.
+ */
+public class StatementSnippet extends Snippet {
+
+    StatementSnippet(StatementKey key, String userSource, Wrap guts) {
+        super(key, userSource, guts, null, SubKind.STATEMENT_SUBKIND);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,465 @@
+/*
+ * 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 com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.util.Context;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.ToolProvider;
+import static jdk.jshell.Util.*;
+import com.sun.source.tree.ImportTree;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.util.JavacMessages;
+import jdk.jshell.MemoryFileManager.OutputMemoryJavaFileObject;
+import java.util.Collections;
+import java.util.Locale;
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.lang.model.util.Elements;
+import javax.tools.FileObject;
+import jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject;
+import jdk.jshell.ClassTracker.ClassInfo;
+
+/**
+ * The primary interface to the compiler API.  Parsing, analysis, and
+ * compilation to class files (in memory).
+ * @author Robert Field
+ */
+class TaskFactory {
+
+    private final JavaCompiler compiler;
+    private final MemoryFileManager fileManager;
+    private final JShell state;
+    private String classpath = System.getProperty("java.class.path");
+
+    TaskFactory(JShell state) {
+        this.state = state;
+        this.compiler = ToolProvider.getSystemJavaCompiler();
+        if (compiler == null) {
+            throw new UnsupportedOperationException("Compiler not available, must be run with full JDK 9.");
+        }
+        if (!System.getProperty("java.specification.version").equals("1.9"))  {
+            throw new UnsupportedOperationException("Wrong compiler, must be run with full JDK 9.");
+        }
+        this.fileManager = new MemoryFileManager(
+                compiler.getStandardFileManager(null, null, null), state);
+    }
+
+    void addToClasspath(String path) {
+        classpath = classpath + File.pathSeparator + path;
+        List<String> args = new ArrayList<>();
+        args.add(classpath);
+        fileManager().handleOption("-classpath", args.iterator());
+    }
+
+    MemoryFileManager fileManager() {
+        return fileManager;
+    }
+
+    private interface SourceHandler<T> {
+
+        JavaFileObject sourceToFileObject(MemoryFileManager fm, T t);
+
+        Diag diag(Diagnostic<? extends JavaFileObject> d);
+    }
+
+    private class StringSourceHandler implements SourceHandler<String> {
+
+        @Override
+        public JavaFileObject sourceToFileObject(MemoryFileManager fm, String src) {
+            return fm.createSourceFileObject(src, "$NeverUsedName$", src);
+        }
+
+        @Override
+        public Diag diag(final Diagnostic<? extends JavaFileObject> d) {
+            return new Diag() {
+
+                @Override
+                public boolean isError() {
+                    return d.getKind() == Diagnostic.Kind.ERROR;
+                }
+
+                @Override
+                public long getPosition() {
+                    return d.getPosition();
+                }
+
+                @Override
+                public long getStartPosition() {
+                    return d.getStartPosition();
+                }
+
+                @Override
+                public long getEndPosition() {
+                    return d.getEndPosition();
+                }
+
+                @Override
+                public String getCode() {
+                    return d.getCode();
+                }
+
+                @Override
+                public String getMessage(Locale locale) {
+                    return expunge(d.getMessage(locale));
+                }
+
+                @Override
+                Unit unitOrNull() {
+                    return null;
+                }
+            };
+        }
+    }
+
+    private class WrapSourceHandler implements SourceHandler<OuterWrap> {
+
+        final OuterWrap wrap;
+
+        WrapSourceHandler(OuterWrap wrap) {
+            this.wrap = wrap;
+        }
+
+        @Override
+        public JavaFileObject sourceToFileObject(MemoryFileManager fm, OuterWrap w) {
+            return fm.createSourceFileObject(w, w.classFullName(), w.wrapped());
+        }
+
+        @Override
+        public Diag diag(Diagnostic<? extends JavaFileObject> d) {
+            return wrap.wrapDiag(d);
+        }
+    }
+
+    private class UnitSourceHandler implements SourceHandler<Unit> {
+
+        @Override
+        public JavaFileObject sourceToFileObject(MemoryFileManager fm, Unit u) {
+            return fm.createSourceFileObject(u,
+                    state.maps.classFullName(u.snippet()),
+                    u.snippet().outerWrap().wrapped());
+        }
+
+        @Override
+        public Diag diag(Diagnostic<? extends JavaFileObject> d) {
+            SourceMemoryJavaFileObject smjfo = (SourceMemoryJavaFileObject) d.getSource();
+            Unit u = (Unit) smjfo.getOrigin();
+            return u.snippet().outerWrap().wrapDiag(d);
+        }
+    }
+
+    /**
+     * Parse a snippet of code (as a String) using the parser subclass.  Return
+     * the parse tree (and errors).
+     */
+    class ParseTask extends BaseTask {
+
+        private final CompilationUnitTree cut;
+        private final List<? extends Tree> units;
+
+        ParseTask(final String source) {
+            super(Stream.of(source),
+                    new StringSourceHandler(),
+                    "-XDallowStringFolding=false", "-proc:none");
+            ReplParserFactory.instance(getContext());
+            Iterable<? extends CompilationUnitTree> asts = parse();
+            Iterator<? extends CompilationUnitTree> it = asts.iterator();
+            if (it.hasNext()) {
+                this.cut = it.next();
+                List<? extends ImportTree> imps = cut.getImports();
+                this.units = !imps.isEmpty() ? imps : cut.getTypeDecls();
+            } else {
+                this.cut = null;
+                this.units = Collections.emptyList();
+            }
+        }
+
+        private Iterable<? extends CompilationUnitTree> parse() {
+            try {
+                return task.parse();
+            } catch (Exception ex) {
+                throw new InternalError("Exception during parse - " + ex.getMessage(), ex);
+            }
+        }
+
+        List<? extends Tree> units() {
+            return units;
+        }
+
+        @Override
+        CompilationUnitTree cuTree() {
+            return cut;
+        }
+    }
+
+    /**
+     * Run the normal "analyze()" pass of the compiler over the wrapped snippet.
+     */
+    class AnalyzeTask extends BaseTask {
+
+        private final CompilationUnitTree cut;
+
+        AnalyzeTask(final OuterWrap wrap) {
+            this(Stream.of(wrap),
+                    new WrapSourceHandler(wrap),
+                    "-XDshouldStopPolicy=FLOW", "-proc:none");
+        }
+
+        AnalyzeTask(final Collection<Unit> units) {
+            this(units.stream(), new UnitSourceHandler(),
+                    "-XDshouldStopPolicy=FLOW", "-Xlint:unchecked", "-proc:none");
+        }
+
+        <T>AnalyzeTask(final Stream<T> stream, SourceHandler<T> sourceHandler,
+                String... extraOptions) {
+            super(stream, sourceHandler, extraOptions);
+            Iterator<? extends CompilationUnitTree> cuts = analyze().iterator();
+            if (cuts.hasNext()) {
+                this.cut = cuts.next();
+                //proc.debug("AnalyzeTask element=%s  cutp=%s  cut=%s\n", e, cutp, cut);
+            } else {
+                this.cut = null;
+                //proc.debug("AnalyzeTask -- no elements -- %s\n", getDiagnostics());
+            }
+        }
+
+        private Iterable<? extends CompilationUnitTree> analyze() {
+            try {
+                Iterable<? extends CompilationUnitTree> cuts = task.parse();
+                task.analyze();
+                return cuts;
+            } catch (Exception ex) {
+                throw new InternalError("Exception during analyze - " + ex.getMessage(), ex);
+            }
+        }
+
+        @Override
+        CompilationUnitTree cuTree() {
+            return cut;
+        }
+
+        Elements getElements() {
+            return task.getElements();
+        }
+
+        javax.lang.model.util.Types getTypes() {
+            return task.getTypes();
+        }
+    }
+
+    /**
+     * Unit the wrapped snippet to class files.
+     */
+    class CompileTask extends BaseTask {
+
+        private final Map<Unit, List<OutputMemoryJavaFileObject>> classObjs = new HashMap<>();
+
+        CompileTask(Collection<Unit> units) {
+            super(units.stream(), new UnitSourceHandler(),
+                    "-Xlint:unchecked", "-proc:none");
+        }
+
+        boolean compile() {
+            fileManager.registerClassFileCreationListener(this::listenForNewClassFile);
+            boolean result = task.call();
+            fileManager.registerClassFileCreationListener(null);
+            return result;
+        }
+
+
+        List<ClassInfo> classInfoList(Unit u) {
+            List<OutputMemoryJavaFileObject> l = classObjs.get(u);
+            if (l == null) return Collections.emptyList();
+            return l.stream()
+                    .map(fo -> state.classTracker.classInfo(fo.getName(), fo.getBytes()))
+                    .collect(Collectors.toList());
+        }
+
+        private void listenForNewClassFile(OutputMemoryJavaFileObject jfo, JavaFileManager.Location location,
+                String className, JavaFileObject.Kind kind, FileObject sibling) {
+            //debug("listenForNewClassFile %s loc=%s kind=%s\n", className, location, kind);
+            if (location == CLASS_OUTPUT) {
+                state.debug(DBG_GEN, "Compiler generating class %s\n", className);
+                Unit u = ((sibling instanceof SourceMemoryJavaFileObject)
+                        && (((SourceMemoryJavaFileObject) sibling).getOrigin() instanceof Unit))
+                        ? (Unit) ((SourceMemoryJavaFileObject) sibling).getOrigin()
+                        : null;
+                classObjs.compute(u, (k, v) -> (v == null)? new ArrayList<>() : v)
+                        .add(jfo);
+            }
+        }
+
+        @Override
+        CompilationUnitTree cuTree() {
+            throw new UnsupportedOperationException("Not supported.");
+        }
+    }
+
+    abstract class BaseTask {
+
+        final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
+        final JavacTaskImpl task;
+        private DiagList diags = null;
+        private final SourceHandler<?> sourceHandler;
+        private final Context context = new Context();
+        private Types types;
+        private JavacMessages messages;
+        private Trees trees;
+
+        private <T>BaseTask(Stream<T> inputs,
+                //BiFunction<MemoryFileManager, T, JavaFileObject> sfoCreator,
+                SourceHandler<T> sh,
+                String... extraOptions) {
+            this.sourceHandler = sh;
+            List<String> options = Arrays.asList(extraOptions);
+            Iterable<? extends JavaFileObject> compilationUnits = inputs
+                            .map(in -> sh.sourceToFileObject(fileManager, in))
+                            .collect(Collectors.toList());
+            this.task = (JavacTaskImpl) ((JavacTool) compiler).getTask(null,
+                    fileManager, diagnostics, options, null,
+                    compilationUnits, context);
+        }
+
+        abstract CompilationUnitTree cuTree();
+
+        Diag diag(Diagnostic<? extends JavaFileObject> diag) {
+            return sourceHandler.diag(diag);
+        }
+
+        Context getContext() {
+            return context;
+        }
+
+        Types types() {
+            if (types == null) {
+                types = Types.instance(context);
+            }
+            return types;
+        }
+
+        JavacMessages messages() {
+            if (messages == null) {
+                messages = JavacMessages.instance(context);
+            }
+            return messages;
+        }
+
+        Trees trees() {
+            if (trees == null) {
+                trees = Trees.instance(task);
+            }
+            return trees;
+        }
+
+        // ------------------ diags functionality
+
+        DiagList getDiagnostics() {
+            if (diags == null) {
+                LinkedHashMap<String, Diag> diagMap = new LinkedHashMap<>();
+                for (Diagnostic<? extends JavaFileObject> in : diagnostics.getDiagnostics()) {
+                    Diag d = diag(in);
+                    String uniqueKey = d.getCode() + ":" + d.getPosition() + ":" + d.getMessage(null);
+                    diagMap.put(uniqueKey, d);
+                }
+                diags = new DiagList(diagMap.values());
+            }
+            return diags;
+        }
+
+        boolean hasErrors() {
+            return getDiagnostics().hasErrors();
+        }
+
+        String shortErrorMessage() {
+            StringBuilder sb = new StringBuilder();
+            for (Diag diag : getDiagnostics()) {
+                for (String line : diag.getMessage(null).split("\\r?\\n")) {
+                    if (!line.trim().startsWith("location:")) {
+                        sb.append(line);
+                    }
+                }
+            }
+            return sb.toString();
+        }
+
+        void debugPrintDiagnostics(String src) {
+            for (Diag diag : getDiagnostics()) {
+                state.debug(DBG_GEN, "ERROR --\n");
+                for (String line : diag.getMessage(null).split("\\r?\\n")) {
+                    if (!line.trim().startsWith("location:")) {
+                        state.debug(DBG_GEN, "%s\n", line);
+                    }
+                }
+                int start = (int) diag.getStartPosition();
+                int end = (int) diag.getEndPosition();
+                if (src != null) {
+                    String[] srcLines = src.split("\\r?\\n");
+                    for (String line : srcLines) {
+                        state.debug(DBG_GEN, "%s\n", line);
+                    }
+
+                    StringBuilder sb = new StringBuilder();
+                    for (int i = 0; i < start; ++i) {
+                        sb.append(' ');
+                    }
+                    sb.append('^');
+                    if (end > start) {
+                        for (int i = start + 1; i < end; ++i) {
+                            sb.append('-');
+                        }
+                        sb.append('^');
+                    }
+                    state.debug(DBG_GEN, "%s\n", sb.toString());
+                }
+                state.debug(DBG_GEN, "printDiagnostics start-pos = %d ==> %d -- wrap = %s\n",
+                        diag.getStartPosition(), start, this);
+                state.debug(DBG_GEN, "Code: %s\n", diag.getCode());
+                state.debug(DBG_GEN, "Pos: %d (%d - %d) -- %s\n", diag.getPosition(),
+                        diag.getStartPosition(), diag.getEndPosition(), diag.getMessage(null));
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/TreeDependencyScanner.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,128 @@
+/*
+ * 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.source.tree.ClassTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.ImportTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.PackageTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.util.DefinedBy;
+import com.sun.tools.javac.util.DefinedBy.Api;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import javax.lang.model.element.Name;
+
+/**
+ * Search a compiler API parse tree for dependencies.
+ */
+class TreeDependencyScanner extends TreeScanner<Void, Set<String>> {
+
+    private final Set<String> decl = new HashSet<>();
+    private final Set<String> body = new HashSet<>();
+
+    public void scan(Tree node) {
+        scan(node, decl);
+    }
+
+    public Collection<String> declareReferences() {
+        return decl;
+    }
+
+    public Collection<String> bodyReferences() {
+        return body;
+    }
+
+    private void add(Set<String> p, Name name) {
+        p.add(name.toString());
+    }
+
+    // -- Differentiate declaration references from body references ---
+
+    @Override @DefinedBy(Api.COMPILER_TREE)
+    public Void visitClass(ClassTree node, Set<String> p) {
+        scan(node.getModifiers(), p);
+        scan(node.getTypeParameters(), p);
+        scan(node.getExtendsClause(), p);
+        scan(node.getImplementsClause(), p);
+        scan(node.getMembers(), body);
+        return null;
+    }
+
+    @Override @DefinedBy(Api.COMPILER_TREE)
+    public Void visitMethod(MethodTree node, Set<String> p) {
+        scan(node.getModifiers(), p);
+        scan(node.getReturnType(), p);
+        scan(node.getTypeParameters(), p);
+        scan(node.getParameters(), p);
+        scan(node.getReceiverParameter(), p);
+        scan(node.getThrows(), p);
+        scan(node.getBody(), body);
+        scan(node.getDefaultValue(), body);
+        return null;
+    }
+
+    @Override @DefinedBy(Api.COMPILER_TREE)
+    public Void visitVariable(VariableTree node, Set<String> p) {
+        scan(node.getModifiers(), p);
+        scan(node.getType(), p);
+        scan(node.getNameExpression(), p);
+        scan(node.getInitializer(), body);
+        return null;
+    }
+
+    // --- Ignore these ---
+
+    @Override @DefinedBy(Api.COMPILER_TREE)
+    public Void visitPackage(PackageTree node, Set<String> p) {
+        return null;
+    }
+
+    @Override @DefinedBy(Api.COMPILER_TREE)
+    public Void visitImport(ImportTree node, Set<String> p) {
+        return null;
+    }
+
+
+    // -- Actual Symbol names ---
+
+    @Override @DefinedBy(Api.COMPILER_TREE)
+    public Void visitMemberSelect(MemberSelectTree node, Set<String> p) {
+        add(p, node.getIdentifier());
+        return super.visitMemberSelect(node, p);
+    }
+
+    @Override @DefinedBy(Api.COMPILER_TREE)
+    public Void visitIdentifier(IdentifierTree node, Set<String> p) {
+        add(p, node.getName());
+        return super.visitIdentifier(node, p);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,283 @@
+/*
+ * 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 com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ReturnTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.MethodType;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.util.JavacMessages;
+import com.sun.tools.javac.util.Name;
+import static jdk.jshell.Util.isDoIt;
+import jdk.jshell.Wrap.Range;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.BinaryOperator;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Utilities for analyzing compiler API parse trees.
+ * @author Robert Field
+ */
+
+class TreeDissector {
+
+    private static final String OBJECT_TYPE = "Object";
+
+    static class ExpressionInfo {
+
+        boolean isNonVoid;
+        String typeName;
+        ExpressionTree tree;
+        String signature;
+    }
+
+    private final TaskFactory.BaseTask bt;
+    private ClassTree firstClass;
+    private SourcePositions theSourcePositions = null;
+
+    TreeDissector(TaskFactory.BaseTask bt) {
+        this.bt = bt;
+    }
+
+
+    ClassTree firstClass() {
+        if (firstClass == null) {
+            firstClass = computeFirstClass();
+        }
+        return firstClass;
+    }
+
+    CompilationUnitTree cuTree() {
+        return bt.cuTree();
+    }
+
+    Types types() {
+        return bt.types();
+    }
+
+    Trees trees() {
+        return bt.trees();
+    }
+
+    SourcePositions getSourcePositions() {
+        if (theSourcePositions == null) {
+            theSourcePositions = trees().getSourcePositions();
+        }
+        return theSourcePositions;
+    }
+
+    int getStartPosition(Tree tree) {
+        return (int) getSourcePositions().getStartPosition(cuTree(), tree);
+    }
+
+    int getEndPosition(Tree tree) {
+        return (int) getSourcePositions().getEndPosition(cuTree(), tree);
+    }
+
+    Range treeToRange(Tree tree) {
+        return new Range(getStartPosition(tree), getEndPosition(tree));
+    }
+
+    Range treeListToRange(List<? extends Tree> treeList) {
+        int start = Integer.MAX_VALUE;
+        int end = -1;
+        for (Tree t : treeList) {
+            int tstart = getStartPosition(t);
+            int tend = getEndPosition(t);
+            if (tstart < start) {
+                start = tstart;
+            }
+            if (tend > end) {
+                end = tend;
+            }
+        }
+        if (start == Integer.MAX_VALUE) {
+            return null;
+        }
+        return new Range(start, end);
+    }
+
+    Tree firstClassMember() {
+        if (firstClass() != null) {
+            //TODO: missing classes
+            for (Tree mem : firstClass().getMembers()) {
+                if (mem.getKind() == Tree.Kind.VARIABLE) {
+                    return mem;
+                }
+                if (mem.getKind() == Tree.Kind.METHOD) {
+                    MethodTree mt = (MethodTree) mem;
+                    if (!isDoIt(mt.getName()) && !mt.getName().toString().equals("<init>")) {
+                        return mt;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    StatementTree firstStatement() {
+        if (firstClass() != null) {
+            for (Tree mem : firstClass().getMembers()) {
+                if (mem.getKind() == Tree.Kind.METHOD) {
+                    MethodTree mt = (MethodTree) mem;
+                    if (isDoIt(mt.getName())) {
+                        List<? extends StatementTree> stmts = mt.getBody().getStatements();
+                        if (!stmts.isEmpty()) {
+                            return stmts.get(0);
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    VariableTree firstVariable() {
+        if (firstClass() != null) {
+            for (Tree mem : firstClass().getMembers()) {
+                if (mem.getKind() == Tree.Kind.VARIABLE) {
+                    VariableTree vt = (VariableTree) mem;
+                    return vt;
+                }
+            }
+        }
+        return null;
+    }
+
+    private ClassTree computeFirstClass() {
+        if (cuTree() == null) {
+            return null;
+        }
+        for (Tree decl : cuTree().getTypeDecls()) {
+            if (decl.getKind() == Tree.Kind.CLASS || decl.getKind() == Tree.Kind.INTERFACE) {
+                return (ClassTree) decl;
+            }
+        }
+        return null;
+    }
+
+    ExpressionInfo typeOfReturnStatement(JavacMessages messages, BinaryOperator<String> fullClassNameAndPackageToClass) {
+        ExpressionInfo ei = new ExpressionInfo();
+        Tree unitTree = firstStatement();
+        if (unitTree instanceof ReturnTree) {
+            ei.tree = ((ReturnTree) unitTree).getExpression();
+            if (ei.tree != null) {
+                TreePath viPath = trees().getPath(cuTree(), ei.tree);
+                if (viPath != null) {
+                    TypeMirror tm = trees().getTypeMirror(viPath);
+                    if (tm != null) {
+                        Type type = (Type)tm;
+                        TypePrinter tp = new TypePrinter(messages, fullClassNameAndPackageToClass, type);
+                        ei.typeName = tp.visit(type, Locale.getDefault());
+                        switch (tm.getKind()) {
+                            case VOID:
+                            case NONE:
+                            case ERROR:
+                            case OTHER:
+                                break;
+                            case NULL:
+                                ei.isNonVoid = true;
+                                ei.typeName = OBJECT_TYPE;
+                                break;
+                            default: {
+                                ei.isNonVoid = true;
+                                break;
+
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return ei;
+    }
+
+    String typeOfMethod() {
+        Tree unitTree = firstClassMember();
+        if (unitTree instanceof JCMethodDecl) {
+            JCMethodDecl mtree = (JCMethodDecl) unitTree;
+            Type mt = types().erasure(mtree.type);
+            if (mt instanceof MethodType) {
+                return signature(types(), (MethodType) mt);
+            }
+        }
+        return null;
+    }
+
+    static String signature(Types types, MethodType mt) {
+        TDSignatureGenerator sg = new TDSignatureGenerator(types);
+        sg.assembleSig(mt);
+        return sg.toString();
+    }
+
+    /**
+     * Signature Generation
+     */
+    private static class TDSignatureGenerator extends Types.SignatureGenerator {
+
+        /**
+         * An output buffer for type signatures.
+         */
+        StringBuilder sb = new StringBuilder();
+
+        TDSignatureGenerator(Types types) {
+            super(types);
+        }
+
+        @Override
+        protected void append(char ch) {
+            sb.append(ch);
+        }
+
+        @Override
+        protected void append(byte[] ba) {
+            sb.append(new String(ba));
+        }
+
+        @Override
+        protected void append(Name name) {
+            sb.append(name);
+        }
+
+        @Override
+        public String toString() {
+            return sb.toString();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/TypeDeclSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,54 @@
+/*
+ * 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.TypeDeclKey;
+
+/**
+ * Snippet for a type definition (a class, interface, enum, or annotation
+ * interface definition).
+ * The Kind is {@link jdk.jshell.Snippet.Kind#TYPE_DECL}.
+ * <p>
+ * <code>TypeDeclSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ */
+public class TypeDeclSnippet extends DeclarationSnippet {
+
+    TypeDeclSnippet(TypeDeclKey key, String userSource, Wrap guts,
+            String unitName, SubKind subkind, Wrap corralled,
+            Collection<String> declareReferences,
+            Collection<String> bodyReferences) {
+        super(key, userSource, guts, unitName, subkind, corralled, declareReferences, bodyReferences);
+    }
+
+    /**** internal access ****/
+
+    @Override
+    TypeDeclKey key() {
+        return (TypeDeclKey) super.key();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/TypePrinter.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,148 @@
+/*
+ * 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 static com.sun.tools.javac.code.Flags.COMPOUND;
+import static com.sun.tools.javac.code.Kinds.Kind.PCK;
+import com.sun.tools.javac.code.Printer;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.PackageSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.ClassType;
+import com.sun.tools.javac.util.JavacMessages;
+import java.util.Locale;
+import java.util.function.BinaryOperator;
+
+/**
+ * Print types in source form.
+ */
+class TypePrinter extends Printer {
+
+    private final JavacMessages messages;
+    private final BinaryOperator<String> fullClassNameAndPackageToClass;
+    private final Type typeToPrint;
+
+    TypePrinter(JavacMessages messages, BinaryOperator<String> fullClassNameAndPackageToClass, Type typeToPrint) {
+        this.messages = messages;
+        this.fullClassNameAndPackageToClass = fullClassNameAndPackageToClass;
+        this.typeToPrint = typeToPrint;
+    }
+
+    @Override
+    protected String localize(Locale locale, String key, Object... args) {
+        return messages.getLocalizedString(locale, key, args);
+    }
+
+    @Override
+    protected String capturedVarId(Type.CapturedType t, Locale locale) {
+        throw new InternalError("should never call this");
+    }
+
+    @Override
+    public String visitCapturedType(Type.CapturedType t, Locale locale) {
+        if (t == typeToPrint) {
+            return visit(t.getUpperBound(), locale);
+        } else {
+            return visit(t.wildcard, locale);
+        }
+    }
+
+    @Override
+    public String visitType(Type t, Locale locale) {
+        String s = (t.tsym == null || t.tsym.name == null)
+                ? "Object" // none
+                : t.tsym.name.toString();
+        return s;
+    }
+
+    /**
+     * Converts a class name into a (possibly localized) string. Anonymous
+     * inner classes get converted into a localized string.
+     *
+     * @param t the type of the class whose name is to be rendered
+     * @param longform if set, the class' fullname is displayed - if unset the
+     * short name is chosen (w/o package)
+     * @param locale the locale in which the string is to be rendered
+     * @return localized string representation
+     */
+    @Override
+    protected String className(ClassType t, boolean longform, Locale locale) {
+        Symbol sym = t.tsym;
+        if (sym.name.length() == 0 && (sym.flags() & COMPOUND) != 0) {
+            /***
+            StringBuilder s = new StringBuilder(visit(t.supertype_field, locale));
+            for (List<Type> is = t.interfaces_field; is.nonEmpty(); is = is.tail) {
+                s.append('&');
+                s.append(visit(is.head, locale));
+            }
+            return s.toString();
+            ***/
+            return "Object";
+        } else if (sym.name.length() == 0) {
+            // Anonymous
+            String s;
+            ClassType norm = (ClassType) t.tsym.type;
+            if (norm == null) {
+                s = "object";
+            } else if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) {
+                s = visit(norm.interfaces_field.head, locale);
+            } else {
+                s = visit(norm.supertype_field, locale);
+            }
+            return s;
+        } else if (longform) {
+            String pkg = "";
+            for (Symbol psym = sym; psym != null; psym = psym.owner) {
+                if (psym.kind == PCK) {
+                    pkg = psym.getQualifiedName().toString();
+                    break;
+                }
+            }
+            return fullClassNameAndPackageToClass.apply(
+                    sym.getQualifiedName().toString(),
+                    pkg
+            );
+        } else {
+            return sym.name.toString();
+        }
+    }
+
+    @Override
+    public String visitClassSymbol(ClassSymbol sym, Locale locale) {
+        return sym.name.isEmpty()
+                ? sym.flatname.toString() // Anonymous
+                : sym.fullname.toString();
+    }
+
+    @Override
+    public String visitPackageSymbol(PackageSymbol s, Locale locale) {
+        return s.isUnnamed()
+                ? ""   // Unnamed package
+                : s.fullname.toString();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Unit.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,493 @@
+/*
+ * 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.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+import com.sun.jdi.ReferenceType;
+import jdk.jshell.Snippet.Kind;
+import jdk.jshell.Snippet.Status;
+import jdk.jshell.Snippet.SubKind;
+import jdk.jshell.TaskFactory.AnalyzeTask;
+import jdk.jshell.ClassTracker.ClassInfo;
+import jdk.jshell.TaskFactory.CompileTask;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.Collectors.toSet;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_EVNT;
+import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
+import static jdk.jshell.Snippet.Status.OVERWRITTEN;
+import static jdk.jshell.Snippet.Status.RECOVERABLE_DEFINED;
+import static jdk.jshell.Snippet.Status.RECOVERABLE_NOT_DEFINED;
+import static jdk.jshell.Snippet.Status.REJECTED;
+import static jdk.jshell.Snippet.Status.VALID;
+import static jdk.jshell.Util.expunge;
+
+/**
+ * Tracks the compilation and load of a new or updated snippet.
+ * @author Robert Field
+ */
+final class Unit {
+
+    private final JShell state;
+    private final Snippet si;
+    private final Snippet siOld;
+    private final boolean isDependency;
+    private final boolean isNew;
+    private final Snippet causalSnippet;
+    private final DiagList generatedDiagnostics;
+
+    private int seq;
+    private int seqInitial;
+    private Wrap activeGuts;
+    private Status status;
+    private Status prevStatus;
+    private boolean signatureChanged;
+    private DiagList compilationDiagnostics;
+    private DiagList recompilationDiagnostics = null;
+    private List<String> unresolved;
+    private SnippetEvent replaceOldEvent;
+    private List<SnippetEvent> secondaryEvents;
+    private boolean isAttemptingCorral;
+    private List<ClassInfo> toRedefine;
+    private boolean dependenciesNeeded;
+
+    Unit(JShell state, Snippet si, Snippet causalSnippet,
+            DiagList generatedDiagnostics) {
+        this.state = state;
+        this.si = si;
+        this.isDependency = causalSnippet != null;
+        this.siOld = isDependency
+                ? si
+                : state.maps.getSnippet(si.key());
+        this.isNew = siOld == null;
+        this.causalSnippet = causalSnippet;
+        this.generatedDiagnostics = generatedDiagnostics;
+
+        this.seq = isNew? 0 : siOld.sequenceNumber();
+        this.seqInitial = seq;
+        this.prevStatus = (isNew || isDependency)
+                ? si.status()
+                : siOld.status();
+        si.setSequenceNumber(seq);
+    }
+
+    // Drop entry
+    Unit(JShell state, Snippet si) {
+        this.state = state;
+        this.si = si;
+        this.siOld = null;
+        this.isDependency = false;
+        this.isNew = false;
+        this.causalSnippet = null;
+        this.generatedDiagnostics = new DiagList();
+        this.prevStatus = si.status();
+        si.setDropped();
+        this.status = si.status();
+    }
+
+    @Override
+    public int hashCode() {
+        return si.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return (o instanceof Unit)
+                ? si.equals(((Unit) o).si)
+                : false;
+    }
+
+    Snippet snippet() {
+        return si;
+    }
+
+    boolean isDependency() {
+        return isDependency;
+    }
+
+    boolean isNew() {
+        return isNew;
+    }
+
+    boolean isRedundant() {
+        return !isNew && !isDependency() && !si.isExecutable() &&
+                prevStatus.isDefined &&
+                siOld.source().equals(si.source());
+    }
+
+    void initialize(Collection<Unit> working) {
+        isAttemptingCorral = false;
+        dependenciesNeeded = false;
+        toRedefine = null; // assure NPE if classToLoad not called
+        activeGuts = si.guts();
+        markOldDeclarationOverwritten();
+        setWrap(working, working);
+    }
+
+    void setWrap(Collection<Unit> except, Collection<Unit> plus) {
+        si.setOuterWrap(isImport()
+                ? OuterWrap.wrapImport(si.source(), activeGuts)
+                : state.eval.wrapInClass(si,
+                        except.stream().map(u -> u.snippet().key()).collect(toSet()),
+                        activeGuts,
+                        plus.stream().map(u -> u.snippet())
+                                .filter(sn -> sn != si)
+                                .collect(toList())));
+    }
+
+    void setDiagnostics(AnalyzeTask ct) {
+        setDiagnostics(ct.getDiagnostics().ofUnit(this));
+    }
+
+    void setDiagnostics(DiagList diags) {
+        compilationDiagnostics = diags;
+        UnresolvedExtractor ue = new UnresolvedExtractor(diags);
+        unresolved = ue.unresolved();
+        state.debug(DBG_GEN, "++setCompilationInfo() %s\n%s\n-- diags: %s\n",
+                si, si.outerWrap().wrapped(), diags);
+    }
+
+    private boolean isRecoverable() {
+        // Unit failed, use corralling if it is defined on this Snippet,
+        // and either all the errors are resolution errors or this is a
+        // redeclare of an existing method
+        return compilationDiagnostics.hasErrors()
+                && si instanceof DeclarationSnippet
+                && (isDependency()
+                    || (si.subKind() != SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND
+                        && compilationDiagnostics.hasResolutionErrorsAndNoOthers()));
+    }
+
+    /**
+     * If it meets the conditions for corralling, install the corralled wrap
+     * @return true is the corralled wrap was installed
+     */
+    boolean corralIfNeeded(Collection<Unit> working) {
+        if (isRecoverable()
+                && si.corralled() != null) {
+            activeGuts = si.corralled();
+            setWrap(working, working);
+            return isAttemptingCorral = true;
+        }
+        return isAttemptingCorral = false;
+    }
+
+    void setCorralledDiagnostics(AnalyzeTask cct) {
+        // set corralled diagnostics, but don't reset unresolved
+        recompilationDiagnostics = cct.getDiagnostics().ofUnit(this);
+        state.debug(DBG_GEN, "++recomp %s\n%s\n-- diags: %s\n",
+                si, si.outerWrap().wrapped(), recompilationDiagnostics);
+    }
+
+    boolean smashingErrorDiagnostics(CompileTask ct) {
+        if (isDefined()) {
+            // set corralled diagnostics, but don't reset unresolved
+            DiagList dl = ct.getDiagnostics().ofUnit(this);
+            if (dl.hasErrors()) {
+                setDiagnostics(dl);
+                status = RECOVERABLE_NOT_DEFINED;
+                // overwrite orginal bytes
+                state.debug(DBG_GEN, "++smashingErrorDiagnostics %s\n%s\n-- diags: %s\n",
+                        si, si.outerWrap().wrapped(), dl);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void setStatus() {
+        if (!compilationDiagnostics.hasErrors()) {
+            status = VALID;
+        } else if (isRecoverable()) {
+            if (isAttemptingCorral && !recompilationDiagnostics.hasErrors()) {
+                status = RECOVERABLE_DEFINED;
+            } else {
+                status = RECOVERABLE_NOT_DEFINED;
+            }
+        } else {
+            status = REJECTED;
+        }
+        checkForOverwrite();
+
+        state.debug(DBG_GEN, "setStatus() %s - status: %s\n",
+                si, status);
+    }
+
+    /**
+     * Must be called for each unit
+     * @return
+     */
+    boolean isDefined() {
+        return status.isDefined;
+    }
+
+    /**
+     * Process the class information from the last compile.
+     * Requires loading of returned list.
+     * @return the list of classes to load
+     */
+    Stream<ClassInfo> classesToLoad(List<ClassInfo> cil) {
+        toRedefine = new ArrayList<>();
+        List<ClassInfo> toLoad = new ArrayList<>();
+        if (status.isDefined && !isImport()) {
+            cil.stream().forEach(ci -> {
+                if (!ci.isLoaded()) {
+                    if (ci.getReferenceTypeOrNull() == null) {
+                        toLoad.add(ci);
+                        ci.setLoaded();
+                        dependenciesNeeded = true;
+                    } else {
+                        toRedefine.add(ci);
+                    }
+                }
+            });
+        }
+        return toLoad.stream();
+    }
+
+    /**
+     * Redefine classes needing redefine.
+     * classesToLoad() must be called first.
+     * @return true if all redefines succeeded (can be vacuously true)
+     */
+    boolean doRedefines() {
+         if (toRedefine.isEmpty()) {
+            return true;
+        }
+        Map<ReferenceType, byte[]> mp = toRedefine.stream()
+                .collect(toMap(ci -> ci.getReferenceTypeOrNull(), ci -> ci.getBytes()));
+        if (state.executionControl().commandRedefine(mp)) {
+            // success, mark as loaded
+            toRedefine.stream().forEach(ci -> ci.setLoaded());
+            return true;
+        } else {
+            // failed to redefine
+            return false;
+        }
+    }
+
+    void markForReplacement() {
+        // increment for replace class wrapper
+        si.setSequenceNumber(++seq);
+    }
+
+    private boolean isImport() {
+        return si.kind() == Kind.IMPORT;
+    }
+
+    private boolean sigChanged() {
+        return (status.isDefined != prevStatus.isDefined)
+                || (seq != seqInitial && status.isDefined)
+                || signatureChanged;
+    }
+
+    Stream<Unit> effectedDependents() {
+        return sigChanged() || dependenciesNeeded || status == RECOVERABLE_NOT_DEFINED
+                ? dependents()
+                : Stream.empty();
+    }
+
+    Stream<Unit> dependents() {
+        return state.maps.getDependents(si)
+                    .stream()
+                    .filter(xsi -> xsi != si && xsi.status().isActive)
+                    .map(xsi -> new Unit(state, xsi, si, new DiagList()));
+    }
+
+    void finish() {
+        recordCompilation();
+        state.maps.installSnippet(si);
+    }
+
+    private void markOldDeclarationOverwritten() {
+        if (si != siOld && siOld != null && siOld.status().isActive) {
+            // Mark the old declaraion as replaced
+            replaceOldEvent = new SnippetEvent(siOld,
+                    siOld.status(), OVERWRITTEN,
+                    false, si, null, null);
+            siOld.setOverwritten();
+        }
+    }
+
+    private DiagList computeDiagnostics() {
+        DiagList diagnostics = new DiagList();
+        DiagList diags = compilationDiagnostics;
+        if (status == RECOVERABLE_DEFINED || status == RECOVERABLE_NOT_DEFINED) {
+            UnresolvedExtractor ue = new UnresolvedExtractor(diags);
+            diagnostics.addAll(ue.otherAll());
+        } else {
+            unresolved = Collections.emptyList();
+            diagnostics.addAll(diags);
+        }
+        diagnostics.addAll(generatedDiagnostics);
+        return diagnostics;
+    }
+
+    private void recordCompilation() {
+        state.maps.mapDependencies(si);
+        DiagList diags = computeDiagnostics();
+        si.setCompilationStatus(status, unresolved, diags);
+        state.debug(DBG_GEN, "recordCompilation: %s -- status %s, unresolved %s\n",
+                si, status, unresolved);
+    }
+
+    private void checkForOverwrite() {
+        secondaryEvents = new ArrayList<>();
+        if (replaceOldEvent != null) secondaryEvents.add(replaceOldEvent);
+
+        // Defined methods can overwrite methods of other (equivalent) snippets
+        if (si.kind() == Kind.METHOD && status.isDefined) {
+            String oqpt = ((MethodSnippet) si).qualifiedParameterTypes();
+            String nqpt = computeQualifiedParameterTypes(si);
+            if (!nqpt.equals(oqpt)) {
+                ((MethodSnippet) si).setQualifiedParamaterTypes(nqpt);
+                Status overwrittenStatus = overwriteMatchingMethod(si);
+                if (overwrittenStatus != null) {
+                    prevStatus = overwrittenStatus;
+                    signatureChanged = true;
+                }
+            }
+        }
+    }
+
+    // Check if there is a method whose user-declared parameter types are
+    // different (and thus has a different snippet) but whose compiled parameter
+    // types are the same. if so, consider it an overwrite replacement.
+    private Status overwriteMatchingMethod(Snippet si) {
+        String qpt = ((MethodSnippet) si).qualifiedParameterTypes();
+
+        // Look through all methods for a method of the same name, with the
+        // same computed qualified parameter types
+        Status overwrittenStatus = null;
+        for (MethodSnippet sn : state.methods()) {
+            if (sn != null && sn != si && sn.status().isActive && sn.name().equals(si.name())) {
+                if (qpt.equals(sn.qualifiedParameterTypes())) {
+                    overwrittenStatus = sn.status();
+                    SnippetEvent se = new SnippetEvent(
+                            sn, overwrittenStatus, OVERWRITTEN,
+                            false, si, null, null);
+                    sn.setOverwritten();
+                    secondaryEvents.add(se);
+                    state.debug(DBG_EVNT,
+                            "Overwrite event #%d -- key: %s before: %s status: %s sig: %b cause: %s\n",
+                            secondaryEvents.size(), se.snippet(), se.previousStatus(),
+                            se.status(), se.isSignatureChange(), se.causeSnippet());
+                }
+            }
+        }
+        return overwrittenStatus;
+    }
+
+    private String computeQualifiedParameterTypes(Snippet si) {
+        MethodSnippet msi = (MethodSnippet) si;
+        String qpt;
+        AnalyzeTask at = state.taskFactory.new AnalyzeTask(msi.outerWrap());
+        String rawSig = new TreeDissector(at).typeOfMethod();
+        String signature = expunge(rawSig);
+        int paren = signature.lastIndexOf(')');
+        if (paren < 0) {
+            // Uncompilable snippet, punt with user parameter types
+            qpt = msi.parameterTypes();
+        } else {
+            qpt = signature.substring(0, paren + 1);
+        }
+        return qpt;
+    }
+
+    SnippetEvent event(String value, Exception exception) {
+        boolean wasSignatureChanged = sigChanged();
+        state.debug(DBG_EVNT, "Snippet: %s id: %s before: %s status: %s sig: %b cause: %s\n",
+                si, si.id(), prevStatus, si.status(), wasSignatureChanged, causalSnippet);
+        return new SnippetEvent(si, prevStatus, si.status(),
+                wasSignatureChanged, causalSnippet, value, exception);
+    }
+
+    List<SnippetEvent> secondaryEvents() {
+        return secondaryEvents;
+    }
+
+    @Override
+    public String toString() {
+        return "Unit(" + si.name() + ")";
+    }
+
+    /**
+     * Separate out the unresolvedDependencies errors from both the other
+     * corralling errors and the overall errors.
+     */
+    private static class UnresolvedExtractor {
+
+        private static final String RESOLVE_ERROR_SYMBOL = "symbol:";
+        private static final String RESOLVE_ERROR_LOCATION = "location:";
+
+        //TODO extract from tree instead -- note: internationalization
+        private final Set<String> unresolved = new LinkedHashSet<>();
+        private final DiagList otherErrors = new DiagList();
+        private final DiagList otherAll = new DiagList();
+
+        UnresolvedExtractor(DiagList diags) {
+            for (Diag diag : diags) {
+                if (diag.isError()) {
+                    if (diag.isResolutionError()) {
+                        String m = diag.getMessage(null);
+                        int symPos = m.indexOf(RESOLVE_ERROR_SYMBOL);
+                        if (symPos >= 0) {
+                            m = m.substring(symPos + RESOLVE_ERROR_SYMBOL.length());
+                            int symLoc = m.indexOf(RESOLVE_ERROR_LOCATION);
+                            if (symLoc >= 0) {
+                                m = m.substring(0, symLoc);
+                            }
+                            m = m.trim();
+                            unresolved.add(m);
+                            continue;
+                        }
+                    }
+                    otherErrors.add(diag);
+                }
+                otherAll.add(diag);
+            }
+        }
+
+        DiagList otherCorralledErrors() {
+            return otherErrors;
+        }
+
+        DiagList otherAll() {
+            return otherAll;
+        }
+
+        List<String> unresolved() {
+            return new ArrayList<>(unresolved);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/UnresolvedReferenceException.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+/**
+ * Exception reported on attempting to execute a
+ * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
+ * method.
+ * <p>
+ * The stack can be queried by methods on <code>Exception</code>.
+ * 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 UnresolvedReferenceException extends Exception {
+
+    final MethodSnippet methodSnippet;
+
+    UnresolvedReferenceException(MethodSnippet methodSnippet, StackTraceElement[] stackElements) {
+        super("Attempt to invoke method with unresolved references");
+        this.methodSnippet = methodSnippet;
+        this.setStackTrace(stackElements);
+    }
+
+    /**
+     * Return the method Snippet which has the unresolved reference(s).
+     * @return the <code>MethodSnippet</code> of the
+     * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
+     * method.
+     */
+    public MethodSnippet getMethodSnippet() {
+        return methodSnippet;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Util.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,94 @@
+ /*
+ * 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.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.lang.model.element.Name;
+import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
+import static jdk.internal.jshell.remote.RemoteCodes.prefixPattern;
+
+/**
+ * Assorted shared utilities.
+ * @author Robert Field
+ */
+class Util {
+
+    static final String REPL_CLASS_PREFIX = "$REPL";
+    static final String REPL_DOESNOTMATTER_CLASS_NAME = REPL_CLASS_PREFIX+"00DOESNOTMATTER";
+
+    static boolean isDoIt(Name name) {
+        return isDoIt(name.toString());
+    }
+
+    static boolean isDoIt(String sname) {
+        return sname.equals(DOIT_METHOD_NAME);
+    }
+
+    static String expunge(String s) {
+        StringBuilder sb = new StringBuilder();
+        for (String comp : prefixPattern.split(s)) {
+            sb.append(comp);
+        }
+        return sb.toString();
+    }
+
+    static String asLetters(int i) {
+        if (i == 0) {
+            return "";
+        }
+
+        char buf[] = new char[33];
+        int charPos = 32;
+
+        i = -i;
+        while (i <= -26) {
+            buf[charPos--] = (char) ('A'-(i % 26));
+            i = i / 26;
+        }
+        buf[charPos] = (char) ('A'-i);
+
+        return new String(buf, charPos, (33 - charPos));
+    }
+
+
+    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;
+        }
+    }
+
+    static <T> Stream<T> stream(Iterable<T> iterable) {
+        return StreamSupport.stream(iterable.spliterator(), false);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,57 @@
+/*
+ * 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.VarKey;
+
+/**
+ * Snippet for a variable definition.
+ * The Kind is {@link jdk.jshell.Snippet.Kind#VAR}.
+ * <p>
+ * <code>VarSnippet</code> is immutable: an access to
+ * any of its methods will always return the same result.
+ * and thus is thread-safe.
+ * @jls 8.3: FieldDeclaration.
+ */
+public class VarSnippet extends DeclarationSnippet {
+
+    final String typeName;
+
+     VarSnippet(VarKey key, String userSource, Wrap guts,
+            String name, SubKind subkind, String typeName,
+            Collection<String> declareReferences) {
+        super(key, userSource, guts, name, subkind, null, declareReferences, null);
+        this.typeName = typeName;
+    }
+
+    /**
+     * A String representation of the type of the variable.
+     * @return the variable type as a String.
+     */
+    public String typeName() {
+        return typeName;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Wrap.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,458 @@
+/*
+ * 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.List;
+import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
+
+/**
+ * Wrapping of source into Java methods, fields, etc.  All but outer layer
+ * wrapping with imports and class.
+ *
+ * @author Robert Field
+ */
+abstract class Wrap implements GeneralWrap {
+
+    private static Wrap methodWrap(String prefix, String source, String suffix) {
+        Wrap wunit = new NoWrap(source);
+        return new DoitMethodWrap(new CompoundWrap(prefix, wunit, suffix));
+    }
+
+    public static Wrap methodWrap(String source) {
+        return methodWrap("", source, semi(source) + "        return null;\n");
+    }
+
+    public static Wrap methodReturnWrap(String source) {
+        return methodWrap("return ", source, semi(source));
+    }
+
+    public static Wrap methodUnreachableSemiWrap(String source) {
+        return methodWrap("", source, semi(source));
+    }
+
+    public static Wrap methodUnreachableWrap(String source) {
+        return methodWrap("", source, "");
+    }
+
+    public static Wrap corralledMethod(String source, Range modRange, Range tpRange, Range typeRange, String name, Range paramRange, Range throwsRange, int id) {
+        List<Object> l = new ArrayList<>();
+        l.add("    public static\n    ");
+        if (!modRange.isEmpty()) {
+            l.add(new RangeWrap(source, modRange));
+            l.add(" ");
+        }
+        if (tpRange != null) {
+            l.add("<");
+            l.add(new RangeWrap(source, tpRange));
+            l.add("> ");
+        }
+        l.add(new RangeWrap(source, typeRange));
+        l.add(" " + name + "(\n        ");
+        if (paramRange != null) {
+            l.add(new RangeWrap(source, paramRange));
+        }
+        l.add(") ");
+        if (throwsRange != null) {
+            l.add("throws ");
+            l.add(new RangeWrap(source, throwsRange));
+        }
+        l.add(" {\n        throw new jdk.internal.jshell.remote.RemoteResolutionException(" + id + ");\n}\n");
+        return new CompoundWrap(l.toArray());
+    }
+
+    /**
+     *
+     * @param in
+     * @param rname
+     * @param rinit Initializer or null
+     * @param rdecl Type name and name
+     * @return
+     */
+    public static Wrap varWrap(String source, Range rtype, String brackets, Range rname, Range rinit) {
+        RangeWrap wname = new RangeWrap(source, rname);
+        RangeWrap wtype = new RangeWrap(source, rtype);
+        Wrap wVarDecl = new VarDeclareWrap(wtype, brackets, wname);
+        Wrap wmeth;
+
+        if (rinit == null) {
+            wmeth = new CompoundWrap(new NoWrap(" "), "   return null;\n");
+        } else {
+            RangeWrap winit = new RangeWrap(source, rinit);
+        // int x = y
+            // int x_ = y; return x = x_;
+            // decl + "_ = " + init ; + "return " + name + "=" + name + "_ ;"
+            wmeth = new CompoundWrap(
+                    wtype, brackets + " ", wname, "_ =\n        ", winit, semi(winit),
+                    "        return ", wname, " = ", wname, "_;\n"
+            );
+        }
+        Wrap wInitMeth = new DoitMethodWrap(wmeth);
+        return new CompoundWrap(wVarDecl, wInitMeth);
+    }
+
+    public static Wrap tempVarWrap(String source, String typename, String name) {
+        RangeWrap winit = new NoWrap(source);
+        // y
+        // return $1 = y;
+        // "return " + $1 + "=" + init ;
+        Wrap wmeth = new CompoundWrap("return " + name + " =\n        ", winit, semi(winit));
+        Wrap wInitMeth = new DoitMethodWrap(wmeth);
+
+        String varDecl = "    public static\n    " + typename + " " + name + ";\n";
+        return new CompoundWrap(varDecl, wInitMeth);
+    }
+
+    public static Wrap importWrap(String source) {
+        return new NoWrap(source);
+    }
+
+    public static Wrap classMemberWrap(String source) {
+        Wrap w = new NoWrap(source);
+        return new CompoundWrap("    public static\n    ", w);
+    }
+
+    private static int countLines(String s) {
+        return countLines(s, 0, s.length());
+    }
+
+    private static int countLines(String s, int from, int toEx) {
+        int cnt = 0;
+        int idx = from;
+        while ((idx = s.indexOf('\n', idx)) > 0) {
+            if (idx >= toEx) break;
+            ++cnt;
+            ++idx;
+        }
+        return cnt;
+    }
+
+    public static final class Range {
+        final int begin;
+        final int end;
+
+        Range(int begin, int end) {
+            this.begin = begin;
+            this.end = end;
+        }
+
+        Range(String s) {
+            this.begin = 0;
+            this.end = s.length();
+        }
+
+        String part(String s) {
+            return s.substring(begin, end);
+        }
+
+        int length() {
+            return end - begin;
+        }
+
+        boolean isEmpty() {
+            return end == begin;
+        }
+
+        void verify(String s) {
+            if (begin < 0 || end <= begin || end > s.length()) {
+                throw new InternalError("Bad Range: " + s + "[" + begin + "," + end + "]");
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Range[" + begin + "," + end + "]";
+        }
+    }
+
+    public static class CompoundWrap extends Wrap {
+
+        final Object[] os;
+        final String wrapped;
+        final int snidxFirst;
+        final int snidxLast;
+        final int snlineFirst;
+        final int snlineLast;
+
+        CompoundWrap(Object... os) {
+            this.os = os;
+            int sniFirst = Integer.MAX_VALUE;
+            int sniLast = Integer.MIN_VALUE;
+            int snlnFirst = Integer.MAX_VALUE;
+            int snlnLast = Integer.MIN_VALUE;
+            StringBuilder sb = new StringBuilder();
+            for (Object o : os) {
+                if (o instanceof String) {
+                    String s = (String) o;
+                    sb.append(s);
+                } else if (o instanceof Wrap) {
+                    Wrap w = (Wrap) o;
+                    if (w.firstSnippetIndex() < sniFirst) {
+                        sniFirst = w.firstSnippetIndex();
+                    }
+                    if (w.lastSnippetIndex() > sniLast) {
+                        sniLast = w.lastSnippetIndex();
+                    }
+                    if (w.firstSnippetLine() < snlnFirst) {
+                        snlnFirst = w.firstSnippetLine();
+                    }
+                    if (w.lastSnippetLine() > snlnLast) {
+                        snlnLast = w.lastSnippetLine();
+                    }
+                    sb.append(w.wrapped());
+                } else {
+                    throw new InternalError("Bad object in CommoundWrap: " + o);
+                }
+            }
+            this.wrapped = sb.toString();
+            this.snidxFirst = sniFirst;
+            this.snidxLast = sniLast;
+            this.snlineFirst = snlnFirst;
+            this.snlineLast = snlnLast;
+        }
+
+        @Override
+        public String wrapped() {
+            return wrapped;
+        }
+
+        @Override
+        public int snippetIndexToWrapIndex(int sni) {
+            int before = 0;
+            for (Object o : os) {
+                if (o instanceof String) {
+                    String s = (String) o;
+                    before += s.length();
+                } else if (o instanceof Wrap) {
+                    Wrap w = (Wrap) o;
+                    if (sni >= w.firstSnippetIndex() && sni <= w.lastSnippetIndex()) {
+                        return w.snippetIndexToWrapIndex(sni) + before;
+                    }
+                    before += w.wrapped().length();
+                }
+            }
+            return 0;
+        }
+
+        @Override
+        public int wrapIndexToSnippetIndex(int wi) {
+            int before = 0;
+            for (Object o : os) {
+                if (o instanceof String) {
+                    String s = (String) o;
+                    before += s.length();
+                } else if (o instanceof Wrap) {
+                    Wrap w = (Wrap) o;
+                    int len = w.wrapped().length();
+                    if ((wi - before) <= len) {
+                        //System.err.printf("Defer to wrap %s - wi: %d. before; %d   -- %s  >>> %s\n",
+                        //        w, wi, before, w.debugPos(wi - before), w.wrapped());
+                        return w.wrapIndexToSnippetIndex(wi - before);
+                    }
+                    before += len;
+                }
+            }
+            return lastSnippetIndex();
+        }
+
+        @Override
+        public int firstSnippetIndex() {
+            return snidxFirst;
+        }
+
+        @Override
+        public int lastSnippetIndex() {
+            return snidxLast;
+        }
+
+        @Override
+        public int snippetLineToWrapLine(int snline) {
+            int before = 0;
+            for (Object o : os) {
+                if (o instanceof String) {
+                    String s = (String) o;
+                    before += countLines(s);
+                } else if (o instanceof Wrap) {
+                    Wrap w = (Wrap) o;
+                    if (snline >= w.firstSnippetLine() && snline <= w.lastSnippetLine()) {
+                        return w.snippetLineToWrapLine(snline) + before;
+                    }
+                    before += countLines(w.wrapped());
+                }
+            }
+            return 0;
+        }
+
+        @Override
+        public int wrapLineToSnippetLine(int wline) {
+            int before = 0;
+            for (Object o : os) {
+                if (o instanceof String) {
+                    String s = (String) o;
+                    before += countLines(s);
+                } else if (o instanceof Wrap) {
+                    Wrap w = (Wrap) o;
+                    int lns = countLines(w.wrapped());
+                    if ((wline - before) < lns) {
+                        return w.wrapLineToSnippetLine(wline - before);
+                    }
+                    before += lns;
+                }
+            }
+            return 0;
+        }
+
+        @Override
+        public int firstSnippetLine() {
+            return snlineFirst;
+        }
+
+        @Override
+        public int lastSnippetLine() {
+            return snlineLast;
+        }
+
+
+    }
+
+    private static class RangeWrap extends Wrap {
+
+        final Range range;
+        final String wrapped;
+        final int firstSnline;
+        final int lastSnline;
+
+        RangeWrap(String snippetSource, Range usedWithinSnippet) {
+            this.range = usedWithinSnippet;
+            this.wrapped = usedWithinSnippet.part(snippetSource);
+            usedWithinSnippet.verify(snippetSource);
+            this.firstSnline = countLines(snippetSource, 0, range.begin);
+            this.lastSnline = firstSnline + countLines(snippetSource, range.begin, range.end);
+        }
+
+        @Override
+        public String wrapped() {
+            return wrapped;
+        }
+
+        @Override
+        public int snippetIndexToWrapIndex(int sni) {
+            if (sni < range.begin) {
+                return 0;
+            }
+            if (sni > range.end) {
+                return range.length();
+            }
+            return sni - range.begin;
+        }
+
+        @Override
+        public int wrapIndexToSnippetIndex(int wi) {
+            if (wi < 0) {
+                return 0; // bad index
+            }
+            int max = range.length();
+            if (wi > max) {
+                wi = max;
+            }
+            return wi + range.begin;
+        }
+
+        @Override
+        public int firstSnippetIndex() {
+            return range.begin;
+        }
+
+        @Override
+        public int lastSnippetIndex() {
+            return range.end;
+        }
+
+        @Override
+        public int snippetLineToWrapLine(int snline) {
+            if (snline < firstSnline) {
+                return 0;
+            }
+            if (snline >= lastSnline) {
+                return lastSnline - firstSnline;
+            }
+            return snline - firstSnline;
+        }
+
+        @Override
+        public int wrapLineToSnippetLine(int wline) {
+            if (wline < 0) {
+                return 0; // bad index
+            }
+            int max = lastSnline - firstSnline;
+            if (wline > max) {
+                wline = max;
+            }
+            return wline + firstSnline;
+        }
+
+        @Override
+        public int firstSnippetLine() {
+            return firstSnline;
+        }
+
+        @Override
+        public int lastSnippetLine() {
+            return lastSnline;
+        }
+
+    }
+
+    private static class NoWrap extends RangeWrap {
+
+        NoWrap(String unit) {
+            super(unit, new Range(unit));
+        }
+    }
+
+    private static String semi(Wrap w) {
+        return semi(w.wrapped());
+    }
+
+    private static String semi(String s) {
+        return ((s.endsWith(";")) ? "\n" : ((s.endsWith(";\n")) ? "" : ";\n"));
+    }
+
+    private static class DoitMethodWrap extends CompoundWrap {
+
+        DoitMethodWrap(Wrap w) {
+            super("    public static Object " + DOIT_METHOD_NAME + "() throws Throwable {\n"
+                    + "        ", w,
+                    "    }\n");
+        }
+    }
+
+    private static class VarDeclareWrap extends CompoundWrap {
+
+        VarDeclareWrap(Wrap wtype, String brackets, Wrap wname) {
+            super("    public static ", wtype, brackets + " ", wname, semi(wname));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/package-info.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+/**
+ * Provides interfaces for creating tools, such as a Read-Eval-Print Loop (REPL),
+ * which interactively evaluate "snippets" of Java programming language code.
+ * Where a "snippet" is a single expression, statement, or declaration.
+ * This functionality can be used to enhance tools such as IDEs or can be
+ * stand-alone.
+ * <p>
+ * {@link jdk.jshell.JShell} is the central class.  An instance of
+ * <code>JShell</code> holds the evaluation state, which is both the current
+ * set of source snippets and the execution state they have produced.
+ * <p>
+ * Each source snippet is represented by an instance of a subclass of
+ * {@link jdk.jshell.Snippet}. For example, a statement is represented by an
+ * instance of {@link jdk.jshell.StatementSnippet}, and a method declaration is
+ * represented by an instance of {@link jdk.jshell.MethodSnippet}.
+ * Snippets are created when {@link jdk.jshell.JShell#eval(java.lang.String)}
+ * is invoked with an input which includes one or more snippets of code.
+ * <p>
+ * Any change to the compilation status of a snippet is reported with a
+ * {@link jdk.jshell.SnippetEvent}.  There are three major kinds of
+ * changes to the status of a snippet: it can created with <code>eval</code>,
+ * it can be dropped from the active source state with
+ * {@link jdk.jshell.JShell#drop(jdk.jshell.PersistentSnippet)}, and it can have
+ * its status updated as a result of a status change in another snippet.
+ * For
+ * example: given <code>js</code>, an instance of <code>JShell</code>, executing
+ * <code>js.eval("int x = 5;")</code> will add the variable <code>x</code> to
+ * the source state and will generate an event describing the creation of a
+ * {@link jdk.jshell.VarSnippet} for <code>x</code>. Then executing
+ * <code>js.eval("int timesx(int val) { return val * x; }")</code> will add
+ * a method to the source state and will generate an event
+ * describing the creation of a {@link jdk.jshell.MethodSnippet} for
+ * <code>timesx</code>.
+ * Assume that <code>varx</code> holds the snippet created by the first
+ * call to <code>eval</code>, executing <code>js.drop(varx)</code> will
+ * generate two events: one for changing the status of the
+ * variable snippet to <code>DROPPED</code> and one for
+ * updating the method snippet (which now has an unresolved reference to
+ * <code>x</code>).
+ * <p>
+ * Of course, for any general application of the API, the input would not be
+ * fixed strings, but would come from the user. Below is a very simplified
+ * example of how the API might be used to implement a REPL.
+ * <pre>
+* {@code
+ *     import java.io.ByteArrayInputStream;
+ *     import java.io.Console;
+ *     import java.util.List;
+ *     import jdk.jshell.*;
+ *     import jdk.jshell.Snippet.Status;
+ *
+ *     class ExampleJShell {
+ *         public static void main(String[] args) {
+ *             Console console = System.console();
+ *             try (JShell js = JShell.create()) {
+ *                 do {
+ *                     System.out.print("Enter some Java code: ");
+ *                     String input = console.readLine();
+ *                     if (input == null) {
+ *                         break;
+ *                     }
+ *                     List&lt;SnippetEvent&gt; events = js.eval(input);
+ *                     for (SnippetEvent e : events) {
+ *                         StringBuilder sb = new StringBuilder();
+ *                         if (e.causeSnippet == null) {
+ *                             //  We have a snippet creation event
+ *                             switch (e.status) {
+ *                                 case VALID:
+ *                                     sb.append("Successful ");
+ *                                     break;
+ *                                 case RECOVERABLE_DEFINED:
+ *                                     sb.append("With unresolved references ");
+ *                                     break;
+ *                                 case RECOVERABLE_NOT_DEFINED:
+ *                                     sb.append("Possibly reparable, failed  ");
+ *                                     break;
+ *                                 case REJECTED:
+ *                                     sb.append("Failed ");
+ *                                     break;
+ *                             }
+ *                             if (e.previousStatus == Status.NONEXISTENT) {
+ *                                 sb.append("addition");
+ *                             } else {
+ *                                 sb.append("modification");
+ *                             }
+ *                             sb.append(" of ");
+ *                             sb.append(e.snippet.source());
+ *                             System.out.println(sb);
+ *                             if (e.value != null) {
+ *                                 System.out.printf("Value is: %s\n", e.value);
+ *                             }
+ *                             System.out.flush();
+ *                         }
+ *                     }
+ *                 } while (true);
+ *             }
+ *             System.out.println("\nGoodbye");
+ *         }
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * To register for status change events use
+ * {@link jdk.jshell.JShell#onSnippetEvent(java.util.function.Consumer)}.
+ * These events are only generated by <code>eval</code> and <code>drop</code>,
+ * the return values of these methods are the list of events generated by that
+ * call.  So, as in the example above, events can be used without registering
+ * to receive events.
+ * <p>
+ * If you experiment with this example, you will see that failing to terminate
+ * a statement or variable declaration with a semi-colon will simply fail.
+ * An unfinished entry (for example a desired multi-line method) will also just
+ * fail after one line.  The utilities in {@link jdk.jshell.SourceCodeAnalysis}
+ * provide source boundary and completeness analysis to address cases like
+ * those.  <code>SourceCodeAnalysis</code> also provides suggested completions
+ * of input, as might be used in tab-completion.
+ */
+
+
+package jdk.jshell;
+
--- a/langtools/test/com/sun/javadoc/testSimpleTag/TestSimpleTag.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/com/sun/javadoc/testSimpleTag/TestSimpleTag.java	Wed Oct 21 18:40:01 2015 -0700
@@ -51,7 +51,8 @@
                 "-tag", "regular:a:Regular Tag:",
                 "-tag", "back-slash\\:tag\\\\:a:Back-Slash-Tag:",
                 testSrc("C.java"));
-        checkExit(Exit.FAILED); // TODO: investigate why failed
+        // doclint fails because '\' is not allowed in tag name
+        checkExit(Exit.FAILED);
 
         checkOutput("C.html", true,
                 "<span class=\"simpleTagLabel\">Todo:</span>",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/AnalysisTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test SourceCodeAnalysis
+ * @build KullaTesting TestingInputStream
+ * @run testng AnalysisTest
+ */
+
+import org.testng.annotations.Test;
+
+@Test
+public class AnalysisTest extends KullaTesting {
+
+    public void testSource() {
+        assertAnalyze("int x=3//test", "int x=3;//test", "", true);
+        assertAnalyze("int x=3 ;//test", "int x=3 ;//test", "", true);
+        assertAnalyze("int x=3 //;", "int x=3; //;", "", true);
+        assertAnalyze("void m5() {} /// hgjghj", "void m5() {} /// hgjghj", "", true);
+        assertAnalyze("int ff; int v // hi", "int ff;", " int v // hi", true);
+    }
+
+    public void testSourceSlashStar() {
+        assertAnalyze("/*zoo*/int x=3 /*test*/", "/*zoo*/int x=3; /*test*/", "", true);
+        assertAnalyze("/*zoo*/int x=3 ;/*test*/", "/*zoo*/int x=3 ;/*test*/", "", true);
+        assertAnalyze("int x=3 /*;*/", "int x=3; /*;*/", "", true);
+        assertAnalyze("void m5() {} /*hgjghj*/", "void m5() {} /*hgjghj*/", "", true);
+        assertAnalyze("int ff; int v /*hgjghj*/", "int ff;", " int v /*hgjghj*/", true);
+    }
+
+    public void testIncomplete() {
+        assertAnalyze("void m() { //erer", null, "void m() { //erer\n", false);
+        assertAnalyze("int m=//", null, "int m=//\n", false);
+    }
+
+    public void testExpression() {
+        assertAnalyze("45//test", "45//test", "", true);
+        assertAnalyze("45;//test", "45;//test", "", true);
+        assertAnalyze("45//;", "45//;", "", true);
+        assertAnalyze("/*zoo*/45/*test*/", "/*zoo*/45/*test*/", "", true);
+        assertAnalyze("/*zoo*/45;/*test*/", "/*zoo*/45;/*test*/", "", true);
+        assertAnalyze("45/*;*/", "45/*;*/", "", true);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ClassMembersTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,355 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test access to members of user defined class.
+ * @build KullaTesting TestingInputStream ExpectedDiagnostic
+ * @run testng/timeout=600 ClassMembersTest
+ */
+
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.tools.Diagnostic;
+
+import jdk.jshell.SourceCodeAnalysis;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class ClassMembersTest extends KullaTesting {
+
+    @Test(dataProvider = "memberTestCase")
+    public void memberTest(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) {
+        MemberTestCase testCase = new MemberTestCase(accessModifier, codeChunk, isStaticMember, isStaticReference);
+        assertEval(testCase.generateSource());
+        String expectedMessage = testCase.expectedMessage;
+        if (testCase.codeChunk != CodeChunk.CONSTRUCTOR || testCase.isAccessible()) {
+            assertEval("A a = new A();");
+        }
+        if (expectedMessage == null) {
+            assertEval(testCase.useCodeChunk());
+        } else {
+            assertDeclareFail(testCase.useCodeChunk(), expectedMessage);
+        }
+    }
+
+    private List<String> parseCode(String input) {
+        List<String> list = new ArrayList<>();
+        SourceCodeAnalysis codeAnalysis = getAnalysis();
+        String source = input;
+        while (!source.trim().isEmpty()) {
+            SourceCodeAnalysis.CompletionInfo info = codeAnalysis.analyzeCompletion(source);
+            list.add(info.source);
+            source = info.remaining;
+        }
+        return list;
+    }
+
+    @Test(dataProvider = "memberTestCase")
+    public void extendsMemberTest(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) {
+        MemberTestCase testCase = new ExtendsMemberTestCase(accessModifier, codeChunk, isStaticMember, isStaticReference);
+        String input = testCase.generateSource();
+        List<String> ss = parseCode(input);
+        assertEval(ss.get(0));
+        if (testCase.codeChunk != CodeChunk.CONSTRUCTOR || testCase.isAccessible()) {
+            assertEval(ss.get(1));
+            assertEval("B b = new B();");
+        }
+        String expectedMessage = testCase.expectedMessage;
+        if (expectedMessage == null) {
+            assertEval(testCase.useCodeChunk());
+        } else {
+            assertDeclareFail(testCase.useCodeChunk(), expectedMessage);
+        }
+    }
+
+    @Test
+    public void interfaceTest() {
+        String interfaceSource =
+                "interface A {\n" +
+                "   default int defaultMethod() { return 1; }\n" +
+                "   static int staticMethod() { return 2; }\n" +
+                "   int method();\n" +
+                "   class Inner1 {}\n" +
+                "   static class Inner2 {}\n" +
+                "}";
+        assertEval(interfaceSource);
+        assertEval("A.staticMethod();", "2");
+        String classSource =
+                "class B implements A {\n" +
+                "   public int method() { return 3; }\n" +
+                "}";
+        assertEval(classSource);
+        assertEval("B b = new B();");
+        assertEval("b.defaultMethod();", "1");
+        assertDeclareFail("B.staticMethod();",
+                new ExpectedDiagnostic("compiler.err.cant.resolve.location.args", 0, 14, 1, -1, -1, Diagnostic.Kind.ERROR));
+        assertEval("b.method();", "3");
+        assertEval("new A.Inner1();");
+        assertEval("new A.Inner2();");
+        assertEval("new B.Inner1();");
+        assertEval("new B.Inner2();");
+    }
+
+    @Test
+    public void enumTest() {
+        String enumSource =
+                "enum E {A(\"s\");\n" +
+                "   private final String s;\n" +
+                "   private E(String s) { this.s = s; }\n" +
+                "   public String method() { return s; }\n" +
+                "   private String privateMethod() { return s; }\n" +
+                "   public static String staticMethod() { return staticPrivateMethod(); }\n" +
+                "   private static String staticPrivateMethod() { return \"a\"; }\n" +
+                "}";
+        assertEval(enumSource);
+        assertEval("E a = E.A;", "A");
+        assertDeclareFail("a.s;",
+                new ExpectedDiagnostic("compiler.err.report.access", 0, 3, 1, -1, -1, Diagnostic.Kind.ERROR));
+        assertDeclareFail("new E(\"q\");",
+                new ExpectedDiagnostic("compiler.err.enum.cant.be.instantiated", 0, 10, 0, -1, -1, Diagnostic.Kind.ERROR));
+        assertEval("a.method();", "\"s\"");
+        assertDeclareFail("a.privateMethod();",
+                new ExpectedDiagnostic("compiler.err.report.access", 0, 15, 1, -1, -1, Diagnostic.Kind.ERROR));
+        assertEval("E.staticMethod();", "\"a\"");
+        assertDeclareFail("a.staticPrivateMethod();",
+                new ExpectedDiagnostic("compiler.err.report.access", 0, 21, 1, -1, -1, Diagnostic.Kind.ERROR));
+        assertDeclareFail("E.method();",
+                new ExpectedDiagnostic("compiler.err.non-static.cant.be.ref", 0, 8, 1, -1, -1, Diagnostic.Kind.ERROR));
+    }
+
+    @Test(enabled = false) // TODO 8080354
+    public void annotationTest() {
+        assertEval("import java.lang.annotation.*;");
+        for (RetentionPolicy policy : RetentionPolicy.values()) {
+            String annotationSource =
+                    "@Retention(RetentionPolicy." + policy.toString() + ")\n" +
+                    "@interface A {}";
+            assertEval(annotationSource);
+            String classSource =
+                    "@A class C {\n" +
+                    "   @A C() {}\n" +
+                    "   @A void f() {}\n" +
+                    "   @A int f;\n" +
+                    "   @A class Inner {}\n" +
+                    "}";
+            assertEval(classSource);
+            String isRuntimeVisible = policy == RetentionPolicy.RUNTIME ? "true" : "false";
+            assertEval("C.class.getAnnotationsByType(A.class).length > 0;", isRuntimeVisible);
+            assertEval("C.class.getDeclaredConstructor().getAnnotationsByType(A.class).length > 0;", isRuntimeVisible);
+            assertEval("C.class.getDeclaredMethod(\"f\").getAnnotationsByType(A.class).length > 0;", isRuntimeVisible);
+            assertEval("C.class.getDeclaredField(\"f\").getAnnotationsByType(A.class).length > 0;", isRuntimeVisible);
+            assertEval("C.Inner.class.getAnnotationsByType(A.class).length > 0;", isRuntimeVisible);
+        }
+    }
+
+    @DataProvider(name = "memberTestCase")
+    public Object[][] memberTestCaseGenerator() {
+        List<Object[]> list = new ArrayList<>();
+        for (AccessModifier accessModifier : AccessModifier.values()) {
+            for (Static isStaticMember : Static.values()) {
+                for (Static isStaticReference : Static.values()) {
+                    for (CodeChunk codeChunk : CodeChunk.values()) {
+                        if (codeChunk == CodeChunk.CONSTRUCTOR && isStaticMember == Static.STATIC) {
+                            continue;
+                        }
+                        list.add(new Object[]{ accessModifier, codeChunk, isStaticMember, isStaticReference });
+                    }
+                }
+            }
+        }
+        return list.toArray(new Object[list.size()][]);
+    }
+
+    public static class ExtendsMemberTestCase extends MemberTestCase {
+
+        public ExtendsMemberTestCase(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember, Static isStaticReference) {
+            super(accessModifier, codeChunk, isStaticMember, isStaticReference);
+        }
+
+        @Override
+        public String getSourceTemplate() {
+            return super.getSourceTemplate() + "\n"
+                    + "class B extends A {}";
+        }
+
+        @Override
+        public String errorMessage() {
+            if (!isAccessible()) {
+                if (codeChunk == CodeChunk.METHOD) {
+                    return "compiler.err.cant.resolve.location.args";
+                }
+                if (codeChunk == CodeChunk.CONSTRUCTOR) {
+                    return "compiler.err.cant.resolve.location";
+                }
+            }
+            return super.errorMessage();
+        }
+
+        @Override
+        public String useCodeChunk() {
+            return useCodeChunk("B");
+        }
+    }
+
+    public static class MemberTestCase {
+        public final AccessModifier accessModifier;
+        public final CodeChunk codeChunk;
+        public final Static isStaticMember;
+        public final Static isStaticReference;
+        public final String expectedMessage;
+
+        public MemberTestCase(AccessModifier accessModifier, CodeChunk codeChunk, Static isStaticMember,
+                              Static isStaticReference) {
+            this.accessModifier = accessModifier;
+            this.codeChunk = codeChunk;
+            this.isStaticMember = isStaticMember;
+            this.isStaticReference = isStaticReference;
+            this.expectedMessage = errorMessage();
+        }
+
+        public String getSourceTemplate() {
+            return  "class A {\n" +
+                    "    #MEMBER#\n" +
+                    "}";
+        }
+
+        public boolean isAccessible() {
+            return accessModifier != AccessModifier.PRIVATE;
+        }
+
+        public String errorMessage() {
+            if (!isAccessible()) {
+                return "compiler.err.report.access";
+            }
+            if (codeChunk == CodeChunk.INNER_INTERFACE) {
+                return "compiler.err.abstract.cant.be.instantiated";
+            }
+            if (isStaticMember == Static.STATIC) {
+                if (isStaticReference == Static.NO && codeChunk == CodeChunk.INNER_CLASS) {
+                    return "compiler.err.qualified.new.of.static.class";
+                }
+                return null;
+            }
+            if (isStaticReference == Static.STATIC) {
+                if (codeChunk == CodeChunk.CONSTRUCTOR) {
+                    return null;
+                }
+                if (codeChunk == CodeChunk.INNER_CLASS) {
+                    return "compiler.err.encl.class.required";
+                }
+                return "compiler.err.non-static.cant.be.ref";
+            }
+            return null;
+        }
+
+        public String generateSource() {
+            return getSourceTemplate().replace("#MEMBER#", codeChunk.generateSource(accessModifier, isStaticMember));
+        }
+
+        protected String useCodeChunk(String className) {
+            String name = className.toLowerCase();
+            switch (codeChunk) {
+                case CONSTRUCTOR:
+                    return String.format("new %s();", className);
+                case METHOD:
+                    if (isStaticReference == Static.STATIC) {
+                        return String.format("%s.method();", className);
+                    } else {
+                        return String.format("%s.method();", name);
+                    }
+                case FIELD:
+                    if (isStaticReference == Static.STATIC) {
+                        return String.format("%s.field;", className);
+                    } else {
+                        return String.format("%s.field;", name);
+                    }
+                case INNER_CLASS:
+                    if (isStaticReference == Static.STATIC) {
+                        return String.format("new %s.Inner();", className);
+                    } else {
+                        return String.format("%s.new Inner();", name);
+                    }
+                case INNER_INTERFACE:
+                    return String.format("new %s.Inner();", className);
+                default:
+                    throw new AssertionError("Unknown code chunk: " + this);
+            }
+        }
+
+        public String useCodeChunk() {
+            return useCodeChunk("A");
+        }
+    }
+
+    public enum AccessModifier {
+        PUBLIC("public"),
+        PROTECTED("protected"),
+        PACKAGE_PRIVATE(""),
+        PRIVATE("private");
+
+        private final String modifier;
+
+        AccessModifier(String modifier) {
+            this.modifier = modifier;
+        }
+
+        public String getModifier() {
+            return modifier;
+        }
+    }
+
+    public enum Static {
+        STATIC("static"), NO("");
+
+        private final String modifier;
+
+        Static(String modifier) {
+            this.modifier = modifier;
+        }
+
+        public String getModifier() {
+            return modifier;
+        }
+    }
+
+    public enum CodeChunk {
+        CONSTRUCTOR("#MODIFIER# A() {}"),
+        METHOD("#MODIFIER# int method() { return 10; }"),
+        FIELD("#MODIFIER# int field = 10;"),
+        INNER_CLASS("#MODIFIER# class Inner {}"),
+        INNER_INTERFACE("#MODIFIER# interface Inner {}");
+
+        private final String code;
+
+        CodeChunk(String code) {
+            this.code = code;
+        }
+
+        public String generateSource(AccessModifier accessModifier, Static isStatic) {
+            return code.replace("#MODIFIER#", accessModifier.getModifier() + " " + isStatic.getModifier());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ClassPathTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for EvalState#addToClasspath
+ * @library /tools/lib
+ * @build KullaTesting TestingInputStream ToolBox Compiler
+ * @run testng ClassPathTest
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.testng.annotations.Test;
+
+@Test
+public class ClassPathTest extends KullaTesting {
+
+    private final Compiler compiler = new Compiler();
+    private final Path outDir = Paths.get("class_path_test");
+
+    public void testDirectory() {
+        compiler.compile(outDir, "package pkg; public class TestDirectory { }");
+        assertDeclareFail("import pkg.TestDirectory;", "compiler.err.doesnt.exist");
+        assertDeclareFail("new pkg.TestDirectory();", "compiler.err.doesnt.exist");
+        addToClasspath(compiler.getPath(outDir));
+        assertEval("new pkg.TestDirectory();");
+    }
+
+    public void testJar() {
+        compiler.compile(outDir, "package pkg; public class TestJar { }");
+        String jarName = "test.jar";
+        compiler.jar(outDir, jarName, "pkg/TestJar.class");
+        assertDeclareFail("import pkg.TestJar;", "compiler.err.doesnt.exist");
+        assertDeclareFail("new pkg.TestJar();", "compiler.err.doesnt.exist");
+        addToClasspath(compiler.getPath(outDir).resolve(jarName));
+        assertEval("new pkg.TestJar();");
+    }
+
+    public void testAmbiguousDirectory() {
+        Path p1 = outDir.resolve("dir1");
+        compiler.compile(p1,
+                "package p; public class TestAmbiguous {\n" +
+                "   public String toString() {\n" +
+                "       return \"first\";" +
+                "   }\n" +
+                "}");
+        addToClasspath(compiler.getPath(p1));
+        Path p2 = outDir.resolve("dir2");
+        compiler.compile(p2,
+                "package p; public class TestAmbiguous {\n" +
+                "   public String toString() {\n" +
+                "       return \"second\";" +
+                "   }\n" +
+                "}");
+        addToClasspath(compiler.getPath(p2));
+        assertEval("new p.TestAmbiguous();", "first");
+    }
+
+    public void testAmbiguousJar() {
+        Path p1 = outDir.resolve("dir1");
+        compiler.compile(p1,
+                "package p; public class TestAmbiguous {\n" +
+                "   public String toString() {\n" +
+                "       return \"first\";" +
+                "   }\n" +
+                "}");
+        String jarName = "test.jar";
+        compiler.jar(p1, jarName, "p/TestAmbiguous.class");
+        addToClasspath(compiler.getPath(p1.resolve(jarName)));
+        Path p2 = outDir.resolve("dir2");
+        compiler.compile(p2,
+                "package p; public class TestAmbiguous {\n" +
+                "   public String toString() {\n" +
+                "       return \"second\";" +
+                "   }\n" +
+                "}");
+        addToClasspath(compiler.getPath(p2));
+        assertEval("new p.TestAmbiguous();", "first");
+    }
+
+    public void testEmptyClassPath() {
+        addToClasspath("");
+        assertEval("new java.util.ArrayList<String>();");
+    }
+
+    public void testUnknown() {
+        addToClasspath(compiler.getPath(outDir.resolve("UNKNOWN")));
+        assertDeclareFail("new Unknown();", "compiler.err.cant.resolve.location");
+        addToClasspath(compiler.getPath(outDir.resolve("UNKNOWN.jar")));
+        assertDeclareFail("new Unknown();", "compiler.err.cant.resolve.location");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ClassesTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for EvaluationState.classes
+ * @build KullaTesting TestingInputStream ExpectedDiagnostic
+ * @run testng ClassesTest
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.tools.Diagnostic;
+
+import jdk.jshell.Snippet;
+import jdk.jshell.TypeDeclSnippet;
+import jdk.jshell.VarSnippet;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import jdk.jshell.Diag;
+import static jdk.jshell.Snippet.Status.VALID;
+import static jdk.jshell.Snippet.Status.RECOVERABLE_NOT_DEFINED;
+import static jdk.jshell.Snippet.Status.DROPPED;
+import static jdk.jshell.Snippet.Status.REJECTED;
+import static jdk.jshell.Snippet.SubKind.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static jdk.jshell.Snippet.Status.OVERWRITTEN;
+
+@Test
+public class ClassesTest extends KullaTesting {
+
+    public void noClasses() {
+        assertNumberOfActiveClasses(0);
+    }
+
+    public void testSignature1() {
+        TypeDeclSnippet c1 = classKey(assertEval("class A extends B {}", added(RECOVERABLE_NOT_DEFINED)));
+        assertTypeDeclSnippet(c1, "A", RECOVERABLE_NOT_DEFINED, CLASS_SUBKIND, 1, 0);
+        TypeDeclSnippet c2 = classKey(assertEval("@interface A { Class<B> f() default B.class; }",
+                ste(MAIN_SNIPPET, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, null),
+                ste(c1, RECOVERABLE_NOT_DEFINED, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertTypeDeclSnippet(c2, "A", RECOVERABLE_NOT_DEFINED, ANNOTATION_TYPE_SUBKIND, 1, 0);
+        TypeDeclSnippet c3 = classKey(assertEval("enum A {; private A(B b) {} }",
+                ste(MAIN_SNIPPET, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, null),
+                ste(c2, RECOVERABLE_NOT_DEFINED, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertTypeDeclSnippet(c3, "A", RECOVERABLE_NOT_DEFINED, ENUM_SUBKIND, 1, 0);
+        TypeDeclSnippet c4 = classKey(assertEval("interface A extends B {}",
+                ste(MAIN_SNIPPET, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, null),
+                ste(c3, RECOVERABLE_NOT_DEFINED, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertTypeDeclSnippet(c4, "A", RECOVERABLE_NOT_DEFINED, INTERFACE_SUBKIND, 1, 0);
+        TypeDeclSnippet c5 = classKey(assertEval("class A { void f(B b) {} }",
+                ste(MAIN_SNIPPET, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, null),
+                ste(c4, RECOVERABLE_NOT_DEFINED, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertTypeDeclSnippet(c5, "A", RECOVERABLE_NOT_DEFINED, CLASS_SUBKIND, 1, 0);
+    }
+
+    public void testSignature2() {
+        TypeDeclSnippet c1 = (TypeDeclSnippet) assertDeclareFail("class A { void f() { return g(); } }", "compiler.err.prob.found.req");
+        assertTypeDeclSnippet(c1, "A", REJECTED, CLASS_SUBKIND, 0, 2);
+        TypeDeclSnippet c2 = classKey(assertEval("class A { int f() { return g(); } }",
+                ste(c1, REJECTED, RECOVERABLE_NOT_DEFINED, false, null)));
+        assertTypeDeclSnippet(c2, "A", RECOVERABLE_NOT_DEFINED, CLASS_SUBKIND, 1, 0);
+        assertDrop(c2,
+                ste(c2, RECOVERABLE_NOT_DEFINED, DROPPED, false, null));
+    }
+
+    public void classDeclaration() {
+        assertEval("class A { }");
+        assertClasses(clazz(KullaTesting.ClassType.CLASS, "A"));
+    }
+
+
+    public void interfaceDeclaration() {
+        assertEval("interface A { }");
+        assertClasses(clazz(KullaTesting.ClassType.INTERFACE, "A"));
+    }
+
+    public void annotationDeclaration() {
+        assertEval("@interface A { }");
+        assertClasses(clazz(KullaTesting.ClassType.ANNOTATION, "A"));
+    }
+
+    public void enumDeclaration() {
+        assertEval("enum A { }");
+        assertClasses(clazz(KullaTesting.ClassType.ENUM, "A"));
+    }
+
+    public void classesDeclaration() {
+        assertEval("interface A { }");
+        assertEval("class B implements A { }");
+        assertEval("interface C extends A { }");
+        assertEval("enum D implements C { }");
+        assertEval("@interface E { }");
+        assertClasses(
+                clazz(KullaTesting.ClassType.INTERFACE, "A"),
+                clazz(KullaTesting.ClassType.CLASS, "B"),
+                clazz(KullaTesting.ClassType.INTERFACE, "C"),
+                clazz(KullaTesting.ClassType.ENUM, "D"),
+                clazz(KullaTesting.ClassType.ANNOTATION, "E"));
+        assertActiveKeys();
+    }
+
+    public void classesRedeclaration1() {
+        Snippet a = classKey(assertEval("class A { }"));
+        Snippet b = classKey(assertEval("interface B { }"));
+        assertClasses(clazz(KullaTesting.ClassType.CLASS, "A"), clazz(KullaTesting.ClassType.INTERFACE, "B"));
+        assertActiveKeys();
+
+        assertEval("interface A { }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertClasses(clazz(KullaTesting.ClassType.INTERFACE, "A"),
+                clazz(KullaTesting.ClassType.INTERFACE, "B"));
+        assertActiveKeys();
+
+        assertEval("interface B { } //again",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(b, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertClasses(clazz(KullaTesting.ClassType.INTERFACE, "A"),
+                clazz(KullaTesting.ClassType.INTERFACE, "B"));
+        assertActiveKeys();
+    }
+
+    public void classesRedeclaration2() {
+        assertEval("class A { }");
+        assertClasses(clazz(KullaTesting.ClassType.CLASS, "A"));
+        assertActiveKeys();
+
+        Snippet b = classKey(assertEval("class B extends A { }"));
+        assertClasses(clazz(KullaTesting.ClassType.CLASS, "A"),
+                clazz(KullaTesting.ClassType.CLASS, "B"));
+        assertActiveKeys();
+
+        Snippet c = classKey(assertEval("class C extends B { }"));
+        assertClasses(clazz(KullaTesting.ClassType.CLASS, "A"),
+                clazz(KullaTesting.ClassType.CLASS, "B"), clazz(KullaTesting.ClassType.CLASS, "C"));
+        assertActiveKeys();
+
+        assertEval("interface B { }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(b, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(c, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+        assertClasses(clazz(KullaTesting.ClassType.CLASS, "A"),
+                clazz(KullaTesting.ClassType.INTERFACE, "B"), clazz(KullaTesting.ClassType.CLASS, "C"));
+        assertEval("new C();",
+                DiagCheck.DIAG_ERROR,
+                DiagCheck.DIAG_ERROR,
+                added(REJECTED));
+        assertActiveKeys();
+    }
+
+    public void classesCyclic1() {
+        Snippet b = classKey(assertEval("class B extends A { }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        Snippet a = classKey(assertEval("class A extends B { }", DiagCheck.DIAG_IGNORE, DiagCheck.DIAG_IGNORE,
+                added(RECOVERABLE_NOT_DEFINED),
+                ste(b, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, MAIN_SNIPPET)));
+        /***
+        assertDeclareFail("class A extends B { }", "****",
+                added(REJECTED),
+                ste(b, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, MAIN_SNIPPET));
+        ***/
+        // It is random which one it shows up in, but cyclic error should be there
+        List<Diag> diagsA = getState().diagnostics(a);
+        List<Diag> diagsB = getState().diagnostics(b);
+        List<Diag> diags;
+        if (diagsA.isEmpty()) {
+            diags = diagsB;
+        } else {
+            diags = diagsA;
+            assertTrue(diagsB.isEmpty());
+        }
+        assertEquals(diags.size(), 1, "Expected one error");
+        assertEquals(diags.get(0).getCode(), "compiler.err.cyclic.inheritance", "Expected cyclic inheritance error");
+        assertActiveKeys();
+    }
+
+    public void classesCyclic2() {
+        Snippet d = classKey(assertEval("class D extends E { }", added(RECOVERABLE_NOT_DEFINED)));
+        assertEval("class E { D d; }",
+                added(VALID),
+                ste(d, RECOVERABLE_NOT_DEFINED, VALID, true, MAIN_SNIPPET));
+        assertActiveKeys();
+    }
+
+    public void classesCyclic3() {
+        Snippet outer = classKey(assertEval("class Outer { class Inner extends Foo { } }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        Snippet foo = classKey(assertEval("class Foo { } ",
+                added(VALID),
+                ste(outer, RECOVERABLE_NOT_DEFINED, VALID, true, MAIN_SNIPPET)));
+        assertEval(" class Foo extends Outer { }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(foo, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(outer, VALID, VALID, true, MAIN_SNIPPET));
+        assertActiveKeys();
+    }
+
+    public void classesIgnoredModifiers() {
+        assertDeclareWarn1("public interface A { }",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 6, 0, -1, -1, Diagnostic.Kind.WARNING));
+        assertDeclareWarn1("static class B implements A { }",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 6, 0, -1, -1, Diagnostic.Kind.WARNING));
+        assertDeclareWarn1("final interface C extends A { }",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 5, 0, -1, -1, Diagnostic.Kind.WARNING));
+        assertDeclareWarn1("protected enum D implements C { }",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 9, 0, -1, -1, Diagnostic.Kind.WARNING));
+        assertActiveKeys();
+    }
+
+    public void ignoreModifierSpaceIssue() {
+        assertEval("interface I { void f(); } ");
+        // there should not be a space between 'I' and '{' to reproduce the failure
+        assertEval("class C implements I{ public void f() {}}");
+        assertClasses(clazz(KullaTesting.ClassType.CLASS, "C"), clazz(KullaTesting.ClassType.INTERFACE, "I"));
+        assertActiveKeys();
+    }
+
+    @DataProvider(name = "innerClasses")
+    public Object[][] innerClasses() {
+        List<Object[]> list = new ArrayList<>();
+        for (ClassType outerClassType : ClassType.values()) {
+            for (ClassType innerClassType : ClassType.values()) {
+                list.add(new Object[]{outerClassType, innerClassType});
+            }
+        }
+        return list.toArray(new Object[list.size()][]);
+    }
+
+    @Test(dataProvider = "innerClasses")
+    public void innerClasses(ClassType outerClassType, ClassType innerClassType) {
+        String source =
+                outerClassType + " A {" + (outerClassType == ClassType.ENUM ? ";" : "") +
+                innerClassType + " B { }" +
+                "}";
+        assertEval(source);
+        assertNumberOfActiveClasses(1);
+        assertActiveKeys();
+    }
+
+    public void testInnerClassesCrash() {
+        Snippet a = classKey(assertEval("class A { class B extends A {} }"));
+        Snippet a2 = classKey(assertEval("class A { interface I1 extends I2 {} interface I2 {} }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertEval("class A { A a = new A() {}; }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a2, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+    }
+
+    public void testInnerClassesCrash1() {
+        assertEval("class A { class B extends A {} B getB() { return new B();} }");
+        assertEquals(varKey(assertEval("A a = new A();")).name(), "a");
+        VarSnippet variableKey = varKey(assertEval("a.getB();"));
+        assertEquals(variableKey.typeName(), "A.B");
+    }
+
+    public void testInnerClassesCrash2() {
+        assertEval("class A { interface I1 extends I2 {} interface I2 {} I1 x; }");
+        assertEquals(varKey(assertEval("A a = new A();")).name(), "a");
+        VarSnippet variableKey = varKey(assertEval("a.x;"));
+        assertEquals(variableKey.typeName(), "A.I1");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/CommandCompletionTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test Command Completion
+ * @library /tools/lib
+ * @build ReplToolTesting TestingInputStream Compiler ToolBox
+ * @run testng CommandCompletionTest
+ */
+
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.testng.annotations.Test;
+
+@Test
+public class CommandCompletionTest extends ReplToolTesting {
+
+    public void testCommand() {
+        assertCompletion("/f|", false, "/feedback ");
+        assertCompletion("/deb|", false);
+        assertCompletion("/feedback v|", false, "verbose");
+        assertCompletion("/c|", false, "/classes ", "/classpath ");
+        assertCompletion("/h|", false, "/help ", "/history ");
+        assertCompletion("/feedback |", false,
+                "?", "concise", "default", "normal", "off", "verbose");
+    }
+
+    public void testList() {
+        assertCompletion("/l|", false, "/list ");
+        assertCompletion("/list |", false, "all");
+        assertCompletion("/list q|", false);
+    }
+
+    public void testDrop() {
+        assertCompletion("/d|", false, "/drop ");
+
+        test(false, new String[] {"-nostartup"},
+                a -> assertClass(a, "class cTest {}", "class", "cTest"),
+                a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"),
+                a -> assertVariable(a, "int", "fTest"),
+                a -> assertCompletion(a, "/drop |", false, "1", "2", "3", "cTest", "fTest", "mTest"),
+                a -> assertCompletion(a, "/drop f|", false, "fTest")
+        );
+    }
+
+    public void testEdit() {
+        assertCompletion("/e|", false, "/edit ", "/exit ");
+        assertCompletion("/ed|", false, "/edit ");
+
+        test(false, new String[]{"-nostartup"},
+                a -> assertClass(a, "class cTest {}", "class", "cTest"),
+                a -> assertMethod(a, "int mTest() { return 0; }", "()I", "mTest"),
+                a -> assertVariable(a, "int", "fTest"),
+                a -> assertCompletion(a, "/edit |", false, "1", "2", "3", "cTest", "fTest", "mTest"),
+                a -> assertCompletion(a, "/edit f|", false, "fTest")
+        );
+    }
+
+    public void testOpen() throws IOException {
+        Compiler compiler = new Compiler();
+        assertCompletion("/o|", false, "/open ");
+        List<String> p1 = listFiles(Paths.get(""));
+        FileSystems.getDefault().getRootDirectories().forEach(s -> p1.add(s.toString()));
+        Collections.sort(p1);
+        assertCompletion("/open |", false, p1.toArray(new String[p1.size()]));
+        Path classDir = compiler.getClassDir();
+        List<String> p2 = listFiles(classDir);
+        assertCompletion("/open " + classDir + "/|", false, p2.toArray(new String[p2.size()]));
+    }
+
+    public void testSave() throws IOException {
+        Compiler compiler = new Compiler();
+        assertCompletion("/s|", false, "/save ", "/savestart ", "/seteditor ", "/setstart ");
+        List<String> p1 = listFiles(Paths.get(""));
+        Collections.addAll(p1, "all ", "history ");
+        FileSystems.getDefault().getRootDirectories().forEach(s -> p1.add(s.toString()));
+        Collections.sort(p1);
+        assertCompletion("/save |", false, p1.toArray(new String[p1.size()]));
+        Path classDir = compiler.getClassDir();
+        List<String> p2 = listFiles(classDir);
+        assertCompletion("/save " + classDir + "/|",
+                false, p2.toArray(new String[p2.size()]));
+        assertCompletion("/save all " + classDir + "/|",
+                false, p2.toArray(new String[p2.size()]));
+    }
+
+    public void testClassPath() throws IOException {
+        assertCompletion("/classp|", false, "/classpath ");
+        Compiler compiler = new Compiler();
+        Path outDir = compiler.getPath("testClasspathCompletion");
+        Files.createDirectories(outDir);
+        Files.createDirectories(outDir.resolve("dir"));
+        createIfNeeded(outDir.resolve("test.jar"));
+        createIfNeeded(outDir.resolve("test.zip"));
+        compiler.compile(outDir, "package pkg; public class A { public String toString() { return \"A\"; } }");
+        String jarName = "test.jar";
+        compiler.jar(outDir, jarName, "pkg/A.class");
+        compiler.getPath(outDir).resolve(jarName);
+        List<String> paths = listFiles(outDir, CLASSPATH_FILTER);
+        assertCompletion("/classpath " + outDir + "/|", false, paths.toArray(new String[paths.size()]));
+    }
+
+    public void testUserHome() throws IOException {
+        List<String> completions;
+        Path home = Paths.get(System.getProperty("user.home"));
+        try (Stream<Path> content = Files.list(home)) {
+            completions = content.filter(CLASSPATH_FILTER)
+                                 .map(file -> file.getFileName().toString() + (Files.isDirectory(file) ? "/" : ""))
+                                 .sorted()
+                                 .collect(Collectors.toList());
+        }
+        assertCompletion("/classpath ~/|", false, completions.toArray(new String[completions.size()]));
+    }
+
+    private void createIfNeeded(Path file) throws IOException {
+        if (!Files.exists(file))
+            Files.createFile(file);
+    }
+    private List<String> listFiles(Path path) throws IOException {
+        return listFiles(path, ACCEPT_ALL);
+    }
+
+    private List<String> listFiles(Path path, Predicate<? super Path> filter) throws IOException {
+        try (Stream<Path> stream = Files.list(path)) {
+            return stream.filter(filter)
+                         .map(p -> p.getFileName().toString() + (Files.isDirectory(p) ? "/" : ""))
+                         .sorted()
+                         .collect(Collectors.toList());
+        }
+    }
+
+    private static final Predicate<? super Path> ACCEPT_ALL =
+            (file) -> !file.endsWith(".") && !file.endsWith("..");
+
+    private static final Predicate<? super Path> CLASSPATH_FILTER =
+            (file) -> ACCEPT_ALL.test(file) &&
+                    (Files.isDirectory(file) ||
+                     file.getFileName().toString().endsWith(".jar") ||
+                     file.getFileName().toString().endsWith(".zip"));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/Compiler.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+public class Compiler {
+
+    private final ToolBox tb = new ToolBox();
+
+    public Path getClassDir() {
+        String classes = ToolBox.testClasses;
+        if (classes == null) {
+            return Paths.get("build");
+        } else {
+            return Paths.get(classes);
+        }
+    }
+
+    public Path getPath(String path) {
+        return getPath(Paths.get(path));
+    }
+
+    public Path getPath(Path path) {
+        return getClassDir().resolve(path);
+    }
+
+    public void compile(String...sources) {
+        compile(Paths.get("."), sources);
+    }
+
+    public void compile(Path directory, String...sources) {
+        Path classDir = getClassDir();
+        tb.new JavacTask()
+                .options("-d", classDir.resolve(directory).toString())
+                .sources(sources)
+                .run();
+    }
+
+    public void jar(String jarName, String...files) {
+        jar(Paths.get("."), jarName, files);
+    }
+
+    public void jar(Path directory, String jarName, String...files) {
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        Path classDirPath = getClassDir();
+        Path baseDir = classDirPath.resolve(directory);
+        Path jarPath = baseDir.resolve(jarName);
+        tb.new JarTask(jarPath.toString())
+                .manifest(manifest)
+                .baseDir(baseDir.toString())
+                .files(files).run();
+    }
+
+    public void writeToFile(Path path, String...sources) {
+        try {
+            if (path.getParent() != null) {
+                Files.createDirectories(path.getParent());
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        try (BufferedWriter writer = Files.newBufferedWriter(path)) {
+            for (String source : sources) {
+                writer.append(source);
+                writer.append('\n');
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/CompletenessStressTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.lang.model.element.Modifier;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.BreakTree;
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ContinueTree;
+import com.sun.source.tree.DoWhileLoopTree;
+import com.sun.source.tree.ExpressionStatementTree;
+import com.sun.source.tree.ForLoopTree;
+import com.sun.source.tree.IfTree;
+import com.sun.source.tree.ImportTree;
+import com.sun.source.tree.LabeledStatementTree;
+import com.sun.source.tree.LineMap;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ReturnTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.SwitchTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.WhileLoopTree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacTaskImpl;
+
+import jdk.jshell.SourceCodeAnalysis;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static java.lang.Integer.max;
+import static java.lang.Integer.min;
+import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
+
+public class CompletenessStressTest extends KullaTesting {
+    public final static String JDK_ROOT_SRC_PROP = "jdk.root.src";
+    public final static String JDK_ROOT_SRC;
+
+    static {
+        JDK_ROOT_SRC = System.getProperty(JDK_ROOT_SRC_PROP);
+    }
+
+    public File getSourceFile(String fileName) {
+        for (File dir : getDirectoriesToTest()) {
+            File file = new File(dir, fileName);
+            if (file.exists()) {
+                return file;
+            }
+        }
+        throw new AssertionError("File not found: " + fileName);
+    }
+
+    public File[] getDirectoriesToTest() {
+        return new File[]{
+                new File(JDK_ROOT_SRC, "nashorn/src"),
+                new File(JDK_ROOT_SRC, "langtools/src"),
+                new File(JDK_ROOT_SRC, "jaxp/src"),
+                new File(JDK_ROOT_SRC, "jaxws/src"),
+                new File(JDK_ROOT_SRC, "jdk/src"),
+                new File(JDK_ROOT_SRC, "corba/src")
+        };
+    }
+
+    @DataProvider(name = "crawler")
+    public Object[][] dataProvider() throws IOException {
+        File[] srcDirs = getDirectoriesToTest();
+        List<String[]> list = new ArrayList<>();
+        for (File srcDir : srcDirs) {
+            String srcDirName = srcDir.getAbsolutePath();
+            // this is just to obtain pretty test names for testng tests
+            List<String[]> a = Files.walk(Paths.get(srcDirName))
+                    .map(Path::toFile)
+                    .map(File::getAbsolutePath)
+                    .filter(n -> n.endsWith(".java"))
+                    .map(n -> n.replace(srcDirName, ""))
+                    .map(n -> new String[]{n})
+                    .collect(Collectors.toList());
+            if (a.isEmpty()) {
+                throw new AssertionError("Java sources have not been found in directory: " + srcDirName);
+            }
+            list.addAll(a);
+        }
+        return list.toArray(new String[list.size()][]);
+    }
+
+    @Test(dataProvider = "crawler")
+    public void testFile(String fileName) throws IOException {
+        File file = getSourceFile(fileName);
+        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        final StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
+        boolean success = true;
+        StringWriter writer = new StringWriter();
+        writer.write("Testing : " + file.toString() + "\n");
+        String text = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
+        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file);
+        JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fileManager, null, null, null, compilationUnits);
+        Iterable<? extends CompilationUnitTree> asts = task.parse();
+        Trees trees = Trees.instance(task);
+        SourcePositions sp = trees.getSourcePositions();
+
+        for (CompilationUnitTree cut : asts) {
+            for (ImportTree imp : cut.getImports()) {
+                success &= testStatement(writer, sp, text, cut, imp);
+            }
+            for (Tree decl : cut.getTypeDecls()) {
+                success &= testStatement(writer, sp, text, cut, decl);
+                if (decl instanceof ClassTree) {
+                    ClassTree ct = (ClassTree) decl;
+                    for (Tree mem : ct.getMembers()) {
+                        if (mem instanceof MethodTree) {
+                            MethodTree mt = (MethodTree) mem;
+                            BlockTree bt = mt.getBody();
+                            // No abstract methods or constructors
+                            if (bt != null && mt.getReturnType() != null) {
+                                // The modifiers synchronized, abstract, and default are not allowed on
+                                // top-level declarations and are errors.
+                                Set<Modifier> modifier = mt.getModifiers().getFlags();
+                                if (!modifier.contains(Modifier.ABSTRACT)
+                                        && !modifier.contains(Modifier.SYNCHRONIZED)
+                                        && !modifier.contains(Modifier.DEFAULT)) {
+                                    success &= testStatement(writer, sp, text, cut, mt);
+                                }
+                                testBlock(writer, sp, text, cut, bt);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        fileManager.close();
+        if (!success) {
+            throw new AssertionError(writer.toString());
+        }
+    }
+
+    private boolean isLegal(StatementTree st) {
+        return !(st instanceof ReturnTree) &&
+                !(st instanceof ContinueTree) && !(st instanceof BreakTree);
+    }
+
+    private boolean testBranch(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, StatementTree statementTree) {
+        if (statementTree instanceof BlockTree) {
+            return testBlock(writer, sp, text, cut, (BlockTree) statementTree);
+        } else if (isLegal(statementTree)) {
+            return testStatement(writer, sp, text, cut, statementTree);
+        }
+        return true;
+    }
+
+    private boolean testBlock(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, BlockTree blockTree) {
+        boolean success = true;
+        for (StatementTree st : blockTree.getStatements()) {
+            if (isLegal(st)) {
+                success &= testStatement(writer, sp, text, cut, st);
+            }
+            if (st instanceof IfTree) {
+                IfTree ifTree = (IfTree) st;
+                success &= testBranch(writer, sp, text, cut, ifTree.getThenStatement());
+                success &= testBranch(writer, sp, text, cut, ifTree.getElseStatement());
+            } else if (st instanceof WhileLoopTree) {
+                WhileLoopTree whileLoopTree = (WhileLoopTree) st;
+                success &= testBranch(writer, sp, text, cut, whileLoopTree.getStatement());
+            } else if (st instanceof DoWhileLoopTree) {
+                DoWhileLoopTree doWhileLoopTree = (DoWhileLoopTree) st;
+                success &= testBranch(writer, sp, text, cut, doWhileLoopTree.getStatement());
+            } else if (st instanceof ForLoopTree) {
+                ForLoopTree forLoopTree = (ForLoopTree) st;
+                success &= testBranch(writer, sp, text, cut, forLoopTree.getStatement());
+            } else if (st instanceof LabeledStatementTree) {
+                LabeledStatementTree labelTree = (LabeledStatementTree) st;
+                success &= testBranch(writer, sp, text, cut, labelTree.getStatement());
+            } else if (st instanceof SwitchTree) {
+                SwitchTree switchTree = (SwitchTree) st;
+                for (CaseTree caseTree : switchTree.getCases()) {
+                    for (StatementTree statementTree : caseTree.getStatements()) {
+                        success &= testBranch(writer, sp, text, cut, statementTree);
+                    }
+                }
+            }
+        }
+        return success;
+    }
+
+    private boolean testStatement(StringWriter writer, SourcePositions sp, String text, CompilationUnitTree cut, Tree statement) {
+        if (statement == null) {
+            return true;
+        }
+        int start = (int) sp.getStartPosition(cut, statement);
+        int end = (int) sp.getEndPosition(cut, statement);
+        char ch = text.charAt(end - 1);
+        SourceCodeAnalysis.Completeness expected = COMPLETE;
+        LineMap lineMap = cut.getLineMap();
+        int row = (int) lineMap.getLineNumber(start);
+        int column = (int) lineMap.getColumnNumber(start);
+        switch (ch) {
+            case ',':
+            case ';':
+                expected = (statement instanceof ExpressionStatementTree)
+                        ? COMPLETE
+                        : COMPLETE_WITH_SEMI;
+                --end;
+                break;
+            case '}':
+                break;
+            default:
+                writer.write(String.format("Unexpected end: row %d, column %d: '%c' -- %s\n",
+                        row, column, ch, text.substring(start, end)));
+                return true;
+        }
+        String unit = text.substring(start, end);
+        SourceCodeAnalysis.CompletionInfo ci = getAnalysis().analyzeCompletion(unit);
+        if (ci.completeness != expected) {
+            if (expected == COMPLETE_WITH_SEMI && (ci.completeness == CONSIDERED_INCOMPLETE || ci.completeness == EMPTY)) {
+                writer.write(String.format("Empty statement: row %d, column %d: -- %s\n",
+                        start, end, unit));
+            } else {
+                String oops = unit.substring(max(0, ci.unitEndPos - 10), ci.unitEndPos) + "|||" +
+                        unit.substring(ci.unitEndPos, min(unit.length(), ci.unitEndPos + 10));
+                writer.write(String.format("Expected %s got %s: '%s'  row %d, column %d: -- %s\n",
+                        expected, ci.completeness, oops, row, column, unit));
+                return false;
+            }
+        }
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/CompletenessTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test SourceCodeAnalysis
+ * @build KullaTesting TestingInputStream
+ * @run testng CompletenessTest
+ */
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.testng.annotations.Test;
+import jdk.jshell.SourceCodeAnalysis.Completeness;
+
+import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
+
+@Test
+public class CompletenessTest extends KullaTesting {
+
+    // Add complete units that end with semicolon to complete_with_semi (without
+    // the semicolon).  Both cases will be tested.
+    static final String[] complete = new String[] {
+        "{ x= 4; }",
+        "int mm(int x) {kll}",
+        "if (t) { ddd; }",
+        "for (int i = 0; i < lines.length(); ++i) { foo }",
+        "while (ct == null) { switch (current.kind) { case EOF: { } } }",
+        "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) { new CT(UNMATCHED, current, \"Unmatched \" + unmatched); }",
+        "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM); }",
+        "List<T> f() { return null; }",
+        "List<?> f() { return null; }",
+        "List<? extends Object> f() { return null; }",
+        "Map<? extends Object, ? super Object> f() { return null; }",
+        "class C { int z; }",
+        "synchronized (r) { f(); }",
+        "try { } catch (Exception ex) { }",
+        "try { } catch (Exception ex) { } finally { }",
+        "try { } finally { }",
+        "try (java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)) { }",
+        "foo: while (true) { printf(\"Innn\"); break foo; }",
+        ";",
+    };
+
+    static final String[] expression = new String[] {
+        "test",
+        "x + y",
+        "x + y ++",
+        "p = 9",
+        "match(BRACKETS, TokenKind.LBRACKET)",
+        "new C()",
+        "new C() { public String toString() { return \"Hi\"; } }",
+        "new int[]",
+        "new int[] {1, 2,3}",
+        "new Foo() {}",
+        "i >= 0 && Character.isWhitespace(s.charAt(i))",
+    };
+
+    static final String[] complete_with_semi = new String[] {
+        "int mm",
+        "if (t) ddd",
+        "int p = 9",
+        "int p",
+        "Deque<Token> stack = new ArrayDeque<>()",
+        "final Deque<Token> stack = new ArrayDeque<>()",
+        "java.util.Scanner input = new java.util.Scanner(System.in)",
+        "java.util.Scanner input = new java.util.Scanner(System.in) { }",
+        "int j = -i",
+        "String[] a = { \"AAA\" }",
+        "assert true",
+        "int path[]",
+        "int path[][]",
+        "int path[][] = new int[22][]",
+        "int path[] = new int[22]",
+        "int path[] = new int[] {1, 2, 3}",
+        "int[] path",
+        "int path[] = new int[22]",
+        "int path[][] = new int[22][]",
+        "for (Object o : a) System.out.println(\"Yep\")",
+        "while (os == null) System.out.println(\"Yep\")",
+        "do f(); while (t)",
+        "if (os == null) System.out.println(\"Yep\")",
+        "if (t) if (!t) System.out.println(123)",
+        "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else break",
+        "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else continue",
+        "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else return",
+        "throw ex",
+        "C c = new C()",
+        "java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)",
+        "BufferedReader br = new BufferedReader(new FileReader(path))",
+        "bar: g()",
+        "baz: while (true) if (t()) printf('-'); else break baz",
+    };
+
+    static final String[] considered_incomplete = new String[] {
+        "if (t)",
+        "if (t) { } else",
+        "if (t) if (!t)",
+        "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE))",
+        "for (int i = 0; i < 10; ++i)",
+        "while (os == null)",
+    };
+
+    static final String[] definitely_incomplete = new String[] {
+        "int mm(",
+        "int mm(int x",
+        "int mm(int x)",
+        "int mm(int x) {",
+        "int mm(int x) {kll",
+        "if",
+        "if (",
+        "if (t",
+        "if (t) {",
+        "if (t) { ddd",
+        "if (t) { ddd;",
+        "if (t) if (",
+        "if (stack.isEmpty()) {",
+        "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) {",
+        "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) { new CT(UNMATCHED, current, \"Unmatched \" + unmatched);",
+        "x +",
+        "int",
+        "for (int i = 0; i < lines.length(); ++i) {",
+        "new",
+        "new C(",
+        "new int[",
+        "new int[] {1, 2,3",
+        "new int[] {",
+        "while (ct == null) {",
+        "while (ct == null) { switch (current.kind) {",
+        "while (ct == null) { switch (current.kind) { case EOF: {",
+        "while (ct == null) { switch (current.kind) { case EOF: { } }",
+        "enum TK {",
+        "enum TK { EOF(TokenKind.EOF, 0),",
+        "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM)",
+        "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM); ",
+    };
+
+    static final String[] unknown = new String[] {
+        "new ;"
+    };
+
+    static final Map<Completeness, String[]> statusToCases = new HashMap<>();
+    static {
+        statusToCases.put(COMPLETE, complete);
+        statusToCases.put(COMPLETE_WITH_SEMI, complete_with_semi);
+        statusToCases.put(CONSIDERED_INCOMPLETE, considered_incomplete);
+        statusToCases.put(DEFINITELY_INCOMPLETE, definitely_incomplete);
+    }
+
+    private void assertStatus(String input, Completeness status, String source) {
+        String augSrc;
+        switch (status) {
+            case COMPLETE_WITH_SEMI:
+                augSrc = source + ";";
+                break;
+
+            case DEFINITELY_INCOMPLETE:
+            case CONSIDERED_INCOMPLETE:
+                augSrc = null;
+                break;
+
+            case EMPTY:
+            case COMPLETE:
+            case UNKNOWN:
+                augSrc = source;
+                break;
+
+            default:
+                throw new AssertionError();
+        }
+        assertAnalyze(input, status, augSrc);
+    }
+
+    private void assertStatus(String[] ins, Completeness status) {
+        for (String input : ins) {
+            assertStatus(input, status, input);
+        }
+    }
+
+    public void test_complete() {
+        assertStatus(complete, COMPLETE);
+    }
+
+    public void test_expression() {
+        assertStatus(expression, COMPLETE);
+    }
+
+    public void test_complete_with_semi() {
+        assertStatus(complete_with_semi, COMPLETE_WITH_SEMI);
+    }
+
+    public void test_considered_incomplete() {
+        assertStatus(considered_incomplete, CONSIDERED_INCOMPLETE);
+    }
+
+    public void test_definitely_incomplete() {
+        assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE);
+    }
+
+    public void test_unknown() {
+        assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE);
+    }
+
+    public void testCompleted_complete_with_semi() {
+        for (String in : complete_with_semi) {
+            String input = in + ";";
+            assertStatus(input, COMPLETE, input);
+        }
+    }
+
+    public void testCompleted_expression_with_semi() {
+        for (String in : expression) {
+            String input = in + ";";
+            assertStatus(input, COMPLETE, input);
+        }
+    }
+
+    public void testCompleted_considered_incomplete() {
+        for (String in : considered_incomplete) {
+            String input = in + ";";
+            assertStatus(input, COMPLETE, input);
+        }
+    }
+
+    private void assertSourceByStatus(String first) {
+        for (Map.Entry<Completeness, String[]> e : statusToCases.entrySet()) {
+            for (String in : e.getValue()) {
+                String input = first + in;
+                assertAnalyze(input, COMPLETE, first, in, true);
+            }
+        }
+    }
+
+    public void testCompleteSource_complete() {
+        for (String input : complete) {
+            assertSourceByStatus(input);
+        }
+    }
+
+    public void testCompleteSource_complete_with_semi() {
+        for (String in : complete_with_semi) {
+            String input = in + ";";
+            assertSourceByStatus(input);
+        }
+    }
+
+    public void testCompleteSource_expression() {
+        for (String in : expression) {
+            String input = in + ";";
+            assertSourceByStatus(input);
+        }
+    }
+
+    public void testCompleteSource_considered_incomplete() {
+        for (String in : considered_incomplete) {
+            String input = in + ";";
+            assertSourceByStatus(input);
+        }
+    }
+
+    public void testTrailingSlash() {
+        assertStatus("\"abc\\", UNKNOWN, "\"abc\\");
+    }
+
+    public void testMiscSource() {
+        assertStatus("if (t) if ", DEFINITELY_INCOMPLETE, "if (t) if"); //Bug
+        assertStatus("int m() {} dfd", COMPLETE, "int m() {}");
+        assertStatus("int p = ", DEFINITELY_INCOMPLETE, "int p ="); //Bug
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/CompletionSuggestionTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,524 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test Completion
+ * @library /tools/lib
+ * @build KullaTesting TestingInputStream ToolBox Compiler
+ * @run testng CompletionSuggestionTest
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+
+import jdk.jshell.Snippet;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.VALID;
+import static jdk.jshell.Snippet.Status.OVERWRITTEN;
+
+@Test
+public class CompletionSuggestionTest extends KullaTesting {
+
+    public void testMemberExpr() {
+        assertEval("class Test { static void test() { } }");
+        assertCompletion("Test.t|", "test()");
+        assertEval("Test ccTestInstance = new Test();");
+        assertCompletion("ccTestInstance.t|", "toString()");
+        assertCompletion(" ccTe|", "ccTestInstance");
+        assertCompletion("String value = ccTestInstance.to|", "toString()");
+        assertCompletion("java.util.Coll|", "Collection", "Collections");
+        assertCompletion("String.cla|", "class");
+        assertCompletion("boolean.cla|", "class");
+        assertCompletion("byte.cla|", "class");
+        assertCompletion("short.cla|", "class");
+        assertCompletion("char.cla|", "class");
+        assertCompletion("int.cla|", "class");
+        assertCompletion("float.cla|", "class");
+        assertCompletion("long.cla|", "class");
+        assertCompletion("double.cla|", "class");
+        assertCompletion("void.cla|", "class");
+        assertCompletion("Object[].|", "class");
+        assertCompletion("int[].|", "class");
+        assertEval("Object[] ao = null;");
+        assertCompletion("int i = ao.|", "length");
+        assertEval("int[] ai = null;");
+        assertCompletion("int i = ai.|", "length");
+        assertCompletionIncludesExcludes("\"\".|",
+                new HashSet<>(Collections.emptyList()),
+                new HashSet<>(Arrays.asList("String(")));
+        assertEval("double d = 0;");
+        assertEval("void m() {}");
+        assertCompletionIncludesExcludes("d.|",
+                new HashSet<>(Collections.emptyList()),
+                new HashSet<>(Arrays.asList("class")));
+        assertCompletionIncludesExcludes("m().|",
+                new HashSet<>(Collections.emptyList()),
+                new HashSet<>(Arrays.asList("class")));
+        assertEval("class C {class D {} static class E {} enum F {} interface H {} void method() {} int number;}");
+        assertCompletionIncludesExcludes("C.|",
+                new HashSet<>(Arrays.asList("D", "E", "F", "H", "class")),
+                new HashSet<>(Arrays.asList("method()", "number")));
+        assertCompletionIncludesExcludes("new C().|",
+                new HashSet<>(Arrays.asList("method()", "number")),
+                new HashSet<>(Arrays.asList("D", "E", "F", "H", "class")));
+        assertCompletionIncludesExcludes("new C() {}.|",
+                new HashSet<>(Arrays.asList("method()", "number")),
+                new HashSet<>(Arrays.asList("D", "E", "F", "H", "class")));
+    }
+
+    public void testStartOfExpression() {
+        assertEval("int ccTest = 0;");
+        assertCompletion("System.err.println(cc|", "ccTest");
+        assertCompletion("for (int i = cc|", "ccTest");
+    }
+
+    public void testParameter() {
+        assertCompletion("class C{void method(int num){num|", "num");
+    }
+
+    public void testPrimitive() {
+        Set<String> primitives = new HashSet<>(Arrays.asList("boolean", "char", "byte", "short", "int", "long", "float", "double"));
+        Set<String> onlyVoid = new HashSet<>(Collections.singletonList("void"));
+        Set<String> primitivesOrVoid = new HashSet<>(primitives);
+        primitivesOrVoid.addAll(onlyVoid);
+
+        assertCompletionIncludesExcludes("|",
+                primitivesOrVoid,
+                new HashSet<>(Collections.emptyList()));
+        assertCompletionIncludesExcludes("int num = |",
+                primitivesOrVoid,
+                new HashSet<>(Collections.emptyList()));
+        assertCompletionIncludesExcludes("num = |",
+                primitivesOrVoid,
+                new HashSet<>(Collections.emptyList()));
+        assertCompletionIncludesExcludes("class C{void m() {|",
+                primitivesOrVoid,
+                new HashSet<>(Collections.emptyList()));
+        assertCompletionIncludesExcludes("void method(|",
+                primitives,
+                onlyVoid);
+        assertCompletionIncludesExcludes("void method(int num, |",
+                primitives,
+                onlyVoid);
+        assertCompletion("new java.util.ArrayList<doub|");
+        assertCompletion("class A extends doubl|");
+        assertCompletion("class A implements doubl|");
+        assertCompletion("interface A extends doubl|");
+        assertCompletion("enum A implements doubl|");
+        assertCompletion("class A<T extends doubl|");
+    }
+
+    public void testEmpty() {
+        assertCompletionIncludesExcludes("|",
+                new HashSet<>(Arrays.asList("Object", "Void")),
+                new HashSet<>(Arrays.asList("$REPL00DOESNOTMATTER")));
+        assertCompletionIncludesExcludes("V|",
+                new HashSet<>(Collections.singletonList("Void")),
+                new HashSet<>(Collections.singletonList("Object")));
+        assertCompletionIncludesExcludes("{ |",
+                new HashSet<>(Arrays.asList("Object", "Void")),
+                new HashSet<>(Arrays.asList("$REPL00DOESNOTMATTER")));
+    }
+
+    public void testSmartCompletion() {
+        assertEval("int ccTest1 = 0;");
+        assertEval("int ccTest2 = 0;");
+        assertEval("String ccTest3 = null;");
+        assertEval("void method(int i, String str) { }");
+        assertEval("void method(String str, int i) { }");
+        assertEval("java.util.List<String> list = null;");
+        assertCompletion("int ccTest4 = |", true, "ccTest1", "ccTest2");
+        assertCompletion("ccTest2 = |", true, "ccTest1", "ccTest2");
+        assertCompletion("int ccTest4 = ccTe|", "ccTest1", "ccTest2", "ccTest3");
+        assertCompletion("int ccTest4 = ccTest3.len|", true, "length()");
+        assertCompletion("method(|", true, "ccTest1", "ccTest2", "ccTest3");
+        assertCompletion("method(0, |", true, "ccTest3");
+        assertCompletion("list.add(|", true, "ccTest1", "ccTest2", "ccTest3");
+        assertCompletion("list.add(0, |", true, "ccTest3");
+        assertCompletion("new String(|", true, "ccTest3");
+        assertCompletion("new String(new char[0], |", true, "ccTest1", "ccTest2");
+        assertCompletionIncludesExcludes("new jav|", new HashSet<>(Arrays.asList("java", "javax")), Collections.emptySet());
+        assertCompletion("Class<String> clazz = String.c|", true, "class");
+
+        Snippet klass = classKey(assertEval("class Klass {void method(int n) {} private void method(String str) {}}"));
+        assertCompletion("new Klass().method(|", true, "ccTest1", "ccTest2");
+        Snippet klass2 = classKey(assertEval("class Klass {static void method(int n) {} void method(String str) {}}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(klass, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertCompletion("Klass.method(|", true, "ccTest1", "ccTest2");
+        assertEval("class Klass {Klass(int n) {} private Klass(String str) {}}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(klass2, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertCompletion("new Klass(|", true, "ccTest1", "ccTest2");
+    }
+
+    public void testSmartCompletionInOverriddenMethodInvocation() {
+        assertEval("int ccTest1 = 0;");
+        assertEval("int ccTest2 = 0;");
+        assertEval("String ccTest3 = null;");
+        assertCompletion("\"\".wait(|", true, "ccTest1", "ccTest2");
+        assertEval("class Base {void method(int n) {}}");
+        assertEval("class Extend extends Base {}");
+        assertCompletion("new Extend().method(|", true, "ccTest1", "ccTest2");
+    }
+
+    public void testSmartCompletionForBoxedType() {
+        assertEval("int ccTest1 = 0;");
+        assertEval("Integer ccTest2 = 0;");
+        assertEval("Object ccTest3 = null;");
+        assertEval("int method1(int n) {return n;}");
+        assertEval("Integer method2(Integer n) {return n;}");
+        assertEval("Object method3(Object o) {return o;}");
+        assertCompletion("int ccTest4 = |", true, "ccTest1", "ccTest2", "method1(", "method2(");
+        assertCompletion("Integer ccTest4 = |", true, "ccTest1", "ccTest2", "method1(", "method2(");
+        assertCompletion("Object ccTest4 = |", true, "ccTest1", "ccTest2", "ccTest3", "method1(", "method2(", "method3(");
+        assertCompletion("method1(|", true, "ccTest1", "ccTest2", "method1(", "method2(");
+        assertCompletion("method2(|", true, "ccTest1", "ccTest2", "method1(", "method2(");
+        assertCompletion("method3(|", true, "ccTest1", "ccTest2", "ccTest3", "method1(", "method2(", "method3(");
+    }
+
+    public void testNewClass() {
+        assertCompletion("String str = new Strin|", "String(", "StringBuffer(", "StringBuilder(", "StringIndexOutOfBoundsException(");
+        assertCompletion("String str = new java.lang.Strin|", "String(", "StringBuffer(", "StringBuilder(", "StringIndexOutOfBoundsException(");
+        assertCompletion("String str = new |", true, "String(");
+        assertCompletion("String str = new java.lang.|", true, "String(");
+        assertCompletion("throw new Strin|", true, "StringIndexOutOfBoundsException(");
+
+        assertEval("class A{class B{} class C {C(int n) {}} static class D {} interface I {}}");
+        assertEval("A a;");
+        assertCompletion("new A().new |", "B()", "C(");
+        assertCompletion("a.new |", "B()", "C(");
+        assertCompletion("new A.|", "D()");
+
+        assertEval("enum E{; class A {}}");
+        assertEval("interface I{; class A {}}");
+        assertCompletion("new E.|", "A()");
+        assertCompletion("new I.|", "A()");
+        assertCompletion("new String(I.A|", "A");
+    }
+
+    public void testFullyQualified() {
+        assertCompletion("Optional<String> opt = java.u|", "util");
+        assertCompletionIncludesExcludes("Optional<Strings> opt = java.util.O|", new HashSet<>(Collections.singletonList("Optional")), Collections.emptySet());
+
+        assertEval("void method(java.util.Optional<String> opt) {}");
+        assertCompletion("method(java.u|", "util");
+
+        assertCompletion("Object.notElement.|");
+        assertCompletion("Object o = com.su|", "sun");
+    }
+
+    public void testCheckAccessibility() {
+        assertCompletion("java.util.regex.Pattern.co|", "compile(");
+    }
+
+    public void testCompletePackages() {
+        assertCompletion("java.u|", "util");
+        assertCompletionIncludesExcludes("jav|", new HashSet<>(Arrays.asList("java", "javax")), Collections.emptySet());
+    }
+
+    public void testImports() {
+        assertCompletion("import java.u|", "util");
+        assertCompletionIncludesExcludes("import jav|", new HashSet<>(Arrays.asList("java", "javax")), Collections.emptySet());
+        assertCompletion("import static java.u|", "util");
+        assertCompletionIncludesExcludes("import static jav|", new HashSet<>(Arrays.asList("java", "javax")), Collections.emptySet());
+        assertCompletion("import static java.lang.Boolean.g|", "getBoolean");
+        assertCompletion("import java.util.*|");
+        assertCompletionIncludesExcludes("import java.lang.String.|",
+                Collections.emptySet(),
+                new HashSet<>(Arrays.asList("CASE_INSENSITIVE_ORDER", "copyValueOf", "format", "join", "valueOf", "class", "length")));
+        assertCompletionIncludesExcludes("import static java.lang.String.|",
+                new HashSet<>(Arrays.asList("CASE_INSENSITIVE_ORDER", "copyValueOf", "format", "join", "valueOf")),
+                new HashSet<>(Arrays.asList("class", "length")));
+        assertCompletionIncludesExcludes("import java.util.Map.|",
+                new HashSet<>(Arrays.asList("Entry")),
+                new HashSet<>(Arrays.asList("class")));
+    }
+
+    public void testBrokenClassFile() throws Exception {
+        Compiler compiler = new Compiler();
+        Path testOutDir = Paths.get("CompletionTestBrokenClassFile");
+        String input = "package test.inner; public class Test {}";
+        compiler.compile(testOutDir, input);
+        addToClasspath(compiler.getPath(testOutDir).resolve("test"));
+        assertCompletion("import inner.|");
+    }
+
+    public void testDocumentation() {
+        assertDocumentation("System.getProperty(|",
+                "java.lang.System.getProperty(java.lang.String arg0)",
+                "java.lang.System.getProperty(java.lang.String arg0, java.lang.String arg1)");
+        assertEval("char[] chars = null;");
+        assertDocumentation("new String(chars, |",
+                "java.lang.String(char[] arg0, int arg1, int arg2)");
+        assertDocumentation("String.format(|",
+                "java.lang.String.format(java.lang.String arg0, java.lang.Object... arg1)",
+                "java.lang.String.format(java.util.Locale arg0, java.lang.String arg1, java.lang.Object... arg2)");
+        assertDocumentation("\"\".getBytes(\"\"|", "java.lang.String.getBytes(int arg0, int arg1, byte[] arg2, int arg3)",
+                                                    "java.lang.String.getBytes(java.lang.String arg0)",
+                                                    "java.lang.String.getBytes(java.nio.charset.Charset arg0)");
+        assertDocumentation("\"\".getBytes(\"\" |", "java.lang.String.getBytes(int arg0, int arg1, byte[] arg2, int arg3)",
+                                                     "java.lang.String.getBytes(java.lang.String arg0)",
+                                                     "java.lang.String.getBytes(java.nio.charset.Charset arg0)");
+    }
+
+    public void testMethodsWithNoArguments() {
+        assertDocumentation("System.out.println(|",
+                "java.io.PrintStream.println()",
+                "java.io.PrintStream.println(boolean arg0)",
+                "java.io.PrintStream.println(char arg0)",
+                "java.io.PrintStream.println(int arg0)",
+                "java.io.PrintStream.println(long arg0)",
+                "java.io.PrintStream.println(float arg0)",
+                "java.io.PrintStream.println(double arg0)",
+                "java.io.PrintStream.println(char[] arg0)",
+                "java.io.PrintStream.println(java.lang.String arg0)",
+                "java.io.PrintStream.println(java.lang.Object arg0)");
+    }
+
+    public void testErroneous() {
+        assertCompletion("Undefined.|");
+    }
+
+    public void testClinit() {
+        assertEval("enum E{;}");
+        assertEval("class C{static{}}");
+        assertCompletionIncludesExcludes("E.|", Collections.emptySet(), new HashSet<>(Collections.singletonList("<clinit>")));
+        assertCompletionIncludesExcludes("C.|", Collections.emptySet(), new HashSet<>(Collections.singletonList("<clinit>")));
+    }
+
+    public void testMethodHeaderContext() {
+        assertCompletion("private void f(Runn|", "Runnable");
+        assertCompletion("void f(Runn|", "Runnable");
+        assertCompletion("void f(Object o1, Runn|", "Runnable");
+        assertCompletion("void f(Object o1) throws Num|", true, "NumberFormatException");
+        assertCompletion("void f(Object o1) throws java.lang.Num|", true, "NumberFormatException");
+        assertEval("class HogeHoge {static class HogeHogeException extends Exception {}}");
+        assertCompletion("void f(Object o1) throws Hoge|", "HogeHoge");
+        assertCompletion("void f(Object o1) throws HogeHoge.|", true, "HogeHogeException");
+    }
+
+    public void testTypeVariables() {
+        assertCompletion("class A<TYPE> { public void test() { TY|", "TYPE");
+        assertCompletion("class A<TYPE> { public static void test() { TY|");
+        assertCompletion("class A<TYPE> { public <TYPE> void test() { TY|", "TYPE");
+        assertCompletion("class A<TYPE> { public static <TYPE> void test() { TY|", "TYPE");
+    }
+
+    public void testGeneric() {
+        assertEval("import java.util.concurrent.*;");
+        assertCompletion("java.util.List<Integ|", "Integer");
+        assertCompletion("class A<TYPE extends Call|", "Callable");
+        assertCompletion("class A<TYPE extends Callable<TY|", "TYPE");
+        assertCompletion("<TYPE> void f(TY|", "TYPE");
+        assertCompletion("class A<TYPE extends Callable<? sup|", "super");
+        assertCompletion("class A<TYPE extends Callable<? super TY|", "TYPE");
+    }
+
+    public void testFields() {
+        assertEval("interface Interface { int field = 0; }");
+        Snippet clazz = classKey(assertEval("class Clazz {" +
+                "static int staticField = 0;" +
+                "int field = 0;" +
+                " }"));
+        assertCompletion("Interface.fiel|", "field");
+        assertCompletion("Clazz.staticFiel|", "staticField");
+        assertCompletion("new Interface() {}.fiel|");
+        assertCompletion("new Clazz().staticFiel|");
+        assertCompletion("new Clazz().fiel|", "field");
+        assertCompletion("new Clazz() {}.fiel|", "field");
+        assertEval("class Clazz implements Interface {}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(clazz, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertCompletion("Clazz.fiel|", "field");
+        assertCompletion("new Clazz().fiel|");
+        assertCompletion("new Clazz() {}.fiel|");
+    }
+
+    public void testMethods() {
+        assertEval("interface Interface {" +
+                "default int defaultMethod() { return 0; }" +
+                "static int staticMethod() { return 0; }" +
+                "}");
+        Snippet clazz = classKey(assertEval("class Clazz {" +
+                "static int staticMethod() { return 0; }" +
+                "int method() { return 0; }" +
+                "}"));
+        assertCompletion("Interface.staticMeth|", "staticMethod()");
+        assertCompletion("Clazz.staticMeth|", "staticMethod()");
+        assertCompletion("new Interface() {}.defaultMe||", "defaultMethod()");
+        assertCompletion("new Clazz().staticMeth|");
+        assertCompletion("new Clazz().meth|", "method()");
+        assertEval("class Clazz implements Interface {}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(clazz, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertCompletion("Clazz.staticMeth|");
+        assertCompletion("new Clazz() {}.defaultM|", "defaultMethod()");
+    }
+
+    @Test(enabled = false) // TODO 8129422
+    public void testUncompletedDeclaration() {
+        assertCompletion("class Clazz { Claz|", "Clazz");
+        assertCompletion("class Clazz { class A extends Claz|", "Clazz");
+        assertCompletion("class Clazz { Clazz clazz; Object o = cla|", "clazz");
+        assertCompletion("class Clazz { static Clazz clazz; Object o = cla|", "clazz");
+        assertCompletion("class Clazz { Clazz clazz; static Object o = cla|", true);
+        assertCompletion("class Clazz { void method(Claz|", "Clazz");
+        assertCompletion("class A { int method() { return 0; } int a = meth|", "method");
+        assertCompletion("class A { int field = 0; int method() { return fiel|", "field");
+        assertCompletion("class A { static int method() { return 0; } int a = meth|", "method");
+        assertCompletion("class A { static int field = 0; int method() { return fiel|", "field");
+        assertCompletion("class A { int method() { return 0; } static int a = meth|", true);
+        assertCompletion("class A { int field = 0; static int method() { return fiel|", true);
+    }
+
+    @Test(enabled = false) // TODO 8129421
+    public void testClassDeclaration() {
+        assertEval("interface Interface {}");
+        assertCompletion("interface A extends Interf|", "Interface");
+        assertCompletion("class A implements Interf|", "Interface");
+        assertEval("class Clazz {}");
+        assertCompletion("class A extends Claz|", "Clazz");
+        assertCompletion("class A extends Clazz implements Interf|", "Interface");
+        assertEval("interface Interface1 {}");
+        assertCompletion("class A extends Clazz implements Interface, Interf|", "Interface", "Interface1");
+        assertCompletion("interface A implements Claz|");
+        assertCompletion("interface A implements Inter|");
+        assertCompletion("class A implements Claz|", true);
+        assertCompletion("class A extends Clazz implements Interface, Interf|", true, "Interface1");
+    }
+
+    public void testDocumentationOfUserDefinedMethods() {
+        assertEval("void f() {}");
+        assertDocumentation("f(|", "f()");
+        assertEval("void f(int a) {}");
+        assertDocumentation("f(|", "f()", "f(int arg0)");
+        assertEval("<T> void f(T... a) {}", DiagCheck.DIAG_WARNING, DiagCheck.DIAG_OK);
+        assertDocumentation("f(|", "f()", "f(int arg0)", "f(T... arg0)");
+        assertEval("class A {}");
+        assertEval("void f(A a) {}");
+        assertDocumentation("f(|", "f()", "f(int arg0)", "f(T... arg0)", "f(A arg0)");
+    }
+
+    public void testDocumentationOfUserDefinedConstructors() {
+        Snippet a = classKey(assertEval("class A {}"));
+        assertDocumentation("new A(|", "A()");
+        Snippet a2 = classKey(assertEval("class A { A() {} A(int a) {}}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertDocumentation("new A(|", "A()", "A(int arg0)");
+        assertEval("class A<T> { A(T a) {} A(int a) {}}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a2, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertDocumentation("new A(|", "A(T arg0)", "A(int arg0)");
+    }
+
+    public void testDocumentationOfOverriddenMethods() {
+        assertDocumentation("\"\".wait(|",
+            "java.lang.Object.wait(long arg0)",
+            "java.lang.Object.wait(long arg0, int arg1)",
+            "java.lang.Object.wait()");
+        assertEval("class Base {void method() {}}");
+        Snippet e = classKey(assertEval("class Extend extends Base {}"));
+        assertDocumentation("new Extend().method(|", "Base.method()");
+        assertEval("class Extend extends Base {void method() {}}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(e, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertDocumentation("new Extend().method(|", "Extend.method()");
+    }
+
+    public void testDocumentationOfInvisibleMethods() {
+        assertDocumentation("Object.wait(|", "");
+        assertDocumentation("\"\".indexOfSupplementary(|", "");
+        Snippet a = classKey(assertEval("class A {void method() {}}"));
+        assertDocumentation("A.method(|", "");
+        assertEval("class A {private void method() {}}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertDocumentation("new A().method(|", "");
+    }
+
+    public void testDocumentationOfInvisibleConstructors() {
+        assertDocumentation("new Compiler(|", "");
+        assertEval("class A { private A() {} }");
+        assertDocumentation("new A(|", "");
+    }
+
+    public void testDocumentationWithBoxing() {
+        assertEval("int primitive = 0;");
+        assertEval("Integer boxed = 0;");
+        assertEval("Object object = null;");
+        assertEval("void method(int n, Object o) { }");
+        assertEval("void method(Object n, int o) { }");
+        assertDocumentation("method(primitive,|",
+                "method(int arg0, java.lang.Object arg1)",
+                "method(java.lang.Object arg0, int arg1)");
+        assertDocumentation("method(boxed,|",
+                "method(int arg0, java.lang.Object arg1)",
+                "method(java.lang.Object arg0, int arg1)");
+        assertDocumentation("method(object,|",
+                "method(java.lang.Object arg0, int arg1)");
+    }
+
+    public void testVarArgs() {
+        assertEval("int i = 0;");
+        assertEval("class Foo1 { static void m(int... i) { } } ");
+        assertCompletion("Foo1.m(|", true, "i");
+        assertCompletion("Foo1.m(i, |", true, "i");
+        assertCompletion("Foo1.m(i, i, |", true, "i");
+        assertEval("class Foo2 { static void m(String s, int... i) { } } ");
+        assertCompletion("Foo2.m(|", true);
+        assertCompletion("Foo2.m(i, |", true);
+        assertCompletion("Foo2.m(\"\", |", true, "i");
+        assertCompletion("Foo2.m(\"\", i, |", true, "i");
+        assertCompletion("Foo2.m(\"\", i, i, |", true, "i");
+        assertEval("class Foo3 { Foo3(String s, int... i) { } } ");
+        assertCompletion("new Foo3(|", true);
+        assertCompletion("new Foo3(i, |", true);
+        assertCompletion("new Foo3(\"\", |", true, "i");
+        assertCompletion("new Foo3(\"\", i, |", true, "i");
+        assertCompletion("new Foo3(\"\", i, i, |", true, "i");
+        assertEval("int[] ia = null;");
+        assertCompletion("Foo1.m(ia, |", true);
+        assertEval("class Foo4 { static void m(int... i) { } static void m(int[] ia, String str) { } } ");
+        assertEval("String str = null;");
+        assertCompletion("Foo4.m(ia, |", true, "str");
+    }
+
+    public void testConstructorAsMemberOf() {
+        assertEval("class Baz<X> { Baz(X x) { } } ");
+        assertEval("String str = null;");
+        assertEval("Integer i = null;");
+        assertCompletion("new Baz(|", true, "i", "str");
+        assertCompletion("new Baz<String>(|", true, "str");
+        assertCompletion("Baz<String> bz = new Baz<>(|", true, "str");
+        assertEval("class Foo { static void m(String str) {} static void m(Baz<String> baz) {} }");
+        assertCompletion("Foo.m(new Baz<>(|", true, "str");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/CustomEditor.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+public class CustomEditor implements AutoCloseable {
+
+    public static final int SOURCE_CODE = 0;
+    public static final int GET_SOURCE_CODE = 1;
+    public static final int REMOVE_CODE = 2;
+
+    public static final int EXIT_CODE = -1;
+    public static final int ACCEPT_CODE = -2;
+    public static final int CANCEL_CODE = -3;
+
+    private final Socket socket;
+    private final Path path;
+    private final StringWriter writer;
+    private final String source;
+
+    public CustomEditor(int port, String fileName) throws IOException {
+        this.socket = new Socket((String) null, port);
+        this.path = Paths.get(fileName);
+        this.writer = new StringWriter();
+        this.source = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
+    }
+
+    public void loop() throws IOException {
+        DataInputStream input = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
+        DataOutputStream output = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
+
+        while (true) {
+            int cmd = input.readInt();
+            switch (cmd) {
+                case EXIT_CODE: {
+                    Files.write(path, Collections.singletonList(writer.toString()));
+                    return;
+                }
+                case GET_SOURCE_CODE: {
+                    byte[] bytes = source.getBytes(StandardCharsets.UTF_8);
+                    output.writeInt(bytes.length);
+                    output.write(bytes);
+                    output.flush();
+                    break;
+                }
+                case REMOVE_CODE: {
+                    // only for external editor
+                    Files.delete(path);
+                    break;
+                }
+                case CANCEL_CODE: {
+                    return;
+                }
+                case ACCEPT_CODE: {
+                    Files.write(path, Collections.singletonList(writer.toString()));
+                    break;
+                }
+                case SOURCE_CODE: {
+                    int length = input.readInt();
+                    byte[] bytes = new byte[length];
+                    input.readFully(bytes);
+                    writer.write(new String(bytes, StandardCharsets.UTF_8));
+                    break;
+                }
+            }
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+        if (args.length != 2) {
+            System.err.println("Usage: port file");
+            System.exit(1);
+        }
+        try (CustomEditor editor = new CustomEditor(Integer.parseInt(args[0]), args[1])) {
+            editor.loop();
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        socket.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/DropTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test of JShell#drop().
+ * @build KullaTesting TestingInputStream
+ * @run testng DropTest
+ */
+
+import jdk.jshell.DeclarationSnippet;
+import jdk.jshell.PersistentSnippet;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.*;
+
+@Test
+public class DropTest extends KullaTesting {
+
+    public void testDrop() {
+        PersistentSnippet var = varKey(assertEval("int x;"));
+        PersistentSnippet method = methodKey(assertEval("int mu() { return x * 4; }"));
+        PersistentSnippet clazz = classKey(assertEval("class C { String v() { return \"#\" + mu(); } }"));
+        assertDrop(var,
+                ste(var, VALID, DROPPED, true, null),
+                ste(method, VALID, RECOVERABLE_DEFINED, false, var));
+        //assertDrop(method,
+        //        ste(method, RECOVERABLE_DEFINED, DROPPED, false, null),
+        //        ste(clazz, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, method));
+        //assertDeclareFail("new C();", "compiler.err.cant.resolve.location");
+        assertVariables();
+        assertMethods();
+        assertClasses();
+        assertActiveKeys();
+
+        assertEval("int x = 10;", "10",
+                ste(var, DROPPED, VALID, true, null),
+                ste(method, RECOVERABLE_DEFINED, VALID, false, MAIN_SNIPPET));
+        PersistentSnippet c0 = varKey(assertEval("C c0 = new C();"));
+        assertEval("c0.v();", "\"#40\"");
+        assertEval("C c = new C();");
+        assertEval("c.v();", "\"#40\"");
+        assertEval("int mu() { return x * 3; }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(method, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("c.v();", "\"#30\"");
+        assertEval("class C { String v() { return \"@\" + mu(); } }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(clazz, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("c0.v();", "\"@30\"");
+        assertDrop(c0,
+                ste(c0, VALID, DROPPED, true, null));
+        assertEval("c = new C();");
+        assertEval("c.v();", "\"@30\"");
+
+        assertVariables();
+        assertMethods();
+        assertClasses();
+        assertActiveKeys();
+    }
+
+    @Test(enabled = false) // TODO 8081431
+    public void testDropImport() {
+        PersistentSnippet imp = importKey(assertEval("import java.util.*;"));
+        PersistentSnippet decl = varKey(
+                assertEval("List<Integer> list = Arrays.asList(1, 2, 3);", "[1, 2, 3]"));
+        assertEval("list;", "[1, 2, 3]");
+        assertDrop(imp,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(imp, VALID, DROPPED, true, null),
+                ste(decl, VALID, RECOVERABLE_NOT_DEFINED, true, imp));
+        assertDeclareFail("list;", "compiler.err.cant.resolve.location");
+    }
+
+    public void testDropVarToMethod() {
+        PersistentSnippet x = varKey(assertEval("int x;"));
+        DeclarationSnippet method = methodKey(assertEval("double mu() { return x * 4; }"));
+        assertEval("x == 0;", "true");
+        assertEval("mu() == 0.0;", "true");
+
+        assertDrop(x,
+                ste(x, VALID, DROPPED, true, null),
+                ste(method, VALID, RECOVERABLE_DEFINED, false, x));
+        assertUnresolvedDependencies1(method, RECOVERABLE_DEFINED, "variable x");
+        assertEvalUnresolvedException("mu();", "mu", 1, 0);
+
+        assertVariables();
+        assertMethods();
+        assertActiveKeys();
+    }
+
+    public void testDropMethodToMethod() {
+        PersistentSnippet a = methodKey(assertEval("double a() { return 2; }"));
+        DeclarationSnippet b = methodKey(assertEval("double b() { return a() * 10; }"));
+        assertEval("double c() { return b() * 3; }");
+        DeclarationSnippet d = methodKey(assertEval("double d() { return c() + 1000; }"));
+        assertEval("d();", "1060.0");
+        assertDrop(a,
+                ste(a, VALID, DROPPED, true, null),
+                ste(b, VALID, RECOVERABLE_DEFINED, false, a));
+        assertUnresolvedDependencies1(b, RECOVERABLE_DEFINED, "method a()");
+        assertUnresolvedDependencies(d, 0);
+        assertEvalUnresolvedException("d();", "b", 1, 0);
+        assertMethods();
+        assertActiveKeys();
+    }
+
+    public void testDropClassToMethod() {
+        PersistentSnippet c = classKey(assertEval("class C { int f() { return 7; } }"));
+        DeclarationSnippet m = methodKey(assertEval("int m() { return new C().f(); }"));
+        assertDrop(c,
+                ste(c, VALID, DROPPED, true, null),
+                ste(m, VALID, RECOVERABLE_DEFINED, false, c));
+        assertUnresolvedDependencies1(m, RECOVERABLE_DEFINED, "class C");
+        assertEvalUnresolvedException("m();", "m", 1, 0);
+        assertActiveKeys();
+    }
+
+    public void testDropVarToClass() {
+        PersistentSnippet x = varKey(assertEval("int x;"));
+        DeclarationSnippet a = classKey(assertEval("class A { double a = 4 * x; }"));
+        assertDrop(x,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(x, VALID, DROPPED, true, null),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, x));
+        assertUnresolvedDependencies1(a, RECOVERABLE_NOT_DEFINED, "variable x");
+        assertDeclareFail("new A().a;", "compiler.err.cant.resolve.location");
+        assertVariables();
+        assertActiveKeys();
+    }
+
+    public void testDropMethodToClass() {
+        PersistentSnippet x = methodKey(assertEval("int x() { return 0; }"));
+        DeclarationSnippet a = classKey(assertEval("class A { double a = 4 * x(); }"));
+        assertDrop(x,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(x, VALID, DROPPED, true, null),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, x));
+        assertUnresolvedDependencies1(a, RECOVERABLE_NOT_DEFINED, "method x()");
+        assertDeclareFail("new A().a;", "compiler.err.cant.resolve.location");
+        assertMethods();
+        assertActiveKeys();
+    }
+
+    public void testDropClassToClass() {
+        PersistentSnippet a = classKey(assertEval("class A {}"));
+        PersistentSnippet b = classKey(assertEval("class B extends A {}"));
+        PersistentSnippet c = classKey(assertEval("class C extends B {}"));
+        PersistentSnippet d = classKey(assertEval("class D extends C {}"));
+        assertDrop(a,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(a, VALID, DROPPED, true, null),
+                ste(b, VALID, RECOVERABLE_NOT_DEFINED, true, a),
+                ste(c, VALID, RECOVERABLE_NOT_DEFINED, true, b),
+                ste(d, VALID, RECOVERABLE_NOT_DEFINED, true, c));
+        assertUnresolvedDependencies1((DeclarationSnippet) b, RECOVERABLE_NOT_DEFINED, "class A");
+        assertDrop(c,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(c, RECOVERABLE_NOT_DEFINED, DROPPED, false, null),
+                ste(d, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, c));
+        assertEval("interface A {}", null, null,
+                DiagCheck.DIAG_OK, DiagCheck.DIAG_ERROR,
+                ste(a, DROPPED, VALID, true, null),
+                ste(b, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, MAIN_SNIPPET));
+        assertClasses();
+        assertActiveKeys();
+    }
+
+    public void testDropNoUpdate() {
+        String as1 = "class A {}";
+        String as2 = "class A extends java.util.ArrayList<Boolean> {}";
+        PersistentSnippet a = classKey(assertEval(as1, added(VALID)));
+        PersistentSnippet b = classKey(assertEval("class B extends A {}", added(VALID)));
+        PersistentSnippet ax = classKey(assertEval(as2,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(b, VALID, VALID, true, MAIN_SNIPPET)));
+        ax = classKey(assertEval(as1,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(ax, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(b, VALID, VALID, true, MAIN_SNIPPET)));
+        assertDrop(b,
+                ste(b, VALID, DROPPED, true, null));
+        ax = classKey(assertEval(as2,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(ax, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertEval(as1,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(ax, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/EditorPadTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Testing built-in editor.
+ * @ignore 8139872
+ * @build ReplToolTesting EditorTestBase
+ * @run testng EditorPadTest
+ */
+
+import java.awt.AWTException;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Point;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import java.awt.event.WindowEvent;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+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.JViewport;
+import javax.swing.SwingUtilities;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class EditorPadTest extends EditorTestBase {
+
+    private static final int DELAY = 500;
+
+    private static Robot robot;
+    private static JFrame frame = null;
+    private static JTextArea area = null;
+    private static JButton cancel = null;
+    private static JButton accept = null;
+    private static JButton exit = null;
+
+    @BeforeClass
+    public static void setUp() {
+        try {
+            robot = new Robot();
+            robot.setAutoWaitForIdle(true);
+            robot.setAutoDelay(DELAY);
+        } catch (AWTException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    @AfterClass
+    public static void shutdown() {
+        executorShutdown();
+    }
+
+    @Override
+    public void writeSource(String s) {
+        SwingUtilities.invokeLater(() -> area.setText(s));
+    }
+
+    @Override
+    public String getSource() {
+        try {
+            String[] s = new String[1];
+            SwingUtilities.invokeAndWait(() -> s[0] = area.getText());
+            return s[0];
+        } catch (InvocationTargetException | InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void accept() {
+        clickOn(accept);
+    }
+
+    @Override
+    public void exit() {
+        clickOn(exit);
+    }
+
+    @Override
+    public void cancel() {
+        clickOn(cancel);
+    }
+
+    @Override
+    public void shutdownEditor() {
+        SwingUtilities.invokeLater(this::clearElements);
+        waitForIdle();
+    }
+
+    @Test
+    public void testShuttingDown() {
+        testEditor(
+                (a) -> assertEditOutput(a, "/e", "", this::shutdownEditor)
+        );
+    }
+
+    private void waitForIdle() {
+        robot.waitForIdle();
+        robot.delay(DELAY);
+    }
+
+    private Future<?> task;
+    @Override
+    public void assertEdit(boolean after, String cmd,
+                           Consumer<String> checkInput, Consumer<String> checkOutput, Action action) {
+        if (!after) {
+            setCommandInput(cmd + "\n");
+            task = getExecutor().submit(() -> {
+                try {
+                    waitForIdle();
+                    SwingUtilities.invokeLater(this::seekElements);
+                    waitForIdle();
+                    checkInput.accept(getSource());
+                    action.accept();
+                } catch (Throwable e) {
+                    shutdownEditor();
+                    if (e instanceof AssertionError) {
+                        throw (AssertionError) e;
+                    }
+                    throw new RuntimeException(e);
+                }
+            });
+        } else {
+            try {
+                task.get();
+                waitForIdle();
+                checkOutput.accept(getCommandOutput());
+            } catch (ExecutionException e) {
+                if (e.getCause() instanceof AssertionError) {
+                    throw (AssertionError) e.getCause();
+                }
+                throw new RuntimeException(e);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            } finally {
+                shutdownEditor();
+            }
+        }
+    }
+
+    private void seekElements() {
+        for (Frame f : Frame.getFrames()) {
+            if (f.getTitle().contains("Edit Pad")) {
+                frame = (JFrame) f;
+                // workaround
+                frame.setLocation(0, 0);
+                Container root = frame.getContentPane();
+                for (Component c : root.getComponents()) {
+                    if (c instanceof JScrollPane) {
+                        JScrollPane scrollPane = (JScrollPane) c;
+                        for (Component comp : scrollPane.getComponents()) {
+                            if (comp instanceof JViewport) {
+                                JViewport view = (JViewport) comp;
+                                area = (JTextArea) view.getComponent(0);
+                            }
+                        }
+                    }
+                    if (c instanceof JPanel) {
+                        JPanel p = (JPanel) c;
+                        for (Component comp : p.getComponents()) {
+                            if (comp instanceof JButton) {
+                                JButton b = (JButton) comp;
+                                switch (b.getText()) {
+                                    case "Cancel":
+                                        cancel = b;
+                                        break;
+                                    case "Exit":
+                                        exit = b;
+                                        break;
+                                    case "Accept":
+                                        accept = b;
+                                        break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void clearElements() {
+        if (frame != null) {
+            frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
+            frame = null;
+        }
+        area = null;
+        accept = null;
+        cancel = null;
+        exit = null;
+    }
+
+    private void clickOn(JButton button) {
+        waitForIdle();
+        Point p = button.getLocationOnScreen();
+        Dimension d = button.getSize();
+        robot.mouseMove(p.x + d.width / 2, p.y + d.height / 2);
+        robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
+        robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/EditorTestBase.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public abstract class EditorTestBase extends ReplToolTesting {
+
+    private static ExecutorService executor;
+
+    public abstract void writeSource(String s);
+    public abstract String getSource();
+    public abstract void accept();
+    public abstract void exit();
+    public abstract void cancel();
+    public abstract void shutdownEditor();
+
+    public void testEditor(ReplTest... tests) {
+        testEditor(false, new String[]{"-nostartup"}, tests);
+    }
+
+    public void testEditor(boolean defaultStartup, String[] args, ReplTest... tests) {
+        test(defaultStartup, args, tests);
+    }
+
+    public abstract void assertEdit(boolean after, String cmd,
+                                    Consumer<String> checkInput, Consumer<String> checkOutput, Action action);
+
+    public void assertEditInput(boolean after, String cmd, Consumer<String> checkInput, Action action) {
+        assertEdit(after, cmd, checkInput, s -> {}, action);
+    }
+
+    public void assertEditOutput(boolean after, String cmd, Consumer<String> checkOutput, Action action) {
+        assertEdit(after, cmd, s -> {}, checkOutput, action);
+    }
+
+    public void assertEditInput(boolean after, String cmd, String input, Action action) {
+        assertEditInput(after, cmd, s -> assertEquals(s, input, "Input"), action);
+    }
+
+    public void assertEditOutput(boolean after, String cmd, String output, Action action) {
+        assertEditOutput(after, cmd, s -> assertEquals(s, output, "command"), action);
+    }
+
+    @Test
+    public void testEditNegative() {
+        for (String edit : new String[] {"/e", "/edit"}) {
+            test(new String[]{"-nostartup"},
+                    a -> assertCommand(a, edit + " 1",
+                            "|  No definition or id named 1 found.  See /classes /methods /vars or /list\n"),
+                    a -> assertCommand(a, edit + " -1",
+                            "|  No definition or id named -1 found.  See /classes /methods /vars or /list\n"),
+                    a -> assertCommand(a, edit + " unknown",
+                            "|  No definition or id named unknown found.  See /classes /methods /vars or /list\n")
+            );
+        }
+    }
+
+    @Test
+    public void testDoNothing() {
+        testEditor(
+                a -> assertVariable(a, "int", "a", "0", "0"),
+                a -> assertEditOutput(a, "/e 1", "", this::exit),
+                a -> assertCommandCheckOutput(a, "/v", assertVariables())
+        );
+    }
+
+    @Test
+    public void testEditVariable1() {
+        testEditor(
+                a -> assertVariable(a, "int", "a", "0", "0"),
+                a -> assertEditOutput(a, "/e 1", "|  Modified variable a of type int with initial value 10\n", () -> {
+                    writeSource("\n\n\nint a = 10;\n\n\n");
+                    exit();
+                    loadVariable(true, "int", "a", "10", "10");
+                }),
+                a -> assertEditOutput(a, "/e 1", "|  Modified variable a of type int with initial value 15\n", () -> {
+                    writeSource("int a = 15;");
+                    exit();
+                    loadVariable(true, "int", "a", "15", "15");
+                }),
+                a -> assertCommandCheckOutput(a, "/v", assertVariables())
+        );
+    }
+
+    @Test
+    public void testEditVariable2() {
+        testEditor(
+                a -> assertVariable(a, "int", "a", "0", "0"),
+                a -> assertEditOutput(a, "/e 1", "|  Added variable b of type int with initial value 10\n", () -> {
+                    writeSource("int b = 10;");
+                    exit();
+                    loadVariable(true, "int", "b", "10", "10");
+                }),
+                a -> assertEditOutput(a, "/e 1", "|  Modified variable a of type int with initial value 15\n", () -> {
+                    writeSource("int a = 15;");
+                    exit();
+                    loadVariable(true, "int", "a", "15", "15");
+                }),
+                a -> assertCommandCheckOutput(a, "/v", assertVariables())
+        );
+    }
+
+    @Test
+    public void testEditClass1() {
+        testEditor(
+                a -> assertClass(a, "class A {}", "class", "A"),
+                a -> assertEditOutput(a, "/e 1", "", () -> {
+                    writeSource("\n\n\nclass A {}\n\n\n");
+                    exit();
+                    loadClass(true, "class A {}", "class", "A");
+                }),
+                a -> assertEditOutput(a, "/e 1",
+                        "|  Replaced enum A\n" +
+                        "|    Update overwrote class A\n", () -> {
+                    writeSource("enum A {}");
+                    exit();
+                    loadClass(true, "enum A {}", "enum", "A");
+                }),
+                a -> assertCommandCheckOutput(a, "/c", assertClasses())
+        );
+    }
+
+    @Test
+    public void testEditClass2() {
+        testEditor(
+                a -> assertClass(a, "class A {}", "class", "A"),
+                a -> assertEditOutput(a, "/e 1", "|  Added class B\n", () -> {
+                    writeSource("class B { }");
+                    exit();
+                    loadClass(true, "class B {}", "class", "B");
+                }),
+                a -> assertEditOutput(a, "/e 1",
+                        "|  Replaced enum A\n" +
+                        "|    Update overwrote class A\n", () -> {
+                    writeSource("enum A {}");
+                    exit();
+                    loadClass(true, "enum A {}", "enum", "A");
+                }),
+                a -> assertCommandCheckOutput(a, "/c", assertClasses())
+        );
+    }
+
+    @Test
+    public void testEditMethod1() {
+        testEditor(
+                a -> assertMethod(a, "void f() {}", "()void", "f"),
+                a -> assertEditOutput(a, "/e 1", "", () -> {
+                    writeSource("\n\n\nvoid f() {}\n\n\n");
+                    exit();
+                    loadMethod(true, "void f() {}", "()void", "f");
+                }),
+                a -> assertEditOutput(a, "/e 1",
+                        "|  Replaced method f()\n" +
+                        "|    Update overwrote method f()\n", () -> {
+                    writeSource("double f() { return 0; }");
+                    exit();
+                    loadMethod(true, "double f() { return 0; }", "()double", "f");
+                }),
+                a -> assertCommandCheckOutput(a, "/m", assertMethods())
+        );
+    }
+
+    @Test
+    public void testEditMethod2() {
+        testEditor(
+                a -> assertMethod(a, "void f() {}", "()void", "f"),
+                a -> assertEditOutput(a, "/e 1", "|  Added method g()\n", () -> {
+                    writeSource("void g() {}");
+                    exit();
+                    loadMethod(true, "void g() {}", "()void", "g");
+                }),
+                a -> assertEditOutput(a, "/e 1",
+                        "|  Replaced method f()\n" +
+                        "|    Update overwrote method f()\n", () -> {
+                    writeSource("double f() { return 0; }");
+                    exit();
+                    loadMethod(true, "double f() { return 0; }", "()double", "f");
+                }),
+                a -> assertCommandCheckOutput(a, "/m", assertMethods())
+        );
+    }
+
+    @Test
+    public void testNoArguments() {
+        testEditor(
+                a -> assertVariable(a, "int", "a"),
+                a -> assertMethod(a, "void f() {}", "()void", "f"),
+                a -> assertClass(a, "class A {}", "class", "A"),
+                a -> assertEditInput(a, "/e", s -> {
+                    String[] ss = s.split("\n");
+                    assertEquals(ss.length, 3, "Expected 3 lines: " + s);
+                    assertEquals(ss[0], "int a;");
+                    assertEquals(ss[1], "void f() {}");
+                    assertEquals(ss[2], "class A {}");
+                }, this::exit)
+        );
+    }
+
+    @Test
+    public void testStartup() {
+        testEditor(true, new String[0],
+                a -> assertEditInput(a, "/e", s -> assertTrue(s.isEmpty(), "Checking of startup: " + s), this::cancel),
+                a -> assertEditInput(a, "/e printf", assertStartsWith("void printf"), this::cancel));
+    }
+
+    @Test
+    public void testCancel() {
+        testEditor(
+                a -> assertVariable(a, "int", "a"),
+                a -> assertEditOutput(a, "/e a", "", () -> {
+                    writeSource("int b = 10");
+                    cancel();
+                })
+        );
+    }
+
+    @Test
+    public void testAccept() {
+        testEditor(
+                a -> assertVariable(a, "int", "a"),
+                a -> assertEditOutput(a, "/e a", "|  Added variable b of type int with initial value 10\n", () -> {
+                    writeSource("int b = 10");
+                    accept();
+                    exit();
+                })
+        );
+    }
+
+    public static ExecutorService getExecutor() {
+        if (executor == null) {
+            executor = Executors.newSingleThreadExecutor();
+        }
+        return executor;
+    }
+
+    public static void executorShutdown() {
+        if (executor != null) {
+            executor.shutdown();
+            executor = null;
+        }
+    }
+
+    interface Action {
+        void accept();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/EmptyTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary null test
+ * @build KullaTesting TestingInputStream
+ * @run testng EmptyTest
+ */
+
+import org.testng.annotations.Test;
+
+@Test
+public class EmptyTest extends KullaTesting {
+
+    public void testEmpty() {
+        assertEvalEmpty("");
+    }
+
+    public void testSpace() {
+        assertEvalEmpty("    ");
+    }
+
+    public void testSemicolon() {
+        assertEval(";", "");
+    }
+
+    public void testSlashStarComment() {
+        assertEvalEmpty("/*test*/");
+    }
+
+    public void testSlashStarCommentSemicolon() {
+        assertEval("/*test*/;", "");
+    }
+
+    public void testSlashComment() {
+        assertEvalEmpty("// test");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ErrorTranslationTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for shell error translation
+ * @library /tools/lib
+ * @build KullaTesting TestingInputStream ExpectedDiagnostic ToolBox Compiler
+ * @run testng ErrorTranslationTest
+ */
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import javax.tools.Diagnostic;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+@Test
+public class ErrorTranslationTest extends ReplToolTesting {
+
+    @Test(enabled = false) // TODO 8080353
+    public void testErrors() {
+        test(
+                a -> assertDiagnostic(a, "abstract void f();", newExpectedDiagnostic(0, 8, 0, -1, -1, Diagnostic.Kind.ERROR)),
+                a -> assertDiagnostic(a, "native void f();", newExpectedDiagnostic(0, 6, 0, -1, -1, Diagnostic.Kind.ERROR)),
+                a -> assertDiagnostic(a, "static void f();", newExpectedDiagnostic(0, 16, 0, -1, -1, Diagnostic.Kind.ERROR)),
+                a -> assertDiagnostic(a, "synchronized void f();", newExpectedDiagnostic(0, 12, 0, -1, -1, Diagnostic.Kind.ERROR)),
+                a -> assertDiagnostic(a, "default void f();", newExpectedDiagnostic(0, 7, 0, -1, -1, Diagnostic.Kind.ERROR))
+        );
+    }
+
+    public void testWarnings() {
+        List<ReplTest> list = new ArrayList<>();
+        ExpectedDiagnostic[] diagnostics = new ExpectedDiagnostic[]{
+                newExpectedDiagnostic(0, 6, 0, -1, -1, Diagnostic.Kind.WARNING),
+                newExpectedDiagnostic(0, 9, 0, -1, -1, Diagnostic.Kind.WARNING),
+                newExpectedDiagnostic(0, 7, 0, -1, -1, Diagnostic.Kind.WARNING),
+                newExpectedDiagnostic(0, 6, 0, -1, -1, Diagnostic.Kind.WARNING),
+                newExpectedDiagnostic(0, 5, 0, -1, -1, Diagnostic.Kind.WARNING)};
+        String[] mods = {"public", "protected", "private", "static", "final"};
+        for (int i = 0; i < mods.length; ++i) {
+            for (String code : new String[] {"class A {}", "void f() {}", "int a;"}) {
+                final int finalI = i;
+                list.add(a -> assertDiagnostic(a, mods[finalI] + " " + code, diagnostics[finalI]));
+            }
+        }
+        test(list.toArray(new ReplTest[list.size()]));
+    }
+
+    @Test(enabled = false) // TODO 8132147
+    public void stressTest() {
+        Compiler compiler = new Compiler();
+        Path oome = compiler.getPath("OOME.repl");
+        Path soe = compiler.getPath("SOE.repl");
+        compiler.writeToFile(oome,
+                "List<byte[]> list = new ArrayList<>();\n",
+                "while (true) {\n" +
+                "   list.add(new byte[1000000]);\n" +
+                "}");
+        compiler.writeToFile(soe,
+                "void f() { f(); }",
+                "f();");
+        List<ReplTest> tests = new ArrayList<>();
+        for (int i = 0; i < 25; ++i) {
+            tests.add(a -> assertCommandCheckOutput(a, "/o " + soe.toString(),
+                    assertStartsWith("|  java.lang.StackOverflowError thrown")));
+            tests.add(a -> assertCommandCheckOutput(a, "/o " + oome.toString(),
+                    assertStartsWith("|  java.lang.OutOfMemoryError thrown: Java heap space")));
+        }
+        test(tests.toArray(new ReplTest[tests.size()]));
+    }
+
+    private ExpectedDiagnostic newExpectedDiagnostic(long startPosition, long endPosition, long position,
+                                                     long lineNumber, long columnNumber, Diagnostic.Kind kind) {
+        return new ExpectedDiagnostic("", startPosition, endPosition, position, lineNumber, columnNumber, kind);
+    }
+
+    private void assertDiagnostic(boolean after, String cmd, ExpectedDiagnostic expectedDiagnostic) {
+        assertCommandCheckOutput(after, cmd, assertDiagnostic(cmd, expectedDiagnostic));
+    }
+
+    private Consumer<String> assertDiagnostic(String expectedSource, ExpectedDiagnostic expectedDiagnostic) {
+        int start = (int) expectedDiagnostic.getStartPosition();
+        int end = (int) expectedDiagnostic.getEndPosition();
+        String expectedMarkingLine = createMarkingLine(start, end);
+        return s -> {
+            String[] lines = s.split("\n");
+            if (lines.length <= 3) {
+                throw new AssertionError("Not enough lines: " + s);
+            }
+            String kind = getKind(expectedDiagnostic.getKind());
+            assertEquals(lines[0], kind);
+            String source;
+            String markingLine;
+            switch (expectedDiagnostic.getKind()) {
+                case ERROR:
+                case WARNING:
+                    source = lines[2];
+                    markingLine = lines[3];
+                    break;
+                default:
+                    throw new AssertionError("Unsupported diagnostic kind: " + expectedDiagnostic.getKind());
+            }
+            assertTrue(source.endsWith(expectedSource), "Expected: " + expectedSource + ", found: " + source);
+            assertEquals(markingLine, expectedMarkingLine, "Input: " + expectedSource + ", marking line: ");
+        };
+    }
+
+    private String createMarkingLine(int start, int end) {
+        assertTrue(end >= start, String.format("End position %d is less than start position %d", end, start));
+        StringBuilder sb = new StringBuilder();
+        sb.append("|  ");
+        for (int i = 0; i < start; ++i) {
+            sb.append(' ');
+        }
+        sb.append('^');
+        for (int i = 1; i < end - start - 1; ++i) {
+            sb.append('-');
+        }
+        if (start < end) {
+            sb.append('^');
+        }
+        return sb.toString();
+    }
+
+    public String getKind(Diagnostic.Kind kind) {
+        switch (kind) {
+            case WARNING:
+                return "|  Warning:";
+            case ERROR:
+                return "|  Error:";
+            default:
+                throw new AssertionError("Unsupported kind: " + kind);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ExceptionsTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for exceptions
+ * @build KullaTesting TestingInputStream
+ * @run testng ExceptionsTest
+ */
+
+import jdk.jshell.SnippetEvent;
+import jdk.jshell.EvalException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import jdk.jshell.Snippet;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+@Test
+public class ExceptionsTest extends KullaTesting {
+
+    public void throwUncheckedException() {
+        String message = "error_message";
+        SnippetEvent cr = assertEvalException("throw new RuntimeException(\"" + message + "\");");
+        assertExceptionMatch(cr,
+                new ExceptionInfo(RuntimeException.class, message,
+                        newStackTraceElement("", "", cr.snippet(), 1)));
+    }
+
+    public void throwCheckedException() {
+        String message = "error_message";
+        SnippetEvent cr = assertEvalException("throw new Exception(\"" + message + "\");");
+        assertExceptionMatch(cr,
+                new ExceptionInfo(Exception.class, message,
+                        newStackTraceElement("", "", cr.snippet(), 1)));
+    }
+
+    public void throwFromStaticMethodOfClass() {
+        String message = "error_message";
+        Snippet s1 = methodKey(assertEval("void f() { throw new RuntimeException(\"" + message + "\"); }"));
+        Snippet s2 = classKey(assertEval("class A { static void g() { f(); } }"));
+        SnippetEvent cr3 = assertEvalException("A.g();");
+        assertExceptionMatch(cr3,
+                new ExceptionInfo(RuntimeException.class, message,
+                        newStackTraceElement("", "f", s1, 1),
+                        newStackTraceElement("A", "g", s2, 1),
+                        newStackTraceElement("", "", cr3.snippet(), 1)));
+    }
+
+    public void throwFromStaticMethodOfInterface() {
+        String message = "error_message";
+        Snippet s1 = methodKey(assertEval("void f() { throw new RuntimeException(\"" + message + "\"); }"));
+        Snippet s2 = classKey(assertEval("interface A { static void g() { f(); } }"));
+        SnippetEvent cr3 = assertEvalException("A.g();");
+        assertExceptionMatch(cr3,
+                new ExceptionInfo(RuntimeException.class, message,
+                        newStackTraceElement("", "f", s1, 1),
+                        newStackTraceElement("A", "g", s2, 1),
+                        newStackTraceElement("", "", cr3.snippet(), 1)));
+    }
+
+    public void throwFromConstructor() {
+        String message = "error_message";
+        Snippet s1 = methodKey(assertEval("void f() { throw new RuntimeException(\"" + message + "\"); }"));
+        Snippet s2 = classKey(assertEval("class A { A() { f(); } }"));
+        SnippetEvent cr3 = assertEvalException("new A();");
+        assertExceptionMatch(cr3,
+                new ExceptionInfo(RuntimeException.class, message,
+                        newStackTraceElement("", "f", s1, 1),
+                        newStackTraceElement("A", "<init>", s2, 1),
+                        newStackTraceElement("", "", cr3.snippet(), 1)));
+    }
+
+    public void throwFromDefaultMethodOfInterface() {
+        String message = "error_message";
+        Snippet s1 = methodKey(assertEval("void f() { throw new RuntimeException(\"" + message + "\"); }"));
+        Snippet s2 = classKey(assertEval("interface A { default void g() { f(); } }"));
+        SnippetEvent cr3 = assertEvalException("new A() { }.g();");
+        assertExceptionMatch(cr3,
+                new ExceptionInfo(RuntimeException.class, message,
+                        newStackTraceElement("", "f", s1, 1),
+                        newStackTraceElement("A", "g", s2, 1),
+                        newStackTraceElement("", "", cr3.snippet(), 1)));
+    }
+
+    public void throwFromLambda() {
+        String message = "lambda";
+        Snippet s1 = varKey(assertEval(
+                "Runnable run = () -> {\n" +
+                "   throw new RuntimeException(\"" + message + "\");\n" +
+                "};"
+        ));
+        SnippetEvent cr2 = assertEvalException("run.run();");
+        assertExceptionMatch(cr2,
+                new ExceptionInfo(RuntimeException.class, message,
+                        newStackTraceElement("", "lambda$", s1, 2),
+                        newStackTraceElement("", "", cr2.snippet(), 1)));
+    }
+
+    public void throwFromAnonymousClass() {
+        String message = "anonymous";
+        Snippet s1 = varKey(assertEval(
+                "Runnable run = new Runnable() {\n" +
+                "   public void run() {\n"+
+                "       throw new RuntimeException(\"" + message + "\");\n" +
+                "   }\n" +
+                "};"
+        ));
+        SnippetEvent cr2 = assertEvalException("run.run();");
+        assertExceptionMatch(cr2,
+                new ExceptionInfo(RuntimeException.class, message,
+                        newStackTraceElement("1", "run", s1, 3),
+                        newStackTraceElement("", "", cr2.snippet(), 1)));
+    }
+
+    public void throwFromLocalClass() {
+        String message = "local";
+        Snippet s1 = methodKey(assertEval(
+                "void f() {\n" +
+                "   class A {\n" +
+                "       void f() {\n"+
+                "           throw new RuntimeException(\"" + message + "\");\n" +
+                "       }\n" +
+                "   }\n" +
+                "   new A().f();\n" +
+                "}"
+        ));
+        SnippetEvent cr2 = assertEvalException("f();");
+        assertExceptionMatch(cr2,
+                new ExceptionInfo(RuntimeException.class, message,
+                        newStackTraceElement("1A", "f", s1, 4),
+                        newStackTraceElement("", "f", s1, 7),
+                        newStackTraceElement("", "", cr2.snippet(), 1)));
+    }
+
+    @Test(enabled = false) // TODO 8129427
+    public void outOfMemory() {
+        assertEval("import java.util.*;");
+        assertEval("List<byte[]> list = new ArrayList<>();");
+        assertExecuteException("while (true) { list.add(new byte[10000]); }", OutOfMemoryError.class);
+    }
+
+    public void stackOverflow() {
+        assertEval("void f() { f(); }");
+        assertExecuteException("f();", StackOverflowError.class);
+    }
+
+    private StackTraceElement newStackTraceElement(String className, String methodName, Snippet key, int lineNumber) {
+        return new StackTraceElement(className, methodName, "#" + key.id(), lineNumber);
+    }
+
+    private static class ExceptionInfo {
+        public final Class<? extends Throwable> exception;
+        public final String message;
+        public final StackTraceElement[] stackTraceElements;
+
+        public ExceptionInfo(Class<? extends Throwable> exception, String message, StackTraceElement...stackTraceElements) {
+            this.exception = exception;
+            this.message = message;
+            this.stackTraceElements = stackTraceElements.length == 0 ? null : stackTraceElements;
+        }
+    }
+
+    private void assertExecuteException(String input, Class<? extends Throwable> exception) {
+        assertExceptionMatch(assertEvalException(input), new ExceptionInfo(exception, null));
+    }
+
+    private void assertExceptionMatch(SnippetEvent cr, ExceptionInfo exceptionInfo) {
+        assertNotNull(cr.exception(), "Expected exception was not thrown: " + exceptionInfo.exception);
+        if (cr.exception() instanceof EvalException) {
+            EvalException ex = (EvalException) cr.exception();
+            String actualException = ex.getExceptionClassName();
+            String expectedException = exceptionInfo.exception.getCanonicalName();
+            String stackTrace = getStackTrace(ex);
+            String source = cr.snippet().source();
+            assertEquals(actualException, expectedException,
+                    String.format("Given \"%s\" expected exception: %s, got: %s%nStack trace:%n%s",
+                            source, expectedException, actualException, stackTrace));
+            if (exceptionInfo.message != null) {
+                assertEquals(ex.getMessage(), exceptionInfo.message,
+                        String.format("Given \"%s\" expected message: %s, got: %s",
+                                source, exceptionInfo.message, ex.getMessage()));
+            }
+            if (exceptionInfo.stackTraceElements != null) {
+                assertStackTrace(ex.getStackTrace(), exceptionInfo.stackTraceElements,
+                        String.format("Given \"%s\"%nStack trace:%n%s%n",
+                                source, stackTrace));
+            }
+        } else {
+            fail("Unexpected execution exceptionInfo: " + cr.exception());
+        }
+    }
+
+    private void assertStackTrace(StackTraceElement[] actual, StackTraceElement[] expected, String message) {
+        if (actual != expected) {
+            if (actual == null || expected == null) {
+                fail(message);
+            } else {
+                assertEquals(actual.length, expected.length, message + " : arrays do not have the same size");
+                for (int i = 0; i < actual.length; ++i) {
+                    StackTraceElement actualElement = actual[i];
+                    StackTraceElement expectedElement = expected[i];
+                    assertEquals(actualElement.getClassName(), expectedElement.getClassName(), message + " : class names");
+                    String expectedMethodName = expectedElement.getMethodName();
+                    if (expectedMethodName.startsWith("lambda$")) {
+                        assertTrue(actualElement.getMethodName().startsWith("lambda$"), message + " : method names");
+                    } else {
+                        assertEquals(actualElement.getMethodName(), expectedElement.getMethodName(), message + " : method names");
+                    }
+                    assertEquals(actualElement.getFileName(), expectedElement.getFileName(), message + " : file names");
+                    assertEquals(actualElement.getLineNumber(), expectedElement.getLineNumber(), message + " : line numbers");
+                }
+            }
+        }
+    }
+
+    private String getStackTrace(EvalException ex) {
+        StringWriter st = new StringWriter();
+        ex.printStackTrace(new PrintWriter(st));
+        return st.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ExpectedDiagnostic.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import javax.tools.Diagnostic;
+
+import jdk.jshell.Diag;
+import static org.testng.Assert.assertEquals;
+
+public class ExpectedDiagnostic {
+
+    private final String code;
+    private final long startPosition;
+    private final long endPosition;
+    private final long position;
+    private final long lineNumber;
+    private final long columnNumber;
+    private final Diagnostic.Kind kind;
+
+    public ExpectedDiagnostic(
+            String code, long startPosition, long endPosition,
+            long position, long lineNumber, long columnNumber,
+            Diagnostic.Kind kind) {
+        this.code = code;
+        this.startPosition = startPosition;
+        this.endPosition = endPosition;
+        this.position = position;
+        this.lineNumber = lineNumber;
+        this.columnNumber = columnNumber;
+        this.kind = kind;
+    }
+
+    public long getStartPosition() {
+        return startPosition;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public long getEndPosition() {
+        return endPosition;
+    }
+
+    public long getPosition() {
+        return position;
+    }
+
+    public long getLineNumber() {
+        return lineNumber;
+    }
+
+    public long getColumnNumber() {
+        return columnNumber;
+    }
+
+    public Diagnostic.Kind getKind() {
+        return kind;
+    }
+
+    public void assertDiagnostic(Diag diagnostic) {
+        String code = diagnostic.getCode();
+        assertEquals(code, this.code, "Expected error: " + this.code + ", got: " + code);
+        assertEquals(diagnostic.isError(), kind == Diagnostic.Kind.ERROR);
+        if (startPosition != -1) {
+            assertEquals(diagnostic.getStartPosition(), startPosition, "Start position");
+        }
+        if (endPosition != -1) {
+            assertEquals(diagnostic.getEndPosition(), endPosition, "End position");
+        }
+        if (position != -1) {
+            assertEquals(diagnostic.getPosition(), position, "Position");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ExternalEditorTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Testing external editor.
+ * @bug 8080843
+ * @ignore 8080843
+ * @build ReplToolTesting CustomEditor EditorTestBase
+ * @run testng ExternalEditorTest
+ */
+
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.function.Consumer;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.fail;
+
+public class ExternalEditorTest extends EditorTestBase {
+
+    private static Path executionScript;
+    private static ServerSocket listener;
+
+    private DataInputStream inputStream;
+    private DataOutputStream outputStream;
+
+    @Override
+    public void writeSource(String s) {
+        try {
+            outputStream.writeInt(CustomEditor.SOURCE_CODE);
+            byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
+            outputStream.writeInt(bytes.length);
+            outputStream.write(bytes);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    @Override
+    public String getSource() {
+        try {
+            outputStream.writeInt(CustomEditor.GET_SOURCE_CODE);
+            int length = inputStream.readInt();
+            byte[] bytes = new byte[length];
+            inputStream.readFully(bytes);
+            return new String(bytes, StandardCharsets.UTF_8);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private void sendCode(int code) {
+        try {
+            outputStream.writeInt(code);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    @Override
+    public void accept() {
+        sendCode(CustomEditor.ACCEPT_CODE);
+    }
+
+    @Override
+    public void exit() {
+        sendCode(CustomEditor.EXIT_CODE);
+        inputStream = null;
+        outputStream = null;
+    }
+
+    @Override
+    public void cancel() {
+        sendCode(CustomEditor.CANCEL_CODE);
+    }
+
+    @Override
+    public void testEditor(boolean defaultStartup, String[] args, ReplTest... tests) {
+        ReplTest[] t = new ReplTest[tests.length + 1];
+        t[0] = a -> assertCommandCheckOutput(a, "/seteditor " + executionScript,
+                assertStartsWith("|  Editor set to: " + executionScript));
+        System.arraycopy(tests, 0, t, 1, tests.length);
+        super.testEditor(defaultStartup, args, t);
+    }
+
+    private static boolean isWindows() {
+        return System.getProperty("os.name").startsWith("Windows");
+    }
+
+    @BeforeClass
+    public static void setUp() throws IOException {
+        listener = new ServerSocket(0);
+        listener.setSoTimeout(30000);
+        int localPort = listener.getLocalPort();
+
+        executionScript = Paths.get(isWindows() ? "editor.bat" : "editor.sh").toAbsolutePath();
+        Path java = Paths.get(System.getProperty("java.home")).resolve("bin").resolve("java");
+        try (BufferedWriter writer = Files.newBufferedWriter(executionScript)) {
+            if(!isWindows()) {
+                writer.append(java.toString()).append(" ")
+                        .append(" -cp ").append(System.getProperty("java.class.path"))
+                        .append(" CustomEditor ").append(Integer.toString(localPort)).append(" $@");
+                executionScript.toFile().setExecutable(true);
+            } else {
+                writer.append(java.toString()).append(" ")
+                        .append(" -cp ").append(System.getProperty("java.class.path"))
+                        .append(" CustomEditor ").append(Integer.toString(localPort)).append(" %*");
+            }
+        }
+    }
+
+    private Future<?> task;
+    @Override
+    public void assertEdit(boolean after, String cmd,
+                           Consumer<String> checkInput, Consumer<String> checkOutput, Action action) {
+        if (!after) {
+            setCommandInput(cmd + "\n");
+            task = getExecutor().submit(() -> {
+                try (Socket socket = listener.accept()) {
+                    inputStream = new DataInputStream(socket.getInputStream());
+                    outputStream = new DataOutputStream(socket.getOutputStream());
+                    checkInput.accept(getSource());
+                    action.accept();
+                } catch (SocketTimeoutException e) {
+                    fail("Socket timeout exception.\n Output: " + getCommandOutput() +
+                            "\n, error: " + getCommandErrorOutput());
+                } catch (Throwable e) {
+                    shutdownEditor();
+                    if (e instanceof AssertionError) {
+                        throw (AssertionError) e;
+                    }
+                    throw new RuntimeException(e);
+                }
+            });
+        } else {
+            try {
+                task.get();
+                checkOutput.accept(getCommandOutput());
+            } catch (ExecutionException e) {
+                if (e.getCause() instanceof AssertionError) {
+                    throw (AssertionError) e.getCause();
+                }
+                throw new RuntimeException(e);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Override
+    public void shutdownEditor() {
+        if (outputStream != null) {
+            exit();
+        }
+    }
+
+    @Test
+    public void setUnknownEditor() {
+        test(
+                a -> assertCommand(a, "/seteditor", "|  /seteditor requires a path argument\n"),
+                a -> assertCommand(a, "/seteditor UNKNOWN", "|  Editor set to: UNKNOWN\n"),
+                a -> assertCommand(a, "int a;", null),
+                a -> assertCommand(a, "/e 1",
+                        "|  Edit Error: process IO failure: Cannot run program \"UNKNOWN\": error=2, No such file or directory\n")
+        );
+    }
+
+    @Test(enabled = false)
+    public void testRemoveTempFile() {
+        test(new String[]{"-nostartup"},
+                a -> assertCommandCheckOutput(a, "/seteditor " + executionScript,
+                        assertStartsWith("|  Editor set to: " + executionScript)),
+                a -> assertVariable(a, "int", "a", "0", "0"),
+                a -> assertEditOutput(a, "/e 1", assertStartsWith("|  Edit Error: Failure read edit file:"), () -> {
+                    sendCode(CustomEditor.REMOVE_CODE);
+                    exit();
+                }),
+                a -> assertCommandCheckOutput(a, "/v", assertVariables())
+        );
+    }
+
+    @AfterClass
+    public static void shutdown() throws IOException {
+        executorShutdown();
+        if (listener != null) {
+            listener.close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/HistoryTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test Completion
+ * @build HistoryTest
+ * @run testng HistoryTest
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.BackingStoreException;
+import jdk.internal.jline.console.history.MemoryHistory;
+
+import jdk.jshell.JShell;
+import jdk.jshell.SourceCodeAnalysis;
+import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
+import org.testng.annotations.Test;
+import jdk.internal.jshell.tool.EditingHistory;
+
+import static org.testng.Assert.*;
+
+@Test
+public class HistoryTest {
+
+    public void testHistory() {
+        JShell eval = JShell.builder()
+                .in(new ByteArrayInputStream(new byte[0]))
+                .out(new PrintStream(new ByteArrayOutputStream()))
+                .err(new PrintStream(new ByteArrayOutputStream()))
+                .build();
+        SourceCodeAnalysis analysis = eval.sourceCodeAnalysis();
+        MemoryPreferences prefs = new MemoryPreferences(null, "");
+        EditingHistory history = new EditingHistory(prefs) {
+            @Override protected CompletionInfo analyzeCompletion(String input) {
+                return analysis.analyzeCompletion(input);
+            }
+        };
+        history.add("void test() {");
+        history.add("    System.err.println(1);");
+        history.add("}");
+        history.add("/exit");
+
+        previousAndAssert(history, "/exit");
+
+        history.previous(); history.previous(); history.previous();
+
+        history.add("void test() { /*changed*/");
+
+        previousAndAssert(history, "}");
+        previousAndAssert(history, "    System.err.println(1);");
+        previousAndAssert(history, "void test() {");
+
+        assertFalse(history.previous());
+
+        nextAndAssert(history, "    System.err.println(1);");
+        nextAndAssert(history, "}");
+        nextAndAssert(history, "");
+
+        history.add("    System.err.println(2);");
+        history.add("} /*changed*/");
+
+        assertEquals(history.size(), 7);
+
+        history.save();
+
+        history = new EditingHistory(prefs) {
+            @Override protected CompletionInfo analyzeCompletion(String input) {
+                return analysis.analyzeCompletion(input);
+            }
+        };
+
+        previousSnippetAndAssert(history, "void test() { /*changed*/");
+        previousSnippetAndAssert(history, "/exit");
+        previousSnippetAndAssert(history, "void test() {");
+
+        assertFalse(history.previousSnippet());
+
+        nextSnippetAndAssert(history, "/exit");
+        nextSnippetAndAssert(history, "void test() { /*changed*/");
+        nextSnippetAndAssert(history, "");
+
+        assertFalse(history.nextSnippet());
+
+        history.add("{");
+        history.add("}");
+
+        history.save();
+
+        history = new EditingHistory(prefs) {
+            @Override protected CompletionInfo analyzeCompletion(String input) {
+                return analysis.analyzeCompletion(input);
+            }
+        };
+
+        previousSnippetAndAssert(history, "{");
+        previousSnippetAndAssert(history, "void test() { /*changed*/");
+        previousSnippetAndAssert(history, "/exit");
+        previousSnippetAndAssert(history, "void test() {");
+
+        while (history.next());
+
+        history.add("/*current1*/");
+        history.add("/*current2*/");
+        history.add("/*current3*/");
+
+        assertEquals(history.currentSessionEntries(), Arrays.asList("/*current1*/", "/*current2*/", "/*current3*/"));
+
+        history.remove(0);
+
+        assertEquals(history.currentSessionEntries(), Arrays.asList("/*current1*/", "/*current2*/", "/*current3*/"));
+
+        while (history.size() > 2)
+            history.remove(0);
+
+        assertEquals(history.currentSessionEntries(), Arrays.asList("/*current2*/", "/*current3*/"));
+
+        for (int i = 0; i < MemoryHistory.DEFAULT_MAX_SIZE * 2; i++) {
+            history.add("/exit");
+        }
+
+        history.add("void test() { /*after full*/");
+        history.add("    System.err.println(1);");
+        history.add("}");
+
+        previousSnippetAndAssert(history, "void test() { /*after full*/");
+    }
+
+    public void testSaveOneHistory() {
+        JShell eval = JShell.builder()
+                .in(new ByteArrayInputStream(new byte[0]))
+                .out(new PrintStream(new ByteArrayOutputStream()))
+                .err(new PrintStream(new ByteArrayOutputStream()))
+                .build();
+        SourceCodeAnalysis analysis = eval.sourceCodeAnalysis();
+        MemoryPreferences prefs = new MemoryPreferences(null, "");
+        EditingHistory history = new EditingHistory(prefs) {
+            @Override protected CompletionInfo analyzeCompletion(String input) {
+                return analysis.analyzeCompletion(input);
+            }
+        };
+
+        history.add("first");
+        history.save();
+    }
+
+    private void previousAndAssert(EditingHistory history, String expected) {
+        assertTrue(history.previous());
+        assertEquals(history.current().toString(), expected);
+    }
+
+    private void nextAndAssert(EditingHistory history, String expected) {
+        assertTrue(history.next());
+        assertEquals(history.current().toString(), expected);
+    }
+
+    private void previousSnippetAndAssert(EditingHistory history, String expected) {
+        assertTrue(history.previousSnippet());
+        assertEquals(history.current().toString(), expected);
+    }
+
+    private void nextSnippetAndAssert(EditingHistory history, String expected) {
+        assertTrue(history.nextSnippet());
+        assertEquals(history.current().toString(), expected);
+    }
+
+    private static final class MemoryPreferences extends AbstractPreferences {
+
+        private final Map<String, String> key2Value = new HashMap<>();
+        private final Map<String, MemoryPreferences> key2SubNode = new HashMap<>();
+
+        public MemoryPreferences(AbstractPreferences parent, String name) {
+            super(parent, name);
+        }
+
+        @Override
+        protected void putSpi(String key, String value) {
+            key2Value.put(key, value);
+        }
+
+        @Override
+        protected String getSpi(String key) {
+            return key2Value.get(key);
+        }
+
+        @Override
+        protected void removeSpi(String key) {
+            key2Value.remove(key);
+        }
+
+        @Override
+        protected void removeNodeSpi() throws BackingStoreException {
+            ((MemoryPreferences) parent()).key2SubNode.remove(name());
+        }
+
+        @Override
+        protected String[] keysSpi() throws BackingStoreException {
+            return key2Value.keySet().toArray(new String[key2Value.size()]);
+        }
+
+        @Override
+        protected String[] childrenNamesSpi() throws BackingStoreException {
+            return key2SubNode.keySet().toArray(new String[key2SubNode.size()]);
+        }
+
+        @Override
+        protected AbstractPreferences childSpi(String name) {
+            return key2SubNode.computeIfAbsent(name, n -> new MemoryPreferences(this, n));
+        }
+
+        @Override
+        protected void syncSpi() throws BackingStoreException {}
+
+        @Override
+        protected void flushSpi() throws BackingStoreException {}
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/IOTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test input/output
+ * @build KullaTesting TestingInputStream
+ * @run testng IOTest
+ */
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+@Test
+public class IOTest extends KullaTesting {
+
+    String LINE_SEPARATOR = System.getProperty("line.separator");
+
+    public void testOutput() {
+        assertEval("System.out.println(\"Test\");");
+        assertEquals(getOutput(), "Test" + LINE_SEPARATOR);
+    }
+
+    public void testErrorOutput() {
+        assertEval("System.err.println(\"Oops\");");
+        assertEquals(getErrorOutput(), "Oops" + LINE_SEPARATOR);
+    }
+
+    public void testInput() {
+        setInput("x");
+        assertEval("(char)System.in.read();", "'x'");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/IdGeneratorTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test custom id generators
+ * @build KullaTesting TestingInputStream
+ * @run testng IdGeneratorTest
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.function.Supplier;
+
+import jdk.jshell.EvalException;
+import jdk.jshell.JShell;
+import jdk.jshell.PersistentSnippet;
+import jdk.jshell.SnippetEvent;
+import jdk.jshell.UnresolvedReferenceException;
+import jdk.jshell.VarSnippet;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+@Test
+public class IdGeneratorTest {
+
+    public JShell.Builder getBuilder() {
+        TestingInputStream inStream = new TestingInputStream();
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream errStream = new ByteArrayOutputStream();
+        return JShell.builder()
+                .in(inStream)
+                .out(new PrintStream(outStream))
+                .err(new PrintStream(errStream));
+    }
+
+    public void testTempNameGenerator() {
+        JShell.Builder builder = getBuilder().tempVariableNameGenerator(new Supplier<String>() {
+            int count = 0;
+
+            @Override
+            public String get() {
+                return "temp" + ++count;
+            }
+        });
+        try (JShell jShell = builder.build()) {
+            for (int i = 0; i < 3; ++i) {
+                VarSnippet v = (VarSnippet) jShell.eval("2 + " + (i + 1)).get(0).snippet();
+                assertEquals("temp" + (i + 1), v.name(), "Custom id: ");
+            }
+        }
+    }
+
+    public void testResetTempNameGenerator() {
+        JShell.Builder builder = getBuilder().tempVariableNameGenerator(() -> {
+            throw new AssertionError("Should not be called");
+        });
+        try (JShell jShell = builder.tempVariableNameGenerator(null).build()) {
+            jShell.eval("2 + 2");
+        }
+    }
+
+    public void testIdGenerator() {
+        JShell.Builder builder = getBuilder().idGenerator(((snippet, id) -> "custom" + id));
+        try (JShell jShell = builder.build()) {
+            List<SnippetEvent> eval = jShell.eval("int a, b;");
+            checkIds(eval);
+            checkIds(jShell.drop((PersistentSnippet) eval.get(0).snippet()));
+        }
+    }
+
+    private void checkIds(List<SnippetEvent> events) {
+        for (SnippetEvent event : events) {
+            assertTrue(event.snippet().id().startsWith("custom"), "Not started with \"custom\": "
+                    + event.snippet().id());
+        }
+    }
+
+    @Test(enabled = false) // TODO 8133507
+    public void testIdInException() {
+        JShell.Builder builder = getBuilder().idGenerator(((snippet, id) -> "custom" + id));
+        try (JShell jShell = builder.build()) {
+            EvalException evalException = (EvalException) jShell.eval("throw new Error();").get(0).exception();
+            for (StackTraceElement ste : evalException.getStackTrace()) {
+                assertTrue(ste.getFileName().startsWith("custom"), "Not started with \"custom\": "
+                        + ste.getFileName());
+            }
+            jShell.eval("void f() { g(); }");
+            UnresolvedReferenceException unresolvedException = (UnresolvedReferenceException) jShell.eval("f();").get(0).exception();
+            for (StackTraceElement ste : unresolvedException.getStackTrace()) {
+                assertTrue(ste.getFileName().startsWith("custom"), "Not started with \"custom\": "
+                        + ste.getFileName());
+            }
+        }
+    }
+
+    public void testResetIdGenerator() {
+        JShell.Builder builder = getBuilder().idGenerator((sn, id) -> {
+            throw new AssertionError("Should not be called");
+        });
+        try (JShell jShell = builder.idGenerator(null).build()) {
+            jShell.eval("2 + 2");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/IgnoreTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test the ignoring of comments and certain modifiers
+ * @build KullaTesting TestingInputStream
+ * @run testng IgnoreTest
+ */
+
+import org.testng.annotations.Test;
+
+import jdk.jshell.MethodSnippet;
+import jdk.jshell.TypeDeclSnippet;
+import jdk.jshell.VarSnippet;
+import static jdk.jshell.Snippet.Status.VALID;
+import static jdk.jshell.Snippet.SubKind.*;
+
+@Test
+public class IgnoreTest extends KullaTesting {
+
+    public void testComment() {
+        assertVarKeyMatch("//t1\n int//t2\n x//t3\n =//t4\n 12//t5\n ;//t6\n",
+                true, "x", VAR_DECLARATION_WITH_INITIALIZER_SUBKIND, "int", added(VALID));
+        assertVarKeyMatch("//t1\n int//t2\n y//t3\n =//t4\n 12//t5\n ;//t6",
+                true, "y", VAR_DECLARATION_WITH_INITIALIZER_SUBKIND, "int", added(VALID));
+        assertDeclarationKeyMatch("       //t0\n" +
+                        "       int//t0\n" +
+                        "       f//t0\n" +
+                        "       (//t0\n" +
+                        "       int x//t1\n" +
+                        "       ) {//t2\n" +
+                        "       return x+//t3\n" +
+                        "       x//t4\n" +
+                        "       ;//t5\n" +
+                        "       }//t6",
+                false, "f", METHOD_SUBKIND, added(VALID));
+    }
+
+    public void testVarModifier() {
+        VarSnippet x1 = (VarSnippet) assertDeclareWarn1("public int x1;", "jdk.eval.warn.illegal.modifiers");
+        assertVariableDeclSnippet(x1, "x1", "int", VALID, VAR_DECLARATION_SUBKIND, 0, 1);
+        VarSnippet x2 = (VarSnippet) assertDeclareWarn1("protected int x2;", "jdk.eval.warn.illegal.modifiers");
+        assertVariableDeclSnippet(x2, "x2", "int", VALID, VAR_DECLARATION_SUBKIND, 0, 1);
+        VarSnippet x3 = (VarSnippet) assertDeclareWarn1("private int x3;", "jdk.eval.warn.illegal.modifiers");
+        assertVariableDeclSnippet(x3, "x3", "int", VALID, VAR_DECLARATION_SUBKIND, 0, 1);
+        VarSnippet x4 = (VarSnippet) assertDeclareWarn1("static int x4;", "jdk.eval.warn.illegal.modifiers");
+        assertVariableDeclSnippet(x4, "x4", "int", VALID, VAR_DECLARATION_SUBKIND, 0, 1);
+        VarSnippet x5 = (VarSnippet) assertDeclareWarn1("final int x5;", "jdk.eval.warn.illegal.modifiers");
+        assertVariableDeclSnippet(x5, "x5", "int", VALID, VAR_DECLARATION_SUBKIND, 0, 1);
+    }
+
+    public void testMethodModifier() {
+        MethodSnippet m1 = (MethodSnippet) assertDeclareWarn1("public void m1() {}", "jdk.eval.warn.illegal.modifiers");
+        assertMethodDeclSnippet(m1, "m1", "()void", VALID, 0, 1);
+        MethodSnippet m2 = (MethodSnippet) assertDeclareWarn1("protected void m2() {}", "jdk.eval.warn.illegal.modifiers");
+        assertMethodDeclSnippet(m2, "m2", "()void", VALID, 0, 1);
+        MethodSnippet m3 = (MethodSnippet) assertDeclareWarn1("private void m3() {}", "jdk.eval.warn.illegal.modifiers");
+        assertMethodDeclSnippet(m3, "m3", "()void", VALID, 0, 1);
+        MethodSnippet m4 = (MethodSnippet) assertDeclareWarn1("static void m4() {}", "jdk.eval.warn.illegal.modifiers");
+        assertMethodDeclSnippet(m4, "m4", "()void", VALID, 0, 1);
+        MethodSnippet m5 = (MethodSnippet) assertDeclareWarn1("final void m5() {}", "jdk.eval.warn.illegal.modifiers");
+        assertMethodDeclSnippet(m5, "m5", "()void", VALID, 0, 1);
+    }
+
+    public void testClassModifier() {
+        TypeDeclSnippet c1 = (TypeDeclSnippet) assertDeclareWarn1("public class C1 {}", "jdk.eval.warn.illegal.modifiers");
+        assertTypeDeclSnippet(c1, "C1", VALID, CLASS_SUBKIND, 0, 1);
+        TypeDeclSnippet c2 = (TypeDeclSnippet) assertDeclareWarn1("protected class C2 {}", "jdk.eval.warn.illegal.modifiers");
+        assertTypeDeclSnippet(c2, "C2", VALID, CLASS_SUBKIND, 0, 1);
+        TypeDeclSnippet c3 = (TypeDeclSnippet) assertDeclareWarn1("private class C3 {}", "jdk.eval.warn.illegal.modifiers");
+        assertTypeDeclSnippet(c3, "C3", VALID, CLASS_SUBKIND, 0, 1);
+        TypeDeclSnippet c4 = (TypeDeclSnippet) assertDeclareWarn1("static class C4 {}", "jdk.eval.warn.illegal.modifiers");
+        assertTypeDeclSnippet(c4, "C4", VALID, CLASS_SUBKIND, 0, 1);
+        TypeDeclSnippet c5 = (TypeDeclSnippet) assertDeclareWarn1("final class C5 {}", "jdk.eval.warn.illegal.modifiers");
+        assertTypeDeclSnippet(c5, "C5", VALID, CLASS_SUBKIND, 0, 1);
+    }
+
+    public void testInsideModifier() {
+        assertEval("import static java.lang.reflect.Modifier.*;");
+        assertEval("class C {"
+                + "public int z;"
+                + "final int f = 3;"
+                + "protected int a;"
+                + "private void m() {}"
+                + "static void b() {}"
+                + "}");
+        assertEval("C.class.getDeclaredField(\"z\").getModifiers() == PUBLIC;", "true");
+        assertEval("C.class.getDeclaredField(\"f\").getModifiers() == FINAL;", "true");
+        assertEval("C.class.getDeclaredField(\"a\").getModifiers() == PROTECTED;", "true");
+        assertEval("C.class.getDeclaredMethod(\"m\").getModifiers() == PRIVATE;", "true");
+        assertEval("C.class.getDeclaredMethod(\"b\").getModifiers() == STATIC;", "true");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/IllegalArgumentExceptionTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Testing IllegalArgumentException.
+ * @build KullaTesting TestingInputStream IllegalArgumentExceptionTest
+ * @run testng IllegalArgumentExceptionTest
+ */
+
+import java.util.function.Consumer;
+
+import jdk.jshell.DeclarationSnippet;
+import jdk.jshell.PersistentSnippet;
+import jdk.jshell.Snippet;
+import jdk.jshell.VarSnippet;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.fail;
+import static jdk.jshell.Snippet.Status.VALID;
+
+@Test
+public class IllegalArgumentExceptionTest extends KullaTesting {
+
+    private void testIllegalArgumentException(Consumer<Snippet> action) {
+        Snippet key = varKey(assertEval("int value;", added(VALID)));
+        tearDown();
+        setUp();
+        assertEval("double value;");
+        try {
+            action.accept(key);
+            fail("Exception expected.");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    public void testVarValue() {
+        testIllegalArgumentException((key) -> getState().varValue((VarSnippet) key));
+    }
+
+    public void testStatus() {
+        testIllegalArgumentException((key) -> getState().status(key));
+    }
+
+    public void testDrop() {
+        testIllegalArgumentException((key) -> getState().drop((PersistentSnippet) key));
+    }
+
+    public void testUnresolved() {
+        testIllegalArgumentException((key) -> getState().unresolvedDependencies((DeclarationSnippet) key));
+    }
+
+    public void testDiagnostics() {
+        testIllegalArgumentException((key) -> getState().diagnostics(key));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ImportTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test imports
+ * @library /tools/lib
+ * @build KullaTesting TestingInputStream ToolBox ExpectedDiagnostic
+ * @run testng ImportTest
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import javax.tools.Diagnostic;
+
+import jdk.jshell.Snippet;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.VALID;
+import static jdk.jshell.Snippet.Status.OVERWRITTEN;
+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;
+
+@Test
+public class ImportTest extends KullaTesting {
+
+    public void testImport() {
+        assertImportKeyMatch("import java.util.List;", "List", SINGLE_TYPE_IMPORT_SUBKIND, added(VALID));
+        assertImportKeyMatch("import java.util.ArrayList;", "ArrayList", SINGLE_TYPE_IMPORT_SUBKIND, added(VALID));
+        assertEval("List<Integer> list = new ArrayList<>();");
+        assertEval("list.add(45);");
+        assertEval("list.size();", "1");
+    }
+
+    public void testImportOnDemand() {
+        assertImportKeyMatch("import java.util.*;", "java.util.*", TYPE_IMPORT_ON_DEMAND_SUBKIND, added(VALID));
+        assertEval("List<Integer> list = new ArrayList<>();");
+        assertEval("list.add(45);");
+        assertEval("list.size();", "1");
+    }
+
+    public void testImportStatic() {
+        assertImportKeyMatch("import static java.lang.Math.PI;", "PI", SINGLE_STATIC_IMPORT_SUBKIND, added(VALID));
+        assertEval("new Double(PI).toString().substring(0, 16).equals(\"3.14159265358979\");", "true");
+    }
+
+    public void testImportStaticOnDemand() {
+        assertImportKeyMatch("import static java.lang.Math.*;", "java.lang.Math.*", STATIC_IMPORT_ON_DEMAND_SUBKIND, added(VALID));
+        assertEval("abs(cos(PI / 2)) < 0.00001;", "true");
+    }
+
+    @Test(enabled = false) // TODO 8129418
+    public void testUnknownPackage() {
+        assertDeclareFail("import unknown.qqq;",
+                new ExpectedDiagnostic("compiler.err.doesnt.exist", 7, 18, 14, -1, -1, Diagnostic.Kind.ERROR));
+        assertDeclareFail("import unknown.*;",
+                new ExpectedDiagnostic("compiler.err.doesnt.exist", 7, 15, 7, -1, -1, Diagnostic.Kind.ERROR));
+    }
+
+    public void testBogusImportIgnoredInFuture() {
+        assertDeclareFail("import unknown.qqq;", "compiler.err.doesnt.exist");
+        assertDeclareFail("import unknown.*;", "compiler.err.doesnt.exist");
+        assertEval("2 + 2;");
+    }
+
+    public void testBadImport() {
+        assertDeclareFail("import static java.lang.reflect.Modifier;",
+                new ExpectedDiagnostic("compiler.err.cant.resolve.location", 14, 31, 23, -1, -1, Diagnostic.Kind.ERROR));
+    }
+
+    public void testBadSyntaxImport() {
+        assertDeclareFail("import not found.*;",
+                new ExpectedDiagnostic("compiler.err.expected", 10, 10, 10, -1, -1, Diagnostic.Kind.ERROR));
+    }
+
+    public void testImportRedefinition() {
+        Compiler compiler = new Compiler();
+        Path path = Paths.get("testImport");
+        compiler.compile(path, "package util; public class ArrayList { public String toString() { return \"MyList\"; } }");
+        compiler.compile(path, "package util; public class A { public static class ArrayList {\n" +
+                "public String toString() { return \"MyInnerList\"; } } }");
+        addToClasspath(compiler.getPath(path));
+
+        assertImportKeyMatch("import util.*;", "util.*", TYPE_IMPORT_ON_DEMAND_SUBKIND, added(VALID));
+        assertEval("new ArrayList();", "MyList", added(VALID));
+
+        Snippet import0 = assertImportKeyMatch("import java.util.ArrayList;", "ArrayList", SINGLE_TYPE_IMPORT_SUBKIND, added(VALID));
+        assertEval("new ArrayList();", "[]");
+
+        Snippet import1 = assertImportKeyMatch("import util.ArrayList;", "ArrayList", SINGLE_TYPE_IMPORT_SUBKIND,
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(import0, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("new ArrayList();", "MyList");
+
+        Snippet import2 = assertImportKeyMatch("import java.util.ArrayList;", "ArrayList", SINGLE_TYPE_IMPORT_SUBKIND,
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(import1, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("new ArrayList();", "[]");
+
+        Snippet import3 = assertImportKeyMatch("import util.A.ArrayList;", "ArrayList", SINGLE_TYPE_IMPORT_SUBKIND,
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(import2, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("new ArrayList();", "MyInnerList");
+
+        Snippet import4 = assertImportKeyMatch("import java.util.ArrayList;", "ArrayList", SINGLE_TYPE_IMPORT_SUBKIND,
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(import3, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("new ArrayList();", "[]");
+
+        Snippet import5 = assertImportKeyMatch("import static util.A.ArrayList;", "ArrayList", SINGLE_STATIC_IMPORT_SUBKIND,
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(import4, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("new ArrayList();", "MyInnerList");
+    }
+
+    public void testImportMemberRedefinition() {
+        Compiler compiler = new Compiler();
+        Path path = Paths.get("testImport");
+        compiler.compile(path, "package util; public class A {" +
+                "public static String field = \"A\";" +
+                "public static String method() { return \"A\"; } }");
+        compiler.compile(path, "package util; public class B {" +
+                "public static String field = \"B\";" +
+                "public static String method() { return \"B\"; } }");
+        addToClasspath(compiler.getPath(path));
+
+        assertImportKeyMatch("import static util.B.*;", "util.B.*", STATIC_IMPORT_ON_DEMAND_SUBKIND, added(VALID));
+        assertEval("field;", "\"B\"");
+        assertEval("method();", "\"B\"");
+
+        assertImportKeyMatch("import static util.A.method;", "method", SINGLE_STATIC_IMPORT_SUBKIND, added(VALID));
+        assertEval("field;", "\"B\"");
+        assertEval("method();", "\"A\"");
+
+        assertImportKeyMatch("import static util.A.field;", "field", SINGLE_STATIC_IMPORT_SUBKIND, added(VALID));
+        assertEval("field;", "\"A\"");
+        assertEval("method();", "\"A\"");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/JShellStateClosedTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Testing IllegalStateException.
+ * @build KullaTesting TestingInputStream JShellStateClosedTest
+ * @run testng JShellStateClosedTest
+ */
+
+import java.util.function.Consumer;
+
+import jdk.jshell.DeclarationSnippet;
+import jdk.jshell.PersistentSnippet;
+import jdk.jshell.Snippet;
+import jdk.jshell.VarSnippet;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.fail;
+
+@Test
+public class JShellStateClosedTest extends KullaTesting {
+
+    private void testStateClosedException(Runnable action) {
+        getState().close();
+        try {
+            action.run();
+            fail("Exception expected");
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+    }
+
+    public void testClasses() {
+        testStateClosedException(() -> getState().types());
+    }
+
+    public void testVariables() {
+        testStateClosedException(() -> getState().variables());
+    }
+
+    public void testMethods() {
+        testStateClosedException(() -> getState().methods());
+    }
+
+    public void testKeys() {
+        testStateClosedException(() -> getState().snippets());
+    }
+
+    public void testEval() {
+        testStateClosedException(() -> getState().eval("int a;"));
+    }
+
+    private void testStateClosedException(Consumer<Snippet> action) {
+        Snippet k = varKey(assertEval("int a;"));
+        getState().close();
+        try {
+            action.accept(k);
+            fail("IllegalStateException expected since closed");
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+    }
+
+    private void testStateClosedWithoutException(Consumer<Snippet> action) {
+        Snippet k = varKey(assertEval("int a;"));
+        getState().close();
+        try {
+            action.accept(k);
+        } catch (IllegalStateException e) {
+            fail("Expected no IllegalStateException even though closed");
+        }
+    }
+
+    public void testStatus() {
+        testStateClosedWithoutException((key) -> getState().status(key));
+    }
+
+    public void testVarValue() {
+        testStateClosedException((key) -> getState().varValue((VarSnippet) key));
+    }
+
+    public void testDrop() {
+        testStateClosedException((key) -> getState().drop((PersistentSnippet) key));
+    }
+
+    public void testUnresolved() {
+        testStateClosedWithoutException((key) -> getState().unresolvedDependencies((DeclarationSnippet) key));
+    }
+
+    public void testDiagnostics() {
+        testStateClosedWithoutException((key) -> getState().diagnostics(key));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/KullaCompletenessStressTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test SourceCodeAnalysis
+ * @build KullaTesting TestingInputStream KullaCompletenessStressTest CompletenessStressTest
+ * @run testng KullaCompletenessStressTest
+ */
+
+import java.io.File;
+
+import org.testng.annotations.Test;
+
+@Test
+public class KullaCompletenessStressTest extends CompletenessStressTest {
+    @Override
+    public File[] getDirectoriesToTest() {
+        return new File[]{ getKullaSourceDirectory() };
+    }
+
+    public File getKullaSourceDirectory() {
+        String src = System.getProperty("test.src");
+        File file;
+        if (src == null) {
+            file = new File("../src/jdk.jshell/share/classes");
+        } else {
+            file = new File(src, "../../../src/jdk.jshell/share/classes");
+        }
+        return file;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/KullaTesting.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,1144 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.StringWriter;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.tools.Diagnostic;
+
+import jdk.jshell.EvalException;
+import jdk.jshell.JShell;
+import jdk.jshell.JShell.Subscription;
+import jdk.jshell.Snippet;
+import jdk.jshell.DeclarationSnippet;
+import jdk.jshell.ExpressionSnippet;
+import jdk.jshell.ImportSnippet;
+import jdk.jshell.Snippet.Kind;
+import jdk.jshell.MethodSnippet;
+import jdk.jshell.PersistentSnippet;
+import jdk.jshell.Snippet.Status;
+import jdk.jshell.Snippet.SubKind;
+import jdk.jshell.TypeDeclSnippet;
+import jdk.jshell.VarSnippet;
+import jdk.jshell.SnippetEvent;
+import jdk.jshell.SourceCodeAnalysis;
+import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
+import jdk.jshell.SourceCodeAnalysis.Completeness;
+import jdk.jshell.SourceCodeAnalysis.Suggestion;
+import jdk.jshell.UnresolvedReferenceException;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+import jdk.jshell.Diag;
+import static jdk.jshell.Snippet.Status.*;
+import static org.testng.Assert.*;
+import static jdk.jshell.Snippet.SubKind.METHOD_SUBKIND;
+
+public class KullaTesting {
+
+    public static final String IGNORE_VALUE = "<ignore-value>";
+    public static final Class<? extends Throwable> IGNORE_EXCEPTION = (new Throwable() {}).getClass();
+    public static final Snippet MAIN_SNIPPET;
+
+    private SourceCodeAnalysis analysis = null;
+    private JShell state = null;
+    private TestingInputStream inStream = null;
+    private ByteArrayOutputStream outStream = null;
+    private ByteArrayOutputStream errStream = null;
+
+    private Map<String, Snippet> idToSnippet = new LinkedHashMap<>();
+    private Set<Snippet> allSnippets = new LinkedHashSet<>();
+    private List<String> classpath;
+
+    static {
+        JShell js = JShell.create();
+        MAIN_SNIPPET = js.eval("MAIN_SNIPPET").get(0).snippet();
+        js.close();
+        assertTrue(MAIN_SNIPPET != null, "Bad MAIN_SNIPPET set-up -- must not be null");
+    }
+
+    public enum DiagCheck {
+        DIAG_OK,
+        DIAG_WARNING,
+        DIAG_ERROR,
+        DIAG_IGNORE
+    }
+
+    public void setInput(String s) {
+        inStream.setInput(s);
+    }
+
+    public String getOutput() {
+        String s = outStream.toString();
+        outStream.reset();
+        return s;
+    }
+
+    public String getErrorOutput() {
+        String s = errStream.toString();
+        errStream.reset();
+        return s;
+    }
+
+    /**
+     * @return the analysis
+     */
+    public SourceCodeAnalysis getAnalysis() {
+        if (analysis == null) {
+            analysis = state.sourceCodeAnalysis();
+        }
+        return analysis;
+    }
+
+    /**
+     * @return the state
+     */
+    public JShell getState() {
+        return state;
+    }
+
+    public List<Snippet> getActiveKeys() {
+        return allSnippets.stream()
+                .filter(k -> getState().status(k).isActive)
+                .collect(Collectors.toList());
+    }
+
+    public void addToClasspath(String path) {
+        classpath.add(path);
+        getState().addToClasspath(path);
+    }
+
+    public void addToClasspath(Path path) {
+        addToClasspath(path.toString());
+    }
+
+    @BeforeMethod
+    public void setUp() {
+        inStream = new TestingInputStream();
+        outStream = new ByteArrayOutputStream();
+        errStream = new ByteArrayOutputStream();
+        state = JShell.builder()
+                .in(inStream)
+                .out(new PrintStream(outStream))
+                .err(new PrintStream(errStream))
+                .build();
+        allSnippets = new LinkedHashSet<>();
+        idToSnippet = new LinkedHashMap<>();
+        classpath = new ArrayList<>();
+    }
+
+    @AfterMethod
+    public void tearDown() {
+        if (state != null) state.close();
+        state = null;
+        analysis = null;
+        allSnippets = null;
+        idToSnippet = null;
+        classpath = null;
+    }
+
+    public List<String> assertUnresolvedDependencies(DeclarationSnippet key, int unresolvedSize) {
+        List<String> unresolved = getState().unresolvedDependencies(key);
+        assertEquals(unresolved.size(), unresolvedSize, "Input: " + key.source() + ", checking unresolved: ");
+        return unresolved;
+    }
+
+    public DeclarationSnippet assertUnresolvedDependencies1(DeclarationSnippet key, Status status, String name) {
+        List<String> unresolved = assertUnresolvedDependencies(key, 1);
+        String input = key.source();
+        assertEquals(unresolved.size(), 1, "Given input: " + input + ", checking unresolved");
+        assertEquals(unresolved.get(0), name, "Given input: " + input + ", checking unresolved: ");
+        assertEquals(getState().status(key), status, "Given input: " + input + ", checking status: ");
+        return key;
+    }
+
+    public MethodSnippet assertEvalUnresolvedException(String input, String name, int unresolvedSize, int diagnosticsSize) {
+        List<SnippetEvent> events = assertEval(input, null, UnresolvedReferenceException.class, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, null);
+        SnippetEvent ste = events.get(0);
+        MethodSnippet methodKey = ((UnresolvedReferenceException) ste.exception()).getMethodSnippet();
+        assertEquals(methodKey.name(), name, "Given input: " + input + ", checking name");
+        assertEquals(getState().unresolvedDependencies(methodKey).size(), unresolvedSize, "Given input: " + input + ", checking unresolved");
+        assertEquals(getState().diagnostics(methodKey).size(), diagnosticsSize, "Given input: " + input + ", checking diagnostics");
+        return methodKey;
+    }
+
+    public Snippet assertKeyMatch(String input, boolean isExecutable, SubKind expectedSubKind, STEInfo mainInfo, STEInfo... updates) {
+        Snippet key = key(assertEval(input, IGNORE_VALUE, mainInfo, updates));
+        String source = key.source();
+        assertEquals(source, input, "Key \"" + input + "\" source mismatch, got: " + source + ", expected: " + input);
+        SubKind subkind = key.subKind();
+        assertEquals(subkind, expectedSubKind, "Key \"" + input + "\" subkind mismatch, got: "
+                + subkind + ", expected: " + expectedSubKind);
+        assertEquals(subkind.isExecutable(), isExecutable, "Key \"" + input + "\", expected isExecutable: "
+                + isExecutable + ", got: " + subkind.isExecutable());
+        Snippet.Kind expectedKind = getKind(key);
+        assertEquals(key.kind(), expectedKind, "Checking kind: ");
+        assertEquals(expectedSubKind.kind(), expectedKind, "Checking kind: ");
+        return key;
+    }
+
+    private Kind getKind(Snippet key) {
+        SubKind expectedSubKind = key.subKind();
+        Kind expectedKind;
+        switch (expectedSubKind) {
+            case SINGLE_TYPE_IMPORT_SUBKIND:
+            case SINGLE_STATIC_IMPORT_SUBKIND:
+            case TYPE_IMPORT_ON_DEMAND_SUBKIND:
+            case STATIC_IMPORT_ON_DEMAND_SUBKIND:
+                expectedKind = Kind.IMPORT;
+                break;
+            case CLASS_SUBKIND:
+            case INTERFACE_SUBKIND:
+            case ENUM_SUBKIND:
+            case ANNOTATION_TYPE_SUBKIND:
+                expectedKind = Kind.TYPE_DECL;
+                break;
+            case METHOD_SUBKIND:
+                expectedKind = Kind.METHOD;
+                break;
+            case VAR_DECLARATION_SUBKIND:
+            case TEMP_VAR_EXPRESSION_SUBKIND:
+            case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND:
+                expectedKind = Kind.VAR;
+                break;
+            case VAR_VALUE_SUBKIND:
+            case ASSIGNMENT_SUBKIND:
+                expectedKind = Kind.EXPRESSION;
+                break;
+            case STATEMENT_SUBKIND:
+                expectedKind = Kind.STATEMENT;
+                break;
+            case UNKNOWN_SUBKIND:
+                expectedKind = Kind.ERRONEOUS;
+                break;
+            default:
+                throw new AssertionError("Unsupported key: " + key.getClass().getCanonicalName());
+        }
+        return expectedKind;
+    }
+
+    public ImportSnippet assertImportKeyMatch(String input, String name, SubKind subkind, STEInfo mainInfo, STEInfo... updates) {
+        Snippet key = assertKeyMatch(input, false, subkind, mainInfo, updates);
+
+        assertTrue(key instanceof ImportSnippet, "Expected an ImportKey, got: " + key.getClass().getName());
+        ImportSnippet importKey = (ImportSnippet) key;
+        assertEquals(importKey.name(), name, "Input \"" + input +
+                "\" name mismatch, got: " + importKey.name() + ", expected: " + name);
+        assertEquals(importKey.kind(), Kind.IMPORT, "Checking kind: ");
+        return importKey;
+    }
+
+    public DeclarationSnippet assertDeclarationKeyMatch(String input, boolean isExecutable, String name, SubKind subkind, STEInfo mainInfo, STEInfo... updates) {
+        Snippet key = assertKeyMatch(input, isExecutable, subkind, mainInfo, updates);
+
+        assertTrue(key instanceof DeclarationSnippet, "Expected a DeclarationKey, got: " + key.getClass().getName());
+        DeclarationSnippet declKey = (DeclarationSnippet) key;
+        assertEquals(declKey.name(), name, "Input \"" + input +
+                "\" name mismatch, got: " + declKey.name() + ", expected: " + name);
+        return declKey;
+    }
+
+    public VarSnippet assertVarKeyMatch(String input, boolean isExecutable, String name, SubKind kind, String typeName, STEInfo mainInfo, STEInfo... updates) {
+        Snippet sn = assertDeclarationKeyMatch(input, isExecutable, name, kind, mainInfo, updates);
+        assertTrue(sn instanceof VarSnippet, "Expected a VarKey, got: " + sn.getClass().getName());
+        VarSnippet variableKey = (VarSnippet) sn;
+        String signature = variableKey.typeName();
+        assertEquals(signature, typeName, "Key \"" + input +
+                "\" typeName mismatch, got: " + signature + ", expected: " + typeName);
+        assertEquals(variableKey.kind(), Kind.VAR, "Checking kind: ");
+        return variableKey;
+    }
+
+    public void assertExpressionKeyMatch(String input, String name, SubKind kind, String typeName) {
+        Snippet key = assertKeyMatch(input, true, kind, added(VALID));
+        assertTrue(key instanceof ExpressionSnippet, "Expected a ExpressionKey, got: " + key.getClass().getName());
+        ExpressionSnippet exprKey = (ExpressionSnippet) key;
+        assertEquals(exprKey.name(), name, "Input \"" + input +
+                "\" name mismatch, got: " + exprKey.name() + ", expected: " + name);
+        assertEquals(exprKey.typeName(), typeName, "Key \"" + input +
+                "\" typeName mismatch, got: " + exprKey.typeName() + ", expected: " + typeName);
+        assertEquals(exprKey.kind(), Kind.EXPRESSION, "Checking kind: ");
+    }
+
+    // For expressions throwing an EvalException
+    public SnippetEvent assertEvalException(String input) {
+        List<SnippetEvent> events = assertEval(input, null, EvalException.class,
+                DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, null);
+        return events.get(0);
+    }
+
+
+    public List<SnippetEvent> assertEvalFail(String input) {
+        return assertEval(input, null, null,
+                DiagCheck.DIAG_ERROR, DiagCheck.DIAG_IGNORE, added(REJECTED));
+    }
+
+    public List<SnippetEvent> assertEval(String input) {
+        return assertEval(input, IGNORE_VALUE, null, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, added(VALID));
+    }
+
+    public List<SnippetEvent> assertEval(String input, String value) {
+        return assertEval(input, value, null, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, added(VALID));
+    }
+
+    public List<SnippetEvent> assertEval(String input, STEInfo mainInfo, STEInfo... updates) {
+        return assertEval(input, IGNORE_VALUE, null, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, mainInfo, updates);
+    }
+
+    public List<SnippetEvent> assertEval(String input, String value,
+            STEInfo mainInfo, STEInfo... updates) {
+        return assertEval(input, value, null, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, mainInfo, updates);
+    }
+
+    public List<SnippetEvent> assertEval(String input, DiagCheck diagMain, DiagCheck diagUpdates) {
+        return assertEval(input, IGNORE_VALUE, null, diagMain, diagUpdates, added(VALID));
+    }
+
+    public List<SnippetEvent> assertEval(String input, DiagCheck diagMain, DiagCheck diagUpdates,
+            STEInfo mainInfo, STEInfo... updates) {
+        return assertEval(input, IGNORE_VALUE, null, diagMain, diagUpdates, mainInfo, updates);
+    }
+
+    public List<SnippetEvent> assertEval(String input,
+            String value, Class<? extends Throwable> exceptionClass,
+            DiagCheck diagMain, DiagCheck diagUpdates,
+            STEInfo mainInfo, STEInfo... updates) {
+        return assertEval(input, diagMain, diagUpdates, new EventChain(mainInfo, value, exceptionClass, updates));
+    }
+
+    // Use this directly or usually indirectly for all non-empty calls to eval()
+    public List<SnippetEvent> assertEval(String input,
+           DiagCheck diagMain, DiagCheck diagUpdates, EventChain... eventChains) {
+        return checkEvents(() -> getState().eval(input), "eval(" + input + ")", diagMain, diagUpdates, eventChains);
+    }
+
+    private Map<Snippet, Snippet> closure(List<SnippetEvent> events) {
+        Map<Snippet, Snippet> transitions = new HashMap<>();
+        for (SnippetEvent event : events) {
+            transitions.put(event.snippet(), event.causeSnippet());
+        }
+        Map<Snippet, Snippet> causeSnippets = new HashMap<>();
+        for (Map.Entry<Snippet, Snippet> entry : transitions.entrySet()) {
+            Snippet snippet = entry.getKey();
+            Snippet cause = getInitialCause(transitions, entry.getValue());
+            causeSnippets.put(snippet, cause);
+        }
+        return causeSnippets;
+    }
+
+    private Snippet getInitialCause(Map<Snippet, Snippet> transitions, Snippet snippet) {
+        Snippet result;
+        while ((result = transitions.get(snippet)) != null) {
+            snippet = result;
+        }
+        return snippet;
+    }
+
+    private Map<Snippet, List<SnippetEvent>> groupByCauseSnippet(List<SnippetEvent> events) {
+        Map<Snippet, List<SnippetEvent>> map = new TreeMap<>((a, b) -> a.id().compareTo(b.id()));
+        for (SnippetEvent event : events) {
+            if (event == null) {
+                throw new InternalError("null event found in " + events);
+            }
+            if (event.snippet() == null) {
+                throw new InternalError("null event Snippet found in " + events);
+            }
+            if (event.snippet().id() == null) {
+                throw new InternalError("null event Snippet id() found in " + events);
+            }
+        }
+        for (SnippetEvent event : events) {
+            if (event.causeSnippet() == null) {
+                map.computeIfAbsent(event.snippet(), ($) -> new ArrayList<>()).add(event);
+            }
+        }
+        Map<Snippet, Snippet> causeSnippets = closure(events);
+        for (SnippetEvent event : events) {
+            Snippet causeSnippet = causeSnippets.get(event.snippet());
+            if (causeSnippet != null) {
+                map.get(causeSnippet).add(event);
+            }
+        }
+        for (Map.Entry<Snippet, List<SnippetEvent>> entry : map.entrySet()) {
+            Collections.sort(entry.getValue(),
+                    (a, b) -> a.causeSnippet() == null
+                            ? -1 : b.causeSnippet() == null
+                            ? 1 : a.snippet().id().compareTo(b.snippet().id()));
+        }
+        return map;
+    }
+
+    private List<STEInfo> getInfos(EventChain... eventChains) {
+        List<STEInfo> list = new ArrayList<>();
+        for (EventChain i : eventChains) {
+            list.add(i.mainInfo);
+            Collections.addAll(list, i.updates);
+        }
+        return list;
+    }
+
+    private List<SnippetEvent> checkEvents(Supplier<List<SnippetEvent>> toTest,
+             String descriptor,
+             DiagCheck diagMain, DiagCheck diagUpdates,
+             EventChain... eventChains) {
+        List<SnippetEvent> dispatched = new ArrayList<>();
+        Subscription token = getState().onSnippetEvent(kse -> {
+            if (dispatched.size() > 0 && dispatched.get(dispatched.size() - 1) == null) {
+                throw new RuntimeException("dispatch event after done");
+            }
+            dispatched.add(kse);
+        });
+        List<SnippetEvent> events = toTest.get();
+        getState().unsubscribe(token);
+        assertEquals(dispatched.size(), events.size(), "dispatched event size not the same as event size");
+        for (int i = events.size() - 1; i >= 0; --i) {
+            assertEquals(dispatched.get(i), events.get(i), "Event element " + i + " does not match");
+        }
+        dispatched.add(null); // mark end of dispatchs
+
+        for (SnippetEvent evt : events) {
+            assertTrue(evt.snippet() != null, "key must never be null, but it was for: " + descriptor);
+            assertTrue(evt.previousStatus() != null, "previousStatus must never be null, but it was for: " + descriptor);
+            assertTrue(evt.status() != null, "status must never be null, but it was for: " + descriptor);
+            assertTrue(evt.status() != NONEXISTENT, "status must not be NONEXISTENT: " + descriptor);
+            if (evt.previousStatus() != NONEXISTENT) {
+                Snippet old = idToSnippet.get(evt.snippet().id());
+                if (old != null) {
+                    switch (evt.status()) {
+                        case DROPPED:
+                            assertEquals(old, evt.snippet(),
+                                    "Drop: Old snippet must be what is dropped -- input: " + descriptor);
+                            break;
+                        case OVERWRITTEN:
+                            assertEquals(old, evt.snippet(),
+                                    "Overwrite: Old snippet (" + old
+                                    + ") must be what is overwritten -- input: "
+                                    + descriptor + " -- " + evt);
+                            break;
+                        default:
+                            if (evt.causeSnippet() == null) {
+                                // New source
+                                assertNotEquals(old, evt.snippet(),
+                                        "New source: Old snippet must be different from the replacing -- input: "
+                                        + descriptor);
+                            } else {
+                                // An update (key Overwrite??)
+                                assertEquals(old, evt.snippet(),
+                                        "Update: Old snippet must be equal to the replacing -- input: "
+                                        + descriptor);
+                            }
+                            break;
+                    }
+                }
+            }
+        }
+        for (SnippetEvent evt : events) {
+            if (evt.causeSnippet() == null && evt.status() != DROPPED) {
+                allSnippets.add(evt.snippet());
+                idToSnippet.put(evt.snippet().id(), evt.snippet());
+            }
+        }
+        assertTrue(events.size() >= 1, "Expected at least one event, got none.");
+        List<STEInfo> all = getInfos(eventChains);
+        if (events.size() != all.size()) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Got events --\n");
+            for (SnippetEvent evt : events) {
+                sb.append("  key: ").append(evt.snippet());
+                sb.append(" before: ").append(evt.previousStatus());
+                sb.append(" status: ").append(evt.status());
+                sb.append(" isSignatureChange: ").append(evt.isSignatureChange());
+                sb.append(" cause: ");
+                if (evt.causeSnippet() == null) {
+                    sb.append("direct");
+                } else {
+                    sb.append(evt.causeSnippet());
+                }
+                sb.append("\n");
+            }
+            sb.append("Expected ").append(all.size());
+            sb.append(" events, got: ").append(events.size());
+            fail(sb.toString());
+        }
+
+        int impactId = 0;
+        Map<Snippet, List<SnippetEvent>> groupedEvents = groupByCauseSnippet(events);
+        assertEquals(groupedEvents.size(), eventChains.length, "Number of main events");
+        for (Map.Entry<Snippet, List<SnippetEvent>> entry : groupedEvents.entrySet()) {
+            EventChain eventChain = eventChains[impactId++];
+            SnippetEvent main = entry.getValue().get(0);
+            Snippet mainKey = main.snippet();
+            if (eventChain.mainInfo != null) {
+                eventChain.mainInfo.assertMatch(entry.getValue().get(0), mainKey);
+                if (eventChain.updates.length > 0) {
+                    if (eventChain.updates.length == 1) {
+                        eventChain.updates[0].assertMatch(entry.getValue().get(1), mainKey);
+                    } else {
+                        Arrays.sort(eventChain.updates, (a, b) -> ((a.snippet() == MAIN_SNIPPET)
+                                ? mainKey
+                                : a.snippet()).id().compareTo(b.snippet().id()));
+                        List<SnippetEvent> updateEvents = new ArrayList<>(entry.getValue().subList(1, entry.getValue().size()));
+                        int idx = 0;
+                        for (SnippetEvent ste : updateEvents) {
+                            eventChain.updates[idx++].assertMatch(ste, mainKey);
+                        }
+                    }
+                }
+            }
+            if (((Object) eventChain.value) != IGNORE_VALUE) {
+                assertEquals(main.value(), eventChain.value, "Expected execution value of: " + eventChain.value +
+                        ", but got: " + main.value());
+            }
+            if (eventChain.exceptionClass != IGNORE_EXCEPTION) {
+                if (main.exception() == null) {
+                    assertEquals(eventChain.exceptionClass, null, "Expected an exception of class "
+                            + eventChain.exceptionClass + " got no exception");
+                } else if (eventChain.exceptionClass == null) {
+                    fail("Expected no exception but got " + main.exception().toString());
+                } else {
+                    assertTrue(eventChain.exceptionClass.isInstance(main.exception()),
+                            "Expected an exception of class " + eventChain.exceptionClass +
+                                    " got: " + main.exception().toString());
+                }
+            }
+            List<Diag> diagnostics = getState().diagnostics(mainKey);
+            switch (diagMain) {
+                case DIAG_OK:
+                    assertEquals(diagnostics.size(), 0, "Expected no diagnostics, got: " + diagnosticsToString(diagnostics));
+                    break;
+                case DIAG_WARNING:
+                    assertFalse(hasFatalError(diagnostics), "Expected no errors, got: " + diagnosticsToString(diagnostics));
+                    break;
+                case DIAG_ERROR:
+                    assertTrue(hasFatalError(diagnostics), "Expected errors, got: " + diagnosticsToString(diagnostics));
+                    break;
+            }
+            if (eventChain.mainInfo != null) {
+                for (STEInfo ste : eventChain.updates) {
+                    diagnostics = getState().diagnostics(ste.snippet());
+                    switch (diagUpdates) {
+                        case DIAG_OK:
+                            assertEquals(diagnostics.size(), 0, "Expected no diagnostics, got: " + diagnosticsToString(diagnostics));
+                            break;
+                        case DIAG_WARNING:
+                            assertFalse(hasFatalError(diagnostics), "Expected no errors, got: " + diagnosticsToString(diagnostics));
+                            break;
+                    }
+                }
+            }
+        }
+        return events;
+    }
+
+    // Use this for all EMPTY calls to eval()
+    public void assertEvalEmpty(String input) {
+        List<SnippetEvent> events = getState().eval(input);
+        assertEquals(events.size(), 0, "Expected no events, got: " + events.size());
+    }
+
+    public VarSnippet varKey(List<SnippetEvent> events) {
+        Snippet key = key(events);
+        assertTrue(key instanceof VarSnippet, "Expected a VariableKey, got: " + key);
+        return (VarSnippet) key;
+    }
+
+    public MethodSnippet methodKey(List<SnippetEvent> events) {
+        Snippet key = key(events);
+        assertTrue(key instanceof MethodSnippet, "Expected a MethodKey, got: " + key);
+        return (MethodSnippet) key;
+    }
+
+    public TypeDeclSnippet classKey(List<SnippetEvent> events) {
+        Snippet key = key(events);
+        assertTrue(key instanceof TypeDeclSnippet, "Expected a ClassKey, got: " + key);
+        return (TypeDeclSnippet) key;
+    }
+
+    public ImportSnippet importKey(List<SnippetEvent> events) {
+        Snippet key = key(events);
+        assertTrue(key instanceof ImportSnippet, "Expected a ImportKey, got: " + key);
+        return (ImportSnippet) key;
+    }
+
+    private Snippet key(List<SnippetEvent> events) {
+        assertTrue(events.size() >= 1, "Expected at least one event, got none.");
+        return events.get(0).snippet();
+    }
+
+    public void assertVarValue(Snippet key, String expected) {
+        String value = state.varValue((VarSnippet) key);
+        assertEquals(value, expected, "Expected var value of: " + expected + ", but got: " + value);
+    }
+
+    public Snippet assertDeclareFail(String input, String expectedErrorCode) {
+        return assertDeclareFail(input, expectedErrorCode, added(REJECTED));
+    }
+
+    public Snippet assertDeclareFail(String input, String expectedErrorCode,
+            STEInfo mainInfo, STEInfo... updates) {
+        return assertDeclareFail(input,
+                new ExpectedDiagnostic(expectedErrorCode, -1, -1, -1, -1, -1, Diagnostic.Kind.ERROR),
+                mainInfo, updates);
+    }
+
+    public Snippet assertDeclareFail(String input, ExpectedDiagnostic expectedDiagnostic) {
+        return assertDeclareFail(input, expectedDiagnostic, added(REJECTED));
+    }
+
+    public Snippet assertDeclareFail(String input, ExpectedDiagnostic expectedDiagnostic,
+            STEInfo mainInfo, STEInfo... updates) {
+        List<SnippetEvent> events = assertEval(input, null, null,
+                DiagCheck.DIAG_ERROR, DiagCheck.DIAG_IGNORE, mainInfo, updates);
+        SnippetEvent e = events.get(0);
+        Snippet key = e.snippet();
+        assertEquals(getState().status(key), REJECTED);
+        List<Diag> diagnostics = getState().diagnostics(e.snippet());
+        assertTrue(diagnostics.size() > 0, "Expected diagnostics, got none");
+        assertDiagnostic(input, diagnostics.get(0), expectedDiagnostic);
+        assertTrue(key != null, "key must never be null, but it was for: " + input);
+        return key;
+    }
+
+    public Snippet assertDeclareWarn1(String input, String expectedErrorCode) {
+        return assertDeclareWarn1(input, new ExpectedDiagnostic(expectedErrorCode, -1, -1, -1, -1, -1, Diagnostic.Kind.WARNING));
+    }
+
+    public Snippet assertDeclareWarn1(String input, ExpectedDiagnostic expectedDiagnostic) {
+        return assertDeclareWarn1(input, expectedDiagnostic, added(VALID));
+    }
+
+    public Snippet assertDeclareWarn1(String input, ExpectedDiagnostic expectedDiagnostic, STEInfo mainInfo, STEInfo... updates) {
+        List<SnippetEvent> events = assertEval(input, IGNORE_VALUE, null,
+                DiagCheck.DIAG_WARNING, DiagCheck.DIAG_IGNORE, mainInfo, updates);
+        SnippetEvent e = events.get(0);
+        List<Diag> diagnostics = getState().diagnostics(e.snippet());
+        assertDiagnostic(input, diagnostics.get(0), expectedDiagnostic);
+        return e.snippet();
+    }
+
+    private void assertDiagnostic(String input, Diag diagnostic, ExpectedDiagnostic expectedDiagnostic) {
+        if (expectedDiagnostic != null) expectedDiagnostic.assertDiagnostic(diagnostic);
+        // assertEquals(diagnostic.getSource(), input, "Diagnostic source");
+    }
+
+    public void assertTypeDeclSnippet(TypeDeclSnippet type, String expectedName,
+            Status expectedStatus, SubKind expectedSubKind,
+            int unressz, int othersz) {
+        assertDeclarationSnippet(type, expectedName, expectedStatus,
+                expectedSubKind, unressz, othersz);
+    }
+
+    public void assertMethodDeclSnippet(MethodSnippet method,
+            String expectedName, String expectedSignature,
+            Status expectedStatus, int unressz, int othersz) {
+        assertDeclarationSnippet(method, expectedName, expectedStatus,
+                METHOD_SUBKIND, unressz, othersz);
+        String signature = method.signature();
+        assertEquals(signature, expectedSignature,
+                "Expected " + method.source() + " to have the name: " +
+                        expectedSignature + ", got: " + signature);
+    }
+
+    public void assertVariableDeclSnippet(VarSnippet var,
+            String expectedName, String expectedTypeName,
+            Status expectedStatus, SubKind expectedSubKind,
+            int unressz, int othersz) {
+        assertDeclarationSnippet(var, expectedName, expectedStatus,
+                expectedSubKind, unressz, othersz);
+        String signature = var.typeName();
+        assertEquals(signature, expectedTypeName,
+                "Expected " + var.source() + " to have the name: " +
+                        expectedTypeName + ", got: " + signature);
+    }
+
+    public void assertDeclarationSnippet(DeclarationSnippet declarationKey,
+            String expectedName,
+            Status expectedStatus, SubKind expectedSubKind,
+            int unressz, int othersz) {
+        assertKey(declarationKey, expectedStatus, expectedSubKind);
+        String source = declarationKey.source();
+        assertEquals(declarationKey.name(), expectedName,
+                "Expected " + source + " to have the name: " + expectedName + ", got: " + declarationKey.name());
+        List<String> unresolved = getState().unresolvedDependencies(declarationKey);
+        assertEquals(unresolved.size(), unressz, "Expected " + source + " to have " + unressz
+                + " unresolved symbols, got: " + unresolved.size());
+        List<Diag> otherCorralledErrors = getState().diagnostics(declarationKey);
+        assertEquals(otherCorralledErrors.size(), othersz, "Expected " + source + " to have " + othersz
+                + " other errors, got: " + otherCorralledErrors.size());
+    }
+
+    public void assertKey(Snippet key, Status expectedStatus, SubKind expectedSubKind) {
+        String source = key.source();
+        SubKind actualSubKind = key.subKind();
+        assertEquals(actualSubKind, expectedSubKind,
+                "Expected " + source + " to have the subkind: " + expectedSubKind + ", got: " + actualSubKind);
+        Status status = getState().status(key);
+        assertEquals(status, expectedStatus, "Expected " + source + " to be "
+                + expectedStatus + ", but it is " + status);
+        Snippet.Kind expectedKind = getKind(key);
+        assertEquals(key.kind(), expectedKind, "Checking kind: ");
+        assertEquals(expectedSubKind.kind(), expectedKind, "Checking kind: ");
+    }
+
+    public void assertDrop(PersistentSnippet key, STEInfo mainInfo, STEInfo... updates) {
+        assertDrop(key, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, mainInfo, updates);
+    }
+
+    public void assertDrop(PersistentSnippet key, DiagCheck diagMain, DiagCheck diagUpdates, STEInfo mainInfo, STEInfo... updates) {
+        assertDrop(key, diagMain, diagUpdates, new EventChain(mainInfo, null, null, updates));
+    }
+
+    public void assertDrop(PersistentSnippet key, DiagCheck diagMain, DiagCheck diagUpdates, EventChain... eventChains) {
+        checkEvents(() -> getState().drop(key), "drop(" + key + ")", diagMain, diagUpdates, eventChains);
+    }
+
+    public void assertAnalyze(String input, String source, String remaining, boolean isComplete) {
+        assertAnalyze(input, null, source, remaining, isComplete);
+    }
+
+     public void assertAnalyze(String input, Completeness status, String source) {
+        assertAnalyze(input, status, source, null, null);
+    }
+
+    public void assertAnalyze(String input, Completeness status, String source, String remaining, Boolean isComplete) {
+        CompletionInfo ci = getAnalysis().analyzeCompletion(input);
+        if (status != null) assertEquals(ci.completeness, status, "Input : " + input + ", status: ");
+        if (source != null) assertEquals(ci.source, source, "Input : " + input + ", source: ");
+        if (remaining != null) assertEquals(ci.remaining, remaining, "Input : " + input + ", remaining: ");
+        if (isComplete != null) {
+            boolean isExpectedComplete = isComplete;
+            assertEquals(ci.completeness.isComplete, isExpectedComplete, "Input : " + input + ", isComplete: ");
+        }
+    }
+
+    public void assertNumberOfActiveVariables(int cnt) {
+        Collection<VarSnippet> variables = getState().variables();
+        assertEquals(variables.size(), cnt, "Variables : " + variables);
+    }
+
+    public void assertNumberOfActiveMethods(int cnt) {
+        Collection<MethodSnippet> methods = getState().methods();
+        assertEquals(methods.size(), cnt, "Methods : " + methods);
+    }
+
+    public void assertNumberOfActiveClasses(int cnt) {
+        Collection<TypeDeclSnippet> classes = getState().types();
+        assertEquals(classes.size(), cnt, "Classes : " + classes);
+    }
+
+    public void assertMembers(Collection<? extends Snippet> members, Set<MemberInfo> expected) {
+        assertEquals(members.size(), expected.size(), "Expected : " + expected + ", actual : " + members);
+        assertEquals(members.stream()
+                        .map(this::getMemberInfo)
+                        .collect(Collectors.toSet()),
+                expected);
+    }
+
+    public void assertKeys(MemberInfo... expected) {
+        int index = 0;
+        List<Snippet> snippets = getState().snippets();
+        assertEquals(allSnippets.size(), snippets.size());
+        for (Snippet sn : snippets) {
+            if (sn.kind().isPersistent && getState().status(sn).isActive) {
+                MemberInfo actual = getMemberInfo(sn);
+                MemberInfo exp = expected[index];
+                assertEquals(actual, exp, String.format("Difference in #%d. Expected: %s, actual: %s",
+                        index, exp, actual));
+                ++index;
+            }
+        }
+    }
+
+    public void assertActiveKeys() {
+        Collection<Snippet> expected = getActiveKeys();
+        assertActiveKeys(expected.toArray(new Snippet[expected.size()]));
+    }
+
+    public void assertActiveKeys(Snippet... expected) {
+        int index = 0;
+        for (Snippet key : getState().snippets()) {
+            if (state.status(key).isActive) {
+                assertEquals(expected[index], key, String.format("Difference in #%d. Expected: %s, actual: %s", index, key, expected[index]));
+                ++index;
+            }
+        }
+    }
+
+    private List<Snippet> filterDeclaredKeys(Predicate<Snippet> p) {
+        return getActiveKeys().stream()
+                .filter(p)
+                .collect(Collectors.toList());
+    }
+
+    public void assertVariables() {
+        assertEquals(getState().variables(), filterDeclaredKeys((key) -> key instanceof VarSnippet), "Variables");
+    }
+
+    public void assertMethods() {
+        assertEquals(getState().methods(), filterDeclaredKeys((key) -> key instanceof MethodSnippet), "Methods");
+    }
+
+    public void assertClasses() {
+        assertEquals(getState().types(), filterDeclaredKeys((key) -> key instanceof TypeDeclSnippet), "Classes");
+    }
+
+    public void assertVariables(MemberInfo...expected) {
+        assertMembers(getState().variables(), Stream.of(expected).collect(Collectors.toSet()));
+    }
+
+    public void assertMethods(MemberInfo...expected) {
+        assertMembers(getState().methods(), Stream.of(expected).collect(Collectors.toSet()));
+        for (MethodSnippet methodKey : getState().methods()) {
+            MemberInfo expectedInfo = null;
+            for (MemberInfo info : expected) {
+                if (info.name.equals(methodKey.name()) && info.type.equals(methodKey.signature())) {
+                    expectedInfo = getMemberInfo(methodKey);
+                }
+            }
+            assertNotNull(expectedInfo, "Not found method: " + methodKey.name());
+            int lastIndexOf = expectedInfo.type.lastIndexOf(')');
+            assertEquals(methodKey.parameterTypes(), expectedInfo.type.substring(1, lastIndexOf), "Parameter types");
+        }
+    }
+
+    public void assertClasses(MemberInfo...expected) {
+        assertMembers(getState().types(), Stream.of(expected).collect(Collectors.toSet()));
+    }
+
+    public void assertCompletion(String code, String... expected) {
+        assertCompletion(code, null, expected);
+    }
+
+    public void assertCompletion(String code, Boolean isSmart, String... expected) {
+        List<String> completions = computeCompletions(code, isSmart);
+        assertEquals(completions, Arrays.asList(expected), "Input: " + code + ", " + completions.toString());
+    }
+
+    public void assertCompletionIncludesExcludes(String code, Set<String> expected, Set<String> notExpected) {
+        assertCompletionIncludesExcludes(code, null, expected, notExpected);
+    }
+
+    public void assertCompletionIncludesExcludes(String code, Boolean isSmart, Set<String> expected, Set<String> notExpected) {
+        List<String> completions = computeCompletions(code, isSmart);
+        assertTrue(completions.containsAll(expected), String.valueOf(completions));
+        assertTrue(Collections.disjoint(completions, notExpected), String.valueOf(completions));
+    }
+
+    private List<String> computeCompletions(String code, Boolean isSmart) {
+        int cursor =  code.indexOf('|');
+        code = code.replace("|", "");
+        assertTrue(cursor > -1, "'|' expected, but not found in: " + code);
+        List<Suggestion> completions =
+                getAnalysis().completionSuggestions(code, cursor, new int[1]); //XXX: ignoring anchor for now
+        return completions.stream()
+                          .filter(s -> isSmart == null || isSmart == s.isSmart)
+                          .map(s -> s.continuation)
+                          .distinct()
+                          .collect(Collectors.toList());
+    }
+
+    public void assertDocumentation(String code, String... expected) {
+        int cursor =  code.indexOf('|');
+        code = code.replace("|", "");
+        assertTrue(cursor > -1, "'|' expected, but not found in: " + code);
+        String documentation = getAnalysis().documentation(code, cursor);
+        Set<String> docSet = Stream.of(documentation.split("\r?\n")).collect(Collectors.toSet());
+        Set<String> expectedSet = Stream.of(expected).collect(Collectors.toSet());
+        assertEquals(docSet, expectedSet, "Input: " + code);
+    }
+
+    public enum ClassType {
+        CLASS("CLASS_SUBKIND") {
+            @Override
+            public String toString() {
+                return "class";
+            }
+        },
+        ENUM("ENUM_SUBKIND") {
+            @Override
+            public String toString() {
+                return "enum";
+            }
+        },
+        INTERFACE("INTERFACE_SUBKIND") {
+            @Override
+            public String toString() {
+                return "interface";
+            }
+        },
+        ANNOTATION("ANNOTATION_TYPE_SUBKIND") {
+            @Override
+            public String toString() {
+                return "@interface";
+            }
+        };
+
+        private final String classType;
+
+        ClassType(String classType) {
+            this.classType = classType;
+        }
+
+        public String getClassType() {
+            return classType;
+        }
+
+        @Override
+        public abstract String toString();
+    }
+
+    public static MemberInfo variable(String type, String name) {
+        return new MemberInfo(type, name);
+    }
+
+    public static MemberInfo method(String signature, String name) {
+        return new MemberInfo(signature, name);
+    }
+
+    public static MemberInfo clazz(ClassType classType, String className) {
+        return new MemberInfo(classType.getClassType(), className);
+    }
+
+    public static class MemberInfo {
+        public final String type;
+        public final String name;
+
+        public MemberInfo(String type, String name) {
+            this.type = type;
+            this.name = name;
+        }
+
+        @Override
+        public int hashCode() {
+            return type.hashCode() + 3 * name.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof MemberInfo) {
+                MemberInfo other = (MemberInfo) o;
+                return type.equals(other.type) && name.equals(other.name);
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s %s", type, name);
+        }
+    }
+
+    public MemberInfo getMemberInfo(Snippet key) {
+        SubKind subkind = key.subKind();
+        switch (subkind) {
+            case CLASS_SUBKIND:
+            case INTERFACE_SUBKIND:
+            case ENUM_SUBKIND:
+            case ANNOTATION_TYPE_SUBKIND:
+                return new MemberInfo(subkind.name(), ((DeclarationSnippet) key).name());
+            case METHOD_SUBKIND:
+                MethodSnippet method = (MethodSnippet) key;
+                return new MemberInfo(method.signature(), method.name());
+            case VAR_DECLARATION_SUBKIND:
+            case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND:
+            case TEMP_VAR_EXPRESSION_SUBKIND:
+                VarSnippet var = (VarSnippet) key;
+                return new MemberInfo(var.typeName(), var.name());
+            default:
+                throw new AssertionError("Unknown snippet : " + key.kind() + " in expression " + key.toString());
+        }
+    }
+
+    public String diagnosticsToString(List<Diag> diagnostics) {
+        StringWriter writer = new StringWriter();
+        for (Diag diag : diagnostics) {
+            writer.write("Error --\n");
+            for (String line : diag.getMessage(null).split("\\r?\\n")) {
+                writer.write(String.format("%s\n", line));
+            }
+        }
+        return writer.toString().replace("\n", System.lineSeparator());
+    }
+
+    public boolean hasFatalError(List<Diag> diagnostics) {
+        for (Diag diag : diagnostics) {
+            if (diag.isError()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static EventChain chain(STEInfo mainInfo, STEInfo... updates) {
+        return chain(mainInfo, IGNORE_VALUE, null, updates);
+    }
+
+    public static EventChain chain(STEInfo mainInfo, String value, Class<? extends Throwable> exceptionClass, STEInfo... updates) {
+        return new EventChain(mainInfo, value, exceptionClass, updates);
+    }
+
+    public static STEInfo ste(Snippet key, Status previousStatus, Status status,
+                Boolean isSignatureChange, Snippet causeKey) {
+        return new STEInfo(key, previousStatus, status, isSignatureChange, causeKey);
+    }
+
+    public static STEInfo added(Status status) {
+        return new STEInfo(MAIN_SNIPPET, NONEXISTENT, status, status.isDefined, null);
+    }
+
+    public static class EventChain {
+        public final STEInfo mainInfo;
+        public final STEInfo[] updates;
+        public final String value;
+        public final Class<? extends Throwable> exceptionClass;
+
+        public EventChain(STEInfo mainInfo, String value, Class<? extends Throwable> exceptionClass, STEInfo... updates) {
+            this.mainInfo = mainInfo;
+            this.updates = updates;
+            this.value = value;
+            this.exceptionClass = exceptionClass;
+        }
+    }
+
+    public static class STEInfo {
+
+        STEInfo(Snippet snippet, Status previousStatus, Status status,
+                Boolean isSignatureChange, Snippet causeSnippet) {
+            this.snippet = snippet;
+            this.previousStatus = previousStatus;
+            this.status = status;
+            this.checkIsSignatureChange = isSignatureChange != null;
+            this.isSignatureChange = checkIsSignatureChange ? isSignatureChange : false;
+            this.causeSnippet = causeSnippet;
+            assertTrue(snippet != null, "Bad test set-up. The match snippet must not be null");
+        }
+
+        final Snippet snippet;
+        final Status previousStatus;
+        final Status status;
+        final boolean isSignatureChange;
+        final Snippet causeSnippet;
+
+         final boolean checkIsSignatureChange;
+        public Snippet snippet() {
+            return snippet;
+        }
+        public Status previousStatus() {
+            return previousStatus;
+        }
+        public Status status() {
+            return status;
+        }
+        public boolean isSignatureChange() {
+            if (!checkIsSignatureChange) {
+                throw new IllegalStateException("isSignatureChange value is undefined");
+            }
+            return isSignatureChange;
+        }
+        public Snippet causeSnippet() {
+            return causeSnippet;
+        }
+        public String value() {
+            return null;
+        }
+        public Exception exception() {
+            return null;
+        }
+
+        public void assertMatch(SnippetEvent ste, Snippet mainSnippet) {
+            assertKeyMatch(ste, ste.snippet(), snippet(), mainSnippet);
+            assertStatusMatch(ste, ste.previousStatus(), previousStatus());
+            assertStatusMatch(ste, ste.status(), status());
+            if (checkIsSignatureChange) {
+                assertEquals(ste.isSignatureChange(), isSignatureChange(),
+                        "Expected " +
+                                (isSignatureChange()? "" : "no ") +
+                                "signature-change, got: " +
+                                (ste.isSignatureChange()? "" : "no ") +
+                                "signature-change" +
+                        "\n   expected-event: " + this + "\n   got-event: " + toString(ste));
+            }
+            assertKeyMatch(ste, ste.causeSnippet(), causeSnippet(), mainSnippet);
+        }
+
+        private void assertKeyMatch(SnippetEvent ste, Snippet sn, Snippet expected, Snippet mainSnippet) {
+            Snippet testKey = expected;
+            if (testKey != null) {
+                if (expected == MAIN_SNIPPET) {
+                    assertNotNull(mainSnippet, "MAIN_SNIPPET used, test must pass value to assertMatch");
+                    testKey = mainSnippet;
+                }
+                if (ste.causeSnippet() == null && ste.status() != DROPPED && expected != MAIN_SNIPPET) {
+                    // Source change, always new snippet -- only match id()
+                    assertTrue(sn != testKey,
+                            "Main-event: Expected new snippet to be != : " + testKey
+                            + "\n   got-event: " + toString(ste));
+                    assertEquals(sn.id(), testKey.id(), "Expected IDs to match: " + testKey + ", got: " + sn
+                            + "\n   expected-event: " + this + "\n   got-event: " + toString(ste));
+                } else {
+                    assertEquals(sn, testKey, "Expected key to be: " + testKey + ", got: " + sn
+                            + "\n   expected-event: " + this + "\n   got-event: " + toString(ste));
+                }
+            }
+        }
+
+        private void assertStatusMatch(SnippetEvent ste, Status status, Status expected) {
+            if (expected != null) {
+                assertEquals(status, expected, "Expected status to be: " + expected + ", got: " + status +
+                        "\n   expected-event: " + this + "\n   got-event: " + toString(ste));
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "STEInfo key: " +
+                    (snippet()==MAIN_SNIPPET? "MAIN_SNIPPET" : (snippet()==null? "ignore" : snippet().id())) +
+                    " before: " + previousStatus() +
+                    " status: " + status() + " sig: " + isSignatureChange() +
+                    " cause: " + (causeSnippet()==null? "null" : causeSnippet().id());
+        }
+
+        private String toString(SnippetEvent ste) {
+            return "key: " + (ste.snippet()==MAIN_SNIPPET? "MAIN_SNIPPET" : ste.snippet().id()) + " before: " + ste.previousStatus()
+                    + " status: " + ste.status() + " sig: " + ste.isSignatureChange()
+                    + " cause: " + ste.causeSnippet();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/MethodsTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for EvaluationState.methods
+ * @build KullaTesting TestingInputStream ExpectedDiagnostic
+ * @run testng MethodsTest
+ */
+
+import javax.tools.Diagnostic;
+
+import jdk.jshell.Snippet;
+import jdk.jshell.MethodSnippet;
+import jdk.jshell.Snippet.Status;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.*;
+import static jdk.jshell.Snippet.SubKind.*;
+
+@Test
+public class MethodsTest extends KullaTesting {
+
+    public void noMethods() {
+        assertNumberOfActiveMethods(0);
+    }
+
+    public void testSignature1() {
+        MethodSnippet m1 = methodKey(assertEval("void f() { g(); }", added(RECOVERABLE_DEFINED)));
+        assertMethodDeclSnippet(m1, "f", "()void", RECOVERABLE_DEFINED, 1, 0);
+        MethodSnippet m2 = methodKey(assertEval("void g() { }",
+                added(VALID),
+                ste(m1, RECOVERABLE_DEFINED, VALID, false, null)));
+        assertMethodDeclSnippet(m2, "g", "()void", VALID, 0, 0);
+    }
+
+    public void testSignature2() {
+        MethodSnippet m1 = (MethodSnippet) assertDeclareFail("void f() { return g(); }", "compiler.err.prob.found.req");
+        assertMethodDeclSnippet(m1, "f", "()void", REJECTED, 0, 2);
+        MethodSnippet m2 = methodKey(assertEval("int f() { return g(); }",
+                ste(m1, REJECTED, RECOVERABLE_DEFINED, true, null)));
+        assertMethodDeclSnippet(m1, "f", "()void", REJECTED, 0, 2);
+        assertMethodDeclSnippet(m2, "f", "()int", RECOVERABLE_DEFINED, 1, 0);
+    }
+
+    @Test(enabled = false) // TODO 8081690
+    public void testSignature3() {
+        MethodSnippet m1 = methodKey(assertEval("void f(Bar b) { }", added(RECOVERABLE_NOT_DEFINED)));
+        assertMethodDeclSnippet(m1, "f", "(Bar)void", RECOVERABLE_NOT_DEFINED, 1, 0);
+        MethodSnippet m2 = methodKey(assertEval("void f(A.Bar b) { }", added(RECOVERABLE_NOT_DEFINED)));
+        assertMethodDeclSnippet(m1, "f", "(Bar)void", RECOVERABLE_NOT_DEFINED, 1, 0);
+        assertMethodDeclSnippet(m2, "f", "(A.Bar)void", RECOVERABLE_NOT_DEFINED, 1, 0);
+        assertDrop(m1, ste(m1, RECOVERABLE_NOT_DEFINED, DROPPED, false, null));
+        assertMethodDeclSnippet(m1, "f", "(Bar)void", DROPPED, 1, 0);
+    }
+
+    public void methods() {
+        assertEval("int x() { return 10; }");
+        assertEval("String y() { return null; }");
+        assertEval("long z() { return 0; }");
+        assertMethods(method("()int", "x"), method("()String", "y"), method("()long", "z"));
+        assertActiveKeys();
+    }
+
+    public void methodOverload() {
+        assertEval("int m() { return 1; }");
+        assertEval("int m(int x) { return 2; }");
+        assertEval("int m(String s) { return 3; }");
+        assertEval("int m(int x, int y) { return 4; }");
+        assertEval("int m(int x, String z) { return 5; }");
+        assertEval("int m(int x, String z, long g) { return 6; }");
+        assertMethods(
+                method("()int", "m"),
+                method("(int)int", "m"),
+                method("(String)int", "m"),
+                method("(int,int)int", "m"),
+                method("(int,String)int", "m"),
+                method("(int,String,long)int", "m")
+        );
+        assertEval("m();", "1");
+        assertEval("m(3);", "2");
+        assertEval("m(\"hi\");", "3");
+        assertEval("m(7, 8);", "4");
+        assertEval("m(7, \"eight\");", "5");
+        assertEval("m(7, \"eight\", 9L);", "6");
+        assertActiveKeys();
+    }
+
+    public void methodsRedeclaration1() {
+        Snippet x = methodKey(assertEval("int x() { return 10; }"));
+        Snippet y = methodKey(assertEval("String y() { return \"\"; }"));
+        assertMethods(method("()int", "x"), method("()String", "y"));
+        assertActiveKeys();
+
+        assertEval("long x() { return 0; }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(x, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertMethods(method("()long", "x"), method("()String", "y"));
+        assertActiveKeys();
+
+        assertEval("String y() { return null; }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(y, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertMethods(method("()long", "x"), method("()String", "y"));
+        assertActiveKeys();
+    }
+
+    public void methodsRedeclaration2() {
+        assertEval("int a() { return 1; }");
+        assertMethods(method("()int", "a"));
+        assertActiveKeys();
+
+        Snippet b = methodKey(assertEval("Integer b() { return a(); }"));
+        assertMethods(method("()int", "a"), method("()Integer", "b"));
+        assertActiveKeys();
+
+        Snippet c = methodKey(assertEval("double c() { return b(); }"));
+        assertMethods(method("()int", "a"), method("()Integer", "b"), method("()double", "c"));
+        assertActiveKeys();
+
+        assertEval("double b() { return 3.14159; }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(b, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(c, VALID, VALID, false, MAIN_SNIPPET));
+        assertMethods(method("()int", "a"), method("()double", "b"), method("()double", "c"));
+        assertEval("c();", "3.14159");
+        assertActiveKeys();
+    }
+
+    public void methodsRedeclaration3() {
+        Snippet x = methodKey(assertEval("int x(Object...a) { return 10; }"));
+        assertMethods(method("(Object...)int", "x"));
+        assertActiveKeys();
+
+        assertEval("int x(Object[]a) { return 10; }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(x, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertMethods(method("(Object[])int", "x"));
+        assertActiveKeys();
+    }
+
+
+    public void methodsRedeclaration4() {
+        Snippet a = methodKey(assertEval("int foo(int a) { return a; }"));
+        assertEval("int x = foo(10);");
+        assertActiveKeys();
+        assertMethods(method("(int)int", "foo"));
+        assertEval("int foo(int a) { return a * a; }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertActiveKeys();
+    }
+
+    public void methodsErrors() {
+        assertDeclareFail("String f();",
+                new ExpectedDiagnostic("compiler.err.missing.meth.body.or.decl.abstract", 0, 11, 7, -1, -1, Diagnostic.Kind.ERROR));
+        assertNumberOfActiveMethods(0);
+        assertActiveKeys();
+
+        assertDeclareFail("abstract String f();",
+                new ExpectedDiagnostic("jdk.eval.error.illegal.modifiers", 0, 8, 0, -1, -1, Diagnostic.Kind.ERROR));
+        assertNumberOfActiveMethods(0);
+        assertActiveKeys();
+
+        assertDeclareFail("native String f();",
+                new ExpectedDiagnostic("jdk.eval.error.illegal.modifiers", 0, 6, 0, -1, -1, Diagnostic.Kind.ERROR));
+        assertNumberOfActiveMethods(0);
+        assertActiveKeys();
+
+        assertDeclareFail("synchronized String f() {return null;}",
+                new ExpectedDiagnostic("jdk.eval.error.illegal.modifiers", 0, 12, 0, -1, -1, Diagnostic.Kind.ERROR));
+        assertNumberOfActiveMethods(0);
+        assertActiveKeys();
+
+        assertDeclareFail("int f() {}", "compiler.err.missing.ret.stmt",
+                ste(MAIN_SNIPPET, REJECTED, REJECTED, false, null));
+        assertNumberOfActiveMethods(0);
+        assertActiveKeys();
+
+        assertEval("String x() { return \"\"; };");
+        assertMethods(method("()String", "x"));
+        assertActiveKeys();
+    }
+
+    public void methodsWarn() {
+        Snippet f = assertDeclareWarn1("public String f() {return null;}",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 6, 0, -1, -1, Diagnostic.Kind.WARNING),
+                added(VALID));
+        assertNumberOfActiveMethods(1);
+        assertActiveKeys();
+
+        f = assertDeclareWarn1("protected String f() {return null;}",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 9, 0, -1, -1, Diagnostic.Kind.WARNING),
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(f, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertNumberOfActiveMethods(1);
+        assertActiveKeys();
+
+        f = assertDeclareWarn1("private String f() {return null;}",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 7, 0, -1, -1, Diagnostic.Kind.WARNING),
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(f, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertNumberOfActiveMethods(1);
+        assertActiveKeys();
+
+        f = assertDeclareWarn1("static String f() {return null;}",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 6, 0, -1, -1, Diagnostic.Kind.WARNING),
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(f, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertNumberOfActiveMethods(1);
+        assertActiveKeys();
+
+        assertDeclareWarn1("final String f() {return null;}",
+                new ExpectedDiagnostic("jdk.eval.warn.illegal.modifiers", 0, 5, 0, -1, -1, Diagnostic.Kind.WARNING),
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(f, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertNumberOfActiveMethods(1);
+        assertActiveKeys();
+    }
+
+    public void methodSignatureUnresolved() {
+        MethodSnippet key = (MethodSnippet) methodKey(assertEval("und m() { return new und(); }", added(RECOVERABLE_NOT_DEFINED)));
+        assertMethodDeclSnippet(key, "m", "()und", RECOVERABLE_NOT_DEFINED, 1, 0);
+        assertUnresolvedDependencies1(key, Status.RECOVERABLE_NOT_DEFINED, "class und");
+        assertEval("class und {}",
+                added(VALID),
+                ste(key, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertMethodDeclSnippet(key, "m", "()und", Status.VALID, 0, 0);
+        assertNumberOfActiveMethods(1);
+        assertActiveKeys();
+    }
+
+    @Test(enabled = false) // TODO 8081689
+    public void classMethodsAreNotVisible() {
+        assertEval(
+            "class A {" +
+                "int foo() {" +
+                    "int x = 10;" +
+                    "int y = 2 * x;" +
+                    "return x * y;" +
+                "}" +
+            "}");
+        assertNumberOfActiveMethods(0);
+        assertEval("int x = 10;", "10");
+        assertEval("int foo() {" +
+                        "int y = 2 * x;" +
+                        "return x * y;" +
+                        "}");
+        assertMethods(method("()int", "foo"));
+        assertEval("foo();", "200");
+        assertActiveKeys();
+    }
+
+    public void lambdas() {
+        assertEval("class Inner1 implements Runnable {" +
+                "public Runnable lambda1 = () -> {};" +
+                "public void function() {}" +
+                "public void run() {}" +
+                "}");
+
+        assertEval("class Inner2 {" +
+                "private Runnable lambda1 = () -> {};" +
+                "private static void staticFunction() {}" +
+                "}");
+
+        // the following method references and lambda functions
+        // generate synthetic methods
+        assertEval("Runnable run = () -> {};");
+        assertEval("Inner1 inner = new Inner1();");
+        assertEval("Runnable l1 = inner::function;");
+        assertEval("Runnable l2 = Inner1::new;");
+        assertEval("inner.lambda1 = inner::function;");
+        assertEval("java.util.stream.IntStream.of(2).mapToObj(int[]::new);");
+        assertNumberOfActiveMethods(0);
+        assertActiveKeys();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ModifiersTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for modifiers
+ * @build KullaTesting TestingInputStream ExpectedDiagnostic
+ * @run testng ModifiersTest
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.tools.Diagnostic;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+@Test
+public class ModifiersTest extends KullaTesting {
+
+    @DataProvider(name = "ignoredModifiers")
+    public Object[][] getTestCases() {
+        List<Object[]> testCases = new ArrayList<>();
+        String[] ignoredModifiers = new String[] {
+            "public", "protected", "private", "static", "final"
+        };
+        for (String ignoredModifier : ignoredModifiers) {
+            for (ClassType classType : ClassType.values()) {
+                testCases.add(new Object[] { ignoredModifier, classType });
+            }
+        }
+        return testCases.toArray(new Object[testCases.size()][]);
+    }
+
+    @Test(dataProvider = "ignoredModifiers")
+    public void ignoredModifiers(String modifier, ClassType classType) {
+        assertDeclareWarn1(
+                String.format("%s %s A {}", modifier, classType), "jdk.eval.warn.illegal.modifiers");
+        assertNumberOfActiveClasses(1);
+        assertClasses(clazz(classType, "A"));
+        assertActiveKeys();
+    }
+
+    public void accessToStaticFieldsOfClass() {
+        assertEval("class A {" +
+                "int x = 14;" +
+                "static int y = 18;" +
+                " }");
+        assertDeclareFail("A.x;",
+                new ExpectedDiagnostic("compiler.err.non-static.cant.be.ref", 0, 3, 1, -1, -1, Diagnostic.Kind.ERROR));
+        assertEval("A.y;", "18");
+        assertEval("new A().x;", "14");
+        assertEval("A.y = 88;", "88");
+        assertActiveKeys();
+    }
+
+    public void accessToStaticMethodsOfClass() {
+        assertEval("class A {" +
+                "void x() {}" +
+                "static void y() {}" +
+                " }");
+        assertDeclareFail("A.x();",
+                new ExpectedDiagnostic("compiler.err.non-static.cant.be.ref", 0, 3, 1, -1, -1, Diagnostic.Kind.ERROR));
+        assertEval("A.y();");
+        assertActiveKeys();
+    }
+
+    public void accessToStaticFieldsOfInterface() {
+        assertEval("interface A {" +
+                "int x = 14;" +
+                "static int y = 18;" +
+                " }");
+        assertEval("A.x;", "14");
+        assertEval("A.y;", "18");
+
+        assertDeclareFail("A.x = 18;",
+                new ExpectedDiagnostic("compiler.err.cant.assign.val.to.final.var", 0, 3, 1, -1, -1, Diagnostic.Kind.ERROR));
+        assertDeclareFail("A.y = 88;",
+                new ExpectedDiagnostic("compiler.err.cant.assign.val.to.final.var", 0, 3, 1, -1, -1, Diagnostic.Kind.ERROR));
+        assertActiveKeys();
+    }
+
+    public void accessToStaticMethodsOfInterface() {
+        assertEval("interface A { static void x() {} }");
+        assertEval("A.x();");
+        assertActiveKeys();
+    }
+
+    public void finalMethod() {
+        assertEval("class A { final void f() {} }");
+        assertDeclareFail("class B extends A { void f() {} }",
+                new ExpectedDiagnostic("compiler.err.override.meth", 20, 31, 25, -1, -1, Diagnostic.Kind.ERROR));
+        assertActiveKeys();
+    }
+
+    //TODO: is this the right semantics?
+    public void finalConstructor() {
+        assertDeclareFail("class A { final A() {} }",
+                new ExpectedDiagnostic("compiler.err.mod.not.allowed.here", 10, 22, 16, -1, -1, Diagnostic.Kind.ERROR));
+        assertActiveKeys();
+    }
+
+    //TODO: is this the right semantics?
+    public void finalDefaultMethod() {
+        assertDeclareFail("interface A { final default void a() {} }",
+                new ExpectedDiagnostic("compiler.err.mod.not.allowed.here", 14, 39, 33, -1, -1, Diagnostic.Kind.ERROR));
+        assertActiveKeys();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/NullTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary null test
+ * @build KullaTesting TestingInputStream
+ * @run testng NullTest
+ */
+
+import org.testng.annotations.Test;
+
+@Test
+public class NullTest extends KullaTesting {
+
+    public void testNull() {
+        assertEval("null;", "null");
+        assertEval("(Object)null;", "null");
+        assertEval("(String)null;", "null");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/RejectedFailedTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for hard errors, like syntax errors
+ * @build KullaTesting
+ * @run testng RejectedFailedTest
+ */
+
+import java.util.List;
+
+import jdk.jshell.Snippet.SubKind;
+import org.testng.annotations.Test;
+
+import jdk.jshell.Diag;
+import jdk.jshell.Snippet;
+import jdk.jshell.Snippet.Kind;
+import jdk.jshell.Snippet.Status;
+
+import jdk.jshell.SnippetEvent;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+@Test
+public class RejectedFailedTest extends KullaTesting {
+
+    private String bad(String input, Kind kind, String prevId) {
+        List<SnippetEvent> events = assertEvalFail(input);
+        assertEquals(events.size(), 1, "Expected one event, got: " + events.size());
+        SnippetEvent e = events.get(0);
+        List<Diag> diagnostics = getState().diagnostics(e.snippet());
+        assertTrue(diagnostics.size() > 0, "Expected diagnostics, got none");
+        assertEquals(e.exception(), null, "Expected exception to be null.");
+        assertEquals(e.value(), null, "Expected value to be null.");
+
+        Snippet key = e.snippet();
+        assertTrue(key != null, "key must be non-null, but was null.");
+        assertEquals(key.kind(), kind, "Expected kind: " + kind + ", got: " + key.kind());
+        SubKind expectedSubKind = kind == Kind.ERRONEOUS ? SubKind.UNKNOWN_SUBKIND : SubKind.METHOD_SUBKIND;
+        assertEquals(key.subKind(), expectedSubKind, "SubKind: ");
+        assertTrue(key.id().compareTo(prevId) > 0, "Current id: " + key.id() + ", previous: " + prevId);
+        assertEquals(getState().diagnostics(key), diagnostics, "Expected retrieved diagnostics to match, but didn't.");
+        assertEquals(key.source(), input, "Expected retrieved source: " +
+                key.source() + " to match input: " + input);
+        assertEquals(getState().status(key), Status.REJECTED, "Expected status of REJECTED, got: " + getState().status(key));
+        return key.id();
+    }
+
+    private void checkByKind(String[] inputs, Kind kind) {
+        String prevId = "";
+        for (String in : inputs) {
+            prevId = bad(in, kind, prevId);
+        }
+    }
+
+    public void testErroneous() {
+        String[] inputsErroneous = {
+                "%&^%&",
+                " a b c",
+                ")",
+                "class interface A",
+        };
+        checkByKind(inputsErroneous, Kind.ERRONEOUS);
+    }
+
+    public void testBadMethod() {
+        String[] inputsMethod = {
+                "transient int m() { return x; }",
+                "int h() { }",
+                "void l() { return 4; }",
+                "int vv(void x) { return 2; }",
+        };
+        checkByKind(inputsMethod, Kind.METHOD);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ReplToolTesting.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,653 @@
+/*
+ * 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import jdk.internal.jshell.tool.JShellTool;
+import jdk.jshell.SourceCodeAnalysis.Suggestion;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+public class ReplToolTesting {
+
+    private final static String DEFAULT_STARTUP_MESSAGE = "|  Welcome to";
+
+    private WaitingTestingInputStream cmdin = null;
+    private ByteArrayOutputStream cmdout = null;
+    private ByteArrayOutputStream cmderr = null;
+    private PromptedCommandOutputStream console = null;
+    private TestingInputStream userin = null;
+    private ByteArrayOutputStream userout = null;
+    private ByteArrayOutputStream usererr = null;
+
+    private List<MemberInfo> keys;
+    private Map<String, VariableInfo> variables;
+    private Map<String, MethodInfo> methods;
+    private Map<String, ClassInfo> classes;
+    private boolean isDefaultStartUp = true;
+
+    public JShellTool repl = null;
+
+    public interface ReplTest {
+        void run(boolean after);
+    }
+
+    public void setCommandInput(String s) {
+        cmdin.setInput(s);
+    }
+
+    public final static Pattern idPattern = Pattern.compile("^\\s+(\\d+)");
+    public Consumer<String> assertList() {
+        return s -> {
+            List<String> lines = Stream.of(s.split("\n"))
+                    .filter(l -> !l.isEmpty())
+                    .collect(Collectors.toList());
+            int previousId = Integer.MIN_VALUE;
+            assertEquals(lines.size(), keys.size(), "Number of keys");
+            for (int i = 0; i < lines.size(); ++i) {
+                String line = lines.get(i);
+                Matcher matcher = idPattern.matcher(line);
+                assertTrue(matcher.find(), "Snippet id not found: " + line);
+                String src = keys.get(i).getSource();
+                assertTrue(line.endsWith(src), "Line '" + line + "' does not end with: " + src);
+                int id = Integer.parseInt(matcher.group(1));
+                assertTrue(previousId < id,
+                        String.format("The previous id is not less than the next one: previous: %d, next: %d",
+                                previousId, id));
+                previousId = id;
+            }
+        };
+    }
+
+    private final static Pattern extractPattern = Pattern.compile("^\\| *(.*)$");
+    private Consumer<String> assertMembers(String message, Map<String, ? extends MemberInfo> set) {
+        return s -> {
+            List<String> lines = Stream.of(s.split("\n"))
+                    .filter(l -> !l.isEmpty())
+                    .collect(Collectors.toList());
+            assertEquals(lines.size(), set.size(), message + " : expected: " + set.keySet() + "\ngot:\n" + lines);
+            for (String line : lines) {
+                Matcher matcher = extractPattern.matcher(line);
+                assertTrue(matcher.find(), line);
+                String src = matcher.group(1);
+                MemberInfo info = set.get(src);
+                assertNotNull(info, "Not found snippet with signature: " + src + ", line: "
+                        + line + ", keys: " + set.keySet() + "\n");
+            }
+        };
+    }
+
+    public Consumer<String> assertVariables() {
+        return assertMembers("Variables", variables);
+    }
+
+    public Consumer<String> assertMethods() {
+        return assertMembers("Methods", methods);
+    }
+
+    public Consumer<String> assertClasses() {
+        return assertMembers("Classes", classes);
+    }
+
+    public String getCommandOutput() {
+        String s = cmdout.toString();
+        cmdout.reset();
+        return s;
+    }
+
+    public String getCommandErrorOutput() {
+        String s = cmderr.toString();
+        cmderr.reset();
+        return s;
+    }
+
+    public void setUserInput(String s) {
+        userin.setInput(s);
+    }
+
+    public String getUserOutput() {
+        String s = userout.toString();
+        userout.reset();
+        return s;
+    }
+
+    public String getUserErrorOutput() {
+        String s = usererr.toString();
+        usererr.reset();
+        return s;
+    }
+
+    public void test(ReplTest... tests) {
+        test(new String[0], tests);
+    }
+
+    public void test(String[] args, ReplTest... tests) {
+        test(true, args, tests);
+    }
+
+    public void test(boolean isDefaultStartUp, String[] args, ReplTest... tests) {
+        test(isDefaultStartUp, args, DEFAULT_STARTUP_MESSAGE, tests);
+    }
+
+    public void test(boolean isDefaultStartUp, String[] args, String startUpMessage, ReplTest... tests) {
+        this.isDefaultStartUp = isDefaultStartUp;
+        initSnippets();
+        ReplTest[] wtests = new ReplTest[tests.length + 3];
+        wtests[0] = a -> assertCommandCheckOutput(a, "<start>",
+                s -> assertTrue(s.startsWith(startUpMessage), "Expected start-up message '" + startUpMessage + "' Got: " + s));
+        wtests[1] = a -> assertCommand(a, "/debug 0", null);
+        System.arraycopy(tests, 0, wtests, 2, tests.length);
+        wtests[tests.length + 2] = a -> assertCommand(a, "/exit", null);
+        testRaw(args, wtests);
+    }
+
+    private void initSnippets() {
+        keys = new ArrayList<>();
+        variables = new HashMap<>();
+        methods = new HashMap<>();
+        classes = new HashMap<>();
+        if (isDefaultStartUp) {
+            methods.put("printf (String,Object...)void",
+                    new MethodInfo("", "(String,Object...)void", "printf"));
+        }
+    }
+
+    public void testRaw(String[] args, ReplTest... tests) {
+        cmdin = new WaitingTestingInputStream();
+        cmdout = new ByteArrayOutputStream();
+        cmderr = new ByteArrayOutputStream();
+        console = new PromptedCommandOutputStream(tests);
+        userin = new TestingInputStream();
+        userout = new ByteArrayOutputStream();
+        usererr = new ByteArrayOutputStream();
+        repl = new JShellTool(
+                cmdin,
+                new PrintStream(cmdout),
+                new PrintStream(cmderr),
+                new PrintStream(console),
+                userin,
+                new PrintStream(userout),
+                new PrintStream(usererr));
+        repl.testPrompt = true;
+        try {
+            repl.start(args);
+        } catch (Exception ex) {
+            fail("Repl tool died with exception", ex);
+        }
+        // perform internal consistency checks on state, if desired
+        String cos = getCommandOutput();
+        String ceos = getCommandErrorOutput();
+        String uos = getUserOutput();
+        String ueos = getUserErrorOutput();
+        assertTrue((cos.isEmpty() || cos.startsWith("|  Goodbye")),
+                "Expected a goodbye, but got: " + cos);
+        assertTrue(ceos.isEmpty(), "Expected empty error output, got: " + ceos);
+        assertTrue(uos.isEmpty(), "Expected empty output, got: " + uos);
+        assertTrue(ueos.isEmpty(), "Expected empty error output, got: " + ueos);
+    }
+
+    public void assertReset(boolean after, String cmd) {
+        assertCommand(after, cmd, "|  Resetting state.\n");
+        initSnippets();
+    }
+
+    public void evaluateExpression(boolean after, String type, String expr, String value) {
+        String output = String.format("\\| *Expression values is: %s\n|" +
+                " *.*temporary variable (\\$\\d+) of type %s", value, type);
+        Pattern outputPattern = Pattern.compile(output);
+        assertCommandCheckOutput(after, expr, s -> {
+            Matcher matcher = outputPattern.matcher(s);
+            assertTrue(matcher.find(), "Output: '" + s + "' does not fit pattern: '" + output + "'");
+            String name = matcher.group(1);
+            VariableInfo tempVar = new TempVariableInfo(expr, type, name, value);
+            variables.put(tempVar.toString(), tempVar);
+            addKey(after, tempVar);
+        });
+    }
+
+    public void loadVariable(boolean after, String type, String name) {
+        loadVariable(after, type, name, null, null);
+    }
+
+    public void loadVariable(boolean after, String type, String name, String expr, String value) {
+        String src = expr == null
+                ? String.format("%s %s", type, name)
+                : String.format("%s %s = %s", type, name, expr);
+        VariableInfo var = expr == null
+                ? new VariableInfo(src, type, name)
+                : new VariableInfo(src, type, name, value);
+        addKey(after, var, variables);
+        addKey(after, var);
+    }
+
+    public void assertVariable(boolean after, String type, String name) {
+        assertVariable(after, type, name, null, null);
+    }
+
+    public void assertVariable(boolean after, String type, String name, String expr, String value) {
+        String src = expr == null
+                ? String.format("%s %s", type, name)
+                : String.format("%s %s = %s", type, name, expr);
+        VariableInfo var = expr == null
+                ? new VariableInfo(src, type, name)
+                : new VariableInfo(src, type, name, value);
+        assertCommandCheckOutput(after, src, var.checkOutput());
+        addKey(after, var, variables);
+        addKey(after, var);
+    }
+
+    public void loadMethod(boolean after, String src, String signature, String name) {
+        MethodInfo method = new MethodInfo(src, signature, name);
+        addKey(after, method, methods);
+        addKey(after, method);
+    }
+
+    public void assertMethod(boolean after, String src, String signature, String name) {
+        MethodInfo method = new MethodInfo(src, signature, name);
+        assertCommandCheckOutput(after, src, method.checkOutput());
+        addKey(after, method, methods);
+        addKey(after, method);
+    }
+
+    public void loadClass(boolean after, String src, String type, String name) {
+        ClassInfo clazz = new ClassInfo(src, type, name);
+        addKey(after, clazz, classes);
+        addKey(after, clazz);
+    }
+
+    public void assertClass(boolean after, String src, String type, String name) {
+        ClassInfo clazz = new ClassInfo(src, type, name);
+        assertCommandCheckOutput(after, src, clazz.checkOutput());
+        addKey(after, clazz, classes);
+        addKey(after, clazz);
+    }
+
+    private <T extends MemberInfo> void addKey(boolean after, T memberInfo, Map<String, T> map) {
+        if (after) {
+            map.entrySet().removeIf(e -> e.getValue().equals(memberInfo));
+            map.put(memberInfo.toString(), memberInfo);
+        }
+    }
+
+    private <T extends MemberInfo> void addKey(boolean after, T memberInfo) {
+        if (after) {
+            for (int i = 0; i < keys.size(); ++i) {
+                MemberInfo m = keys.get(i);
+                if (m.equals(memberInfo)) {
+                    keys.set(i, memberInfo);
+                    return;
+                }
+            }
+            keys.add(memberInfo);
+        }
+    }
+
+    private void dropKey(boolean after, String cmd, String name, Map<String, ? extends MemberInfo> map) {
+        assertCommand(after, cmd, "");
+        if (after) {
+            map.remove(name);
+            for (int i = 0; i < keys.size(); ++i) {
+                MemberInfo m = keys.get(i);
+                if (m.toString().equals(name)) {
+                    keys.remove(i);
+                    return;
+                }
+            }
+            throw new AssertionError("Key not found: " + name + ", keys: " + keys);
+        }
+    }
+
+    public void dropVariable(boolean after, String cmd, String name) {
+        dropKey(after, cmd, name, variables);
+    }
+
+    public void dropMethod(boolean after, String cmd, String name) {
+        dropKey(after, cmd, name, methods);
+    }
+
+    public void dropClass(boolean after, String cmd, String name) {
+        dropKey(after, cmd, name, classes);
+    }
+
+    public void assertCommand(boolean after, String cmd, String out) {
+        assertCommand(after, cmd, out, "", null, "", "");
+    }
+
+    public void assertCommandCheckOutput(boolean after, String cmd, Consumer<String> check) {
+        if (!after) {
+            assertCommand(false, cmd, null);
+        } else {
+            String got = getCommandOutput();
+            check.accept(got);
+            assertCommand(true, cmd, null);
+        }
+    }
+
+    public void assertCommand(boolean after, String cmd, String out, String err,
+            String userinput, String print, String usererr) {
+        if (!after) {
+            if (userinput != null) {
+                setUserInput(userinput);
+            }
+            setCommandInput(cmd + "\n");
+        } else {
+            assertOutput(getCommandOutput(), out, "command");
+            assertOutput(getCommandErrorOutput(), err, "command error");
+            assertOutput(getUserOutput(), print, "user");
+            assertOutput(getUserErrorOutput(), usererr, "user error");
+        }
+    }
+
+    public void assertCompletion(boolean after, String code, boolean isSmart, String... expected) {
+        if (!after) {
+            setCommandInput("\n");
+        } else {
+            assertCompletion(code, isSmart, expected);
+        }
+    }
+
+    public void assertCompletion(String code, boolean isSmart, String... expected) {
+        List<String> completions = computeCompletions(code, isSmart);
+        assertEquals(completions, Arrays.asList(expected), "Command: " + code + ", output: " +
+                completions.toString());
+    }
+
+    private List<String> computeCompletions(String code, boolean isSmart) {
+        JShellTool repl = this.repl != null ? this.repl
+                                      : new JShellTool(null, null, null, null, null, null, null);
+        int cursor =  code.indexOf('|');
+        code = code.replace("|", "");
+        assertTrue(cursor > -1, "'|' not found: " + code);
+        List<Suggestion> completions =
+                repl.commandCompletionSuggestions(code, cursor, new int[1]); //XXX: ignoring anchor for now
+        return completions.stream()
+                          .filter(s -> isSmart == s.isSmart)
+                          .map(s -> s.continuation)
+                          .distinct()
+                          .collect(Collectors.toList());
+    }
+
+    public Consumer<String> assertStartsWith(String prefix) {
+        return (output) -> assertTrue(output.startsWith(prefix), "Output: \'" + output + "' does not start with: " + prefix);
+    }
+
+    public void assertOutput(String got, String expected, String kind) {
+        if (expected != null) {
+            assertEquals(got, expected, "Kind: " + kind + ".\n");
+        }
+    }
+
+    public static abstract class MemberInfo {
+        public final String source;
+        public final String type;
+        public final String name;
+
+        public MemberInfo(String source, String type, String name) {
+            this.source = source;
+            this.type = type;
+            this.name = name;
+        }
+
+        @Override
+        public int hashCode() {
+            return name.hashCode();
+        }
+
+        public abstract Consumer<String> checkOutput();
+
+        public String getSource() {
+            return source;
+        }
+    }
+
+    public static class VariableInfo extends MemberInfo {
+
+        public final String value;
+        public final String initialValue;
+
+        public VariableInfo(String src, String type, String name) {
+            super(src, type, name);
+            this.initialValue = null;
+            switch (type) {
+                case "byte":
+                case "short":
+                case "int":
+                case "long":
+                    value = "0";
+                    break;
+                case "boolean":
+                    value = "false";
+                    break;
+                case "char":
+                    value = "''";
+                    break;
+                case "float":
+                case "double":
+                    value = "0.0";
+                    break;
+                default:
+                    value = "null";
+            }
+        }
+
+        public VariableInfo(String src, String type, String name, String value) {
+            super(src, type, name);
+            this.value = value;
+            this.initialValue = value;
+        }
+
+        @Override
+        public Consumer<String> checkOutput() {
+            String pattern = String.format("\\| *\\w+ variable %s of type %s", name, type);
+            if (initialValue != null) {
+                pattern += " with initial value " + initialValue;
+            }
+            Predicate<String> checkOutput = Pattern.compile(pattern).asPredicate();
+            final String finalPattern = pattern;
+            return output -> assertTrue(checkOutput.test(output),
+                    "Output: " + output + " does not fit pattern: " + finalPattern);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof VariableInfo) {
+                VariableInfo v = (VariableInfo) o;
+                return name.equals(v.name);
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s %s = %s", type, name, value);
+        }
+
+        @Override
+        public String getSource() {
+            String src = super.getSource();
+            return src.endsWith(";") ? src : src + ";";
+        }
+    }
+
+    public static class TempVariableInfo extends VariableInfo {
+
+        public TempVariableInfo(String src, String type, String name, String value) {
+            super(src, type, name, value);
+        }
+
+        @Override
+        public String getSource() {
+            return source;
+        }
+    }
+
+    public static class MethodInfo extends MemberInfo {
+
+        public final String signature;
+
+        public MethodInfo(String source, String signature, String name) {
+            super(source, signature.substring(0, signature.lastIndexOf(')') + 1), name);
+            this.signature = signature;
+        }
+
+        @Override
+        public Consumer<String> checkOutput() {
+            String expectedOutput = String.format("\\| *\\w+ method %s", name);
+            Predicate<String> checkOutput = Pattern.compile(expectedOutput).asPredicate();
+            return s -> assertTrue(checkOutput.test(s), "Expected: '" + expectedOutput + "', actual: " + s);
+        }
+
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof MemberInfo) {
+                MemberInfo m = (MemberInfo) o;
+                return name.equals(m.name) && type.equals(m.type);
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s %s", name, signature);
+        }
+    }
+
+    public static class ClassInfo extends MemberInfo {
+
+        public ClassInfo(String source, String type, String name) {
+            super(source, type, name);
+        }
+
+        @Override
+        public Consumer<String> checkOutput() {
+            String fullType = type.equals("@interface")? "annotation interface" : type;
+            String expectedOutput = String.format("\\| *\\w+ %s %s", fullType, name);
+            Predicate<String> checkOutput = Pattern.compile(expectedOutput).asPredicate();
+            return s -> assertTrue(checkOutput.test(s), "Expected: '" + expectedOutput + "', actual: " + s);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof ClassInfo) {
+                ClassInfo c = (ClassInfo) o;
+                return name.equals(c.name);
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s %s", type, name);
+        }
+    }
+
+    class WaitingTestingInputStream extends TestingInputStream {
+
+        @Override
+        synchronized void setInput(String s) {
+            super.setInput(s);
+            notify();
+        }
+
+        synchronized void waitForInput() {
+            boolean interrupted = false;
+            try {
+                while (available() == 0) {
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                        interrupted = true;
+                        // fall through and retry
+                    }
+                }
+            } finally {
+                if (interrupted) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+
+        @Override
+        public int read() {
+            waitForInput();
+            return super.read();
+        }
+
+        @Override
+        public int read(byte b[], int off, int len) {
+            waitForInput();
+            return super.read(b, off, len);
+        }
+    }
+
+    class PromptedCommandOutputStream extends OutputStream {
+        private final ReplTest[] tests;
+        private int index = 0;
+        PromptedCommandOutputStream(ReplTest[] tests) {
+            this.tests = tests;
+        }
+
+        @Override
+        public synchronized void write(int b) {
+            if (b == 5 || b == 6) {
+                if (index < (tests.length - 1)) {
+                    tests[index].run(true);
+                    tests[index + 1].run(false);
+                } else {
+                    fail("Did not exit Repl tool after test");
+                }
+                ++index;
+            } // For now, anything else is thrown away
+        }
+
+        @Override
+        public synchronized void write(byte b[], int off, int len) {
+            if ((off < 0) || (off > b.length) || (len < 0)
+                    || ((off + len) - b.length > 0)) {
+                throw new IndexOutOfBoundsException();
+            }
+            for (int i = 0; i < len; ++i) {
+                write(b[off + i]);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ReplaceTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,803 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test of Snippet redefinition and replacement.
+ * @build KullaTesting TestingInputStream
+ * @run testng ReplaceTest
+ */
+
+import java.util.Collection;
+
+import jdk.jshell.Snippet;
+import jdk.jshell.MethodSnippet;
+import jdk.jshell.PersistentSnippet;
+import jdk.jshell.TypeDeclSnippet;
+import jdk.jshell.VarSnippet;
+import jdk.jshell.DeclarationSnippet;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.*;
+import static jdk.jshell.Snippet.SubKind.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+@Test
+public class ReplaceTest extends KullaTesting {
+
+    public void testRedefine() {
+        Snippet vx = varKey(assertEval("int x;"));
+        Snippet mu = methodKey(assertEval("int mu() { return x * 4; }"));
+        Snippet c = classKey(assertEval("class C { String v() { return \"#\" + mu(); } }"));
+        assertEval("C c0  = new C();");
+        assertEval("c0.v();", "\"#0\"");
+        assertEval("int x = 10;", "10",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(vx, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("c0.v();", "\"#40\"");
+        assertEval("C c = new C();");
+        assertEval("c.v();", "\"#40\"");
+        assertEval("int mu() { return x * 3; }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(mu, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("c.v();", "\"#30\"");
+        assertEval("class C { String v() { return \"@\" + mu(); } }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(c, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("c0.v();", "\"@30\"");
+        assertEval("c = new C();");
+        assertEval("c.v();", "\"@30\"");
+        assertActiveKeys();
+    }
+
+    public void testReplaceClassToVar() {
+        Snippet oldA = classKey(assertEval("class A { public String toString() { return \"old\"; } }"));
+        Snippet v = varKey(assertEval("A a = new A();", "old"));
+        assertEval("a;", "old");
+        Snippet midA = classKey(assertEval("class A { public String toString() { return \"middle\"; } }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(oldA, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertEval("a;", "middle");
+        assertEval("class A { int x; public String toString() { return \"new\"; } }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(midA, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(v, VALID, VALID, true, MAIN_SNIPPET));
+        assertEval("a;", "null");
+        assertActiveKeys();
+    }
+
+    public void testReplaceVarToMethod() {
+        Snippet x = varKey(assertEval("int x;"));
+        Snippet musn = methodKey(assertEval("double mu() { return x * 4; }"));
+        assertEval("x == 0;", "true");
+        assertEval("mu() == 0.0;", "true");
+        assertEval("double x = 2.5;",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(x, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(musn, VALID, VALID, false, MAIN_SNIPPET));
+        Collection<MethodSnippet> meths = getState().methods();
+        assertEquals(meths.size(), 1);
+        assertTrue(musn == meths.iterator().next(), "Identity must not change");
+        assertEval("x == 2.5;", "true");
+        assertEval("mu() == 10.0;", "true");  // Auto redefine
+        assertActiveKeys();
+    }
+
+    public void testReplaceMethodToMethod() {
+        Snippet a = methodKey(assertEval("double a() { return 2; }"));
+        Snippet b = methodKey(assertEval("double b() { return a() * 10; }"));
+        assertEval("double c() { return b() * 3; }");
+        assertEval("double d() { return c() + 1000; }");
+        assertEval("d();", "1060.0");
+        assertEval("int a() { return 5; }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(b, VALID, VALID, false, MAIN_SNIPPET));
+        assertEval("d();", "1150.0");
+        assertActiveKeys();
+    }
+
+    public void testReplaceClassToMethod() {
+        Snippet c = classKey(assertEval("class C { int f() { return 7; } }"));
+        Snippet m = methodKey(assertEval("int m() { return new C().f(); }"));
+        assertEval("m();", "7");
+        assertEval("class C { int x = 99; int f() { return x; } }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(c, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(m, VALID, VALID, false, MAIN_SNIPPET));
+        assertEval("m();", "99");
+        assertActiveKeys();
+    }
+
+    public void testReplaceVarToClass() {
+        Snippet x = varKey(assertEval("int x;"));
+        Snippet c = classKey(assertEval("class A { double a = 4 * x; }"));
+        assertEval("x == 0;", "true");
+        assertEval("new A().a == 0.0;", "true");
+        assertEval("double x = 2.5;",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(x, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(c, VALID, VALID, false, MAIN_SNIPPET));
+        Collection<TypeDeclSnippet> classes = getState().types();
+        assertEquals(classes.size(), 1);
+        assertTrue(c == classes.iterator().next(), "Identity must not change");
+        assertEval("x == 2.5;", "true");
+        assertEval("new A().a == 10.0;", "true");
+        assertActiveKeys();
+    }
+
+    public void testReplaceMethodToClass() {
+        Snippet x = methodKey(assertEval("int x() { return 0; }"));
+        Snippet c = classKey(assertEval("class A { double a = 4 * x(); }"));
+        assertEval("x() == 0;", "true");
+        assertEval("new A().a == 0.0;", "true");
+        assertEval("double x() { return 2.5; }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(x, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(c, VALID, VALID, false, MAIN_SNIPPET));
+        assertEval("x();", "2.5");
+        Collection<TypeDeclSnippet> classes = getState().types();
+        assertEquals(classes.size(), 1);
+        assertTrue(c == classes.iterator().next(), "Identity must not change");
+        assertEval("x() == 2.5;", "true");
+        assertEval("new A().a == 10.0;", "true");
+        assertActiveKeys();
+    }
+
+    public void testReplaceClassToClass() {
+        TypeDeclSnippet a = classKey(assertEval("class A {}"));
+        assertTypeDeclSnippet(a, "A", VALID, CLASS_SUBKIND, 0, 0);
+        TypeDeclSnippet b = classKey(assertEval("class B extends A {}"));
+        TypeDeclSnippet c = classKey(assertEval("class C extends B {}"));
+        TypeDeclSnippet d = classKey(assertEval("class D extends C {}"));
+        assertEval("class A { int x; public String toString() { return \"NEW\"; } }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(b, VALID, VALID, true, MAIN_SNIPPET),
+                ste(c, VALID, VALID, true, b),
+                ste(d, VALID, VALID, true, c));
+        assertTypeDeclSnippet(b, "B", VALID, CLASS_SUBKIND, 0, 0);
+        assertTypeDeclSnippet(c, "C", VALID, CLASS_SUBKIND, 0, 0);
+        assertTypeDeclSnippet(d, "D", VALID, CLASS_SUBKIND, 0, 0);
+        assertEval("new D();", "NEW");
+        assertActiveKeys();
+    }
+
+    public void testOverwriteReplaceMethod() {
+        MethodSnippet k1 = methodKey(assertEval("String m(Integer i) { return i.toString(); }"));
+        MethodSnippet k2 = methodKey(assertEval("String m(java.lang.Integer i) { return \"java.lang.\" + i.toString(); }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(k1, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertMethodDeclSnippet(k1, "m", "(Integer)String", OVERWRITTEN, 0, 0);
+        assertEval("m(6);", "\"java.lang.6\"");
+        assertEval("String m(Integer i) { return i.toString(); }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(k2, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertMethodDeclSnippet(k2, "m", "(java.lang.Integer)String", OVERWRITTEN, 0, 0);
+        assertEval("m(6);", "\"6\"");
+        assertActiveKeys();
+    }
+
+    public void testOverwriteMethodForwardReferenceClass() {
+        Snippet k1 = methodKey(assertEval("int q(Boo b) { return b.x; }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertUnresolvedDependencies1((MethodSnippet) k1, RECOVERABLE_NOT_DEFINED, "class Boo");
+        assertEval("class Boo { int x = 55; }",
+                added(VALID),
+                ste(k1, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertMethodDeclSnippet((MethodSnippet) k1, "q", "(Boo)int", VALID, 0, 0);
+        assertEval("q(new Boo());", "55");
+        assertActiveKeys();
+    }
+
+    public void testOverwriteMethodForwardReferenceClassImport() {
+        MethodSnippet k1 = methodKey(assertEval("int ff(List lis) { return lis.size(); }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertUnresolvedDependencies1(k1, RECOVERABLE_NOT_DEFINED, "class List");
+        assertEval("import java.util.*;",
+                added(VALID),
+                ste(k1, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertMethodDeclSnippet(k1, "ff", "(List)int", VALID, 0, 0);
+        assertEval("ff(new ArrayList());", "0");
+        assertActiveKeys();
+    }
+
+    public void testForwardVarToMethod() {
+        DeclarationSnippet t = methodKey(assertEval("int t() { return x; }", added(RECOVERABLE_DEFINED)));
+        assertUnresolvedDependencies1(t, RECOVERABLE_DEFINED, "variable x");
+        assertEvalUnresolvedException("t();", "t", 1, 0);
+        Snippet x = varKey(assertEval("int x = 33;", "33",
+                added(VALID),
+                ste(t, RECOVERABLE_DEFINED, VALID, false, null)));
+        assertEval("t();", "33");
+        assertEval("double x = 0.88;",
+                "0.88", null,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(x, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(t, VALID, RECOVERABLE_DEFINED, false, MAIN_SNIPPET));
+        assertEvalUnresolvedException("t();", "t", 0, 1);
+        assertActiveKeys();
+    }
+
+    public void testForwardMethodToMethod() {
+        Snippet t = methodKey(assertEval("int t() { return f(); }", added(RECOVERABLE_DEFINED)));
+        Snippet f = methodKey(assertEval("int f() { return g(); }",
+                added(RECOVERABLE_DEFINED),
+                ste(t, RECOVERABLE_DEFINED, VALID, false, null)));
+        assertUnresolvedDependencies1((DeclarationSnippet) f, RECOVERABLE_DEFINED, "method g()");
+        assertEvalUnresolvedException("t();", "f", 1, 0);
+        Snippet g = methodKey(assertEval("int g() { return 55; }",
+                added(VALID),
+                ste(f, RECOVERABLE_DEFINED, VALID, false, null)));
+        assertEval("t();", "55");
+        assertEval("double g() { return 3.14159; }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(g, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(f, VALID, RECOVERABLE_DEFINED, false, MAIN_SNIPPET));
+        MethodSnippet exsn = assertEvalUnresolvedException("t();", "f", 0, 1);
+        assertTrue(exsn == f, "Identity must not change");
+        assertActiveKeys();
+    }
+
+    public void testForwardClassToMethod() {
+        DeclarationSnippet t = methodKey(assertEval("int t() { return new A().f(); }", added(RECOVERABLE_DEFINED)));
+        assertUnresolvedDependencies1(t, RECOVERABLE_DEFINED, "class A");
+        assertEvalUnresolvedException("t();", "t", 1, 0);
+        Snippet a = classKey(assertEval(
+                "class A {\n" +
+                        "   int f() { return 10; }\n" +
+                "}",
+                added(VALID),
+                ste(t, RECOVERABLE_DEFINED, VALID, false, null)));
+        assertEval("t();", "10");
+        assertEval(
+                "class A {\n" +
+                "   double f() { return 88.0; }\n" +
+                "}",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(t, VALID, RECOVERABLE_DEFINED, false, MAIN_SNIPPET));
+        assertEvalUnresolvedException("t();", "t", 0, 1);
+        assertActiveKeys();
+    }
+
+    public void testForwardVarToClass() {
+        DeclarationSnippet a = classKey(assertEval("class A { int f() { return g; } }", added(RECOVERABLE_NOT_DEFINED)));
+        assertUnresolvedDependencies1(a, RECOVERABLE_NOT_DEFINED, "variable g");
+        Snippet g = varKey(assertEval("int g = 10;", "10",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null)));
+        assertEval("new A().f();", "10");
+        assertEval("double g = 10;", "10.0", null,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(g, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+        assertUnresolvedDependencies(a, 0);
+        assertActiveKeys();
+    }
+
+
+    public void testForwardMethodToClass() {
+        DeclarationSnippet a = classKey(assertEval("class A { int f() { return g(); } }", added(RECOVERABLE_NOT_DEFINED)));
+        assertUnresolvedDependencies1(a, RECOVERABLE_NOT_DEFINED, "method g()");
+        Snippet g = methodKey(assertEval("int g() { return 10; }",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null)));
+        assertEval("new A().f();", "10");
+        assertEval("double g() { return 10; }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(g, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+        assertUnresolvedDependencies(a, 0);
+        assertActiveKeys();
+    }
+
+    public void testForwardClassToClass1() {
+        Snippet a = classKey(assertEval("class A { B b = new B(); }", added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A().b;", "compiler.err.cant.resolve.location");
+
+        Snippet b = classKey(assertEval("class B { public String toString() { return \"B\"; } }",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null)));
+        assertEval("new A().b;", "B");
+        assertEval("interface B { }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(b, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+        assertDeclareFail("new A().b;", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+    }
+
+    public void testForwardClassToClass2() {
+        Snippet a = classKey(assertEval("class A extends B { }", added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+
+        Snippet b = classKey(assertEval("class B { public String toString() { return \"B\"; } }",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null)));
+        assertEval("new A();", "B");
+        assertEval("interface B { }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(b, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+    }
+
+    public void testForwardClassToClass3() {
+        Snippet a = classKey(assertEval("interface A extends B { static int f() { return 10; } }", added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("A.f();", "compiler.err.cant.resolve.location");
+
+        Snippet b = classKey(assertEval("interface B { }",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null)));
+        assertEval("A.f();", "10");
+        assertEval("class B { }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(b, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+        assertDeclareFail("A.f();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+    }
+
+    public void testImportDeclare() {
+        Snippet singleImport = importKey(assertEval("import java.util.List;", added(VALID)));
+        Snippet importOnDemand = importKey(assertEval("import java.util.*;", added(VALID)));
+        Snippet singleStaticImport = importKey(assertEval("import static java.lang.Math.abs;", added(VALID)));
+        Snippet staticImportOnDemand = importKey(assertEval("import static java.lang.Math.*;", added(VALID)));
+        assertEval("import java.util.List; //again",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(singleImport, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("import java.util.*; //again",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(importOnDemand, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("import static java.lang.Math.abs; //again",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(singleStaticImport, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertEval("import static java.lang.Math.*; //again",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(staticImportOnDemand, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertActiveKeys();
+    }
+
+    public void testForwardVariable() {
+        assertEval("int f() { return x; }", added(RECOVERABLE_DEFINED));
+        assertEvalUnresolvedException("f();", "f", 1, 0);
+        assertActiveKeys();
+    }
+
+    public void testLocalClassInUnresolved() {
+        Snippet f = methodKey(assertEval("void f() { class A {} g(); }", added(RECOVERABLE_DEFINED)));
+        assertEval("void g() {}",
+                added(VALID),
+                ste(f, RECOVERABLE_DEFINED, VALID, false, null));
+        assertEval("f();", "");
+    }
+
+    @Test(enabled = false) // TODO 8129420
+    public void testLocalClassEvolve() {
+        Snippet j = methodKey(assertEval("Object j() { return null; }", added(VALID)));
+        assertEval("Object j() { class B {}; return null; }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null));
+        assertEval("Object j() { class B {}; return new B(); }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null));
+        assertEval("j().getClass().getSimpleName();", "\"B\"");
+        assertEval("Object j() { class B { int p; public String toString() { return \"Yep\";} }; return new B(); }",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null));
+        assertEval("j().getClass().getSimpleName();", "\"B\"");
+        assertEval("j();", "Yep");
+    }
+
+    public void testForwardSingleImportMethodToMethod() {
+        DeclarationSnippet string = methodKey(assertEval("String string() { return format(\"string\"); }",
+                added(RECOVERABLE_DEFINED)));
+        assertUnresolvedDependencies1(string, RECOVERABLE_DEFINED, "method format(java.lang.String)");
+        assertEvalUnresolvedException("string();", "string", 1, 0);
+        assertEval("import static java.lang.String.format;",
+                added(VALID),
+                ste(string, RECOVERABLE_DEFINED, VALID, false, null));
+        assertEval("string();", "\"string\"");
+
+        assertEval("double format(String s) { return 0; }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(string, VALID, RECOVERABLE_DEFINED, false, null));
+        assertEvalUnresolvedException("string();", "string", 0, 1);
+        assertActiveKeys();
+    }
+
+    public void testForwardImportMethodOnDemandToMethod() {
+        DeclarationSnippet string = methodKey(assertEval("String string() { return format(\"string\"); }",
+                added(RECOVERABLE_DEFINED)));
+        assertUnresolvedDependencies1(string, RECOVERABLE_DEFINED, "method format(java.lang.String)");
+        assertEvalUnresolvedException("string();", "string", 1, 0);
+        assertEval("import static java.lang.String.*;",
+                added(VALID),
+                ste(string, RECOVERABLE_DEFINED, VALID, false, null));
+        assertEval("string();", "\"string\"");
+
+        assertEval("double format(String s) { return 0; }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(string, VALID, RECOVERABLE_DEFINED, false, null));
+        assertEvalUnresolvedException("string();", "string", 0, 1);
+        assertActiveKeys();
+    }
+
+    public void testForwardSingleImportFieldToMethod() {
+        DeclarationSnippet pi = methodKey(assertEval("double pi() { return PI; }",
+                added(RECOVERABLE_DEFINED)));
+        assertUnresolvedDependencies1(pi, RECOVERABLE_DEFINED, "variable PI");
+        assertEvalUnresolvedException("pi();", "pi", 1, 0);
+        assertEval("import static java.lang.Math.PI;",
+                added(VALID),
+                ste(pi, RECOVERABLE_DEFINED, VALID, false, null));
+        assertEval("Math.abs(pi() - 3.1415) < 0.001;", "true");
+
+        assertEval("String PI;",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(pi, VALID, RECOVERABLE_DEFINED, false, null));
+        assertEvalUnresolvedException("pi();", "pi", 0, 1);
+        assertActiveKeys();
+    }
+
+    public void testForwardImportFieldOnDemandToMethod() {
+        DeclarationSnippet pi = methodKey(assertEval("double pi() { return PI; }",
+                added(RECOVERABLE_DEFINED)));
+        assertUnresolvedDependencies1(pi, RECOVERABLE_DEFINED, "variable PI");
+        assertEvalUnresolvedException("pi();", "pi", 1, 0);
+        assertEval("import static java.lang.Math.*;",
+                added(VALID),
+                ste(pi, RECOVERABLE_DEFINED, VALID, false, MAIN_SNIPPET));
+        assertEval("Math.abs(pi() - 3.1415) < 0.001;", "true");
+
+        assertEval("String PI;",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(pi, VALID, RECOVERABLE_DEFINED, false, MAIN_SNIPPET));
+        assertEvalUnresolvedException("pi();", "pi", 0, 1);
+        assertActiveKeys();
+    }
+
+    public void testForwardSingleImportMethodToClass1() {
+        PersistentSnippet a = classKey(assertEval("class A { String s = format(\"%d\", 10); }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import static java.lang.String.format;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("new A().s;", "\"10\"");
+        PersistentSnippet format = methodKey(assertEval("void format(String s, int d) { }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET)));
+        assertDeclareFail("new A().s;", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(format,
+                ste(format, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, format));
+    }
+
+    public void testForwardSingleImportMethodToClass2() {
+        PersistentSnippet a = classKey(assertEval("class A { String s() { return format(\"%d\", 10); } }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import static java.lang.String.format;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("new A().s();", "\"10\"");
+        PersistentSnippet format = methodKey(assertEval("void format(String s, int d) { }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("new A().s();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(format,
+                ste(format, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, format));
+    }
+
+    public void testForwardSingleImportClassToClass1() {
+        PersistentSnippet a = classKey(assertEval("class A { static List<Integer> list; }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import java.util.List;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("import java.util.Arrays;", added(VALID));
+        assertEval("A.list = Arrays.asList(1, 2, 3);", "[1, 2, 3]");
+
+        PersistentSnippet list = classKey(assertEval("class List {}",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("A.list = Arrays.asList(1, 2, 3);", "compiler.err.already.defined.static.single.import");
+        assertActiveKeys();
+        assertDrop(list,
+                ste(list, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, list));
+    }
+
+    public void testForwardSingleImportClassToClass2() {
+        PersistentSnippet clsA = classKey(assertEval("class A extends ArrayList<Integer> { }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import java.util.ArrayList;",
+                added(VALID),
+                ste(clsA, RECOVERABLE_NOT_DEFINED, VALID, true, MAIN_SNIPPET));
+        Snippet vara = varKey(assertEval("A a = new A();", "[]"));
+
+        PersistentSnippet arraylist = classKey(assertEval("class ArrayList {}",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(clsA, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET),
+                ste(vara, VALID, RECOVERABLE_NOT_DEFINED, true, clsA)));
+        assertDeclareFail("A a = new A();", "compiler.err.cant.resolve.location",
+                ste(MAIN_SNIPPET, RECOVERABLE_NOT_DEFINED, REJECTED, false, null),
+                ste(vara, RECOVERABLE_NOT_DEFINED, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertActiveKeys();
+        assertDrop(arraylist,
+                ste(arraylist, VALID, DROPPED, true, null),
+                ste(clsA, RECOVERABLE_NOT_DEFINED, VALID, true, arraylist));
+    }
+
+    public void testForwardImportOnDemandMethodToClass1() {
+        PersistentSnippet a = classKey(assertEval("class A { String s = format(\"%d\", 10); }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import static java.lang.String.*;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("new A().s;", "\"10\"");
+        PersistentSnippet format = methodKey(assertEval("void format(String s, int d) { }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("new A().s;", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(format,
+                ste(format, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, format));
+    }
+
+    public void testForwardImportOnDemandMethodToClass2() {
+        PersistentSnippet a = classKey(assertEval("class A { String s() { return format(\"%d\", 10); } }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import static java.lang.String.*;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("new A().s();", "\"10\"");
+        PersistentSnippet format = methodKey(assertEval("void format(String s, int d) { }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("new A().s();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(format,
+                ste(format, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, format));
+    }
+
+    public void testForwardImportOnDemandClassToClass1() {
+        PersistentSnippet a = classKey(assertEval("class A { static List<Integer> list; }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import java.util.*;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("A.list = Arrays.asList(1, 2, 3);", "[1, 2, 3]");
+
+        PersistentSnippet list = classKey(assertEval("class List {}",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("A.list = Arrays.asList(1, 2, 3);", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(list,
+                ste(list, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, list));
+    }
+
+    public void testForwardImportOnDemandClassToClass2() {
+        PersistentSnippet clsA = classKey(assertEval("class A extends ArrayList<Integer> { }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import java.util.*;",
+                added(VALID),
+                ste(clsA, RECOVERABLE_NOT_DEFINED, VALID, true, MAIN_SNIPPET));
+        Snippet vara = varKey(assertEval("A a = new A();", "[]"));
+
+        PersistentSnippet arraylist = classKey(assertEval("class ArrayList {}",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(clsA, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET),
+                ste(vara, VALID, RECOVERABLE_NOT_DEFINED, true, clsA)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(arraylist,
+                ste(arraylist, VALID, DROPPED, true, null),
+                ste(clsA, RECOVERABLE_NOT_DEFINED, VALID, true, arraylist),
+                ste(vara, RECOVERABLE_NOT_DEFINED, VALID, true, clsA));
+    }
+
+    public void testForwardSingleImportFieldToClass1() {
+        PersistentSnippet a = classKey(assertEval("class A { static double pi() { return PI; } }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import static java.lang.Math.PI;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("Math.abs(A.pi() - 3.1415) < 0.001;", "true");
+
+        PersistentSnippet list = varKey(assertEval("String PI;",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(list,
+                ste(list, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, list));
+    }
+
+    public void testForwardSingleImportFieldToClass2() {
+        PersistentSnippet a = classKey(assertEval("class A { static double pi = PI; }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import static java.lang.Math.PI;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("Math.abs(A.pi - 3.1415) < 0.001;", "true");
+
+        PersistentSnippet list = varKey(assertEval("String PI;",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(list,
+                ste(list, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, list));
+    }
+
+    public void testForwardImportOnDemandFieldToClass1() {
+        PersistentSnippet a = classKey(assertEval("class A { static double pi() { return PI; } }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import static java.lang.Math.*;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("Math.abs(A.pi() - 3.1415) < 0.001;", "true");
+
+        PersistentSnippet list = varKey(assertEval("String PI;",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(list,
+                ste(list, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, list));
+    }
+
+    public void testForwardImportOnDemandFieldToClass2() {
+        PersistentSnippet a = classKey(assertEval("class A { static double pi = PI; }",
+                added(RECOVERABLE_NOT_DEFINED)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertEval("import static java.lang.Math.*;",
+                added(VALID),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, null));
+        assertEval("Math.abs(A.pi - 3.1415) < 0.001;", "true");
+
+        PersistentSnippet list = varKey(assertEval("String PI;",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(a, VALID, RECOVERABLE_NOT_DEFINED, true, null)));
+        assertDeclareFail("new A();", "compiler.err.cant.resolve.location");
+        assertActiveKeys();
+        assertDrop(list,
+                ste(list, VALID, DROPPED, true, null),
+                ste(a, RECOVERABLE_NOT_DEFINED, VALID, true, list));
+    }
+
+    public void testReplaceCausesMethodReferenceError() {
+        Snippet l = classKey(assertEval("interface Logger { public void log(String message); }", added(VALID)));
+        Snippet v = varKey(assertEval("Logger l = System.out::println;", added(VALID)));
+        assertEval("interface Logger { public boolean accept(String message);  }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(l, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(v, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+    }
+
+    public void testReplaceCausesClassCompilationError() {
+        Snippet l = classKey(assertEval("interface L { }", added(VALID)));
+        Snippet c = classKey(assertEval("class C implements L { }", added(VALID)));
+        assertEval("interface L { void m(); }",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(l, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(c, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+    }
+
+    public void testOverwriteNoUpdate() {
+        String xsi = "int x = 5;";
+        String xsd = "double x = 3.14159;";
+        VarSnippet xi = varKey(assertEval(xsi, added(VALID)));
+        String ms1 = "double m(Integer i) { return i + x; }";
+        String ms2 = "double m(java.lang.Integer i) { return i + x; }";
+        MethodSnippet k1 = methodKey(assertEval(ms1, added(VALID)));
+        VarSnippet xd = varKey(assertEval(xsd,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(xi, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(k1, VALID, VALID, false, MAIN_SNIPPET)));
+        MethodSnippet k2 = methodKey(assertEval(ms2,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null), //TODO: technically, should be false
+                ste(k1, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        VarSnippet xi2 = varKey(assertEval(xsi,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(xd, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(k2, VALID, VALID, false, MAIN_SNIPPET)));
+        varKey(assertEval(xsd,
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(xi2, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(k2, VALID, VALID, false, MAIN_SNIPPET)));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ShutdownTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Shutdown tests
+ * @build KullaTesting TestingInputStream
+ * @run testng ShutdownTest
+ */
+
+import java.util.function.Consumer;
+
+import jdk.jshell.JShell;
+import jdk.jshell.JShell.Subscription;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+@Test
+public class ShutdownTest extends KullaTesting {
+
+    int shutdownCount;
+
+    void shutdownCounter(JShell state) {
+        ++shutdownCount;
+    }
+
+    @Test(enabled = false) //TODO 8139873
+    public void testExit() {
+        shutdownCount = 0;
+        getState().onShutdown(this::shutdownCounter);
+        assertEval("System.exit(1);");
+        assertEquals(shutdownCount, 1);
+    }
+
+    public void testCloseCallback() {
+        shutdownCount = 0;
+        getState().onShutdown(this::shutdownCounter);
+        getState().close();
+        assertEquals(shutdownCount, 1);
+    }
+
+    public void testCloseUnsubscribe() {
+        shutdownCount = 0;
+        Subscription token = getState().onShutdown(this::shutdownCounter);
+        getState().unsubscribe(token);
+        getState().close();
+        assertEquals(shutdownCount, 0);
+    }
+
+    public void testTwoShutdownListeners() {
+        ShutdownListener listener1 = new ShutdownListener();
+        ShutdownListener listener2 = new ShutdownListener();
+        Subscription subscription1 = getState().onShutdown(listener1);
+        Subscription subscription2 = getState().onShutdown(listener2);
+        getState().unsubscribe(subscription1);
+        getState().close();
+
+        assertEquals(listener1.getEvents(), 0, "Checking got events");
+        assertEquals(listener2.getEvents(), 1, "Checking got events");
+
+        getState().close();
+
+        assertEquals(listener1.getEvents(), 0, "Checking got events");
+        assertEquals(listener2.getEvents(), 1, "Checking got events");
+
+        getState().unsubscribe(subscription2);
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class)
+    public void testCloseException() {
+        getState().close();
+        getState().eval("45");
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class,
+          enabled = false) //TODO 8139873
+    public void testShutdownException() {
+        assertEval("System.exit(0);");
+        getState().eval("45");
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testNullCallback() {
+        getState().onShutdown(null);
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class)
+    public void testSubscriptionAfterClose() {
+        getState().close();
+        getState().onShutdown(e -> {});
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class,
+          enabled = false) //TODO 8139873
+    public void testSubscriptionAfterShutdown() {
+        assertEval("System.exit(0);");
+        getState().onShutdown(e -> {});
+    }
+
+    private static class ShutdownListener implements Consumer<JShell> {
+        private int count;
+
+        @Override
+        public void accept(JShell shell) {
+            ++count;
+        }
+
+        public int getEvents() {
+            return count;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/SimpleRegressionTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary simple regression test
+ * @build KullaTesting TestingInputStream
+ * @run testng SimpleRegressionTest
+ */
+
+
+import java.util.List;
+
+import javax.tools.Diagnostic;
+
+import jdk.jshell.Snippet;
+import jdk.jshell.VarSnippet;
+import jdk.jshell.SnippetEvent;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static jdk.jshell.Snippet.SubKind.TEMP_VAR_EXPRESSION_SUBKIND;
+import static jdk.jshell.Snippet.Status.VALID;
+
+@Test
+public class SimpleRegressionTest extends KullaTesting {
+
+    public void testSnippetMemberAssignment() {
+        assertEval("class C { int y; }");
+        assertEval("C c = new C();");
+        assertVarKeyMatch("c.y = 4;", true, "$1", TEMP_VAR_EXPRESSION_SUBKIND, "int", added(VALID));
+    }
+
+    public void testUserTakesTempVarName() {
+        assertEval("int $2 = 4;");
+        assertEval("String $1;");
+        assertVarKeyMatch("1234;", true, "$3", TEMP_VAR_EXPRESSION_SUBKIND, "int", added(VALID));
+    }
+
+    public void testCompileThrow() {
+        assertEvalException("throw new Exception();");
+    }
+
+    public void testMultiSnippetDependencies() {
+        List<SnippetEvent> events = assertEval("int a = 3, b = a+a, c = b *100;",
+                DiagCheck.DIAG_OK, DiagCheck.DIAG_OK,
+                chain(added(VALID)),
+                chain(added(VALID)),
+                chain(added(VALID)));
+        assertEquals(events.get(0).value(), "3");
+        assertEquals(events.get(1).value(), "6");
+        assertEquals(events.get(2).value(), "600");
+        assertEval("c;", "600");
+    }
+
+    public void testNotStmtCannotResolve() {
+        assertDeclareFail("dfasder;", new ExpectedDiagnostic("compiler.err.cant.resolve.location", 0, 7, 0, -1, -1, Diagnostic.Kind.ERROR));
+    }
+
+    public void testNotStmtIncomparable() {
+        assertDeclareFail("true == 5.0;", new ExpectedDiagnostic("compiler.err.incomparable.types", 0, 11, 5, -1, -1, Diagnostic.Kind.ERROR));
+    }
+
+    public void testStringAdd() {
+        assertEval("String s = \"a\" + \"b\";", "\"ab\"");
+    }
+
+    public void testExprSanity() {
+        assertEval("int x = 3;", "3");
+        assertEval("int y = 4;", "4");
+        assertEval("x + y;", "7");
+        assertActiveKeys();
+    }
+
+    public void testGenericMethodCrash() {
+        assertDeclareWarn1("<T> void f(T...a) {}", (ExpectedDiagnostic) null);
+        Snippet sn = methodKey(assertEval("<R> R n(R x) { return x; }", added(VALID)));
+        VarSnippet sne = varKey(assertEval("n(5)", added(VALID)));
+        assertEquals(sne.typeName(), "Integer");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/SnippetStatusListenerTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Subscribe tests
+ * @build KullaTesting TestingInputStream
+ * @run testng SnippetStatusListenerTest
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+import jdk.jshell.DeclarationSnippet;
+import jdk.jshell.JShell.Subscription;
+import jdk.jshell.SnippetEvent;
+import jdk.jshell.TypeDeclSnippet;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.*;
+import static org.testng.Assert.assertEquals;
+
+@Test
+public class SnippetStatusListenerTest extends KullaTesting {
+
+    public void testTwoSnippetEventListeners() {
+        SnippetListener listener1 = new SnippetListener();
+        SnippetListener listener2 = new SnippetListener();
+
+        Subscription subscription1 = getState().onSnippetEvent(listener1);
+        getState().onSnippetEvent(listener2);
+
+        TypeDeclSnippet a = classKey(assertEval("class A {}"));
+        assertEval("interface A {}",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, null));
+        DeclarationSnippet f = (DeclarationSnippet) assertDeclareFail("double f() { }", "compiler.err.missing.ret.stmt");
+        assertEvalException("throw new RuntimeException();");
+        assertEval("int a = 0;");
+
+        List<SnippetEvent> events1 = Collections.unmodifiableList(listener1.getEvents());
+        assertEquals(events1, listener2.getEvents(), "Checking got events");
+        getState().unsubscribe(subscription1);
+
+        assertDrop(f, DiagCheck.DIAG_IGNORE, DiagCheck.DIAG_IGNORE, ste(f, REJECTED, DROPPED, false, null));
+        assertEval("void f() { }", ste(MAIN_SNIPPET, DROPPED, VALID, true, null));
+        assertEvalException("throw new RuntimeException();");
+        assertEquals(listener1.getEvents(), events1, "Checking that unsubscribed listener does not get events");
+
+        List<SnippetEvent> events2 = new ArrayList<>(listener2.getEvents());
+        events2.removeAll(events1);
+
+        assertEquals(events2.size(), 3, "The second listener got events");
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testNullCallback() {
+        getState().onSnippetEvent(null);
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class)
+    public void testSubscriptionAfterClose() {
+        getState().close();
+        getState().onSnippetEvent(e -> {});
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class,
+          enabled = false) //TODO 8139873
+    public void testSubscriptionAfterShutdown() {
+        assertEval("System.exit(0);");
+        getState().onSnippetEvent(e -> {});
+    }
+
+    public void testSubscriptionToAnotherState() {
+        SnippetListener listener = new SnippetListener();
+        Subscription subscription = getState().onSnippetEvent(listener);
+        tearDown();
+        setUp();
+        assertEval("int x;");
+        assertEquals(Collections.emptyList(), listener.getEvents(), "No events");
+        getState().unsubscribe(subscription);
+    }
+
+    private static class SnippetListener implements Consumer<SnippetEvent> {
+        private final List<SnippetEvent> events = new ArrayList<>();
+
+        @Override
+        public void accept(SnippetEvent event) {
+            events.add(event);
+        }
+
+        public List<SnippetEvent> getEvents() {
+            return events;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/SnippetTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary test accessors of Snippet
+ * @build KullaTesting TestingInputStream
+ * @run testng SnippetTest
+ */
+
+import jdk.jshell.Snippet;
+import jdk.jshell.DeclarationSnippet;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.VALID;
+import static jdk.jshell.Snippet.Status.RECOVERABLE_DEFINED;
+import static jdk.jshell.Snippet.Status.OVERWRITTEN;
+import static jdk.jshell.Snippet.SubKind.*;
+
+@Test
+public class SnippetTest extends KullaTesting {
+
+    public void testImportKey() {
+        assertImportKeyMatch("import java.util.List;", "List", SINGLE_TYPE_IMPORT_SUBKIND, added(VALID));
+        assertImportKeyMatch("import java.util.*;", "java.util.*", TYPE_IMPORT_ON_DEMAND_SUBKIND, added(VALID));
+        assertImportKeyMatch("import static java.lang.String.*;", "java.lang.String.*", STATIC_IMPORT_ON_DEMAND_SUBKIND, added(VALID));
+    }
+
+    public void testClassKey() {
+        assertDeclarationKeyMatch("class X {}", false, "X", CLASS_SUBKIND, added(VALID));
+    }
+
+    public void testInterfaceKey() {
+        assertDeclarationKeyMatch("interface I {}", false, "I", INTERFACE_SUBKIND, added(VALID));
+    }
+
+    public void testEnumKey() {
+        assertDeclarationKeyMatch("enum E {}", false, "E", ENUM_SUBKIND, added(VALID));
+    }
+
+    public void testAnnotationKey() {
+        assertDeclarationKeyMatch("@interface A {}", false, "A", ANNOTATION_TYPE_SUBKIND, added(VALID));
+    }
+
+    public void testMethodKey() {
+        assertDeclarationKeyMatch("void m() {}", false, "m", METHOD_SUBKIND, added(VALID));
+    }
+
+    public void testVarDeclarationKey() {
+        assertVarKeyMatch("int a;", false, "a", VAR_DECLARATION_SUBKIND, "int", added(VALID));
+    }
+
+    public void testVarDeclarationWithInitializerKey() {
+        assertVarKeyMatch("double b = 9.0;", true, "b", VAR_DECLARATION_WITH_INITIALIZER_SUBKIND, "double", added(VALID));
+    }
+
+    public void testTempVarExpressionKey() {
+        assertVarKeyMatch("47;", true, "$1", TEMP_VAR_EXPRESSION_SUBKIND, "int", added(VALID));
+    }
+
+    public void testVarValueKey() {
+        assertEval("double x = 4;", "4.0");
+        assertExpressionKeyMatch("x;", "x", VAR_VALUE_SUBKIND, "double");
+    }
+
+    public void testAssignmentKey() {
+        assertEval("int y;");
+        assertExpressionKeyMatch("y = 4;", "y", ASSIGNMENT_SUBKIND, "int");
+    }
+
+    public void testStatementKey() {
+        assertKeyMatch("if (true) {}", true, STATEMENT_SUBKIND, added(VALID));
+        assertKeyMatch("while (true) { break; }", true, STATEMENT_SUBKIND, added(VALID));
+        assertKeyMatch("do { } while (false);", true, STATEMENT_SUBKIND, added(VALID));
+        assertKeyMatch("for (;;) { break; }", true, STATEMENT_SUBKIND, added(VALID));
+    }
+
+    public void noKeys() {
+        assertActiveKeys(new DeclarationSnippet[0]);
+    }
+
+    public void testKeyId1() {
+        Snippet a = classKey(assertEval("class A { }"));
+        assertEval("void f() {  }");
+        assertEval("int f;");
+        assertEval("interface A { }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertKeys(method("()void", "f"), variable("int", "f"), clazz(KullaTesting.ClassType.INTERFACE, "A"));
+        assertActiveKeys();
+    }
+
+    @Test(enabled = false) // TODO 8081689
+    public void testKeyId2() {
+        Snippet g = methodKey(assertEval("void g() { f(); }", added(RECOVERABLE_DEFINED)));
+        Snippet f = methodKey(assertEval("void f() { }",
+                added(VALID),
+                ste(g, RECOVERABLE_DEFINED, VALID, false, null)));
+        assertEval("int f;");
+        assertEval("interface A { }");
+        assertKeys(method("()void", "g"), method("()void", "f"), variable("int", "f"),
+                clazz(KullaTesting.ClassType.INTERFACE, "A"));
+        assertActiveKeys();
+        assertEval("double f() { return 0.0; }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(f, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(g, VALID, VALID, false, MAIN_SNIPPET));
+        assertKeys(method("()void", "g"), method("()double", "f"), variable("int", "f"),
+                clazz(KullaTesting.ClassType.INTERFACE, "A"));
+        assertActiveKeys();
+    }
+
+    public void testKeyId3() {
+        Snippet g = methodKey(assertEval("void g() { f(); }", added(RECOVERABLE_DEFINED)));
+        Snippet f = methodKey(assertEval("void f() { }",
+                added(VALID),
+                ste(g, RECOVERABLE_DEFINED, VALID, false, null)));
+        assertDeclareFail("qqqq;", "compiler.err.cant.resolve.location");
+        assertEval("interface A { }");
+        assertKeys(method("()void", "g"), method("()void", "f"),
+                clazz(KullaTesting.ClassType.INTERFACE, "A"));
+        assertActiveKeys();
+        assertEval("double f() { return 0.0; }",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(f, VALID, OVERWRITTEN, false, MAIN_SNIPPET),
+                ste(g, VALID, VALID, false, MAIN_SNIPPET));
+        assertKeys(method("()void", "g"), clazz(KullaTesting.ClassType.INTERFACE, "A"),
+                method("()double", "f"));
+        assertActiveKeys();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/StartOptionTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Testing start-up options.
+ * @library /tools/lib
+ * @build Compiler ToolBox
+ * @run testng StartOptionTest
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.function.Consumer;
+
+import jdk.internal.jshell.tool.JShellTool;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+@Test
+public class StartOptionTest {
+
+    private ByteArrayOutputStream out;
+    private ByteArrayOutputStream err;
+
+    private JShellTool getShellTool() {
+        return new JShellTool(null, new PrintStream(out), new PrintStream(err), null, null, null, null);
+    }
+
+    private String getOutput() {
+        byte[] bytes = out.toByteArray();
+        out.reset();
+        return new String(bytes, StandardCharsets.UTF_8);
+    }
+
+    private String getError() {
+        byte[] bytes = err.toByteArray();
+        err.reset();
+        return new String(bytes, StandardCharsets.UTF_8);
+    }
+
+    private void start(Consumer<String> checkOutput, Consumer<String> checkError, String... args) throws Exception {
+        JShellTool tool = getShellTool();
+        tool.start(args);
+        if (checkOutput != null) {
+            checkOutput.accept(getOutput());
+        } else {
+            assertEquals("", getOutput(), "Output: ");
+        }
+        if (checkError != null) {
+            checkError.accept(getError());
+        } else {
+            assertEquals("", getError(), "Error: ");
+        }
+    }
+
+    private void start(String expectedOutput, String expectedError, String... args) throws Exception {
+        start(s -> assertEquals(s, expectedOutput, "Output: "), s -> assertEquals(s, expectedError, "Error: "), args);
+    }
+
+    @BeforeMethod
+    public void setUp() {
+        out = new ByteArrayOutputStream();
+        err = new ByteArrayOutputStream();
+    }
+
+    @Test
+    public void testUsage() throws Exception {
+        start(s -> {
+            assertTrue(s.split("\n").length >= 7, s);
+            assertTrue(s.startsWith("Usage:   jshell <options>"), s);
+        },  null, "-help");
+    }
+
+    @Test
+    public void testUnknown() throws Exception {
+        start(s -> {
+            assertTrue(s.split("\n").length >= 7, s);
+            assertTrue(s.startsWith("Usage:   jshell <options>"), s);
+        }, s -> assertEquals(s, "Unknown option: -unknown\n"), "-unknown");
+    }
+
+    @Test(enabled = false) // TODO 8080883
+    public void testStartup() throws Exception {
+        Compiler compiler = new Compiler();
+        Path p = compiler.getPath("file.txt");
+        compiler.writeToFile(p);
+        start("", "Argument to -startup missing.\n", "-startup");
+        start("", "Conflicting -startup or -nostartup option.\n", "-startup", p.toString(), "-startup", p.toString());
+        start("", "Conflicting -startup or -nostartup option.\n", "-nostartup", "-startup", p.toString());
+        start("", "Conflicting -startup option.\n", "-startup", p.toString(), "-nostartup");
+    }
+
+    @Test
+    public void testClasspath() throws Exception {
+        for (String cp : new String[] {"-cp", "-classpath"}) {
+            start("", "Conflicting -classpath option.\n", cp, ".", "-classpath", ".");
+            start("", "Argument to -classpath missing.\n", cp);
+        }
+    }
+
+    @Test
+    public void testVersion() throws Exception {
+        start(s -> assertTrue(s.startsWith("jshell")), null, "-version");
+    }
+
+    @AfterMethod
+    public void tearDown() {
+        out = null;
+        err = null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/StopExecutionTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test JShell#stop
+ * @build KullaTesting TestingInputStream
+ * @run testng StopExecutionTest
+ */
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Random;
+
+import jdk.internal.jshell.tool.StopDetectingInputStream;
+import jdk.internal.jshell.tool.StopDetectingInputStream.State;
+import jdk.jshell.JShell;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+@Test
+public class StopExecutionTest extends KullaTesting {
+
+    private final Object lock = new Object();
+    private boolean isStopped;
+
+    @Test(enabled = false) // TODO 8129546
+    public void testStopLoop() throws InterruptedException {
+        scheduleStop("while (true) ;");
+    }
+
+    @Test(enabled = false) // TODO 8129546
+    public void testStopASleep() throws InterruptedException {
+        scheduleStop("while (true) { try { Thread.sleep(100); } catch (InterruptedException ex) { } }");
+    }
+
+    @Test(enabled = false) // TODO 8129546
+    public void testScriptCatchesStop() throws Exception {
+        scheduleStop("for (int i = 0; i < 30; i++) { try { Thread.sleep(100); } catch (Throwable ex) { } }");
+    }
+
+    private void scheduleStop(String src) throws InterruptedException {
+        JShell state = getState();
+        isStopped = false;
+        StringWriter writer = new StringWriter();
+        PrintWriter out = new PrintWriter(writer);
+        Thread t = new Thread(() -> {
+            int i = 1;
+            int n = 30;
+            synchronized (lock) {
+                do {
+                    state.stop();
+                    if (!isStopped) {
+                        out.println("Not stopped. Try again: " + i);
+                        try {
+                            lock.wait(1000);
+                        } catch (InterruptedException ignored) {
+                        }
+                    }
+                } while (i++ < n && !isStopped);
+                if (!isStopped) {
+                    System.err.println(writer.toString());
+                    fail("Evaluation was not stopped: '" + src + "'");
+                }
+            }
+        });
+        t.start();
+        assertEval(src);
+        synchronized (lock) {
+            out.println("Evaluation was stopped successfully: '" + src + "'");
+            isStopped = true;
+            lock.notify();
+        }
+        // wait until the background thread finishes to prevent from calling 'stop' on closed state.
+        t.join();
+    }
+
+    public void testStopDetectingInputRandom() throws IOException {
+        long seed = System.nanoTime();
+        Random r = new Random(seed);
+
+        for (int m = 0; m < 10; m++) {
+            StopDetectingInputStream buffer = new StopDetectingInputStream(null, null);
+
+            buffer.setState(State.BUFFER);
+
+            for (int i = 0; i < 1000; i++) {
+                int chunkSize = r.nextInt(StopDetectingInputStream.INITIAL_SIZE * 3);
+
+                doChunk(buffer, chunkSize);
+            }
+        }
+    }
+
+    private void doChunk(StopDetectingInputStream buffer, int chunkSize) throws IOException {
+        for (int c = 0; c < chunkSize; c++) {
+            buffer.write(c);
+        }
+
+        for (int c = 0; c < chunkSize; c++) {
+            int read = buffer.read();
+
+            assertEquals(read, c);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/TestingInputStream.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+import java.io.ByteArrayInputStream;
+
+class TestingInputStream extends ByteArrayInputStream {
+
+    TestingInputStream() {
+        super(new byte[0]);
+    }
+
+    void setInput(String s) {
+        this.buf = s.getBytes();
+        this.pos = 0;
+        this.count = buf.length;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ToolBasicTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,846 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for Basic tests for REPL tool
+ * @ignore 8139873
+ * @library /tools/lib
+ * @build KullaTesting TestingInputStream ToolBox Compiler
+ * @run testng ToolBasicTest
+ */
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+import java.util.function.Consumer;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+@Test
+public class ToolBasicTest extends ReplToolTesting {
+
+    public void defineVar() {
+        test(
+                (a) -> assertCommand(a, "int x = 72", "|  Added variable x of type int with initial value 72\n"),
+                (a) -> assertCommand(a, "x", "|  Variable x of type int has value 72\n"),
+                (a) -> assertCommand(a, "/vars", "|    int x = 72\n")
+        );
+    }
+
+    public void defineUnresolvedVar() {
+        test(
+                (a) -> assertCommand(a, "undefined x",
+                        "|  Added variable x, however, it cannot be referenced until class undefined is declared\n"),
+                (a) -> assertCommand(a, "/vars", "|    undefined x = (not-active)\n")
+        );
+    }
+
+    public void testUnresolved() {
+        test(
+                (a) -> assertCommand(a, "int f() { return g() + x + new A().a; }",
+                        "|  Added method f(), however, it cannot be invoked until method g(), variable x, and class A are declared\n"),
+                (a) -> assertCommand(a, "f()",
+                        "|  Attempted to call f which cannot be invoked until method g(), variable x, and class A are declared\n"),
+                (a) -> assertCommand(a, "int g() { return x; }",
+                        "|  Added method g(), however, it cannot be invoked until variable x is declared\n"),
+                (a) -> assertCommand(a, "g()", "|  Attempted to call g which cannot be invoked until variable x is declared\n")
+        );
+    }
+
+    public void elideStartUpFromList() {
+        test(
+                (a) -> assertCommandCheckOutput(a, "123", (s) ->
+                        assertTrue(s.contains("type int"), s)),
+                (a) -> assertCommandCheckOutput(a, "/list", (s) -> {
+                    int cnt;
+                    try (Scanner scanner = new Scanner(s)) {
+                        cnt = 0;
+                        while (scanner.hasNextLine()) {
+                            String line = scanner.nextLine();
+                            if (!line.trim().isEmpty()) {
+                                ++cnt;
+                            }
+                        }
+                    }
+                    assertEquals(cnt, 1, "Expected only one listed line");
+                })
+        );
+    }
+
+    public void elideStartUpFromSave() throws IOException {
+        Compiler compiler = new Compiler();
+        Path path = compiler.getPath("myfile");
+        test(
+                (a) -> assertCommandCheckOutput(a, "123",
+                        (s) -> assertTrue(s.contains("type int"), s)),
+                (a) -> assertCommand(a, "/save " + path.toString(), "")
+        );
+        try (Stream<String> lines = Files.lines(path)) {
+            assertEquals(lines.count(), 1, "Expected only one saved line");
+        }
+    }
+
+    public void testInterrupt() {
+        ReplTest interrupt = (a) -> assertCommand(a, "\u0003", "");
+        for (String s : new String[] { "", "\u0003" }) {
+            test(false, new String[]{"-nostartup"},
+                    (a) -> assertCommand(a, "int a = 2 +" + s, ""),
+                    interrupt,
+                    (a) -> assertCommand(a, "int a\u0003", ""),
+                    (a) -> assertCommand(a, "int a = 2 + 2\u0003", ""),
+                    (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                    (a) -> evaluateExpression(a, "int", "2", "2"),
+                    (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                    (a) -> assertCommand(a, "void f() {", ""),
+                    (a) -> assertCommand(a, "int q = 10;" + s, ""),
+                    interrupt,
+                    (a) -> assertCommand(a, "void f() {}\u0003", ""),
+                    (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                    (a) -> assertMethod(a, "int f() { return 0; }", "()int", "f"),
+                    (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                    (a) -> assertCommand(a, "class A {" + s, ""),
+                    interrupt,
+                    (a) -> assertCommand(a, "class A {}\u0003", ""),
+                    (a) -> assertCommandCheckOutput(a, "/c", assertClasses()),
+                    (a) -> assertClass(a, "interface A {}", "interface", "A"),
+                    (a) -> assertCommandCheckOutput(a, "/c", assertClasses())
+            );
+        }
+    }
+
+    private final Object lock = new Object();
+    private PrintWriter out;
+    private boolean isStopped;
+    private Thread t;
+    private void assertStop(boolean after, String cmd, String output) {
+        if (!after) {
+            isStopped = false;
+            StringWriter writer = new StringWriter();
+            out = new PrintWriter(writer);
+            setCommandInput(cmd + "\n");
+            t = new Thread(() -> {
+                try {
+                    // no chance to know whether cmd is being evaluated
+                    Thread.sleep(5000);
+                } catch (InterruptedException ignored) {
+                }
+                int i = 1;
+                int n = 30;
+                synchronized (lock) {
+                    do {
+                        setCommandInput("\u0003");
+                        if (!isStopped) {
+                            out.println("Not stopped. Try again: " + i);
+                            try {
+                                lock.wait(1000);
+                            } catch (InterruptedException ignored) {
+                            }
+                        }
+                    } while (i++ < n && !isStopped);
+                    if (!isStopped) {
+                        System.err.println(writer.toString());
+                        fail("Evaluation was not stopped: '" + cmd + "'");
+                    }
+                }
+            });
+            t.start();
+        } else {
+            synchronized (lock)  {
+                out.println("Evaluation was stopped successfully: '" + cmd + "'");
+                isStopped = true;
+                lock.notify();
+            }
+            try {
+                t.join();
+                t = null;
+            } catch (InterruptedException ignored) {
+            }
+            assertOutput(getCommandOutput(), "", "command");
+            assertOutput(getCommandErrorOutput(), "", "command error");
+            assertOutput(getUserOutput(), output, "user");
+            assertOutput(getUserErrorOutput(), "", "user error");
+        }
+    }
+
+    public void testStop() {
+        test(
+                (a) -> assertStop(a, "while (true) {}", "Killed.\n"),
+                (a) -> assertStop(a, "while (true) { try { Thread.sleep(100); } catch (InterruptedException ex) { } }", "Killed.\n")
+        );
+    }
+
+    @Test(enabled = false) // TODO 8130450
+    public void testRerun() {
+        test(false, new String[] {"-nostartup"},
+                (a) -> assertCommand(a, "/0", "|  Cannot find snippet 0\n"),
+                (a) -> assertCommand(a, "/5", "|  Cannot find snippet 5\n")
+        );
+        String[] codes = new String[] {
+                "int a = 0;", // var
+                "class A {}", // class
+                "void f() {}", // method
+                "bool b;", // active failed
+                "void g() { h(); }", // active corralled
+        };
+        List<ReplTest> tests = new ArrayList<>();
+        for (String s : codes) {
+            tests.add((a) -> assertCommand(a, s, null));
+        }
+        for (int i = 0; i < codes.length; ++i) {
+            final int finalI = i;
+            Consumer<String> check = (s) -> {
+                String[] ss = s.split("\n");
+                assertEquals(ss[0], codes[finalI]);
+                assertTrue(ss.length > 1, s);
+            };
+            tests.add((a) -> assertCommandCheckOutput(a, "/" + (finalI + 1), check));
+        }
+        for (int i = 0; i < codes.length; ++i) {
+            final int finalI = i;
+            Consumer<String> check = (s) -> {
+                String[] ss = s.split("\n");
+                assertEquals(ss[0], codes[codes.length - finalI - 1]);
+                assertTrue(ss.length > 1, s);
+            };
+            tests.add((a) -> assertCommandCheckOutput(a, "/-" + (finalI + 1), check));
+        }
+        tests.add((a) -> assertCommandCheckOutput(a, "/!", assertStartsWith("void g() { h(); }")));
+        test(false, new String[]{"-nostartup"},
+                tests.toArray(new ReplTest[tests.size()]));
+    }
+
+    public void testRemaining() {
+        test(
+                (a) -> assertCommand(a, "int z; z =", "|  Added variable z of type int\n"),
+                (a) -> assertCommand(a, "5", "|  Variable z has been assigned the value 5\n"),
+                (a) -> assertCommand(a, "/*nada*/; int q =", ""),
+                (a) -> assertCommand(a, "77", "|  Added variable q of type int with initial value 77\n"),
+                (a) -> assertCommand(a, "//comment;", ""),
+                (a) -> assertCommand(a, "int v;", "|  Added variable v of type int\n"),
+                (a) -> assertCommand(a, "int v; int c", "|  Added variable c of type int\n")
+        );
+    }
+
+    public void testDebug() {
+        test(
+                (a) -> assertCommand(a, "/db", "|  Debugging on\n"),
+                (a) -> assertCommand(a, "/debug", "|  Debugging off\n"),
+                (a) -> assertCommand(a, "/debug", "|  Debugging on\n"),
+                (a) -> assertCommand(a, "/db", "|  Debugging off\n")
+        );
+    }
+
+    public void testHelp() {
+        Consumer<String> testOutput = (s) -> {
+            List<String> ss = Stream.of(s.split("\n"))
+                    .filter(l -> !l.isEmpty())
+                    .collect(Collectors.toList());
+            assertTrue(ss.size() >= 5, "Help does not print enough lines:\n" + s);
+        };
+        test(
+                (a) -> assertCommandCheckOutput(a, "/?", testOutput),
+                (a) -> assertCommandCheckOutput(a, "/help", testOutput)
+        );
+    }
+
+    public void oneLineOfError() {
+        test(
+                (a) -> assertCommand(a, "12+", null),
+                (a) -> assertCommandCheckOutput(a, "  true", (s) ->
+                        assertTrue(s.contains("12+") && !s.contains("true"), "Output: '" + s + "'"))
+        );
+    }
+
+    public void defineVariables() {
+        test(
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
+                (a) -> assertVariable(a, "int", "a"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
+                (a) -> assertVariable(a, "double", "a", "1", "1.0"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                (a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
+                (a) -> evaluateExpression(a, "double", "2 * a", "2.0"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                (a) -> assertCommandCheckOutput(a, "/vars", assertVariables())
+        );
+    }
+
+    public void defineMethods() {
+        test(
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
+                (a) -> assertMethod(a, "int f() { return 0; }", "()int", "f"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
+                (a) -> assertMethod(a, "void f(int a) { g(); }", "(int)void", "f"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                (a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
+                (a) -> assertMethod(a, "void g() {}", "()void", "g"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                (a) -> assertCommandCheckOutput(a, "/methods", assertMethods())
+        );
+    }
+
+    public void defineClasses() {
+        test(
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/c", assertClasses()),
+                (a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
+                (a) -> assertClass(a, "class A { }", "class", "A"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/c", assertClasses()),
+                (a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
+                (a) -> assertClass(a, "interface A { }", "interface", "A"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/c", assertClasses()),
+                (a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
+                (a) -> assertClass(a, "enum A { }", "enum", "A"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/c", assertClasses()),
+                (a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
+                (a) -> assertClass(a, "@interface A { }", "@interface", "A"),
+                (a) -> assertCommandCheckOutput(a, "/l", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/list", assertList()),
+                (a) -> assertCommandCheckOutput(a, "/c", assertClasses()),
+                (a) -> assertCommandCheckOutput(a, "/classes", assertClasses())
+        );
+    }
+
+    public void testClasspathDirectory() {
+        Compiler compiler = new Compiler();
+        Path outDir = Paths.get("testClasspathDirectory");
+        compiler.compile(outDir, "package pkg; public class A { public String toString() { return \"A\"; } }");
+        Path classpath = compiler.getPath(outDir);
+        test(
+                (a) -> assertCommand(a, "/cp " + classpath, String.format("|  Path %s added to classpath\n", classpath)),
+                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "\"A\"")
+        );
+        test(new String[] { "-cp", classpath.toString() },
+                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "\"A\"")
+        );
+        test(new String[] { "-classpath", classpath.toString() },
+                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "\"A\"")
+        );
+    }
+
+    public void testClasspathJar() {
+        Compiler compiler = new Compiler();
+        Path outDir = Paths.get("testClasspathJar");
+        compiler.compile(outDir, "package pkg; public class A { public String toString() { return \"A\"; } }");
+        String jarName = "test.jar";
+        compiler.jar(outDir, jarName, "pkg/A.class");
+        Path jarPath = compiler.getPath(outDir).resolve(jarName);
+        test(
+                (a) -> assertCommand(a, "/classpath " + jarPath, String.format("|  Path %s added to classpath\n", jarPath)),
+                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "\"A\"")
+        );
+        test(new String[] { "-cp", jarPath.toString() },
+                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "\"A\"")
+        );
+        test(new String[] { "-classpath", jarPath.toString() },
+                (a) -> evaluateExpression(a, "pkg.A", "new pkg.A();", "\"A\"")
+        );
+    }
+
+    public void testStartupFileOption() {
+        try {
+            Compiler compiler = new Compiler();
+            Path startup = compiler.getPath("StartupFileOption/startup.txt");
+            compiler.writeToFile(startup, "class A { public String toString() { return \"A\"; } }");
+            test(new String[]{"-startup", startup.toString()},
+                    (a) -> evaluateExpression(a, "A", "new A()", "\"A\"\n")
+            );
+            test(new String[]{"-nostartup"},
+                    (a) -> assertCommandCheckOutput(a, "printf(\"\")", assertStartsWith("|  Error:\n|  cannot find symbol"))
+            );
+            test((a) -> assertCommand(a, "printf(\"A\")", "", "", null, "A", ""));
+            test(false, new String[]{"-startup", "UNKNOWN"}, "|  File 'UNKNOWN' for start-up is not found.");
+        } finally {
+            removeStartup();
+        }
+    }
+
+    public void testLoadingFromArgs() {
+        Compiler compiler = new Compiler();
+        Path path = compiler.getPath("loading.repl");
+        compiler.writeToFile(path, "int a = 10; double x = 20; double a = 10;");
+        test(new String[] { path.toString() },
+                (a) -> assertCommand(a, "x", "|  Variable x of type double has value 20.0\n"),
+                (a) -> assertCommand(a, "a", "|  Variable a of type double has value 10.0\n")
+        );
+        Path unknown = compiler.getPath("UNKNOWN.jar");
+        test(true, new String[]{unknown.toString()},
+                "|  File '" + unknown
+                + "' is not found: " + unknown
+                + " (No such file or directory)\n");
+    }
+
+    public void testReset() {
+        test(
+                (a) -> assertReset(a, "/r"),
+                (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                (a) -> assertVariable(a, "int", "x"),
+                (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                (a) -> assertMethod(a, "void f() { }", "()void", "f"),
+                (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                (a) -> assertClass(a, "class A { }", "class", "A"),
+                (a) -> assertCommandCheckOutput(a, "/c", assertClasses()),
+                (a) -> assertReset(a, "/reset"),
+                (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                (a) -> assertCommandCheckOutput(a, "/c", assertClasses())
+        );
+    }
+
+    public void testOpen() {
+        Compiler compiler = new Compiler();
+        Path path = compiler.getPath("testOpen.repl");
+        compiler.writeToFile(path,
+                "int a = 10;\ndouble x = 20;\ndouble a = 10;\n" +
+                        "class A { public String toString() { return \"A\"; } }");
+        for (String s : new String[]{"/o", "/open"}) {
+            test(
+                    (a) -> assertCommand(a, s + " " + path.toString(), ""),
+                    (a) -> assertCommand(a, "a", "|  Variable a of type double has value 10.0\n"),
+                    (a) -> evaluateExpression(a, "A", "new A();", "\"A\""),
+                    (a) -> {
+                        loadVariable(a, "double", "x", "20.0", "20.0");
+                        loadVariable(a, "double", "a", "10.0", "10.0");
+                        loadVariable(a, "A", "$6", "new A();", "A");
+                        loadClass(a, "class A { public String toString() { return \"A\"; } }",
+                                "class", "A");
+                        assertCommandCheckOutput(a, "/c", assertClasses());
+                    },
+                    (a) -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                    (a) -> assertCommandCheckOutput(a, "/v", assertVariables())
+            );
+            Path unknown = compiler.getPath("UNKNOWN.repl");
+            test(
+                    (a) -> assertCommand(a, s + " " + unknown,
+                            "|  File '" + unknown
+                                    + "' is not found: " + unknown
+                                    + " (No such file or directory)\n")
+            );
+        }
+    }
+
+    public void testSave() throws IOException {
+        Compiler compiler = new Compiler();
+        Path path = compiler.getPath("testSave.repl");
+        List<String> list = Arrays.asList(
+                "int a;",
+                "class A { public String toString() { return \"A\"; } }"
+        );
+        for (String s : new String[]{"/s", "/save"}) {
+            test(
+                    (a) -> assertVariable(a, "int", "a"),
+                    (a) -> assertClass(a, "class A { public String toString() { return \"A\"; } }", "class", "A"),
+                    (a) -> assertCommand(a, s + " " + path.toString(), "")
+            );
+            assertEquals(Files.readAllLines(path), list);
+        }
+        for (String s : new String[]{"/s", "/save"}) {
+            List<String> output = new ArrayList<>();
+            test(
+                    (a) -> assertCommand(a, "int a;", null),
+                    (a) -> assertClass(a, "class A { public String toString() { return \"A\"; } }", "class", "A"),
+                    (a) -> assertCommandCheckOutput(a, "/list all", (out) ->
+                            output.addAll(Stream.of(out.split("\n"))
+                                    .filter(str -> !str.isEmpty())
+                                    .map(str -> str.substring(str.indexOf(':') + 2))
+                                    .filter(str -> !str.startsWith("/"))
+                                    .collect(Collectors.toList()))),
+                    (a) -> assertCommand(a, s + " all " + path.toString(), "")
+            );
+            assertEquals(Files.readAllLines(path), output);
+        }
+        for (String s : new String[]{"/s", "/save"}) {
+            List<String> output = new ArrayList<>();
+            test(
+                    (a) -> assertVariable(a, "int", "a"),
+                    (a) -> assertClass(a, "class A { public String toString() { return \"A\"; } }", "class", "A"),
+                    (a) -> assertCommandCheckOutput(a, "/h", (out) ->
+                            output.addAll(Stream.of(out.split("\n"))
+                                    .filter(str -> !str.isEmpty())
+                                    .collect(Collectors.toList()))),
+                    (a) -> assertCommand(a, s + " history " + path.toString(), "")
+            );
+            output.add(s + " history " + path.toString());
+            assertEquals(Files.readAllLines(path), output);
+        }
+    }
+
+    public void testStartSet() throws BackingStoreException {
+        try {
+            Compiler compiler = new Compiler();
+            Path startUpFile = compiler.getPath("startUp.txt");
+            test(
+                    (a) -> assertVariable(a, "int", "a"),
+                    (a) -> assertVariable(a, "double", "b", "10", "10.0"),
+                    (a) -> assertMethod(a, "void f() {}", "()V", "f"),
+                    (a) -> assertCommand(a, "/s " + startUpFile.toString(), null),
+                    (a) -> assertCommand(a, "/setstart " + startUpFile.toString(), null)
+            );
+            Path unknown = compiler.getPath("UNKNOWN");
+            test(
+                    (a) -> assertCommand(a, "/setstart " + unknown.toString(),
+                            "|  File '" + unknown + "' for /setstart is not found.\n")
+            );
+            test(false, new String[0],
+                    (a) -> {
+                        loadVariable(a, "int", "a");
+                        loadVariable(a, "double", "b", "10.0", "10.0");
+                        loadMethod(a, "void f() {}", "()void", "f");
+                        assertCommandCheckOutput(a, "/c", assertClasses());
+                    },
+                    (a) -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                    (a) -> assertCommandCheckOutput(a, "/m", assertMethods())
+            );
+        } finally {
+            removeStartup();
+        }
+    }
+
+    private void removeStartup() {
+        Preferences preferences = Preferences.userRoot().node("tool/REPL");
+        if (preferences != null) {
+            preferences.remove("STARTUP");
+        }
+    }
+
+    public void testUnknownCommand() {
+        test((a) -> assertCommand(a, "/unknown",
+                "|  No such command: /unknown\n" +
+                "|  Type /help for help.\n"));
+    }
+
+    public void testEmptyClassPath() {
+        String[] commands = {"/cp", "/classpath"};
+        test(Stream.of(commands)
+                .map(cmd -> (ReplTest) after -> assertCommand(after, cmd, "|  /classpath requires a path argument\n"))
+                .toArray(ReplTest[]::new));
+
+    }
+
+    public void testNoArgument() {
+        String[] commands = {"/s", "/save", "/o", "/open", "/setstart", "/savestart"};
+        test(Stream.of(commands)
+                .map(cmd -> {
+                    String c = cmd;
+                    if ("/s".equals(cmd)) {
+                        c = "/save";
+                    }
+                    if ("/o".equals(cmd)) {
+                        c = "/open";
+                    }
+                    final String finalC = c;
+                    return (ReplTest) after -> assertCommand(after, cmd,
+                            "|  The " + finalC + " command requires a filename argument.\n");
+                })
+                .toArray(ReplTest[]::new));
+    }
+
+    public void testStartSave() throws IOException {
+        Compiler compiler = new Compiler();
+        Path startSave = compiler.getPath("startSave.txt");
+        test(a -> assertCommand(a, "/savestart " + startSave.toString(), null));
+        List<String> lines = Files.lines(startSave)
+                .filter(s -> !s.isEmpty())
+                .collect(Collectors.toList());
+        assertEquals(lines, Arrays.asList(
+                "import java.util.*;",
+                "import java.io.*;",
+                "import java.math.*;",
+                "import java.net.*;",
+                "import java.util.concurrent.*;",
+                "import java.util.prefs.*;",
+                "import java.util.regex.*;",
+                "void printf(String format, Object... args) { System.out.printf(format, args); }"));
+    }
+
+    public void testConstrainedUpdates() {
+        test(
+                a -> assertClass(a, "class XYZZY { }", "class", "XYZZY"),
+                a -> assertVariable(a, "XYZZY", "xyzzy"),
+                a -> assertCommandCheckOutput(a, "import java.util.stream.*",
+                        (out) -> assertTrue(out.trim().isEmpty(), "Expected no output, got: " + out))
+        );
+    }
+
+    public void testRemoteExit() {
+        test(
+                a -> assertVariable(a, "int", "x"),
+                a -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                a -> assertCommandCheckOutput(a, "System.exit(5);",  s ->
+                        assertTrue(s.contains("terminated"), s)),
+                a -> assertCommandCheckOutput(a, "/v", s ->
+                        assertTrue(s.trim().isEmpty(), s)),
+                a -> assertMethod(a, "void f() { }", "()void", "f"),
+                a -> assertCommandCheckOutput(a, "/m", assertMethods())
+        );
+    }
+
+    public void testListArgs() {
+        Consumer<String> assertList = s -> assertTrue(s.split("\n").length >= 7, s);
+        String arg = "qqqq";
+        Consumer<String> assertError = s -> assertEquals(s, "|  Invalid /list argument: " + arg + "\n");
+        test(
+                a -> assertCommandCheckOutput(a, "/l all", assertList),
+                a -> assertCommandCheckOutput(a, "/list all", assertList),
+                a -> assertCommandCheckOutput(a, "/l " + arg, assertError),
+                a -> assertCommandCheckOutput(a, "/list " + arg, assertError),
+                a -> assertVariable(a, "int", "a"),
+                a -> assertCommandCheckOutput(a, "/l history", assertList),
+                a -> assertCommandCheckOutput(a, "/list history", assertList)
+        );
+    }
+
+    public void testFeedbackNegative() {
+        for (String feedback : new String[]{"/f", "/feedback"}) {
+            test(a -> assertCommandCheckOutput(a, feedback + " aaaa",
+                    assertStartsWith("|  Follow /feedback with of the following")));
+        }
+    }
+
+    public void testFeedbackOff() {
+        for (String feedback : new String[]{"/f", "/feedback"}) {
+            for (String off : new String[]{"o", "off"}) {
+                test(
+                        a -> assertCommand(a, feedback + " " + off, ""),
+                        a -> assertCommand(a, "int a", ""),
+                        a -> assertCommand(a, "void f() {}", ""),
+                        a -> assertCommandCheckOutput(a, "aaaa", assertStartsWith("|  Error:")),
+                        a -> assertCommandCheckOutput(a, "public void f() {}", assertStartsWith("|  Warning:"))
+                );
+            }
+        }
+    }
+
+    public void testFeedbackConcise() {
+        Compiler compiler = new Compiler();
+        Path testConciseFile = compiler.getPath("testConciseFeedback");
+        String[] sources = new String[] {"int a", "void f() {}", "class A {}", "a = 10"};
+        compiler.writeToFile(testConciseFile, sources);
+        for (String feedback : new String[]{"/f", "/feedback"}) {
+            for (String concise : new String[]{"c", "concise"}) {
+                test(
+                        a -> assertCommand(a, feedback + " " + concise, ""),
+                        a -> assertCommand(a, sources[0], ""),
+                        a -> assertCommand(a, sources[1], ""),
+                        a -> assertCommand(a, sources[2], ""),
+                        a -> assertCommand(a, sources[3], "|  a : 10\n"),
+                        a -> assertCommand(a, "/o " + testConciseFile.toString(), "|  a : 10\n")
+                );
+            }
+        }
+    }
+
+    public void testFeedbackNormal() {
+        Compiler compiler = new Compiler();
+        Path testNormalFile = compiler.getPath("testConciseNormal");
+        String[] sources = new String[] {"int a", "void f() {}", "class A {}", "a = 10"};
+        String[] sources2 = new String[] {"int a //again", "void f() {int y = 4;}", "class A {} //again", "a = 10"};
+        String[] output = new String[] {
+                "|  Added variable a of type int\n",
+                "|  Added method f()\n",
+                "|  Added class A\n",
+                "|  Variable a has been assigned the value 10\n"
+        };
+        compiler.writeToFile(testNormalFile, sources2);
+        for (String feedback : new String[]{"/f", "/feedback"}) {
+            for (String feedbackState : new String[]{"n", "normal", "v", "verbose"}) {
+                String f = null;
+                if (feedbackState.startsWith("n")) {
+                    f = "normal";
+                } else if (feedbackState.startsWith("v")) {
+                    f = "verbose";
+                }
+                final String finalF = f;
+                test(
+                        a -> assertCommand(a, feedback + " " + feedbackState, "|  Feedback mode: " + finalF +"\n"),
+                        a -> assertCommand(a, sources[0], output[0]),
+                        a -> assertCommand(a, sources[1], output[1]),
+                        a -> assertCommand(a, sources[2], output[2]),
+                        a -> assertCommand(a, sources[3], output[3]),
+                        a -> assertCommand(a, "/o " + testNormalFile.toString(),
+                                "|  Modified variable a of type int\n" +
+                                "|  Modified method f()\n" +
+                                "|    Update overwrote method f()\n" +
+                                "|  Modified class A\n" +
+                                "|    Update overwrote class A\n" +
+                                "|  Variable a has been assigned the value 10\n")
+                );
+            }
+        }
+    }
+
+    public void testFeedbackDefault() {
+        Compiler compiler = new Compiler();
+        Path testDefaultFile = compiler.getPath("testDefaultFeedback");
+        String[] sources = new String[] {"int a", "void f() {}", "class A {}", "a = 10"};
+        String[] output = new String[] {
+                "|  Added variable a of type int\n",
+                "|  Added method f()\n",
+                "|  Added class A\n",
+                "|  Variable a has been assigned the value 10\n"
+        };
+        compiler.writeToFile(testDefaultFile, sources);
+        for (String feedback : new String[]{"/f", "/feedback"}) {
+            for (String defaultFeedback : new String[]{"", "d", "default"}) {
+                test(
+                        a -> assertCommand(a, "/f o", ""),
+                        a -> assertCommand(a, "int x", ""),
+                        a -> assertCommand(a, feedback + " " + defaultFeedback, "|  Feedback mode: default\n"),
+                        a -> assertCommand(a, sources[0], output[0]),
+                        a -> assertCommand(a, sources[1], output[1]),
+                        a -> assertCommand(a, sources[2], output[2]),
+                        a -> assertCommand(a, sources[3], output[3]),
+                        a -> assertCommand(a, "/o " + testDefaultFile.toString(), "")
+                );
+            }
+        }
+    }
+
+    public void testDrop() {
+        for (String drop : new String[]{"/d", "/drop"}) {
+            test(false, new String[]{"-nostartup"},
+                    a -> assertVariable(a, "int", "a"),
+                    a -> dropVariable(a, drop + " 1", "int a = 0"),
+                    a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
+                    a -> dropMethod(a, drop + " 2", "b ()I"),
+                    a -> assertClass(a, "class A {}", "class", "A"),
+                    a -> dropClass(a, drop + " 3", "class A"),
+                    a -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                    a -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                    a -> assertCommandCheckOutput(a, "/c", assertClasses())
+            );
+            test(false, new String[]{"-nostartup"},
+                    a -> assertVariable(a, "int", "a"),
+                    a -> dropVariable(a, drop + " a", "int a = 0"),
+                    a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
+                    a -> dropMethod(a, drop + " b", "b ()I"),
+                    a -> assertClass(a, "class A {}", "class", "A"),
+                    a -> dropClass(a, drop + " A", "class A"),
+                    a -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                    a -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                    a -> assertCommandCheckOutput(a, "/c", assertClasses())
+            );
+        }
+    }
+
+    public void testDropNegative() {
+        for (String drop : new String[]{"/d", "/drop"}) {
+            test(false, new String[]{"-nostartup"},
+                    a -> assertCommand(a, drop + " 0", "|  No definition or id named 0 found.  See /classes /methods /vars or /list\n"),
+                    a -> assertCommand(a, drop + " a", "|  No definition or id named a found.  See /classes /methods /vars or /list\n"),
+                    a -> assertCommandCheckOutput(a, drop,
+                            assertStartsWith("|  In the /drop argument, please specify an import, variable, method, or class to drop.")),
+                    a -> assertVariable(a, "int", "a"),
+                    a -> assertCommand(a, "a", "|  Variable a of type int has value 0\n"),
+                    a -> assertCommand(a, drop + " 2", "|  The argument did not specify an import, variable, method, or class to drop.\n")
+            );
+        }
+    }
+
+    public void testAmbiguousDrop() {
+        Consumer<String> check = s -> {
+            assertTrue(s.startsWith("|  The argument references more than one import, variable, method, or class"), s);
+            int lines = s.split("\n").length;
+            assertEquals(lines, 5, "Expected 3 ambiguous keys, but found: " + (lines - 2) + "\n" + s);
+        };
+        for (String drop : new String[]{"/d", "/drop"}) {
+            test(
+                    a -> assertVariable(a, "int", "a"),
+                    a -> assertMethod(a, "int a() { return 0; }", "()int", "a"),
+                    a -> assertClass(a, "class a {}", "class", "a"),
+                    a -> assertCommandCheckOutput(a, drop + " a", check),
+                    a -> assertCommandCheckOutput(a, "/v", assertVariables()),
+                    a -> assertCommandCheckOutput(a, "/m", assertMethods()),
+                    a -> assertCommandCheckOutput(a, "/c", assertClasses())
+            );
+            test(
+                    a -> assertMethod(a, "int a() { return 0; }", "()int", "a"),
+                    a -> assertMethod(a, "double a(int a) { return 0; }", "(int)double", "a"),
+                    a -> assertMethod(a, "double a(double a) { return 0; }", "(double)double", "a"),
+                    a -> assertCommandCheckOutput(a, drop + " a", check),
+                    a -> assertCommandCheckOutput(a, "/m", assertMethods())
+            );
+        }
+    }
+
+    public void testHistoryReference() {
+        test(false, new String[]{"-nostartup"},
+                a -> assertCommand(a, "System.err.println(1)", "", "", null, "", "1\n"),
+                a -> assertCommand(a, "System.err.println(2)", "", "", null, "", "2\n"),
+                a -> assertCommand(a, "/-2", "System.err.println(1)\n", "", null, "", "1\n"),
+                a -> assertCommand(a, "/history", "\n" +
+                                                    "/debug 0\n" +
+                                                    "System.err.println(1)\n" +
+                                                    "System.err.println(2)\n" +
+                                                    "System.err.println(1)\n" +
+                                                    "/history\n"),
+                a -> assertCommand(a, "/-2", "System.err.println(2)\n", "", null, "", "2\n"),
+                a -> assertCommand(a, "/!", "System.err.println(2)\n", "", null, "", "2\n"),
+                a -> assertCommand(a, "/2", "System.err.println(2)\n", "", null, "", "2\n"),
+                a -> assertCommand(a, "/1", "System.err.println(1)\n", "", null, "", "1\n")
+        );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/TypeNameTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary null test
+ * @build KullaTesting TestingInputStream
+ * @run testng TypeNameTest
+ */
+
+import jdk.jshell.Snippet;
+import jdk.jshell.VarSnippet;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.VALID;
+import static org.testng.Assert.assertEquals;
+import static jdk.jshell.Snippet.Status.OVERWRITTEN;
+
+@Test
+public class TypeNameTest extends KullaTesting {
+
+    public void testReplClassName() {
+        assertEval("class C {}");
+        VarSnippet sn = (VarSnippet) varKey(assertEval("new C();"));
+        assertEquals(sn.typeName(), "C");
+    }
+
+    public void testReplNestedClassName() {
+        assertEval("class D { static class E {} }");
+        VarSnippet sn = (VarSnippet) varKey(assertEval("new D.E();"));
+        assertEquals(sn.typeName(), "D.E");
+    }
+
+    public void testAnonymousClassName() {
+        assertEval("class C {}");
+        VarSnippet sn = (VarSnippet) varKey(assertEval("new C() { int x; };"));
+        assertEquals(sn.typeName(), "C");
+    }
+
+    public void testCapturedTypeName() {
+        VarSnippet sn = (VarSnippet) varKey(assertEval("\"\".getClass();"));
+        assertEquals(sn.typeName(), "Class<? extends String>");
+    }
+
+    public void testJavaLang() {
+        VarSnippet sn = (VarSnippet) varKey(assertEval("\"\";"));
+        assertEquals(sn.typeName(), "String");
+    }
+
+    public void testNotOverEagerPackageEating() {
+        VarSnippet sn = (VarSnippet) varKey(assertEval("\"\".getClass().getDeclaredMethod(\"hashCode\");"));
+        assertEquals(sn.typeName(), "java.lang.reflect.Method");
+    }
+
+    public void testBounds() {
+        assertEval("java.util.List<? extends String> list1 = java.util.Arrays.asList(\"\");");
+        VarSnippet sn1 = (VarSnippet) varKey(assertEval("list1.iterator().next()"));
+        assertEquals(sn1.typeName(), "String");
+        assertEval("java.util.List<? super String> list2 = java.util.Arrays.asList(\"\");");
+        VarSnippet sn2 = (VarSnippet) varKey(assertEval("list2.iterator().next()"));
+        assertEquals(sn2.typeName(), "Object");
+        assertEval("java.util.List<?> list3 = java.util.Arrays.asList(\"\");");
+        VarSnippet sn3 = (VarSnippet) varKey(assertEval("list3.iterator().next()"));
+        assertEquals(sn3.typeName(), "Object");
+        assertEval("class Test1<X extends CharSequence> { public X get() { return null; } }");
+        Snippet x = varKey(assertEval("Test1<?> x = new Test1<>();"));
+        VarSnippet sn4 = (VarSnippet) varKey(assertEval("x.get()"));
+        assertEquals(sn4.typeName(), "CharSequence");
+        assertEval("class Foo<X extends Number & CharSequence> { public X get() { return null; } }");
+        assertEval("Foo<?> x = new Foo<>();",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(x, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        VarSnippet sn5 = (VarSnippet) varKey(assertEval("x.get()"));
+        assertEquals(sn5.typeName(), "Object");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/VariablesTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Tests for EvaluationState.variables
+ * @build KullaTesting TestingInputStream ExpectedDiagnostic
+ * @run testng VariablesTest
+ */
+
+import java.util.List;
+import javax.tools.Diagnostic;
+
+import jdk.jshell.Snippet;
+import jdk.jshell.TypeDeclSnippet;
+import jdk.jshell.VarSnippet;
+import jdk.jshell.Snippet.SubKind;
+import jdk.jshell.SnippetEvent;
+import org.testng.annotations.Test;
+
+import static jdk.jshell.Snippet.Status.*;
+import static jdk.jshell.Snippet.SubKind.VAR_DECLARATION_SUBKIND;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+@Test
+public class VariablesTest extends KullaTesting {
+
+    public void noVariables() {
+        assertNumberOfActiveVariables(0);
+    }
+
+    private void badVarValue(VarSnippet key) {
+        try {
+            getState().varValue(key);
+            fail("Expected exception for: " + key.source());
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testVarValue1() {
+        VarSnippet v1 = varKey(assertEval("und1 a;", added(RECOVERABLE_NOT_DEFINED)));
+        badVarValue(v1);
+        VarSnippet v2 = varKey(assertEval("und2 a;",
+                ste(MAIN_SNIPPET, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, null),
+                ste(v1, RECOVERABLE_NOT_DEFINED, OVERWRITTEN, false, MAIN_SNIPPET)));
+        badVarValue(v2);
+        TypeDeclSnippet und = classKey(assertEval("class und2 {}",
+                added(VALID),
+                ste(v2, RECOVERABLE_NOT_DEFINED, VALID, true, MAIN_SNIPPET)));
+        assertVarValue(v2, "null");
+        assertDrop(und,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(und, VALID, DROPPED, true, null),
+                ste(v2, VALID, RECOVERABLE_NOT_DEFINED, true, und));
+        badVarValue(v1);
+        badVarValue(v2);
+    }
+
+    public void testVarValue2() {
+        VarSnippet v1 = (VarSnippet) assertDeclareFail("int a = 0.0;", "compiler.err.prob.found.req");
+        badVarValue(v1);
+        VarSnippet v2 = varKey(assertEval("int a = 0;", ste(v1, REJECTED, VALID, true, null)));
+        assertDrop(v2, ste(MAIN_SNIPPET, VALID, DROPPED, true, null));
+        badVarValue(v2);
+    }
+
+    public void testSignature1() {
+        VarSnippet v1 = varKey(assertEval("und1 a;", added(RECOVERABLE_NOT_DEFINED)));
+        assertVariableDeclSnippet(v1, "a", "und1", RECOVERABLE_NOT_DEFINED, VAR_DECLARATION_SUBKIND, 1, 0);
+        VarSnippet v2 = varKey(assertEval("und2 a;",
+                ste(MAIN_SNIPPET, RECOVERABLE_NOT_DEFINED, RECOVERABLE_NOT_DEFINED, false, null),
+                ste(v1, RECOVERABLE_NOT_DEFINED, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertVariableDeclSnippet(v2, "a", "und2", RECOVERABLE_NOT_DEFINED, VAR_DECLARATION_SUBKIND, 1, 0);
+        TypeDeclSnippet und = classKey(assertEval("class und2 {}",
+                added(VALID),
+                ste(v2, RECOVERABLE_NOT_DEFINED, VALID, true, MAIN_SNIPPET)));
+        assertVariableDeclSnippet(v2, "a", "und2", VALID, VAR_DECLARATION_SUBKIND, 0, 0);
+        assertDrop(und,
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                ste(und, VALID, DROPPED, true, null),
+                ste(v2, VALID, RECOVERABLE_NOT_DEFINED, true, und));
+        assertVariableDeclSnippet(v2, "a", "und2", RECOVERABLE_NOT_DEFINED, VAR_DECLARATION_SUBKIND, 1, 0);
+    }
+
+    public void testSignature2() {
+        VarSnippet v1 = (VarSnippet) assertDeclareFail("int a = 0.0;", "compiler.err.prob.found.req");
+        assertVariableDeclSnippet(v1, "a", "int", REJECTED, SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND, 0, 1);
+        VarSnippet v2 = varKey(assertEval("int a = 0;",
+                ste(v1, REJECTED, VALID, true, null)));
+        assertVariableDeclSnippet(v2, "a", "int", VALID, SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND, 0, 0);
+        assertDrop(v2, ste(MAIN_SNIPPET, VALID, DROPPED, true, null));
+        assertVariableDeclSnippet(v2, "a", "int", DROPPED, SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND, 0, 0);
+    }
+
+    public void variables() {
+        VarSnippet snx = varKey(assertEval("int x = 10;"));
+        VarSnippet sny = varKey(assertEval("String y = \"hi\";"));
+        VarSnippet snz = varKey(assertEval("long z;"));
+        assertVariables(variable("int", "x"), variable("String", "y"), variable("long", "z"));
+        assertVarValue(snx, "10");
+        assertVarValue(sny, "\"hi\"");
+        assertVarValue(snz, "0");
+        assertActiveKeys();
+    }
+
+    public void variablesArray() {
+        VarSnippet sn = varKey(assertEval("int[] a = new int[12];"));
+        assertEquals(sn.typeName(), "int[]");
+        assertEval("int len = a.length;", "12");
+        assertVariables(variable("int[]", "a"), variable("int", "len"));
+        assertActiveKeys();
+    }
+
+    public void variablesArrayOld() {
+        VarSnippet sn = varKey(assertEval("int a[] = new int[12];"));
+        assertEquals(sn.typeName(), "int[]");
+        assertEval("int len = a.length;", "12");
+        assertVariables(variable("int[]", "a"), variable("int", "len"));
+        assertActiveKeys();
+    }
+
+    public void variablesRedefinition() {
+        Snippet x = varKey(assertEval("int x = 10;"));
+        Snippet y = varKey(assertEval("String y = \"\";", added(VALID)));
+        assertVariables(variable("int", "x"), variable("String", "y"));
+        assertActiveKeys();
+        assertEval("long x;",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(x, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertVariables(variable("long", "x"), variable("String", "y"));
+        assertActiveKeys();
+        assertEval("String y;",
+                ste(MAIN_SNIPPET, VALID, VALID, false, null),
+                ste(y, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertVariables(variable("long", "x"), variable("String", "y"));
+        assertActiveKeys();
+    }
+
+    public void variablesTemporary() {
+        assertEval("int $1 = 10;", added(VALID));
+        assertEval("2 * $1;", added(VALID));
+        assertVariables(variable("int", "$1"), variable("int", "$2"));
+        assertActiveKeys();
+        assertEval("String y;", added(VALID));
+        assertVariables(variable("int", "$1"), variable("int", "$2"), variable("String", "y"));
+        assertActiveKeys();
+    }
+
+    public void variablesTemporaryNull() {
+        assertEval("null;", added(VALID));
+        assertVariables(variable("Object", "$1"));
+        assertEval("(String) null;", added(VALID));
+        assertVariables(variable("Object", "$1"), variable("String", "$2"));
+        assertActiveKeys();
+        assertEval("\"\";", added(VALID));
+        assertVariables(
+                variable("Object", "$1"),
+                variable("String", "$2"),
+                variable("String", "$3"));
+        assertActiveKeys();
+    }
+
+    public void variablesClassReplace() {
+        assertEval("import java.util.*;", added(VALID));
+        Snippet var = varKey(assertEval("List<Integer> list = new ArrayList<>();", "[]",
+                added(VALID)));
+        assertVariables(variable("List<Integer>", "list"));
+        assertEval("class List {}",
+                DiagCheck.DIAG_OK,
+                DiagCheck.DIAG_ERROR,
+                added(VALID),
+                ste(var, VALID, RECOVERABLE_NOT_DEFINED, true, MAIN_SNIPPET));
+        assertVariables();
+        assertEval("List list = new List();",
+                DiagCheck.DIAG_OK, DiagCheck.DIAG_IGNORE,
+                ste(MAIN_SNIPPET, RECOVERABLE_NOT_DEFINED, VALID, true, null),
+                ste(var, RECOVERABLE_NOT_DEFINED, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertVariables(variable("List", "list"));
+        assertActiveKeys();
+    }
+
+    public void variablesErrors() {
+        assertDeclareFail("String;", new ExpectedDiagnostic("compiler.err.cant.resolve.location", 0, 6, 0, -1, -1, Diagnostic.Kind.ERROR));
+        assertNumberOfActiveVariables(0);
+        assertActiveKeys();
+    }
+
+    public void variablesUnresolvedActiveFailed() {
+        VarSnippet key = varKey(assertEval("und x;", added(RECOVERABLE_NOT_DEFINED)));
+        assertVariableDeclSnippet(key, "x", "und", RECOVERABLE_NOT_DEFINED, VAR_DECLARATION_SUBKIND, 1, 0);
+        assertUnresolvedDependencies1(key, RECOVERABLE_NOT_DEFINED, "class und");
+        assertNumberOfActiveVariables(1);
+        assertActiveKeys();
+    }
+
+    public void variablesUnresolvedError() {
+        assertDeclareFail("und y = null;", new ExpectedDiagnostic("compiler.err.cant.resolve.location", 0, 3, 0, -1, -1, Diagnostic.Kind.ERROR));
+        assertNumberOfActiveVariables(0);
+        assertActiveKeys();
+    }
+
+    public void variablesMultiByteCharacterType() {
+        assertEval("class \u3042 {}");
+        assertEval("\u3042 \u3042 = null;", added(VALID));
+        assertVariables(variable("\u3042", "\u3042"));
+        assertEval("new \u3042()", added(VALID));
+        assertVariables(variable("\u3042", "\u3042"), variable("\u3042", "$1"));
+
+        assertEval("class \u3042\u3044\u3046\u3048\u304a {}");
+        assertEval("\u3042\u3044\u3046\u3048\u304a \u3042\u3044\u3046\u3048\u304a = null;", added(VALID));
+        assertVariables(variable("\u3042", "\u3042"), variable("\u3042", "$1"),
+                variable("\u3042\u3044\u3046\u3048\u304a", "\u3042\u3044\u3046\u3048\u304a"));
+        assertEval("new \u3042\u3044\u3046\u3048\u304a();");
+        assertVariables(variable("\u3042", "\u3042"), variable("\u3042", "$1"),
+                variable("\u3042\u3044\u3046\u3048\u304a", "\u3042\u3044\u3046\u3048\u304a"),
+                variable("\u3042\u3044\u3046\u3048\u304a", "$2"));
+        assertActiveKeys();
+    }
+
+    @Test(enabled = false) // TODO 8081689
+    public void methodVariablesAreNotVisible() {
+        Snippet foo = varKey(assertEval("int foo() {" +
+                        "int x = 10;" +
+                        "int y = 2 * x;" +
+                        "return x * y;" +
+                        "}", added(VALID)));
+        assertNumberOfActiveVariables(0);
+        assertActiveKeys();
+        assertEval("int x = 10;", "10");
+        assertEval("int foo() {" +
+                        "int y = 2 * x;" +
+                        "return x * y;" +
+                        "}",
+                ste(foo, VALID, VALID, false, null));
+        assertVariables(variable("int", "x"));
+        assertActiveKeys();
+        assertEval("foo();", "200");
+        assertVariables(variable("int", "x"), variable("int", "$1"));
+        assertActiveKeys();
+    }
+
+    @Test(enabled = false) // TODO 8081689
+    public void classFieldsAreNotVisible() {
+        Snippet key = classKey(assertEval("class clazz {" +
+                        "int x = 10;" +
+                        "int y = 2 * x;" +
+                        "}"));
+        assertNumberOfActiveVariables(0);
+        assertEval("int x = 10;", "10");
+        assertActiveKeys();
+        assertEval(
+                "class clazz {" +
+                        "int y = 2 * x;" +
+                        "}",
+                ste(key, VALID, VALID, true, null));
+        assertVariables(variable("int", "x"));
+        assertEval("new clazz().y;", "20");
+        assertVariables(variable("int", "x"), variable("int", "$1"));
+        assertActiveKeys();
+    }
+
+    public void multiVariables() {
+        List<SnippetEvent> abc = assertEval("int a, b, c = 10;",
+                DiagCheck.DIAG_OK, DiagCheck.DIAG_OK,
+                chain(added(VALID)),
+                chain(added(VALID)),
+                chain(added(VALID)));
+        Snippet a = abc.get(0).snippet();
+        Snippet b = abc.get(1).snippet();
+        Snippet c = abc.get(2).snippet();
+        assertVariables(variable("int", "a"), variable("int", "b"), variable("int", "c"));
+        assertEval("double a = 1.4, b = 8.8;", DiagCheck.DIAG_OK, DiagCheck.DIAG_OK,
+                chain(ste(MAIN_SNIPPET, VALID, VALID, true, null), ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET)),
+                chain(ste(MAIN_SNIPPET, VALID, VALID, true, null), ste(b, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        assertVariables(variable("double", "a"), variable("double", "b"), variable("int", "c"));
+        assertEval("double c = a + b;",
+                ste(MAIN_SNIPPET, VALID, VALID, true, null),
+                ste(c, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+        assertVariables(variable("double", "a"), variable("double", "b"), variable("double", "c"));
+        assertActiveKeys();
+    }
+
+    public void syntheticVariables() {
+        assertEval("assert false;");
+        assertNumberOfActiveVariables(0);
+        assertActiveKeys();
+    }
+
+    public void undefinedReplaceVariable() {
+        Snippet key = varKey(assertEval("int d = 234;", "234"));
+        assertVariables(variable("int", "d"));
+        String src = "undefined d;";
+        Snippet undefKey = varKey(assertEval(src,
+                ste(MAIN_SNIPPET, VALID, RECOVERABLE_NOT_DEFINED, true, null),
+                ste(key, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
+        //assertEquals(getState().source(snippet), src);
+        //assertEquals(snippet, undefKey);
+        assertEquals(getState().status(undefKey), RECOVERABLE_NOT_DEFINED);
+        List<String> unr = getState().unresolvedDependencies((VarSnippet) undefKey);
+        assertEquals(unr.size(), 1);
+        assertEquals(unr.get(0), "class undefined");
+        assertVariables(variable("undefined", "d"));
+    }
+
+    public void variableTypeName() {
+        assertEquals(varKey(assertEval("\"x\"")).typeName(), "String");
+
+        assertEquals(varKey(assertEval("java.util.regex.Pattern.compile(\"x\")")).typeName(), "java.util.regex.Pattern");
+        assertEval("import java.util.regex.*;", added(VALID));
+        assertEquals(varKey(assertEval("java.util.regex.Pattern.compile(\"x\")")).typeName(), "Pattern");
+
+        assertEquals(varKey(assertEval("new java.util.ArrayList()")).typeName(), "java.util.ArrayList");
+        assertEval("import java.util.ArrayList;", added(VALID));
+        assertEquals(varKey(assertEval("new java.util.ArrayList()")).typeName(), "ArrayList");
+
+        assertEquals(varKey(assertEval("java.util.Locale.Category.FORMAT")).typeName(), "java.util.Locale.Category");
+        assertEval("import static java.util.Locale.Category;", added(VALID));
+        assertEquals(varKey(assertEval("java.util.Locale.Category.FORMAT")).typeName(), "Category");
+    }
+}
--- a/langtools/test/tools/javac/MethodParameters/ClassFileVisitor.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/MethodParameters/ClassFileVisitor.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -147,7 +147,6 @@
         public int mAttrs;
         public int mNumParams;
         public boolean mSynthetic;
-        public boolean mIsLambda;
         public boolean mIsConstructor;
         public boolean mIsClinit;
         public boolean mIsBridge;
@@ -166,7 +165,6 @@
             mIsClinit = mName.equals("<clinit>");
             prefix = cname + "." + mName + "() - ";
             mIsBridge = method.access_flags.is(AccessFlags.ACC_BRIDGE);
-            mIsLambda = mSynthetic && mName.startsWith("lambda$");
 
             if (mIsClinit) {
                 sb = new StringBuilder(); // Discard output
@@ -227,7 +225,7 @@
 
             // IMPL: Whether MethodParameters attributes will be generated
             // for some synthetics is unresolved. For now, assume no.
-            if (mSynthetic && !mIsLambda) {
+            if (mSynthetic) {
                 warn(prefix + "synthetic has MethodParameter attribute");
             }
 
@@ -351,12 +349,10 @@
             } else if (isEnum && mNumParams == 1 && index == 0 && mName.equals("valueOf")) {
                 expect = "name";
                 allowMandated = true;
-            } else if (mIsBridge || mIsLambda) {
+            } else if (mIsBridge) {
                 allowSynthetic = true;
                 /*  you can't expect an special name for bridges' parameters.
-                 *  The name of the original parameters are now copied. Likewise
-                 *  for a method encoding the lambda expression, names are derived
-                 *  from source lambda's parameters and captured enclosing locals.
+                 *  The name of the original parameters are now copied.
                  */
                 expect = null;
             }
--- a/langtools/test/tools/javac/MethodParameters/LambdaTest.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/MethodParameters/LambdaTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8006582 8037546
+ * @bug 8006582 8037546 8138729
  * @summary javac should generate method parameters correctly.
  * @modules jdk.jdeps/com.sun.tools.classfile
  * @build Tester
@@ -32,8 +32,8 @@
  */
 
 /**
- * Post https://bugs.openjdk.java.net/browse/JDK-8037546, this test verifies
- * that MethodParameters attribute for lambdas are emitted properly.
+ * Post https://bugs.openjdk.java.net/browse/JDK-8138729, this test verifies
+ * that MethodParameters attribute are NOT emitted for lambdas.
  */
 class LambdaTest {
 
--- a/langtools/test/tools/javac/MethodParameters/LambdaTest.out	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/MethodParameters/LambdaTest.out	Wed Oct 21 18:40:01 2015 -0700
@@ -1,7 +1,7 @@
 class LambdaTest -- 
 LambdaTest.<init>()
 LambdaTest.foo(i)
-LambdaTest.lambda$static$1(x1/*synthetic*/)/*synthetic*/
-LambdaTest.lambda$null$0(final x1/*synthetic*/, x2/*synthetic*/)/*synthetic*/
+LambdaTest.lambda$static$1(arg0)/*synthetic*/
+LambdaTest.lambda$null$0(arg0, arg1)/*synthetic*/
 static interface LambdaTest$I -- inner
 LambdaTest$I.m(x)
--- a/langtools/test/tools/javac/MethodParameters/ReflectionVisitor.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/MethodParameters/ReflectionVisitor.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -277,7 +277,7 @@
                     param = "final " + param;
                 }
                 sb.append(sep).append(param);
-                if (!m.isBridge() && !m.getName().startsWith("lambda$") && !expect.equals(param)) {
+                if (!m.isBridge() && !expect.equals(param)) {
                     error(prefix + "param[" + i + "]='"
                           + param + "' expected '" + expect + "'");
                     break;
--- a/langtools/test/tools/javac/doctree/DocCommentTester.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/doctree/DocCommentTester.java	Wed Oct 21 18:40:01 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -258,7 +258,6 @@
                     } catch (IOException e) {
                         source = "";
                     }
-
                     // remove existing gold by removing all block comments after the first '{'.
                     int start = source.indexOf("{");
                     while ((start = source.indexOf("\n/*\n", start)) != -1) {
@@ -663,8 +662,6 @@
                 else
                     return s;
             }
-
-
         }
     }
 
@@ -763,17 +760,15 @@
          * Normalize white space in places where the tree does not preserve it.
          */
         String normalize(String s) {
-            return s.trim()
-                    .replaceFirst("\\.\\s++([^@])", ". $1")
+            s = s.trim()
                     .replaceFirst("\\.\\s*\\n *@", ".\n@")
-                    .replaceFirst("\\s+<(/?p|pre|h[1-6])>", " <$1>")
                     .replaceAll("\\{@docRoot\\s+\\}", "{@docRoot}")
                     .replaceAll("\\{@inheritDoc\\s+\\}", "{@inheritDoc}")
                     .replaceAll("(\\{@value\\s+[^}]+)\\s+(\\})", "$1$2")
-                    .replaceAll("\n[ \t]+@", "\n@");
+                    .replaceAll("\n[ \t]+@", "\n@")
+                    .replaceAll("(\\{@code)(\\x20)(\\s+.*)", "$1$3");
+            return s;
         }
-
     }
-
 }
 
--- a/langtools/test/tools/javac/doctree/ElementTest.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/doctree/ElementTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 7021614
+ * @bug 7021614 8078320
  * @summary extend com.sun.source API to support parsing javadoc comments
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.file
@@ -40,13 +40,13 @@
     void simple() { }
 /*
 DocComment[DOC_COMMENT, pos:1
-  firstSentence: empty
-  body: 3
+  firstSentence: 2
     StartElement[START_ELEMENT, pos:1
       name:p
       attributes: empty
     ]
     Text[TEXT, pos:4, para]
+  body: 1
     EndElement[END_ELEMENT, pos:8, p]
   block tags: empty
 ]
--- a/langtools/test/tools/javac/doctree/FirstSentenceTest.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/doctree/FirstSentenceTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 7021614
+ * @bug 7021614 8078320
  * @summary extend com.sun.source API to support parsing javadoc comments
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.file
@@ -117,6 +117,26 @@
 */
 
     /**
+     *
+     * <p>abc def ghi.
+     * jdl mno pqf
+     */
+    void newline_p() { }
+/*
+DocComment[DOC_COMMENT, pos:2
+  firstSentence: 2
+    StartElement[START_ELEMENT, pos:2
+      name:p
+      attributes: empty
+    ]
+    Text[TEXT, pos:5, abc_def_ghi.]
+  body: 1
+    Text[TEXT, pos:19, jdl_mno_pqf]
+  block tags: empty
+]
+*/
+
+    /**
      * abc def ghi
      * </p>jkl mno pqr
      */
@@ -197,6 +217,40 @@
     ]
 ]
 */
-
+    /**
+     * <p> abc def.
+     * ghi jkl
+     */
+    void p_at_zero() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 2
+    StartElement[START_ELEMENT, pos:1
+      name:p
+      attributes: empty
+    ]
+    Text[TEXT, pos:4, _abc_def.]
+  body: 1
+    Text[TEXT, pos:15, ghi_jkl]
+  block tags: empty
+]
+*/
+    /**
+     * abc <p> def. ghi jkl
+     */
+    void p_at_nonzero() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 1
+    Text[TEXT, pos:1, abc]
+  body: 2
+    StartElement[START_ELEMENT, pos:5
+      name:p
+      attributes: empty
+    ]
+    Text[TEXT, pos:8, _def._ghi_jkl]
+  block tags: empty
+]
+*/
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/doctree/InPreTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8078320
+ * @summary extend com.sun.source API to support parsing javadoc comments
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.file
+ *          jdk.compiler/com.sun.tools.javac.tree
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @build DocCommentTester
+ * @run main DocCommentTester InPreTest.java
+ */
+
+class InPreTest {
+    /**
+     * xyz<pre> pqr </pre> abc{@code  def  }ghi
+     */
+    public void after_pre() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 1
+    Text[TEXT, pos:1, xyz]
+  body: 6
+    StartElement[START_ELEMENT, pos:4
+      name:pre
+      attributes: empty
+    ]
+    Text[TEXT, pos:9, _pqr_]
+    EndElement[END_ELEMENT, pos:14, pre]
+    Text[TEXT, pos:20, _abc]
+    Literal[CODE, pos:24, _def__]
+    Text[TEXT, pos:38, ghi]
+  block tags: empty
+]
+*/
+    /**
+     * abc{@code def}ghi
+     */
+    public void no_pre() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 3
+    Text[TEXT, pos:1, abc]
+    Literal[CODE, pos:4, def]
+    Text[TEXT, pos:15, ghi]
+  body: empty
+  block tags: empty
+]
+*/
+    /**
+     * xyz<pre> abc{@code  def  }ghi</pre>
+     */
+    public void pre_after_text() {}
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 1
+    Text[TEXT, pos:1, xyz]
+  body: 5
+    StartElement[START_ELEMENT, pos:4
+      name:pre
+      attributes: empty
+    ]
+    Text[TEXT, pos:9, _abc]
+    Literal[CODE, pos:13, _def__]
+    Text[TEXT, pos:27, ghi]
+    EndElement[END_ELEMENT, pos:30, pre]
+  block tags: empty
+]
+*/
+
+    /**
+     * abc{@code  def  }ghi
+     */
+    public void no_pre_extra_whitespace() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 3
+    Text[TEXT, pos:1, abc]
+    Literal[CODE, pos:4, _def__]
+    Text[TEXT, pos:18, ghi]
+  body: empty
+  block tags: empty
+]
+*/
+    /**
+     * <pre> abc{@code  def  }ghi</pre>
+     */
+    public void in_pre() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 4
+    StartElement[START_ELEMENT, pos:1
+      name:pre
+      attributes: empty
+    ]
+    Text[TEXT, pos:6, _abc]
+    Literal[CODE, pos:10, _def__]
+    Text[TEXT, pos:24, ghi]
+  body: 1
+    EndElement[END_ELEMENT, pos:27, pre]
+  block tags: empty
+]
+*/
+    /**
+     * <pre> abc{@code
+     * def  }ghi</pre>
+     */
+    public void in_pre_with_space_nl() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 4
+    StartElement[START_ELEMENT, pos:1
+      name:pre
+      attributes: empty
+    ]
+    Text[TEXT, pos:6, _abc]
+    Literal[CODE, pos:10, |_def__]
+    Text[TEXT, pos:24, ghi]
+  body: 1
+    EndElement[END_ELEMENT, pos:27, pre]
+  block tags: empty
+]
+*/
+
+    /**
+     * <pre> abc{@code
+     *def  }ghi</pre>
+     */
+    public void in_pre_with_nl() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 4
+    StartElement[START_ELEMENT, pos:1
+      name:pre
+      attributes: empty
+    ]
+    Text[TEXT, pos:6, _abc]
+    Literal[CODE, pos:10, |def__]
+    Text[TEXT, pos:23, ghi]
+  body: 1
+    EndElement[END_ELEMENT, pos:26, pre]
+  block tags: empty
+]
+*/
+    /**
+     * abc {@code
+     */
+    public void bad_code_no_content() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 2
+    Text[TEXT, pos:1, abc_]
+    Erroneous[ERRONEOUS, pos:5
+      code: compiler.err.dc.unterminated.inline.tag
+      body: {@code
+    ]
+  body: empty
+  block tags: empty
+]
+*/
+    /**
+     * abc {@code abc
+     */
+    public void bad_code_content() { }
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: 2
+    Text[TEXT, pos:1, abc_]
+    Erroneous[ERRONEOUS, pos:5
+      code: compiler.err.dc.unterminated.inline.tag
+      body: {@code_abc
+    ]
+  body: empty
+  block tags: empty
+]
+*/
+}
--- a/langtools/test/tools/javac/doctree/TagTest.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/doctree/TagTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 7021614
+ * @bug 7021614 8078320
  * @summary extend com.sun.source API to support parsing javadoc comments
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.file
@@ -35,6 +35,40 @@
 
 class TagTest {
     /**
+     * @tag:colon abc
+     */
+    void custom_tag_with_a_colon() {}
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: empty
+  body: empty
+  block tags: 1
+    UnknownBlockTag[UNKNOWN_BLOCK_TAG, pos:1
+      tag:tag:colon
+      content: 1
+        Text[TEXT, pos:12, abc]
+    ]
+]
+*/
+
+    /**
+     * @tag-hyphen abc
+     */
+    void custom_tag_with_a_hyphen() {}
+/*
+DocComment[DOC_COMMENT, pos:1
+  firstSentence: empty
+  body: empty
+  block tags: 1
+    UnknownBlockTag[UNKNOWN_BLOCK_TAG, pos:1
+      tag:tag-hyphen
+      content: 1
+        Text[TEXT, pos:13, abc]
+    ]
+]
+*/
+
+    /**
      * @author jjg
      */
     void simple_standard_block() { }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/MethodReference74.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8139836
+ * @summary Can't use super::x method reference when x is protected
+ * @run main MethodReference74
+ */
+
+public class MethodReference74 extends pkg.Parent {
+
+    public void protectedMethod() {
+        log = "In child, calling ... ";
+        Runnable r = super::protectedMethod;
+        r.run();
+        run(super::protectedMethod); // test also in invocation context.
+    }
+
+    private void run(Runnable r) {
+        r.run();
+    }
+
+    public static void main(String[] args) {
+        MethodReference74 child = new MethodReference74();
+        child.protectedMethod();
+        if (!child.log.equals("In child, calling ...  parent's  parent's ")) {
+            throw new AssertionError("Unexpected output" + child.log);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/pkg/Parent.java	Wed Oct 21 18:40:01 2015 -0700
@@ -0,0 +1,33 @@
+/*
+ * 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 pkg;
+
+public class Parent {
+
+    public String log;
+
+    protected void protectedMethod() {
+        log += " parent's ";
+    }
+}
--- a/langtools/test/tools/javac/parser/StringFoldingTest.java	Wed Jul 05 20:54:58 2017 +0200
+++ b/langtools/test/tools/javac/parser/StringFoldingTest.java	Wed Oct 21 18:40:01 2015 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 7068902
+ * @bug 7068902 8139751
  * @summary verify that string folding can be enabled or disabled
  * @modules jdk.compiler
  */
@@ -86,11 +86,14 @@
 
         if (disableStringFolding) {
             if (text.contains("FOLDED")) {
-                throw new AssertionError("Expected string folding");
+                throw new AssertionError("Expected no string folding");
+            }
+            if (!text.contains("\"F\"")) {
+                throw new AssertionError("Expected content not found");
             }
         } else {
             if (!text.contains("FOLDED")) {
-                throw new AssertionError("Expected no string folding");
+                throw new AssertionError("Expected string folding");
             }
         }
     }