Automated merge with http://hg.netbeans.org/main/nb-javac jshell_support
authorSvata Dedic <sdedic@netbeans.org>
Fri, 02 Sep 2016 23:54:26 +0200
branchjshell_support
changeset 486022c88db1487b
parent 4847 770db925a587
parent 4859 4468eab96171
Automated merge with http://hg.netbeans.org/main/nb-javac
src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteResolutionException.java
src/jdk.jshell/share/classes/jdk/internal/jshell/tool/EditingHistory.java
src/jdk.jshell/share/classes/jdk/jshell/ClassTracker.java
src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java
src/jdk.jshell/share/classes/jdk/jshell/JDIConnection.java
src/jdk.jshell/share/classes/jdk/jshell/JDIEnv.java
src/jdk.jshell/share/classes/jdk/jshell/JDIEventHandler.java
src/jdk.jshell/share/classes/jdk/jshell/JDINotConnectedException.java
     1.1 --- a/make/netbeans/jshell/nbproject/project.properties	Fri Jun 03 17:06:38 2016 +0200
     1.2 +++ b/make/netbeans/jshell/nbproject/project.properties	Fri Sep 02 23:54:26 2016 +0200
     1.3 @@ -28,7 +28,7 @@
     1.4  dist.jar=${dist.dir}/jshell.jar
     1.5  dist.javadoc.dir=${dist.dir}/javadoc
     1.6  endorsed.classpath=
     1.7 -excludes=jdk/internal/jshell/tool/**
     1.8 +excludes=jdk/internal/jshell/tool/**,**/module-info.java
     1.9  file.reference.javac-api.jar=../nb-javac/dist/javac-api.jar
    1.10  file.reference.javac-impl.jar=../nb-javac/dist/javac-impl.jar
    1.11  file.reference.share-classes=../../../src/jdk.jshell/share/classes
     2.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/debug/InternalDebugControl.java	Fri Jun 03 17:06:38 2016 +0200
     2.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/debug/InternalDebugControl.java	Fri Sep 02 23:54:26 2016 +0200
     2.3 @@ -23,24 +23,60 @@
     2.4  
     2.5  package jdk.internal.jshell.debug;
     2.6  
     2.7 +import java.io.PrintStream;
     2.8  import java.util.HashMap;
     2.9  import java.util.Map;
    2.10  import jdk.jshell.JShell;
    2.11  
    2.12  /**
    2.13 - * Used to externally control output messages for debugging the implementation
    2.14 - * of the JShell API.  This is NOT a supported interface,
    2.15 - * @author Robert Field
    2.16 +/**
    2.17 + * This class is used to externally control output messages for debugging the
    2.18 + * implementation of the JShell API.
    2.19 + * <p>
    2.20 + * This is not part of the SPI, not API.
    2.21   */
    2.22  public class InternalDebugControl {
    2.23 -    public static final int DBG_GEN   = 0b0000001;
    2.24 -    public static final int DBG_FMGR  = 0b0000010;
    2.25 +
    2.26 +    /**
    2.27 +     * This is a static only class; The constructor should never be called.
    2.28 +     */
    2.29 +    private InternalDebugControl() {
    2.30 +    }
    2.31 +
    2.32 +    /**
    2.33 +     * General debugging.
    2.34 +     */
    2.35 +    public static final int DBG_GEN = 0b0000001;
    2.36 +
    2.37 +    /**
    2.38 +     * File manager debuging.
    2.39 +     */
    2.40 +    public static final int DBG_FMGR = 0b0000010;
    2.41 +
    2.42 +    /**
    2.43 +     * Completion analysis debugging.
    2.44 +     */
    2.45      public static final int DBG_COMPA = 0b0000100;
    2.46 -    public static final int DBG_DEP   = 0b0001000;
    2.47 -    public static final int DBG_EVNT  = 0b0010000;
    2.48 +
    2.49 +    /**
    2.50 +     * Dependency debugging.
    2.51 +     */
    2.52 +    public static final int DBG_DEP = 0b0001000;
    2.53 +
    2.54 +    /**
    2.55 +     * Event debugging.
    2.56 +     */
    2.57 +    public static final int DBG_EVNT = 0b0010000;
    2.58  
    2.59      private static Map<JShell, Integer> debugMap = null;
    2.60  
    2.61 +    /**
    2.62 +     * Sets which debug flags are enabled for a given JShell instance. The flags
    2.63 +     * are or'ed bits as defined in {@code DBG_*}.
    2.64 +     *
    2.65 +     * @param state the JShell instance
    2.66 +     * @param flags the or'ed debug bits
    2.67 +     */
    2.68      public static void setDebugFlags(JShell state, int flags) {
    2.69          if (debugMap == null) {
    2.70              debugMap = new HashMap<>();
    2.71 @@ -48,7 +84,14 @@
    2.72          debugMap.put(state, flags);
    2.73      }
    2.74  
    2.75 -    public static boolean debugEnabled(JShell state, int flag) {
    2.76 +    /**
    2.77 +     * Tests if any of the specified debug flags are enabled.
    2.78 +     *
    2.79 +     * @param state the JShell instance
    2.80 +     * @param flag the {@code DBG_*} bits to check
    2.81 +     * @return true if any of the flags are enabled
    2.82 +     */
    2.83 +    public static boolean isDebugEnabled(JShell state, int flag) {
    2.84          if (debugMap == null) {
    2.85              return false;
    2.86          }
    2.87 @@ -58,4 +101,34 @@
    2.88          }
    2.89          return (flags & flag) != 0;
    2.90      }
    2.91 +
    2.92 +    /**
    2.93 +     * Displays debug info if the specified debug flags are enabled.
    2.94 +     *
    2.95 +     * @param state the current JShell instance
    2.96 +     * @param err the {@code PrintStream} to report on
    2.97 +     * @param flags {@code DBG_*} flag bits to check
    2.98 +     * @param format format string for the output
    2.99 +     * @param args args for the format string
   2.100 +     */
   2.101 +    public static void debug(JShell state, PrintStream err, int flags, String format, Object... args) {
   2.102 +        if (isDebugEnabled(state, flags)) {
   2.103 +            err.printf(format, args);
   2.104 +        }
   2.105 +    }
   2.106 +
   2.107 +    /**
   2.108 +     * Displays a fatal exception as debug info.
   2.109 +     *
   2.110 +     * @param state the current JShell instance
   2.111 +     * @param err the {@code PrintStream} to report on
   2.112 +     * @param ex the fatal Exception
   2.113 +     * @param where additional context
   2.114 +     */
   2.115 +    public static void debug(JShell state, PrintStream err, Exception ex, String where) {
   2.116 +        if (isDebugEnabled(state, 0xFFFFFFFF)) {
   2.117 +            err.printf("Fatal error: %s: %s\n", where, ex.getMessage());
   2.118 +            ex.printStackTrace(err);
   2.119 +        }
   2.120 +    }
   2.121  }
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/ClassTracker.java	Fri Sep 02 23:54:26 2016 +0200
     3.3 @@ -0,0 +1,129 @@
     3.4 +/*
     3.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
     3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.7 + *
     3.8 + * This code is free software; you can redistribute it and/or modify it
     3.9 + * under the terms of the GNU General Public License version 2 only, as
    3.10 + * published by the Free Software Foundation.  Oracle designates this
    3.11 + * particular file as subject to the "Classpath" exception as provided
    3.12 + * by Oracle in the LICENSE file that accompanied this code.
    3.13 + *
    3.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    3.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    3.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    3.17 + * version 2 for more details (a copy is included in the LICENSE file that
    3.18 + * accompanied this code).
    3.19 + *
    3.20 + * You should have received a copy of the GNU General Public License version
    3.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    3.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    3.23 + *
    3.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    3.25 + * or visit www.oracle.com if you need additional information or have any
    3.26 + * questions.
    3.27 + */
    3.28 +package jdk.internal.jshell.jdi;
    3.29 +
    3.30 +import java.util.HashMap;
    3.31 +import java.util.Objects;
    3.32 +import com.sun.jdi.ReferenceType;
    3.33 +import java.util.List;
    3.34 +
    3.35 +/**
    3.36 + * Tracks the state of a class.
    3.37 + */
    3.38 +class ClassTracker {
    3.39 +
    3.40 +    private final JDIEnv jdiEnv;
    3.41 +    private final HashMap<String, ClassInfo> map;
    3.42 +
    3.43 +    ClassTracker(JDIEnv jdiEnv) {
    3.44 +        this.jdiEnv = jdiEnv;
    3.45 +        this.map = new HashMap<>();
    3.46 +    }
    3.47 +
    3.48 +    /**
    3.49 +     * Associates a class name, class bytes, and ReferenceType.
    3.50 +     */
    3.51 +    class ClassInfo {
    3.52 +
    3.53 +        // The name of the class -- always set
    3.54 +        private final String className;
    3.55 +
    3.56 +        // The corresponding compiled class bytes when a load or redefine
    3.57 +        // is started.  May not be the loaded bytes.  May be null.
    3.58 +        private byte[] bytes;
    3.59 +
    3.60 +        // The class bytes successfully loaded/redefined into the remote VM.
    3.61 +        private byte[] loadedBytes;
    3.62 +
    3.63 +        // The corresponding JDI ReferenceType.  Used by redefineClasses and
    3.64 +        // acts as indicator of successful load (null if not loaded).
    3.65 +        private ReferenceType rt;
    3.66 +
    3.67 +        private ClassInfo(String className) {
    3.68 +            this.className = className;
    3.69 +        }
    3.70 +
    3.71 +        String getClassName() {
    3.72 +            return className;
    3.73 +        }
    3.74 +
    3.75 +        byte[] getLoadedBytes() {
    3.76 +            return loadedBytes;
    3.77 +        }
    3.78 +
    3.79 +        byte[] getBytes() {
    3.80 +            return bytes;
    3.81 +        }
    3.82 +
    3.83 +        private void setBytes(byte[] potentialBytes) {
    3.84 +            this.bytes = potentialBytes;
    3.85 +        }
    3.86 +
    3.87 +        // The class has been successful loaded redefined.  The class bytes
    3.88 +        // sent are now actually loaded.
    3.89 +        void markLoaded() {
    3.90 +            loadedBytes = bytes;
    3.91 +        }
    3.92 +
    3.93 +        // Ask JDI for the ReferenceType, null if not loaded.
    3.94 +        ReferenceType getReferenceTypeOrNull() {
    3.95 +            if (rt == null) {
    3.96 +                rt = nameToRef(className);
    3.97 +            }
    3.98 +            return rt;
    3.99 +        }
   3.100 +
   3.101 +        private ReferenceType nameToRef(String name) {
   3.102 +            List<ReferenceType> rtl = jdiEnv.vm().classesByName(name);
   3.103 +            if (rtl.size() != 1) {
   3.104 +                return null;
   3.105 +            }
   3.106 +            return rtl.get(0);
   3.107 +        }
   3.108 +
   3.109 +        @Override
   3.110 +        public boolean equals(Object o) {
   3.111 +            return o instanceof ClassInfo
   3.112 +                    && ((ClassInfo) o).className.equals(className);
   3.113 +        }
   3.114 +
   3.115 +        @Override
   3.116 +        public int hashCode() {
   3.117 +            return Objects.hashCode(this.className);
   3.118 +        }
   3.119 +    }
   3.120 +
   3.121 +    // Map a class name to the current compiled class bytes.
   3.122 +    ClassInfo classInfo(String className, byte[] bytes) {
   3.123 +        ClassInfo ci = get(className);
   3.124 +        ci.setBytes(bytes);
   3.125 +        return ci;
   3.126 +    }
   3.127 +
   3.128 +    // Lookup the ClassInfo by class name, create if it does not exist.
   3.129 +    ClassInfo get(String className) {
   3.130 +        return map.computeIfAbsent(className, k -> new ClassInfo(k));
   3.131 +    }
   3.132 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIConnection.java	Fri Sep 02 23:54:26 2016 +0200
     4.3 @@ -0,0 +1,345 @@
     4.4 +/*
     4.5 + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
     4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4.7 + *
     4.8 + * This code is free software; you can redistribute it and/or modify it
     4.9 + * under the terms of the GNU General Public License version 2 only, as
    4.10 + * published by the Free Software Foundation.  Oracle designates this
    4.11 + * particular file as subject to the "Classpath" exception as provided
    4.12 + * by Oracle in the LICENSE file that accompanied this code.
    4.13 + *
    4.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    4.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    4.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    4.17 + * version 2 for more details (a copy is included in the LICENSE file that
    4.18 + * accompanied this code).
    4.19 + *
    4.20 + * You should have received a copy of the GNU General Public License version
    4.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    4.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    4.23 + *
    4.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    4.25 + * or visit www.oracle.com if you need additional information or have any
    4.26 + * questions.
    4.27 + */
    4.28 +
    4.29 +/*
    4.30 + * This source code is provided to illustrate the usage of a given feature
    4.31 + * or technique and has been deliberately simplified. Additional steps
    4.32 + * required for a production-quality application, such as security checks,
    4.33 + * input validation and proper error handling, might not be present in
    4.34 + * this sample code.
    4.35 + */
    4.36 +
    4.37 +
    4.38 +package jdk.internal.jshell.jdi;
    4.39 +
    4.40 +import com.sun.jdi.*;
    4.41 +import com.sun.jdi.connect.*;
    4.42 +
    4.43 +import java.util.*;
    4.44 +import java.util.Map.Entry;
    4.45 +import java.io.*;
    4.46 +import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
    4.47 +
    4.48 +/**
    4.49 + * Connection to a Java Debug Interface VirtualMachine instance.
    4.50 + * Adapted from jdb VMConnection. Message handling, exception handling, and I/O
    4.51 + * redirection changed.  Interface to JShell added.
    4.52 + */
    4.53 +class JDIConnection {
    4.54 +
    4.55 +    private VirtualMachine vm;
    4.56 +    private Process process = null;
    4.57 +    private int outputCompleteCount = 0;
    4.58 +
    4.59 +    private final JDIExecutionControl ec;
    4.60 +    private final Connector connector;
    4.61 +    private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs;
    4.62 +    private final int traceFlags;
    4.63 +
    4.64 +    synchronized void notifyOutputComplete() {
    4.65 +        outputCompleteCount++;
    4.66 +        notifyAll();
    4.67 +    }
    4.68 +
    4.69 +    synchronized void waitOutputComplete() {
    4.70 +        // Wait for stderr and stdout
    4.71 +        if (process != null) {
    4.72 +            while (outputCompleteCount < 2) {
    4.73 +                try {wait();} catch (InterruptedException e) {}
    4.74 +            }
    4.75 +        }
    4.76 +    }
    4.77 +
    4.78 +    private Connector findConnector(String name) {
    4.79 +        for (Connector cntor :
    4.80 +                 Bootstrap.virtualMachineManager().allConnectors()) {
    4.81 +            if (cntor.name().equals(name)) {
    4.82 +                return cntor;
    4.83 +            }
    4.84 +        }
    4.85 +        return null;
    4.86 +    }
    4.87 +
    4.88 +    private Map <String, Connector.Argument> mergeConnectorArgs(Connector connector, Map<String, String> argumentName2Value) {
    4.89 +        Map<String, Connector.Argument> arguments = connector.defaultArguments();
    4.90 +
    4.91 +        for (Entry<String, String> argumentEntry : argumentName2Value.entrySet()) {
    4.92 +            String name = argumentEntry.getKey();
    4.93 +            String value = argumentEntry.getValue();
    4.94 +            Connector.Argument argument = arguments.get(name);
    4.95 +
    4.96 +            if (argument == null) {
    4.97 +                throw new IllegalArgumentException("Argument is not defined for connector:" +
    4.98 +                                          name + " -- " + connector.name());
    4.99 +            }
   4.100 +
   4.101 +            argument.setValue(value);
   4.102 +        }
   4.103 +
   4.104 +        return arguments;
   4.105 +    }
   4.106 +
   4.107 +
   4.108 +    JDIConnection(JDIExecutionControl ec, String connectorName, Map<String, String> argumentName2Value, int traceFlags) {
   4.109 +        this.ec = ec;
   4.110 +        this.connector = findConnector(connectorName);
   4.111 +
   4.112 +        if (connector == null) {
   4.113 +            throw new IllegalArgumentException("No connector named: " + connectorName);
   4.114 +        }
   4.115 +
   4.116 +        connectorArgs = mergeConnectorArgs(connector, argumentName2Value);
   4.117 +        this.traceFlags = traceFlags;
   4.118 +    }
   4.119 +
   4.120 +    synchronized VirtualMachine open() {
   4.121 +        if (connector instanceof LaunchingConnector) {
   4.122 +            vm = launchTarget();
   4.123 +        } else if (connector instanceof AttachingConnector) {
   4.124 +            vm = attachTarget();
   4.125 +        } else if (connector instanceof ListeningConnector) {
   4.126 +            vm = listenTarget();
   4.127 +        } else {
   4.128 +            throw new InternalError("Invalid connect type");
   4.129 +        }
   4.130 +        vm.setDebugTraceMode(traceFlags);
   4.131 +        // Uncomment here and below to enable event requests
   4.132 +        // installEventRequests(vm);
   4.133 +
   4.134 +        return vm;
   4.135 +    }
   4.136 +
   4.137 +    synchronized boolean setConnectorArg(String name, String value) {
   4.138 +        /*
   4.139 +         * Too late if the connection already made
   4.140 +         */
   4.141 +        if (vm != null) {
   4.142 +            return false;
   4.143 +        }
   4.144 +
   4.145 +        Connector.Argument argument = connectorArgs.get(name);
   4.146 +        if (argument == null) {
   4.147 +            return false;
   4.148 +        }
   4.149 +        argument.setValue(value);
   4.150 +        return true;
   4.151 +    }
   4.152 +
   4.153 +    String connectorArg(String name) {
   4.154 +        Connector.Argument argument = connectorArgs.get(name);
   4.155 +        if (argument == null) {
   4.156 +            return "";
   4.157 +        }
   4.158 +        return argument.value();
   4.159 +    }
   4.160 +
   4.161 +    public synchronized VirtualMachine vm() {
   4.162 +        if (vm == null) {
   4.163 +            throw new JDINotConnectedException();
   4.164 +        } else {
   4.165 +            return vm;
   4.166 +        }
   4.167 +    }
   4.168 +
   4.169 +    synchronized boolean isOpen() {
   4.170 +        return (vm != null);
   4.171 +    }
   4.172 +
   4.173 +    boolean isLaunch() {
   4.174 +        return (connector instanceof LaunchingConnector);
   4.175 +    }
   4.176 +
   4.177 +    synchronized boolean isRunning() {
   4.178 +        return process != null && process.isAlive();
   4.179 +    }
   4.180 +
   4.181 +    public synchronized void disposeVM() {
   4.182 +        try {
   4.183 +            if (vm != null) {
   4.184 +                vm.dispose(); // This could NPE, so it is caught below
   4.185 +                vm = null;
   4.186 +            }
   4.187 +        } catch (VMDisconnectedException ex) {
   4.188 +            // Ignore if already closed
   4.189 +        } finally {
   4.190 +            if (process != null) {
   4.191 +                process.destroy();
   4.192 +                process = null;
   4.193 +            }
   4.194 +            waitOutputComplete();
   4.195 +        }
   4.196 +    }
   4.197 +
   4.198 +/*** Preserved for possible future support of event requests
   4.199 +
   4.200 +    private void installEventRequests(VirtualMachine vm) {
   4.201 +        if (vm.canBeModified()){
   4.202 +            setEventRequests(vm);
   4.203 +            resolveEventRequests();
   4.204 +        }
   4.205 +    }
   4.206 +
   4.207 +    private void setEventRequests(VirtualMachine vm) {
   4.208 +        EventRequestManager erm = vm.eventRequestManager();
   4.209 +
   4.210 +        // Normally, we want all uncaught exceptions.  We request them
   4.211 +        // via the same mechanism as Commands.commandCatchException()
   4.212 +        // so the user can ignore them later if they are not
   4.213 +        // interested.
   4.214 +        // FIXME: this works but generates spurious messages on stdout
   4.215 +        //        during startup:
   4.216 +        //          Set uncaught java.lang.Throwable
   4.217 +        //          Set deferred uncaught java.lang.Throwable
   4.218 +        Commands evaluator = new Commands();
   4.219 +        evaluator.commandCatchException
   4.220 +            (new StringTokenizer("uncaught java.lang.Throwable"));
   4.221 +
   4.222 +        ThreadStartRequest tsr = erm.createThreadStartRequest();
   4.223 +        tsr.enable();
   4.224 +        ThreadDeathRequest tdr = erm.createThreadDeathRequest();
   4.225 +        tdr.enable();
   4.226 +    }
   4.227 +
   4.228 +    private void resolveEventRequests() {
   4.229 +        Env.specList.resolveAll();
   4.230 +    }
   4.231 +***/
   4.232 +
   4.233 +    private void dumpStream(InputStream inStream, final PrintStream pStream) throws IOException {
   4.234 +        BufferedReader in =
   4.235 +            new BufferedReader(new InputStreamReader(inStream));
   4.236 +        int i;
   4.237 +        try {
   4.238 +            while ((i = in.read()) != -1) {
   4.239 +                pStream.print((char) i);
   4.240 +            }
   4.241 +        } catch (IOException ex) {
   4.242 +            String s = ex.getMessage();
   4.243 +            if (!s.startsWith("Bad file number")) {
   4.244 +                throw ex;
   4.245 +            }
   4.246 +            // else we got a Bad file number IOException which just means
   4.247 +            // that the debuggee has gone away.  We'll just treat it the
   4.248 +            // same as if we got an EOF.
   4.249 +        }
   4.250 +    }
   4.251 +
   4.252 +    /**
   4.253 +     *  Create a Thread that will retrieve and display any output.
   4.254 +     *  Needs to be high priority, else debugger may exit before
   4.255 +     *  it can be displayed.
   4.256 +     */
   4.257 +    private void displayRemoteOutput(final InputStream inStream, final PrintStream pStream) {
   4.258 +        Thread thr = new Thread("output reader") {
   4.259 +            @Override
   4.260 +            public void run() {
   4.261 +                try {
   4.262 +                    dumpStream(inStream, pStream);
   4.263 +                } catch (IOException ex) {
   4.264 +                    ec.debug(ex, "Failed reading output");
   4.265 +                    ec.jdiEnv.shutdown();
   4.266 +                } finally {
   4.267 +                    notifyOutputComplete();
   4.268 +                }
   4.269 +            }
   4.270 +        };
   4.271 +        thr.setPriority(Thread.MAX_PRIORITY-1);
   4.272 +        thr.start();
   4.273 +    }
   4.274 +
   4.275 +    /**
   4.276 +     *  Create a Thread that will ship all input to remote.
   4.277 +     *  Does it need be high priority?
   4.278 +     */
   4.279 +    private void readRemoteInput(final OutputStream outStream, final InputStream inputStream) {
   4.280 +        Thread thr = new Thread("input reader") {
   4.281 +            @Override
   4.282 +            public void run() {
   4.283 +                try {
   4.284 +                    byte[] buf = new byte[256];
   4.285 +                    int cnt;
   4.286 +                    while ((cnt = inputStream.read(buf)) != -1) {
   4.287 +                        outStream.write(buf, 0, cnt);
   4.288 +                        outStream.flush();
   4.289 +                    }
   4.290 +                } catch (IOException ex) {
   4.291 +                    ec.debug(ex, "Failed reading output");
   4.292 +                    ec.jdiEnv.shutdown();
   4.293 +                }
   4.294 +            }
   4.295 +        };
   4.296 +        thr.setPriority(Thread.MAX_PRIORITY-1);
   4.297 +        thr.start();
   4.298 +    }
   4.299 +
   4.300 +    /* launch child target vm */
   4.301 +    private VirtualMachine launchTarget() {
   4.302 +        LaunchingConnector launcher = (LaunchingConnector)connector;
   4.303 +        try {
   4.304 +            VirtualMachine new_vm = launcher.launch(connectorArgs);
   4.305 +            process = new_vm.process();
   4.306 +            displayRemoteOutput(process.getErrorStream(), ec.execEnv.userErr());
   4.307 +            displayRemoteOutput(process.getInputStream(), ec.execEnv.userOut());
   4.308 +            readRemoteInput(process.getOutputStream(), ec.execEnv.userIn());
   4.309 +            return new_vm;
   4.310 +        } catch (Exception ex) {
   4.311 +            reportLaunchFail(ex, "launch");
   4.312 +        }
   4.313 +        return null;
   4.314 +    }
   4.315 +
   4.316 +    /* JShell currently uses only launch, preserved for futures: */
   4.317 +    /* attach to running target vm */
   4.318 +    private VirtualMachine attachTarget() {
   4.319 +        AttachingConnector attacher = (AttachingConnector)connector;
   4.320 +        try {
   4.321 +            return attacher.attach(connectorArgs);
   4.322 +        } catch (Exception ex) {
   4.323 +            reportLaunchFail(ex, "attach");
   4.324 +        }
   4.325 +        return null;
   4.326 +    }
   4.327 +
   4.328 +    /* JShell currently uses only launch, preserved for futures: */
   4.329 +    /* listen for connection from target vm */
   4.330 +    private VirtualMachine listenTarget() {
   4.331 +        ListeningConnector listener = (ListeningConnector)connector;
   4.332 +        try {
   4.333 +            String retAddress = listener.startListening(connectorArgs);
   4.334 +            ec.debug(DBG_GEN, "Listening at address: " + retAddress);
   4.335 +            vm = listener.accept(connectorArgs);
   4.336 +            listener.stopListening(connectorArgs);
   4.337 +            return vm;
   4.338 +        } catch (Exception ex) {
   4.339 +            reportLaunchFail(ex, "listen");
   4.340 +        }
   4.341 +        return null;
   4.342 +    }
   4.343 +
   4.344 +    private void reportLaunchFail(Exception ex, String context) {
   4.345 +        throw new InternalError("Failed remote " + context + ": " + connector +
   4.346 +                " -- " + connectorArgs, ex);
   4.347 +    }
   4.348 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIEnv.java	Fri Sep 02 23:54:26 2016 +0200
     5.3 @@ -0,0 +1,80 @@
     5.4 +/*
     5.5 + * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
     5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     5.7 + *
     5.8 + * This code is free software; you can redistribute it and/or modify it
     5.9 + * under the terms of the GNU General Public License version 2 only, as
    5.10 + * published by the Free Software Foundation.  Oracle designates this
    5.11 + * particular file as subject to the "Classpath" exception as provided
    5.12 + * by Oracle in the LICENSE file that accompanied this code.
    5.13 + *
    5.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    5.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    5.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    5.17 + * version 2 for more details (a copy is included in the LICENSE file that
    5.18 + * accompanied this code).
    5.19 + *
    5.20 + * You should have received a copy of the GNU General Public License version
    5.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    5.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    5.23 + *
    5.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    5.25 + * or visit www.oracle.com if you need additional information or have any
    5.26 + * questions.
    5.27 + */
    5.28 +
    5.29 +package jdk.internal.jshell.jdi;
    5.30 +
    5.31 +import java.util.Map;
    5.32 +
    5.33 +import com.sun.jdi.*;
    5.34 +import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
    5.35 +
    5.36 +/**
    5.37 + * Representation of a Java Debug Interface environment
    5.38 + * Select methods extracted from jdb Env; shutdown() adapted to JShell shutdown.
    5.39 + */
    5.40 +class JDIEnv {
    5.41 +
    5.42 +    private JDIConnection connection;
    5.43 +    private final JDIExecutionControl ec;
    5.44 +
    5.45 +    JDIEnv(JDIExecutionControl ec) {
    5.46 +        this.ec = ec;
    5.47 +    }
    5.48 +
    5.49 +    void init(String connectorName, Map<String, String> argumentName2Value, boolean openNow, int flags) {
    5.50 +        connection = new JDIConnection(ec, connectorName, argumentName2Value, flags);
    5.51 +        if (!connection.isLaunch() || openNow) {
    5.52 +            connection.open();
    5.53 +        }
    5.54 +    }
    5.55 +
    5.56 +    JDIConnection connection() {
    5.57 +        return connection;
    5.58 +    }
    5.59 +
    5.60 +    VirtualMachine vm() {
    5.61 +        return connection.vm();
    5.62 +    }
    5.63 +
    5.64 +    void shutdown() {
    5.65 +        if (connection != null) {
    5.66 +            try {
    5.67 +                connection.disposeVM();
    5.68 +            } catch (VMDisconnectedException e) {
    5.69 +                // Shutting down after the VM has gone away. This is
    5.70 +                // not an error, and we just ignore it.
    5.71 +            } catch (Throwable e) {
    5.72 +                ec.debug(DBG_GEN, null, "disposeVM threw: " + e);
    5.73 +            }
    5.74 +        }
    5.75 +        if (ec.execEnv.state() != null) { // If state has been set-up
    5.76 +            try {
    5.77 +                ec.execEnv.closeDown();
    5.78 +            } catch (Throwable e) {
    5.79 +                ec.debug(DBG_GEN, null, "state().closeDown() threw: " + e);
    5.80 +            }
    5.81 +        }
    5.82 +    }
    5.83 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIEventHandler.java	Fri Sep 02 23:54:26 2016 +0200
     6.3 @@ -0,0 +1,181 @@
     6.4 +/*
     6.5 + * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
     6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     6.7 + *
     6.8 + * This code is free software; you can redistribute it and/or modify it
     6.9 + * under the terms of the GNU General Public License version 2 only, as
    6.10 + * published by the Free Software Foundation.  Oracle designates this
    6.11 + * particular file as subject to the "Classpath" exception as provided
    6.12 + * by Oracle in the LICENSE file that accompanied this code.
    6.13 + *
    6.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    6.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    6.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    6.17 + * version 2 for more details (a copy is included in the LICENSE file that
    6.18 + * accompanied this code).
    6.19 + *
    6.20 + * You should have received a copy of the GNU General Public License version
    6.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    6.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    6.23 + *
    6.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    6.25 + * or visit www.oracle.com if you need additional information or have any
    6.26 + * questions.
    6.27 + */
    6.28 +
    6.29 +package jdk.internal.jshell.jdi;
    6.30 +
    6.31 +import com.sun.jdi.*;
    6.32 +import com.sun.jdi.event.*;
    6.33 +
    6.34 +/**
    6.35 + * Handler of Java Debug Interface events.
    6.36 + * Adapted from jdb EventHandler; Handling of events not used by JShell stubbed out.
    6.37 + */
    6.38 +class JDIEventHandler implements Runnable {
    6.39 +
    6.40 +    Thread thread;
    6.41 +    volatile boolean connected = true;
    6.42 +    boolean completed = false;
    6.43 +    String shutdownMessageKey;
    6.44 +    final JDIEnv env;
    6.45 +
    6.46 +    JDIEventHandler(JDIEnv env) {
    6.47 +        this.env = env;
    6.48 +        this.thread = new Thread(this, "event-handler");
    6.49 +        this.thread.start();
    6.50 +    }
    6.51 +
    6.52 +    synchronized void shutdown() {
    6.53 +        connected = false;  // force run() loop termination
    6.54 +        thread.interrupt();
    6.55 +        while (!completed) {
    6.56 +            try {wait();} catch (InterruptedException exc) {}
    6.57 +        }
    6.58 +    }
    6.59 +
    6.60 +    @Override
    6.61 +    public void run() {
    6.62 +        EventQueue queue = env.vm().eventQueue();
    6.63 +        while (connected) {
    6.64 +            try {
    6.65 +                EventSet eventSet = queue.remove();
    6.66 +                boolean resumeStoppedApp = false;
    6.67 +                EventIterator it = eventSet.eventIterator();
    6.68 +                while (it.hasNext()) {
    6.69 +                    resumeStoppedApp |= handleEvent(it.nextEvent());
    6.70 +                }
    6.71 +
    6.72 +                if (resumeStoppedApp) {
    6.73 +                    eventSet.resume();
    6.74 +                }
    6.75 +            } catch (InterruptedException exc) {
    6.76 +                // Do nothing. Any changes will be seen at top of loop.
    6.77 +            } catch (VMDisconnectedException discExc) {
    6.78 +                handleDisconnectedException();
    6.79 +                break;
    6.80 +            }
    6.81 +        }
    6.82 +        synchronized (this) {
    6.83 +            completed = true;
    6.84 +            notifyAll();
    6.85 +        }
    6.86 +    }
    6.87 +
    6.88 +    private boolean handleEvent(Event event) {
    6.89 +        if (event instanceof ExceptionEvent) {
    6.90 +            exceptionEvent(event);
    6.91 +        } else if (event instanceof WatchpointEvent) {
    6.92 +            fieldWatchEvent(event);
    6.93 +        } else if (event instanceof MethodEntryEvent) {
    6.94 +            methodEntryEvent(event);
    6.95 +        } else if (event instanceof MethodExitEvent) {
    6.96 +            methodExitEvent(event);
    6.97 +        } else if (event instanceof ClassPrepareEvent) {
    6.98 +            classPrepareEvent(event);
    6.99 +        } else if (event instanceof ThreadStartEvent) {
   6.100 +            threadStartEvent(event);
   6.101 +        } else if (event instanceof ThreadDeathEvent) {
   6.102 +            threadDeathEvent(event);
   6.103 +        } else if (event instanceof VMStartEvent) {
   6.104 +            vmStartEvent(event);
   6.105 +            return true;
   6.106 +        } else {
   6.107 +            handleExitEvent(event);
   6.108 +        }
   6.109 +        return true;
   6.110 +    }
   6.111 +
   6.112 +    private boolean vmDied = false;
   6.113 +
   6.114 +    private void handleExitEvent(Event event) {
   6.115 +        if (event instanceof VMDeathEvent) {
   6.116 +            vmDied = true;
   6.117 +            shutdownMessageKey = "The application exited";
   6.118 +        } else if (event instanceof VMDisconnectEvent) {
   6.119 +            connected = false;
   6.120 +            if (!vmDied) {
   6.121 +                shutdownMessageKey = "The application has been disconnected";
   6.122 +            }
   6.123 +        } else {
   6.124 +            throw new InternalError("Unexpected event type: " +
   6.125 +                    event.getClass());
   6.126 +        }
   6.127 +        env.shutdown();
   6.128 +    }
   6.129 +
   6.130 +    synchronized void handleDisconnectedException() {
   6.131 +        /*
   6.132 +         * A VMDisconnectedException has happened while dealing with
   6.133 +         * another event. We need to flush the event queue, dealing only
   6.134 +         * with exit events (VMDeath, VMDisconnect) so that we terminate
   6.135 +         * correctly.
   6.136 +         */
   6.137 +        EventQueue queue = env.vm().eventQueue();
   6.138 +        while (connected) {
   6.139 +            try {
   6.140 +                EventSet eventSet = queue.remove();
   6.141 +                EventIterator iter = eventSet.eventIterator();
   6.142 +                while (iter.hasNext()) {
   6.143 +                    handleExitEvent(iter.next());
   6.144 +                }
   6.145 +            } catch (InterruptedException exc) {
   6.146 +                // ignore
   6.147 +            } catch (InternalError exc) {
   6.148 +                // ignore
   6.149 +            }
   6.150 +        }
   6.151 +    }
   6.152 +
   6.153 +    private void vmStartEvent(Event event)  {
   6.154 +        VMStartEvent se = (VMStartEvent)event;
   6.155 +    }
   6.156 +
   6.157 +    private void methodEntryEvent(Event event)  {
   6.158 +        MethodEntryEvent me = (MethodEntryEvent)event;
   6.159 +    }
   6.160 +
   6.161 +    private void methodExitEvent(Event event)  {
   6.162 +        MethodExitEvent me = (MethodExitEvent)event;
   6.163 +    }
   6.164 +
   6.165 +    private void fieldWatchEvent(Event event)  {
   6.166 +        WatchpointEvent fwe = (WatchpointEvent)event;
   6.167 +    }
   6.168 +
   6.169 +    private void classPrepareEvent(Event event)  {
   6.170 +        ClassPrepareEvent cle = (ClassPrepareEvent)event;
   6.171 +    }
   6.172 +
   6.173 +    private void exceptionEvent(Event event) {
   6.174 +        ExceptionEvent ee = (ExceptionEvent)event;
   6.175 +    }
   6.176 +
   6.177 +    private void threadDeathEvent(Event event) {
   6.178 +        ThreadDeathEvent tee = (ThreadDeathEvent)event;
   6.179 +    }
   6.180 +
   6.181 +    private void threadStartEvent(Event event) {
   6.182 +        ThreadStartEvent tse = (ThreadStartEvent)event;
   6.183 +    }
   6.184 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIExecutionControl.java	Fri Sep 02 23:54:26 2016 +0200
     7.3 @@ -0,0 +1,595 @@
     7.4 +/*
     7.5 + * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
     7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.7 + *
     7.8 + * This code is free software; you can redistribute it and/or modify it
     7.9 + * under the terms of the GNU General Public License version 2 only, as
    7.10 + * published by the Free Software Foundation.  Oracle designates this
    7.11 + * particular file as subject to the "Classpath" exception as provided
    7.12 + * by Oracle in the LICENSE file that accompanied this code.
    7.13 + *
    7.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    7.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    7.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    7.17 + * version 2 for more details (a copy is included in the LICENSE file that
    7.18 + * accompanied this code).
    7.19 + *
    7.20 + * You should have received a copy of the GNU General Public License version
    7.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    7.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    7.23 + *
    7.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    7.25 + * or visit www.oracle.com if you need additional information or have any
    7.26 + * questions.
    7.27 + */
    7.28 +
    7.29 +package jdk.internal.jshell.jdi;
    7.30 +
    7.31 +import static jdk.internal.jshell.remote.RemoteCodes.*;
    7.32 +import java.io.DataInputStream;
    7.33 +import java.io.InputStream;
    7.34 +import java.io.IOException;
    7.35 +import java.io.ObjectInputStream;
    7.36 +import java.io.ObjectOutputStream;
    7.37 +import java.io.PrintStream;
    7.38 +import java.net.ServerSocket;
    7.39 +import java.net.Socket;
    7.40 +import com.sun.jdi.*;
    7.41 +import java.io.EOFException;
    7.42 +import java.util.Arrays;
    7.43 +import java.util.Collection;
    7.44 +import java.util.HashMap;
    7.45 +import java.util.List;
    7.46 +import java.util.Map;
    7.47 +import static java.util.stream.Collectors.toList;
    7.48 +import jdk.jshell.JShellException;
    7.49 +import jdk.jshell.spi.ExecutionControl;
    7.50 +import jdk.jshell.spi.ExecutionEnv;
    7.51 +import jdk.internal.jshell.jdi.ClassTracker.ClassInfo;
    7.52 +import static java.util.stream.Collectors.toMap;
    7.53 +import jdk.internal.jshell.debug.InternalDebugControl;
    7.54 +import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
    7.55 +
    7.56 +/**
    7.57 + * Controls the remote execution environment.
    7.58 + * Interfaces to the JShell-core by implementing ExecutionControl SPI.
    7.59 + * Interfaces to RemoteAgent over a socket and via JDI.
    7.60 + * Launches a remote process.
    7.61 + */
    7.62 +public class JDIExecutionControl implements ExecutionControl {
    7.63 +
    7.64 +    ExecutionEnv execEnv;
    7.65 +    JDIEnv jdiEnv;
    7.66 +    private ClassTracker tracker;
    7.67 +    private JDIEventHandler handler;
    7.68 +    private Socket socket;
    7.69 +    private ObjectInputStream remoteIn;
    7.70 +    private ObjectOutputStream remoteOut;
    7.71 +    private String remoteVMOptions;
    7.72 +
    7.73 +    /**
    7.74 +     * Initializes the launching JDI execution engine. Initialize JDI and use it
    7.75 +     * to launch the remote JVM. Set-up control and result communications socket
    7.76 +     * to the remote execution environment. This socket also transports the
    7.77 +     * input/output channels.
    7.78 +     *
    7.79 +     * @param execEnv the execution environment provided by the JShell-core
    7.80 +     * @throws IOException
    7.81 +     */
    7.82 +    @Override
    7.83 +    public void start(ExecutionEnv execEnv) throws IOException {
    7.84 +        this.execEnv = execEnv;
    7.85 +        this.jdiEnv = new JDIEnv(this);
    7.86 +        this.tracker = new ClassTracker(jdiEnv);
    7.87 +        StringBuilder sb = new StringBuilder();
    7.88 +        execEnv.extraRemoteVMOptions().stream()
    7.89 +                .forEach(s -> {
    7.90 +                    sb.append(" ");
    7.91 +                    sb.append(s);
    7.92 +                });
    7.93 +        this.remoteVMOptions = sb.toString();
    7.94 +        try (ServerSocket listener = new ServerSocket(0)) {
    7.95 +            // timeout after 60 seconds
    7.96 +            listener.setSoTimeout(60000);
    7.97 +            int port = listener.getLocalPort();
    7.98 +            jdiGo(port);
    7.99 +            this.socket = listener.accept();
   7.100 +            // out before in -- match remote creation so we don't hang
   7.101 +            this.remoteOut = new ObjectOutputStream(socket.getOutputStream());
   7.102 +            PipeInputStream commandIn = new PipeInputStream();
   7.103 +            new DemultiplexInput(socket.getInputStream(), commandIn, execEnv.userOut(), execEnv.userErr()).start();
   7.104 +            this.remoteIn = new ObjectInputStream(commandIn);
   7.105 +        }
   7.106 +    }
   7.107 +
   7.108 +    /**
   7.109 +     * Closes the execution engine. Send an exit command to the remote agent.
   7.110 +     * Shuts down the JDI connection. Should this close the socket?
   7.111 +     */
   7.112 +    @Override
   7.113 +    public void close() {
   7.114 +        try {
   7.115 +            if (remoteOut != null) {
   7.116 +                remoteOut.writeInt(CMD_EXIT);
   7.117 +                remoteOut.flush();
   7.118 +            }
   7.119 +            JDIConnection c = jdiEnv.connection();
   7.120 +            if (c != null) {
   7.121 +                c.disposeVM();
   7.122 +            }
   7.123 +        } catch (IOException ex) {
   7.124 +            debug(DBG_GEN, "Exception on JDI exit: %s\n", ex);
   7.125 +        }
   7.126 +    }
   7.127 +
   7.128 +    /**
   7.129 +     * Loads the list of classes specified. Sends a load command to the remote
   7.130 +     * agent with pairs of classname/bytes.
   7.131 +     *
   7.132 +     * @param classes the names of the wrapper classes to loaded
   7.133 +     * @return true if all classes loaded successfully
   7.134 +     */
   7.135 +    @Override
   7.136 +    public boolean load(Collection<String> classes) {
   7.137 +        try {
   7.138 +            // Create corresponding ClassInfo instances to track the classes.
   7.139 +            // Each ClassInfo has the current class bytes associated with it.
   7.140 +            List<ClassInfo> infos = withBytes(classes);
   7.141 +            // Send a load command to the remote agent.
   7.142 +            remoteOut.writeInt(CMD_LOAD);
   7.143 +            remoteOut.writeInt(classes.size());
   7.144 +            for (ClassInfo ci : infos) {
   7.145 +                remoteOut.writeUTF(ci.getClassName());
   7.146 +                remoteOut.writeObject(ci.getBytes());
   7.147 +            }
   7.148 +            remoteOut.flush();
   7.149 +            // Retrieve and report results from the remote agent.
   7.150 +            boolean result = readAndReportResult();
   7.151 +            // For each class that now has a JDI ReferenceType, mark the bytes
   7.152 +            // as loaded.
   7.153 +            infos.stream()
   7.154 +                    .filter(ci -> ci.getReferenceTypeOrNull() != null)
   7.155 +                    .forEach(ci -> ci.markLoaded());
   7.156 +            return result;
   7.157 +        } catch (IOException ex) {
   7.158 +            debug(DBG_GEN, "IOException on remote load operation: %s\n", ex);
   7.159 +            return false;
   7.160 +        }
   7.161 +    }
   7.162 +
   7.163 +    /**
   7.164 +     * Invoke the doit method on the specified class.
   7.165 +     *
   7.166 +     * @param classname name of the wrapper class whose doit should be invoked
   7.167 +     * @return return the result value of the doit
   7.168 +     * @throws JShellException if a user exception was thrown (EvalException) or
   7.169 +     * an unresolved reference was encountered (UnresolvedReferenceException)
   7.170 +     */
   7.171 +    @Override
   7.172 +    public String invoke(String classname, String methodname) throws JShellException {
   7.173 +        try {
   7.174 +            synchronized (STOP_LOCK) {
   7.175 +                userCodeRunning = true;
   7.176 +            }
   7.177 +            // Send the invoke command to the remote agent.
   7.178 +            remoteOut.writeInt(CMD_INVOKE);
   7.179 +            remoteOut.writeUTF(classname);
   7.180 +            remoteOut.writeUTF(methodname);
   7.181 +            remoteOut.flush();
   7.182 +            // Retrieve and report results from the remote agent.
   7.183 +            if (readAndReportExecutionResult()) {
   7.184 +                String result = remoteIn.readUTF();
   7.185 +                return result;
   7.186 +            }
   7.187 +        } catch (IOException | RuntimeException ex) {
   7.188 +            if (!jdiEnv.connection().isRunning()) {
   7.189 +                // The JDI connection is no longer live, shutdown.
   7.190 +                jdiEnv.shutdown();
   7.191 +            } else {
   7.192 +                debug(DBG_GEN, "Exception on remote invoke: %s\n", ex);
   7.193 +                return "Execution failure: " + ex.getMessage();
   7.194 +            }
   7.195 +        } finally {
   7.196 +            synchronized (STOP_LOCK) {
   7.197 +                userCodeRunning = false;
   7.198 +            }
   7.199 +        }
   7.200 +        return "";
   7.201 +    }
   7.202 +
   7.203 +    /**
   7.204 +     * Retrieves the value of a JShell variable.
   7.205 +     *
   7.206 +     * @param classname name of the wrapper class holding the variable
   7.207 +     * @param varname name of the variable
   7.208 +     * @return the value as a String
   7.209 +     */
   7.210 +    @Override
   7.211 +    public String varValue(String classname, String varname) {
   7.212 +        try {
   7.213 +            // Send the variable-value command to the remote agent.
   7.214 +            remoteOut.writeInt(CMD_VARVALUE);
   7.215 +            remoteOut.writeUTF(classname);
   7.216 +            remoteOut.writeUTF(varname);
   7.217 +            remoteOut.flush();
   7.218 +            // Retrieve and report results from the remote agent.
   7.219 +            if (readAndReportResult()) {
   7.220 +                String result = remoteIn.readUTF();
   7.221 +                return result;
   7.222 +            }
   7.223 +        } catch (EOFException ex) {
   7.224 +            jdiEnv.shutdown();
   7.225 +        } catch (IOException ex) {
   7.226 +            debug(DBG_GEN, "Exception on remote var value: %s\n", ex);
   7.227 +            return "Execution failure: " + ex.getMessage();
   7.228 +        }
   7.229 +        return "";
   7.230 +    }
   7.231 +
   7.232 +    /**
   7.233 +     * Adds a path to the remote classpath.
   7.234 +     *
   7.235 +     * @param cp the additional path element
   7.236 +     * @return true if succesful
   7.237 +     */
   7.238 +    @Override
   7.239 +    public boolean addToClasspath(String cp) {
   7.240 +        try {
   7.241 +            // Send the classpath addition command to the remote agent.
   7.242 +            remoteOut.writeInt(CMD_CLASSPATH);
   7.243 +            remoteOut.writeUTF(cp);
   7.244 +            remoteOut.flush();
   7.245 +            // Retrieve and report results from the remote agent.
   7.246 +            return readAndReportResult();
   7.247 +        } catch (IOException ex) {
   7.248 +            throw new InternalError("Classpath addition failed: " + cp, ex);
   7.249 +        }
   7.250 +    }
   7.251 +
   7.252 +    /**
   7.253 +     * Redefine the specified classes. Where 'redefine' is, as in JDI and JVMTI,
   7.254 +     * an in-place replacement of the classes (preserving class identity) --
   7.255 +     * that is, existing references to the class do not need to be recompiled.
   7.256 +     * This implementation uses JDI redefineClasses. It will be unsuccessful if
   7.257 +     * the signature of the class has changed (see the JDI spec). The
   7.258 +     * JShell-core is designed to adapt to unsuccessful redefine.
   7.259 +     *
   7.260 +     * @param classes the names of the classes to redefine
   7.261 +     * @return true if all the classes were redefined
   7.262 +     */
   7.263 +    @Override
   7.264 +    public boolean redefine(Collection<String> classes) {
   7.265 +        try {
   7.266 +            // Create corresponding ClassInfo instances to track the classes.
   7.267 +            // Each ClassInfo has the current class bytes associated with it.
   7.268 +            List<ClassInfo> infos = withBytes(classes);
   7.269 +            // Convert to the JDI ReferenceType to class bytes map form needed
   7.270 +            // by JDI.
   7.271 +            Map<ReferenceType, byte[]> rmp = infos.stream()
   7.272 +                    .collect(toMap(
   7.273 +                            ci -> ci.getReferenceTypeOrNull(),
   7.274 +                            ci -> ci.getBytes()));
   7.275 +            // Attempt redefine.  Throws exceptions on failure.
   7.276 +            jdiEnv.vm().redefineClasses(rmp);
   7.277 +            // Successful: mark the bytes as loaded.
   7.278 +            infos.stream()
   7.279 +                    .forEach(ci -> ci.markLoaded());
   7.280 +            return true;
   7.281 +        } catch (UnsupportedOperationException ex) {
   7.282 +            // A form of class transformation not supported by JDI
   7.283 +            return false;
   7.284 +        } catch (Exception ex) {
   7.285 +            debug(DBG_GEN, "Exception on JDI redefine: %s\n", ex);
   7.286 +            return false;
   7.287 +        }
   7.288 +    }
   7.289 +
   7.290 +    /**
   7.291 +     * Converts a collection of class names into ClassInfo instances associated
   7.292 +     * with the most recently compiled class bytes.
   7.293 +     *
   7.294 +     * @param classes names of the classes
   7.295 +     * @return a list of corresponding ClassInfo instances
   7.296 +     */
   7.297 +    private List<ClassInfo> withBytes(Collection<String> classes) {
   7.298 +        return classes.stream()
   7.299 +                .map(cn -> tracker.classInfo(cn, execEnv.getClassBytes(cn)))
   7.300 +                .collect(toList());
   7.301 +    }
   7.302 +
   7.303 +    /**
   7.304 +     * Reports the status of the named class. UNKNOWN if not loaded. CURRENT if
   7.305 +     * the most recent successfully loaded/redefined bytes match the current
   7.306 +     * compiled bytes.
   7.307 +     *
   7.308 +     * @param classname the name of the class to test
   7.309 +     * @return the status
   7.310 +     */
   7.311 +    @Override
   7.312 +    public ClassStatus getClassStatus(String classname) {
   7.313 +        ClassInfo ci = tracker.get(classname);
   7.314 +        if (ci.getReferenceTypeOrNull() == null) {
   7.315 +            // If the class does not have a JDI ReferenceType it has not been loaded
   7.316 +            return ClassStatus.UNKNOWN;
   7.317 +        }
   7.318 +        // Compare successfully loaded with last compiled bytes.
   7.319 +        return (Arrays.equals(execEnv.getClassBytes(classname), ci.getLoadedBytes()))
   7.320 +                ? ClassStatus.CURRENT
   7.321 +                : ClassStatus.NOT_CURRENT;
   7.322 +    }
   7.323 +
   7.324 +    /**
   7.325 +     * Reports results from a remote agent command that does not expect
   7.326 +     * exceptions.
   7.327 +     *
   7.328 +     * @return true if successful
   7.329 +     * @throws IOException if the connection has dropped
   7.330 +     */
   7.331 +    private boolean readAndReportResult() throws IOException {
   7.332 +        int ok = remoteIn.readInt();
   7.333 +        switch (ok) {
   7.334 +            case RESULT_SUCCESS:
   7.335 +                return true;
   7.336 +            case RESULT_FAIL: {
   7.337 +                String ex = remoteIn.readUTF();
   7.338 +                debug(DBG_GEN, "Exception on remote operation: %s\n", ex);
   7.339 +                return false;
   7.340 +            }
   7.341 +            default: {
   7.342 +                debug(DBG_GEN, "Bad remote result code: %s\n", ok);
   7.343 +                return false;
   7.344 +            }
   7.345 +        }
   7.346 +    }
   7.347 +
   7.348 +    /**
   7.349 +     * Reports results from a remote agent command that expects runtime
   7.350 +     * exceptions.
   7.351 +     *
   7.352 +     * @return true if successful
   7.353 +     * @throws IOException if the connection has dropped
   7.354 +     * @throws EvalException if a user exception was encountered on invoke
   7.355 +     * @throws UnresolvedReferenceException if an unresolved reference was
   7.356 +     * encountered
   7.357 +     */
   7.358 +    private boolean readAndReportExecutionResult() throws IOException, JShellException {
   7.359 +        int ok = remoteIn.readInt();
   7.360 +        switch (ok) {
   7.361 +            case RESULT_SUCCESS:
   7.362 +                return true;
   7.363 +            case RESULT_FAIL: {
   7.364 +                // An internal error has occurred.
   7.365 +                String ex = remoteIn.readUTF();
   7.366 +                return false;
   7.367 +            }
   7.368 +            case RESULT_EXCEPTION: {
   7.369 +                // A user exception was encountered.
   7.370 +                String exceptionClassName = remoteIn.readUTF();
   7.371 +                String message = remoteIn.readUTF();
   7.372 +                StackTraceElement[] elems = readStackTrace();
   7.373 +                throw execEnv.createEvalException(message, exceptionClassName, elems);
   7.374 +            }
   7.375 +            case RESULT_CORRALLED: {
   7.376 +                // An unresolved reference was encountered.
   7.377 +                int id = remoteIn.readInt();
   7.378 +                StackTraceElement[] elems = readStackTrace();
   7.379 +                throw execEnv.createUnresolvedReferenceException(id, elems);
   7.380 +            }
   7.381 +            case RESULT_KILLED: {
   7.382 +                // Execution was aborted by the stop()
   7.383 +                debug(DBG_GEN, "Killed.");
   7.384 +                return false;
   7.385 +            }
   7.386 +            default: {
   7.387 +                debug(DBG_GEN, "Bad remote result code: %s\n", ok);
   7.388 +                return false;
   7.389 +            }
   7.390 +        }
   7.391 +    }
   7.392 +
   7.393 +    private StackTraceElement[] readStackTrace() throws IOException {
   7.394 +        int elemCount = remoteIn.readInt();
   7.395 +        StackTraceElement[] elems = new StackTraceElement[elemCount];
   7.396 +        for (int i = 0; i < elemCount; ++i) {
   7.397 +            String className = remoteIn.readUTF();
   7.398 +            String methodName = remoteIn.readUTF();
   7.399 +            String fileName = remoteIn.readUTF();
   7.400 +            int line = remoteIn.readInt();
   7.401 +            elems[i] = new StackTraceElement(className, methodName, fileName, line);
   7.402 +        }
   7.403 +        return elems;
   7.404 +    }
   7.405 +
   7.406 +    /**
   7.407 +     * Launch the remote agent as a JDI connection.
   7.408 +     *
   7.409 +     * @param port the socket port for (non-JDI) commands
   7.410 +     */
   7.411 +    private void jdiGo(int port) {
   7.412 +        //MessageOutput.textResources = ResourceBundle.getBundle("impl.TTYResources",
   7.413 +        //        Locale.getDefault());
   7.414 +
   7.415 +        // Set-up for a fresh launch of a remote agent with any user-specified VM options.
   7.416 +        String connectorName = "com.sun.jdi.CommandLineLaunch";
   7.417 +        Map<String, String> argumentName2Value = new HashMap<>();
   7.418 +        argumentName2Value.put("main", "jdk.internal.jshell.remote.RemoteAgent " + port);
   7.419 +        argumentName2Value.put("options", remoteVMOptions);
   7.420 +
   7.421 +        boolean launchImmediately = true;
   7.422 +        int traceFlags = 0;// VirtualMachine.TRACE_SENDS | VirtualMachine.TRACE_EVENTS;
   7.423 +
   7.424 +        // Launch.
   7.425 +        jdiEnv.init(connectorName, argumentName2Value, launchImmediately, traceFlags);
   7.426 +
   7.427 +        if (jdiEnv.connection().isOpen() && jdiEnv.vm().canBeModified()) {
   7.428 +            /*
   7.429 +             * Connection opened on startup. Start event handler
   7.430 +             * immediately, telling it (through arg 2) to stop on the
   7.431 +             * VM start event.
   7.432 +             */
   7.433 +            handler = new JDIEventHandler(jdiEnv);
   7.434 +        }
   7.435 +    }
   7.436 +
   7.437 +    private final Object STOP_LOCK = new Object();
   7.438 +    private boolean userCodeRunning = false;
   7.439 +
   7.440 +    /**
   7.441 +     * Interrupt a running invoke.
   7.442 +     */
   7.443 +    @Override
   7.444 +    public void stop() {
   7.445 +        synchronized (STOP_LOCK) {
   7.446 +            if (!userCodeRunning) {
   7.447 +                return;
   7.448 +            }
   7.449 +
   7.450 +            VirtualMachine vm = handler.env.vm();
   7.451 +            vm.suspend();
   7.452 +            try {
   7.453 +                OUTER:
   7.454 +                for (ThreadReference thread : vm.allThreads()) {
   7.455 +                    // could also tag the thread (e.g. using name), to find it easier
   7.456 +                    for (StackFrame frame : thread.frames()) {
   7.457 +                        String remoteAgentName = "jdk.internal.jshell.remote.RemoteAgent";
   7.458 +                        if (remoteAgentName.equals(frame.location().declaringType().name())
   7.459 +                                && "commandLoop".equals(frame.location().method().name())) {
   7.460 +                            ObjectReference thiz = frame.thisObject();
   7.461 +                            if (((BooleanValue) thiz.getValue(thiz.referenceType().fieldByName("inClientCode"))).value()) {
   7.462 +                                thiz.setValue(thiz.referenceType().fieldByName("expectingStop"), vm.mirrorOf(true));
   7.463 +                                ObjectReference stopInstance = (ObjectReference) thiz.getValue(thiz.referenceType().fieldByName("stopException"));
   7.464 +
   7.465 +                                vm.resume();
   7.466 +                                debug(DBG_GEN, "Attempting to stop the client code...\n");
   7.467 +                                thread.stop(stopInstance);
   7.468 +                                thiz.setValue(thiz.referenceType().fieldByName("expectingStop"), vm.mirrorOf(false));
   7.469 +                            }
   7.470 +
   7.471 +                            break OUTER;
   7.472 +                        }
   7.473 +                    }
   7.474 +                }
   7.475 +            } catch (ClassNotLoadedException | IncompatibleThreadStateException | InvalidTypeException ex) {
   7.476 +                debug(DBG_GEN, "Exception on remote stop: %s\n", ex);
   7.477 +            } finally {
   7.478 +                vm.resume();
   7.479 +            }
   7.480 +        }
   7.481 +    }
   7.482 +
   7.483 +    void debug(int flags, String format, Object... args) {
   7.484 +        InternalDebugControl.debug(execEnv.state(), execEnv.userErr(), flags, format, args);
   7.485 +    }
   7.486 +
   7.487 +    void debug(Exception ex, String where) {
   7.488 +        InternalDebugControl.debug(execEnv.state(), execEnv.userErr(), ex, where);
   7.489 +    }
   7.490 +
   7.491 +    private final class DemultiplexInput extends Thread {
   7.492 +
   7.493 +        private final DataInputStream delegate;
   7.494 +        private final PipeInputStream command;
   7.495 +        private final PrintStream out;
   7.496 +        private final PrintStream err;
   7.497 +
   7.498 +        public DemultiplexInput(InputStream input,
   7.499 +                PipeInputStream command,
   7.500 +                PrintStream out,
   7.501 +                PrintStream err) {
   7.502 +            super("output reader");
   7.503 +            this.delegate = new DataInputStream(input);
   7.504 +            this.command = command;
   7.505 +            this.out = out;
   7.506 +            this.err = err;
   7.507 +        }
   7.508 +
   7.509 +        public void run() {
   7.510 +            try {
   7.511 +                while (true) {
   7.512 +                    int nameLen = delegate.read();
   7.513 +                    if (nameLen == (-1))
   7.514 +                        break;
   7.515 +                    byte[] name = new byte[nameLen];
   7.516 +                    DemultiplexInput.this.delegate.readFully(name);
   7.517 +                    int dataLen = delegate.read();
   7.518 +                    byte[] data = new byte[dataLen];
   7.519 +                    DemultiplexInput.this.delegate.readFully(data);
   7.520 +                    switch (new String(name, "UTF-8")) {
   7.521 +                        case "err":
   7.522 +                            err.write(data);
   7.523 +                            break;
   7.524 +                        case "out":
   7.525 +                            out.write(data);
   7.526 +                            break;
   7.527 +                        case "command":
   7.528 +                            for (byte b : data) {
   7.529 +                                command.write(Byte.toUnsignedInt(b));
   7.530 +                            }
   7.531 +                            break;
   7.532 +                    }
   7.533 +                }
   7.534 +            } catch (IOException ex) {
   7.535 +                debug(ex, "Failed reading output");
   7.536 +            } finally {
   7.537 +                command.close();
   7.538 +            }
   7.539 +        }
   7.540 +
   7.541 +    }
   7.542 +
   7.543 +    public static final class PipeInputStream extends InputStream {
   7.544 +        public static final int INITIAL_SIZE = 128;
   7.545 +
   7.546 +        private int[] buffer = new int[INITIAL_SIZE];
   7.547 +        private int start;
   7.548 +        private int end;
   7.549 +        private boolean closed;
   7.550 +
   7.551 +        @Override
   7.552 +        public synchronized int read() {
   7.553 +            while (start == end) {
   7.554 +                if (closed) {
   7.555 +                    return -1;
   7.556 +                }
   7.557 +                try {
   7.558 +                    wait();
   7.559 +                } catch (InterruptedException ex) {
   7.560 +                    //ignore
   7.561 +                }
   7.562 +            }
   7.563 +            try {
   7.564 +                return buffer[start];
   7.565 +            } finally {
   7.566 +                start = (start + 1) % buffer.length;
   7.567 +            }
   7.568 +        }
   7.569 +
   7.570 +        public synchronized void write(int b) {
   7.571 +            if (closed)
   7.572 +                throw new IllegalStateException("Already closed.");
   7.573 +            int newEnd = (end + 1) % buffer.length;
   7.574 +            if (newEnd == start) {
   7.575 +                //overflow:
   7.576 +                int[] newBuffer = new int[buffer.length * 2];
   7.577 +                int rightPart = (end > start ? end : buffer.length) - start;
   7.578 +                int leftPart = end > start ? 0 : start - 1;
   7.579 +                System.arraycopy(buffer, start, newBuffer, 0, rightPart);
   7.580 +                System.arraycopy(buffer, 0, newBuffer, rightPart, leftPart);
   7.581 +                buffer = newBuffer;
   7.582 +                start = 0;
   7.583 +                end = rightPart + leftPart;
   7.584 +                newEnd = end + 1;
   7.585 +            }
   7.586 +            buffer[end] = b;
   7.587 +            end = newEnd;
   7.588 +            notifyAll();
   7.589 +        }
   7.590 +
   7.591 +        @Override
   7.592 +        public synchronized void close() {
   7.593 +            closed = true;
   7.594 +            notifyAll();
   7.595 +        }
   7.596 +
   7.597 +    }
   7.598 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDINotConnectedException.java	Fri Sep 02 23:54:26 2016 +0200
     8.3 @@ -0,0 +1,43 @@
     8.4 +/*
     8.5 + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
     8.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     8.7 + *
     8.8 + * This code is free software; you can redistribute it and/or modify it
     8.9 + * under the terms of the GNU General Public License version 2 only, as
    8.10 + * published by the Free Software Foundation.  Oracle designates this
    8.11 + * particular file as subject to the "Classpath" exception as provided
    8.12 + * by Oracle in the LICENSE file that accompanied this code.
    8.13 + *
    8.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    8.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    8.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    8.17 + * version 2 for more details (a copy is included in the LICENSE file that
    8.18 + * accompanied this code).
    8.19 + *
    8.20 + * You should have received a copy of the GNU General Public License version
    8.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    8.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    8.23 + *
    8.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    8.25 + * or visit www.oracle.com if you need additional information or have any
    8.26 + * questions.
    8.27 + */
    8.28 +
    8.29 +package jdk.internal.jshell.jdi;
    8.30 +
    8.31 +/**
    8.32 + * Internal exception when Java Debug Interface VirtualMacine is not connected.
    8.33 + * Copy of jdb VMNotConnectedException.
    8.34 + */
    8.35 +class JDINotConnectedException extends RuntimeException {
    8.36 +
    8.37 +    private static final long serialVersionUID = -7433430494903950165L;
    8.38 +
    8.39 +    public JDINotConnectedException() {
    8.40 +        super();
    8.41 +    }
    8.42 +
    8.43 +    public JDINotConnectedException(String s) {
    8.44 +        super(s);
    8.45 +    }
    8.46 +}
     9.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteAgent.java	Fri Jun 03 17:06:38 2016 +0200
     9.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteAgent.java	Fri Sep 02 23:54:26 2016 +0200
     9.3 @@ -24,10 +24,14 @@
     9.4   */
     9.5  
     9.6  package jdk.internal.jshell.remote;
     9.7 +import jdk.jshell.spi.SPIResolutionException;
     9.8  import java.io.File;
     9.9  import java.io.IOException;
    9.10  import java.io.ObjectInputStream;
    9.11  import java.io.ObjectOutputStream;
    9.12 +import java.io.OutputStream;
    9.13 +import java.io.PrintStream;
    9.14 +import java.io.UnsupportedEncodingException;
    9.15  import java.lang.reflect.Field;
    9.16  import java.lang.reflect.InvocationTargetException;
    9.17  import java.lang.reflect.Method;
    9.18 @@ -35,7 +39,9 @@
    9.19  
    9.20  import java.util.ArrayList;
    9.21  import java.util.List;
    9.22 +
    9.23  import static jdk.internal.jshell.remote.RemoteCodes.*;
    9.24 +
    9.25  import java.util.Map;
    9.26  import java.util.TreeMap;
    9.27  
    9.28 @@ -59,7 +65,10 @@
    9.29      void commandLoop(Socket socket) throws IOException {
    9.30          // in before out -- so we don't hang the controlling process
    9.31          ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
    9.32 -        ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
    9.33 +        OutputStream socketOut = socket.getOutputStream();
    9.34 +        System.setOut(new PrintStream(new MultiplexingOutputStream("out", socketOut), true));
    9.35 +        System.setErr(new PrintStream(new MultiplexingOutputStream("err", socketOut), true));
    9.36 +        ObjectOutputStream out = new ObjectOutputStream(new MultiplexingOutputStream("command", socketOut));
    9.37          while (true) {
    9.38              int cmd = in.readInt();
    9.39              switch (cmd) {
    9.40 @@ -103,9 +112,11 @@
    9.41                          out.flush();
    9.42                          break;
    9.43                      }
    9.44 +                    String methodName = in.readUTF();
    9.45                      Method doitMethod;
    9.46                      try {
    9.47 -                        doitMethod = klass.getDeclaredMethod(DOIT_METHOD_NAME, new Class<?>[0]);
    9.48 +                        this.getClass().getModule().addExports(SPIResolutionException.class.getPackage().getName(), klass.getModule());
    9.49 +                        doitMethod = klass.getDeclaredMethod(methodName, new Class<?>[0]);
    9.50                          doitMethod.setAccessible(true);
    9.51                          Object res;
    9.52                          try {
    9.53 @@ -129,9 +140,9 @@
    9.54                      } catch (InvocationTargetException ex) {
    9.55                          Throwable cause = ex.getCause();
    9.56                          StackTraceElement[] elems = cause.getStackTrace();
    9.57 -                        if (cause instanceof RemoteResolutionException) {
    9.58 +                        if (cause instanceof SPIResolutionException) {
    9.59                              out.writeInt(RESULT_CORRALLED);
    9.60 -                            out.writeInt(((RemoteResolutionException) cause).id);
    9.61 +                            out.writeInt(((SPIResolutionException) cause).id());
    9.62                          } else {
    9.63                              out.writeInt(RESULT_EXCEPTION);
    9.64                              out.writeUTF(cause.getClass().getName());
    9.65 @@ -245,19 +256,71 @@
    9.66          if (value == null) {
    9.67              return "null";
    9.68          } else if (value instanceof String) {
    9.69 -            return "\"" + expunge((String)value) + "\"";
    9.70 +            return "\"" + (String)value + "\"";
    9.71          } else if (value instanceof Character) {
    9.72              return "'" + value + "'";
    9.73          } else {
    9.74 -            return expunge(value.toString());
    9.75 +            return value.toString();
    9.76          }
    9.77      }
    9.78  
    9.79 -    static String expunge(String s) {
    9.80 -        StringBuilder sb = new StringBuilder();
    9.81 -        for (String comp : prefixPattern.split(s)) {
    9.82 -            sb.append(comp);
    9.83 +    private static final class MultiplexingOutputStream extends OutputStream {
    9.84 +
    9.85 +        private static final int PACKET_SIZE = 127;
    9.86 +
    9.87 +        private final byte[] name;
    9.88 +        private final OutputStream delegate;
    9.89 +
    9.90 +        public MultiplexingOutputStream(String name, OutputStream delegate) {
    9.91 +            try {
    9.92 +                this.name = name.getBytes("UTF-8");
    9.93 +                this.delegate = delegate;
    9.94 +            } catch (UnsupportedEncodingException ex) {
    9.95 +                throw new IllegalStateException(ex); //should not happen
    9.96 +            }
    9.97          }
    9.98 -        return sb.toString();
    9.99 +
   9.100 +        @Override
   9.101 +        public void write(int b) throws IOException {
   9.102 +            synchronized (delegate) {
   9.103 +                delegate.write(name.length); //assuming the len is small enough to fit into byte
   9.104 +                delegate.write(name);
   9.105 +                delegate.write(1);
   9.106 +                delegate.write(b);
   9.107 +                delegate.flush();
   9.108 +            }
   9.109 +        }
   9.110 +
   9.111 +        @Override
   9.112 +        public void write(byte[] b, int off, int len) throws IOException {
   9.113 +            synchronized (delegate) {
   9.114 +                int i = 0;
   9.115 +                while (len > 0) {
   9.116 +                    int size = Math.min(PACKET_SIZE, len);
   9.117 +
   9.118 +                    delegate.write(name.length); //assuming the len is small enough to fit into byte
   9.119 +                    delegate.write(name);
   9.120 +                    delegate.write(size);
   9.121 +                    delegate.write(b, off + i, size);
   9.122 +                    i += size;
   9.123 +                    len -= size;
   9.124 +                }
   9.125 +
   9.126 +                delegate.flush();
   9.127 +            }
   9.128 +        }
   9.129 +
   9.130 +        @Override
   9.131 +        public void flush() throws IOException {
   9.132 +            super.flush();
   9.133 +            delegate.flush();
   9.134 +        }
   9.135 +
   9.136 +        @Override
   9.137 +        public void close() throws IOException {
   9.138 +            super.close();
   9.139 +            delegate.close();
   9.140 +        }
   9.141 +
   9.142      }
   9.143  }
    10.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteCodes.java	Fri Jun 03 17:06:38 2016 +0200
    10.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteCodes.java	Fri Sep 02 23:54:26 2016 +0200
    10.3 @@ -23,11 +23,8 @@
    10.4   * questions.
    10.5   */
    10.6  
    10.7 -
    10.8  package jdk.internal.jshell.remote;
    10.9  
   10.10 -import java.util.regex.Pattern;
   10.11 -
   10.12  /**
   10.13   * Communication constants shared between the main process and the remote
   10.14   * execution process
   10.15 @@ -47,9 +44,4 @@
   10.16      public static final int RESULT_EXCEPTION = 102;
   10.17      public static final int RESULT_CORRALLED = 103;
   10.18      public static final int RESULT_KILLED    = 104;
   10.19 -
   10.20 -    public static final String DOIT_METHOD_NAME = "do_it$";
   10.21 -    public static final String replClass = "\\$REPL(?<num>\\d+)[A-Z]*";
   10.22 -    public static final Pattern prefixPattern = Pattern.compile("(REPL\\.)?" + replClass + "[\\$\\.]?");
   10.23 -
   10.24  }
    11.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteResolutionException.java	Fri Jun 03 17:06:38 2016 +0200
    11.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3 @@ -1,49 +0,0 @@
    11.4 -/*
    11.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    11.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    11.7 - *
    11.8 - * This code is free software; you can redistribute it and/or modify it
    11.9 - * under the terms of the GNU General Public License version 2 only, as
   11.10 - * published by the Free Software Foundation.  Oracle designates this
   11.11 - * particular file as subject to the "Classpath" exception as provided
   11.12 - * by Oracle in the LICENSE file that accompanied this code.
   11.13 - *
   11.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   11.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   11.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   11.17 - * version 2 for more details (a copy is included in the LICENSE file that
   11.18 - * accompanied this code).
   11.19 - *
   11.20 - * You should have received a copy of the GNU General Public License version
   11.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   11.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   11.23 - *
   11.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   11.25 - * or visit www.oracle.com if you need additional information or have any
   11.26 - * questions.
   11.27 - */
   11.28 -
   11.29 -package jdk.internal.jshell.remote;
   11.30 -
   11.31 -/**
   11.32 - * The exception thrown on the remote side upon executing a
   11.33 - * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
   11.34 - * user method. This exception is not seen by the end user nor through the API.
   11.35 - * @author Robert Field
   11.36 - */
   11.37 -@SuppressWarnings("serial")             // serialVersionUID intentionally omitted
   11.38 -public class RemoteResolutionException extends RuntimeException {
   11.39 -
   11.40 -    final int id;
   11.41 -
   11.42 -    /**
   11.43 -     * The throw of this exception is generated into the body of a
   11.44 -     * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
   11.45 -     * method.
   11.46 -     * @param id An internal identifier of the specific method
   11.47 -     */
   11.48 -    public RemoteResolutionException(int id) {
   11.49 -        super("RemoteResolutionException");
   11.50 -        this.id = id;
   11.51 -    }
   11.52 -}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ArgTokenizer.java	Fri Sep 02 23:54:26 2016 +0200
    12.3 @@ -0,0 +1,373 @@
    12.4 +/*
    12.5 + * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
    12.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    12.7 + *
    12.8 + * This code is free software; you can redistribute it and/or modify it
    12.9 + * under the terms of the GNU General Public License version 2 only, as
   12.10 + * published by the Free Software Foundation.  Oracle designates this
   12.11 + * particular file as subject to the "Classpath" exception as provided
   12.12 + * by Oracle in the LICENSE file that accompanied this code.
   12.13 + *
   12.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   12.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   12.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   12.17 + * version 2 for more details (a copy is included in the LICENSE file that
   12.18 + * accompanied this code).
   12.19 + *
   12.20 + * You should have received a copy of the GNU General Public License version
   12.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   12.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   12.23 + *
   12.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   12.25 + * or visit www.oracle.com if you need additional information or have any
   12.26 + * questions.
   12.27 + */
   12.28 +
   12.29 +package jdk.internal.jshell.tool;
   12.30 +
   12.31 +import java.util.ArrayList;
   12.32 +import java.util.Arrays;
   12.33 +import java.util.HashMap;
   12.34 +import java.util.List;
   12.35 +import java.util.Map;
   12.36 +import java.util.stream.Stream;
   12.37 +import static java.util.stream.Collectors.toList;
   12.38 +
   12.39 +/**
   12.40 + * Parse command arguments, derived from StreamTokenizer by
   12.41 + * @author  James Gosling
   12.42 + */
   12.43 +class ArgTokenizer {
   12.44 +
   12.45 +    private final String str;
   12.46 +    private final String prefix;
   12.47 +    private final int length;
   12.48 +    private int next = 0;
   12.49 +    private char buf[] = new char[20];
   12.50 +    private int mark;
   12.51 +
   12.52 +    private final byte ctype[] = new byte[256];
   12.53 +    private static final byte CT_ALPHA = 0;
   12.54 +    private static final byte CT_WHITESPACE = 1;
   12.55 +    private static final byte CT_QUOTE = 8;
   12.56 +
   12.57 +    private String sval;
   12.58 +    private boolean isQuoted = false;
   12.59 +
   12.60 +    private final Map<String, Boolean> options = new HashMap<>();
   12.61 +    private final List<String> badOptions = new ArrayList<>();
   12.62 +
   12.63 +    ArgTokenizer(String prefix, String arg) {
   12.64 +        this.str = arg;
   12.65 +        this.prefix = prefix + " ";
   12.66 +        this.length = arg.length();
   12.67 +        quoteChar('"');
   12.68 +        quoteChar('\'');
   12.69 +        whitespaceChars(0x09, 0x0D);
   12.70 +        whitespaceChars(0x1C, 0x20);
   12.71 +        whitespaceChars(0x85, 0x85);
   12.72 +        whitespaceChars(0xA0, 0xA0);
   12.73 +    }
   12.74 +
   12.75 +    /**
   12.76 +     * Return the next non-option argument. Encountered options are stored.
   12.77 +     *
   12.78 +     * @return the token string, or null if there are no more tokens
   12.79 +     */
   12.80 +    String next() {
   12.81 +        while (true) {
   12.82 +            nextToken();
   12.83 +            if (sval != null && !isQuoted() && sval.startsWith("-")) {
   12.84 +                foundOption(sval);
   12.85 +            } else {
   12.86 +                break;
   12.87 +            }
   12.88 +        }
   12.89 +        return sval;
   12.90 +    }
   12.91 +
   12.92 +    private void foundOption(String opt) {
   12.93 +        if (options.containsKey(opt)) {
   12.94 +            options.put(opt, true);
   12.95 +            return;
   12.96 +        }
   12.97 +
   12.98 +        List<Map.Entry<String,Boolean>> matches =
   12.99 +                options.entrySet()
  12.100 +                       .stream()
  12.101 +                       .filter(e -> e.getKey().startsWith(opt))
  12.102 +                       .collect(toList());
  12.103 +        if (matches.size() == 1) {
  12.104 +            matches.get(0).setValue(true);
  12.105 +        } else {
  12.106 +            badOptions.add(opt);
  12.107 +        }
  12.108 +    }
  12.109 +
  12.110 +    String[] next(String... strings) {
  12.111 +        return next(Arrays.stream(strings));
  12.112 +    }
  12.113 +
  12.114 +    String[] next(Stream<String> stream) {
  12.115 +        next();
  12.116 +        if (sval == null) {
  12.117 +            return null;
  12.118 +        }
  12.119 +        String[] matches = stream
  12.120 +                .filter(s -> s.startsWith(sval))
  12.121 +                .toArray(size -> new String[size]);
  12.122 +        return matches;
  12.123 +    }
  12.124 +
  12.125 +    /**
  12.126 +     * Set the allowed options. Must be called before any options would be read
  12.127 +     * and before calling any of the option functionality below.
  12.128 +     */
  12.129 +    void allowedOptions(String... opts) {
  12.130 +        for (String opt : opts) {
  12.131 +            options.put(opt, false);
  12.132 +        }
  12.133 +    }
  12.134 +
  12.135 +    /**
  12.136 +     * Has the specified option been encountered.
  12.137 +     *
  12.138 +     * @param opt the option to check
  12.139 +     * @return true if the option has been encountered
  12.140 +     */
  12.141 +    boolean hasOption(String opt) {
  12.142 +        Boolean has = options.get(opt);
  12.143 +        if (has == null) {
  12.144 +            throw new InternalError("hasOption called before allowedOptions or on bad option");
  12.145 +        }
  12.146 +        return has;
  12.147 +    }
  12.148 +
  12.149 +    /**
  12.150 +     * Return the number of encountered options
  12.151 +     *
  12.152 +     * @return the option count
  12.153 +     */
  12.154 +    int optionCount() {
  12.155 +        return (int) options.entrySet().stream()
  12.156 +                .filter(e -> e.getValue())
  12.157 +                .count();
  12.158 +    }
  12.159 +
  12.160 +    /**
  12.161 +     * Return the bad options encountered. Bad options are those that were not
  12.162 +     * listed in the call to allowedOptions().
  12.163 +     *
  12.164 +     * @return as space-separated list the bad options encountered, or the empty
  12.165 +     * string if none.
  12.166 +     */
  12.167 +    String badOptions() {
  12.168 +        return String.join(" ", badOptions);
  12.169 +    }
  12.170 +
  12.171 +    /**
  12.172 +     * Consume the remainder of the input. This is useful to sure all options
  12.173 +     * have been encountered and to check to unexpected additional non-option
  12.174 +     * input.
  12.175 +     *
  12.176 +     * @return the string-separated concatenation of all remaining non-option
  12.177 +     * arguments.
  12.178 +     */
  12.179 +    String remainder() {
  12.180 +        List<String> rem = new ArrayList<>();
  12.181 +        while (next() != null) {
  12.182 +            rem.add(sval);
  12.183 +        }
  12.184 +        return String.join(" ", rem);
  12.185 +    }
  12.186 +
  12.187 +    String val() {
  12.188 +        return sval;
  12.189 +    }
  12.190 +
  12.191 +    boolean isQuoted() {
  12.192 +        return isQuoted;
  12.193 +    }
  12.194 +
  12.195 +    String whole() {
  12.196 +        return prefix + str;
  12.197 +    }
  12.198 +
  12.199 +    void mark() {
  12.200 +        mark = next;
  12.201 +    }
  12.202 +
  12.203 +    void rewind() {
  12.204 +        next = mark;
  12.205 +    }
  12.206 +
  12.207 +    /**
  12.208 +     * Reads a single character.
  12.209 +     *
  12.210 +     * @return The character read, or -1 if the end of the stream has been
  12.211 +     * reached
  12.212 +     */
  12.213 +    private int read() {
  12.214 +        if (next >= length) {
  12.215 +            return -1;
  12.216 +        }
  12.217 +        return str.charAt(next++);
  12.218 +    }
  12.219 +
  12.220 +    /**
  12.221 +     * Specifies that all characters <i>c</i> in the range
  12.222 +     * <code>low&nbsp;&lt;=&nbsp;<i>c</i>&nbsp;&lt;=&nbsp;high</code>
  12.223 +     * are white space characters. White space characters serve only to
  12.224 +     * separate tokens in the input stream.
  12.225 +     *
  12.226 +     * <p>Any other attribute settings for the characters in the specified
  12.227 +     * range are cleared.
  12.228 +     *
  12.229 +     * @param   low   the low end of the range.
  12.230 +     * @param   hi    the high end of the range.
  12.231 +     */
  12.232 +    private void whitespaceChars(int low, int hi) {
  12.233 +        if (low < 0)
  12.234 +            low = 0;
  12.235 +        if (hi >= ctype.length)
  12.236 +            hi = ctype.length - 1;
  12.237 +        while (low <= hi)
  12.238 +            ctype[low++] = CT_WHITESPACE;
  12.239 +    }
  12.240 +
  12.241 +    /**
  12.242 +     * Specifies that matching pairs of this character delimit string
  12.243 +     * constants in this tokenizer.
  12.244 +     * <p>
  12.245 +     * If a string quote character is encountered, then a string is
  12.246 +     * recognized, consisting of all characters after (but not including)
  12.247 +     * the string quote character, up to (but not including) the next
  12.248 +     * occurrence of that same string quote character, or a line
  12.249 +     * terminator, or end of file. The usual escape sequences such as
  12.250 +     * {@code "\u005Cn"} and {@code "\u005Ct"} are recognized and
  12.251 +     * converted to single characters as the string is parsed.
  12.252 +     *
  12.253 +     * <p>Any other attribute settings for the specified character are cleared.
  12.254 +     *
  12.255 +     * @param   ch   the character.
  12.256 +     */
  12.257 +    private void quoteChar(int ch) {
  12.258 +        if (ch >= 0 && ch < ctype.length)
  12.259 +            ctype[ch] = CT_QUOTE;
  12.260 +    }
  12.261 +
  12.262 +    private int unicode2ctype(int c) {
  12.263 +        switch (c) {
  12.264 +            case 0x1680:
  12.265 +            case 0x180E:
  12.266 +            case 0x200A:
  12.267 +            case 0x202F:
  12.268 +            case 0x205F:
  12.269 +            case 0x3000:
  12.270 +                return CT_WHITESPACE;
  12.271 +            default:
  12.272 +                return CT_ALPHA;
  12.273 +        }
  12.274 +    }
  12.275 +
  12.276 +    /**
  12.277 +     * Parses the next token of this tokenizer.
  12.278 +     */
  12.279 +    public void nextToken() {
  12.280 +        byte ct[] = ctype;
  12.281 +        int c;
  12.282 +        int lctype;
  12.283 +        sval = null;
  12.284 +        isQuoted = false;
  12.285 +
  12.286 +        do {
  12.287 +            c = read();
  12.288 +            if (c < 0) {
  12.289 +                return;
  12.290 +            }
  12.291 +            lctype = (c < 256) ? ct[c] : unicode2ctype(c);
  12.292 +        } while (lctype == CT_WHITESPACE);
  12.293 +
  12.294 +        if (lctype == CT_ALPHA) {
  12.295 +            int i = 0;
  12.296 +            do {
  12.297 +                if (i >= buf.length) {
  12.298 +                    buf = Arrays.copyOf(buf, buf.length * 2);
  12.299 +                }
  12.300 +                buf[i++] = (char) c;
  12.301 +                c = read();
  12.302 +                lctype = c < 0 ? CT_WHITESPACE : (c < 256)? ct[c] : unicode2ctype(c);
  12.303 +            } while (lctype == CT_ALPHA);
  12.304 +            if (c >= 0) --next; // push last back
  12.305 +            sval = String.copyValueOf(buf, 0, i);
  12.306 +            return;
  12.307 +        }
  12.308 +
  12.309 +        if (lctype == CT_QUOTE) {
  12.310 +            int quote = c;
  12.311 +            int i = 0;
  12.312 +            /* Invariants (because \Octal needs a lookahead):
  12.313 +             *   (i)  c contains char value
  12.314 +             *   (ii) d contains the lookahead
  12.315 +             */
  12.316 +            int d = read();
  12.317 +            while (d >= 0 && d != quote) {
  12.318 +                if (d == '\\') {
  12.319 +                    c = read();
  12.320 +                    int first = c;   /* To allow \377, but not \477 */
  12.321 +                    if (c >= '0' && c <= '7') {
  12.322 +                        c = c - '0';
  12.323 +                        int c2 = read();
  12.324 +                        if ('0' <= c2 && c2 <= '7') {
  12.325 +                            c = (c << 3) + (c2 - '0');
  12.326 +                            c2 = read();
  12.327 +                            if ('0' <= c2 && c2 <= '7' && first <= '3') {
  12.328 +                                c = (c << 3) + (c2 - '0');
  12.329 +                                d = read();
  12.330 +                            } else
  12.331 +                                d = c2;
  12.332 +                        } else
  12.333 +                          d = c2;
  12.334 +                    } else {
  12.335 +                        switch (c) {
  12.336 +                        case 'a':
  12.337 +                            c = 0x7;
  12.338 +                            break;
  12.339 +                        case 'b':
  12.340 +                            c = '\b';
  12.341 +                            break;
  12.342 +                        case 'f':
  12.343 +                            c = 0xC;
  12.344 +                            break;
  12.345 +                        case 'n':
  12.346 +                            c = '\n';
  12.347 +                            break;
  12.348 +                        case 'r':
  12.349 +                            c = '\r';
  12.350 +                            break;
  12.351 +                        case 't':
  12.352 +                            c = '\t';
  12.353 +                            break;
  12.354 +                        case 'v':
  12.355 +                            c = 0xB;
  12.356 +                            break;
  12.357 +                        }
  12.358 +                        d = read();
  12.359 +                    }
  12.360 +                } else {
  12.361 +                    c = d;
  12.362 +                    d = read();
  12.363 +                }
  12.364 +                if (i >= buf.length) {
  12.365 +                    buf = Arrays.copyOf(buf, buf.length * 2);
  12.366 +                }
  12.367 +                buf[i++] = (char)c;
  12.368 +            }
  12.369 +
  12.370 +            if (d == quote) {
  12.371 +                isQuoted = true;
  12.372 +            }
  12.373 +            sval = String.copyValueOf(buf, 0, i);
  12.374 +        }
  12.375 +    }
  12.376 +}
    13.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java	Fri Jun 03 17:06:38 2016 +0200
    13.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java	Fri Sep 02 23:54:26 2016 +0200
    13.3 @@ -1,5 +1,5 @@
    13.4  /*
    13.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    13.6 + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
    13.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    13.8   *
    13.9   * This code is free software; you can redistribute it and/or modify it
   13.10 @@ -25,7 +25,7 @@
   13.11  
   13.12  package jdk.internal.jshell.tool;
   13.13  
   13.14 -import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
   13.15 +import jdk.jshell.SourceCodeAnalysis.QualifiedNames;
   13.16  import jdk.jshell.SourceCodeAnalysis.Suggestion;
   13.17  
   13.18  import java.awt.event.ActionListener;
   13.19 @@ -34,24 +34,36 @@
   13.20  import java.io.PrintStream;
   13.21  import java.io.UncheckedIOException;
   13.22  import java.lang.reflect.Method;
   13.23 +import java.util.ArrayList;
   13.24 +import java.util.Collection;
   13.25 +import java.util.Collections;
   13.26 +import java.util.HashMap;
   13.27  import java.util.List;
   13.28  import java.util.Locale;
   13.29 +import java.util.Map;
   13.30  import java.util.Objects;
   13.31  import java.util.Optional;
   13.32  import java.util.function.Supplier;
   13.33 +import java.util.prefs.BackingStoreException;
   13.34 +import java.util.stream.Collectors;
   13.35 +import java.util.stream.Stream;
   13.36  
   13.37  import jdk.internal.jline.NoInterruptUnixTerminal;
   13.38  import jdk.internal.jline.Terminal;
   13.39  import jdk.internal.jline.TerminalFactory;
   13.40 +import jdk.internal.jline.TerminalSupport;
   13.41  import jdk.internal.jline.WindowsTerminal;
   13.42  import jdk.internal.jline.console.ConsoleReader;
   13.43  import jdk.internal.jline.console.KeyMap;
   13.44  import jdk.internal.jline.console.UserInterruptException;
   13.45  import jdk.internal.jline.console.completer.Completer;
   13.46 +import jdk.internal.jline.extra.EditingHistory;
   13.47  import jdk.internal.jshell.tool.StopDetectingInputStream.State;
   13.48  
   13.49  class ConsoleIOContext extends IOContext {
   13.50  
   13.51 +    private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_";
   13.52 +
   13.53      final JShellTool repl;
   13.54      final StopDetectingInputStream input;
   13.55      final ConsoleReader in;
   13.56 @@ -63,7 +75,9 @@
   13.57          this.repl = repl;
   13.58          this.input = new StopDetectingInputStream(() -> repl.state.stop(), ex -> repl.hard("Error on input: %s", ex));
   13.59          Terminal term;
   13.60 -        if (System.getProperty("os.name").toLowerCase(Locale.US).contains(TerminalFactory.WINDOWS)) {
   13.61 +        if (System.getProperty("test.jdk") != null) {
   13.62 +            term = new TestTerminal(input);
   13.63 +        } else if (System.getProperty("os.name").toLowerCase(Locale.US).contains(TerminalFactory.WINDOWS)) {
   13.64              term = new JShellWindowsTerminal(input);
   13.65          } else {
   13.66              term = new JShellUnixTerminal(input);
   13.67 @@ -72,12 +86,18 @@
   13.68          in = new ConsoleReader(cmdin, cmdout, term);
   13.69          in.setExpandEvents(false);
   13.70          in.setHandleUserInterrupt(true);
   13.71 -        in.setHistory(history = new EditingHistory(JShellTool.PREFS) {
   13.72 -            @Override protected CompletionInfo analyzeCompletion(String input) {
   13.73 -                return repl.analysis.analyzeCompletion(input);
   13.74 +        List<String> persistenHistory = Stream.of(repl.prefs.keys())
   13.75 +                                              .filter(key -> key.startsWith(HISTORY_LINE_PREFIX))
   13.76 +                                              .sorted()
   13.77 +                                              .map(key -> repl.prefs.get(key, null))
   13.78 +                                              .collect(Collectors.toList());
   13.79 +        in.setHistory(history = new EditingHistory(in, persistenHistory) {
   13.80 +            @Override protected boolean isComplete(CharSequence input) {
   13.81 +                return repl.analysis.analyzeCompletion(input.toString()).completeness.isComplete;
   13.82              }
   13.83          });
   13.84          in.setBellEnabled(true);
   13.85 +        in.setCopyPasteDetection(true);
   13.86          in.addCompleter(new Completer() {
   13.87              private String lastTest;
   13.88              private int lastCursor;
   13.89 @@ -124,7 +144,7 @@
   13.90                      } catch (IOException ex) {
   13.91                          throw new IllegalStateException(ex);
   13.92                      }
   13.93 -                    result.add("<press tab to see more>");
   13.94 +                    result.add(repl.messageFormat("jshell.console.see.more"));
   13.95                      return cursor; //anchor should not be used.
   13.96                  }
   13.97  
   13.98 @@ -142,8 +162,11 @@
   13.99              }
  13.100          });
  13.101          bind(DOCUMENTATION_SHORTCUT, (ActionListener) evt -> documentation(repl));
  13.102 -        bind(CTRL_UP, (ActionListener) evt -> moveHistoryToSnippet(((EditingHistory) in.getHistory())::previousSnippet));
  13.103 -        bind(CTRL_DOWN, (ActionListener) evt -> moveHistoryToSnippet(((EditingHistory) in.getHistory())::nextSnippet));
  13.104 +        for (FixComputer computer : FIX_COMPUTERS) {
  13.105 +            for (String shortcuts : SHORTCUT_FIXES) {
  13.106 +                bind(shortcuts + computer.shortcut, (ActionListener) evt -> fixes(computer));
  13.107 +            }
  13.108 +        }
  13.109      }
  13.110  
  13.111      @Override
  13.112 @@ -168,7 +191,24 @@
  13.113  
  13.114      @Override
  13.115      public void close() throws IOException {
  13.116 -        history.save();
  13.117 +        //save history:
  13.118 +        try {
  13.119 +            for (String key : repl.prefs.keys()) {
  13.120 +                if (key.startsWith(HISTORY_LINE_PREFIX))
  13.121 +                    repl.prefs.remove(key);
  13.122 +            }
  13.123 +            Collection<? extends String> savedHistory = history.save();
  13.124 +            if (!savedHistory.isEmpty()) {
  13.125 +                int len = (int) Math.ceil(Math.log10(savedHistory.size()+1));
  13.126 +                String format = HISTORY_LINE_PREFIX + "%0" + len + "d";
  13.127 +                int index = 0;
  13.128 +                for (String historyLine : savedHistory) {
  13.129 +                    repl.prefs.put(String.format(format, index++), historyLine);
  13.130 +                }
  13.131 +            }
  13.132 +        } catch (BackingStoreException ex) {
  13.133 +            throw new IllegalStateException(ex);
  13.134 +        }
  13.135          in.shutdown();
  13.136          try {
  13.137              in.getTerminal().restore();
  13.138 @@ -177,30 +217,6 @@
  13.139          }
  13.140      }
  13.141  
  13.142 -    private void moveHistoryToSnippet(Supplier<Boolean> action) {
  13.143 -        if (!action.get()) {
  13.144 -            try {
  13.145 -                in.beep();
  13.146 -            } catch (IOException ex) {
  13.147 -                throw new IllegalStateException(ex);
  13.148 -            }
  13.149 -        } else {
  13.150 -            try {
  13.151 -                //could use:
  13.152 -                //in.resetPromptLine(in.getPrompt(), in.getHistory().current().toString(), -1);
  13.153 -                //but that would mean more re-writing on the screen, (and prints an additional
  13.154 -                //empty line), so using setBuffer directly:
  13.155 -                Method setBuffer = in.getClass().getDeclaredMethod("setBuffer", String.class);
  13.156 -
  13.157 -                setBuffer.setAccessible(true);
  13.158 -                setBuffer.invoke(in, in.getHistory().current().toString());
  13.159 -                in.flush();
  13.160 -            } catch (ReflectiveOperationException | IOException ex) {
  13.161 -                throw new IllegalStateException(ex);
  13.162 -            }
  13.163 -        }
  13.164 -    }
  13.165 -
  13.166      private void bind(String shortcut, Object action) {
  13.167          KeyMap km = in.getKeys();
  13.168          for (int i = 0; i < shortcut.length(); i++) {
  13.169 @@ -214,8 +230,11 @@
  13.170      }
  13.171  
  13.172      private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB
  13.173 -    private static final String CTRL_UP = "\033\133\061\073\065\101"; //Ctrl-UP
  13.174 -    private static final String CTRL_DOWN = "\033\133\061\073\065\102"; //Ctrl-DOWN
  13.175 +    private static final String[] SHORTCUT_FIXES = {
  13.176 +        "\033\015", //Alt-Enter (Linux)
  13.177 +        "\033\133\061\067\176", //F6/Alt-F1 (Mac)
  13.178 +        "\u001BO3P" //Alt-F1 (Linux)
  13.179 +    };
  13.180  
  13.181      private void documentation(JShellTool repl) {
  13.182          String buffer = in.getCursorBuffer().buffer.toString();
  13.183 @@ -290,6 +309,185 @@
  13.184          history.fullHistoryReplace(source);
  13.185      }
  13.186  
  13.187 +    //compute possible options/Fixes based on the selected FixComputer, present them to the user,
  13.188 +    //and perform the selected one:
  13.189 +    private void fixes(FixComputer computer) {
  13.190 +        String input = prefix + in.getCursorBuffer().toString();
  13.191 +        int cursor = prefix.length() + in.getCursorBuffer().cursor;
  13.192 +        FixResult candidates = computer.compute(repl, input, cursor);
  13.193 +
  13.194 +        try {
  13.195 +            final boolean printError = candidates.error != null && !candidates.error.isEmpty();
  13.196 +            if (printError) {
  13.197 +                in.println(candidates.error);
  13.198 +            }
  13.199 +            if (candidates.fixes.isEmpty()) {
  13.200 +                in.beep();
  13.201 +                if (printError) {
  13.202 +                    in.redrawLine();
  13.203 +                    in.flush();
  13.204 +                }
  13.205 +            } else if (candidates.fixes.size() == 1 && !computer.showMenu) {
  13.206 +                if (printError) {
  13.207 +                    in.redrawLine();
  13.208 +                    in.flush();
  13.209 +                }
  13.210 +                candidates.fixes.get(0).perform(in);
  13.211 +            } else {
  13.212 +                List<Fix> fixes = new ArrayList<>(candidates.fixes);
  13.213 +                fixes.add(0, new Fix() {
  13.214 +                    @Override
  13.215 +                    public String displayName() {
  13.216 +                        return repl.messageFormat("jshell.console.do.nothing");
  13.217 +                    }
  13.218 +
  13.219 +                    @Override
  13.220 +                    public void perform(ConsoleReader in) throws IOException {
  13.221 +                        in.redrawLine();
  13.222 +                    }
  13.223 +                });
  13.224 +
  13.225 +                Map<Character, Fix> char2Fix = new HashMap<>();
  13.226 +                in.println();
  13.227 +                for (int i = 0; i < fixes.size(); i++) {
  13.228 +                    Fix fix = fixes.get(i);
  13.229 +                    char2Fix.put((char) ('0' + i), fix);
  13.230 +                    in.println("" + i + ": " + fixes.get(i).displayName());
  13.231 +                }
  13.232 +                in.print(repl.messageFormat("jshell.console.choice"));
  13.233 +                in.flush();
  13.234 +                int read;
  13.235 +
  13.236 +                read = in.readCharacter();
  13.237 +
  13.238 +                Fix fix = char2Fix.get((char) read);
  13.239 +
  13.240 +                if (fix == null) {
  13.241 +                    in.beep();
  13.242 +                    fix = fixes.get(0);
  13.243 +                }
  13.244 +
  13.245 +                in.println();
  13.246 +
  13.247 +                fix.perform(in);
  13.248 +
  13.249 +                in.flush();
  13.250 +            }
  13.251 +        } catch (IOException ex) {
  13.252 +            ex.printStackTrace();
  13.253 +        }
  13.254 +    }
  13.255 +
  13.256 +    /**
  13.257 +     * A possible action which the user can choose to perform.
  13.258 +     */
  13.259 +    public interface Fix {
  13.260 +        /**
  13.261 +         * A name that should be shown to the user.
  13.262 +         */
  13.263 +        public String displayName();
  13.264 +        /**
  13.265 +         * Perform the given action.
  13.266 +         */
  13.267 +        public void perform(ConsoleReader in) throws IOException;
  13.268 +    }
  13.269 +
  13.270 +    /**
  13.271 +     * A factory for {@link Fix}es.
  13.272 +     */
  13.273 +    public abstract static class FixComputer {
  13.274 +        private final char shortcut;
  13.275 +        private final boolean showMenu;
  13.276 +
  13.277 +        /**
  13.278 +         * Construct a new FixComputer. {@code shortcut} defines the key which should trigger this FixComputer.
  13.279 +         * If {@code showMenu} is {@code false}, and this computer returns exactly one {@code Fix},
  13.280 +         * no options will be show to the user, and the given {@code Fix} will be performed.
  13.281 +         */
  13.282 +        public FixComputer(char shortcut, boolean showMenu) {
  13.283 +            this.shortcut = shortcut;
  13.284 +            this.showMenu = showMenu;
  13.285 +        }
  13.286 +
  13.287 +        /**
  13.288 +         * Compute possible actions for the given code.
  13.289 +         */
  13.290 +        public abstract FixResult compute(JShellTool repl, String code, int cursor);
  13.291 +    }
  13.292 +
  13.293 +    /**
  13.294 +     * A list of {@code Fix}es with a possible error that should be shown to the user.
  13.295 +     */
  13.296 +    public static class FixResult {
  13.297 +        public final List<Fix> fixes;
  13.298 +        public final String error;
  13.299 +
  13.300 +        public FixResult(List<Fix> fixes, String error) {
  13.301 +            this.fixes = fixes;
  13.302 +            this.error = error;
  13.303 +        }
  13.304 +    }
  13.305 +
  13.306 +    private static final FixComputer[] FIX_COMPUTERS = new FixComputer[] {
  13.307 +        new FixComputer('v', false) { //compute "Introduce variable" Fix:
  13.308 +            @Override
  13.309 +            public FixResult compute(JShellTool repl, String code, int cursor) {
  13.310 +                String type = repl.analysis.analyzeType(code, cursor);
  13.311 +                if (type == null) {
  13.312 +                    return new FixResult(Collections.emptyList(), null);
  13.313 +                }
  13.314 +                return new FixResult(Collections.singletonList(new Fix() {
  13.315 +                    @Override
  13.316 +                    public String displayName() {
  13.317 +                        return repl.messageFormat("jshell.console.create.variable");
  13.318 +                    }
  13.319 +                    @Override
  13.320 +                    public void perform(ConsoleReader in) throws IOException {
  13.321 +                        in.redrawLine();
  13.322 +                        in.setCursorPosition(0);
  13.323 +                        in.putString(type + "  = ");
  13.324 +                        in.setCursorPosition(in.getCursorBuffer().cursor - 3);
  13.325 +                        in.flush();
  13.326 +                    }
  13.327 +                }), null);
  13.328 +            }
  13.329 +        },
  13.330 +        new FixComputer('i', true) { //compute "Add import" Fixes:
  13.331 +            @Override
  13.332 +            public FixResult compute(JShellTool repl, String code, int cursor) {
  13.333 +                QualifiedNames res = repl.analysis.listQualifiedNames(code, cursor);
  13.334 +                List<Fix> fixes = new ArrayList<>();
  13.335 +                for (String fqn : res.getNames()) {
  13.336 +                    fixes.add(new Fix() {
  13.337 +                        @Override
  13.338 +                        public String displayName() {
  13.339 +                            return "import: " + fqn;
  13.340 +                        }
  13.341 +                        @Override
  13.342 +                        public void perform(ConsoleReader in) throws IOException {
  13.343 +                            repl.state.eval("import " + fqn + ";");
  13.344 +                            in.println("Imported: " + fqn);
  13.345 +                            in.redrawLine();
  13.346 +                        }
  13.347 +                    });
  13.348 +                }
  13.349 +                if (res.isResolvable()) {
  13.350 +                    return new FixResult(Collections.emptyList(),
  13.351 +                            repl.messageFormat("jshell.console.resolvable"));
  13.352 +                } else {
  13.353 +                    String error = "";
  13.354 +                    if (fixes.isEmpty()) {
  13.355 +                        error = repl.messageFormat("jshell.console.no.candidate");
  13.356 +                    }
  13.357 +                    if (!res.isUpToDate()) {
  13.358 +                        error += repl.messageFormat("jshell.console.incomplete");
  13.359 +                    }
  13.360 +                    return new FixResult(fixes, error);
  13.361 +                }
  13.362 +            }
  13.363 +        }
  13.364 +    };
  13.365 +
  13.366      private static final class JShellUnixTerminal extends NoInterruptUnixTerminal {
  13.367  
  13.368          private final StopDetectingInputStream input;
  13.369 @@ -341,4 +539,22 @@
  13.370          }
  13.371  
  13.372      }
  13.373 +
  13.374 +    private static final class TestTerminal extends TerminalSupport {
  13.375 +
  13.376 +        private final StopDetectingInputStream input;
  13.377 +
  13.378 +        public TestTerminal(StopDetectingInputStream input) throws Exception {
  13.379 +            super(true);
  13.380 +            setAnsiSupported(false);
  13.381 +            setEchoEnabled(true);
  13.382 +            this.input = input;
  13.383 +        }
  13.384 +
  13.385 +        @Override
  13.386 +        public InputStream wrapInIfNeeded(InputStream in) throws IOException {
  13.387 +            return input.setInputStream(super.wrapInIfNeeded(in));
  13.388 +        }
  13.389 +
  13.390 +    }
  13.391  }
    14.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/EditingHistory.java	Fri Jun 03 17:06:38 2016 +0200
    14.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.3 @@ -1,381 +0,0 @@
    14.4 -/*
    14.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    14.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    14.7 - *
    14.8 - * This code is free software; you can redistribute it and/or modify it
    14.9 - * under the terms of the GNU General Public License version 2 only, as
   14.10 - * published by the Free Software Foundation.  Oracle designates this
   14.11 - * particular file as subject to the "Classpath" exception as provided
   14.12 - * by Oracle in the LICENSE file that accompanied this code.
   14.13 - *
   14.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   14.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   14.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14.17 - * version 2 for more details (a copy is included in the LICENSE file that
   14.18 - * accompanied this code).
   14.19 - *
   14.20 - * You should have received a copy of the GNU General Public License version
   14.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   14.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   14.23 - *
   14.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   14.25 - * or visit www.oracle.com if you need additional information or have any
   14.26 - * questions.
   14.27 - */
   14.28 -package jdk.internal.jshell.tool;
   14.29 -
   14.30 -import java.util.ArrayList;
   14.31 -import java.util.HashSet;
   14.32 -import java.util.Iterator;
   14.33 -import java.util.List;
   14.34 -import java.util.ListIterator;
   14.35 -import java.util.Set;
   14.36 -import java.util.prefs.BackingStoreException;
   14.37 -import java.util.prefs.Preferences;
   14.38 -import java.util.stream.Collectors;
   14.39 -import java.util.stream.Stream;
   14.40 -
   14.41 -import jdk.internal.jline.console.history.History;
   14.42 -import jdk.internal.jline.console.history.History.Entry;
   14.43 -import jdk.internal.jline.console.history.MemoryHistory;
   14.44 -import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
   14.45 -
   14.46 -/*Public for tests (HistoryTest).
   14.47 - */
   14.48 -public abstract class EditingHistory implements History {
   14.49 -
   14.50 -    private final Preferences prefs;
   14.51 -    private final History fullHistory;
   14.52 -    private History currentDelegate;
   14.53 -
   14.54 -    protected EditingHistory(Preferences prefs) {
   14.55 -        this.prefs = prefs;
   14.56 -        this.fullHistory = new MemoryHistory();
   14.57 -        this.currentDelegate = fullHistory;
   14.58 -        load();
   14.59 -    }
   14.60 -
   14.61 -    @Override
   14.62 -    public int size() {
   14.63 -        return currentDelegate.size();
   14.64 -    }
   14.65 -
   14.66 -    @Override
   14.67 -    public boolean isEmpty() {
   14.68 -        return currentDelegate.isEmpty();
   14.69 -    }
   14.70 -
   14.71 -    @Override
   14.72 -    public int index() {
   14.73 -        return currentDelegate.index();
   14.74 -    }
   14.75 -
   14.76 -    @Override
   14.77 -    public void clear() {
   14.78 -        if (currentDelegate != fullHistory)
   14.79 -            throw new IllegalStateException("narrowed");
   14.80 -        currentDelegate.clear();
   14.81 -    }
   14.82 -
   14.83 -    @Override
   14.84 -    public CharSequence get(int index) {
   14.85 -        return currentDelegate.get(index);
   14.86 -    }
   14.87 -
   14.88 -    @Override
   14.89 -    public void add(CharSequence line) {
   14.90 -        NarrowingHistoryLine currentLine = null;
   14.91 -        int origIndex = fullHistory.index();
   14.92 -        int fullSize;
   14.93 -        try {
   14.94 -            fullHistory.moveToEnd();
   14.95 -            fullSize = fullHistory.index();
   14.96 -            if (currentDelegate == fullHistory) {
   14.97 -                if (origIndex < fullHistory.index()) {
   14.98 -                    for (Entry entry : fullHistory) {
   14.99 -                        if (!(entry.value() instanceof NarrowingHistoryLine))
  14.100 -                            continue;
  14.101 -                        int[] cluster = ((NarrowingHistoryLine) entry.value()).span;
  14.102 -                        if (cluster[0] == origIndex && cluster[1] > cluster[0]) {
  14.103 -                            currentDelegate = new MemoryHistory();
  14.104 -                            for (int i = cluster[0]; i <= cluster[1]; i++) {
  14.105 -                                currentDelegate.add(fullHistory.get(i));
  14.106 -                            }
  14.107 -                        }
  14.108 -                    }
  14.109 -                }
  14.110 -            }
  14.111 -            fullHistory.moveToEnd();
  14.112 -            while (fullHistory.previous()) {
  14.113 -                CharSequence c = fullHistory.current();
  14.114 -                if (c instanceof NarrowingHistoryLine) {
  14.115 -                    currentLine = (NarrowingHistoryLine) c;
  14.116 -                    break;
  14.117 -                }
  14.118 -            }
  14.119 -        } finally {
  14.120 -            fullHistory.moveTo(origIndex);
  14.121 -        }
  14.122 -        if (currentLine == null || currentLine.span[1] != (-1)) {
  14.123 -            line = currentLine = new NarrowingHistoryLine(line, fullSize);
  14.124 -        }
  14.125 -        StringBuilder complete = new StringBuilder();
  14.126 -        for (int i = currentLine.span[0]; i < fullSize; i++) {
  14.127 -            complete.append(fullHistory.get(i));
  14.128 -        }
  14.129 -        complete.append(line);
  14.130 -        if (analyzeCompletion(complete.toString()).completeness.isComplete) {
  14.131 -            currentLine.span[1] = fullSize; //TODO: +1?
  14.132 -            currentDelegate = fullHistory;
  14.133 -        }
  14.134 -        fullHistory.add(line);
  14.135 -    }
  14.136 -
  14.137 -    protected abstract CompletionInfo analyzeCompletion(String input);
  14.138 -
  14.139 -    @Override
  14.140 -    public void set(int index, CharSequence item) {
  14.141 -        if (currentDelegate != fullHistory)
  14.142 -            throw new IllegalStateException("narrowed");
  14.143 -        currentDelegate.set(index, item);
  14.144 -    }
  14.145 -
  14.146 -    @Override
  14.147 -    public CharSequence remove(int i) {
  14.148 -        if (currentDelegate != fullHistory)
  14.149 -            throw new IllegalStateException("narrowed");
  14.150 -        return currentDelegate.remove(i);
  14.151 -    }
  14.152 -
  14.153 -    @Override
  14.154 -    public CharSequence removeFirst() {
  14.155 -        if (currentDelegate != fullHistory)
  14.156 -            throw new IllegalStateException("narrowed");
  14.157 -        return currentDelegate.removeFirst();
  14.158 -    }
  14.159 -
  14.160 -    @Override
  14.161 -    public CharSequence removeLast() {
  14.162 -        if (currentDelegate != fullHistory)
  14.163 -            throw new IllegalStateException("narrowed");
  14.164 -        return currentDelegate.removeLast();
  14.165 -    }
  14.166 -
  14.167 -    @Override
  14.168 -    public void replace(CharSequence item) {
  14.169 -        if (currentDelegate != fullHistory)
  14.170 -            throw new IllegalStateException("narrowed");
  14.171 -        currentDelegate.replace(item);
  14.172 -    }
  14.173 -
  14.174 -    @Override
  14.175 -    public ListIterator<Entry> entries(int index) {
  14.176 -        return currentDelegate.entries(index);
  14.177 -    }
  14.178 -
  14.179 -    @Override
  14.180 -    public ListIterator<Entry> entries() {
  14.181 -        return currentDelegate.entries();
  14.182 -    }
  14.183 -
  14.184 -    @Override
  14.185 -    public Iterator<Entry> iterator() {
  14.186 -        return currentDelegate.iterator();
  14.187 -    }
  14.188 -
  14.189 -    @Override
  14.190 -    public CharSequence current() {
  14.191 -        return currentDelegate.current();
  14.192 -    }
  14.193 -
  14.194 -    @Override
  14.195 -    public boolean previous() {
  14.196 -        return currentDelegate.previous();
  14.197 -    }
  14.198 -
  14.199 -    @Override
  14.200 -    public boolean next() {
  14.201 -        return currentDelegate.next();
  14.202 -    }
  14.203 -
  14.204 -    @Override
  14.205 -    public boolean moveToFirst() {
  14.206 -        return currentDelegate.moveToFirst();
  14.207 -    }
  14.208 -
  14.209 -    @Override
  14.210 -    public boolean moveToLast() {
  14.211 -        return currentDelegate.moveToLast();
  14.212 -    }
  14.213 -
  14.214 -    @Override
  14.215 -    public boolean moveTo(int index) {
  14.216 -        return currentDelegate.moveTo(index);
  14.217 -    }
  14.218 -
  14.219 -    @Override
  14.220 -    public void moveToEnd() {
  14.221 -        currentDelegate.moveToEnd();
  14.222 -    }
  14.223 -
  14.224 -    public boolean previousSnippet() {
  14.225 -        for (int i = index() - 1; i >= 0; i--) {
  14.226 -            if (get(i) instanceof NarrowingHistoryLine) {
  14.227 -                moveTo(i);
  14.228 -                return true;
  14.229 -            }
  14.230 -        }
  14.231 -
  14.232 -        return false;
  14.233 -    }
  14.234 -
  14.235 -    public boolean nextSnippet() {
  14.236 -        for (int i = index() + 1; i < size(); i++) {
  14.237 -            if (get(i) instanceof NarrowingHistoryLine) {
  14.238 -                moveTo(i);
  14.239 -                return true;
  14.240 -            }
  14.241 -        }
  14.242 -
  14.243 -        if (index() < size()) {
  14.244 -            moveToEnd();
  14.245 -            return true;
  14.246 -        }
  14.247 -
  14.248 -        return false;
  14.249 -    }
  14.250 -
  14.251 -    private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_";
  14.252 -    private static final String HISTORY_SNIPPET_START = "HISTORY_SNIPPET";
  14.253 -
  14.254 -    public final void load() {
  14.255 -        try {
  14.256 -            Set<Integer> snippetsStart = new HashSet<>();
  14.257 -            for (String start : prefs.get(HISTORY_SNIPPET_START, "").split(";")) {
  14.258 -                if (!start.isEmpty())
  14.259 -                    snippetsStart.add(Integer.parseInt(start));
  14.260 -            }
  14.261 -            List<String> keys = Stream.of(prefs.keys()).sorted().collect(Collectors.toList());
  14.262 -            NarrowingHistoryLine currentHistoryLine = null;
  14.263 -            int currentLine = 0;
  14.264 -            for (String key : keys) {
  14.265 -                if (!key.startsWith(HISTORY_LINE_PREFIX))
  14.266 -                    continue;
  14.267 -                CharSequence line = prefs.get(key, "");
  14.268 -                if (snippetsStart.contains(currentLine)) {
  14.269 -                    class PersistentNarrowingHistoryLine extends NarrowingHistoryLine implements PersistentEntryMarker {
  14.270 -                        public PersistentNarrowingHistoryLine(CharSequence delegate, int start) {
  14.271 -                            super(delegate, start);
  14.272 -                        }
  14.273 -                    }
  14.274 -                    line = currentHistoryLine = new PersistentNarrowingHistoryLine(line, currentLine);
  14.275 -                } else {
  14.276 -                    class PersistentLine implements CharSequence, PersistentEntryMarker {
  14.277 -                        private final CharSequence delegate;
  14.278 -                        public PersistentLine(CharSequence delegate) {
  14.279 -                            this.delegate = delegate;
  14.280 -                        }
  14.281 -                        @Override public int length() {
  14.282 -                            return delegate.length();
  14.283 -                        }
  14.284 -                        @Override public char charAt(int index) {
  14.285 -                            return delegate.charAt(index);
  14.286 -                        }
  14.287 -                        @Override public CharSequence subSequence(int start, int end) {
  14.288 -                            return delegate.subSequence(start, end);
  14.289 -                        }
  14.290 -                        @Override public String toString() {
  14.291 -                            return delegate.toString();
  14.292 -                        }
  14.293 -                    }
  14.294 -                    line = new PersistentLine(line);
  14.295 -                }
  14.296 -                if (currentHistoryLine != null)
  14.297 -                    currentHistoryLine.span[1] = currentLine;
  14.298 -                currentLine++;
  14.299 -                fullHistory.add(line);
  14.300 -            }
  14.301 -            currentLine = 0;
  14.302 -        } catch (BackingStoreException ex) {
  14.303 -            throw new IllegalStateException(ex);
  14.304 -        }
  14.305 -    }
  14.306 -
  14.307 -    public void save() {
  14.308 -        try {
  14.309 -            for (String key : prefs.keys()) {
  14.310 -                if (key.startsWith(HISTORY_LINE_PREFIX))
  14.311 -                    prefs.remove(key);
  14.312 -            }
  14.313 -            Iterator<Entry> entries = fullHistory.iterator();
  14.314 -            if (entries.hasNext()) {
  14.315 -                int len = (int) Math.ceil(Math.log10(fullHistory.size()+1));
  14.316 -                String format = HISTORY_LINE_PREFIX + "%0" + len + "d";
  14.317 -                StringBuilder snippetStarts = new StringBuilder();
  14.318 -                String snippetStartDelimiter = "";
  14.319 -                while (entries.hasNext()) {
  14.320 -                    Entry entry = entries.next();
  14.321 -                    prefs.put(String.format(format, entry.index()), entry.value().toString());
  14.322 -                    if (entry.value() instanceof NarrowingHistoryLine) {
  14.323 -                        snippetStarts.append(snippetStartDelimiter);
  14.324 -                        snippetStarts.append(entry.index());
  14.325 -                        snippetStartDelimiter = ";";
  14.326 -                    }
  14.327 -                }
  14.328 -                prefs.put(HISTORY_SNIPPET_START, snippetStarts.toString());
  14.329 -            }
  14.330 -        } catch (BackingStoreException ex) {
  14.331 -            throw new IllegalStateException(ex);
  14.332 -        }
  14.333 -    }
  14.334 -
  14.335 -    public List<String> currentSessionEntries() {
  14.336 -        List<String> result = new ArrayList<>();
  14.337 -
  14.338 -        for (Entry e : fullHistory) {
  14.339 -            if (!(e.value() instanceof PersistentEntryMarker)) {
  14.340 -                result.add(e.value().toString());
  14.341 -            }
  14.342 -        }
  14.343 -
  14.344 -        return result;
  14.345 -    }
  14.346 -
  14.347 -    void fullHistoryReplace(String source) {
  14.348 -        fullHistory.replace(source);
  14.349 -    }
  14.350 -
  14.351 -    private class NarrowingHistoryLine implements CharSequence {
  14.352 -        private final CharSequence delegate;
  14.353 -        private final int[] span;
  14.354 -
  14.355 -        public NarrowingHistoryLine(CharSequence delegate, int start) {
  14.356 -            this.delegate = delegate;
  14.357 -            this.span = new int[] {start, -1};
  14.358 -        }
  14.359 -
  14.360 -        @Override
  14.361 -        public int length() {
  14.362 -            return delegate.length();
  14.363 -        }
  14.364 -
  14.365 -        @Override
  14.366 -        public char charAt(int index) {
  14.367 -            return delegate.charAt(index);
  14.368 -        }
  14.369 -
  14.370 -        @Override
  14.371 -        public CharSequence subSequence(int start, int end) {
  14.372 -            return delegate.subSequence(start, end);
  14.373 -        }
  14.374 -
  14.375 -        @Override
  14.376 -        public String toString() {
  14.377 -            return delegate.toString();
  14.378 -        }
  14.379 -
  14.380 -    }
  14.381 -
  14.382 -    private interface PersistentEntryMarker {}
  14.383 -}
  14.384 -
    15.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ExternalEditor.java	Fri Jun 03 17:06:38 2016 +0200
    15.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ExternalEditor.java	Fri Sep 02 23:54:26 2016 +0200
    15.3 @@ -33,6 +33,7 @@
    15.4  import java.nio.file.Path;
    15.5  import java.nio.file.WatchKey;
    15.6  import java.nio.file.WatchService;
    15.7 +import java.util.Arrays;
    15.8  import java.util.function.Consumer;
    15.9  import java.util.stream.Collectors;
   15.10  import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
   15.11 @@ -58,7 +59,7 @@
   15.12          this.input = input;
   15.13      }
   15.14  
   15.15 -    private void edit(String cmd, String initialText) {
   15.16 +    private void edit(String[] cmd, String initialText) {
   15.17          try {
   15.18              setupWatch(initialText);
   15.19              launch(cmd);
   15.20 @@ -106,8 +107,10 @@
   15.21          watchedThread.start();
   15.22      }
   15.23  
   15.24 -    private void launch(String cmd) throws IOException {
   15.25 -        ProcessBuilder pb = new ProcessBuilder(cmd, tmpfile.toString());
   15.26 +    private void launch(String[] cmd) throws IOException {
   15.27 +        String[] params = Arrays.copyOf(cmd, cmd.length + 1);
   15.28 +        params[cmd.length] = tmpfile.toString();
   15.29 +        ProcessBuilder pb = new ProcessBuilder(params);
   15.30          pb = pb.inheritIO();
   15.31  
   15.32          try {
   15.33 @@ -139,7 +142,7 @@
   15.34          }
   15.35      }
   15.36  
   15.37 -    static void edit(String cmd, Consumer<String> errorHandler, String initialText,
   15.38 +    static void edit(String[] cmd, Consumer<String> errorHandler, String initialText,
   15.39              Consumer<String> saveHandler, IOContext input) {
   15.40          ExternalEditor ed = new ExternalEditor(errorHandler,  saveHandler, input);
   15.41          ed.edit(cmd, initialText);
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java	Fri Sep 02 23:54:26 2016 +0200
    16.3 @@ -0,0 +1,1098 @@
    16.4 +/*
    16.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    16.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    16.7 + *
    16.8 + * This code is free software; you can redistribute it and/or modify it
    16.9 + * under the terms of the GNU General Public License version 2 only, as
   16.10 + * published by the Free Software Foundation.  Oracle designates this
   16.11 + * particular file as subject to the "Classpath" exception as provided
   16.12 + * by Oracle in the LICENSE file that accompanied this code.
   16.13 + *
   16.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   16.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   16.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   16.17 + * version 2 for more details (a copy is included in the LICENSE file that
   16.18 + * accompanied this code).
   16.19 + *
   16.20 + * You should have received a copy of the GNU General Public License version
   16.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   16.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   16.23 + *
   16.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   16.25 + * or visit www.oracle.com if you need additional information or have any
   16.26 + * questions.
   16.27 + */
   16.28 +
   16.29 +package jdk.internal.jshell.tool;
   16.30 +
   16.31 +import java.util.ArrayList;
   16.32 +import java.util.Arrays;
   16.33 +import java.util.Collection;
   16.34 +import java.util.EnumSet;
   16.35 +import java.util.HashMap;
   16.36 +import java.util.Iterator;
   16.37 +import java.util.List;
   16.38 +import java.util.Locale;
   16.39 +import java.util.Map;
   16.40 +import java.util.Map.Entry;
   16.41 +import java.util.regex.Matcher;
   16.42 +import java.util.regex.Pattern;
   16.43 +import static java.util.stream.Collectors.joining;
   16.44 +
   16.45 +/**
   16.46 + * Feedback customization support
   16.47 + *
   16.48 + * @author Robert Field
   16.49 + */
   16.50 +class Feedback {
   16.51 +
   16.52 +    // Patern for substituted fields within a customized format string
   16.53 +    private static final Pattern FIELD_PATTERN = Pattern.compile("\\{(.*?)\\}");
   16.54 +
   16.55 +    // Internal field name for truncation length
   16.56 +    private static final String TRUNCATION_FIELD = "<truncation>";
   16.57 +
   16.58 +    // For encoding to Properties String
   16.59 +    private static final String RECORD_SEPARATOR = "\u241E";
   16.60 +
   16.61 +    // Current mode -- initial value is placeholder during start-up
   16.62 +    private Mode mode = new Mode("");
   16.63 +
   16.64 +    // Retained current mode -- for checks
   16.65 +    private Mode retainedCurrentMode = null;
   16.66 +
   16.67 +    // Mapping of mode name to mode
   16.68 +    private final Map<String, Mode> modeMap = new HashMap<>();
   16.69 +
   16.70 +    // Mapping of mode names to encoded retained mode
   16.71 +    private final Map<String, String> retainedMap = new HashMap<>();
   16.72 +
   16.73 +    // Mapping selector enum names to enums
   16.74 +    private final Map<String, Selector<?>> selectorMap = new HashMap<>();
   16.75 +
   16.76 +    private static final long ALWAYS = bits(FormatCase.all, FormatAction.all, FormatWhen.all,
   16.77 +            FormatResolve.all, FormatUnresolved.all, FormatErrors.all);
   16.78 +    private static final long ANY = 0L;
   16.79 +
   16.80 +    public boolean shouldDisplayCommandFluff() {
   16.81 +        return mode.commandFluff;
   16.82 +    }
   16.83 +
   16.84 +    public String getPre() {
   16.85 +        return mode.format("pre", ANY);
   16.86 +    }
   16.87 +
   16.88 +    public String getPost() {
   16.89 +        return mode.format("post", ANY);
   16.90 +    }
   16.91 +
   16.92 +    public String getErrorPre() {
   16.93 +        return mode.format("errorpre", ANY);
   16.94 +    }
   16.95 +
   16.96 +    public String getErrorPost() {
   16.97 +        return mode.format("errorpost", ANY);
   16.98 +    }
   16.99 +
  16.100 +    public String format(FormatCase fc, FormatAction fa, FormatWhen fw,
  16.101 +                    FormatResolve fr, FormatUnresolved fu, FormatErrors fe,
  16.102 +                    String name, String type, String value, String unresolved, List<String> errorLines) {
  16.103 +        return mode.format(fc, fa, fw, fr, fu, fe,
  16.104 +                name, type, value, unresolved, errorLines);
  16.105 +    }
  16.106 +
  16.107 +    public String getPrompt(String nextId) {
  16.108 +        return mode.getPrompt(nextId);
  16.109 +    }
  16.110 +
  16.111 +    public String getContinuationPrompt(String nextId) {
  16.112 +        return mode.getContinuationPrompt(nextId);
  16.113 +    }
  16.114 +
  16.115 +    public boolean setFeedback(MessageHandler messageHandler, ArgTokenizer at) {
  16.116 +        return new Setter(messageHandler, at).setFeedback();
  16.117 +    }
  16.118 +
  16.119 +    public boolean setFormat(MessageHandler messageHandler, ArgTokenizer at) {
  16.120 +        return new Setter(messageHandler, at).setFormat();
  16.121 +    }
  16.122 +
  16.123 +    public boolean setTruncation(MessageHandler messageHandler, ArgTokenizer at) {
  16.124 +        return new Setter(messageHandler, at).setTruncation();
  16.125 +    }
  16.126 +
  16.127 +    public boolean setMode(MessageHandler messageHandler, ArgTokenizer at) {
  16.128 +        return new Setter(messageHandler, at).setMode();
  16.129 +    }
  16.130 +
  16.131 +    public boolean setPrompt(MessageHandler messageHandler, ArgTokenizer at) {
  16.132 +        return new Setter(messageHandler, at).setPrompt();
  16.133 +    }
  16.134 +
  16.135 +    public String retainFeedback(MessageHandler messageHandler, ArgTokenizer at) {
  16.136 +        return new Setter(messageHandler, at).retainFeedback();
  16.137 +    }
  16.138 +
  16.139 +    public String retainMode(MessageHandler messageHandler, ArgTokenizer at) {
  16.140 +        return new Setter(messageHandler, at).retainMode();
  16.141 +    }
  16.142 +
  16.143 +    public boolean restoreEncodedModes(MessageHandler messageHandler, String encoded) {
  16.144 +        return new Setter(messageHandler, new ArgTokenizer("<init>", "")).restoreEncodedModes(encoded);
  16.145 +    }
  16.146 +
  16.147 +    public void markModesReadOnly() {
  16.148 +        modeMap.values().stream()
  16.149 +                .forEach(m -> m.readOnly = true);
  16.150 +    }
  16.151 +
  16.152 +    {
  16.153 +        for (FormatCase e : EnumSet.allOf(FormatCase.class))
  16.154 +            selectorMap.put(e.name().toLowerCase(Locale.US), e);
  16.155 +        for (FormatAction e : EnumSet.allOf(FormatAction.class))
  16.156 +            selectorMap.put(e.name().toLowerCase(Locale.US), e);
  16.157 +        for (FormatResolve e : EnumSet.allOf(FormatResolve.class))
  16.158 +            selectorMap.put(e.name().toLowerCase(Locale.US), e);
  16.159 +        for (FormatUnresolved e : EnumSet.allOf(FormatUnresolved.class))
  16.160 +            selectorMap.put(e.name().toLowerCase(Locale.US), e);
  16.161 +        for (FormatErrors e : EnumSet.allOf(FormatErrors.class))
  16.162 +            selectorMap.put(e.name().toLowerCase(Locale.US), e);
  16.163 +        for (FormatWhen e : EnumSet.allOf(FormatWhen.class))
  16.164 +            selectorMap.put(e.name().toLowerCase(Locale.US), e);
  16.165 +    }
  16.166 +
  16.167 +    /**
  16.168 +     * Holds all the context of a mode mode
  16.169 +     */
  16.170 +    private static class Mode {
  16.171 +
  16.172 +        // Name of mode
  16.173 +        final String name;
  16.174 +
  16.175 +        // Display command verification/information
  16.176 +        boolean commandFluff;
  16.177 +
  16.178 +        // Event cases: class, method, expression, ...
  16.179 +        final Map<String, List<Setting>> cases;
  16.180 +
  16.181 +        boolean readOnly = false;
  16.182 +
  16.183 +        String prompt = "\n-> ";
  16.184 +        String continuationPrompt = ">> ";
  16.185 +
  16.186 +        static class Setting {
  16.187 +            final long enumBits;
  16.188 +            final String format;
  16.189 +            Setting(long enumBits, String format) {
  16.190 +                this.enumBits = enumBits;
  16.191 +                this.format = format;
  16.192 +            }
  16.193 +        }
  16.194 +
  16.195 +        /**
  16.196 +         * Set up an empty mode.
  16.197 +         *
  16.198 +         * @param name
  16.199 +         * @param commandFluff True if should display command fluff messages
  16.200 +         */
  16.201 +        Mode(String name) {
  16.202 +            this.name = name;
  16.203 +            this.cases = new HashMap<>();
  16.204 +            add("name",       new Setting(ALWAYS, "%1$s"));
  16.205 +            add("type",       new Setting(ALWAYS, "%2$s"));
  16.206 +            add("value",      new Setting(ALWAYS, "%3$s"));
  16.207 +            add("unresolved", new Setting(ALWAYS, "%4$s"));
  16.208 +            add("errors",     new Setting(ALWAYS, "%5$s"));
  16.209 +            add("err",        new Setting(ALWAYS, "%6$s"));
  16.210 +
  16.211 +            add("errorline",  new Setting(ALWAYS, "    {err}%n"));
  16.212 +
  16.213 +            add("pre",        new Setting(ALWAYS, "|  "));
  16.214 +            add("post",       new Setting(ALWAYS, "%n"));
  16.215 +            add("errorpre",   new Setting(ALWAYS, "|  "));
  16.216 +            add("errorpost",  new Setting(ALWAYS, "%n"));
  16.217 +        }
  16.218 +
  16.219 +        /**
  16.220 +         * Set up a copied mode.
  16.221 +         *
  16.222 +         * @param name
  16.223 +         * @param m Mode to copy, or null for no fresh
  16.224 +         */
  16.225 +        Mode(String name, Mode m) {
  16.226 +            this.name = name;
  16.227 +            this.commandFluff = m.commandFluff;
  16.228 +            this.prompt = m.prompt;
  16.229 +            this.continuationPrompt = m.continuationPrompt;
  16.230 +            this.cases = new HashMap<>();
  16.231 +            m.cases.entrySet().stream()
  16.232 +                    .forEach(fes -> fes.getValue()
  16.233 +                    .forEach(ing -> add(fes.getKey(), ing)));
  16.234 +
  16.235 +        }
  16.236 +
  16.237 +        /**
  16.238 +         * Set up a mode reconstituted from a preferences string.
  16.239 +         *
  16.240 +         * @param it the encoded Mode broken into String chunks, may contain
  16.241 +         * subsequent encoded modes
  16.242 +         */
  16.243 +        Mode(Iterator<String> it) {
  16.244 +            this.name = it.next();
  16.245 +            this.commandFluff = Boolean.parseBoolean(it.next());
  16.246 +            this.prompt = it.next();
  16.247 +            this.continuationPrompt = it.next();
  16.248 +            cases = new HashMap<>();
  16.249 +            String field;
  16.250 +            while (!(field = it.next()).equals("***")) {
  16.251 +                String open = it.next();
  16.252 +                assert open.equals("(");
  16.253 +                List<Setting> settings = new ArrayList<>();
  16.254 +                String bits;
  16.255 +                while (!(bits = it.next()).equals(")")) {
  16.256 +                    String format = it.next();
  16.257 +                    Setting ing = new Setting(Long.parseLong(bits), format);
  16.258 +                    settings.add(ing);
  16.259 +                }
  16.260 +                cases.put(field, settings);
  16.261 +            }
  16.262 +        }
  16.263 +
  16.264 +        /**
  16.265 +         * Set if this mode displays informative/confirmational messages on
  16.266 +         * commands.
  16.267 +         *
  16.268 +         * @param fluff the value to set
  16.269 +         */
  16.270 +        void setCommandFluff(boolean fluff) {
  16.271 +            commandFluff = fluff;
  16.272 +        }
  16.273 +
  16.274 +        /**
  16.275 +         * Encodes the mode into a String so it can be saved in Preferences.
  16.276 +         *
  16.277 +         * @return the string representation
  16.278 +         */
  16.279 +        String encode() {
  16.280 +            List<String> el = new ArrayList<>();
  16.281 +            el.add(name);
  16.282 +            el.add(String.valueOf(commandFluff));
  16.283 +            el.add(prompt);
  16.284 +            el.add(continuationPrompt);
  16.285 +            for (Entry<String, List<Setting>> es : cases.entrySet()) {
  16.286 +                el.add(es.getKey());
  16.287 +                el.add("(");
  16.288 +                for (Setting ing : es.getValue()) {
  16.289 +                    el.add(String.valueOf(ing.enumBits));
  16.290 +                    el.add(ing.format);
  16.291 +                }
  16.292 +                el.add(")");
  16.293 +            }
  16.294 +            el.add("***");
  16.295 +            return String.join(RECORD_SEPARATOR, el);
  16.296 +        }
  16.297 +
  16.298 +        private boolean add(String field, Setting ing) {
  16.299 +            List<Setting> settings =  cases.computeIfAbsent(field, k -> new ArrayList<>());
  16.300 +            if (settings == null) {
  16.301 +                return false;
  16.302 +            }
  16.303 +            settings.add(ing);
  16.304 +            return true;
  16.305 +        }
  16.306 +
  16.307 +        void set(String field,
  16.308 +                Collection<FormatCase> cc, Collection<FormatAction> ca, Collection<FormatWhen> cw,
  16.309 +                Collection<FormatResolve> cr, Collection<FormatUnresolved> cu, Collection<FormatErrors> ce,
  16.310 +                String format) {
  16.311 +            long bits = bits(cc, ca, cw, cr, cu, ce);
  16.312 +            set(field, bits, format);
  16.313 +        }
  16.314 +
  16.315 +        void set(String field, long bits, String format) {
  16.316 +            add(field, new Setting(bits, format));
  16.317 +        }
  16.318 +
  16.319 +        /**
  16.320 +         * Lookup format Replace fields with context specific formats.
  16.321 +         *
  16.322 +         * @return format string
  16.323 +         */
  16.324 +        String format(String field, long bits) {
  16.325 +            List<Setting> settings = cases.get(field);
  16.326 +            if (settings == null) {
  16.327 +                return ""; //TODO error?
  16.328 +            }
  16.329 +            String format = null;
  16.330 +            for (int i = settings.size() - 1; i >= 0; --i) {
  16.331 +                Setting ing = settings.get(i);
  16.332 +                long mask = ing.enumBits;
  16.333 +                if ((bits & mask) == bits) {
  16.334 +                    format = ing.format;
  16.335 +                    break;
  16.336 +                }
  16.337 +            }
  16.338 +            if (format == null || format.isEmpty()) {
  16.339 +                return "";
  16.340 +            }
  16.341 +            Matcher m = FIELD_PATTERN.matcher(format);
  16.342 +            StringBuffer sb = new StringBuffer(format.length());
  16.343 +            while (m.find()) {
  16.344 +                String fieldName = m.group(1);
  16.345 +                String sub = format(fieldName, bits);
  16.346 +                m.appendReplacement(sb, Matcher.quoteReplacement(sub));
  16.347 +            }
  16.348 +            m.appendTail(sb);
  16.349 +            return sb.toString();
  16.350 +        }
  16.351 +
  16.352 +        // Compute the display output given full context and values
  16.353 +        String format(FormatCase fc, FormatAction fa, FormatWhen fw,
  16.354 +                    FormatResolve fr, FormatUnresolved fu, FormatErrors fe,
  16.355 +                    String name, String type, String value, String unresolved, List<String> errorLines) {
  16.356 +            // Convert the context into a bit representation used as selectors for store field formats
  16.357 +            long bits = bits(fc, fa, fw, fr, fu, fe);
  16.358 +            String fname = name==null? "" : name;
  16.359 +            String ftype = type==null? "" : type;
  16.360 +            // Compute the representation of value
  16.361 +            String fvalue;
  16.362 +            if (value==null) {
  16.363 +                fvalue = "";
  16.364 +            } else {
  16.365 +                // Retrieve the truncation length
  16.366 +                String truncField = format(TRUNCATION_FIELD, bits);
  16.367 +                if (truncField.isEmpty()) {
  16.368 +                    // No truncation set, use whole value
  16.369 +                    fvalue = value;
  16.370 +                } else {
  16.371 +                    // Convert truncation length to int
  16.372 +                    // this is safe since it has been tested before it is set
  16.373 +                    int trunc = Integer.parseUnsignedInt(truncField);
  16.374 +                    if (value.length() > trunc) {
  16.375 +                        if (trunc <= 5) {
  16.376 +                            // Very short truncations have no room for "..."
  16.377 +                            fvalue = value.substring(0, trunc);
  16.378 +                        } else {
  16.379 +                            // Normal truncation, make total length equal truncation length
  16.380 +                            fvalue = value.substring(0, trunc - 4) + " ...";
  16.381 +                        }
  16.382 +                    } else {
  16.383 +                        // Within truncation length, use whole value
  16.384 +                        fvalue = value;
  16.385 +                    }
  16.386 +                }
  16.387 +            }
  16.388 +            String funresolved = unresolved==null? "" : unresolved;
  16.389 +            String errors = errorLines.stream()
  16.390 +                    .map(el -> String.format(
  16.391 +                            format("errorline", bits),
  16.392 +                            fname, ftype, fvalue, funresolved, "*cannot-use-errors-here*", el))
  16.393 +                    .collect(joining());
  16.394 +            return String.format(
  16.395 +                    format("display", bits),
  16.396 +                    fname, ftype, fvalue, funresolved, errors, "*cannot-use-err-here*");
  16.397 +        }
  16.398 +
  16.399 +        void setPrompts(String prompt, String continuationPrompt) {
  16.400 +            this.prompt = prompt;
  16.401 +            this.continuationPrompt = continuationPrompt;
  16.402 +        }
  16.403 +
  16.404 +        String getPrompt(String nextId) {
  16.405 +            return String.format(prompt, nextId);
  16.406 +        }
  16.407 +
  16.408 +        String getContinuationPrompt(String nextId) {
  16.409 +            return String.format(continuationPrompt, nextId);
  16.410 +        }
  16.411 +    }
  16.412 +
  16.413 +    // Representation of one instance of all the enum values as bits in a long
  16.414 +    private static long bits(FormatCase fc, FormatAction fa, FormatWhen fw,
  16.415 +            FormatResolve fr, FormatUnresolved fu, FormatErrors fe) {
  16.416 +        long res = 0L;
  16.417 +        res |= 1 << fc.ordinal();
  16.418 +        res <<= FormatAction.count;
  16.419 +        res |= 1 << fa.ordinal();
  16.420 +        res <<= FormatWhen.count;
  16.421 +        res |= 1 << fw.ordinal();
  16.422 +        res <<= FormatResolve.count;
  16.423 +        res |= 1 << fr.ordinal();
  16.424 +        res <<= FormatUnresolved.count;
  16.425 +        res |= 1 << fu.ordinal();
  16.426 +        res <<= FormatErrors.count;
  16.427 +        res |= 1 << fe.ordinal();
  16.428 +        return res;
  16.429 +    }
  16.430 +
  16.431 +    // Representation of a space of enum values as or'edbits in a long
  16.432 +    private static long bits(Collection<FormatCase> cc, Collection<FormatAction> ca, Collection<FormatWhen> cw,
  16.433 +                Collection<FormatResolve> cr, Collection<FormatUnresolved> cu, Collection<FormatErrors> ce) {
  16.434 +        long res = 0L;
  16.435 +        for (FormatCase fc : cc)
  16.436 +            res |= 1 << fc.ordinal();
  16.437 +        res <<= FormatAction.count;
  16.438 +        for (FormatAction fa : ca)
  16.439 +            res |= 1 << fa.ordinal();
  16.440 +        res <<= FormatWhen.count;
  16.441 +        for (FormatWhen fw : cw)
  16.442 +            res |= 1 << fw.ordinal();
  16.443 +        res <<= FormatResolve.count;
  16.444 +        for (FormatResolve fr : cr)
  16.445 +            res |= 1 << fr.ordinal();
  16.446 +        res <<= FormatUnresolved.count;
  16.447 +        for (FormatUnresolved fu : cu)
  16.448 +            res |= 1 << fu.ordinal();
  16.449 +        res <<= FormatErrors.count;
  16.450 +        for (FormatErrors fe : ce)
  16.451 +            res |= 1 << fe.ordinal();
  16.452 +        return res;
  16.453 +    }
  16.454 +
  16.455 +    interface Selector<E extends Enum<E> & Selector<E>> {
  16.456 +        SelectorCollector<E> collector(Setter.SelectorList sl);
  16.457 +        String doc();
  16.458 +    }
  16.459 +
  16.460 +    /**
  16.461 +     * The event cases
  16.462 +     */
  16.463 +    public enum FormatCase implements Selector<FormatCase> {
  16.464 +        IMPORT("import declaration"),
  16.465 +        CLASS("class declaration"),
  16.466 +        INTERFACE("interface declaration"),
  16.467 +        ENUM("enum declaration"),
  16.468 +        ANNOTATION("annotation interface declaration"),
  16.469 +        METHOD("method declaration -- note: {type}==parameter-types"),
  16.470 +        VARDECL("variable declaration without init"),
  16.471 +        VARINIT("variable declaration with init"),
  16.472 +        EXPRESSION("expression -- note: {name}==scratch-variable-name"),
  16.473 +        VARVALUE("variable value expression"),
  16.474 +        ASSIGNMENT("assign variable"),
  16.475 +        STATEMENT("statement");
  16.476 +        String doc;
  16.477 +        static final EnumSet<FormatCase> all = EnumSet.allOf(FormatCase.class);
  16.478 +        static final int count = all.size();
  16.479 +
  16.480 +        @Override
  16.481 +        public SelectorCollector<FormatCase> collector(Setter.SelectorList sl) {
  16.482 +            return sl.cases;
  16.483 +        }
  16.484 +
  16.485 +        @Override
  16.486 +        public String doc() {
  16.487 +            return doc;
  16.488 +        }
  16.489 +
  16.490 +        private FormatCase(String doc) {
  16.491 +            this.doc = doc;
  16.492 +        }
  16.493 +    }
  16.494 +
  16.495 +    /**
  16.496 +     * The event actions
  16.497 +     */
  16.498 +    public enum FormatAction implements Selector<FormatAction> {
  16.499 +        ADDED("snippet has been added"),
  16.500 +        MODIFIED("an existing snippet has been modified"),
  16.501 +        REPLACED("an existing snippet has been replaced with a new snippet"),
  16.502 +        OVERWROTE("an existing snippet has been overwritten"),
  16.503 +        DROPPED("snippet has been dropped"),
  16.504 +        USED("snippet was used when it cannot be");
  16.505 +        String doc;
  16.506 +        static final EnumSet<FormatAction> all = EnumSet.allOf(FormatAction.class);
  16.507 +        static final int count = all.size();
  16.508 +
  16.509 +        @Override
  16.510 +        public SelectorCollector<FormatAction> collector(Setter.SelectorList sl) {
  16.511 +            return sl.actions;
  16.512 +        }
  16.513 +
  16.514 +        @Override
  16.515 +        public String doc() {
  16.516 +            return doc;
  16.517 +        }
  16.518 +
  16.519 +        private FormatAction(String doc) {
  16.520 +            this.doc = doc;
  16.521 +        }
  16.522 +    }
  16.523 +
  16.524 +    /**
  16.525 +     * When the event occurs: primary or update
  16.526 +     */
  16.527 +    public enum FormatWhen implements Selector<FormatWhen> {
  16.528 +        PRIMARY("the entered snippet"),
  16.529 +        UPDATE("an update to a dependent snippet");
  16.530 +        String doc;
  16.531 +        static final EnumSet<FormatWhen> all = EnumSet.allOf(FormatWhen.class);
  16.532 +        static final int count = all.size();
  16.533 +
  16.534 +        @Override
  16.535 +        public SelectorCollector<FormatWhen> collector(Setter.SelectorList sl) {
  16.536 +            return sl.whens;
  16.537 +        }
  16.538 +
  16.539 +        @Override
  16.540 +        public String doc() {
  16.541 +            return doc;
  16.542 +        }
  16.543 +
  16.544 +        private FormatWhen(String doc) {
  16.545 +            this.doc = doc;
  16.546 +        }
  16.547 +    }
  16.548 +
  16.549 +    /**
  16.550 +     * Resolution problems
  16.551 +     */
  16.552 +    public enum FormatResolve implements Selector<FormatResolve> {
  16.553 +        OK("resolved correctly"),
  16.554 +        DEFINED("defined despite recoverably unresolved references"),
  16.555 +        NOTDEFINED("not defined because of recoverably unresolved references");
  16.556 +        String doc;
  16.557 +        static final EnumSet<FormatResolve> all = EnumSet.allOf(FormatResolve.class);
  16.558 +        static final int count = all.size();
  16.559 +
  16.560 +        @Override
  16.561 +        public SelectorCollector<FormatResolve> collector(Setter.SelectorList sl) {
  16.562 +            return sl.resolves;
  16.563 +        }
  16.564 +
  16.565 +        @Override
  16.566 +        public String doc() {
  16.567 +            return doc;
  16.568 +        }
  16.569 +
  16.570 +        private FormatResolve(String doc) {
  16.571 +            this.doc = doc;
  16.572 +        }
  16.573 +    }
  16.574 +
  16.575 +    /**
  16.576 +     * Count of unresolved references
  16.577 +     */
  16.578 +    public enum FormatUnresolved implements Selector<FormatUnresolved> {
  16.579 +        UNRESOLVED0("no names are unresolved"),
  16.580 +        UNRESOLVED1("one name is unresolved"),
  16.581 +        UNRESOLVED2("two or more names are unresolved");
  16.582 +        String doc;
  16.583 +        static final EnumSet<FormatUnresolved> all = EnumSet.allOf(FormatUnresolved.class);
  16.584 +        static final int count = all.size();
  16.585 +
  16.586 +        @Override
  16.587 +        public SelectorCollector<FormatUnresolved> collector(Setter.SelectorList sl) {
  16.588 +            return sl.unresolvedCounts;
  16.589 +        }
  16.590 +
  16.591 +        @Override
  16.592 +        public String doc() {
  16.593 +            return doc;
  16.594 +        }
  16.595 +
  16.596 +        private FormatUnresolved(String doc) {
  16.597 +            this.doc = doc;
  16.598 +        }
  16.599 +    }
  16.600 +
  16.601 +    /**
  16.602 +     * Count of unresolved references
  16.603 +     */
  16.604 +    public enum FormatErrors implements Selector<FormatErrors> {
  16.605 +        ERROR0("no errors"),
  16.606 +        ERROR1("one error"),
  16.607 +        ERROR2("two or more errors");
  16.608 +        String doc;
  16.609 +        static final EnumSet<FormatErrors> all = EnumSet.allOf(FormatErrors.class);
  16.610 +        static final int count = all.size();
  16.611 +
  16.612 +        @Override
  16.613 +        public SelectorCollector<FormatErrors> collector(Setter.SelectorList sl) {
  16.614 +            return sl.errorCounts;
  16.615 +        }
  16.616 +
  16.617 +        @Override
  16.618 +        public String doc() {
  16.619 +            return doc;
  16.620 +        }
  16.621 +
  16.622 +        private FormatErrors(String doc) {
  16.623 +            this.doc = doc;
  16.624 +        }
  16.625 +    }
  16.626 +
  16.627 +    class SelectorCollector<E extends Enum<E> & Selector<E>> {
  16.628 +        final EnumSet<E> all;
  16.629 +        EnumSet<E> set = null;
  16.630 +        SelectorCollector(EnumSet<E> all) {
  16.631 +            this.all = all;
  16.632 +        }
  16.633 +        void add(Object o) {
  16.634 +            @SuppressWarnings("unchecked")
  16.635 +            E e = (E) o;
  16.636 +            if (set == null) {
  16.637 +                set = EnumSet.of(e);
  16.638 +            } else {
  16.639 +                set.add(e);
  16.640 +            }
  16.641 +        }
  16.642 +
  16.643 +        boolean isEmpty() {
  16.644 +            return set == null;
  16.645 +        }
  16.646 +
  16.647 +        EnumSet<E> getSet() {
  16.648 +            return set == null
  16.649 +                    ? all
  16.650 +                    : set;
  16.651 +        }
  16.652 +    }
  16.653 +
  16.654 +    // Class used to set custom eval output formats
  16.655 +    // For both /set format  -- Parse arguments, setting custom format, or printing error
  16.656 +    private class Setter {
  16.657 +
  16.658 +        private final ArgTokenizer at;
  16.659 +        private final MessageHandler messageHandler;
  16.660 +        boolean valid = true;
  16.661 +
  16.662 +        Setter(MessageHandler messageHandler, ArgTokenizer at) {
  16.663 +            this.messageHandler = messageHandler;
  16.664 +            this.at = at;
  16.665 +        }
  16.666 +
  16.667 +        void fluff(String format, Object... args) {
  16.668 +            messageHandler.fluff(format, args);
  16.669 +        }
  16.670 +
  16.671 +        void fluffmsg(String messageKey, Object... args) {
  16.672 +            messageHandler.fluffmsg(messageKey, args);
  16.673 +        }
  16.674 +
  16.675 +        void errorat(String messageKey, Object... args) {
  16.676 +            Object[] a2 = Arrays.copyOf(args, args.length + 2);
  16.677 +            a2[args.length] = at.whole();
  16.678 +            messageHandler.errormsg(messageKey, a2);
  16.679 +        }
  16.680 +
  16.681 +        // For /set prompt <mode> "<prompt>" "<continuation-prompt>"
  16.682 +        boolean setPrompt() {
  16.683 +            Mode m = nextMode();
  16.684 +            if (valid && m.readOnly) {
  16.685 +                errorat("jshell.err.not.valid.with.predefined.mode", m.name);
  16.686 +                valid = false;
  16.687 +            }
  16.688 +            String prompt = valid ? nextFormat() : null;
  16.689 +            String continuationPrompt = valid ? nextFormat() : null;
  16.690 +            if (valid) {
  16.691 +                m.setPrompts(prompt, continuationPrompt);
  16.692 +            } else {
  16.693 +                fluffmsg("jshell.msg.see", "/help /set prompt");
  16.694 +            }
  16.695 +            return valid;
  16.696 +        }
  16.697 +
  16.698 +        /**
  16.699 +         * Set mode. Create, changed, or delete a feedback mode. For @{code /set
  16.700 +         * mode <mode> [<old-mode>] [-command|-quiet|-delete]}.
  16.701 +         *
  16.702 +         * @return true if successful
  16.703 +         */
  16.704 +        boolean setMode() {
  16.705 +            at.allowedOptions("-command", "-quiet", "-delete");
  16.706 +            String umode = nextModeIdentifier();
  16.707 +            Mode om = null;
  16.708 +            String omode = at.next();
  16.709 +            if (valid && omode != null) {
  16.710 +                om = toMode(omode);
  16.711 +            }
  16.712 +            checkOptionsAndRemainingInput();
  16.713 +            boolean commandOption = at.hasOption("-command");
  16.714 +            boolean quietOption = at.hasOption("-quiet");
  16.715 +            boolean deleteOption = at.hasOption("-delete");
  16.716 +            // Only one (or zero) of the options can be used
  16.717 +            if (valid && at.optionCount() > 1) {
  16.718 +                errorat("jshell.err.conflicting.options");
  16.719 +                valid = false;
  16.720 +            }
  16.721 +            if (valid) {
  16.722 +                Mode m = modeMap.get(umode);
  16.723 +                if (m != null && m.readOnly) {
  16.724 +                    // Cannot make changes to a the built-in modes
  16.725 +                    errorat("jshell.err.not.valid.with.predefined.mode", m.name);
  16.726 +                    valid = false;
  16.727 +                } else if (deleteOption) {
  16.728 +                    if (m == null) {
  16.729 +                        // Cannot delete a mode that does not exist
  16.730 +                        errorat("jshell.err.mode.unknown", umode);
  16.731 +                        valid = false;
  16.732 +                    } else if (mode.name.equals(m.name)) {
  16.733 +                        // Cannot delete the current mode out from under us
  16.734 +                        errorat("jshell.err.cannot.delete.current.mode", umode);
  16.735 +                        valid = false;
  16.736 +                    } else {
  16.737 +                        // Remove the mode
  16.738 +                        modeMap.remove(umode);
  16.739 +                    }
  16.740 +                } else {
  16.741 +                    if (om != null || m == null) {
  16.742 +                        // We are copying and existing mode and/or creating a
  16.743 +                        // brand-new mode -- in either case create from scratch
  16.744 +                        m = (om != null)
  16.745 +                                ? new Mode(umode, om)
  16.746 +                                : new Mode(umode);
  16.747 +                        modeMap.put(umode, m);
  16.748 +                        fluffmsg("jshell.msg.feedback.new.mode", m.name);
  16.749 +                        // Set the current mode by name, in case we just smashed
  16.750 +                        // the current mode
  16.751 +                        if (umode.equals(mode.name)) {
  16.752 +                            mode = modeMap.get(mode.name);
  16.753 +                        }
  16.754 +                    }
  16.755 +                    if (commandOption || quietOption || om == null) {
  16.756 +                        // set command fluff, if explicit, or wholly new
  16.757 +                        m.setCommandFluff(!quietOption);
  16.758 +                    }
  16.759 +                }
  16.760 +            }
  16.761 +            if (!valid) {
  16.762 +                fluffmsg("jshell.msg.see", "/help /set mode");
  16.763 +            }
  16.764 +            return valid;
  16.765 +        }
  16.766 +
  16.767 +        // For /set feedback <mode>
  16.768 +        boolean setFeedback() {
  16.769 +            Mode m = nextMode();
  16.770 +            if (valid) {
  16.771 +                mode = m;
  16.772 +                fluffmsg("jshell.msg.feedback.mode", mode.name);
  16.773 +            } else {
  16.774 +                fluffmsg("jshell.msg.see", "/help /set feedback");
  16.775 +                printFeedbackModes();
  16.776 +            }
  16.777 +            return valid;
  16.778 +        }
  16.779 +
  16.780 +        // For /set format <mode> "<format>" <selector>...
  16.781 +        boolean setFormat() {
  16.782 +            Mode m = nextMode();
  16.783 +            if (valid && m.readOnly) {
  16.784 +                errorat("jshell.err.not.valid.with.predefined.mode", m.name);
  16.785 +                valid = false;
  16.786 +            }
  16.787 +            String field = valid
  16.788 +                    ? toIdentifier(at.next(), "jshell.err.missing.field", "jshell.err.field.name")
  16.789 +                    : null;
  16.790 +            String format = valid ? nextFormat() : null;
  16.791 +            return installFormat(m, field, format, "/help /set format");
  16.792 +        }
  16.793 +
  16.794 +        // For /set truncation <mode> <length> <selector>...
  16.795 +        boolean setTruncation() {
  16.796 +            Mode m = nextMode();
  16.797 +            if (valid && m.readOnly) {
  16.798 +                errorat("jshell.err.not.valid.with.predefined.mode", m.name);
  16.799 +                valid = false;
  16.800 +            }
  16.801 +            String length = at.next();
  16.802 +            if (length == null) {
  16.803 +                errorat("jshell.err.truncation.expected.length");
  16.804 +                valid = false;
  16.805 +            } else {
  16.806 +                try {
  16.807 +                    // Assure that integer format is correct
  16.808 +                    Integer.parseUnsignedInt(length);
  16.809 +                } catch (NumberFormatException ex) {
  16.810 +                    errorat("jshell.err.truncation.length.not.integer", length);
  16.811 +                    valid = false;
  16.812 +                }
  16.813 +            }
  16.814 +            // install length into an internal format field
  16.815 +            return installFormat(m, TRUNCATION_FIELD, length, "/help /set truncation");
  16.816 +        }
  16.817 +
  16.818 +        String retainFeedback() {
  16.819 +            String umode = at.next();
  16.820 +            if (umode != null) {
  16.821 +                toModeIdentifier(umode);
  16.822 +                Mode m = valid ? toMode(umode) : null;
  16.823 +                if (valid && !m.readOnly && !retainedMap.containsKey(m.name)) {
  16.824 +                    errorat("jshell.err.retained.feedback.mode.must.be.retained.or.predefined");
  16.825 +                    valid = false;
  16.826 +                }
  16.827 +                if (valid) {
  16.828 +                    mode = m;
  16.829 +                    retainedCurrentMode = m;
  16.830 +                    fluffmsg("jshell.msg.feedback.mode", mode.name);
  16.831 +                } else {
  16.832 +                    fluffmsg("jshell.msg.see", "/help /retain feedback");
  16.833 +                    return null;
  16.834 +                }
  16.835 +            }
  16.836 +            return mode.name;
  16.837 +        }
  16.838 +
  16.839 +        /**
  16.840 +         * Retain (or delete from retention) a previously set mode.
  16.841 +         *
  16.842 +         * @return all retained modes encoded into a String
  16.843 +         */
  16.844 +        String retainMode() {
  16.845 +            at.allowedOptions("-delete");
  16.846 +            String umode = nextModeIdentifier();
  16.847 +            // -delete is the only valid option, fail for anything else
  16.848 +            checkOptionsAndRemainingInput();
  16.849 +            boolean deleteOption = at.hasOption("-delete");
  16.850 +            // Lookup the mode
  16.851 +            Mode m;
  16.852 +            if (!valid) {
  16.853 +                m = null;
  16.854 +                // Skip this stuff, we have failed already
  16.855 +            } else if (deleteOption) {
  16.856 +                // If delete, allow for deleting, from retention, a mode that
  16.857 +                // has been locally deleted but is retained.
  16.858 +                // Also require the full name.
  16.859 +                m = modeMap.get(umode);
  16.860 +                if (m == null && !retainedMap.containsKey(umode)) {
  16.861 +                    errorat("jshell.err.mode.unknown", umode);
  16.862 +                    valid = false;
  16.863 +                }
  16.864 +            } else {
  16.865 +                // For retain do normal lookup and checking
  16.866 +                m = toMode(umode);
  16.867 +            }
  16.868 +
  16.869 +            // Built-in modes cannot be retained or deleted
  16.870 +            if (valid && m != null && m.readOnly) {
  16.871 +                errorat("jshell.err.not.valid.with.predefined.mode", umode);
  16.872 +                valid = false;
  16.873 +            }
  16.874 +            if (valid) {
  16.875 +                if (deleteOption) {
  16.876 +                    if (mode.name.equals(umode)) {
  16.877 +                        // Cannot delete the current mode out from under us
  16.878 +                        errorat("jshell.err.cannot.delete.current.mode", umode);
  16.879 +                        valid = false;
  16.880 +                    } else if (retainedCurrentMode != null && retainedCurrentMode.name.equals(umode)) {
  16.881 +                        // Cannot delete the retained mode or re-start has error
  16.882 +                        errorat("jshell.err.cannot.delete.retained.mode", umode);
  16.883 +                        valid = false;
  16.884 +                    } else {
  16.885 +                        // Delete the mode
  16.886 +                        modeMap.remove(umode);
  16.887 +                        retainedMap.remove(umode);
  16.888 +                    }
  16.889 +                } else {
  16.890 +                    // Retain the current encoding
  16.891 +                    retainedMap.put(m.name, m.encode());
  16.892 +                }
  16.893 +            }
  16.894 +            if (valid) {
  16.895 +                // Join all the retained encodings
  16.896 +                return String.join(RECORD_SEPARATOR, retainedMap.values());
  16.897 +            } else {
  16.898 +                fluffmsg("jshell.msg.see", "/help /retain mode");
  16.899 +                return null;
  16.900 +            }
  16.901 +        }
  16.902 +
  16.903 +        boolean restoreEncodedModes(String allEncoded) {
  16.904 +            try {
  16.905 +                // Iterate over each record in each encoded mode
  16.906 +                String[] ms = allEncoded.split(RECORD_SEPARATOR);
  16.907 +                Iterator<String> itr = Arrays.asList(ms).iterator();
  16.908 +                while (itr.hasNext()) {
  16.909 +                    // Reconstruct the encoded mode
  16.910 +                    Mode m = new Mode(itr);
  16.911 +                    modeMap.put(m.name, m);
  16.912 +                    // Continue to retain it a new retains occur
  16.913 +                    retainedMap.put(m.name, m.encode());
  16.914 +                }
  16.915 +                return true;
  16.916 +            } catch (Throwable exc) {
  16.917 +                // Catastrophic corruption -- clear map
  16.918 +                errorat("jshell.err.retained.mode.failure", exc);
  16.919 +                retainedMap.clear();
  16.920 +                return false;
  16.921 +            }
  16.922 +        }
  16.923 +
  16.924 +        // install the format of a field under parsed selectors
  16.925 +        boolean installFormat(Mode m, String field, String format, String help) {
  16.926 +            String slRaw;
  16.927 +            List<SelectorList> slList = new ArrayList<>();
  16.928 +            while (valid && (slRaw = at.next()) != null) {
  16.929 +                SelectorList sl = new SelectorList();
  16.930 +                sl.parseSelectorList(slRaw);
  16.931 +                slList.add(sl);
  16.932 +            }
  16.933 +            if (valid) {
  16.934 +                if (slList.isEmpty()) {
  16.935 +                    // No selectors specified, then always the format
  16.936 +                    m.set(field, ALWAYS, format);
  16.937 +                } else {
  16.938 +                    // Set the format of the field for specified selector
  16.939 +                    slList.stream()
  16.940 +                            .forEach(sl -> m.set(field,
  16.941 +                                sl.cases.getSet(), sl.actions.getSet(), sl.whens.getSet(),
  16.942 +                                sl.resolves.getSet(), sl.unresolvedCounts.getSet(), sl.errorCounts.getSet(),
  16.943 +                                format));
  16.944 +                }
  16.945 +            } else {
  16.946 +                fluffmsg("jshell.msg.see", help);
  16.947 +            }
  16.948 +            return valid;
  16.949 +        }
  16.950 +
  16.951 +        void checkOptionsAndRemainingInput() {
  16.952 +            if (!valid) {
  16.953 +                return;
  16.954 +            }
  16.955 +            String junk = at.remainder();
  16.956 +            if (!junk.isEmpty()) {
  16.957 +                errorat("jshell.err.unexpected.at.end", junk);
  16.958 +                valid = false;
  16.959 +            } else {
  16.960 +                String bad = at.badOptions();
  16.961 +                if (!bad.isEmpty()) {
  16.962 +                    errorat("jshell.err.unknown.option", bad);
  16.963 +                    valid = false;
  16.964 +                }
  16.965 +            }
  16.966 +        }
  16.967 +
  16.968 +        /**
  16.969 +         * Check that the specified string is an identifier (Java identifier).
  16.970 +         * If null display the missing error. If it is not an identifier,
  16.971 +         * display the error.
  16.972 +         *
  16.973 +         * @param id the string to check, MUST be the most recently retrieved
  16.974 +         * token from 'at'.
  16.975 +         * @param missing the resource error to display if null
  16.976 +         * @param err the resource error to display if not an identifier
  16.977 +         * @return the identifier string, or null if null or not an identifier
  16.978 +         */
  16.979 +        String toIdentifier(String id, String missing, String err) {
  16.980 +            if (id == null) {
  16.981 +                errorat(missing);
  16.982 +                valid = false;
  16.983 +                return null;
  16.984 +            }
  16.985 +            if (at.isQuoted() ||
  16.986 +                    !id.codePoints().allMatch(cp -> Character.isJavaIdentifierPart(cp))) {
  16.987 +                errorat(err, id);
  16.988 +                valid = false;
  16.989 +                return null;
  16.990 +            }
  16.991 +            return id;
  16.992 +        }
  16.993 +
  16.994 +        String toModeIdentifier(String id) {
  16.995 +            return toIdentifier(id, "jshell.err.missing.mode", "jshell.err.mode.name");
  16.996 +        }
  16.997 +
  16.998 +        String nextModeIdentifier() {
  16.999 +            return toModeIdentifier(at.next());
 16.1000 +        }
 16.1001 +
 16.1002 +        Mode nextMode() {
 16.1003 +            String umode = nextModeIdentifier();
 16.1004 +            return toMode(umode);
 16.1005 +        }
 16.1006 +
 16.1007 +        Mode toMode(String umode) {
 16.1008 +            if (!valid) {
 16.1009 +                return null;
 16.1010 +            }
 16.1011 +            if (umode == null) {
 16.1012 +                errorat("jshell.err.missing.mode");
 16.1013 +                valid = false;
 16.1014 +                return null;
 16.1015 +            }
 16.1016 +            Mode m = modeMap.get(umode);
 16.1017 +            if (m != null) {
 16.1018 +                return m;
 16.1019 +            }
 16.1020 +            // Failing an exact match, go searching
 16.1021 +            Mode[] matches = modeMap.entrySet().stream()
 16.1022 +                    .filter(e -> e.getKey().startsWith(umode))
 16.1023 +                    .map(e -> e.getValue())
 16.1024 +                    .toArray(size -> new Mode[size]);
 16.1025 +            if (matches.length == 1) {
 16.1026 +                return matches[0];
 16.1027 +            } else {
 16.1028 +                valid = false;
 16.1029 +                if (matches.length == 0) {
 16.1030 +                    errorat("jshell.err.feedback.does.not.match.mode", umode);
 16.1031 +                } else {
 16.1032 +                    errorat("jshell.err.feedback.ambiguous.mode", umode);
 16.1033 +                }
 16.1034 +                printFeedbackModes();
 16.1035 +                return null;
 16.1036 +            }
 16.1037 +        }
 16.1038 +
 16.1039 +        void printFeedbackModes() {
 16.1040 +            fluffmsg("jshell.msg.feedback.mode.following");
 16.1041 +            modeMap.keySet().stream()
 16.1042 +                    .forEach(mk -> fluff("   %s", mk));
 16.1043 +        }
 16.1044 +
 16.1045 +        // Test if the format string is correctly
 16.1046 +        final String nextFormat() {
 16.1047 +            String format = at.next();
 16.1048 +            if (format == null) {
 16.1049 +                errorat("jshell.err.feedback.expected.format");
 16.1050 +                valid = false;
 16.1051 +                return null;
 16.1052 +            }
 16.1053 +            if (!at.isQuoted()) {
 16.1054 +                errorat("jshell.err.feedback.must.be.quoted", format);
 16.1055 +                valid = false;
 16.1056 +                return null;
 16.1057 +            }
 16.1058 +            return format;
 16.1059 +        }
 16.1060 +
 16.1061 +        class SelectorList {
 16.1062 +
 16.1063 +            SelectorCollector<FormatCase> cases = new SelectorCollector<>(FormatCase.all);
 16.1064 +            SelectorCollector<FormatAction> actions = new SelectorCollector<>(FormatAction.all);
 16.1065 +            SelectorCollector<FormatWhen> whens = new SelectorCollector<>(FormatWhen.all);
 16.1066 +            SelectorCollector<FormatResolve> resolves = new SelectorCollector<>(FormatResolve.all);
 16.1067 +            SelectorCollector<FormatUnresolved> unresolvedCounts = new SelectorCollector<>(FormatUnresolved.all);
 16.1068 +            SelectorCollector<FormatErrors> errorCounts = new SelectorCollector<>(FormatErrors.all);
 16.1069 +
 16.1070 +            final void parseSelectorList(String sl) {
 16.1071 +                for (String s : sl.split("-")) {
 16.1072 +                    SelectorCollector<?> lastCollector = null;
 16.1073 +                    for (String as : s.split(",")) {
 16.1074 +                        if (!as.isEmpty()) {
 16.1075 +                            Selector<?> sel = selectorMap.get(as);
 16.1076 +                            if (sel == null) {
 16.1077 +                                errorat("jshell.err.feedback.not.a.valid.selector", as, s);
 16.1078 +                                valid = false;
 16.1079 +                                return;
 16.1080 +                            }
 16.1081 +                            SelectorCollector<?> collector = sel.collector(this);
 16.1082 +                            if (lastCollector == null) {
 16.1083 +                                if (!collector.isEmpty()) {
 16.1084 +                                    errorat("jshell.err.feedback.multiple.sections", as, s);
 16.1085 +                                    valid = false;
 16.1086 +                                    return;
 16.1087 +                                }
 16.1088 +                            } else if (collector != lastCollector) {
 16.1089 +                                errorat("jshell.err.feedback.different.selector.kinds", as, s);
 16.1090 +                                valid = false;
 16.1091 +                                return;
 16.1092 +                            }
 16.1093 +                            collector.add(sel);
 16.1094 +                            lastCollector = collector;
 16.1095 +                        }
 16.1096 +                    }
 16.1097 +                }
 16.1098 +            }
 16.1099 +        }
 16.1100 +    }
 16.1101 +}
    17.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Fri Jun 03 17:06:38 2016 +0200
    17.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Fri Sep 02 23:54:26 2016 +0200
    17.3 @@ -1,5 +1,5 @@
    17.4  /*
    17.5 - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
    17.6 + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
    17.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    17.8   *
    17.9   * This code is free software; you can redistribute it and/or modify it
   17.10 @@ -42,6 +42,7 @@
   17.11  import java.nio.file.NoSuchFileException;
   17.12  import java.nio.file.Path;
   17.13  import java.nio.file.Paths;
   17.14 +import java.text.MessageFormat;
   17.15  import java.util.ArrayList;
   17.16  import java.util.Arrays;
   17.17  import java.util.Collections;
   17.18 @@ -49,6 +50,7 @@
   17.19  import java.util.LinkedHashMap;
   17.20  import java.util.LinkedHashSet;
   17.21  import java.util.List;
   17.22 +import java.util.Locale;
   17.23  import java.util.Map;
   17.24  import java.util.Map.Entry;
   17.25  import java.util.Scanner;
   17.26 @@ -64,41 +66,61 @@
   17.27  
   17.28  import jdk.internal.jshell.debug.InternalDebugControl;
   17.29  import jdk.internal.jshell.tool.IOContext.InputInterruptedException;
   17.30 +import jdk.jshell.DeclarationSnippet;
   17.31  import jdk.jshell.Diag;
   17.32  import jdk.jshell.EvalException;
   17.33 +import jdk.jshell.ExpressionSnippet;
   17.34 +import jdk.jshell.ImportSnippet;
   17.35  import jdk.jshell.JShell;
   17.36 -import jdk.jshell.Snippet;
   17.37 -import jdk.jshell.DeclarationSnippet;
   17.38 -import jdk.jshell.TypeDeclSnippet;
   17.39 +import jdk.jshell.JShell.Subscription;
   17.40  import jdk.jshell.MethodSnippet;
   17.41  import jdk.jshell.PersistentSnippet;
   17.42 -import jdk.jshell.VarSnippet;
   17.43 -import jdk.jshell.ExpressionSnippet;
   17.44 +import jdk.jshell.Snippet;
   17.45  import jdk.jshell.Snippet.Status;
   17.46 +import jdk.jshell.SnippetEvent;
   17.47  import jdk.jshell.SourceCodeAnalysis;
   17.48  import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
   17.49  import jdk.jshell.SourceCodeAnalysis.Suggestion;
   17.50 -import jdk.jshell.SnippetEvent;
   17.51 +import jdk.jshell.TypeDeclSnippet;
   17.52  import jdk.jshell.UnresolvedReferenceException;
   17.53 -import jdk.jshell.Snippet.SubKind;
   17.54 -import jdk.jshell.JShell.Subscription;
   17.55 +import jdk.jshell.VarSnippet;
   17.56  
   17.57  import static java.nio.file.StandardOpenOption.CREATE;
   17.58  import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
   17.59  import static java.nio.file.StandardOpenOption.WRITE;
   17.60  import java.util.MissingResourceException;
   17.61 +import java.util.Optional;
   17.62  import java.util.ResourceBundle;
   17.63 +import java.util.Spliterators;
   17.64 +import java.util.function.Function;
   17.65 +import java.util.function.Supplier;
   17.66 +import jdk.internal.jshell.tool.Feedback.FormatAction;
   17.67 +import jdk.internal.jshell.tool.Feedback.FormatCase;
   17.68 +import jdk.internal.jshell.tool.Feedback.FormatErrors;
   17.69 +import jdk.internal.jshell.tool.Feedback.FormatResolve;
   17.70 +import jdk.internal.jshell.tool.Feedback.FormatUnresolved;
   17.71 +import jdk.internal.jshell.tool.Feedback.FormatWhen;
   17.72  import static java.util.stream.Collectors.toList;
   17.73 +import static jdk.jshell.Snippet.SubKind.VAR_VALUE_SUBKIND;
   17.74 +import static java.util.stream.Collectors.toMap;
   17.75 +import static jdk.internal.jshell.debug.InternalDebugControl.DBG_COMPA;
   17.76 +import static jdk.internal.jshell.debug.InternalDebugControl.DBG_DEP;
   17.77 +import static jdk.internal.jshell.debug.InternalDebugControl.DBG_EVNT;
   17.78 +import static jdk.internal.jshell.debug.InternalDebugControl.DBG_FMGR;
   17.79 +import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
   17.80  
   17.81  /**
   17.82   * Command line REPL tool for Java using the JShell API.
   17.83   * @author Robert Field
   17.84   */
   17.85 -public class JShellTool {
   17.86 +public class JShellTool implements MessageHandler {
   17.87  
   17.88 +    private static final String LINE_SEP = System.getProperty("line.separator");
   17.89      private static final Pattern LINEBREAK = Pattern.compile("\\R");
   17.90 -    private static final Pattern HISTORY_ALL_FILENAME = Pattern.compile(
   17.91 -            "((?<cmd>(all|history))(\\z|\\p{javaWhitespace}+))?(?<filename>.*)");
   17.92 +    private static final String RECORD_SEPARATOR = "\u241E";
   17.93 +    private static final String RB_NAME_PREFIX  = "jdk.internal.jshell.tool.resources";
   17.94 +    private static final String VERSION_RB_NAME = RB_NAME_PREFIX + ".version";
   17.95 +    private static final String L10N_RB_NAME    = RB_NAME_PREFIX + ".l10n";
   17.96  
   17.97      final InputStream cmdin;
   17.98      final PrintStream cmdout;
   17.99 @@ -107,6 +129,10 @@
  17.100      final InputStream userin;
  17.101      final PrintStream userout;
  17.102      final PrintStream usererr;
  17.103 +    final Preferences prefs;
  17.104 +    final Locale locale;
  17.105 +
  17.106 +    final Feedback feedback = new Feedback();
  17.107  
  17.108      /**
  17.109       * The constructor for the tool (used by tool launch via main and by test
  17.110 @@ -118,10 +144,13 @@
  17.111       * @param userin code execution input (not yet functional)
  17.112       * @param userout code execution output  -- System.out.printf("hi")
  17.113       * @param usererr code execution error stream  -- System.err.printf("Oops")
  17.114 +     * @param prefs preferences to use
  17.115 +     * @param locale locale to use
  17.116       */
  17.117      public JShellTool(InputStream cmdin, PrintStream cmdout, PrintStream cmderr,
  17.118              PrintStream console,
  17.119 -            InputStream userin, PrintStream userout, PrintStream usererr) {
  17.120 +            InputStream userin, PrintStream userout, PrintStream usererr,
  17.121 +            Preferences prefs, Locale locale) {
  17.122          this.cmdin = cmdin;
  17.123          this.cmdout = cmdout;
  17.124          this.cmderr = cmderr;
  17.125 @@ -129,27 +158,39 @@
  17.126          this.userin = userin;
  17.127          this.userout = userout;
  17.128          this.usererr = usererr;
  17.129 +        this.prefs = prefs;
  17.130 +        this.locale = locale;
  17.131      }
  17.132  
  17.133 +    private ResourceBundle versionRB = null;
  17.134 +    private ResourceBundle outputRB  = null;
  17.135 +
  17.136      private IOContext input = null;
  17.137      private boolean regenerateOnDeath = true;
  17.138      private boolean live = false;
  17.139 +    private boolean feedbackInitialized = false;
  17.140 +    private String commandLineFeedbackMode = null;
  17.141 +    private List<String> remoteVMOptions = new ArrayList<>();
  17.142  
  17.143      SourceCodeAnalysis analysis;
  17.144      JShell state = null;
  17.145      Subscription shutdownSubscription = null;
  17.146  
  17.147      private boolean debug = false;
  17.148 -    private boolean displayPrompt = true;
  17.149      public boolean testPrompt = false;
  17.150 -    private Feedback feedback = Feedback.Default;
  17.151      private String cmdlineClasspath = null;
  17.152 -    private String cmdlineStartup = null;
  17.153 -    private String editor = null;
  17.154 +    private String startup = null;
  17.155 +    private String[] editor = null;
  17.156  
  17.157 -    static final Preferences PREFS = Preferences.userRoot().node("tool/REPL");
  17.158 +    // Commands and snippets which should be replayed
  17.159 +    private List<String> replayableHistory;
  17.160 +    private List<String> replayableHistoryPrevious;
  17.161  
  17.162 -    static final String STARTUP_KEY = "STARTUP";
  17.163 +    static final String STARTUP_KEY  = "STARTUP";
  17.164 +    static final String EDITOR_KEY   = "EDITOR";
  17.165 +    static final String FEEDBACK_KEY = "FEEDBACK";
  17.166 +    static final String MODE_KEY     = "MODE";
  17.167 +    static final String REPLAY_RESTORE_KEY = "REPLAY_RESTORE";
  17.168  
  17.169      static final String DEFAULT_STARTUP =
  17.170              "\n" +
  17.171 @@ -162,13 +203,25 @@
  17.172              "import java.util.regex.*;\n" +
  17.173              "void printf(String format, Object... args) { System.out.printf(format, args); }\n";
  17.174  
  17.175 -    // Tool id (tid) mapping
  17.176 +    // Tool id (tid) mapping: the three name spaces
  17.177      NameSpace mainNamespace;
  17.178      NameSpace startNamespace;
  17.179      NameSpace errorNamespace;
  17.180 +
  17.181 +    // Tool id (tid) mapping: the current name spaces
  17.182      NameSpace currentNameSpace;
  17.183 +
  17.184      Map<Snippet,SnippetInfo> mapSnippet;
  17.185  
  17.186 +    /**
  17.187 +     * Is the input/output currently interactive
  17.188 +     *
  17.189 +     * @return true if console
  17.190 +     */
  17.191 +    boolean interactive() {
  17.192 +        return input != null && input.interactiveOutput();
  17.193 +    }
  17.194 +
  17.195      void debug(String format, Object... args) {
  17.196          if (debug) {
  17.197              cmderr.printf(format + "\n", args);
  17.198 @@ -176,38 +229,196 @@
  17.199      }
  17.200  
  17.201      /**
  17.202 -     * For more verbose feedback modes
  17.203 +     * Base output for command output -- no pre- or post-fix
  17.204 +     *
  17.205 +     * @param printf format
  17.206 +     * @param printf args
  17.207 +     */
  17.208 +    void rawout(String format, Object... args) {
  17.209 +        cmdout.printf(format, args);
  17.210 +    }
  17.211 +
  17.212 +    /**
  17.213 +     * Must show command output
  17.214 +     *
  17.215       * @param format printf format
  17.216       * @param args printf args
  17.217       */
  17.218 -    void fluff(String format, Object... args) {
  17.219 -        if (feedback() != Feedback.Off && feedback() != Feedback.Concise) {
  17.220 +    void hard(String format, Object... args) {
  17.221 +        rawout(feedback.getPre() + format + feedback.getPost(), args);
  17.222 +    }
  17.223 +
  17.224 +    /**
  17.225 +     * Error command output
  17.226 +     *
  17.227 +     * @param format printf format
  17.228 +     * @param args printf args
  17.229 +     */
  17.230 +    void error(String format, Object... args) {
  17.231 +        rawout(feedback.getErrorPre() + format + feedback.getErrorPost(), args);
  17.232 +    }
  17.233 +
  17.234 +    /**
  17.235 +     * Optional output
  17.236 +     *
  17.237 +     * @param format printf format
  17.238 +     * @param args printf args
  17.239 +     */
  17.240 +    @Override
  17.241 +    public void fluff(String format, Object... args) {
  17.242 +        if (feedback.shouldDisplayCommandFluff() && interactive()) {
  17.243              hard(format, args);
  17.244          }
  17.245      }
  17.246  
  17.247      /**
  17.248 -     * For concise feedback mode only
  17.249 +     * Optional output -- with embedded per- and post-fix
  17.250 +     *
  17.251       * @param format printf format
  17.252       * @param args printf args
  17.253       */
  17.254 -    void concise(String format, Object... args) {
  17.255 -        if (feedback() == Feedback.Concise) {
  17.256 -            hard(format, args);
  17.257 +    void fluffRaw(String format, Object... args) {
  17.258 +        if (feedback.shouldDisplayCommandFluff() && interactive()) {
  17.259 +            rawout(format, args);
  17.260          }
  17.261      }
  17.262  
  17.263      /**
  17.264 -     * For all feedback modes -- must show
  17.265 -     * @param format printf format
  17.266 -     * @param args printf args
  17.267 +     * Print using resource bundle look-up and adding prefix and postfix
  17.268 +     *
  17.269 +     * @param key the resource key
  17.270       */
  17.271 -    void hard(String format, Object... args) {
  17.272 -        cmdout.printf("|  " + format + "\n", args);
  17.273 +    String getResourceString(String key) {
  17.274 +        if (outputRB == null) {
  17.275 +            try {
  17.276 +                outputRB = ResourceBundle.getBundle(L10N_RB_NAME, locale);
  17.277 +            } catch (MissingResourceException mre) {
  17.278 +                error("Cannot find ResourceBundle: %s for locale: %s", L10N_RB_NAME, locale);
  17.279 +                return "";
  17.280 +            }
  17.281 +        }
  17.282 +        String s;
  17.283 +        try {
  17.284 +            s = outputRB.getString(key);
  17.285 +        } catch (MissingResourceException mre) {
  17.286 +            error("Missing resource: %s in %s", key, L10N_RB_NAME);
  17.287 +            return "";
  17.288 +        }
  17.289 +        return s;
  17.290 +    }
  17.291 +
  17.292 +    /**
  17.293 +     * Add prefixing to embedded newlines in a string, leading with the normal
  17.294 +     * prefix
  17.295 +     *
  17.296 +     * @param s the string to prefix
  17.297 +     */
  17.298 +    String prefix(String s) {
  17.299 +        return prefix(s, feedback.getPre());
  17.300 +    }
  17.301 +
  17.302 +    /**
  17.303 +     * Add prefixing to embedded newlines in a string
  17.304 +     *
  17.305 +     * @param s the string to prefix
  17.306 +     * @param leading the string to prepend
  17.307 +     */
  17.308 +    String prefix(String s, String leading) {
  17.309 +        if (s == null || s.isEmpty()) {
  17.310 +            return "";
  17.311 +        }
  17.312 +        return leading
  17.313 +                + s.substring(0, s.length() - 1).replaceAll("\\R", System.getProperty("line.separator") + feedback.getPre())
  17.314 +                + s.substring(s.length() - 1, s.length());
  17.315 +    }
  17.316 +
  17.317 +    /**
  17.318 +     * Print using resource bundle look-up and adding prefix and postfix
  17.319 +     *
  17.320 +     * @param key the resource key
  17.321 +     */
  17.322 +    void hardrb(String key) {
  17.323 +        String s = prefix(getResourceString(key));
  17.324 +        cmdout.println(s);
  17.325 +    }
  17.326 +
  17.327 +    /**
  17.328 +     * Format using resource bundle look-up using MessageFormat
  17.329 +     *
  17.330 +     * @param key the resource key
  17.331 +     * @param args
  17.332 +     */
  17.333 +    String messageFormat(String key, Object... args) {
  17.334 +        String rs = getResourceString(key);
  17.335 +        return MessageFormat.format(rs, args);
  17.336 +    }
  17.337 +
  17.338 +    /**
  17.339 +     * Print using resource bundle look-up, MessageFormat, and add prefix and
  17.340 +     * postfix
  17.341 +     *
  17.342 +     * @param key the resource key
  17.343 +     * @param args
  17.344 +     */
  17.345 +    void hardmsg(String key, Object... args) {
  17.346 +        cmdout.println(prefix(messageFormat(key, args)));
  17.347 +    }
  17.348 +
  17.349 +    /**
  17.350 +     * Print error using resource bundle look-up, MessageFormat, and add prefix
  17.351 +     * and postfix
  17.352 +     *
  17.353 +     * @param key the resource key
  17.354 +     * @param args
  17.355 +     */
  17.356 +    @Override
  17.357 +    public void errormsg(String key, Object... args) {
  17.358 +        cmdout.println(prefix(messageFormat(key, args), feedback.getErrorPre()));
  17.359 +    }
  17.360 +
  17.361 +    /**
  17.362 +     * Print command-line error using resource bundle look-up, MessageFormat
  17.363 +     *
  17.364 +     * @param key the resource key
  17.365 +     * @param args
  17.366 +     */
  17.367 +    void startmsg(String key, Object... args) {
  17.368 +        cmderr.println(prefix(messageFormat(key, args), ""));
  17.369 +    }
  17.370 +
  17.371 +    /**
  17.372 +     * Print (fluff) using resource bundle look-up, MessageFormat, and add
  17.373 +     * prefix and postfix
  17.374 +     *
  17.375 +     * @param key the resource key
  17.376 +     * @param args
  17.377 +     */
  17.378 +    @Override
  17.379 +    public void fluffmsg(String key, Object... args) {
  17.380 +        if (feedback.shouldDisplayCommandFluff() && interactive()) {
  17.381 +            hardmsg(key, args);
  17.382 +        }
  17.383 +    }
  17.384 +
  17.385 +    <T> void hardPairs(Stream<T> stream, Function<T, String> a, Function<T, String> b) {
  17.386 +        Map<String, String> a2b = stream.collect(toMap(a, b,
  17.387 +                (m1, m2) -> m1,
  17.388 +                () -> new LinkedHashMap<>()));
  17.389 +        int aLen = 0;
  17.390 +        for (String av : a2b.keySet()) {
  17.391 +            aLen = Math.max(aLen, av.length());
  17.392 +        }
  17.393 +        String format = "   %-" + aLen + "s -- %s";
  17.394 +        String indentedNewLine = LINE_SEP + feedback.getPre()
  17.395 +                + String.format("   %-" + (aLen + 4) + "s", "");
  17.396 +        for (Entry<String, String> e : a2b.entrySet()) {
  17.397 +            hard(format, e.getKey(), e.getValue().replaceAll("\n", indentedNewLine));
  17.398 +        }
  17.399      }
  17.400  
  17.401      /**
  17.402       * Trim whitespace off end of string
  17.403 +     *
  17.404       * @param s
  17.405       * @return
  17.406       */
  17.407 @@ -231,7 +442,9 @@
  17.408       */
  17.409      public static void main(String[] args) throws Exception {
  17.410          new JShellTool(System.in, System.out, System.err, System.out,
  17.411 -                 new ByteArrayInputStream(new byte[0]), System.out, System.err)
  17.412 +                 new ByteArrayInputStream(new byte[0]), System.out, System.err,
  17.413 +                 Preferences.userRoot().node("tool/JShell"),
  17.414 +                 Locale.getDefault())
  17.415                  .start(args);
  17.416      }
  17.417  
  17.418 @@ -247,15 +460,36 @@
  17.419      }
  17.420  
  17.421      private void start(IOContext in, List<String> loadList) {
  17.422 +        // If startup hasn't been set by command line, set from retained/default
  17.423 +        if (startup == null) {
  17.424 +            startup = prefs.get(STARTUP_KEY, null);
  17.425 +            if (startup == null) {
  17.426 +                startup = DEFAULT_STARTUP;
  17.427 +            }
  17.428 +        }
  17.429 +
  17.430 +        // Read retained editor setting (if any)
  17.431 +        String editorString = prefs.get(EDITOR_KEY, "");
  17.432 +        if (editorString == null || editorString.isEmpty()) {
  17.433 +            editor = null;
  17.434 +        } else {
  17.435 +            editor = editorString.split(RECORD_SEPARATOR);
  17.436 +        }
  17.437 +
  17.438          resetState(); // Initialize
  17.439  
  17.440 +        // Read replay history from last jshell session into previous history
  17.441 +        String prevReplay = prefs.get(REPLAY_RESTORE_KEY, null);
  17.442 +        if (prevReplay != null) {
  17.443 +            replayableHistoryPrevious = Arrays.asList(prevReplay.split(RECORD_SEPARATOR));
  17.444 +        }
  17.445 +
  17.446          for (String loadFile : loadList) {
  17.447 -            cmdOpen(loadFile);
  17.448 +            runFile(loadFile, "jshell");
  17.449          }
  17.450  
  17.451          if (regenerateOnDeath) {
  17.452 -            fluff("Welcome to JShell -- Version %s", version());
  17.453 -            fluff("Type /help for help");
  17.454 +            hardmsg("jshell.msg.welcome", version());
  17.455          }
  17.456  
  17.457          try {
  17.458 @@ -286,13 +520,13 @@
  17.459                      case "-classpath":
  17.460                      case "-cp":
  17.461                          if (cmdlineClasspath != null) {
  17.462 -                            cmderr.printf("Conflicting -classpath option.\n");
  17.463 +                            startmsg("jshell.err.opt.classpath.conflict");
  17.464                              return null;
  17.465                          }
  17.466                          if (ai.hasNext()) {
  17.467                              cmdlineClasspath = ai.next();
  17.468                          } else {
  17.469 -                            cmderr.printf("Argument to -classpath missing.\n");
  17.470 +                            startmsg("jshell.err.opt.classpath.arg");
  17.471                              return null;
  17.472                          }
  17.473                          break;
  17.474 @@ -305,37 +539,46 @@
  17.475                      case "-fullversion":
  17.476                          cmdout.printf("jshell %s\n", fullVersion());
  17.477                          return null;
  17.478 -                    case "-startup":
  17.479 -                        if (cmdlineStartup != null) {
  17.480 -                            cmderr.printf("Conflicting -startup or -nostartup option.\n");
  17.481 +                    case "-feedback":
  17.482 +                        if (ai.hasNext()) {
  17.483 +                            commandLineFeedbackMode = ai.next();
  17.484 +                        } else {
  17.485 +                            startmsg("jshell.err.opt.feedback.arg");
  17.486                              return null;
  17.487                          }
  17.488 -                        if (ai.hasNext()) {
  17.489 -                            String filename = ai.next();
  17.490 -                            try {
  17.491 -                                byte[] encoded = Files.readAllBytes(Paths.get(filename));
  17.492 -                                cmdlineStartup = new String(encoded);
  17.493 -                            } catch (AccessDeniedException e) {
  17.494 -                                hard("File '%s' for start-up is not accessible.", filename);
  17.495 -                            } catch (NoSuchFileException e) {
  17.496 -                                hard("File '%s' for start-up is not found.", filename);
  17.497 -                            } catch (Exception e) {
  17.498 -                                hard("Exception while reading start-up file: %s", e);
  17.499 -                            }
  17.500 -                        } else {
  17.501 -                            cmderr.printf("Argument to -startup missing.\n");
  17.502 +                        break;
  17.503 +                    case "-q":
  17.504 +                        commandLineFeedbackMode = "concise";
  17.505 +                        break;
  17.506 +                    case "-qq":
  17.507 +                        commandLineFeedbackMode = "silent";
  17.508 +                        break;
  17.509 +                    case "-v":
  17.510 +                        commandLineFeedbackMode = "verbose";
  17.511 +                        break;
  17.512 +                    case "-startup":
  17.513 +                        if (startup != null) {
  17.514 +                            startmsg("jshell.err.opt.startup.conflict");
  17.515 +                            return null;
  17.516 +                        }
  17.517 +                        startup = readFile(ai.hasNext()? ai.next() : null, "'-startup'");
  17.518 +                        if (startup == null) {
  17.519                              return null;
  17.520                          }
  17.521                          break;
  17.522                      case "-nostartup":
  17.523 -                        if (cmdlineStartup != null && !cmdlineStartup.isEmpty()) {
  17.524 -                            cmderr.printf("Conflicting -startup option.\n");
  17.525 +                        if (startup != null && !startup.isEmpty()) {
  17.526 +                            startmsg("jshell.err.opt.startup.conflict");
  17.527                              return null;
  17.528                          }
  17.529 -                        cmdlineStartup = "";
  17.530 +                        startup = "";
  17.531                          break;
  17.532                      default:
  17.533 -                        cmderr.printf("Unknown option: %s\n", arg);
  17.534 +                        if (arg.startsWith("-R")) {
  17.535 +                            remoteVMOptions.add(arg.substring(2));
  17.536 +                            break;
  17.537 +                        }
  17.538 +                        startmsg("jshell.err.opt.unknown", arg);
  17.539                          printUsage();
  17.540                          return null;
  17.541                  }
  17.542 @@ -347,14 +590,28 @@
  17.543      }
  17.544  
  17.545      private void printUsage() {
  17.546 -        cmdout.printf("Usage:   jshell <options> <load files>\n");
  17.547 -        cmdout.printf("where possible options include:\n");
  17.548 -        cmdout.printf("  -classpath <path>          Specify where to find user class files\n");
  17.549 -        cmdout.printf("  -cp <path>                 Specify where to find user class files\n");
  17.550 -        cmdout.printf("  -startup <file>            One run replacement for the start-up definitions\n");
  17.551 -        cmdout.printf("  -nostartup                 Do not run the start-up definitions\n");
  17.552 -        cmdout.printf("  -help                      Print a synopsis of standard options\n");
  17.553 -        cmdout.printf("  -version                   Version information\n");
  17.554 +        cmdout.print(getResourceString("help.usage"));
  17.555 +    }
  17.556 +
  17.557 +    /**
  17.558 +     * Message handler to use during initial start-up.
  17.559 +     */
  17.560 +    private class InitMessageHandler implements MessageHandler {
  17.561 +
  17.562 +        @Override
  17.563 +        public void fluff(String format, Object... args) {
  17.564 +            //ignore
  17.565 +        }
  17.566 +
  17.567 +        @Override
  17.568 +        public void fluffmsg(String messageKey, Object... args) {
  17.569 +            //ignore
  17.570 +        }
  17.571 +
  17.572 +        @Override
  17.573 +        public void errormsg(String messageKey, Object... args) {
  17.574 +            startmsg(messageKey, args);
  17.575 +        }
  17.576      }
  17.577  
  17.578      private void resetState() {
  17.579 @@ -367,6 +624,10 @@
  17.580          mapSnippet = new LinkedHashMap<>();
  17.581          currentNameSpace = startNamespace;
  17.582  
  17.583 +        // Reset the replayable history, saving the old for restore
  17.584 +        replayableHistoryPrevious = replayableHistory;
  17.585 +        replayableHistory = new ArrayList<>();
  17.586 +
  17.587          state = JShell.builder()
  17.588                  .in(userin)
  17.589                  .out(userout)
  17.590 @@ -375,37 +636,70 @@
  17.591                  .idGenerator((sn, i) -> (currentNameSpace == startNamespace || state.status(sn).isActive)
  17.592                          ? currentNameSpace.tid(sn)
  17.593                          : errorNamespace.tid(sn))
  17.594 +                .remoteVMOptions(remoteVMOptions.toArray(new String[remoteVMOptions.size()]))
  17.595                  .build();
  17.596 -        analysis = state.sourceCodeAnalysis();
  17.597          shutdownSubscription = state.onShutdown((JShell deadState) -> {
  17.598              if (deadState == state) {
  17.599 -                hard("State engine terminated.  See /history");
  17.600 +                hardmsg("jshell.msg.terminated");
  17.601                  live = false;
  17.602              }
  17.603          });
  17.604 +        analysis = state.sourceCodeAnalysis();
  17.605          live = true;
  17.606 +        if (!feedbackInitialized) {
  17.607 +            // One time per run feedback initialization
  17.608 +            feedbackInitialized = true;
  17.609 +            initFeedback();
  17.610 +        }
  17.611  
  17.612          if (cmdlineClasspath != null) {
  17.613              state.addToClasspath(cmdlineClasspath);
  17.614          }
  17.615  
  17.616 +        startUpRun(startup);
  17.617 +        currentNameSpace = mainNamespace;
  17.618 +    }
  17.619  
  17.620 -        String start;
  17.621 -        if (cmdlineStartup == null) {
  17.622 -            start = PREFS.get(STARTUP_KEY, "<nada>");
  17.623 -            if (start.equals("<nada>")) {
  17.624 -                start = DEFAULT_STARTUP;
  17.625 -                PREFS.put(STARTUP_KEY, DEFAULT_STARTUP);
  17.626 +    //where -- one-time per run initialization of feedback modes
  17.627 +    private void initFeedback() {
  17.628 +        // No fluff, no prefix, for init failures
  17.629 +        MessageHandler initmh = new InitMessageHandler();
  17.630 +        // Execute the feedback initialization code in the resource file
  17.631 +        startUpRun(getResourceString("startup.feedback"));
  17.632 +        // These predefined modes are read-only
  17.633 +        feedback.markModesReadOnly();
  17.634 +        // Restore user defined modes retained on previous run with /retain mode
  17.635 +        String encoded = prefs.get(MODE_KEY, null);
  17.636 +        if (encoded != null && !encoded.isEmpty()) {
  17.637 +            if (!feedback.restoreEncodedModes(initmh, encoded)) {
  17.638 +                // Catastrophic corruption -- remove the retained modes
  17.639 +                prefs.remove(MODE_KEY);
  17.640              }
  17.641 +        }
  17.642 +        if (commandLineFeedbackMode != null) {
  17.643 +            // The feedback mode to use was specified on the command line, use it
  17.644 +            if (!feedback.setFeedback(initmh, new ArgTokenizer("-feedback", commandLineFeedbackMode))) {
  17.645 +                regenerateOnDeath = false;
  17.646 +            }
  17.647 +            commandLineFeedbackMode = null;
  17.648          } else {
  17.649 -            start = cmdlineStartup;
  17.650 +            String fb = prefs.get(FEEDBACK_KEY, null);
  17.651 +            if (fb != null) {
  17.652 +                // Restore the feedback mode to use that was retained
  17.653 +                // on a previous run with /retain feedback
  17.654 +                feedback.retainFeedback(initmh, new ArgTokenizer("/retain feedback", fb));
  17.655 +            }
  17.656          }
  17.657 +    }
  17.658 +
  17.659 +    //where
  17.660 +    private void startUpRun(String start) {
  17.661          try (IOContext suin = new FileScannerIOContext(new StringReader(start))) {
  17.662              run(suin);
  17.663          } catch (Exception ex) {
  17.664 -            hard("Unexpected exception reading start-up: %s\n", ex);
  17.665 +            hardmsg("jshell.err.startup.unexpected.exception", ex);
  17.666 +            ex.printStackTrace(cmdout);
  17.667          }
  17.668 -        currentNameSpace = mainNamespace;
  17.669      }
  17.670  
  17.671      private void closeState() {
  17.672 @@ -428,16 +722,14 @@
  17.673              String incomplete = "";
  17.674              while (live) {
  17.675                  String prompt;
  17.676 -                if (in.interactiveOutput() && displayPrompt) {
  17.677 +                if (currentNameSpace == mainNamespace) {
  17.678                      prompt = testPrompt
  17.679                                      ? incomplete.isEmpty()
  17.680                                              ? "\u0005" //ENQ
  17.681                                              : "\u0006" //ACK
  17.682                                      : incomplete.isEmpty()
  17.683 -                                            ? feedback() == Feedback.Concise
  17.684 -                                                    ? "-> "
  17.685 -                                                    : "\n-> "
  17.686 -                                            : ">> "
  17.687 +                                            ? feedback.getPrompt(currentNameSpace.tidNext())
  17.688 +                                            : feedback.getContinuationPrompt(currentNameSpace.tidNext())
  17.689                      ;
  17.690                  } else {
  17.691                      prompt = "";
  17.692 @@ -471,12 +763,18 @@
  17.693                  }
  17.694              }
  17.695          } catch (IOException ex) {
  17.696 -            hard("Unexpected exception: %s\n", ex);
  17.697 +            errormsg("jshell.err.unexpected.exception", ex);
  17.698          } finally {
  17.699              input = oldInput;
  17.700          }
  17.701      }
  17.702  
  17.703 +    private void addToReplayHistory(String s) {
  17.704 +        if (currentNameSpace == mainNamespace) {
  17.705 +            replayableHistory.add(s);
  17.706 +        }
  17.707 +    }
  17.708 +
  17.709      private String processSourceCatchingReset(String src) {
  17.710          try {
  17.711              input.beforeUserCode();
  17.712 @@ -491,12 +789,14 @@
  17.713      }
  17.714  
  17.715      private void processCommand(String cmd) {
  17.716 -        try {
  17.717 -            //handle "/[number]"
  17.718 -            cmdUseHistoryEntry(Integer.parseInt(cmd.substring(1)));
  17.719 -            return ;
  17.720 -        } catch (NumberFormatException ex) {
  17.721 -            //ignore
  17.722 +        if (cmd.startsWith("/-")) {
  17.723 +            try {
  17.724 +                //handle "/-[number]"
  17.725 +                cmdUseHistoryEntry(Integer.parseInt(cmd.substring(1)));
  17.726 +                return ;
  17.727 +            } catch (NumberFormatException ex) {
  17.728 +                //ignore
  17.729 +            }
  17.730          }
  17.731          String arg = "";
  17.732          int idx = cmd.indexOf(' ');
  17.733 @@ -504,15 +804,39 @@
  17.734              arg = cmd.substring(idx + 1).trim();
  17.735              cmd = cmd.substring(0, idx);
  17.736          }
  17.737 -        Command command = commands.get(cmd);
  17.738 -        if (command == null || command.kind == CommandKind.HELP_ONLY) {
  17.739 -            hard("No such command: %s", cmd);
  17.740 -            fluff("Type /help for help.");
  17.741 -        } else {
  17.742 -            command.run.accept(arg);
  17.743 +        Command[] candidates = findCommand(cmd, c -> c.kind.isRealCommand);
  17.744 +        switch (candidates.length) {
  17.745 +            case 0:
  17.746 +                if (!rerunHistoryEntryById(cmd.substring(1))) {
  17.747 +                    errormsg("jshell.err.no.such.command.or.snippet.id", cmd);
  17.748 +                    fluffmsg("jshell.msg.help.for.help");
  17.749 +                }   break;
  17.750 +            case 1:
  17.751 +                Command command = candidates[0];
  17.752 +                // If comand was successful and is of a replayable kind, add it the replayable history
  17.753 +                if (command.run.apply(arg) && command.kind == CommandKind.REPLAY) {
  17.754 +                    addToReplayHistory((command.command + " " + arg).trim());
  17.755 +                }   break;
  17.756 +            default:
  17.757 +                errormsg("jshell.err.command.ambiguous", cmd,
  17.758 +                        Arrays.stream(candidates).map(c -> c.command).collect(Collectors.joining(", ")));
  17.759 +                fluffmsg("jshell.msg.help.for.help");
  17.760 +                break;
  17.761          }
  17.762      }
  17.763  
  17.764 +    private Command[] findCommand(String cmd, Predicate<Command> filter) {
  17.765 +        Command exact = commands.get(cmd);
  17.766 +        if (exact != null)
  17.767 +            return new Command[] {exact};
  17.768 +
  17.769 +        return commands.values()
  17.770 +                       .stream()
  17.771 +                       .filter(filter)
  17.772 +                       .filter(command -> command.command.startsWith(cmd))
  17.773 +                       .toArray(size -> new Command[size]);
  17.774 +    }
  17.775 +
  17.776      private static Path toPathResolvingUserHome(String pathString) {
  17.777          if (pathString.replace(File.separatorChar, '/').startsWith("~/"))
  17.778              return Paths.get(System.getProperty("user.home"), pathString.substring(2));
  17.779 @@ -521,21 +845,34 @@
  17.780      }
  17.781  
  17.782      static final class Command {
  17.783 -        public final String[] aliases;
  17.784 -        public final String params;
  17.785 -        public final String description;
  17.786 -        public final Consumer<String> run;
  17.787 +        public final String command;
  17.788 +        public final String helpKey;
  17.789 +        public final Function<String,Boolean> run;
  17.790          public final CompletionProvider completions;
  17.791          public final CommandKind kind;
  17.792  
  17.793 -        public Command(String command, String alias, String params, String description, Consumer<String> run, CompletionProvider completions) {
  17.794 -            this(command, alias, params, description, run, completions, CommandKind.NORMAL);
  17.795 +        // NORMAL Commands
  17.796 +        public Command(String command, Function<String,Boolean> run, CompletionProvider completions) {
  17.797 +            this(command, run, completions, CommandKind.NORMAL);
  17.798          }
  17.799  
  17.800 -        public Command(String command, String alias, String params, String description, Consumer<String> run, CompletionProvider completions, CommandKind kind) {
  17.801 -            this.aliases = alias != null ? new String[] {command, alias} : new String[] {command};
  17.802 -            this.params = params;
  17.803 -            this.description = description;
  17.804 +        // Special kinds of Commands
  17.805 +        public Command(String command, Function<String,Boolean> run, CompletionProvider completions, CommandKind kind) {
  17.806 +            this(command, "help." + command.substring(1),
  17.807 +                    run, completions, kind);
  17.808 +        }
  17.809 +
  17.810 +        // Documentation pseudo-commands
  17.811 +        public Command(String command, String helpKey, CommandKind kind) {
  17.812 +            this(command, helpKey,
  17.813 +                    arg -> { throw new IllegalStateException(); },
  17.814 +                    EMPTY_COMPLETION_PROVIDER,
  17.815 +                    kind);
  17.816 +        }
  17.817 +
  17.818 +        public Command(String command, String helpKey, Function<String,Boolean> run, CompletionProvider completions, CommandKind kind) {
  17.819 +            this.command = command;
  17.820 +            this.helpKey = helpKey;
  17.821              this.run = run;
  17.822              this.completions = completions;
  17.823              this.kind = kind;
  17.824 @@ -548,9 +885,20 @@
  17.825      }
  17.826  
  17.827      enum CommandKind {
  17.828 -        NORMAL,
  17.829 -        HIDDEN,
  17.830 -        HELP_ONLY;
  17.831 +        NORMAL(true, true, true),
  17.832 +        REPLAY(true, true, true),
  17.833 +        HIDDEN(true, false, false),
  17.834 +        HELP_ONLY(false, true, false),
  17.835 +        HELP_SUBJECT(false, false, false);
  17.836 +
  17.837 +        final boolean isRealCommand;
  17.838 +        final boolean showInHelp;
  17.839 +        final boolean shouldSuggestCompletions;
  17.840 +        private CommandKind(boolean isRealCommand, boolean showInHelp, boolean shouldSuggestCompletions) {
  17.841 +            this.isRealCommand = isRealCommand;
  17.842 +            this.showInHelp = showInHelp;
  17.843 +            this.shouldSuggestCompletions = shouldSuggestCompletions;
  17.844 +        }
  17.845      }
  17.846  
  17.847      static final class FixedCompletionProvider implements CompletionProvider {
  17.848 @@ -579,12 +927,12 @@
  17.849      }
  17.850  
  17.851      private static final CompletionProvider EMPTY_COMPLETION_PROVIDER = new FixedCompletionProvider();
  17.852 +    private static final CompletionProvider KEYWORD_COMPLETION_PROVIDER = new FixedCompletionProvider("-all ", "-start ", "-history ");
  17.853 +    private static final CompletionProvider RELOAD_OPTIONS_COMPLETION_PROVIDER = new FixedCompletionProvider("-restore", "-quiet");
  17.854      private static final CompletionProvider FILE_COMPLETION_PROVIDER = fileCompletions(p -> true);
  17.855      private final Map<String, Command> commands = new LinkedHashMap<>();
  17.856      private void registerCommand(Command cmd) {
  17.857 -        for (String str : cmd.aliases) {
  17.858 -            commands.put(str, cmd);
  17.859 -        }
  17.860 +        commands.put(cmd.command, cmd);
  17.861      }
  17.862      private static CompletionProvider fileCompletions(Predicate<Path> accept) {
  17.863          return (code, cursor, anchor) -> {
  17.864 @@ -617,11 +965,10 @@
  17.865                                      p.getFileName().toString().endsWith(".jar"));
  17.866      }
  17.867  
  17.868 -    private CompletionProvider editCompletion() {
  17.869 +    private CompletionProvider snippetCompletion(Supplier<List<? extends Snippet>> snippetsSupplier) {
  17.870          return (prefix, cursor, anchor) -> {
  17.871              anchor[0] = 0;
  17.872 -            return state.snippets()
  17.873 -                        .stream()
  17.874 +            return snippetsSupplier.get()                        .stream()
  17.875                          .flatMap(k -> (k instanceof DeclarationSnippet)
  17.876                                  ? Stream.of(String.valueOf(k.id()), ((DeclarationSnippet) k).name())
  17.877                                  : Stream.of(String.valueOf(k.id())))
  17.878 @@ -631,13 +978,21 @@
  17.879          };
  17.880      }
  17.881  
  17.882 +    private CompletionProvider snippetKeywordCompletion(Supplier<List<? extends Snippet>> snippetsSupplier) {
  17.883 +        return (code, cursor, anchor) -> {
  17.884 +            List<Suggestion> result = new ArrayList<>();
  17.885 +            result.addAll(KEYWORD_COMPLETION_PROVIDER.completionSuggestions(code, cursor, anchor));
  17.886 +            result.addAll(snippetCompletion(snippetsSupplier).completionSuggestions(code, cursor, anchor));
  17.887 +            return result;
  17.888 +        };
  17.889 +    }
  17.890 +
  17.891      private static CompletionProvider saveCompletion() {
  17.892 -        CompletionProvider keyCompletion = new FixedCompletionProvider("all ", "history ");
  17.893          return (code, cursor, anchor) -> {
  17.894              List<Suggestion> result = new ArrayList<>();
  17.895              int space = code.indexOf(' ');
  17.896              if (space == (-1)) {
  17.897 -                result.addAll(keyCompletion.completionSuggestions(code, cursor, anchor));
  17.898 +                result.addAll(KEYWORD_COMPLETION_PROVIDER.completionSuggestions(code, cursor, anchor));
  17.899              }
  17.900              result.addAll(FILE_COMPLETION_PROVIDER.completionSuggestions(code.substring(space + 1), cursor - space - 1, anchor));
  17.901              anchor[0] += space + 1;
  17.902 @@ -645,81 +1000,134 @@
  17.903          };
  17.904      }
  17.905  
  17.906 -    // Table of commands -- with command forms, argument kinds, help message, implementation, ...
  17.907 +    private static CompletionProvider reloadCompletion() {
  17.908 +        return (code, cursor, anchor) -> {
  17.909 +            List<Suggestion> result = new ArrayList<>();
  17.910 +            int pastSpace = code.indexOf(' ') + 1; // zero if no space
  17.911 +            result.addAll(RELOAD_OPTIONS_COMPLETION_PROVIDER.completionSuggestions(code.substring(pastSpace), cursor - pastSpace, anchor));
  17.912 +            anchor[0] += pastSpace;
  17.913 +            return result;
  17.914 +        };
  17.915 +    }
  17.916 +
  17.917 +    // Snippet lists
  17.918 +
  17.919 +    List<Snippet> allSnippets() {
  17.920 +        return state.snippets();
  17.921 +    }
  17.922 +
  17.923 +    List<PersistentSnippet> dropableSnippets() {
  17.924 +        return state.snippets().stream()
  17.925 +                .filter(sn -> state.status(sn).isActive && sn instanceof PersistentSnippet)
  17.926 +                .map(sn -> (PersistentSnippet) sn)
  17.927 +                .collect(toList());
  17.928 +    }
  17.929 +
  17.930 +    List<VarSnippet> allVarSnippets() {
  17.931 +        return state.snippets().stream()
  17.932 +                .filter(sn -> sn.kind() == Snippet.Kind.VAR)
  17.933 +                .map(sn -> (VarSnippet) sn)
  17.934 +                .collect(toList());
  17.935 +    }
  17.936 +
  17.937 +    List<MethodSnippet> allMethodSnippets() {
  17.938 +        return state.snippets().stream()
  17.939 +                .filter(sn -> sn.kind() == Snippet.Kind.METHOD)
  17.940 +                .map(sn -> (MethodSnippet) sn)
  17.941 +                .collect(toList());
  17.942 +    }
  17.943 +
  17.944 +    List<TypeDeclSnippet> allTypeSnippets() {
  17.945 +        return state.snippets().stream()
  17.946 +                .filter(sn -> sn.kind() == Snippet.Kind.TYPE_DECL)
  17.947 +                .map(sn -> (TypeDeclSnippet) sn)
  17.948 +                .collect(toList());
  17.949 +    }
  17.950 +
  17.951 +    // Table of commands -- with command forms, argument kinds, helpKey message, implementation, ...
  17.952  
  17.953      {
  17.954 -        registerCommand(new Command("/list", "/l", "[all]", "list the source you have typed",
  17.955 -                                    arg -> cmdList(arg),
  17.956 -                                    new FixedCompletionProvider("all")));
  17.957 -        registerCommand(new Command("/seteditor", null, "<executable>", "set the external editor command to use",
  17.958 -                                    arg -> cmdSetEditor(arg),
  17.959 -                                    EMPTY_COMPLETION_PROVIDER));
  17.960 -        registerCommand(new Command("/edit", "/e", "<name or id>", "edit a source entry referenced by name or id",
  17.961 -                                    arg -> cmdEdit(arg),
  17.962 -                                    editCompletion()));
  17.963 -        registerCommand(new Command("/drop", "/d", "<name or id>", "delete a source entry referenced by name or id",
  17.964 -                                    arg -> cmdDrop(arg),
  17.965 -                                    editCompletion()));
  17.966 -        registerCommand(new Command("/save", "/s", "[all|history] <file>", "save the source you have typed",
  17.967 -                                    arg -> cmdSave(arg),
  17.968 -                                    saveCompletion()));
  17.969 -        registerCommand(new Command("/open", "/o", "<file>", "open a file as source input",
  17.970 -                                    arg -> cmdOpen(arg),
  17.971 -                                    FILE_COMPLETION_PROVIDER));
  17.972 -        registerCommand(new Command("/vars", "/v", null, "list the declared variables and their values",
  17.973 -                                    arg -> cmdVars(),
  17.974 -                                    EMPTY_COMPLETION_PROVIDER));
  17.975 -        registerCommand(new Command("/methods", "/m", null, "list the declared methods and their signatures",
  17.976 -                                    arg -> cmdMethods(),
  17.977 -                                    EMPTY_COMPLETION_PROVIDER));
  17.978 -        registerCommand(new Command("/classes", "/c", null, "list the declared classes",
  17.979 -                                    arg -> cmdClasses(),
  17.980 -                                    EMPTY_COMPLETION_PROVIDER));
  17.981 -        registerCommand(new Command("/imports", "/i", null, "list the imported items",
  17.982 -                                    arg -> cmdImports(),
  17.983 -                                    EMPTY_COMPLETION_PROVIDER));
  17.984 -        registerCommand(new Command("/exit", "/x", null, "exit the REPL",
  17.985 -                                    arg -> cmdExit(),
  17.986 -                                    EMPTY_COMPLETION_PROVIDER));
  17.987 -        registerCommand(new Command("/reset", "/r", null, "reset everything in the REPL",
  17.988 -                                    arg -> cmdReset(),
  17.989 -                                    EMPTY_COMPLETION_PROVIDER));
  17.990 -        registerCommand(new Command("/feedback", "/f", "<level>", "feedback information: off, concise, normal, verbose, default, or ?",
  17.991 -                                    arg -> cmdFeedback(arg),
  17.992 -                                    new FixedCompletionProvider("off", "concise", "normal", "verbose", "default", "?")));
  17.993 -        registerCommand(new Command("/prompt", "/p", null, "toggle display of a prompt",
  17.994 -                                    arg -> cmdPrompt(),
  17.995 -                                    EMPTY_COMPLETION_PROVIDER));
  17.996 -        registerCommand(new Command("/classpath", "/cp", "<path>", "add a path to the classpath",
  17.997 -                                    arg -> cmdClasspath(arg),
  17.998 -                                    classPathCompletion()));
  17.999 -        registerCommand(new Command("/history", "/h", null, "history of what you have typed",
 17.1000 -                                    arg -> cmdHistory(),
 17.1001 -                                    EMPTY_COMPLETION_PROVIDER));
 17.1002 -        registerCommand(new Command("/setstart", null, "<file>", "read file and set as the new start-up definitions",
 17.1003 -                                    arg -> cmdSetStart(arg),
 17.1004 -                                    FILE_COMPLETION_PROVIDER));
 17.1005 -        registerCommand(new Command("/savestart", null, "<file>", "save the default start-up definitions to the file",
 17.1006 -                                    arg -> cmdSaveStart(arg),
 17.1007 -                                    FILE_COMPLETION_PROVIDER));
 17.1008 -        registerCommand(new Command("/debug", "/db", "", "toggle debugging of the REPL",
 17.1009 -                                    arg -> cmdDebug(arg),
 17.1010 -                                    EMPTY_COMPLETION_PROVIDER,
 17.1011 -                                    CommandKind.HIDDEN));
 17.1012 -        registerCommand(new Command("/help", "/?", "", "this help message",
 17.1013 -                                    arg -> cmdHelp(),
 17.1014 -                                    EMPTY_COMPLETION_PROVIDER));
 17.1015 -        registerCommand(new Command("/!", null, "", "re-run last snippet",
 17.1016 -                                    arg -> cmdUseHistoryEntry(-1),
 17.1017 -                                    EMPTY_COMPLETION_PROVIDER));
 17.1018 -        registerCommand(new Command("/<n>", null, "", "re-run n-th snippet",
 17.1019 -                                    arg -> { throw new IllegalStateException(); },
 17.1020 -                                    EMPTY_COMPLETION_PROVIDER,
 17.1021 -                                    CommandKind.HELP_ONLY));
 17.1022 -        registerCommand(new Command("/-<n>", null, "", "re-run n-th previous snippet",
 17.1023 -                                    arg -> { throw new IllegalStateException(); },
 17.1024 -                                    EMPTY_COMPLETION_PROVIDER,
 17.1025 -                                    CommandKind.HELP_ONLY));
 17.1026 +        registerCommand(new Command("/list",
 17.1027 +                arg -> cmdList(arg),
 17.1028 +                snippetKeywordCompletion(this::allSnippets)));
 17.1029 +        registerCommand(new Command("/edit",
 17.1030 +                arg -> cmdEdit(arg),
 17.1031 +                snippetCompletion(this::allSnippets)));
 17.1032 +        registerCommand(new Command("/drop",
 17.1033 +                arg -> cmdDrop(arg),
 17.1034 +                snippetCompletion(this::dropableSnippets),
 17.1035 +                CommandKind.REPLAY));
 17.1036 +        registerCommand(new Command("/save",
 17.1037 +                arg -> cmdSave(arg),
 17.1038 +                saveCompletion()));
 17.1039 +        registerCommand(new Command("/open",
 17.1040 +                arg -> cmdOpen(arg),
 17.1041 +                FILE_COMPLETION_PROVIDER));
 17.1042 +        registerCommand(new Command("/vars",
 17.1043 +                arg -> cmdVars(arg),
 17.1044 +                snippetKeywordCompletion(this::allVarSnippets)));
 17.1045 +        registerCommand(new Command("/methods",
 17.1046 +                arg -> cmdMethods(arg),
 17.1047 +                snippetKeywordCompletion(this::allMethodSnippets)));
 17.1048 +        registerCommand(new Command("/types",
 17.1049 +                arg -> cmdTypes(arg),
 17.1050 +                snippetKeywordCompletion(this::allTypeSnippets)));
 17.1051 +        registerCommand(new Command("/imports",
 17.1052 +                arg -> cmdImports(),
 17.1053 +                EMPTY_COMPLETION_PROVIDER));
 17.1054 +        registerCommand(new Command("/exit",
 17.1055 +                arg -> cmdExit(),
 17.1056 +                EMPTY_COMPLETION_PROVIDER));
 17.1057 +        registerCommand(new Command("/reset",
 17.1058 +                arg -> cmdReset(),
 17.1059 +                EMPTY_COMPLETION_PROVIDER));
 17.1060 +        registerCommand(new Command("/reload",
 17.1061 +                arg -> cmdReload(arg),
 17.1062 +                reloadCompletion()));
 17.1063 +        registerCommand(new Command("/classpath",
 17.1064 +                arg -> cmdClasspath(arg),
 17.1065 +                classPathCompletion(),
 17.1066 +                CommandKind.REPLAY));
 17.1067 +        registerCommand(new Command("/history",
 17.1068 +                arg -> cmdHistory(),
 17.1069 +                EMPTY_COMPLETION_PROVIDER));
 17.1070 +        registerCommand(new Command("/debug",
 17.1071 +                arg -> cmdDebug(arg),
 17.1072 +                EMPTY_COMPLETION_PROVIDER,
 17.1073 +                CommandKind.HIDDEN));
 17.1074 +        registerCommand(new Command("/help",
 17.1075 +                arg -> cmdHelp(arg),
 17.1076 +                EMPTY_COMPLETION_PROVIDER));
 17.1077 +        registerCommand(new Command("/set",
 17.1078 +                arg -> cmdSet(arg),
 17.1079 +                new FixedCompletionProvider(SET_SUBCOMMANDS)));
 17.1080 +        registerCommand(new Command("/retain",
 17.1081 +                arg -> cmdRetain(arg),
 17.1082 +                new FixedCompletionProvider(RETAIN_SUBCOMMANDS)));
 17.1083 +        registerCommand(new Command("/?",
 17.1084 +                "help.quest",
 17.1085 +                arg -> cmdHelp(arg),
 17.1086 +                EMPTY_COMPLETION_PROVIDER,
 17.1087 +                CommandKind.NORMAL));
 17.1088 +        registerCommand(new Command("/!",
 17.1089 +                "help.bang",
 17.1090 +                arg -> cmdUseHistoryEntry(-1),
 17.1091 +                EMPTY_COMPLETION_PROVIDER,
 17.1092 +                CommandKind.NORMAL));
 17.1093 +
 17.1094 +        // Documentation pseudo-commands
 17.1095 +        registerCommand(new Command("/<id>",
 17.1096 +                "help.id",
 17.1097 +                CommandKind.HELP_ONLY));
 17.1098 +        registerCommand(new Command("/-<n>",
 17.1099 +                "help.previous",
 17.1100 +                CommandKind.HELP_ONLY));
 17.1101 +        registerCommand(new Command("intro",
 17.1102 +                "help.intro",
 17.1103 +                CommandKind.HELP_SUBJECT));
 17.1104 +        registerCommand(new Command("shortcuts",
 17.1105 +                "help.shortcuts",
 17.1106 +                CommandKind.HELP_SUBJECT));
 17.1107      }
 17.1108  
 17.1109      public List<Suggestion> commandCompletionSuggestions(String code, int cursor, int[] anchor) {
 17.1110 @@ -731,17 +1139,17 @@
 17.1111              result = commands.values()
 17.1112                               .stream()
 17.1113                               .distinct()
 17.1114 -                             .filter(cmd -> cmd.kind != CommandKind.HIDDEN && cmd.kind != CommandKind.HELP_ONLY)
 17.1115 -                             .map(cmd -> cmd.aliases[0])
 17.1116 +                             .filter(cmd -> cmd.kind.shouldSuggestCompletions)
 17.1117 +                             .map(cmd -> cmd.command)
 17.1118                               .filter(key -> key.startsWith(prefix))
 17.1119                               .map(key -> new Suggestion(key + " ", false));
 17.1120              anchor[0] = 0;
 17.1121          } else {
 17.1122              String arg = prefix.substring(space + 1);
 17.1123              String cmd = prefix.substring(0, space);
 17.1124 -            Command command = commands.get(cmd);
 17.1125 -            if (command != null) {
 17.1126 -                result = command.completions.completionSuggestions(arg, cursor - space, anchor).stream();
 17.1127 +            Command[] candidates = findCommand(cmd, c -> true);
 17.1128 +            if (candidates.length == 1) {
 17.1129 +                result = candidates[0].completions.completionSuggestions(arg, cursor - space, anchor).stream();
 17.1130                  anchor[0] += space + 1;
 17.1131              } else {
 17.1132                  result = Stream.empty();
 17.1133 @@ -760,7 +1168,7 @@
 17.1134              String cmd = code.substring(0, space);
 17.1135              Command command = commands.get(cmd);
 17.1136              if (command != null) {
 17.1137 -                return command.description;
 17.1138 +                return getResourceString(command.helpKey + ".summary");
 17.1139              }
 17.1140          }
 17.1141  
 17.1142 @@ -769,28 +1177,197 @@
 17.1143  
 17.1144      // --- Command implementations ---
 17.1145  
 17.1146 -    void cmdSetEditor(String arg) {
 17.1147 -        if (arg.isEmpty()) {
 17.1148 -            hard("/seteditor requires a path argument");
 17.1149 -        } else {
 17.1150 -            editor = arg;
 17.1151 -            fluff("Editor set to: %s", arg);
 17.1152 +    private static final String[] SET_SUBCOMMANDS = new String[]{
 17.1153 +        "format", "truncation", "feedback", "mode", "prompt", "editor", "start"};
 17.1154 +
 17.1155 +    private static final String[] RETAIN_SUBCOMMANDS = new String[]{
 17.1156 +        "feedback", "mode", "editor", "start"};
 17.1157 +
 17.1158 +    final boolean cmdSet(String arg) {
 17.1159 +        String cmd = "/set";
 17.1160 +        ArgTokenizer at = new ArgTokenizer(cmd, arg.trim());
 17.1161 +        String which = subCommand(cmd, at, SET_SUBCOMMANDS);
 17.1162 +        if (which == null) {
 17.1163 +            return false;
 17.1164 +        }
 17.1165 +        switch (which) {
 17.1166 +            case "format":
 17.1167 +                return feedback.setFormat(this, at);
 17.1168 +            case "truncation":
 17.1169 +                return feedback.setTruncation(this, at);
 17.1170 +            case "feedback":
 17.1171 +                return feedback.setFeedback(this, at);
 17.1172 +            case "mode":
 17.1173 +                return feedback.setMode(this, at);
 17.1174 +            case "prompt":
 17.1175 +                return feedback.setPrompt(this, at);
 17.1176 +            case "editor":
 17.1177 +                return setEditor(at, true);
 17.1178 +            case "start":
 17.1179 +                return setStart(cmd, at, true);
 17.1180 +            default:
 17.1181 +                errormsg("jshell.err.arg", cmd, at.val());
 17.1182 +                return false;
 17.1183          }
 17.1184      }
 17.1185  
 17.1186 -    void cmdClasspath(String arg) {
 17.1187 -        if (arg.isEmpty()) {
 17.1188 -            hard("/classpath requires a path argument");
 17.1189 -        } else {
 17.1190 -            state.addToClasspath(toPathResolvingUserHome(arg).toString());
 17.1191 -            fluff("Path %s added to classpath", arg);
 17.1192 +    final boolean cmdRetain(String arg) {
 17.1193 +        String cmd = "/retain";
 17.1194 +        ArgTokenizer at = new ArgTokenizer(cmd, arg.trim());
 17.1195 +        String which = subCommand(cmd, at, RETAIN_SUBCOMMANDS);
 17.1196 +        if (which == null) {
 17.1197 +            return false;
 17.1198 +        }
 17.1199 +        switch (which) {
 17.1200 +            case "feedback": {
 17.1201 +                String fb = feedback.retainFeedback(this, at);
 17.1202 +                if (fb != null) {
 17.1203 +                    // If a feedback mode has been set now, or in the past, retain it
 17.1204 +                    prefs.put(FEEDBACK_KEY, fb);
 17.1205 +                    return true;
 17.1206 +                }
 17.1207 +                return false;
 17.1208 +            }
 17.1209 +            case "mode":
 17.1210 +                String retained = feedback.retainMode(this, at);
 17.1211 +                if (retained != null) {
 17.1212 +                    // Retain this mode and all previously retained modes
 17.1213 +                    prefs.put(MODE_KEY, retained);
 17.1214 +                    return true;
 17.1215 +                }
 17.1216 +                return false;
 17.1217 +            case "editor":
 17.1218 +                if (!setEditor(at, false)) {
 17.1219 +                    return false;
 17.1220 +                }
 17.1221 +                // retain editor setting
 17.1222 +                prefs.put(EDITOR_KEY, (editor == null)
 17.1223 +                        ? ""
 17.1224 +                        : String.join(RECORD_SEPARATOR, editor));
 17.1225 +                return true;
 17.1226 +            case "start": {
 17.1227 +                if (!setStart(cmd, at, false)) {
 17.1228 +                    return false;
 17.1229 +                }
 17.1230 +                // retain startup setting
 17.1231 +                prefs.put(STARTUP_KEY, startup);
 17.1232 +                return true;
 17.1233 +            }
 17.1234 +            default:
 17.1235 +                errormsg("jshell.err.arg", cmd, at.val());
 17.1236 +                return false;
 17.1237          }
 17.1238      }
 17.1239  
 17.1240 -    void cmdDebug(String arg) {
 17.1241 +    // Print the help doc for the specified sub-command
 17.1242 +    boolean printSubCommandHelp(String cmd, ArgTokenizer at, String helpPrefix, String[] subs) {
 17.1243 +        String which = subCommand(cmd, at, subs);
 17.1244 +        if (which == null) {
 17.1245 +            return false;
 17.1246 +        }
 17.1247 +        hardrb(helpPrefix + which);
 17.1248 +        return true;
 17.1249 +    }
 17.1250 +
 17.1251 +    // Find which, if any, sub-command matches
 17.1252 +    String subCommand(String cmd, ArgTokenizer at, String[] subs) {
 17.1253 +        String[] matches = at.next(subs);
 17.1254 +        if (matches == null) {
 17.1255 +            // No sub-command was given
 17.1256 +            errormsg("jshell.err.sub.arg", cmd);
 17.1257 +            return null;
 17.1258 +        }
 17.1259 +        if (matches.length == 0) {
 17.1260 +            // There are no matching sub-commands
 17.1261 +            errormsg("jshell.err.arg", cmd, at.val());
 17.1262 +            fluffmsg("jshell.msg.use.one.of", Arrays.stream(subs)
 17.1263 +                    .collect(Collectors.joining(", "))
 17.1264 +            );
 17.1265 +            return null;
 17.1266 +        }
 17.1267 +        if (matches.length > 1) {
 17.1268 +            // More than one sub-command matches the initial characters provided
 17.1269 +            errormsg("jshell.err.sub.ambiguous", cmd, at.val());
 17.1270 +            fluffmsg("jshell.msg.use.one.of", Arrays.stream(matches)
 17.1271 +                    .collect(Collectors.joining(", "))
 17.1272 +            );
 17.1273 +            return null;
 17.1274 +        }
 17.1275 +        return matches[0];
 17.1276 +    }
 17.1277 +
 17.1278 +    // The sub-command:  /set editor <editor-command-line>>
 17.1279 +    boolean setEditor(ArgTokenizer at, boolean argsRequired) {
 17.1280 +        at.allowedOptions("-default");
 17.1281 +        String prog = at.next();
 17.1282 +        List<String> ed = new ArrayList<>();
 17.1283 +        while (at.val() != null) {
 17.1284 +            ed.add(at.val());
 17.1285 +            at.nextToken();
 17.1286 +        }
 17.1287 +        if (!checkOptionsAndRemainingInput(at)) {
 17.1288 +            return false;
 17.1289 +        }
 17.1290 +        boolean defaultOption = at.hasOption("-default");
 17.1291 +        if (prog != null) {
 17.1292 +            if (defaultOption) {
 17.1293 +                errormsg("jshell.err.default.option.or.program", at.whole());
 17.1294 +                return false;
 17.1295 +            }
 17.1296 +            editor = ed.toArray(new String[ed.size()]);
 17.1297 +            fluffmsg("jshell.msg.set.editor.set", prog);
 17.1298 +        } else if (defaultOption) {
 17.1299 +            editor = null;
 17.1300 +        } else if (argsRequired) {
 17.1301 +            errormsg("jshell.err.set.editor.arg");
 17.1302 +            return false;
 17.1303 +        }
 17.1304 +        return true;
 17.1305 +    }
 17.1306 +
 17.1307 +    // The sub-command:  /set start <start-file>
 17.1308 +    boolean setStart(String cmd, ArgTokenizer at, boolean argsRequired) {
 17.1309 +        at.allowedOptions("-default", "-none");
 17.1310 +        String fn = at.next();
 17.1311 +        if (!checkOptionsAndRemainingInput(at)) {
 17.1312 +            return false;
 17.1313 +        }
 17.1314 +        int argCount = at.optionCount() + ((fn != null) ? 1 : 0);
 17.1315 +        if (argCount > 1 || argsRequired && argCount == 0) {
 17.1316 +            errormsg("jshell.err.option.or.filename", at.whole());
 17.1317 +            return false;
 17.1318 +        }
 17.1319 +        if (fn != null) {
 17.1320 +            String init = readFile(fn, cmd + " start");
 17.1321 +            if (init == null) {
 17.1322 +                return false;
 17.1323 +            } else {
 17.1324 +                startup = init;
 17.1325 +                return true;
 17.1326 +            }
 17.1327 +        } else if (at.hasOption("-default")) {
 17.1328 +            startup = DEFAULT_STARTUP;
 17.1329 +        } else if (at.hasOption("-none")) {
 17.1330 +            startup = "";
 17.1331 +        }
 17.1332 +        return true;
 17.1333 +    }
 17.1334 +
 17.1335 +    boolean cmdClasspath(String arg) {
 17.1336 +        if (arg.isEmpty()) {
 17.1337 +            errormsg("jshell.err.classpath.arg");
 17.1338 +            return false;
 17.1339 +        } else {
 17.1340 +            state.addToClasspath(toPathResolvingUserHome(arg).toString());
 17.1341 +            fluffmsg("jshell.msg.classpath", arg);
 17.1342 +            return true;
 17.1343 +        }
 17.1344 +    }
 17.1345 +
 17.1346 +    boolean cmdDebug(String arg) {
 17.1347          if (arg.isEmpty()) {
 17.1348              debug = !debug;
 17.1349 -            InternalDebugControl.setDebugFlags(state, debug ? InternalDebugControl.DBG_GEN : 0);
 17.1350 +            InternalDebugControl.setDebugFlags(state, debug ? DBG_GEN : 0);
 17.1351              fluff("Debugging %s", debug ? "on" : "off");
 17.1352          } else {
 17.1353              int flags = 0;
 17.1354 @@ -806,202 +1383,297 @@
 17.1355                          fluff("REPL tool debugging on");
 17.1356                          break;
 17.1357                      case 'g':
 17.1358 -                        flags |= InternalDebugControl.DBG_GEN;
 17.1359 +                        flags |= DBG_GEN;
 17.1360                          fluff("General debugging on");
 17.1361                          break;
 17.1362                      case 'f':
 17.1363 -                        flags |= InternalDebugControl.DBG_FMGR;
 17.1364 +                        flags |= DBG_FMGR;
 17.1365                          fluff("File manager debugging on");
 17.1366                          break;
 17.1367                      case 'c':
 17.1368 -                        flags |= InternalDebugControl.DBG_COMPA;
 17.1369 +                        flags |= DBG_COMPA;
 17.1370                          fluff("Completion analysis debugging on");
 17.1371                          break;
 17.1372                      case 'd':
 17.1373 -                        flags |= InternalDebugControl.DBG_DEP;
 17.1374 +                        flags |= DBG_DEP;
 17.1375                          fluff("Dependency debugging on");
 17.1376                          break;
 17.1377                      case 'e':
 17.1378 -                        flags |= InternalDebugControl.DBG_EVNT;
 17.1379 +                        flags |= DBG_EVNT;
 17.1380                          fluff("Event debugging on");
 17.1381                          break;
 17.1382                      default:
 17.1383                          hard("Unknown debugging option: %c", ch);
 17.1384                          fluff("Use: 0 r g f c d");
 17.1385 -                        break;
 17.1386 +                        return false;
 17.1387                  }
 17.1388              }
 17.1389              InternalDebugControl.setDebugFlags(state, flags);
 17.1390          }
 17.1391 +        return true;
 17.1392      }
 17.1393  
 17.1394 -    private void cmdExit() {
 17.1395 +    private boolean cmdExit() {
 17.1396          regenerateOnDeath = false;
 17.1397          live = false;
 17.1398 -        fluff("Goodbye\n");
 17.1399 +        if (!replayableHistory.isEmpty()) {
 17.1400 +            // Prevent history overflow by calculating what will fit, starting
 17.1401 +            // with must recent
 17.1402 +            int sepLen = RECORD_SEPARATOR.length();
 17.1403 +            int length = 0;
 17.1404 +            int first = replayableHistory.size();
 17.1405 +            while(length < Preferences.MAX_VALUE_LENGTH && --first >= 0) {
 17.1406 +                length += replayableHistory.get(first).length() + sepLen;
 17.1407 +            }
 17.1408 +            String hist = replayableHistory
 17.1409 +                    .subList(first + 1, replayableHistory.size())
 17.1410 +                    .stream()
 17.1411 +                    .reduce( (a, b) -> a + RECORD_SEPARATOR + b)
 17.1412 +                    .get();
 17.1413 +            prefs.put(REPLAY_RESTORE_KEY, hist);
 17.1414 +        }
 17.1415 +        fluffmsg("jshell.msg.goodbye");
 17.1416 +        return true;
 17.1417      }
 17.1418  
 17.1419 -    private void cmdFeedback(String arg) {
 17.1420 -        switch (arg) {
 17.1421 -            case "":
 17.1422 -            case "d":
 17.1423 -            case "default":
 17.1424 -                feedback = Feedback.Default;
 17.1425 -                break;
 17.1426 -            case "o":
 17.1427 -            case "off":
 17.1428 -                feedback = Feedback.Off;
 17.1429 -                break;
 17.1430 -            case "c":
 17.1431 -            case "concise":
 17.1432 -                feedback = Feedback.Concise;
 17.1433 -                break;
 17.1434 -            case "n":
 17.1435 -            case "normal":
 17.1436 -                feedback = Feedback.Normal;
 17.1437 -                break;
 17.1438 -            case "v":
 17.1439 -            case "verbose":
 17.1440 -                feedback = Feedback.Verbose;
 17.1441 -                break;
 17.1442 -            default:
 17.1443 -                hard("Follow /feedback with of the following:");
 17.1444 -                hard("  off       (errors and critical output only)");
 17.1445 -                hard("  concise");
 17.1446 -                hard("  normal");
 17.1447 -                hard("  verbose");
 17.1448 -                hard("  default");
 17.1449 -                hard("You may also use just the first letter, for example: /f c");
 17.1450 -                hard("In interactive mode 'default' is the same as 'normal', from a file it is the same as 'off'");
 17.1451 -                return;
 17.1452 +    boolean cmdHelp(String arg) {
 17.1453 +        ArgTokenizer at = new ArgTokenizer("/help", arg);
 17.1454 +        String subject = at.next();
 17.1455 +        if (subject != null) {
 17.1456 +            Command[] matches = commands.values().stream()
 17.1457 +                    .filter(c -> c.command.startsWith(subject))
 17.1458 +                    .toArray(size -> new Command[size]);
 17.1459 +            at.mark();
 17.1460 +            String sub = at.next();
 17.1461 +            if (sub != null && matches.length == 1) {
 17.1462 +                String cmd = matches[0].command;
 17.1463 +                switch (cmd) {
 17.1464 +                    case "/set":
 17.1465 +                        at.rewind();
 17.1466 +                        return printSubCommandHelp(cmd, at, "help.set.", SET_SUBCOMMANDS);
 17.1467 +                    case "/retain":
 17.1468 +                        at.rewind();
 17.1469 +                        return printSubCommandHelp(cmd, at, "help.retain.", RETAIN_SUBCOMMANDS);
 17.1470 +                }
 17.1471 +            }
 17.1472 +            if (matches.length > 0) {
 17.1473 +                for (Command c : matches) {
 17.1474 +                    hard("");
 17.1475 +                    hard("%s", c.command);
 17.1476 +                    hard("");
 17.1477 +                    hardrb(c.helpKey);
 17.1478 +                }
 17.1479 +                return true;
 17.1480 +            } else {
 17.1481 +                errormsg("jshell.err.help.arg", arg);
 17.1482 +            }
 17.1483          }
 17.1484 -        fluff("Feedback mode: %s", feedback.name().toLowerCase());
 17.1485 +        hardmsg("jshell.msg.help.begin");
 17.1486 +        hardPairs(commands.values().stream()
 17.1487 +                .filter(cmd -> cmd.kind.showInHelp),
 17.1488 +                cmd -> cmd.command + " " + getResourceString(cmd.helpKey + ".args"),
 17.1489 +                cmd -> getResourceString(cmd.helpKey + ".summary")
 17.1490 +        );
 17.1491 +        hardmsg("jshell.msg.help.subject");
 17.1492 +        hardPairs(commands.values().stream()
 17.1493 +                .filter(cmd -> cmd.kind == CommandKind.HELP_SUBJECT),
 17.1494 +                cmd -> cmd.command,
 17.1495 +                cmd -> getResourceString(cmd.helpKey + ".summary")
 17.1496 +        );
 17.1497 +        return true;
 17.1498      }
 17.1499  
 17.1500 -    void cmdHelp() {
 17.1501 -        int synopsisLen = 0;
 17.1502 -        Map<String, String> synopsis2Description = new LinkedHashMap<>();
 17.1503 -        for (Command cmd : new LinkedHashSet<>(commands.values())) {
 17.1504 -            if (cmd.kind == CommandKind.HIDDEN)
 17.1505 -                continue;
 17.1506 -            StringBuilder synopsis = new StringBuilder();
 17.1507 -            if (cmd.aliases.length > 1) {
 17.1508 -                synopsis.append(String.format("%-3s or ", cmd.aliases[1]));
 17.1509 -            } else {
 17.1510 -                synopsis.append("       ");
 17.1511 -            }
 17.1512 -            synopsis.append(cmd.aliases[0]);
 17.1513 -            if (cmd.params != null)
 17.1514 -                synopsis.append(" ").append(cmd.params);
 17.1515 -            synopsis2Description.put(synopsis.toString(), cmd.description);
 17.1516 -            synopsisLen = Math.max(synopsisLen, synopsis.length());
 17.1517 -        }
 17.1518 -        cmdout.println("Type a Java language expression, statement, or declaration.");
 17.1519 -        cmdout.println("Or type one of the following commands:\n");
 17.1520 -        for (Entry<String, String> e : synopsis2Description.entrySet()) {
 17.1521 -            cmdout.print(String.format("%-" + synopsisLen + "s", e.getKey()));
 17.1522 -            cmdout.print(" -- ");
 17.1523 -            cmdout.println(e.getValue());
 17.1524 -        }
 17.1525 -        cmdout.println();
 17.1526 -        cmdout.println("Supported shortcuts include:");
 17.1527 -        cmdout.println("<tab>       -- show possible completions for the current text");
 17.1528 -        cmdout.println("Shift-<tab> -- for current method or constructor invocation, show a synopsis of the method/constructor");
 17.1529 -    }
 17.1530 -
 17.1531 -    private void cmdHistory() {
 17.1532 +    private boolean cmdHistory() {
 17.1533          cmdout.println();
 17.1534          for (String s : input.currentSessionHistory()) {
 17.1535              // No number prefix, confusing with snippet ids
 17.1536              cmdout.printf("%s\n", s);
 17.1537          }
 17.1538 +        return true;
 17.1539      }
 17.1540  
 17.1541      /**
 17.1542 -     * Convert a user argument to a list of snippets referenced by that
 17.1543 -     * argument (or lack of argument).
 17.1544 -     * @param arg The user's argument to the command
 17.1545 -     * @return a list of referenced snippets
 17.1546 +     * Avoid parameterized varargs possible heap pollution warning.
 17.1547       */
 17.1548 -    private List<Snippet> argToSnippets(String arg) {
 17.1549 -        List<Snippet> snippets = new ArrayList<>();
 17.1550 -        if (arg.isEmpty()) {
 17.1551 -            // Default is all user snippets
 17.1552 -            for (Snippet sn : state.snippets()) {
 17.1553 -                if (notInStartUp(sn)) {
 17.1554 -                    snippets.add(sn);
 17.1555 +    private interface SnippetPredicate<T extends Snippet> extends Predicate<T> { }
 17.1556 +
 17.1557 +    /**
 17.1558 +     * Apply filters to a stream until one that is non-empty is found.
 17.1559 +     * Adapted from Stuart Marks
 17.1560 +     *
 17.1561 +     * @param supplier Supply the Snippet stream to filter
 17.1562 +     * @param filters Filters to attempt
 17.1563 +     * @return The non-empty filtered Stream, or null
 17.1564 +     */
 17.1565 +    @SafeVarargs
 17.1566 +    private static <T extends Snippet> Stream<T> nonEmptyStream(Supplier<Stream<T>> supplier,
 17.1567 +            SnippetPredicate<T>... filters) {
 17.1568 +        for (SnippetPredicate<T> filt : filters) {
 17.1569 +            Iterator<T> iterator = supplier.get().filter(filt).iterator();
 17.1570 +            if (iterator.hasNext()) {
 17.1571 +                return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
 17.1572 +            }
 17.1573 +        }
 17.1574 +        return null;
 17.1575 +    }
 17.1576 +
 17.1577 +    private boolean inStartUp(Snippet sn) {
 17.1578 +        return mapSnippet.get(sn).space == startNamespace;
 17.1579 +    }
 17.1580 +
 17.1581 +    private boolean isActive(Snippet sn) {
 17.1582 +        return state.status(sn).isActive;
 17.1583 +    }
 17.1584 +
 17.1585 +    private boolean mainActive(Snippet sn) {
 17.1586 +        return !inStartUp(sn) && isActive(sn);
 17.1587 +    }
 17.1588 +
 17.1589 +    private boolean matchingDeclaration(Snippet sn, String name) {
 17.1590 +        return sn instanceof DeclarationSnippet
 17.1591 +                && ((DeclarationSnippet) sn).name().equals(name);
 17.1592 +    }
 17.1593 +
 17.1594 +    /**
 17.1595 +     * Convert user arguments to a Stream of snippets referenced by those
 17.1596 +     * arguments (or lack of arguments).
 17.1597 +     *
 17.1598 +     * @param snippets the base list of possible snippets
 17.1599 +     * @param defFilter the filter to apply to the arguments if no argument
 17.1600 +     * @param rawargs the user's argument to the command, maybe be the empty
 17.1601 +     * string
 17.1602 +     * @return a Stream of referenced snippets or null if no matches are found
 17.1603 +     */
 17.1604 +    private <T extends Snippet> Stream<T> argsOptionsToSnippets(List<T> snippets,
 17.1605 +            Predicate<Snippet> defFilter, String rawargs, String cmd) {
 17.1606 +        ArgTokenizer at = new ArgTokenizer(cmd, rawargs.trim());
 17.1607 +        at.allowedOptions("-all", "-start");
 17.1608 +        List<String> args = new ArrayList<>();
 17.1609 +        String s;
 17.1610 +        while ((s = at.next()) != null) {
 17.1611 +            args.add(s);
 17.1612 +        }
 17.1613 +        if (!checkOptionsAndRemainingInput(at)) {
 17.1614 +            return null;
 17.1615 +        }
 17.1616 +        if (at.optionCount() > 0 && args.size() > 0) {
 17.1617 +            errormsg("jshell.err.may.not.specify.options.and.snippets", at.whole());
 17.1618 +            return null;
 17.1619 +        }
 17.1620 +        if (at.optionCount() > 1) {
 17.1621 +            errormsg("jshell.err.conflicting.options", at.whole());
 17.1622 +            return null;
 17.1623 +        }
 17.1624 +        if (at.hasOption("-all")) {
 17.1625 +            // all snippets including start-up, failed, and overwritten
 17.1626 +            return snippets.stream();
 17.1627 +        }
 17.1628 +        if (at.hasOption("-start")) {
 17.1629 +            // start-up snippets
 17.1630 +            return snippets.stream()
 17.1631 +                    .filter(this::inStartUp);
 17.1632 +        }
 17.1633 +        if (args.isEmpty()) {
 17.1634 +            // Default is all active user snippets
 17.1635 +            return snippets.stream()
 17.1636 +                    .filter(defFilter);
 17.1637 +        }
 17.1638 +        return argsToSnippets(snippets, args);
 17.1639 +    }
 17.1640 +
 17.1641 +    /**
 17.1642 +     * Convert user arguments to a Stream of snippets referenced by those
 17.1643 +     * arguments.
 17.1644 +     *
 17.1645 +     * @param snippets the base list of possible snippets
 17.1646 +     * @param args the user's argument to the command, maybe be the empty list
 17.1647 +     * @return a Stream of referenced snippets or null if no matches to specific
 17.1648 +     * arg
 17.1649 +     */
 17.1650 +    private <T extends Snippet> Stream<T> argsToSnippets(List<T> snippets,
 17.1651 +            List<String> args) {
 17.1652 +        Stream<T> result = null;
 17.1653 +        for (String arg : args) {
 17.1654 +            // Find the best match
 17.1655 +            Stream<T> st = layeredSnippetSearch(snippets, arg);
 17.1656 +            if (st == null) {
 17.1657 +                Stream<Snippet> est = layeredSnippetSearch(state.snippets(), arg);
 17.1658 +                if (est == null) {
 17.1659 +                    errormsg("jshell.err.no.such.snippets", arg);
 17.1660 +                } else {
 17.1661 +                    errormsg("jshell.err.the.snippet.cannot.be.used.with.this.command",
 17.1662 +                            arg, est.findFirst().get().source());
 17.1663                  }
 17.1664 -            }
 17.1665 -        } else {
 17.1666 -            // Look for all declarations with matching names
 17.1667 -            for (Snippet key : state.snippets()) {
 17.1668 -                switch (key.kind()) {
 17.1669 -                    case METHOD:
 17.1670 -                    case VAR:
 17.1671 -                    case TYPE_DECL:
 17.1672 -                        if (((DeclarationSnippet) key).name().equals(arg)) {
 17.1673 -                            snippets.add(key);
 17.1674 -                        }
 17.1675 -                        break;
 17.1676 -                }
 17.1677 -            }
 17.1678 -            // If no declarations found, look for an id of this name
 17.1679 -            if (snippets.isEmpty()) {
 17.1680 -                for (Snippet sn : state.snippets()) {
 17.1681 -                    if (sn.id().equals(arg)) {
 17.1682 -                        snippets.add(sn);
 17.1683 -                        break;
 17.1684 -                    }
 17.1685 -                }
 17.1686 -            }
 17.1687 -            // If still no matches found, give an error
 17.1688 -            if (snippets.isEmpty()) {
 17.1689 -                hard("No definition or id named %s found.  See /classes /methods /vars or /list", arg);
 17.1690                  return null;
 17.1691              }
 17.1692 +            if (result == null) {
 17.1693 +                result = st;
 17.1694 +            } else {
 17.1695 +                result = Stream.concat(result, st);
 17.1696 +            }
 17.1697          }
 17.1698 -        return snippets;
 17.1699 +        return result;
 17.1700      }
 17.1701  
 17.1702 -    private void cmdDrop(String arg) {
 17.1703 -        if (arg.isEmpty()) {
 17.1704 -            hard("In the /drop argument, please specify an import, variable, method, or class to drop.");
 17.1705 -            hard("Specify by id or name. Use /list to see ids. Use /reset to reset all state.");
 17.1706 -            return;
 17.1707 -        }
 17.1708 -        List<Snippet> snippetSet = argToSnippets(arg);
 17.1709 -        if (snippetSet == null) {
 17.1710 -            return;
 17.1711 -        }
 17.1712 -        snippetSet = snippetSet.stream()
 17.1713 -                .filter(sn -> state.status(sn).isActive)
 17.1714 -                .collect(toList());
 17.1715 -        snippetSet.removeIf(sn -> !(sn instanceof PersistentSnippet));
 17.1716 -        if (snippetSet.isEmpty()) {
 17.1717 -            hard("The argument did not specify an import, variable, method, or class to drop.");
 17.1718 -            return;
 17.1719 -        }
 17.1720 -        if (snippetSet.size() > 1) {
 17.1721 -            hard("The argument references more than one import, variable, method, or class.");
 17.1722 -            hard("Try again with one of the ids below:");
 17.1723 -            for (Snippet sn : snippetSet) {
 17.1724 -                cmdout.printf("%4s : %s\n", sn.id(), sn.source().replace("\n", "\n       "));
 17.1725 -            }
 17.1726 -            return;
 17.1727 -        }
 17.1728 -        PersistentSnippet psn = (PersistentSnippet) snippetSet.iterator().next();
 17.1729 -        state.drop(psn).forEach(this::handleEvent);
 17.1730 +    private <T extends Snippet> Stream<T> layeredSnippetSearch(List<T> snippets, String arg) {
 17.1731 +        return nonEmptyStream(
 17.1732 +                // the stream supplier
 17.1733 +                () -> snippets.stream(),
 17.1734 +                // look for active user declarations matching the name
 17.1735 +                sn -> isActive(sn) && matchingDeclaration(sn, arg),
 17.1736 +                // else, look for any declarations matching the name
 17.1737 +                sn -> matchingDeclaration(sn, arg),
 17.1738 +                // else, look for an id of this name
 17.1739 +                sn -> sn.id().equals(arg)
 17.1740 +        );
 17.1741      }
 17.1742  
 17.1743 -    private void cmdEdit(String arg) {
 17.1744 -        List<Snippet> snippetSet = argToSnippets(arg);
 17.1745 -        if (snippetSet == null) {
 17.1746 -            return;
 17.1747 +    private boolean cmdDrop(String rawargs) {
 17.1748 +        ArgTokenizer at = new ArgTokenizer("/drop", rawargs.trim());
 17.1749 +        at.allowedOptions();
 17.1750 +        List<String> args = new ArrayList<>();
 17.1751 +        String s;
 17.1752 +        while ((s = at.next()) != null) {
 17.1753 +            args.add(s);
 17.1754 +        }
 17.1755 +        if (!checkOptionsAndRemainingInput(at)) {
 17.1756 +            return false;
 17.1757 +        }
 17.1758 +        if (args.isEmpty()) {
 17.1759 +            errormsg("jshell.err.drop.arg");
 17.1760 +            return false;
 17.1761 +        }
 17.1762 +        Stream<PersistentSnippet> stream = argsToSnippets(dropableSnippets(), args);
 17.1763 +        if (stream == null) {
 17.1764 +            // Snippet not found. Error already printed
 17.1765 +            fluffmsg("jshell.msg.see.classes.etc");
 17.1766 +            return false;
 17.1767 +        }
 17.1768 +        List<PersistentSnippet> snippets = stream.collect(toList());
 17.1769 +        if (snippets.size() > args.size()) {
 17.1770 +            // One of the args references more thean one snippet
 17.1771 +            errormsg("jshell.err.drop.ambiguous");
 17.1772 +            fluffmsg("jshell.msg.use.one.of", snippets.stream()
 17.1773 +                    .map(sn -> String.format("\n/drop %-5s :   %s", sn.id(), sn.source().replace("\n", "\n       ")))
 17.1774 +                    .collect(Collectors.joining(", "))
 17.1775 +            );
 17.1776 +            return false;
 17.1777 +        }
 17.1778 +        snippets.stream()
 17.1779 +                .forEach(sn -> state.drop(sn).forEach(this::handleEvent));
 17.1780 +        return true;
 17.1781 +    }
 17.1782 +
 17.1783 +    private boolean cmdEdit(String arg) {
 17.1784 +        Stream<Snippet> stream = argsOptionsToSnippets(state.snippets(),
 17.1785 +                this::mainActive, arg, "/edit");
 17.1786 +        if (stream == null) {
 17.1787 +            return false;
 17.1788          }
 17.1789          Set<String> srcSet = new LinkedHashSet<>();
 17.1790 -        for (Snippet key : snippetSet) {
 17.1791 -            String src = key.source();
 17.1792 -            switch (key.subKind()) {
 17.1793 +        stream.forEachOrdered(sn -> {
 17.1794 +            String src = sn.source();
 17.1795 +            switch (sn.subKind()) {
 17.1796                  case VAR_VALUE_SUBKIND:
 17.1797                      break;
 17.1798                  case ASSIGNMENT_SUBKIND:
 17.1799 @@ -1016,7 +1688,7 @@
 17.1800                      srcSet.add(src);
 17.1801                      break;
 17.1802              }
 17.1803 -        }
 17.1804 +        });
 17.1805          StringBuilder sb = new StringBuilder();
 17.1806          for (String s : srcSet) {
 17.1807              sb.append(s);
 17.1808 @@ -1026,10 +1698,17 @@
 17.1809          Consumer<String> saveHandler = new SaveHandler(src, srcSet);
 17.1810          Consumer<String> errorHandler = s -> hard("Edit Error: %s", s);
 17.1811          if (editor == null) {
 17.1812 -            EditPad.edit(errorHandler, src, saveHandler);
 17.1813 +            try {
 17.1814 +                EditPad.edit(errorHandler, src, saveHandler);
 17.1815 +            } catch (RuntimeException ex) {
 17.1816 +                errormsg("jshell.err.cant.launch.editor", ex);
 17.1817 +                fluffmsg("jshell.msg.try.set.editor");
 17.1818 +                return false;
 17.1819 +            }
 17.1820          } else {
 17.1821              ExternalEditor.edit(editor, errorHandler, src, saveHandler, input);
 17.1822          }
 17.1823 +        return true;
 17.1824      }
 17.1825      //where
 17.1826      // receives editor requests to save
 17.1827 @@ -1067,7 +1746,7 @@
 17.1828                      }
 17.1829                      currSrcs = nextSrcs;
 17.1830                  } catch (IllegalStateException ex) {
 17.1831 -                    hard("Resetting...");
 17.1832 +                    hardmsg("jshell.msg.resetting");
 17.1833                      resetState();
 17.1834                      currSrcs = new LinkedHashSet<>(); // re-process everything
 17.1835                  }
 17.1836 @@ -1087,155 +1766,185 @@
 17.1837          }
 17.1838      }
 17.1839  
 17.1840 -    private void cmdList(String arg) {
 17.1841 -        boolean all = false;
 17.1842 -        switch (arg) {
 17.1843 -            case "all":
 17.1844 -                all = true;
 17.1845 -                break;
 17.1846 -            case "history":
 17.1847 -                cmdHistory();
 17.1848 -                return;
 17.1849 -            case "":
 17.1850 -                break;
 17.1851 -            default:
 17.1852 -                hard("Invalid /list argument: %s", arg);
 17.1853 -                return;
 17.1854 +    private boolean cmdList(String arg) {
 17.1855 +        if (arg.length() >= 2 && "-history".startsWith(arg)) {
 17.1856 +            return cmdHistory();
 17.1857          }
 17.1858 -        boolean hasOutput = false;
 17.1859 -        for (Snippet sn : state.snippets()) {
 17.1860 -            if (all || (notInStartUp(sn) && state.status(sn).isActive)) {
 17.1861 -                if (!hasOutput) {
 17.1862 -                    cmdout.println();
 17.1863 -                    hasOutput = true;
 17.1864 -                }
 17.1865 -                cmdout.printf("%4s : %s\n", sn.id(), sn.source().replace("\n", "\n       "));
 17.1866 +        Stream<Snippet> stream = argsOptionsToSnippets(state.snippets(),
 17.1867 +                this::mainActive, arg, "/list");
 17.1868 +        if (stream == null) {
 17.1869 +            return false;
 17.1870 +        }
 17.1871  
 17.1872 +        // prevent double newline on empty list
 17.1873 +        boolean[] hasOutput = new boolean[1];
 17.1874 +        stream.forEachOrdered(sn -> {
 17.1875 +            if (!hasOutput[0]) {
 17.1876 +                cmdout.println();
 17.1877 +                hasOutput[0] = true;
 17.1878              }
 17.1879 -        }
 17.1880 +            cmdout.printf("%4s : %s\n", sn.id(), sn.source().replace("\n", "\n       "));
 17.1881 +        });
 17.1882 +        return true;
 17.1883      }
 17.1884  
 17.1885 -    private void cmdOpen(String filename) {
 17.1886 -        if (filename.isEmpty()) {
 17.1887 -            hard("The /open command requires a filename argument.");
 17.1888 -        } else {
 17.1889 +    private boolean cmdOpen(String filename) {
 17.1890 +        return runFile(filename, "/open");
 17.1891 +    }
 17.1892 +
 17.1893 +    private boolean runFile(String filename, String context) {
 17.1894 +        if (!filename.isEmpty()) {
 17.1895              try {
 17.1896                  run(new FileScannerIOContext(toPathResolvingUserHome(filename).toString()));
 17.1897 +                return true;
 17.1898              } catch (FileNotFoundException e) {
 17.1899 -                hard("File '%s' is not found: %s", filename, e.getMessage());
 17.1900 +                errormsg("jshell.err.file.not.found", context, filename, e.getMessage());
 17.1901              } catch (Exception e) {
 17.1902 -                hard("Exception while reading file: %s", e);
 17.1903 +                errormsg("jshell.err.file.exception", context, filename, e);
 17.1904              }
 17.1905 +        } else {
 17.1906 +            errormsg("jshell.err.file.filename", context);
 17.1907          }
 17.1908 +        return false;
 17.1909      }
 17.1910  
 17.1911 -    private void cmdPrompt() {
 17.1912 -        displayPrompt = !displayPrompt;
 17.1913 -        fluff("Prompt will %sdisplay. Use /prompt to toggle.", displayPrompt ? "" : "NOT ");
 17.1914 -        concise("Prompt: %s", displayPrompt ? "on" : "off");
 17.1915 +    /**
 17.1916 +     * Read an external file. Error messages accessed via keyPrefix
 17.1917 +     *
 17.1918 +     * @param filename file to access or null
 17.1919 +     * @param context printable non-natural language context for errors
 17.1920 +     * @return contents of file as string
 17.1921 +     */
 17.1922 +    String readFile(String filename, String context) {
 17.1923 +        if (filename != null) {
 17.1924 +            try {
 17.1925 +                byte[] encoded = Files.readAllBytes(Paths.get(filename));
 17.1926 +                return new String(encoded);
 17.1927 +            } catch (AccessDeniedException e) {
 17.1928 +                errormsg("jshell.err.file.not.accessible", context, filename, e.getMessage());
 17.1929 +            } catch (NoSuchFileException e) {
 17.1930 +                errormsg("jshell.err.file.not.found", context, filename);
 17.1931 +            } catch (Exception e) {
 17.1932 +                errormsg("jshell.err.file.exception", context, filename, e);
 17.1933 +            }
 17.1934 +        } else {
 17.1935 +            errormsg("jshell.err.file.filename", context);
 17.1936 +        }
 17.1937 +        return null;
 17.1938 +
 17.1939      }
 17.1940  
 17.1941 -    private void cmdReset() {
 17.1942 +    private boolean cmdReset() {
 17.1943          live = false;
 17.1944 -        fluff("Resetting state.");
 17.1945 +        fluffmsg("jshell.msg.resetting.state");
 17.1946 +        return true;
 17.1947      }
 17.1948  
 17.1949 -    private void cmdSave(String arg_filename) {
 17.1950 -        Matcher mat = HISTORY_ALL_FILENAME.matcher(arg_filename);
 17.1951 -        if (!mat.find()) {
 17.1952 -            hard("Malformed argument to the /save command: %s", arg_filename);
 17.1953 -            return;
 17.1954 +    private boolean cmdReload(String rawargs) {
 17.1955 +        ArgTokenizer at = new ArgTokenizer("/reload", rawargs.trim());
 17.1956 +        at.allowedOptions("-restore", "-quiet");
 17.1957 +        if (!checkOptionsAndRemainingInput(at)) {
 17.1958 +            return false;
 17.1959          }
 17.1960 -        boolean useHistory = false;
 17.1961 -        boolean saveAll = false;
 17.1962 -        String cmd = mat.group("cmd");
 17.1963 -        if (cmd != null) switch (cmd) {
 17.1964 -            case "all":
 17.1965 -                saveAll = true;
 17.1966 -                break;
 17.1967 -            case "history":
 17.1968 -                useHistory = true;
 17.1969 -                break;
 17.1970 +        Iterable<String> history;
 17.1971 +        if (at.hasOption("-restore")) {
 17.1972 +            if (replayableHistoryPrevious == null) {
 17.1973 +                errormsg("jshell.err.reload.no.previous");
 17.1974 +                return false;
 17.1975 +            }
 17.1976 +            history = replayableHistoryPrevious;
 17.1977 +            fluffmsg("jshell.err.reload.restarting.previous.state");
 17.1978 +        } else {
 17.1979 +            history = replayableHistory;
 17.1980 +            fluffmsg("jshell.err.reload.restarting.state");
 17.1981          }
 17.1982 -        String filename = mat.group("filename");
 17.1983 -        if (filename == null ||filename.isEmpty()) {
 17.1984 -            hard("The /save command requires a filename argument.");
 17.1985 -            return;
 17.1986 +        boolean echo = !at.hasOption("-quiet");
 17.1987 +        resetState();
 17.1988 +        run(new ReloadIOContext(history,
 17.1989 +                echo ? cmdout : null));
 17.1990 +        return true;
 17.1991 +    }
 17.1992 +
 17.1993 +    private boolean cmdSave(String rawargs) {
 17.1994 +        ArgTokenizer at = new ArgTokenizer("/save", rawargs.trim());
 17.1995 +        at.allowedOptions("-all", "-start", "-history");
 17.1996 +        String filename = at.next();
 17.1997 +        if (filename == null) {
 17.1998 +            errormsg("jshell.err.file.filename", "/save");
 17.1999 +            return false;
 17.2000 +        }
 17.2001 +        if (!checkOptionsAndRemainingInput(at)) {
 17.2002 +            return false;
 17.2003 +        }
 17.2004 +        if (at.optionCount() > 1) {
 17.2005 +            errormsg("jshell.err.conflicting.options", at.whole());
 17.2006 +            return false;
 17.2007          }
 17.2008          try (BufferedWriter writer = Files.newBufferedWriter(toPathResolvingUserHome(filename),
 17.2009                  Charset.defaultCharset(),
 17.2010                  CREATE, TRUNCATE_EXISTING, WRITE)) {
 17.2011 -            if (useHistory) {
 17.2012 +            if (at.hasOption("-history")) {
 17.2013                  for (String s : input.currentSessionHistory()) {
 17.2014                      writer.write(s);
 17.2015                      writer.write("\n");
 17.2016                  }
 17.2017 +            } else if (at.hasOption("-start")) {
 17.2018 +                writer.append(startup);
 17.2019              } else {
 17.2020 -                for (Snippet sn : state.snippets()) {
 17.2021 -                    if (saveAll || notInStartUp(sn)) {
 17.2022 -                        writer.write(sn.source());
 17.2023 -                        writer.write("\n");
 17.2024 -                    }
 17.2025 +                List<Snippet> sns = at.hasOption("-all")
 17.2026 +                        ? state.snippets()
 17.2027 +                        : state.snippets().stream().filter(this::mainActive).collect(toList());
 17.2028 +                for (Snippet sn : sns) {
 17.2029 +                    writer.write(sn.source());
 17.2030 +                    writer.write("\n");
 17.2031                  }
 17.2032              }
 17.2033          } catch (FileNotFoundException e) {
 17.2034 -            hard("File '%s' for save is not accessible: %s", filename, e.getMessage());
 17.2035 +            errormsg("jshell.err.file.not.found", "/save", filename, e.getMessage());
 17.2036 +            return false;
 17.2037          } catch (Exception e) {
 17.2038 -            hard("Exception while saving: %s", e);
 17.2039 +            errormsg("jshell.err.file.exception", "/save", filename, e);
 17.2040 +            return false;
 17.2041          }
 17.2042 +        return true;
 17.2043      }
 17.2044  
 17.2045 -    private void cmdSetStart(String filename) {
 17.2046 -        if (filename.isEmpty()) {
 17.2047 -            hard("The /setstart command requires a filename argument.");
 17.2048 -        } else {
 17.2049 -            try {
 17.2050 -                byte[] encoded = Files.readAllBytes(toPathResolvingUserHome(filename));
 17.2051 -                String init = new String(encoded);
 17.2052 -                PREFS.put(STARTUP_KEY, init);
 17.2053 -            } catch (AccessDeniedException e) {
 17.2054 -                hard("File '%s' for /setstart is not accessible.", filename);
 17.2055 -            } catch (NoSuchFileException e) {
 17.2056 -                hard("File '%s' for /setstart is not found.", filename);
 17.2057 -            } catch (Exception e) {
 17.2058 -                hard("Exception while reading start set file: %s", e);
 17.2059 -            }
 17.2060 +    private boolean cmdVars(String arg) {
 17.2061 +        Stream<VarSnippet> stream = argsOptionsToSnippets(allVarSnippets(),
 17.2062 +                this::isActive, arg, "/vars");
 17.2063 +        if (stream == null) {
 17.2064 +            return false;
 17.2065          }
 17.2066 +        stream.forEachOrdered(vk ->
 17.2067 +        {
 17.2068 +            String val = state.status(vk) == Status.VALID
 17.2069 +                    ? state.varValue(vk)
 17.2070 +                    : "jshell.msg.vars.not.active";
 17.2071 +            hard("  %s %s = %s", vk.typeName(), vk.name(), val);
 17.2072 +        });
 17.2073 +        return true;
 17.2074      }
 17.2075  
 17.2076 -    private void cmdSaveStart(String filename) {
 17.2077 -        if (filename.isEmpty()) {
 17.2078 -            hard("The /savestart command requires a filename argument.");
 17.2079 -        } else {
 17.2080 -            try {
 17.2081 -                Files.write(toPathResolvingUserHome(filename), DEFAULT_STARTUP.getBytes());
 17.2082 -            } catch (AccessDeniedException e) {
 17.2083 -                hard("File '%s' for /savestart is not accessible.", filename);
 17.2084 -            } catch (NoSuchFileException e) {
 17.2085 -                hard("File '%s' for /savestart cannot be located.", filename);
 17.2086 -            } catch (Exception e) {
 17.2087 -                hard("Exception while saving default startup file: %s", e);
 17.2088 -            }
 17.2089 +    private boolean cmdMethods(String arg) {
 17.2090 +        Stream<MethodSnippet> stream = argsOptionsToSnippets(allMethodSnippets(),
 17.2091 +                this::isActive, arg, "/methods");
 17.2092 +        if (stream == null) {
 17.2093 +            return false;
 17.2094          }
 17.2095 +        stream.forEachOrdered(mk
 17.2096 +                -> hard("  %s %s", mk.name(), mk.signature())
 17.2097 +        );
 17.2098 +        return true;
 17.2099      }
 17.2100  
 17.2101 -    private void cmdVars() {
 17.2102 -        for (VarSnippet vk : state.variables()) {
 17.2103 -            String val = state.status(vk) == Status.VALID
 17.2104 -                    ? state.varValue(vk)
 17.2105 -                    : "(not-active)";
 17.2106 -            hard("  %s %s = %s", vk.typeName(), vk.name(), val);
 17.2107 +    private boolean cmdTypes(String arg) {
 17.2108 +        Stream<TypeDeclSnippet> stream = argsOptionsToSnippets(allTypeSnippets(),
 17.2109 +                this::isActive, arg, "/types");
 17.2110 +        if (stream == null) {
 17.2111 +            return false;
 17.2112          }
 17.2113 -    }
 17.2114 -
 17.2115 -    private void cmdMethods() {
 17.2116 -        for (MethodSnippet mk : state.methods()) {
 17.2117 -            hard("  %s %s", mk.name(), mk.signature());
 17.2118 -        }
 17.2119 -    }
 17.2120 -
 17.2121 -    private void cmdClasses() {
 17.2122 -        for (TypeDeclSnippet ck : state.types()) {
 17.2123 +        stream.forEachOrdered(ck
 17.2124 +        -> {
 17.2125              String kind;
 17.2126              switch (ck.subKind()) {
 17.2127                  case INTERFACE_SUBKIND:
 17.2128 @@ -1256,29 +1965,62 @@
 17.2129                      break;
 17.2130              }
 17.2131              hard("  %s %s", kind, ck.name());
 17.2132 -        }
 17.2133 +        });
 17.2134 +        return true;
 17.2135      }
 17.2136  
 17.2137 -    private void cmdImports() {
 17.2138 +    private boolean cmdImports() {
 17.2139          state.imports().forEach(ik -> {
 17.2140              hard("  import %s%s", ik.isStatic() ? "static " : "", ik.fullname());
 17.2141          });
 17.2142 +        return true;
 17.2143      }
 17.2144  
 17.2145 -    private void cmdUseHistoryEntry(int index) {
 17.2146 +    private boolean cmdUseHistoryEntry(int index) {
 17.2147          List<Snippet> keys = state.snippets();
 17.2148          if (index < 0)
 17.2149              index += keys.size();
 17.2150          else
 17.2151              index--;
 17.2152          if (index >= 0 && index < keys.size()) {
 17.2153 -            String source = keys.get(index).source();
 17.2154 -            cmdout.printf("%s\n", source);
 17.2155 -            input.replaceLastHistoryEntry(source);
 17.2156 -            processSourceCatchingReset(source);
 17.2157 +            rerunSnippet(keys.get(index));
 17.2158          } else {
 17.2159 -            hard("Cannot find snippet %d", index + 1);
 17.2160 +            errormsg("jshell.err.out.of.range");
 17.2161 +            return false;
 17.2162          }
 17.2163 +        return true;
 17.2164 +    }
 17.2165 +
 17.2166 +    boolean checkOptionsAndRemainingInput(ArgTokenizer at) {
 17.2167 +        String junk = at.remainder();
 17.2168 +        if (!junk.isEmpty()) {
 17.2169 +            errormsg("jshell.err.unexpected.at.end", junk, at.whole());
 17.2170 +            return false;
 17.2171 +        } else {
 17.2172 +            String bad = at.badOptions();
 17.2173 +            if (!bad.isEmpty()) {
 17.2174 +                errormsg("jshell.err.unknown.option", bad, at.whole());
 17.2175 +                return false;
 17.2176 +            }
 17.2177 +        }
 17.2178 +        return true;
 17.2179 +    }
 17.2180 +
 17.2181 +    private boolean rerunHistoryEntryById(String id) {
 17.2182 +        Optional<Snippet> snippet = state.snippets().stream()
 17.2183 +            .filter(s -> s.id().equals(id))
 17.2184 +            .findFirst();
 17.2185 +        return snippet.map(s -> {
 17.2186 +            rerunSnippet(s);
 17.2187 +            return true;
 17.2188 +        }).orElse(false);
 17.2189 +    }
 17.2190 +
 17.2191 +    private void rerunSnippet(Snippet snippet) {
 17.2192 +        String source = snippet.source();
 17.2193 +        cmdout.printf("%s\n", source);
 17.2194 +        input.replaceLastHistoryEntry(source);
 17.2195 +        processSourceCatchingReset(source);
 17.2196      }
 17.2197  
 17.2198      /**
 17.2199 @@ -1292,68 +2034,55 @@
 17.2200                  .collect(toList());
 17.2201      }
 17.2202  
 17.2203 -    void printDiagnostics(String source, List<Diag> diagnostics, boolean embed) {
 17.2204 -        String padding = embed? "    " : "";
 17.2205 -        for (Diag diag : diagnostics) {
 17.2206 -            //assert diag.getSource().equals(source);
 17.2207 +    void displayDiagnostics(String source, Diag diag, List<String> toDisplay) {
 17.2208 +        for (String line : diag.getMessage(null).split("\\r?\\n")) { // TODO: Internationalize
 17.2209 +            if (!line.trim().startsWith("location:")) {
 17.2210 +                toDisplay.add(line);
 17.2211 +            }
 17.2212 +        }
 17.2213  
 17.2214 -            if (!embed) {
 17.2215 -                if (diag.isError()) {
 17.2216 -                    hard("Error:");
 17.2217 -                } else {
 17.2218 -                    hard("Warning:");
 17.2219 -                }
 17.2220 +        int pstart = (int) diag.getStartPosition();
 17.2221 +        int pend = (int) diag.getEndPosition();
 17.2222 +        Matcher m = LINEBREAK.matcher(source);
 17.2223 +        int pstartl = 0;
 17.2224 +        int pendl = -2;
 17.2225 +        while (m.find(pstartl)) {
 17.2226 +            pendl = m.start();
 17.2227 +            if (pendl >= pstart) {
 17.2228 +                break;
 17.2229 +            } else {
 17.2230 +                pstartl = m.end();
 17.2231              }
 17.2232 +        }
 17.2233 +        if (pendl < pstart) {
 17.2234 +            pendl = source.length();
 17.2235 +        }
 17.2236 +        toDisplay.add(source.substring(pstartl, pendl));
 17.2237  
 17.2238 -            for (String line : diag.getMessage(null).split("\\r?\\n")) {
 17.2239 -                if (!line.trim().startsWith("location:")) {
 17.2240 -                    hard("%s%s", padding, line);
 17.2241 -                }
 17.2242 +        StringBuilder sb = new StringBuilder();
 17.2243 +        int start = pstart - pstartl;
 17.2244 +        for (int i = 0; i < start; ++i) {
 17.2245 +            sb.append(' ');
 17.2246 +        }
 17.2247 +        sb.append('^');
 17.2248 +        boolean multiline = pend > pendl;
 17.2249 +        int end = (multiline ? pendl : pend) - pstartl - 1;
 17.2250 +        if (end > start) {
 17.2251 +            for (int i = start + 1; i < end; ++i) {
 17.2252 +                sb.append('-');
 17.2253              }
 17.2254 +            if (multiline) {
 17.2255 +                sb.append("-...");
 17.2256 +            } else {
 17.2257 +                sb.append('^');
 17.2258 +            }
 17.2259 +        }
 17.2260 +        toDisplay.add(sb.toString());
 17.2261  
 17.2262 -            int pstart = (int) diag.getStartPosition();
 17.2263 -            int pend = (int) diag.getEndPosition();
 17.2264 -            Matcher m = LINEBREAK.matcher(source);
 17.2265 -            int pstartl = 0;
 17.2266 -            int pendl = -2;
 17.2267 -            while (m.find(pstartl)) {
 17.2268 -                pendl = m.start();
 17.2269 -                if (pendl >= pstart) {
 17.2270 -                    break;
 17.2271 -                } else {
 17.2272 -                    pstartl = m.end();
 17.2273 -                }
 17.2274 -            }
 17.2275 -            if (pendl < pstart) {
 17.2276 -                pendl = source.length();
 17.2277 -            }
 17.2278 -            fluff("%s%s", padding, source.substring(pstartl, pendl));
 17.2279 -
 17.2280 -            StringBuilder sb = new StringBuilder();
 17.2281 -            int start = pstart - pstartl;
 17.2282 -            for (int i = 0; i < start; ++i) {
 17.2283 -                sb.append(' ');
 17.2284 -            }
 17.2285 -            sb.append('^');
 17.2286 -            boolean multiline = pend > pendl;
 17.2287 -            int end = (multiline ? pendl : pend) - pstartl - 1;
 17.2288 -            if (end > start) {
 17.2289 -                for (int i = start + 1; i < end; ++i) {
 17.2290 -                    sb.append('-');
 17.2291 -                }
 17.2292 -                if (multiline) {
 17.2293 -                    sb.append("-...");
 17.2294 -                } else {
 17.2295 -                    sb.append('^');
 17.2296 -                }
 17.2297 -            }
 17.2298 -            fluff("%s%s", padding, sb.toString());
 17.2299 -
 17.2300 -            debug("printDiagnostics start-pos = %d ==> %d -- wrap = %s", diag.getStartPosition(), start, this);
 17.2301 -            debug("Code: %s", diag.getCode());
 17.2302 -            debug("Pos: %d (%d - %d)", diag.getPosition(),
 17.2303 -                    diag.getStartPosition(), diag.getEndPosition());
 17.2304 -        }
 17.2305 +        debug("printDiagnostics start-pos = %d ==> %d -- wrap = %s", diag.getStartPosition(), start, this);
 17.2306 +        debug("Code: %s", diag.getCode());
 17.2307 +        debug("Pos: %d (%d - %d)", diag.getPosition(),
 17.2308 +                diag.getStartPosition(), diag.getEndPosition());
 17.2309      }
 17.2310  
 17.2311      private String processSource(String srcInput) throws IllegalStateException {
 17.2312 @@ -1373,13 +2102,28 @@
 17.2313      private boolean processCompleteSource(String source) throws IllegalStateException {
 17.2314          debug("Compiling: %s", source);
 17.2315          boolean failed = false;
 17.2316 +        boolean isActive = false;
 17.2317          List<SnippetEvent> events = state.eval(source);
 17.2318          for (SnippetEvent e : events) {
 17.2319 +            // Report the event, recording failure
 17.2320              failed |= handleEvent(e);
 17.2321 +
 17.2322 +            // If any main snippet is active, this should be replayable
 17.2323 +            // also ignore var value queries
 17.2324 +            isActive |= e.causeSnippet() == null &&
 17.2325 +                    e.status().isActive &&
 17.2326 +                    e.snippet().subKind() != VAR_VALUE_SUBKIND;
 17.2327          }
 17.2328 +        // If this is an active snippet and it didn't cause the backend to die,
 17.2329 +        // add it to the replayable history
 17.2330 +        if (isActive && live) {
 17.2331 +            addToReplayHistory(source);
 17.2332 +        }
 17.2333 +
 17.2334          return failed;
 17.2335      }
 17.2336  
 17.2337 +    // Handle incoming snippet events -- return true on failure
 17.2338      private boolean handleEvent(SnippetEvent ste) {
 17.2339          Snippet sn = ste.snippet();
 17.2340          if (sn == null) {
 17.2341 @@ -1390,167 +2134,45 @@
 17.2342          String source = sn.source();
 17.2343          if (ste.causeSnippet() == null) {
 17.2344              // main event
 17.2345 -            printDiagnostics(source, diagnostics, false);
 17.2346 -            if (ste.status().isActive) {
 17.2347 +            for (Diag d : diagnostics) {
 17.2348 +                hardmsg(d.isError()? "jshell.msg.error" : "jshell.msg.warning");
 17.2349 +                List<String> disp = new ArrayList<>();
 17.2350 +                displayDiagnostics(source, d, disp);
 17.2351 +                disp.stream()
 17.2352 +                        .forEach(l -> hard("%s", l));
 17.2353 +            }
 17.2354 +
 17.2355 +            if (ste.status() != Status.REJECTED) {
 17.2356                  if (ste.exception() != null) {
 17.2357                      if (ste.exception() instanceof EvalException) {
 17.2358                          printEvalException((EvalException) ste.exception());
 17.2359                          return true;
 17.2360                      } else if (ste.exception() instanceof UnresolvedReferenceException) {
 17.2361 -                        printUnresolved((UnresolvedReferenceException) ste.exception());
 17.2362 +                        printUnresolvedException((UnresolvedReferenceException) ste.exception());
 17.2363                      } else {
 17.2364                          hard("Unexpected execution exception: %s", ste.exception());
 17.2365                          return true;
 17.2366                      }
 17.2367                  } else {
 17.2368 -                    displayDeclarationAndValue(ste, false, ste.value());
 17.2369 +                    new DisplayEvent(ste, false, ste.value(), diagnostics).displayDeclarationAndValue();
 17.2370                  }
 17.2371 -            } else if (ste.status() == Status.REJECTED) {
 17.2372 +            } else {
 17.2373                  if (diagnostics.isEmpty()) {
 17.2374 -                    hard("Failed.");
 17.2375 +                    errormsg("jshell.err.failed");
 17.2376                  }
 17.2377                  return true;
 17.2378              }
 17.2379 -        } else if (ste.status() == Status.REJECTED) {
 17.2380 -            //TODO -- I don't believe updates can cause failures any more
 17.2381 -            hard("Caused failure of dependent %s --", ((DeclarationSnippet) sn).name());
 17.2382 -            printDiagnostics(source, diagnostics, true);
 17.2383          } else {
 17.2384              // Update
 17.2385 -            SubKind subkind = sn.subKind();
 17.2386 -            if (sn instanceof DeclarationSnippet
 17.2387 -                    && (feedback() == Feedback.Verbose
 17.2388 -                    || ste.status() == Status.OVERWRITTEN
 17.2389 -                    || subkind == SubKind.VAR_DECLARATION_SUBKIND
 17.2390 -                    || subkind == SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND)) {
 17.2391 -                // Under the conditions, display update information
 17.2392 -                displayDeclarationAndValue(ste, true, null);
 17.2393 +            if (sn instanceof DeclarationSnippet) {
 17.2394                  List<Diag> other = errorsOnly(diagnostics);
 17.2395 -                if (other.size() > 0) {
 17.2396 -                    printDiagnostics(source, other, true);
 17.2397 -                }
 17.2398 +
 17.2399 +                // display update information
 17.2400 +                new DisplayEvent(ste, true, ste.value(), other).displayDeclarationAndValue();
 17.2401              }
 17.2402          }
 17.2403          return false;
 17.2404      }
 17.2405 -
 17.2406 -    @SuppressWarnings("fallthrough")
 17.2407 -    private void displayDeclarationAndValue(SnippetEvent ste, boolean update, String value) {
 17.2408 -        Snippet key = ste.snippet();
 17.2409 -        String declared;
 17.2410 -        Status status = ste.status();
 17.2411 -        switch (status) {
 17.2412 -            case VALID:
 17.2413 -            case RECOVERABLE_DEFINED:
 17.2414 -            case RECOVERABLE_NOT_DEFINED:
 17.2415 -                if (ste.previousStatus().isActive) {
 17.2416 -                    declared = ste.isSignatureChange()
 17.2417 -                        ? "Replaced"
 17.2418 -                        : "Modified";
 17.2419 -                } else {
 17.2420 -                    declared = "Added";
 17.2421 -                }
 17.2422 -                break;
 17.2423 -            case OVERWRITTEN:
 17.2424 -                declared = "Overwrote";
 17.2425 -                break;
 17.2426 -            case DROPPED:
 17.2427 -                declared = "Dropped";
 17.2428 -                break;
 17.2429 -            case REJECTED:
 17.2430 -                declared = "Rejected";
 17.2431 -                break;
 17.2432 -            case NONEXISTENT:
 17.2433 -            default:
 17.2434 -                // Should not occur
 17.2435 -                declared = ste.previousStatus().toString() + "=>" + status.toString();
 17.2436 -        }
 17.2437 -        if (update) {
 17.2438 -            declared = "  Update " + declared.toLowerCase();
 17.2439 -        }
 17.2440 -        String however;
 17.2441 -        if (key instanceof DeclarationSnippet && (status == Status.RECOVERABLE_DEFINED || status == Status.RECOVERABLE_NOT_DEFINED)) {
 17.2442 -            String cannotUntil = (status == Status.RECOVERABLE_NOT_DEFINED)
 17.2443 -                    ? " cannot be referenced until"
 17.2444 -                    : " cannot be invoked until";
 17.2445 -            however = (update? " which" : ", however, it") + cannotUntil + unresolved((DeclarationSnippet) key);
 17.2446 -        } else {
 17.2447 -            however = "";
 17.2448 -        }
 17.2449 -        switch (key.subKind()) {
 17.2450 -            case CLASS_SUBKIND:
 17.2451 -                fluff("%s class %s%s", declared, ((TypeDeclSnippet) key).name(), however);
 17.2452 -                break;
 17.2453 -            case INTERFACE_SUBKIND:
 17.2454 -                fluff("%s interface %s%s", declared, ((TypeDeclSnippet) key).name(), however);
 17.2455 -                break;
 17.2456 -            case ENUM_SUBKIND:
 17.2457 -                fluff("%s enum %s%s", declared, ((TypeDeclSnippet) key).name(), however);
 17.2458 -                break;
 17.2459 -            case ANNOTATION_TYPE_SUBKIND:
 17.2460 -                fluff("%s annotation interface %s%s", declared, ((TypeDeclSnippet) key).name(), however);
 17.2461 -                break;
 17.2462 -            case METHOD_SUBKIND:
 17.2463 -                fluff("%s method %s(%s)%s", declared, ((MethodSnippet) key).name(),
 17.2464 -                        ((MethodSnippet) key).parameterTypes(), however);
 17.2465 -                break;
 17.2466 -            case VAR_DECLARATION_SUBKIND:
 17.2467 -                if (!update) {
 17.2468 -                    VarSnippet vk = (VarSnippet) key;
 17.2469 -                    if (status == Status.RECOVERABLE_NOT_DEFINED) {
 17.2470 -                        fluff("%s variable %s%s", declared, vk.name(), however);
 17.2471 -                    } else {
 17.2472 -                        fluff("%s variable %s of type %s%s", declared, vk.name(), vk.typeName(), however);
 17.2473 -                    }
 17.2474 -                    break;
 17.2475 -                }
 17.2476 -            // Fall through
 17.2477 -            case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: {
 17.2478 -                VarSnippet vk = (VarSnippet) key;
 17.2479 -                if (status == Status.RECOVERABLE_NOT_DEFINED) {
 17.2480 -                    if (!update) {
 17.2481 -                        fluff("%s variable %s%s", declared, vk.name(), however);
 17.2482 -                        break;
 17.2483 -                    }
 17.2484 -                } else if (update) {
 17.2485 -                    if (ste.isSignatureChange()) {
 17.2486 -                        hard("%s variable %s, reset to null", declared, vk.name());
 17.2487 -                    }
 17.2488 -                } else {
 17.2489 -                    fluff("%s variable %s of type %s with initial value %s",
 17.2490 -                            declared, vk.name(), vk.typeName(), value);
 17.2491 -                    concise("%s : %s", vk.name(), value);
 17.2492 -                }
 17.2493 -                break;
 17.2494 -            }
 17.2495 -            case TEMP_VAR_EXPRESSION_SUBKIND: {
 17.2496 -                VarSnippet vk = (VarSnippet) key;
 17.2497 -                if (update) {
 17.2498 -                    hard("%s temporary variable %s, reset to null", declared, vk.name());
 17.2499 -                 } else {
 17.2500 -                    fluff("Expression value is: %s", (value));
 17.2501 -                    fluff("  assigned to temporary variable %s of type %s", vk.name(), vk.typeName());
 17.2502 -                    concise("%s : %s", vk.name(), value);
 17.2503 -                }
 17.2504 -                break;
 17.2505 -            }
 17.2506 -            case OTHER_EXPRESSION_SUBKIND:
 17.2507 -                fluff("Expression value is: %s", (value));
 17.2508 -                break;
 17.2509 -            case VAR_VALUE_SUBKIND: {
 17.2510 -                ExpressionSnippet ek = (ExpressionSnippet) key;
 17.2511 -                fluff("Variable %s of type %s has value %s", ek.name(), ek.typeName(), (value));
 17.2512 -                concise("%s : %s", ek.name(), value);
 17.2513 -                break;
 17.2514 -            }
 17.2515 -            case ASSIGNMENT_SUBKIND: {
 17.2516 -                ExpressionSnippet ek = (ExpressionSnippet) key;
 17.2517 -                fluff("Variable %s has been assigned the value %s", ek.name(), (value));
 17.2518 -                concise("%s : %s", ek.name(), value);
 17.2519 -                break;
 17.2520 -            }
 17.2521 -        }
 17.2522 -    }
 17.2523      //where
 17.2524      void printStackTrace(StackTraceElement[] stes) {
 17.2525          for (StackTraceElement ste : stes) {
 17.2526 @@ -1572,9 +2194,9 @@
 17.2527              String fileName = ste.getFileName();
 17.2528              int lineNumber = ste.getLineNumber();
 17.2529              String loc = ste.isNativeMethod()
 17.2530 -                    ? "Native Method"
 17.2531 +                    ? getResourceString("jshell.msg.native.method")
 17.2532                      : fileName == null
 17.2533 -                            ? "Unknown Source"
 17.2534 +                            ? getResourceString("jshell.msg.unknown.source")
 17.2535                              : lineNumber >= 0
 17.2536                                      ? fileName + ":" + lineNumber
 17.2537                                      : fileName;
 17.2538 @@ -1583,28 +2205,11 @@
 17.2539          }
 17.2540      }
 17.2541      //where
 17.2542 -    void printUnresolved(UnresolvedReferenceException ex) {
 17.2543 -        MethodSnippet corralled =  ex.getMethodSnippet();
 17.2544 +    void printUnresolvedException(UnresolvedReferenceException ex) {
 17.2545 +        DeclarationSnippet corralled =  ex.getSnippet();
 17.2546          List<Diag> otherErrors = errorsOnly(state.diagnostics(corralled));
 17.2547 -        StringBuilder sb = new StringBuilder();
 17.2548 -        if (otherErrors.size() > 0) {
 17.2549 -            if (state.unresolvedDependencies(corralled).size() > 0) {
 17.2550 -                sb.append(" and");
 17.2551 -            }
 17.2552 -            if (otherErrors.size() == 1) {
 17.2553 -                sb.append(" this error is addressed --");
 17.2554 -            } else {
 17.2555 -                sb.append(" these errors are addressed --");
 17.2556 -            }
 17.2557 -        } else {
 17.2558 -            sb.append(".");
 17.2559 -        }
 17.2560 -
 17.2561 -        hard("Attempted to call %s which cannot be invoked until%s", corralled.name(),
 17.2562 -                unresolved(corralled), sb.toString());
 17.2563 -        if (otherErrors.size() > 0) {
 17.2564 -            printDiagnostics(corralled.source(), otherErrors, true);
 17.2565 -        }
 17.2566 +        new DisplayEvent(corralled, state.status(corralled), FormatAction.USED, true, null, otherErrors)
 17.2567 +                .displayDeclarationAndValue();
 17.2568      }
 17.2569      //where
 17.2570      void printEvalException(EvalException ex) {
 17.2571 @@ -1615,76 +2220,198 @@
 17.2572          }
 17.2573          printStackTrace(ex.getStackTrace());
 17.2574      }
 17.2575 -    //where
 17.2576 -    String unresolved(DeclarationSnippet key) {
 17.2577 -        List<String> unr = state.unresolvedDependencies(key);
 17.2578 -        StringBuilder sb = new StringBuilder();
 17.2579 -        int fromLast = unr.size();
 17.2580 -        if (fromLast > 0) {
 17.2581 -            sb.append(" ");
 17.2582 +
 17.2583 +    private FormatAction toAction(Status status, Status previousStatus, boolean isSignatureChange) {
 17.2584 +        FormatAction act;
 17.2585 +        switch (status) {
 17.2586 +            case VALID:
 17.2587 +            case RECOVERABLE_DEFINED:
 17.2588 +            case RECOVERABLE_NOT_DEFINED:
 17.2589 +                if (previousStatus.isActive) {
 17.2590 +                    act = isSignatureChange
 17.2591 +                            ? FormatAction.REPLACED
 17.2592 +                            : FormatAction.MODIFIED;
 17.2593 +                } else {
 17.2594 +                    act = FormatAction.ADDED;
 17.2595 +                }
 17.2596 +                break;
 17.2597 +            case OVERWRITTEN:
 17.2598 +                act = FormatAction.OVERWROTE;
 17.2599 +                break;
 17.2600 +            case DROPPED:
 17.2601 +                act = FormatAction.DROPPED;
 17.2602 +                break;
 17.2603 +            case REJECTED:
 17.2604 +            case NONEXISTENT:
 17.2605 +            default:
 17.2606 +                // Should not occur
 17.2607 +                error("Unexpected status: " + previousStatus.toString() + "=>" + status.toString());
 17.2608 +                act = FormatAction.DROPPED;
 17.2609          }
 17.2610 -        for (String u : unr) {
 17.2611 -            --fromLast;
 17.2612 -            sb.append(u);
 17.2613 -            if (fromLast == 0) {
 17.2614 -                // No suffix
 17.2615 -            } else if (fromLast == 1) {
 17.2616 -                sb.append(", and ");
 17.2617 +        return act;
 17.2618 +    }
 17.2619 +
 17.2620 +    class DisplayEvent {
 17.2621 +        private final Snippet sn;
 17.2622 +        private final FormatAction action;
 17.2623 +        private final boolean update;
 17.2624 +        private final String value;
 17.2625 +        private final List<String> errorLines;
 17.2626 +        private final FormatResolve resolution;
 17.2627 +        private final String unresolved;
 17.2628 +        private final FormatUnresolved unrcnt;
 17.2629 +        private final FormatErrors errcnt;
 17.2630 +
 17.2631 +        DisplayEvent(SnippetEvent ste, boolean update, String value, List<Diag> errors) {
 17.2632 +            this(ste.snippet(), ste.status(), toAction(ste.status(), ste.previousStatus(), ste.isSignatureChange()), update, value, errors);
 17.2633 +        }
 17.2634 +
 17.2635 +        DisplayEvent(Snippet sn, Status status, FormatAction action, boolean update, String value, List<Diag> errors) {
 17.2636 +            this.sn = sn;
 17.2637 +            this.action = action;
 17.2638 +            this.update = update;
 17.2639 +            this.value = value;
 17.2640 +            this.errorLines = new ArrayList<>();
 17.2641 +            for (Diag d : errors) {
 17.2642 +                displayDiagnostics(sn.source(), d, errorLines);
 17.2643 +            }
 17.2644 +            int unresolvedCount;
 17.2645 +            if (sn instanceof DeclarationSnippet && (status == Status.RECOVERABLE_DEFINED || status == Status.RECOVERABLE_NOT_DEFINED)) {
 17.2646 +                resolution = (status == Status.RECOVERABLE_NOT_DEFINED)
 17.2647 +                        ? FormatResolve.NOTDEFINED
 17.2648 +                        : FormatResolve.DEFINED;
 17.2649 +                unresolved = unresolved((DeclarationSnippet) sn);
 17.2650 +                unresolvedCount = state.unresolvedDependencies((DeclarationSnippet) sn).size();
 17.2651              } else {
 17.2652 -                sb.append(", ");
 17.2653 +                resolution = FormatResolve.OK;
 17.2654 +                unresolved = "";
 17.2655 +                unresolvedCount = 0;
 17.2656 +            }
 17.2657 +            unrcnt = unresolvedCount == 0
 17.2658 +                    ? FormatUnresolved.UNRESOLVED0
 17.2659 +                    : unresolvedCount == 1
 17.2660 +                        ? FormatUnresolved.UNRESOLVED1
 17.2661 +                        : FormatUnresolved.UNRESOLVED2;
 17.2662 +            errcnt = errors.isEmpty()
 17.2663 +                    ? FormatErrors.ERROR0
 17.2664 +                    : errors.size() == 1
 17.2665 +                        ? FormatErrors.ERROR1
 17.2666 +                        : FormatErrors.ERROR2;
 17.2667 +        }
 17.2668 +
 17.2669 +        private String unresolved(DeclarationSnippet key) {
 17.2670 +            List<String> unr = state.unresolvedDependencies(key);
 17.2671 +            StringBuilder sb = new StringBuilder();
 17.2672 +            int fromLast = unr.size();
 17.2673 +            if (fromLast > 0) {
 17.2674 +                sb.append(" ");
 17.2675 +            }
 17.2676 +            for (String u : unr) {
 17.2677 +                --fromLast;
 17.2678 +                sb.append(u);
 17.2679 +                switch (fromLast) {
 17.2680 +                    // No suffix
 17.2681 +                    case 0:
 17.2682 +                        break;
 17.2683 +                    case 1:
 17.2684 +                        sb.append(", and ");
 17.2685 +                        break;
 17.2686 +                    default:
 17.2687 +                        sb.append(", ");
 17.2688 +                        break;
 17.2689 +                }
 17.2690 +            }
 17.2691 +            return sb.toString();
 17.2692 +        }
 17.2693 +
 17.2694 +        private void custom(FormatCase fcase, String name) {
 17.2695 +            custom(fcase, name, null);
 17.2696 +        }
 17.2697 +
 17.2698 +        private void custom(FormatCase fcase, String name, String type) {
 17.2699 +            String display = feedback.format(fcase, action, (update ? FormatWhen.UPDATE : FormatWhen.PRIMARY),
 17.2700 +                    resolution, unrcnt, errcnt,
 17.2701 +                    name, type, value, unresolved, errorLines);
 17.2702 +            if (interactive()) {
 17.2703 +                cmdout.print(display);
 17.2704              }
 17.2705          }
 17.2706 -        switch (unr.size()) {
 17.2707 -            case 0:
 17.2708 -                break;
 17.2709 -            case 1:
 17.2710 -                sb.append(" is declared");
 17.2711 -                break;
 17.2712 -            default:
 17.2713 -                sb.append(" are declared");
 17.2714 -                break;
 17.2715 +
 17.2716 +        @SuppressWarnings("fallthrough")
 17.2717 +        private void displayDeclarationAndValue() {
 17.2718 +            switch (sn.subKind()) {
 17.2719 +                case CLASS_SUBKIND:
 17.2720 +                    custom(FormatCase.CLASS, ((TypeDeclSnippet) sn).name());
 17.2721 +                    break;
 17.2722 +                case INTERFACE_SUBKIND:
 17.2723 +                    custom(FormatCase.INTERFACE, ((TypeDeclSnippet) sn).name());
 17.2724 +                    break;
 17.2725 +                case ENUM_SUBKIND:
 17.2726 +                    custom(FormatCase.ENUM, ((TypeDeclSnippet) sn).name());
 17.2727 +                    break;
 17.2728 +                case ANNOTATION_TYPE_SUBKIND:
 17.2729 +                    custom(FormatCase.ANNOTATION, ((TypeDeclSnippet) sn).name());
 17.2730 +                    break;
 17.2731 +                case METHOD_SUBKIND:
 17.2732 +                    custom(FormatCase.METHOD, ((MethodSnippet) sn).name(), ((MethodSnippet) sn).parameterTypes());
 17.2733 +                    break;
 17.2734 +                case VAR_DECLARATION_SUBKIND: {
 17.2735 +                    VarSnippet vk = (VarSnippet) sn;
 17.2736 +                    custom(FormatCase.VARDECL, vk.name(), vk.typeName());
 17.2737 +                    break;
 17.2738 +                }
 17.2739 +                case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: {
 17.2740 +                    VarSnippet vk = (VarSnippet) sn;
 17.2741 +                    custom(FormatCase.VARINIT, vk.name(), vk.typeName());
 17.2742 +                    break;
 17.2743 +                }
 17.2744 +                case TEMP_VAR_EXPRESSION_SUBKIND: {
 17.2745 +                    VarSnippet vk = (VarSnippet) sn;
 17.2746 +                    custom(FormatCase.EXPRESSION, vk.name(), vk.typeName());
 17.2747 +                    break;
 17.2748 +                }
 17.2749 +                case OTHER_EXPRESSION_SUBKIND:
 17.2750 +                    error("Unexpected expression form -- value is: %s", (value));
 17.2751 +                    break;
 17.2752 +                case VAR_VALUE_SUBKIND: {
 17.2753 +                    ExpressionSnippet ek = (ExpressionSnippet) sn;
 17.2754 +                    custom(FormatCase.VARVALUE, ek.name(), ek.typeName());
 17.2755 +                    break;
 17.2756 +                }
 17.2757 +                case ASSIGNMENT_SUBKIND: {
 17.2758 +                    ExpressionSnippet ek = (ExpressionSnippet) sn;
 17.2759 +                    custom(FormatCase.ASSIGNMENT, ek.name(), ek.typeName());
 17.2760 +                    break;
 17.2761 +                }
 17.2762 +                case SINGLE_TYPE_IMPORT_SUBKIND:
 17.2763 +                case TYPE_IMPORT_ON_DEMAND_SUBKIND:
 17.2764 +                case SINGLE_STATIC_IMPORT_SUBKIND:
 17.2765 +                case STATIC_IMPORT_ON_DEMAND_SUBKIND:
 17.2766 +                    custom(FormatCase.IMPORT, ((ImportSnippet) sn).name());
 17.2767 +                    break;
 17.2768 +                case STATEMENT_SUBKIND:
 17.2769 +                    custom(FormatCase.STATEMENT, null);
 17.2770 +                    break;
 17.2771 +            }
 17.2772          }
 17.2773 -        return sb.toString();
 17.2774 -    }
 17.2775 -
 17.2776 -    enum Feedback {
 17.2777 -        Default,
 17.2778 -        Off,
 17.2779 -        Concise,
 17.2780 -        Normal,
 17.2781 -        Verbose
 17.2782 -    }
 17.2783 -
 17.2784 -    Feedback feedback() {
 17.2785 -        if (feedback == Feedback.Default) {
 17.2786 -            return input == null || input.interactiveOutput() ? Feedback.Normal : Feedback.Off;
 17.2787 -        }
 17.2788 -        return feedback;
 17.2789 -    }
 17.2790 -
 17.2791 -    boolean notInStartUp(Snippet sn) {
 17.2792 -        return mapSnippet.get(sn).space != startNamespace;
 17.2793      }
 17.2794  
 17.2795      /** The current version number as a string.
 17.2796       */
 17.2797 -    static String version() {
 17.2798 +    String version() {
 17.2799          return version("release");  // mm.nn.oo[-milestone]
 17.2800      }
 17.2801  
 17.2802      /** The current full version number as a string.
 17.2803       */
 17.2804 -    static String fullVersion() {
 17.2805 +    String fullVersion() {
 17.2806          return version("full"); // mm.mm.oo[-milestone]-build
 17.2807      }
 17.2808  
 17.2809 -    private static final String versionRBName = "jdk.internal.jshell.tool.resources.version";
 17.2810 -    private static ResourceBundle versionRB;
 17.2811 -
 17.2812 -    private static String version(String key) {
 17.2813 +    private String version(String key) {
 17.2814          if (versionRB == null) {
 17.2815              try {
 17.2816 -                versionRB = ResourceBundle.getBundle(versionRBName);
 17.2817 +                versionRB = ResourceBundle.getBundle(VERSION_RB_NAME, locale);
 17.2818              } catch (MissingResourceException e) {
 17.2819                  return "(version info not available)";
 17.2820              }
 17.2821 @@ -1732,31 +2459,11 @@
 17.2822      }
 17.2823  }
 17.2824  
 17.2825 -class ScannerIOContext extends IOContext {
 17.2826 -
 17.2827 -    private final Scanner scannerIn;
 17.2828 -    private final PrintStream pStream;
 17.2829 -
 17.2830 -    public ScannerIOContext(Scanner scannerIn, PrintStream pStream) {
 17.2831 -        this.scannerIn = scannerIn;
 17.2832 -        this.pStream = pStream;
 17.2833 -    }
 17.2834 -
 17.2835 -    @Override
 17.2836 -    public String readLine(String prompt, String prefix) {
 17.2837 -        if (pStream != null && prompt != null) {
 17.2838 -            pStream.print(prompt);
 17.2839 -        }
 17.2840 -        if (scannerIn.hasNextLine()) {
 17.2841 -            return scannerIn.nextLine();
 17.2842 -        } else {
 17.2843 -            return null;
 17.2844 -        }
 17.2845 -    }
 17.2846 +abstract class NonInteractiveIOContext extends IOContext {
 17.2847  
 17.2848      @Override
 17.2849      public boolean interactiveOutput() {
 17.2850 -        return true;
 17.2851 +        return false;
 17.2852      }
 17.2853  
 17.2854      @Override
 17.2855 @@ -1765,11 +2472,6 @@
 17.2856      }
 17.2857  
 17.2858      @Override
 17.2859 -    public void close() {
 17.2860 -        scannerIn.close();
 17.2861 -    }
 17.2862 -
 17.2863 -    @Override
 17.2864      public boolean terminalEditorRunning() {
 17.2865          return false;
 17.2866      }
 17.2867 @@ -1795,19 +2497,62 @@
 17.2868      }
 17.2869  }
 17.2870  
 17.2871 +class ScannerIOContext extends NonInteractiveIOContext {
 17.2872 +    private final Scanner scannerIn;
 17.2873 +
 17.2874 +    ScannerIOContext(Scanner scannerIn) {
 17.2875 +        this.scannerIn = scannerIn;
 17.2876 +    }
 17.2877 +
 17.2878 +    @Override
 17.2879 +    public String readLine(String prompt, String prefix) {
 17.2880 +        if (scannerIn.hasNextLine()) {
 17.2881 +            return scannerIn.nextLine();
 17.2882 +        } else {
 17.2883 +            return null;
 17.2884 +        }
 17.2885 +    }
 17.2886 +
 17.2887 +    @Override
 17.2888 +    public void close() {
 17.2889 +        scannerIn.close();
 17.2890 +    }
 17.2891 +}
 17.2892 +
 17.2893  class FileScannerIOContext extends ScannerIOContext {
 17.2894  
 17.2895 -    public FileScannerIOContext(String fn) throws FileNotFoundException {
 17.2896 +    FileScannerIOContext(String fn) throws FileNotFoundException {
 17.2897          this(new FileReader(fn));
 17.2898      }
 17.2899  
 17.2900 -    public FileScannerIOContext(Reader rdr) throws FileNotFoundException {
 17.2901 -        super(new Scanner(rdr), null);
 17.2902 +    FileScannerIOContext(Reader rdr) throws FileNotFoundException {
 17.2903 +        super(new Scanner(rdr));
 17.2904 +    }
 17.2905 +}
 17.2906 +
 17.2907 +class ReloadIOContext extends NonInteractiveIOContext {
 17.2908 +    private final Iterator<String> it;
 17.2909 +    private final PrintStream echoStream;
 17.2910 +
 17.2911 +    ReloadIOContext(Iterable<String> history, PrintStream echoStream) {
 17.2912 +        this.it = history.iterator();
 17.2913 +        this.echoStream = echoStream;
 17.2914      }
 17.2915  
 17.2916      @Override
 17.2917 -    public boolean interactiveOutput() {
 17.2918 -        return false;
 17.2919 +    public String readLine(String prompt, String prefix) {
 17.2920 +        String s = it.hasNext()
 17.2921 +                ? it.next()
 17.2922 +                : null;
 17.2923 +        if (echoStream != null && s != null) {
 17.2924 +            String p = "-: ";
 17.2925 +            String p2 = "\n   ";
 17.2926 +            echoStream.printf("%s%s\n", p, s.replace("\n", p2));
 17.2927 +        }
 17.2928 +        return s;
 17.2929 +    }
 17.2930 +
 17.2931 +    @Override
 17.2932 +    public void close() {
 17.2933      }
 17.2934  }
 17.2935 -
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/MessageHandler.java	Fri Sep 02 23:54:26 2016 +0200
    18.3 @@ -0,0 +1,41 @@
    18.4 +/*
    18.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    18.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    18.7 + *
    18.8 + * This code is free software; you can redistribute it and/or modify it
    18.9 + * under the terms of the GNU General Public License version 2 only, as
   18.10 + * published by the Free Software Foundation.  Oracle designates this
   18.11 + * particular file as subject to the "Classpath" exception as provided
   18.12 + * by Oracle in the LICENSE file that accompanied this code.
   18.13 + *
   18.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   18.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   18.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   18.17 + * version 2 for more details (a copy is included in the LICENSE file that
   18.18 + * accompanied this code).
   18.19 + *
   18.20 + * You should have received a copy of the GNU General Public License version
   18.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   18.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   18.23 + *
   18.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   18.25 + * or visit www.oracle.com if you need additional information or have any
   18.26 + * questions.
   18.27 + */
   18.28 +
   18.29 +package jdk.internal.jshell.tool;
   18.30 +
   18.31 +
   18.32 +/**
   18.33 + * User message reporting support
   18.34 + *
   18.35 + * @author Robert Field
   18.36 + */
   18.37 +public interface MessageHandler {
   18.38 +
   18.39 +    void fluff(String format, Object... args);
   18.40 +
   18.41 +    void fluffmsg(String messageKey, Object... args);
   18.42 +
   18.43 +    void errormsg(String messageKey, Object... args);
   18.44 +}
    19.1 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/StopDetectingInputStream.java	Fri Jun 03 17:06:38 2016 +0200
    19.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/StopDetectingInputStream.java	Fri Sep 02 23:54:26 2016 +0200
    19.3 @@ -22,6 +22,7 @@
    19.4   * or visit www.oracle.com if you need additional information or have any
    19.5   * questions.
    19.6   */
    19.7 +
    19.8  package jdk.internal.jshell.tool;
    19.9  
   19.10  import java.io.IOException;
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties	Fri Sep 02 23:54:26 2016 +0200
    20.3 @@ -0,0 +1,765 @@
    20.4 +#
    20.5 +# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    20.6 +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    20.7 +#
    20.8 +# This code is free software; you can redistribute it and/or modify it
    20.9 +# under the terms of the GNU General Public License version 2 only, as
   20.10 +# published by the Free Software Foundation.  Oracle designates this
   20.11 +# particular file as subject to the "Classpath" exception as provided
   20.12 +# by Oracle in the LICENSE file that accompanied this code.
   20.13 +#
   20.14 +# This code is distributed in the hope that it will be useful, but WITHOUT
   20.15 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   20.16 +# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   20.17 +# version 2 for more details (a copy is included in the LICENSE file that
   20.18 +# accompanied this code).
   20.19 +#
   20.20 +# You should have received a copy of the GNU General Public License version
   20.21 +# 2 along with this work; if not, write to the Free Software Foundation,
   20.22 +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20.23 +#
   20.24 +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   20.25 +# or visit www.oracle.com if you need additional information or have any
   20.26 +# questions.
   20.27 +#
   20.28 +
   20.29 +jshell.msg.welcome =\
   20.30 +Welcome to JShell -- Version {0}\n\
   20.31 +For an introduction type: /help intro\n
   20.32 +jshell.err.opt.classpath.conflict = Conflicting -classpath option.
   20.33 +jshell.err.opt.classpath.arg = Argument to -classpath missing.
   20.34 +jshell.err.opt.feedback.arg = Argument to -feedback missing. Mode required.
   20.35 +jshell.err.opt.startup.conflict = Conflicting -startup or -nostartup option.
   20.36 +jshell.err.opt.unknown = Unknown option: {0}
   20.37 +
   20.38 +jshell.msg.terminated =\
   20.39 +State engine terminated.\n\
   20.40 +Restore definitions with: /reload -restore
   20.41 +
   20.42 +jshell.msg.use.one.of = Use one of: {0}
   20.43 +jshell.msg.see.classes.etc = See /types, /methods, /vars, or /list
   20.44 +jshell.err.arg = Invalid ''{0}'' argument: {1}
   20.45 +jshell.msg.see = See {0} for help.
   20.46 +
   20.47 +jshell.err.file.not.accessible = File ''{1}'' for ''{0}'' is not accessible: {2}
   20.48 +jshell.err.file.not.found = File ''{1}'' for ''{0}'' is not found.
   20.49 +jshell.err.file.exception = File ''{1}'' for ''{0}'' threw exception: {2}
   20.50 +jshell.err.file.filename = ''{0}'' requires a filename argument.
   20.51 +
   20.52 +jshell.err.startup.unexpected.exception = Unexpected exception reading start-up: {0}
   20.53 +jshell.err.unexpected.exception = Unexpected exception: {0}
   20.54 +
   20.55 +jshell.err.no.such.command.or.snippet.id = No such command or snippet id: {0}
   20.56 +jshell.err.command.ambiguous = Command: ''{0}'' is ambiguous: {1}
   20.57 +jshell.err.set.editor.arg = The ''/set editor'' command requires a path argument
   20.58 +jshell.msg.set.editor.set = Editor set to: {0}
   20.59 +jshell.err.cant.launch.editor = Cannot launch editor -- unexpected exception: {0}
   20.60 +jshell.msg.try.set.editor = Try /set editor to use external editor.
   20.61 +
   20.62 +jshell.msg.try.command.without.args = Try ''{0}'' without arguments.
   20.63 +jshell.msg.no.active = There are no active definitions.
   20.64 +
   20.65 +jshell.msg.resetting = Resetting...
   20.66 +jshell.msg.resetting.state = Resetting state.
   20.67 +
   20.68 +jshell.err.reload.no.previous = No previous history to restore
   20.69 +jshell.err.reload.restarting.previous.state = Restarting and restoring from previous state.
   20.70 +jshell.err.reload.restarting.state = Restarting and restoring state.
   20.71 +
   20.72 +jshell.msg.vars.not.active = (not-active)
   20.73 +
   20.74 +jshell.err.out.of.range = Out of range
   20.75 +
   20.76 +jshell.msg.error = Error:
   20.77 +jshell.msg.warning = Warning:
   20.78 +
   20.79 +jshell.err.sub.arg = The ''{0}'' command requires a sub-command. See: ''/help {0}''
   20.80 +jshell.err.sub.ambiguous = Ambiguous sub-command argument to ''{0}'': {1}
   20.81 +
   20.82 +jshell.err.classpath.arg = The /classpath command requires a path argument.
   20.83 +jshell.msg.classpath = Path ''{0}'' added to classpath
   20.84 +
   20.85 +jshell.err.help.arg = No commands or subjects start with the provided argument: {0}
   20.86 +jshell.msg.help.begin =\
   20.87 +Type a Java language expression, statement, or declaration.\n\
   20.88 +Or type one of the following commands:\n
   20.89 +jshell.msg.help.subject =\n\
   20.90 +For more information type ''/help'' followed by the name of command or a subject.\n\
   20.91 +For example ''/help /list'' or ''/help intro''.  Subjects:\n
   20.92 +
   20.93 +jshell.err.drop.arg =\
   20.94 +In the /drop argument, please specify an import, variable, method, or class to drop.\n\
   20.95 +Specify by id or name. Use /list to see ids. Use /reset to reset all state.
   20.96 +jshell.err.drop.ambiguous = The argument references more than one import, variable, method, or class.
   20.97 +jshell.err.failed = Failed.
   20.98 +jshell.msg.native.method = Native Method
   20.99 +jshell.msg.unknown.source = Unknown Source
  20.100 +jshell.msg.goodbye = Goodbye
  20.101 +
  20.102 +jshell.msg.help.for.help = Type /help for help.
  20.103 +
  20.104 +jshell.err.mode.name = Expected a feedback mode name: {0}
  20.105 +jshell.err.missing.mode = Missing the feedback mode -- {0}
  20.106 +jshell.err.field.name = Expected a field name: {0} -- {1}
  20.107 +jshell.err.missing.field = Missing the field name -- {0}
  20.108 +jshell.err.mode.unknown = No feedback mode named: {0} -- {1}
  20.109 +
  20.110 +jshell.err.feedback.does.not.match.mode = Does not match any current feedback mode: {0} -- {1}
  20.111 +jshell.err.feedback.ambiguous.mode = Matches more then one current feedback mode: {0} -- {1}
  20.112 +jshell.err.feedback.expected.format = Expected format missing -- {0}
  20.113 +jshell.err.feedback.must.be.quoted = Format ''{0}'' must be quoted -- {1}
  20.114 +jshell.err.feedback.not.a.valid.selector = Not a valid selector ''{0}'' in ''{1}'' -- {2}
  20.115 +jshell.err.feedback.multiple.sections = Selector kind in multiple sections of selector list ''{0}'' in ''{1}'' -- {2}
  20.116 +jshell.err.feedback.different.selector.kinds = Different selector kinds in same sections of selector list ''{0}'' in ''{1}'' -- {2}
  20.117 +
  20.118 +jshell.msg.feedback.new.mode = Created new feedback mode: {0}
  20.119 +jshell.msg.feedback.mode = Feedback mode: {0}
  20.120 +jshell.msg.feedback.mode.following = The feedback mode should be one of the following:
  20.121 +
  20.122 +jshell.err.truncation.expected.length = Expected truncation length -- {0}
  20.123 +jshell.err.truncation.length.not.integer = Truncation length must be an integer: {0} -- {1}
  20.124 +
  20.125 +jshell.err.not.valid.with.predefined.mode = Not valid with a predefined mode: {0} -- {1}
  20.126 +jshell.err.retained.feedback.mode.must.be.retained.or.predefined = \
  20.127 +''/retain feedback <mode>'' requires that <mode> is predefined or has been retained with ''/retain mode'' -- {0}
  20.128 +
  20.129 +jshell.err.unknown.option = Unknown option: {0} -- {1}
  20.130 +jshell.err.default.option.or.program = Specify -default option or program, not both -- {0}
  20.131 +jshell.err.option.or.filename = Specify either one option or a startup file name -- {0}
  20.132 +jshell.err.unexpected.at.end = Unexpected arguments at end of command: {0} -- {1}
  20.133 +jshell.err.conflicting.options = Conflicting options -- {0}
  20.134 +jshell.err.cannot.delete.current.mode = The current feedback mode ''{0}'' cannot be deleted, use ''/set feedback'' first -- {1}
  20.135 +jshell.err.cannot.delete.retained.mode = The retained feedback mode ''{0}'' cannot be deleted, use ''/retain feedback'' first -- {1}
  20.136 +jshell.err.may.not.specify.options.and.snippets = Options and snippets must not both be used: {0}
  20.137 +jshell.err.no.such.snippets = No such snippet: {0}
  20.138 +jshell.err.the.snippet.cannot.be.used.with.this.command = This command does not accept the snippet ''{0}'' : {1}
  20.139 +jshell.err.retained.mode.failure = Failure in retained modes (modes cleared) -- {0} {1}
  20.140 +
  20.141 +jshell.console.see.more = <press tab to see more>
  20.142 +jshell.console.do.nothing = Do nothing
  20.143 +jshell.console.choice = Choice: \
  20.144 +
  20.145 +jshell.console.create.variable = Create variable
  20.146 +jshell.console.resolvable = \nThe identifier is resolvable in this context.
  20.147 +jshell.console.no.candidate = \nNo candidate fully qualified names found to import.
  20.148 +jshell.console.incomplete = \nResults may be incomplete; try again later for complete results.
  20.149 +
  20.150 +
  20.151 +help.usage = \
  20.152 +Usage:   jshell <options> <load files>\n\
  20.153 +where possible options include:\n\
  20.154 +\    -classpath <path>    Specify where to find user class files\n\
  20.155 +\    -cp <path>           Specify where to find user class files\n\
  20.156 +\    -startup <file>      One run replacement for the start-up definitions\n\
  20.157 +\    -nostartup           Do not run the start-up definitions\n\
  20.158 +\    -feedback <mode>     Specify the initial feedback mode. The mode may be\n\
  20.159 +\                         predefined (silent, concise, normal, or verbose) or\n\
  20.160 +\                         previously user-defined\n\
  20.161 +\    -q                   Quiet feedback.  Same as: -feedback concise\n\
  20.162 +\    -qq                  Really quiet feedback.  Same as: -feedback silent\n\
  20.163 +\    -v                   Verbose feedback.  Same as: -feedback verbose\n\
  20.164 +\    -J<flag>             Pass <flag> directly to the runtime system.\n\
  20.165 +\                         Use one -J for each runtime flag or flag argument\n\
  20.166 +\    -R<flag>             Pass <flag> to the remote runtime system.\n\
  20.167 +\                         Use one -R for each remote flag or flag argument\n\
  20.168 +\    -help                Print this synopsis of standard options\n\
  20.169 +\    -version             Version information\n
  20.170 +
  20.171 +help.list.summary = list the source you have typed
  20.172 +help.list.args = [<name or id>|-all|-start]
  20.173 +help.list =\
  20.174 +Show the source of snippets, prefaced with the snippet id.\n\
  20.175 +\n\
  20.176 +/list\n\t\
  20.177 +    List the currently active snippets of code that you typed or read with /open\n\n\
  20.178 +/list -start\n\t\
  20.179 +    List the automatically evaluated start-up snippets\n\n\
  20.180 +/list -all\n\t\
  20.181 +    List all snippets including failed, overwritten, dropped, and start-up\n\n\
  20.182 +/list <name>\n\t\
  20.183 +    List snippets with the specified name (preference for active snippets)\n\n\
  20.184 +/list <id>\n\t\
  20.185 +    List the snippet with the specified snippet id
  20.186 +
  20.187 +help.edit.summary = edit a source entry referenced by name or id
  20.188 +help.edit.args = <name or id>
  20.189 +help.edit =\
  20.190 +Edit a snippet or snippets of source in an external editor.\n\
  20.191 +The editor to use is set with /set editor.\n\
  20.192 +If no editor has been set, a simple editor will be launched.\n\
  20.193 +\n\
  20.194 +/edit <name>\n\t\
  20.195 +    Edit the snippet or snippets with the specified name (preference for active snippets)\n\n\
  20.196 +/edit <id>\n\t\
  20.197 +    Edit the snippet with the specified snippet id\n\n\
  20.198 +/edit\n\t\
  20.199 +    Edit the currently active snippets of code that you typed or read with /open
  20.200 +
  20.201 +help.drop.summary = delete a source entry referenced by name or id
  20.202 +help.drop.args = <name or id>
  20.203 +help.drop =\
  20.204 +Drop a snippet -- making it inactive.\n\
  20.205 +\n\
  20.206 +/drop <name>\n\t\
  20.207 +    Drop the snippet with the specified name\n\n\
  20.208 +/drop <id>\n\t\
  20.209 +    Drop the snippet with the specified snippet id
  20.210 +
  20.211 +help.save.summary = Save snippet source to a file.
  20.212 +help.save.args = [-all|-history|-start] <file>
  20.213 +help.save =\
  20.214 +Save the specified snippets and/or commands to the specified file.\n\
  20.215 +\n\
  20.216 +/save <file>\n\t\
  20.217 +    Save the source of current active snippets to the file.\n\n\
  20.218 +/save -all <file>\n\t\
  20.219 +    Save the source of all snippets to the file.\n\t\
  20.220 +    Includes source including overwritten, failed, and start-up code.\n\n\
  20.221 +/save -history <file>\n\t\
  20.222 +    Save the sequential history of all commands and snippets entered since jshell was launched.\n\n\
  20.223 +/save -start <file>\n\t\
  20.224 +    Save the default start-up definitions to the file.
  20.225 +
  20.226 +help.open.summary = open a file as source input
  20.227 +help.open.args = <file>
  20.228 +help.open =\
  20.229 +Open a file and read its contents as snippets and commands.\n\
  20.230 +\n\
  20.231 +/open <file>\n\t\
  20.232 +    Read the specified file as jshell input.
  20.233 +
  20.234 +help.vars.summary = list the declared variables and their values
  20.235 +help.vars.args = [<name or id>|-all|-start]
  20.236 +help.vars =\
  20.237 +List the type, name, and value of jshell variables.\n\
  20.238 +\n\
  20.239 +/vars\n\t\
  20.240 +    List the type, name, and value of the current active jshell variables\n\n\
  20.241 +/vars <name>\n\t\
  20.242 +    List jshell variables with the specified name (preference for active variables)\n\n\
  20.243 +/vars <id>\n\t\
  20.244 +    List the jshell variable with the specified snippet id\n\n\
  20.245 +/vars -start\n\t\
  20.246 +    List the automatically added start-up jshell variables\n\n\
  20.247 +/vars -all\n\t\
  20.248 +    List all jshell variables including failed, overwritten, dropped, and start-up
  20.249 +
  20.250 +help.methods.summary = list the declared methods and their signatures
  20.251 +help.methods.args = [<name or id>|-all|-start]
  20.252 +help.methods =\
  20.253 +List the name, parameter types, and return type of jshell methods.\n\
  20.254 +\n\
  20.255 +/methods\n\t\
  20.256 +    List the name, parameter types, and return type of the current active jshell methods\n\n\
  20.257 +/methods <name>\n\t\
  20.258 +    List jshell methods with the specified name (preference for active methods)\n\n\
  20.259 +/methods <id>\n\t\
  20.260 +    List the jshell method with the specified snippet id\n\n\
  20.261 +/methods -start\n\t\
  20.262 +    List the automatically added start-up jshell methods\n\n\
  20.263 +/methods -all\n\t\
  20.264 +    List all snippets including failed, overwritten, dropped, and start-up
  20.265 +
  20.266 +help.types.summary = list the declared types
  20.267 +help.types.args =[<name or id>|-all|-start]
  20.268 +help.types =\
  20.269 +List jshell classes, interfaces, and enums.\n\
  20.270 +\n\
  20.271 +/types\n\t\
  20.272 +    List the current active jshell classes, interfaces, and enums.\n\n\
  20.273 +/types <name>\n\t\
  20.274 +    List jshell types with the specified name (preference for active types)\n\n\
  20.275 +/types <id>\n\t\
  20.276 +    List the jshell type with the specified snippet id\n\n\
  20.277 +/types -start\n\t\
  20.278 +    List the automatically added start-up jshell types\n\n\
  20.279 +/types -all\n\t\
  20.280 +    List all jshell types including failed, overwritten, dropped, and start-up
  20.281 +
  20.282 +help.imports.summary = list the imported items
  20.283 +help.imports.args =
  20.284 +help.imports =\
  20.285 +List the current active jshell imports.
  20.286 +
  20.287 +help.exit.summary = exit jshell
  20.288 +help.exit.args =
  20.289 +help.exit =\
  20.290 +Leave the jshell tool.  No work is saved.\n\
  20.291 +Save any work before using this command
  20.292 +
  20.293 +help.reset.summary = reset jshell
  20.294 +help.reset.args =
  20.295 +help.reset =\
  20.296 +Reset the jshell tool code and execution state:\n\t\
  20.297 +   * All entered code is lost.\n\t\
  20.298 +   * Start-up code is re-executed.\n\t\
  20.299 +   * The execution state is restarted.\n\t\
  20.300 +   * The classpath is cleared.\n\
  20.301 +Tool settings are maintained, as set with: /set ...\n\
  20.302 +Save any work before using this command
  20.303 +
  20.304 +help.reload.summary = reset and replay relevant history -- current or previous (-restore)
  20.305 +help.reload.args = [-restore] [-quiet]
  20.306 +help.reload =\
  20.307 +Reset the jshell tool code and execution state then replay each\n\
  20.308 +jshell valid command and valid snippet in the order they were entered.\n\
  20.309 +\n\
  20.310 +/reload\n\t\
  20.311 +     Reset and replay the valid history since jshell was entered, or\n\t\
  20.312 +     a /reset, or /reload command was executed -- whichever is most\n\t\
  20.313 +     recent.\n\n\
  20.314 +/reload -restore\n\t\
  20.315 +     Reset and replay the valid history between the previous and most\n\t\
  20.316 +     recent time that jshell was entered, or a /reset, or /reload\n\t\
  20.317 +     command was executed. This can thus be used to restore a previous\n\t\
  20.318 +     jshell tool sesson.\n\n\
  20.319 +/reload [-restore] -quiet\n\t\
  20.320 +     With the '-quiet' argument the replay is not shown.  Errors will display.
  20.321 +
  20.322 +help.classpath.summary = add a path to the classpath
  20.323 +help.classpath.args = <path>
  20.324 +help.classpath =\
  20.325 +Append a additional path to the classpath.
  20.326 +
  20.327 +help.history.summary = history of what you have typed
  20.328 +help.history.args =
  20.329 +help.history =\
  20.330 +Display the history of snippet and command input since this jshell was launched.
  20.331 +
  20.332 +help.debug.summary = toggle debugging of the jshell
  20.333 +help.debug.args = [0][r][g][f][c][d][e]
  20.334 +help.debug =\
  20.335 +Display debugging information for the jshell implementation.\n\
  20.336 +0: Debugging off\n\
  20.337 +r: Tool level debugging on\n\
  20.338 +g: General debugging on\n\
  20.339 +f: File manager debugging on\n\
  20.340 +c: Completion analysis debugging on\n\
  20.341 +d: Dependency debugging on\n\
  20.342 +e: Event debugging on
  20.343 +
  20.344 +help.help.summary = get information about jshell
  20.345 +help.help.args = [<command>|<subject>]
  20.346 +help.help =\
  20.347 +Display information about jshell.\n\
  20.348 +/help\n\t\
  20.349 +     List the jshell commands and help subjects.\n\n\
  20.350 +/help <command>\n\t\
  20.351 +     Display information about the specified comand. The slash must be included.\n\t\
  20.352 +     Only the first few letters of the command are needed -- if more than one\n\t\
  20.353 +     each will be displayed.  Example:  /help /li\n\n\
  20.354 +/help <subject>\n\t\
  20.355 +     Display information about the specified help subject. Example: /help intro
  20.356 +
  20.357 +help.set.summary = set jshell configuration information
  20.358 +help.set.args = editor|start|feedback|mode|prompt|truncation|format ...
  20.359 +help.set =\
  20.360 +Set jshell configuration information, including:\n\
  20.361 +the external editor to use, the start-up definitions to use, a new feedback mode,\n\
  20.362 +the command prompt, the feedback mode to use, or the format of output.\n\
  20.363 +\n\
  20.364 +/set editor <command> <optional-arg>...\n\t\
  20.365 +     Specify the command to launch for the /edit command.\n\t\
  20.366 +     The <command> is an operating system dependent string.\n\n\
  20.367 +/set start <file>\n\t\
  20.368 +     The contents of the specified <file> become the default start-up snippets and commands.\n\n\
  20.369 +/set feedback <mode>\n\t\
  20.370 +     Set the feedback mode describing displayed feedback for entered snippets and commands.\n\n\
  20.371 +/set mode <mode> [<old-mode>] [-command|-quiet|-delete]\n\t\
  20.372 +     Create or update a user-defined feedback mode, optionally copying from an existing mode.\n\n\
  20.373 +/set prompt <mode> "<prompt>" "<continuation-prompt>"\n\t\
  20.374 +     Set the displayed prompts for a given feedback mode.\n\n\
  20.375 +/set truncation <mode> <length> <selector>...\n\t\
  20.376 +     Set the maximum length of a displayed value\n\
  20.377 +/set format <mode> <field> "<format>" <selector>...\n\t\
  20.378 +     Configure a feedback mode by setting the format of a field when the selector matchs.\n\n\
  20.379 +To get more information about one of these forms, use /help with the form specified.\n\
  20.380 +For example:   /help /set format
  20.381 +
  20.382 +help.retain.summary = retain jshell configuration information for subsequent sessions
  20.383 +help.retain.args = editor|start|feedback|mode
  20.384 +help.retain =\
  20.385 +Retain jshell configuration information for future invocations of the jshell tool,\n\
  20.386 +including: the external editor to use, the start-up definitions to use, the\n\
  20.387 +configuration of a feedback mode, or the feedback mode to use.\n\
  20.388 +\n\
  20.389 +/retain editor [<command> <optional-arg>...]\n\t\
  20.390 +     Specify the command to launch for the /edit command.\n\t\
  20.391 +     The <command> is an operating system dependent string.\n\n\
  20.392 +/retain start [<file>]\n\t\
  20.393 +     The contents of the specified <file> become the default start-up snippets and commands.\n\n\
  20.394 +/retain feedback [<mode>]\n\t\
  20.395 +     Set the feedback mode describing displayed feedback for entered snippets and commands.\n\n\
  20.396 +/retain mode <mode>\n\t\
  20.397 +     Create a user-defined feedback mode, optionally copying from an existing mode.\n\n\
  20.398 +To get more information about one of these forms, use /help with the form specified.\n\
  20.399 +For example:   /help /retain feedback
  20.400 +
  20.401 +help.quest.summary = get information about jshell
  20.402 +help.quest.args = [<command>|<subject>]
  20.403 +help.quest =\
  20.404 +Display information about jshell (abbreviation for /help).\n\
  20.405 +/?\n\t\
  20.406 +     Display list of commands and help subjects.\n\
  20.407 +/? <command>\n\t\
  20.408 +     Display information about the specified comand. The slash must be included.\n\t\
  20.409 +     Only the first few letters of the command are needed -- if more than one\n\t\
  20.410 +     match, each will be displayed.  Example:  /? /li\n\
  20.411 +/? <subject>\n\t\
  20.412 +     Display information about the specified help subject. Example: /? intro
  20.413 +
  20.414 +help.bang.summary = re-run last snippet
  20.415 +help.bang.args =
  20.416 +help.bang =\
  20.417 +Reevaluate the most recently entered snippet.
  20.418 +
  20.419 +help.id.summary = re-run snippet by id
  20.420 +help.id.args =
  20.421 +help.id =\
  20.422 +Reevaluate the snippet specified by the id.
  20.423 +
  20.424 +help.previous.summary = re-run n-th previous snippet
  20.425 +help.previous.args =
  20.426 +help.previous =\
  20.427 +Reevaluate the n-th most recently entered snippet.
  20.428 +
  20.429 +help.intro.summary = an introduction to the jshell tool
  20.430 +help.intro =\
  20.431 +The jshell tool allows you to execute Java code, getting immediate results.\n\
  20.432 +You can enter a Java definition (variable, method, class, etc), like:  int x = 8\n\
  20.433 +or a Java expression, like:  x + x\n\
  20.434 +or a Java statement or import.\n\
  20.435 +These little chunks of Java code are called 'snippets'.\n\
  20.436 +\n\
  20.437 +There are also jshell commands that allow you to understand and\n\
  20.438 +control what you are doing, like:  /list\n\
  20.439 +\n\
  20.440 +For a list of commands: /help
  20.441 +
  20.442 +help.shortcuts.summary = a description of shortcuts
  20.443 +help.shortcuts =\
  20.444 +Supported shortcuts include:\n\
  20.445 +\n\
  20.446 +<tab>\n\t\t\
  20.447 +        After entering the first few letters of a Java identifier,\n\t\t\
  20.448 +        a jshell command, or, in some cases, a jshell command argument,\n\t\t\
  20.449 +        press the <tab> key to complete the input.\n\t\t\
  20.450 +        If there is more than one completion, show possible completions.\n\n\
  20.451 +Shift-<tab>\n\t\t\
  20.452 +        After the name and open parenthesis of a method or constructor invocation,\n\t\t\
  20.453 +        hold the <shift> key and press the <tab> to see a synopsis of all\n\t\t\
  20.454 +        matching methods/constructors.\n\n\
  20.455 +<fix-shortcut> v\n\t\t\
  20.456 +        After a complete expression, press "<fix-shortcut> v" to introduce a new variable\n\t\t\
  20.457 +        whose type is based on the type of the expression.\n\t\t\
  20.458 +        The "<fix-shortcut>" is either Alt-F1 or Alt-Enter, depending on the platform.\n\n\
  20.459 +<fix-shortcut> i\n\t\t\
  20.460 +        After an unresolvable identifier, press "<fix-shortcut> i" and jshell will propose\n\t\t\
  20.461 +        possible fully qualified names based on the content of the specified classpath.\n\t\t\
  20.462 +        The "<fix-shortcut>" is either Alt-F1 or Alt-Enter, depending on the platform.
  20.463 +
  20.464 +help.set.format = \
  20.465 +Set the format for reporting a snippet event.\n\
  20.466 +\n\t\
  20.467 +/set format <mode> <field> "<format>" <selector>...\n\
  20.468 +\n\
  20.469 +Where <mode> is the name of a previously defined feedback mode -- see '/help /set mode'.\n\
  20.470 +Where <field> is the name of context-specific format to define.\n\
  20.471 +Where <format> is a quoted string which will be the value of the field if one of\n\
  20.472 +the selectors matches (or there are no selectors). When the format is used,\n\
  20.473 +field names enclosed in braces are replaced with the value of the field at that\n\
  20.474 +time. These fields may have been previously defined with this command or may be\n\
  20.475 +one of these predefined fields specific to the context:\n\t\
  20.476 +{name}       == The name, e.g.: the variable name, ...\n\t\
  20.477 +{type}       == The type name. The type of a variable or expression, the\n\t\t\t\
  20.478 +                 parameter types of a method\n\t\
  20.479 +{value}      == The result value of an expression or variable initialization\n\t\
  20.480 +{unresolved} == The list of unresolved references\n\t\
  20.481 +{errors}     == The list of recoverable errors (during the processing of the\n\t\t\t\
  20.482 +                "display" field only)\n\t\
  20.483 +{err}        == An unformatted error line (during the processing of the\n\t\t\t\
  20.484 +                "errorline" field only)\n\
  20.485 +The following fields are accessed by the tool to determine the displayed feedback:\n\t\
  20.486 +{display}    == The displayed message for a snippet event\n\t\
  20.487 +{errorline}  == The format of one error line within the "errors" field\n\t\
  20.488 +{pre}        == The feedback prefix (begins command feedback)\n\t\
  20.489 +{post}       == The feedback postfix (ends command feedback)\n\t\
  20.490 +{errorpre}   == The error prefix (begins error feedback)\n\t\
  20.491 +{errorpost}  == The error postfix (ends error feedback)\n\
  20.492 +These fields have default settings (which may be overwritten).\n\
  20.493 +Where <selector> is the context in which the format is applied.\n\
  20.494 +The structure of selector is a hyphen separated list of selector kind lists.\n\
  20.495 +A selector kind list is a comma separated list of values of one selector kind.\n\
  20.496 +A selector matches if each selector kind list matches; A selector kind list\n\
  20.497 +matches if one of the values matches.\n\n\
  20.498 +The case selector kind describes the kind of snippet.  The values are:\n\t\
  20.499 +   import     -- import declaration\n\t\
  20.500 +   class      -- class declaration\n\t\
  20.501 +   interface  -- interface declaration\n\t\
  20.502 +   enum       -- enum declaration\n\t\
  20.503 +   annotation -- annotation interface declaration\n\t\
  20.504 +   method     -- method declaration -- note: {type}==parameter-types\n\t\
  20.505 +   vardecl    -- variable declaration without init\n\t\
  20.506 +   varinit    -- variable declaration with init\n\t\
  20.507 +   expression -- expression -- note: {name}==scratch-variable-name\n\t\
  20.508 +   varvalue   -- variable value expression\n\t\
  20.509 +   assignment -- assign variable\n\t\
  20.510 +   statement  -- statement\n\
  20.511 +The action selector kind describes what happened to the snippet.  The values are:\n\t\
  20.512 +   added     -- snippet has been added\n\t\
  20.513 +   modified  -- an existing snippet has been modified\n\t\
  20.514 +   replaced  -- an existing snippet has been replaced with a new snippet\n\t\
  20.515 +   overwrote -- an existing snippet has been overwritten\n\t\
  20.516 +   dropped   -- snippet has been dropped\n\t\
  20.517 +   used      -- snippet was used when it cannot be\n\
  20.518 +The when-did-it-occur selector kind describes if this is a direct or indirect action.  The values are:\n\t\
  20.519 +   primary -- the entered snippet\n\t\
  20.520 +   update  -- an update to a dependent snippet\n\
  20.521 +The resolution-state selector kind describes the state of resolution/definition of the snippet.  The values are:\n\t\
  20.522 +   ok         -- resolved correctly\n\t\
  20.523 +   defined    -- defined despite recoverably unresolved references\n\t\
  20.524 +   notdefined -- not defined because of recoverably unresolved references\n\
  20.525 +The unresolved-count selector kind describes the number of unresolved references.  The values are:\n\t\
  20.526 +   unresolved0 -- no names are unresolved\n\t\
  20.527 +   unresolved1 -- one name is unresolved\n\t\
  20.528 +   unresolved2 -- two or more names are unresolved\n\
  20.529 +The errors-count selector kind describes the number of errors.  The values are:\n\t\
  20.530 +   error0 -- no errors\n\t\
  20.531 +   error1 -- one error\n\t\
  20.532 +   error2 -- two or more errors\n\n\
  20.533 +Examples:\n\t\
  20.534 +/set format myformat action 'Created' added-primary\n\t\
  20.535 +/set format myformat action 'Update replaced' replaced-update\n\t\
  20.536 +/set format myformat display '{pre}{action} class {name}{post}' class-ok\n\t\
  20.537 +/set format myformat display '{pre}{action} variable {name}, reset to null{post}' replaced-vardecl,varinit-ok-update\n\n\
  20.538 +Note that subsequent selectors for a field may overwrite some or all of previous used selectors -- last one wins\n
  20.539 +
  20.540 +help.set.truncation = \
  20.541 +Set the max length a displayed value.\n\
  20.542 +\n\t\
  20.543 +/set truncation <mode> <length> <selector>...\n\
  20.544 +\n\
  20.545 +Where <mode> is the name of a previously defined feedback mode -- see '/help /set mode'.\n\
  20.546 +Where <length> is an unsigned integer representing a maximum length.\n\
  20.547 +Where <format> is a quoted string which will be the value of the field if one of\n\
  20.548 +Where <selector> is only needed if you wish to fine-tune value truncation length\n\
  20.549 +by context, <selector> is the context in which the truncation is applied.\n\
  20.550 +The structure of selector is a hyphen separated list of selector kind lists.\n\
  20.551 +A selector kind list is a comma separated list of values of one selector kind.\n\
  20.552 +A selector matches if each selector kind list matches; A selector kind list\n\
  20.553 +matches if one of the values matches.\n\n\
  20.554 +Below are the relevant selector kinds for truncation.\n\n\
  20.555 +The case selector kind describes the kind of snippet.  The values are:\n\t\
  20.556 +   vardecl    -- variable declaration without init\n\t\
  20.557 +   varinit    -- variable declaration with init\n\t\
  20.558 +   expression -- expression -- note: {name}==scratch-variable-name\n\t\
  20.559 +   varvalue   -- variable value expression\n\t\
  20.560 +   assignment -- assign variable\n\t\
  20.561 +The action selector kind describes what happened to the snippet.  The values are:\n\t\
  20.562 +   added     -- snippet has been added\n\t\
  20.563 +   modified  -- an existing snippet has been modified\n\t\
  20.564 +   replaced  -- an existing snippet has been replaced with a new snippet\n\
  20.565 +Examples:\n\t\
  20.566 +/set trunc mymode 80\n\t\
  20.567 +/set truncation mymode 45 expression\n\t\
  20.568 +/set truncation mymode 0 vardecl-modified,replaced\n\n\
  20.569 +Note that subsequent selectors for a field may overwrite some or all of previous used selectors -- last one wins\n
  20.570 +
  20.571 +help.set.feedback = \
  20.572 +Set the feedback mode describing displayed feedback for entered snippets and commands.\n\
  20.573 +\n\t\
  20.574 +/set feedback <mode>\n\
  20.575 +\n\
  20.576 +Where <mode> is the name of a previously defined feedback mode.\n\
  20.577 +You may use just enough letters to make it unique.\n\
  20.578 +User-defined modes can be added, see '/help /set mode'\n\
  20.579 +Currently defined feedback modes:\n
  20.580 +
  20.581 +help.set.mode = \
  20.582 +Create a user-defined feedback mode, optionally copying from an existing mode.\n\
  20.583 +\n\t\
  20.584 +/set mode <mode> [<old-mode>] [-command|-quiet|-delete]\n\
  20.585 +\n\
  20.586 +Where <new-mode> is the name of a mode you wish to create.\n\
  20.587 +Where <old-mode> is the name of a previously defined feedback mode.\n\
  20.588 +If <old-mode> is present, its settings are copied to the new mode.\n\
  20.589 +'-command' vs '-quiet' determines if informative/verifying command feedback is displayed.\n\
  20.590 +\n\
  20.591 +Once the new mode is created, use '/set format' and '/set prompt' to configure it.\n\
  20.592 +Use '/set feedback' to use the new mode.\n\
  20.593 +
  20.594 +help.set.prompt = \
  20.595 +Set the prompts.  Both the normal prompt and the continuation-prompt must be set.\n\
  20.596 +\n\t\
  20.597 +/set prompt <mode> \"<prompt>\" \"<continuation-prompt>\"\n\
  20.598 +\n\
  20.599 +Where <mode> is the name of a previously defined feedback mode.\n\
  20.600 +Where <prompt> and <continuation-prompt> are quoted strings printed as input prompts;\n\
  20.601 +Both may optionally contain '%s' which will be substituted with the next snippet id --\n\
  20.602 +note that what is entered may not be assigned that id, for example it may be an error or command.\n\
  20.603 +The continuation-prompt is used on the second and subsequent lines of a multi-line snippet.\n
  20.604 +
  20.605 +help.set.editor =\
  20.606 +Specify the command to launch for the /edit command.\n\
  20.607 +\n\t\
  20.608 +/set editor <command>|-default\n\
  20.609 +\n\
  20.610 +The <command> is an operating system dependent string.\n\
  20.611 +The <command> may include space-separated arguments (such as flags)\n\
  20.612 +When /edit is used, the temporary file to edit will be appended as the last argument.\n\
  20.613 +If instead the -default option is specified, the built-in default editor will be used.
  20.614 +
  20.615 +help.set.start =\
  20.616 +Set the start-up configuration -- a sequence of snippets and commands read at start-up.\n\
  20.617 +\n\t\
  20.618 +/set start <file>|-default|-none\n\
  20.619 +\n\
  20.620 +The contents of the specified <file> become the start-up snippets and commands used\n\
  20.621 +when the /reset or /reload commands are used in this session.\n\
  20.622 +If instead the -default option is specified, the predefined start-up snippets\n\
  20.623 +will be used.\n\
  20.624 +If the -none option is used, the start-up will be empty -- no start-up snippets\n\
  20.625 +or commands will be used.\n\
  20.626 +This command is good for testing the start-up settings.  To retain them for future\n\
  20.627 +runs of the jshell tool use the command:\n\t\
  20.628 +/retain start\n
  20.629 +
  20.630 +help.retain.feedback = \
  20.631 +Retain which feedback mode to use for displayed feedback for entered snippets and commands.\n\
  20.632 +This feedback mode will be used in this and future sessions of the jshell tool.\n\
  20.633 +\n\t\
  20.634 +/retain feedback [<mode>]\n\
  20.635 +\n\
  20.636 +Where <mode> is the name of a previously defined feedback mode.\n\
  20.637 +You may use just enough letters to make it unique.\n\
  20.638 +If the <mode> is not specified, this command retains the current mode (as set\n\
  20.639 +with the most recent /set feedback or /retain feedback command.)\n\
  20.640 +
  20.641 +help.retain.mode = \
  20.642 +Retain the existence and configuration of a user-defined feedback mode.\n\
  20.643 +This mode will be available in this and future sessions of the jshell tool.
  20.644 +\n\t\
  20.645 +/retain mode <mode>\n\
  20.646 +\n\
  20.647 +Where <mode> is the name of a mode you wish to retain.\n\
  20.648 +The <mode> must previously have been created with /set mode and\n\
  20.649 +configured as desired with /set prompt, /set format, and /set truncation.\n
  20.650 +
  20.651 +help.retain.editor =\
  20.652 +Retain the command to launch for the /edit command.  This command will be invoked when\n\
  20.653 +the /edit command is used in this and future sessions of the jshell tool.\n\
  20.654 +\n\t\
  20.655 +/retain editor [<command>|-default]\n\
  20.656 +\n\
  20.657 +If <command> is specified, it is an operating system dependent string which\n\
  20.658 +may include space-separated arguments (such as flags). When /edit is used, the\n\
  20.659 +temporary file to edit will be appended as the last argument.\n\
  20.660 +If instead the -default option is specified, the built-in default editor will be used.\n\
  20.661 +If neither is specified, the editor set in the last /set editor or /retain editor\n\
  20.662 +command will be used.\n\
  20.663 +The editor will be retained and used in this and future runs of the jshell tool.
  20.664 +
  20.665 +help.retain.start =\
  20.666 +Retain the start-up configuration -- a sequence of snippets and commands read\n\
  20.667 +at start-up.\n\
  20.668 +\n\t\
  20.669 +/retain start [<file>|-default|-none]\n\
  20.670 +\n\
  20.671 +If <file> is specified, the contents of the specified <file> become the\n\
  20.672 +start-up snippets\n\
  20.673 +and commands.\n\
  20.674 +If instead the -default option is specified, the predefined start-up snippets\n\
  20.675 +will be the start-up.\n\
  20.676 +If the -none option is used, the start-up will be empty -- no start-up snippets\n\
  20.677 +or commands will be used.\n\
  20.678 +If none of these is specified, the start-up is the last specified in a\n\
  20.679 +''/set start'' or ''/retain start'' command.\n\
  20.680 +The start-up will be retained and used when the jshell tool is started or reset
  20.681 +
  20.682 +startup.feedback = \
  20.683 +/set mode verbose -command    \n\
  20.684 +\n\
  20.685 +/set prompt verbose '\\njshell> '   '   ...> '    \n\
  20.686 +\n\
  20.687 +/set format verbose pre '|  '    \n\
  20.688 +/set format verbose post '%n'    \n\
  20.689 +/set format verbose errorpre '|  '    \n\
  20.690 +/set format verbose errorpost '%n'    \n\
  20.691 +\n\
  20.692 +/set format verbose errorline '{post}{pre}    {err}'    \n\
  20.693 +\n\
  20.694 +/set format verbose action 'created' added-primary    \n\
  20.695 +/set format verbose action 'modified' modified-primary    \n\
  20.696 +/set format verbose action 'replaced' replaced-primary    \n\
  20.697 +/set format verbose action 'overwrote' overwrote-primary    \n\
  20.698 +/set format verbose action 'dropped' dropped-primary    \n\
  20.699 +/set format verbose action '  update created' added-update    \n\
  20.700 +/set format verbose action '  update modified' modified-update    \n\
  20.701 +/set format verbose action '  update replaced' replaced-update    \n\
  20.702 +/set format verbose action '  update overwrote' overwrote-update    \n\
  20.703 +/set format verbose action '  update dropped' dropped-update    \n\
  20.704 +\n\
  20.705 +/set format verbose until ', however, it cannot be instanciated or its methods invoked until'   defined-class-primary    \n\
  20.706 +/set format verbose until ', however, its methods cannot be invoked until'                      defined-interface-primary    \n\
  20.707 +/set format verbose until ', however, it cannot be used until'                                  defined-enum,annotation-primary    \n\
  20.708 +/set format verbose until ', however, it cannot be invoked until'                               defined-method-primary    \n\
  20.709 +/set format verbose until ', however, it cannot be referenced until'                            notdefined-primary    \n\
  20.710 +/set format verbose until ' which cannot be instanciated or its methods invoked until'          defined-class-update    \n\
  20.711 +/set format verbose until ' whose methods cannot be invoked until'                              defined-interface-update    \n\
  20.712 +/set format verbose until ' which cannot be invoked until'                                      defined-method-update    \n\
  20.713 +/set format verbose until ' which cannot be referenced until'                                   notdefined-update    \n\
  20.714 +\n\
  20.715 +/set format verbose unrerr '{unresolved} is declared'                                           unresolved1-error0    \n\
  20.716 +/set format verbose unrerr '{unresolved} are declared'                                          unresolved2-error0    \n\
  20.717 +/set format verbose unrerr ' this error is corrected: {errors}'                                 unresolved0-error1    \n\
  20.718 +/set format verbose unrerr '{unresolved} is declared and this error is corrected: {errors}'     unresolved1-error1    \n\
  20.719 +/set format verbose unrerr '{unresolved} are declared and this error is corrected: {errors}'    unresolved2-error1    \n\
  20.720 +/set format verbose unrerr ' these errors are corrected: {errors}'                              unresolved0-error2    \n\
  20.721 +/set format verbose unrerr '{unresolved} is declared and these errors are corrected: {errors}'  unresolved1-error2    \n\
  20.722 +/set format verbose unrerr '{unresolved} are declared and these errors are corrected: {errors}' unresolved2-error2    \n\
  20.723 +\n\
  20.724 +/set format verbose resolve '{until}{unrerr}'                                                   added,modified,replaced,used    \n\
  20.725 +\n\
  20.726 +/set format verbose typeKind 'class'                  class    \n\
  20.727 +/set format verbose typeKind 'interface'              interface    \n\
  20.728 +/set format verbose typeKind 'enum'                   enum    \n\
  20.729 +/set format verbose typeKind 'annotation interface'   annotation    \n\
  20.730 +\n\
  20.731 +/set format verbose result '{name} ==> {value}{post}'                                        added,modified,replaced-ok-primary    \n\
  20.732 +\n\
  20.733 +/set format verbose display '{result}{pre}created scratch variable {name} : {type}{post}'    expression-added,modified,replaced-primary    \n\
  20.734 +/set format verbose display '{result}{pre}value of {name} : {type}{post}'                    varvalue-primary    \n\
  20.735 +/set format verbose display '{result}{pre}assigned to {name} : {type}{post}'                 assignment-primary    \n\
  20.736 +/set format verbose display '{result}{pre}{action} variable {name} : {type}{resolve}{post}'  varinit,vardecl    \n\
  20.737 +/set format verbose display '{pre}{action} variable {name}{resolve}{post}'                   vardecl,varinit-notdefined    \n\
  20.738 +/set format verbose display '{pre}{action} variable {name}{post}'                            dropped-vardecl,varinit,expression    \n\
  20.739 +/set format verbose display '{pre}{action} variable {name}, reset to null{post}'             replaced-vardecl,varinit-ok-update    \n\
  20.740 +\n\
  20.741 +/set format verbose display '{pre}{action} {typeKind} {name}{resolve}{post}'                 class,interface,enum,annotation    \n\
  20.742 +/set format verbose display '{pre}{action} method {name}({type}){resolve}{post}'             method    \n\
  20.743 +\n\
  20.744 +/set format verbose display '{pre}attempted to use {typeKind} {name}{resolve}{post}'         used-class,interface,enum,annotation    \n\
  20.745 +/set format verbose display '{pre}attempted to call method {name}({type}){resolve}{post}'    used-method    \n\
  20.746 +\n\
  20.747 +/set truncation verbose 80\n\
  20.748 +/set truncation verbose 500                                                                  varvalue\n\
  20.749 +\n\
  20.750 +/set mode normal -command verbose    \n\
  20.751 +/set format normal display ''                                                               added,modified,replaced,overwrote,dropped-update    \n\
  20.752 +/set format normal display '{pre}{action} variable {name}, reset to null{post}'             replaced-vardecl,varinit-ok-update    \n\
  20.753 +/set format normal display '{result}'                                                       added,modified,replaced-expression,varvalue,assignment,varinit,vardecl-ok-primary    \n\
  20.754 +/set mode concise -quiet normal    \n\
  20.755 +\n\
  20.756 +/set prompt concise 'jshell> '   '   ...> '    \n\
  20.757 +\n\
  20.758 +/set format concise display ''                                                              class,interface,enum,annotation,method,assignment,varinit,vardecl-ok    \n\
  20.759 +\n\
  20.760 +/set feedback normal    \n\
  20.761 +\n\
  20.762 +/set mode silent -quiet    \n\
  20.763 +/set prompt silent '-> ' '>> '    \n\
  20.764 +/set format silent pre '|  '    \n\
  20.765 +/set format silent post '%n'    \n\
  20.766 +/set format silent errorpre '|  '    \n\
  20.767 +/set format silent errorpost '%n'    \n\
  20.768 +/set format silent display ''    \n
    21.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/ClassTracker.java	Fri Jun 03 17:06:38 2016 +0200
    21.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.3 @@ -1,97 +0,0 @@
    21.4 -/*
    21.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    21.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    21.7 - *
    21.8 - * This code is free software; you can redistribute it and/or modify it
    21.9 - * under the terms of the GNU General Public License version 2 only, as
   21.10 - * published by the Free Software Foundation.  Oracle designates this
   21.11 - * particular file as subject to the "Classpath" exception as provided
   21.12 - * by Oracle in the LICENSE file that accompanied this code.
   21.13 - *
   21.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   21.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   21.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   21.17 - * version 2 for more details (a copy is included in the LICENSE file that
   21.18 - * accompanied this code).
   21.19 - *
   21.20 - * You should have received a copy of the GNU General Public License version
   21.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   21.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   21.23 - *
   21.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   21.25 - * or visit www.oracle.com if you need additional information or have any
   21.26 - * questions.
   21.27 - */
   21.28 -
   21.29 -package jdk.jshell;
   21.30 -
   21.31 -import java.util.Arrays;
   21.32 -import java.util.HashMap;
   21.33 -import com.sun.jdi.ReferenceType;
   21.34 -
   21.35 -/**
   21.36 - * Tracks the state of a class through compilation and loading in the remote
   21.37 - * environment.
   21.38 - *
   21.39 - * @author Robert Field
   21.40 - */
   21.41 -class ClassTracker {
   21.42 -
   21.43 -    private final JShell state;
   21.44 -    private final HashMap<String, ClassInfo> map;
   21.45 -
   21.46 -    ClassTracker(JShell state) {
   21.47 -        this.state = state;
   21.48 -        this.map = new HashMap<>();
   21.49 -    }
   21.50 -
   21.51 -    class ClassInfo {
   21.52 -
   21.53 -        private final String className;
   21.54 -        private byte[] bytes;
   21.55 -        private byte[] loadedBytes;
   21.56 -        private Object rt;
   21.57 -
   21.58 -        private ClassInfo(String className) {
   21.59 -            this.className = className;
   21.60 -        }
   21.61 -
   21.62 -        String getClassName() {
   21.63 -            return className;
   21.64 -        }
   21.65 -
   21.66 -        byte[] getBytes() {
   21.67 -            return bytes;
   21.68 -        }
   21.69 -
   21.70 -        void setBytes(byte[] bytes) {
   21.71 -            this.bytes = bytes;
   21.72 -        }
   21.73 -
   21.74 -        void setLoaded() {
   21.75 -            loadedBytes = bytes;
   21.76 -        }
   21.77 -
   21.78 -        boolean isLoaded() {
   21.79 -            return Arrays.equals(loadedBytes, bytes);
   21.80 -        }
   21.81 -
   21.82 -        Object getReferenceTypeOrNull() {
   21.83 -            if (rt == null) {
   21.84 -                rt = state.executionControl().nameToRef(className);
   21.85 -            }
   21.86 -            return rt;
   21.87 -        }
   21.88 -    }
   21.89 -
   21.90 -    ClassInfo classInfo(String className, byte[] bytes) {
   21.91 -        ClassInfo ci = map.computeIfAbsent(className, k -> new ClassInfo(k));
   21.92 -        ci.setBytes(bytes);
   21.93 -        return ci;
   21.94 -    }
   21.95 -
   21.96 -    ClassInfo get(String className) {
   21.97 -        return map.get(className);
   21.98 -    }
   21.99 -
  21.100 -}
    22.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Fri Jun 03 17:06:38 2016 +0200
    22.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Fri Sep 02 23:54:26 2016 +0200
    22.3 @@ -1,5 +1,5 @@
    22.4  /*
    22.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    22.6 + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
    22.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    22.8   *
    22.9   * This code is free software; you can redistribute it and/or modify it
   22.10 @@ -231,7 +231,7 @@
   22.11  
   22.12          // Declarations and type parameters (thus expressions)
   22.13          EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL),  //  extends
   22.14 -        COMMA(TokenKind.COMMA, XEXPR|XDECL|XSTART),  //  ,
   22.15 +        COMMA(TokenKind.COMMA, XEXPR|XDECL),  //  ,
   22.16          AMP(TokenKind.AMP, XEXPR|XDECL),  //  &
   22.17          GT(TokenKind.GT, XEXPR|XDECL),  //  >
   22.18          LT(TokenKind.LT, XEXPR|XDECL1),  //  <
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/Corraller.java	Fri Sep 02 23:54:26 2016 +0200
    23.3 @@ -0,0 +1,157 @@
    23.4 +/*
    23.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    23.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    23.7 + *
    23.8 + * This code is free software; you can redistribute it and/or modify it
    23.9 + * under the terms of the GNU General Public License version 2 only, as
   23.10 + * published by the Free Software Foundation.  Oracle designates this
   23.11 + * particular file as subject to the "Classpath" exception as provided
   23.12 + * by Oracle in the LICENSE file that accompanied this code.
   23.13 + *
   23.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   23.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   23.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   23.17 + * version 2 for more details (a copy is included in the LICENSE file that
   23.18 + * accompanied this code).
   23.19 + *
   23.20 + * You should have received a copy of the GNU General Public License version
   23.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   23.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   23.23 + *
   23.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   23.25 + * or visit www.oracle.com if you need additional information or have any
   23.26 + * questions.
   23.27 + */
   23.28 +
   23.29 +package jdk.jshell;
   23.30 +
   23.31 +import java.io.IOException;
   23.32 +import java.io.StringWriter;
   23.33 +import com.sun.source.tree.ClassTree;
   23.34 +import com.sun.source.tree.MethodTree;
   23.35 +import com.sun.source.tree.Tree;
   23.36 +import com.sun.tools.javac.code.Flags;
   23.37 +import com.sun.tools.javac.tree.JCTree;
   23.38 +import com.sun.tools.javac.tree.JCTree.JCBlock;
   23.39 +import com.sun.tools.javac.tree.JCTree.JCClassDecl;
   23.40 +import com.sun.tools.javac.tree.JCTree.JCExpression;
   23.41 +import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
   23.42 +import com.sun.tools.javac.tree.JCTree.JCNewClass;
   23.43 +import com.sun.tools.javac.tree.JCTree.JCStatement;
   23.44 +import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
   23.45 +import com.sun.tools.javac.tree.Pretty;
   23.46 +import com.sun.tools.javac.tree.TreeMaker;
   23.47 +import com.sun.tools.javac.util.Context;
   23.48 +import com.sun.tools.javac.util.List;
   23.49 +import com.sun.tools.javac.util.ListBuffer;
   23.50 +import com.sun.tools.javac.util.Names;
   23.51 +import static com.sun.tools.javac.code.Flags.STATIC;
   23.52 +import static com.sun.tools.javac.code.Flags.INTERFACE;
   23.53 +import static com.sun.tools.javac.code.Flags.ENUM;
   23.54 +import static com.sun.tools.javac.code.Flags.PUBLIC;
   23.55 +import com.sun.tools.javac.util.Name;
   23.56 +import jdk.jshell.spi.SPIResolutionException;
   23.57 +
   23.58 +/**
   23.59 + * Produce a corralled version of the Wrap for a snippet.
   23.60 + * Incoming tree is mutated.
   23.61 + *
   23.62 + * @author Robert Field
   23.63 + */
   23.64 +class Corraller extends Pretty {
   23.65 +
   23.66 +    private final StringWriter out;
   23.67 +    private final int keyIndex;
   23.68 +    private final TreeMaker make;
   23.69 +    private final Names names;
   23.70 +    private JCBlock resolutionExceptionBlock;
   23.71 +
   23.72 +    public Corraller(int keyIndex, Context context) {
   23.73 +        this(new StringWriter(), keyIndex, context);
   23.74 +    }
   23.75 +
   23.76 +    private Corraller(StringWriter out, int keyIndex, Context context) {
   23.77 +        super(out, false);
   23.78 +        this.out = out;
   23.79 +        this.keyIndex = keyIndex;
   23.80 +        this.make = TreeMaker.instance(context);
   23.81 +        this.names = Names.instance(context);
   23.82 +    }
   23.83 +
   23.84 +    public Wrap corralType(ClassTree ct) {
   23.85 +        ((JCClassDecl) ct).mods.flags |= Flags.STATIC | Flags.PUBLIC;
   23.86 +        return corral(ct);
   23.87 +    }
   23.88 +
   23.89 +    public Wrap corralMethod(MethodTree mt) {
   23.90 +        ((JCMethodDecl) mt).mods.flags |= Flags.STATIC | Flags.PUBLIC;
   23.91 +        return corral(mt);
   23.92 +    }
   23.93 +
   23.94 +    private Wrap corral(Tree tree) {
   23.95 +        try {
   23.96 +            printStat((JCTree) tree);
   23.97 +        } catch (IOException e) {
   23.98 +            throw new AssertionError(e);
   23.99 +        }
  23.100 +        return Wrap.simpleWrap(out.toString());
  23.101 +    }
  23.102 +
  23.103 +    @Override
  23.104 +    public void visitBlock(JCBlock tree) {
  23.105 +        // Top-level executable blocks (usually method bodies) are corralled
  23.106 +        super.visitBlock((tree.flags & STATIC) != 0
  23.107 +                ? tree
  23.108 +                : resolutionExceptionBlock());
  23.109 +    }
  23.110 +
  23.111 +    @Override
  23.112 +    public void visitVarDef(JCVariableDecl tree) {
  23.113 +        // No field inits in corralled classes
  23.114 +        tree.init = null;
  23.115 +        super.visitVarDef(tree);
  23.116 +    }
  23.117 +
  23.118 +    @Override
  23.119 +    public void visitClassDef(JCClassDecl tree) {
  23.120 +        if ((tree.mods.flags & (INTERFACE | ENUM)) == 0 &&
  23.121 +                !tree.getMembers().stream()
  23.122 +                .anyMatch(t -> t.getKind() == Tree.Kind.METHOD &&
  23.123 +                ((MethodTree) t).getName() == tree.name.table.names.init)) {
  23.124 +            // Generate a default constructor, since
  23.125 +            // this is a regular class and there are no constructors
  23.126 +            ListBuffer<JCTree> ndefs = new ListBuffer<>();
  23.127 +            ndefs.addAll(tree.defs);
  23.128 +            ndefs.add(make.MethodDef(make.Modifiers(PUBLIC),
  23.129 +                    tree.name.table.names.init,
  23.130 +                    null, List.nil(), List.nil(), List.nil(),
  23.131 +                    resolutionExceptionBlock(), null));
  23.132 +            tree.defs = ndefs.toList();
  23.133 +        }
  23.134 +        super.visitClassDef(tree);
  23.135 +    }
  23.136 +
  23.137 +    // Build a compiler tree for an exception throwing block, e.g.:
  23.138 +    // {
  23.139 +    //     throw new jdk.jshell.spi.SPIResolutionException(9);
  23.140 +    // }
  23.141 +    private JCBlock resolutionExceptionBlock() {
  23.142 +        if (resolutionExceptionBlock == null) {
  23.143 +            JCExpression expClass = null;
  23.144 +            // Split the exception class name at dots
  23.145 +            for (String id : SPIResolutionException.class.getName().split("\\.")) {
  23.146 +                Name nm = names.fromString(id);
  23.147 +                if (expClass == null) {
  23.148 +                    expClass = make.Ident(nm);
  23.149 +                } else {
  23.150 +                    expClass = make.Select(expClass, nm);
  23.151 +                }
  23.152 +            }
  23.153 +            JCNewClass exp = make.NewClass(null,
  23.154 +                    null, expClass, List.of(make.Literal(keyIndex)), null);
  23.155 +            resolutionExceptionBlock = make.Block(0L, List.<JCStatement>of(
  23.156 +                    make.Throw(exp)));
  23.157 +        }
  23.158 +        return resolutionExceptionBlock;
  23.159 +    }
  23.160 +}
    24.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    24.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    24.3 @@ -22,6 +22,7 @@
    24.4   * or visit www.oracle.com if you need additional information or have any
    24.5   * questions.
    24.6   */
    24.7 +
    24.8  package jdk.jshell;
    24.9  
   24.10  import java.util.Collection;
   24.11 @@ -83,6 +84,6 @@
   24.12  
   24.13      @Override
   24.14      String importLine(JShell state) {
   24.15 -        return "import static " + state.maps.classFullName(this) + "." + name() + ";\n";
   24.16 +        return "import static " + classFullName() + "." + name() + ";\n";
   24.17      }
   24.18  }
    25.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/Diag.java	Fri Jun 03 17:06:38 2016 +0200
    25.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/Diag.java	Fri Sep 02 23:54:26 2016 +0200
    25.3 @@ -36,12 +36,18 @@
    25.4      // Simplified view on compiler Diagnostic.
    25.5  
    25.6      /**
    25.7 +     * In-package creation only.
    25.8 +     */
    25.9 +    Diag() {
   25.10 +    }
   25.11 +
   25.12 +    /**
   25.13       * Used to signal that no position is available.
   25.14       */
   25.15      public final static long NOPOS = Diagnostic.NOPOS;
   25.16  
   25.17      /**
   25.18 -     * Is this diagnostic and error (as opposed to a warning or note)
   25.19 +     * Is this diagnostic an error (as opposed to a warning or note)
   25.20       * @return true if this diagnostic is an error
   25.21       */
   25.22      public abstract boolean isError();
   25.23 @@ -55,8 +61,7 @@
   25.24       * <p>{@code getPosition() <= getEndPosition()}
   25.25       *
   25.26       * @return character offset from beginning of source; {@link
   25.27 -     * #NOPOS} if {@link #getSource()} would return {@code null} or if
   25.28 -     * no location is suitable
   25.29 +     * #NOPOS} if the position is not available.
   25.30       */
   25.31      public abstract long getPosition();
   25.32  
   25.33 @@ -101,10 +106,12 @@
   25.34      // *** Internal support ***
   25.35  
   25.36      /**
   25.37 -     * Internal: If this is from a compile, extract the compilation Unit.
   25.38 +     * Internal: If this is from a compile/analyze wrapped in an outer class, extract the snippet.
   25.39       * Otherwise null.
   25.40       */
   25.41 -    abstract Unit unitOrNull();
   25.42 +    Snippet snippetOrNull() {
   25.43 +        return null;
   25.44 +    }
   25.45  
   25.46      /**
   25.47       * This is an unreachable-statement error
   25.48 @@ -125,6 +132,7 @@
   25.49       */
   25.50      boolean isResolutionError() {
   25.51          //TODO: try javac RESOLVE_ERROR flag
   25.52 -        return getCode().startsWith("compiler.err.cant.resolve");
   25.53 +        return getCode().startsWith("compiler.err.cant.resolve")
   25.54 +                || getCode().equals("compiler.err.cant.apply.symbol");
   25.55      }
   25.56  }
    26.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/DiagList.java	Fri Jun 03 17:06:38 2016 +0200
    26.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/DiagList.java	Fri Sep 02 23:54:26 2016 +0200
    26.3 @@ -106,7 +106,10 @@
    26.4  
    26.5      DiagList ofUnit(Unit u) {
    26.6          return this.stream()
    26.7 -                .filter(d -> d.unitOrNull() == u)
    26.8 +                .filter(d -> {
    26.9 +                    Snippet snn = d.snippetOrNull();
   26.10 +                    return snn == u.snippet();
   26.11 +                })
   26.12                  .collect(Collectors.toCollection(() -> new DiagList()));
   26.13      }
   26.14  
    27.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    27.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    27.3 @@ -22,6 +22,7 @@
    27.4   * or visit www.oracle.com if you need additional information or have any
    27.5   * questions.
    27.6   */
    27.7 +
    27.8  package jdk.jshell;
    27.9  
   27.10  import jdk.jshell.Key.ErroneousKey;
    28.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Fri Jun 03 17:06:38 2016 +0200
    28.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Fri Sep 02 23:54:26 2016 +0200
    28.3 @@ -1,5 +1,5 @@
    28.4  /*
    28.5 - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
    28.6 + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
    28.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    28.8   *
    28.9   * This code is free software; you can redistribute it and/or modify it
   28.10 @@ -22,7 +22,6 @@
   28.11   * or visit www.oracle.com if you need additional information or have any
   28.12   * questions.
   28.13   */
   28.14 -
   28.15  package jdk.jshell;
   28.16  
   28.17  import java.util.ArrayList;
   28.18 @@ -50,9 +49,9 @@
   28.19  import java.io.Writer;
   28.20  import java.util.LinkedHashSet;
   28.21  import java.util.Set;
   28.22 -import jdk.jshell.ClassTracker.ClassInfo;
   28.23  import jdk.jshell.Key.ErroneousKey;
   28.24  import jdk.jshell.Key.MethodKey;
   28.25 +import jdk.jshell.Key.TypeDeclKey;
   28.26  import jdk.jshell.Snippet.SubKind;
   28.27  import jdk.jshell.TaskFactory.AnalyzeTask;
   28.28  import jdk.jshell.TaskFactory.BaseTask;
   28.29 @@ -64,9 +63,9 @@
   28.30  import static java.util.stream.Collectors.toList;
   28.31  import static java.util.stream.Collectors.toSet;
   28.32  import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
   28.33 -import static jdk.jshell.Util.*;
   28.34 -import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
   28.35 -import static jdk.internal.jshell.remote.RemoteCodes.prefixPattern;
   28.36 +import static jdk.jshell.Util.DOIT_METHOD_NAME;
   28.37 +import static jdk.jshell.Util.PREFIX_PATTERN;
   28.38 +import static jdk.jshell.Util.expunge;
   28.39  import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
   28.40  import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
   28.41  import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND;
   28.42 @@ -132,7 +131,7 @@
   28.43      }
   28.44  
   28.45      private List<SnippetEvent> processImport(String userSource, String compileSource) {
   28.46 -        Wrap guts = Wrap.importWrap(compileSource);
   28.47 +        Wrap guts = Wrap.simpleWrap(compileSource);
   28.48          Matcher mat = IMPORT_PATTERN.matcher(compileSource);
   28.49          String fullname;
   28.50          String name;
   28.51 @@ -190,7 +189,7 @@
   28.52  
   28.53      private List<SnippetEvent> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
   28.54          List<SnippetEvent> allEvents = new ArrayList<>();
   28.55 -        TreeDissector dis = new TreeDissector(pt);
   28.56 +        TreeDissector dis = TreeDissector.createByFirstClass(pt);
   28.57          for (Tree unitTree : units) {
   28.58              VariableTree vt = (VariableTree) unitTree;
   28.59              String name = vt.getName().toString();
   28.60 @@ -206,10 +205,7 @@
   28.61              }
   28.62              Range rtype = dis.treeToRange(baseType);
   28.63              Range runit = dis.treeToRange(vt);
   28.64 -            // in an incomplete source, the end may be positioned after the whole source.
   28.65 -            if (runit.end < compileSource.length()) {
   28.66 -                runit = new Range(runit.begin, runit.end - 1);
   28.67 -            }
   28.68 +            runit = new Range(runit.begin, runit.end - 1);
   28.69              ExpressionTree it = vt.getInitializer();
   28.70              Range rinit = null;
   28.71              int nameMax = runit.end - 1;
   28.72 @@ -298,16 +294,19 @@
   28.73          TreeDependencyScanner tds = new TreeDependencyScanner();
   28.74          tds.scan(unitTree);
   28.75  
   28.76 -        TreeDissector dis = new TreeDissector(pt);
   28.77 +        TreeDissector dis = TreeDissector.createByFirstClass(pt);
   28.78  
   28.79          ClassTree klassTree = (ClassTree) unitTree;
   28.80          String name = klassTree.getSimpleName().toString();
   28.81 +        DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
   28.82 +        TypeDeclKey key = state.keyMap.keyForClass(name);
   28.83 +        // Corralling mutates.  Must be last use of pt, unitTree, klassTree
   28.84 +        Wrap corralled = new Corraller(key.index(), pt.getContext()).corralType(klassTree);
   28.85 +
   28.86          Wrap guts = Wrap.classMemberWrap(compileSource);
   28.87 -        Wrap corralled = null; //TODO
   28.88 -        Snippet snip = new TypeDeclSnippet(state.keyMap.keyForClass(name), userSource, guts,
   28.89 +        Snippet snip = new TypeDeclSnippet(key, userSource, guts,
   28.90                  name, snippetKind,
   28.91                  corralled, tds.declareReferences(), tds.bodyReferences());
   28.92 -        DiagList modDiag = modifierDiagnostics(klassTree.getModifiers(), dis, false);
   28.93          return declare(snip, modDiag);
   28.94      }
   28.95  
   28.96 @@ -337,54 +336,38 @@
   28.97          return declare(snip);
   28.98      }
   28.99  
  28.100 -    private OuterWrap wrapInClass(String className, Set<Key> except, String userSource, Wrap guts, Collection<Snippet> plus) {
  28.101 -        String imports = state.maps.packageAndImportsExcept(except, plus);
  28.102 -        return OuterWrap.wrapInClass(state.maps.packageName(), className, imports, userSource, guts);
  28.103 -    }
  28.104 -
  28.105 -    OuterWrap wrapInClass(Snippet snip, Set<Key> except, Wrap guts, Collection<Snippet> plus) {
  28.106 -        return wrapInClass(snip.className(), except, snip.source(), guts, plus);
  28.107 -    }
  28.108 -
  28.109      private AnalyzeTask trialCompile(Wrap guts) {
  28.110 -        OuterWrap outer = wrapInClass(REPL_DOESNOTMATTER_CLASS_NAME,
  28.111 -                Collections.emptySet(), "", guts, null);
  28.112 +        OuterWrap outer = state.outerMap.wrapInTrialClass(guts);
  28.113          return state.taskFactory.new AnalyzeTask(outer);
  28.114      }
  28.115  
  28.116      private List<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
  28.117          TreeDependencyScanner tds = new TreeDependencyScanner();
  28.118          tds.scan(unitTree);
  28.119 +        TreeDissector dis = TreeDissector.createByFirstClass(pt);
  28.120  
  28.121          MethodTree mt = (MethodTree) unitTree;
  28.122 -        TreeDissector dis = new TreeDissector(pt);
  28.123 -        DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
  28.124 -        if (modDiag.hasErrors()) {
  28.125 -            return compileFailResult(modDiag, userSource);
  28.126 -        }
  28.127 -        String unitName = mt.getName().toString();
  28.128 -        Wrap guts = Wrap.classMemberWrap(compileSource);
  28.129 -
  28.130 -        Range modRange = dis.treeToRange(mt.getModifiers());
  28.131 -        Range tpRange = dis.treeListToRange(mt.getTypeParameters());
  28.132 -        Range typeRange = dis.treeToRange(mt.getReturnType());
  28.133          String name = mt.getName().toString();
  28.134 -        Range paramRange = dis.treeListToRange(mt.getParameters());
  28.135 -        Range throwsRange = dis.treeListToRange(mt.getThrows());
  28.136 -
  28.137          String parameterTypes
  28.138                  = mt.getParameters()
  28.139                  .stream()
  28.140                  .map(param -> dis.treeToRange(param.getType()).part(compileSource))
  28.141                  .collect(Collectors.joining(","));
  28.142 +        Tree returnType = mt.getReturnType();
  28.143 +        DiagList modDiag = modifierDiagnostics(mt.getModifiers(), dis, true);
  28.144 +        MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
  28.145 +        // Corralling mutates.  Must be last use of pt, unitTree, mt
  28.146 +        Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt);
  28.147 +
  28.148 +        if (modDiag.hasErrors()) {
  28.149 +            return compileFailResult(modDiag, userSource);
  28.150 +        }
  28.151 +        Wrap guts = Wrap.classMemberWrap(compileSource);
  28.152 +        Range typeRange = dis.treeToRange(returnType);
  28.153          String signature = "(" + parameterTypes + ")" + typeRange.part(compileSource);
  28.154  
  28.155 -        MethodKey key = state.keyMap.keyForMethod(name, parameterTypes);
  28.156 -        // rewrap with correct Key index
  28.157 -        Wrap corralled = Wrap.corralledMethod(compileSource,
  28.158 -                modRange, tpRange, typeRange, name, paramRange, throwsRange, key.index());
  28.159          Snippet snip = new MethodSnippet(key, userSource, guts,
  28.160 -                unitName, signature,
  28.161 +                name, signature,
  28.162                  corralled, tds.declareReferences(), tds.bodyReferences());
  28.163          return declare(snip, modDiag);
  28.164      }
  28.165 @@ -421,9 +404,9 @@
  28.166      private ExpressionInfo typeOfExpression(String expression) {
  28.167          Wrap guts = Wrap.methodReturnWrap(expression);
  28.168          TaskFactory.AnalyzeTask at = trialCompile(guts);
  28.169 -        if (!at.hasErrors() && at.cuTree() != null) {
  28.170 -            return new TreeDissector(at)
  28.171 -                    .typeOfReturnStatement(at.messages(), state.maps::fullClassNameAndPackageToClass);
  28.172 +        if (!at.hasErrors() && at.firstCuTree() != null) {
  28.173 +            return TreeDissector.createByFirstClass(at)
  28.174 +                    .typeOfReturnStatement(at, state);
  28.175          }
  28.176          return null;
  28.177      }
  28.178 @@ -453,13 +436,6 @@
  28.179  
  28.180      private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) {
  28.181          Unit c = new Unit(state, si, null, generatedDiagnostics);
  28.182 -
  28.183 -        // Ignores duplicates
  28.184 -        //TODO: remove, modify, or move to edit
  28.185 -        if (c.isRedundant()) {
  28.186 -            return Collections.emptyList();
  28.187 -        }
  28.188 -
  28.189          Set<Unit> ins = new LinkedHashSet<>();
  28.190          ins.add(c);
  28.191          Set<Unit> outs = compileAndLoad(ins);
  28.192 @@ -475,54 +451,100 @@
  28.193  
  28.194          // If appropriate, execute the snippet
  28.195          String value = null;
  28.196 -        Exception exception = null;
  28.197 -        if (si.isExecutable() && si.status().isDefined) {
  28.198 -            try {
  28.199 -                value = state.executionControl().commandInvoke(state.maps.classFullName(si));
  28.200 -                value = si.subKind().hasValue()
  28.201 -                        ? expunge(value)
  28.202 -                        : "";
  28.203 -            } catch (EvalException ex) {
  28.204 -                exception = translateExecutionException(ex);
  28.205 -            } catch (UnresolvedReferenceException ex) {
  28.206 -                exception = ex;
  28.207 +        JShellException exception = null;
  28.208 +        if (si.status().isDefined) {
  28.209 +            if (si.isExecutable()) {
  28.210 +                try {
  28.211 +                value = state.executionControl().invoke(si.classFullName(), DOIT_METHOD_NAME);
  28.212 +                    value = si.subKind().hasValue()
  28.213 +                            ? expunge(value)
  28.214 +                            : "";
  28.215 +                } catch (EvalException ex) {
  28.216 +                    exception = translateExecutionException(ex);
  28.217 +                } catch (JShellException ex) {
  28.218 +                    // UnresolvedReferenceException
  28.219 +                    exception = ex;
  28.220 +                }
  28.221 +            } else if (si.subKind() == SubKind.VAR_DECLARATION_SUBKIND) {
  28.222 +                switch (((VarSnippet) si).typeName()) {
  28.223 +                    case "byte":
  28.224 +                    case "short":
  28.225 +                    case "int":
  28.226 +                    case "long":
  28.227 +                        value = "0";
  28.228 +                        break;
  28.229 +                    case "float":
  28.230 +                    case "double":
  28.231 +                        value = "0.0";
  28.232 +                        break;
  28.233 +                    case "boolean":
  28.234 +                        value = "false";
  28.235 +                        break;
  28.236 +                    case "char":
  28.237 +                        value = "''";
  28.238 +                        break;
  28.239 +                    default:
  28.240 +                        value = "null";
  28.241 +                        break;
  28.242 +                }
  28.243              }
  28.244          }
  28.245          return events(c, outs, value, exception);
  28.246      }
  28.247  
  28.248 -    private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, Exception exception) {
  28.249 +    private boolean interestingEvent(SnippetEvent e) {
  28.250 +        return e.isSignatureChange()
  28.251 +                    || e.causeSnippet() == null
  28.252 +                    || e.status() != e.previousStatus()
  28.253 +                    || e.exception() != null;
  28.254 +    }
  28.255 +
  28.256 +    private List<SnippetEvent> events(Unit c, Collection<Unit> outs, String value, JShellException exception) {
  28.257          List<SnippetEvent> events = new ArrayList<>();
  28.258          events.add(c.event(value, exception));
  28.259          events.addAll(outs.stream()
  28.260                  .filter(u -> u != c)
  28.261                  .map(u -> u.event(null, null))
  28.262 +                .filter(this::interestingEvent)
  28.263                  .collect(Collectors.toList()));
  28.264          events.addAll(outs.stream()
  28.265                  .flatMap(u -> u.secondaryEvents().stream())
  28.266 +                .filter(this::interestingEvent)
  28.267                  .collect(Collectors.toList()));
  28.268          //System.err.printf("Events: %s\n", events);
  28.269          return events;
  28.270      }
  28.271 -    
  28.272 +
  28.273 +    private Set<OuterWrap> outerWrapSet(Collection<Unit> units) {
  28.274 +        return units.stream()
  28.275 +                .map(u -> u.snippet().outerWrap())
  28.276 +                .collect(toSet());
  28.277 +    }
  28.278 +
  28.279      private Set<Unit> compileAndLoad(Set<Unit> ins) {
  28.280          if (ins.isEmpty()) {
  28.281              return ins;
  28.282          }
  28.283          Set<Unit> replaced = new LinkedHashSet<>();
  28.284 +        // Loop until dependencies and errors are stable
  28.285          while (true) {
  28.286              state.debug(DBG_GEN, "compileAndLoad  %s\n", ins);
  28.287  
  28.288 -            ins.stream().forEach(u -> u.initialize(ins));
  28.289 -            AnalyzeTask at = state.taskFactory.new AnalyzeTask(ins);
  28.290 +            ins.stream().forEach(u -> u.initialize());
  28.291 +            ins.stream().forEach(u -> u.setWrap(ins, ins));
  28.292 +            AnalyzeTask at = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
  28.293              ins.stream().forEach(u -> u.setDiagnostics(at));
  28.294 +
  28.295              // corral any Snippets that need it
  28.296 -            if (ins.stream().filter(u -> u.corralIfNeeded(ins)).count() > 0) {
  28.297 +            AnalyzeTask cat;
  28.298 +            if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
  28.299                  // if any were corralled, re-analyze everything
  28.300 -                AnalyzeTask cat = state.taskFactory.new AnalyzeTask(ins);
  28.301 +                cat = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
  28.302                  ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
  28.303 +            } else {
  28.304 +                cat = at;
  28.305              }
  28.306 -            ins.stream().forEach(u -> u.setStatus());
  28.307 +            ins.stream().forEach(u -> u.setStatus(cat));
  28.308              // compile and load the legit snippets
  28.309              boolean success;
  28.310              while (true) {
  28.311 @@ -539,7 +561,7 @@
  28.312                      legit.stream().forEach(u -> u.setWrap(ins, legit));
  28.313  
  28.314                      // generate class files for those capable
  28.315 -                    CompileTask ct = state.taskFactory.new CompileTask(legit, state.getCompilerOptions());
  28.316 +                    CompileTask ct = state.taskFactory.new CompileTask(outerWrapSet(legit));
  28.317                      if (!ct.compile()) {
  28.318                          // oy! compile failed because of recursive new unresolved
  28.319                          if (legit.stream()
  28.320 @@ -555,8 +577,8 @@
  28.321  
  28.322                      // load all new classes
  28.323                      load(legit.stream()
  28.324 -                            .flatMap(u -> u.classesToLoad(ct.classInfoList(u)))
  28.325 -                            .collect(toList()));
  28.326 +                            .flatMap(u -> u.classesToLoad(ct.classList(u.snippet().outerWrap())))
  28.327 +                            .collect(toSet()));
  28.328                      // attempt to redefine the remaining classes
  28.329                      List<Unit> toReplace = legit.stream()
  28.330                              .filter(u -> !u.doRedefines())
  28.331 @@ -590,9 +612,13 @@
  28.332          }
  28.333      }
  28.334  
  28.335 -    private void load(List<ClassInfo> cil) {
  28.336 -        if (!cil.isEmpty()) {
  28.337 -            state.executionControl().commandLoad(cil);
  28.338 +    /**
  28.339 +     * If there are classes to load, loads by calling the execution engine.
  28.340 +     * @param classnames names of the classes to load.
  28.341 +     */
  28.342 +    private void load(Collection<String> classnames) {
  28.343 +        if (!classnames.isEmpty()) {
  28.344 +            state.executionControl().load(classnames);
  28.345          }
  28.346      }
  28.347  
  28.348 @@ -608,20 +634,14 @@
  28.349          StackTraceElement[] elems = new StackTraceElement[last + 1];
  28.350          for (int i = 0; i <= last; ++i) {
  28.351              StackTraceElement r = raw[i];
  28.352 -            String rawKlass = r.getClassName();
  28.353 -            Matcher matcher = prefixPattern.matcher(rawKlass);
  28.354 -            String num;
  28.355 -            if (matcher.find() && (num = matcher.group("num")) != null) {
  28.356 -                int end = matcher.end();
  28.357 -                if (rawKlass.charAt(end - 1) == '$') {
  28.358 -                    --end;
  28.359 -                }
  28.360 -                int id = Integer.parseInt(num);
  28.361 -                Snippet si = state.maps.getSnippet(id);
  28.362 -                String klass = expunge(rawKlass);
  28.363 +            OuterSnippetsClassWrap outer = state.outerMap.getOuter(r.getClassName());
  28.364 +            if (outer != null) {
  28.365 +                String klass = expunge(r.getClassName());
  28.366                  String method = r.getMethodName().equals(DOIT_METHOD_NAME) ? "" : r.getMethodName();
  28.367 -                String file = "#" + id;
  28.368 -                int line = si.outerWrap().wrapLineToSnippetLine(r.getLineNumber() - 1) + 1;
  28.369 +                int wln = r.getLineNumber() - 1;
  28.370 +                int line = outer.wrapLineToSnippetLine(wln) + 1;
  28.371 +                Snippet sn = outer.wrapLineToSnippet(wln);
  28.372 +                String file = "#" + sn.id();
  28.373                  elems[i] = new StackTraceElement(klass, method, file, line);
  28.374              } else if (r.getFileName().equals("<none>")) {
  28.375                  elems[i] = new StackTraceElement(r.getClassName(), r.getMethodName(), null, r.getLineNumber());
  28.376 @@ -637,7 +657,7 @@
  28.377      }
  28.378  
  28.379      private boolean isWrap(StackTraceElement ste) {
  28.380 -        return prefixPattern.matcher(ste.getClassName()).find();
  28.381 +        return PREFIX_PATTERN.matcher(ste.getClassName()).find();
  28.382      }
  28.383  
  28.384      private DiagList modifierDiagnostics(ModifiersTree modtree,
  28.385 @@ -651,17 +671,19 @@
  28.386              ModifierDiagnostic(List<Modifier> list, boolean fatal) {
  28.387                  this.fatal = fatal;
  28.388                  StringBuilder sb = new StringBuilder();
  28.389 -                sb.append((list.size() > 1) ? "Modifiers " : "Modifier ");
  28.390                  for (Modifier mod : list) {
  28.391                      sb.append("'");
  28.392                      sb.append(mod.toString());
  28.393                      sb.append("' ");
  28.394                  }
  28.395 -                sb.append("not permitted in top-level declarations");
  28.396 -                if (!fatal) {
  28.397 -                    sb.append(", ignored");
  28.398 -                }
  28.399 -                this.message = sb.toString();
  28.400 +                String key = (list.size() > 1)
  28.401 +                        ? fatal
  28.402 +                            ? "jshell.diag.modifier.plural.fatal"
  28.403 +                            : "jshell.diag.modifier.plural.ignore"
  28.404 +                        : fatal
  28.405 +                            ? "jshell.diag.modifier.single.fatal"
  28.406 +                            : "jshell.diag.modifier.single.ignore";
  28.407 +                this.message = state.messageFormat(key, sb.toString());
  28.408              }
  28.409  
  28.410              @Override
  28.411 @@ -695,11 +717,6 @@
  28.412              public String getMessage(Locale locale) {
  28.413                  return message;
  28.414              }
  28.415 -
  28.416 -            @Override
  28.417 -            Unit unitOrNull() {
  28.418 -                return null;
  28.419 -            }
  28.420          }
  28.421  
  28.422          List<Modifier> list = new ArrayList<>();
    29.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/EvalException.java	Fri Jun 03 17:06:38 2016 +0200
    29.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/EvalException.java	Fri Sep 02 23:54:26 2016 +0200
    29.3 @@ -40,7 +40,7 @@
    29.4   * empty string.
    29.5   */
    29.6  @SuppressWarnings("serial")             // serialVersionUID intentionally omitted
    29.7 -public class EvalException extends Exception {
    29.8 +public class EvalException extends JShellException {
    29.9      private final String exceptionClass;
   29.10  
   29.11      EvalException(String message, String exceptionClass, StackTraceElement[] stackElements) {
    30.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/ExecutionControl.java	Fri Jun 03 17:06:38 2016 +0200
    30.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.3 @@ -1,318 +0,0 @@
    30.4 -/*
    30.5 - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
    30.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    30.7 - *
    30.8 - * This code is free software; you can redistribute it and/or modify it
    30.9 - * under the terms of the GNU General Public License version 2 only, as
   30.10 - * published by the Free Software Foundation.  Oracle designates this
   30.11 - * particular file as subject to the "Classpath" exception as provided
   30.12 - * by Oracle in the LICENSE file that accompanied this code.
   30.13 - *
   30.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   30.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   30.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   30.17 - * version 2 for more details (a copy is included in the LICENSE file that
   30.18 - * accompanied this code).
   30.19 - *
   30.20 - * You should have received a copy of the GNU General Public License version
   30.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   30.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   30.23 - *
   30.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   30.25 - * or visit www.oracle.com if you need additional information or have any
   30.26 - * questions.
   30.27 - */
   30.28 -
   30.29 -package jdk.jshell;
   30.30 -
   30.31 -import static jdk.internal.jshell.remote.RemoteCodes.*;
   30.32 -import java.io.IOException;
   30.33 -import java.io.ObjectInputStream;
   30.34 -import java.io.ObjectOutputStream;
   30.35 -import java.net.ServerSocket;
   30.36 -import java.net.Socket;
   30.37 -import com.sun.jdi.*;
   30.38 -import java.io.EOFException;
   30.39 -import java.util.List;
   30.40 -import java.util.Map;
   30.41 -import jdk.jshell.ClassTracker.ClassInfo;
   30.42 -import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
   30.43 -
   30.44 -/**
   30.45 - * Controls the remote execution environment.
   30.46 - *
   30.47 - * @author Robert Field
   30.48 - */
   30.49 -class ExecutionControl {
   30.50 -
   30.51 -    private final JDIEnv env;
   30.52 -    private final SnippetMaps maps;
   30.53 -    private JDIEventHandler handler;
   30.54 -    private Socket socket;
   30.55 -    protected ObjectInputStream in;
   30.56 -    protected ObjectOutputStream out;
   30.57 -    private final JShell proc;
   30.58 -
   30.59 -    ExecutionControl(JDIEnv env, SnippetMaps maps, JShell proc) {
   30.60 -        this.env = env;
   30.61 -        this.maps = maps;
   30.62 -        this.proc = proc;
   30.63 -    }
   30.64 -
   30.65 -    void launch() throws IOException {
   30.66 -        try (ServerSocket listener = new ServerSocket(0)) {
   30.67 -            // timeout after 60 seconds
   30.68 -            listener.setSoTimeout(60000);
   30.69 -            int port = listener.getLocalPort();
   30.70 -            jdiGo(port);
   30.71 -            socket = listener.accept();
   30.72 -            // out before in -- match remote creation so we don't hang
   30.73 -            out = new ObjectOutputStream(socket.getOutputStream());
   30.74 -            in = new ObjectInputStream(socket.getInputStream());
   30.75 -        }
   30.76 -    }
   30.77 -
   30.78 -    void commandExit() {
   30.79 -        try {
   30.80 -            if (out != null) {
   30.81 -                out.writeInt(CMD_EXIT);
   30.82 -                out.flush();
   30.83 -            }
   30.84 -            JDIConnection c = env.connection();
   30.85 -            if (c != null) {
   30.86 -                c.disposeVM();
   30.87 -            }
   30.88 -        } catch (IOException ex) {
   30.89 -            proc.debug(DBG_GEN, "Exception on JDI exit: %s\n", ex);
   30.90 -        }
   30.91 -    }
   30.92 -
   30.93 -
   30.94 -    boolean commandLoad(List<ClassInfo> cil) {
   30.95 -        try {
   30.96 -            out.writeInt(CMD_LOAD);
   30.97 -            out.writeInt(cil.size());
   30.98 -            for (ClassInfo ci : cil) {
   30.99 -                out.writeUTF(ci.getClassName());
  30.100 -                out.writeObject(ci.getBytes());
  30.101 -            }
  30.102 -            out.flush();
  30.103 -            return readAndReportResult();
  30.104 -        } catch (IOException ex) {
  30.105 -            proc.debug(DBG_GEN, "IOException on remote load operation: %s\n", ex);
  30.106 -            return false;
  30.107 -        }
  30.108 -    }
  30.109 -
  30.110 -    String commandInvoke(String classname) throws EvalException, UnresolvedReferenceException {
  30.111 -        try {
  30.112 -            synchronized (STOP_LOCK) {
  30.113 -                userCodeRunning = true;
  30.114 -            }
  30.115 -            out.writeInt(CMD_INVOKE);
  30.116 -            out.writeUTF(classname);
  30.117 -            out.flush();
  30.118 -            if (readAndReportExecutionResult()) {
  30.119 -                String result = in.readUTF();
  30.120 -                return result;
  30.121 -            }
  30.122 -        } catch (EOFException ex) {
  30.123 -            env.shutdown();
  30.124 -        } catch (IOException | ClassNotFoundException ex) {
  30.125 -            proc.debug(DBG_GEN, "Exception on remote invoke: %s\n", ex);
  30.126 -            return "Execution failure: " + ex.getMessage();
  30.127 -        } finally {
  30.128 -            synchronized (STOP_LOCK) {
  30.129 -                userCodeRunning = false;
  30.130 -            }
  30.131 -        }
  30.132 -        return "";
  30.133 -    }
  30.134 -
  30.135 -    String commandVarValue(String classname, String varname) {
  30.136 -        try {
  30.137 -            out.writeInt(CMD_VARVALUE);
  30.138 -            out.writeUTF(classname);
  30.139 -            out.writeUTF(varname);
  30.140 -            out.flush();
  30.141 -            if (readAndReportResult()) {
  30.142 -                String result = in.readUTF();
  30.143 -                return result;
  30.144 -            }
  30.145 -        } catch (EOFException ex) {
  30.146 -            env.shutdown();
  30.147 -        } catch (IOException ex) {
  30.148 -            proc.debug(DBG_GEN, "Exception on remote var value: %s\n", ex);
  30.149 -            return "Execution failure: " + ex.getMessage();
  30.150 -        }
  30.151 -        return "";
  30.152 -    }
  30.153 -
  30.154 -    boolean commandAddToClasspath(String cp) {
  30.155 -        try {
  30.156 -            out.writeInt(CMD_CLASSPATH);
  30.157 -            out.writeUTF(cp);
  30.158 -            out.flush();
  30.159 -            return readAndReportResult();
  30.160 -        } catch (IOException ex) {
  30.161 -            throw new InternalError("Classpath addition failed: " + cp, ex);
  30.162 -        }
  30.163 -    }
  30.164 -
  30.165 -    boolean commandRedefine(Map<Object, byte[]> mp) {
  30.166 -        try {
  30.167 -            env.vm().redefineClasses((Map<ReferenceType, byte[]>)(Map)mp);
  30.168 -            return true;
  30.169 -        } catch (UnsupportedOperationException ex) {
  30.170 -            return false;
  30.171 -        } catch (Exception ex) {
  30.172 -            proc.debug(DBG_GEN, "Exception on JDI redefine: %s\n", ex);
  30.173 -            return false;
  30.174 -        }
  30.175 -    }
  30.176 -
  30.177 -    Object nameToRef(String name) {
  30.178 -        List<ReferenceType> rtl = env.vm().classesByName(name);
  30.179 -        if (rtl.size() != 1) {
  30.180 -            return null;
  30.181 -        }
  30.182 -        return rtl.get(0);
  30.183 -    }
  30.184 -
  30.185 -    protected boolean readAndReportResult() throws IOException {
  30.186 -        int ok = in.readInt();
  30.187 -        switch (ok) {
  30.188 -            case RESULT_SUCCESS:
  30.189 -                return true;
  30.190 -            case RESULT_FAIL: {
  30.191 -                String ex = in.readUTF();
  30.192 -                proc.debug(DBG_GEN, "Exception on remote operation: %s\n", ex);
  30.193 -                return false;
  30.194 -            }
  30.195 -            default: {
  30.196 -                proc.debug(DBG_GEN, "Bad remote result code: %s\n", ok);
  30.197 -                return false;
  30.198 -            }
  30.199 -        }
  30.200 -    }
  30.201 -
  30.202 -    private boolean readAndReportExecutionResult() throws IOException, ClassNotFoundException, EvalException, UnresolvedReferenceException {
  30.203 -        int ok = in.readInt();
  30.204 -        switch (ok) {
  30.205 -            case RESULT_SUCCESS:
  30.206 -                return true;
  30.207 -            case RESULT_FAIL: {
  30.208 -                String ex = in.readUTF();
  30.209 -                proc.debug(DBG_GEN, "Exception on remote operation: %s\n", ex);
  30.210 -                return false;
  30.211 -            }
  30.212 -            case RESULT_EXCEPTION: {
  30.213 -                String exceptionClassName = in.readUTF();
  30.214 -                String message = in.readUTF();
  30.215 -                StackTraceElement[] elems = readStackTrace();
  30.216 -                EvalException ee = new EvalException(message, exceptionClassName, elems);
  30.217 -                throw ee;
  30.218 -            }
  30.219 -            case RESULT_CORRALLED: {
  30.220 -                int id = in.readInt();
  30.221 -                StackTraceElement[] elems = readStackTrace();
  30.222 -                Snippet si = maps.getSnippet(id);
  30.223 -                throw new UnresolvedReferenceException((MethodSnippet) si, elems);
  30.224 -            }
  30.225 -            case RESULT_KILLED: {
  30.226 -                proc.out.println("Killed.");
  30.227 -                return false;
  30.228 -            }
  30.229 -            default: {
  30.230 -                proc.debug(DBG_GEN, "Bad remote result code: %s\n", ok);
  30.231 -                return false;
  30.232 -            }
  30.233 -        }
  30.234 -    }
  30.235 -
  30.236 -    private StackTraceElement[] readStackTrace() throws IOException {
  30.237 -        int elemCount = in.readInt();
  30.238 -        StackTraceElement[] elems = new StackTraceElement[elemCount];
  30.239 -        for (int i = 0; i < elemCount; ++i) {
  30.240 -            String className = in.readUTF();
  30.241 -            String methodName = in.readUTF();
  30.242 -            String fileName = in.readUTF();
  30.243 -            int line = in.readInt();
  30.244 -            elems[i] = new StackTraceElement(className, methodName, fileName, line);
  30.245 -        }
  30.246 -        return elems;
  30.247 -    }
  30.248 -
  30.249 -    private void jdiGo(int port) {
  30.250 -        //MessageOutput.textResources = ResourceBundle.getBundle("impl.TTYResources",
  30.251 -        //        Locale.getDefault());
  30.252 -
  30.253 -        String connect = "com.sun.jdi.CommandLineLaunch:";
  30.254 -        String cmdLine = "jdk.internal.jshell.remote.RemoteAgent";
  30.255 -        String classPath = System.getProperty("java.class.path");
  30.256 -        String bootclassPath = System.getProperty("sun.boot.class.path");
  30.257 -        String javaArgs = "-classpath " + classPath + " -Xbootclasspath:" + bootclassPath;
  30.258 -
  30.259 -        String connectSpec = connect + "main=" + cmdLine + " " + port + ",options=" + javaArgs + ",";
  30.260 -        boolean launchImmediately = true;
  30.261 -        int traceFlags = 0;// VirtualMachine.TRACE_SENDS | VirtualMachine.TRACE_EVENTS;
  30.262 -
  30.263 -        env.init(connectSpec, launchImmediately, traceFlags);
  30.264 -
  30.265 -        if (env.connection().isOpen() && env.vm().canBeModified()) {
  30.266 -            /*
  30.267 -             * Connection opened on startup. Start event handler
  30.268 -             * immediately, telling it (through arg 2) to stop on the
  30.269 -             * VM start event.
  30.270 -             */
  30.271 -            handler = new JDIEventHandler(env);
  30.272 -        }
  30.273 -    }
  30.274 -
  30.275 -    protected final Object STOP_LOCK = new Object();
  30.276 -    private boolean userCodeRunning = false;
  30.277 -    
  30.278 -    /**
  30.279 -     * @return true, if the user code is currently executing.
  30.280 -     */
  30.281 -    protected final boolean isUserCodeRunning() {
  30.282 -        return userCodeRunning;
  30.283 -    }
  30.284 -    
  30.285 -    void commandStop() {
  30.286 -        synchronized (STOP_LOCK) {
  30.287 -            if (!userCodeRunning)
  30.288 -                return ;
  30.289 -
  30.290 -            VirtualMachine vm = handler.env.vm();
  30.291 -            vm.suspend();
  30.292 -            try {
  30.293 -                OUTER: for (ThreadReference thread : vm.allThreads()) {
  30.294 -                    // could also tag the thread (e.g. using name), to find it easier
  30.295 -                    for (StackFrame frame : thread.frames()) {
  30.296 -                        String remoteAgentName = "jdk.internal.jshell.remote.RemoteAgent";
  30.297 -                        if (remoteAgentName.equals(frame.location().declaringType().name()) &&
  30.298 -                            "commandLoop".equals(frame.location().method().name())) {
  30.299 -                            ObjectReference thiz = frame.thisObject();
  30.300 -                            if (((BooleanValue) thiz.getValue(thiz.referenceType().fieldByName("inClientCode"))).value()) {
  30.301 -                                thiz.setValue(thiz.referenceType().fieldByName("expectingStop"), vm.mirrorOf(true));
  30.302 -                                ObjectReference stopInstance = (ObjectReference) thiz.getValue(thiz.referenceType().fieldByName("stopException"));
  30.303 -
  30.304 -                                vm.resume();
  30.305 -                                proc.debug(DBG_GEN, "Attempting to stop the client code...\n");
  30.306 -                                thread.stop(stopInstance);
  30.307 -                                thiz.setValue(thiz.referenceType().fieldByName("expectingStop"), vm.mirrorOf(false));
  30.308 -                            }
  30.309 -
  30.310 -                            break OUTER;
  30.311 -                        }
  30.312 -                    }
  30.313 -                }
  30.314 -            } catch (ClassNotLoadedException | IncompatibleThreadStateException | InvalidTypeException  ex) {
  30.315 -                proc.debug(DBG_GEN, "Exception on remote stop: %s\n", ex);
  30.316 -            } finally {
  30.317 -                vm.resume();
  30.318 -            }
  30.319 -        }
  30.320 -    }
  30.321 -}
    31.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    31.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    31.3 @@ -22,6 +22,7 @@
    31.4   * or visit www.oracle.com if you need additional information or have any
    31.5   * questions.
    31.6   */
    31.7 +
    31.8  package jdk.jshell;
    31.9  
   31.10  import jdk.jshell.Key.ExpressionKey;
    32.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    32.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    32.3 @@ -22,6 +22,7 @@
    32.4   * or visit www.oracle.com if you need additional information or have any
    32.5   * questions.
    32.6   */
    32.7 +
    32.8  package jdk.jshell;
    32.9  
   32.10  import jdk.jshell.Key.ImportKey;
    33.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/JDIConnection.java	Fri Jun 03 17:06:38 2016 +0200
    33.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.3 @@ -1,546 +0,0 @@
    33.4 -/*
    33.5 - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
    33.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    33.7 - *
    33.8 - * This code is free software; you can redistribute it and/or modify it
    33.9 - * under the terms of the GNU General Public License version 2 only, as
   33.10 - * published by the Free Software Foundation.  Oracle designates this
   33.11 - * particular file as subject to the "Classpath" exception as provided
   33.12 - * by Oracle in the LICENSE file that accompanied this code.
   33.13 - *
   33.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   33.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   33.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   33.17 - * version 2 for more details (a copy is included in the LICENSE file that
   33.18 - * accompanied this code).
   33.19 - *
   33.20 - * You should have received a copy of the GNU General Public License version
   33.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   33.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   33.23 - *
   33.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   33.25 - * or visit www.oracle.com if you need additional information or have any
   33.26 - * questions.
   33.27 - */
   33.28 -
   33.29 -/*
   33.30 - * This source code is provided to illustrate the usage of a given feature
   33.31 - * or technique and has been deliberately simplified. Additional steps
   33.32 - * required for a production-quality application, such as security checks,
   33.33 - * input validation and proper error handling, might not be present in
   33.34 - * this sample code.
   33.35 - */
   33.36 -
   33.37 -
   33.38 -package jdk.jshell;
   33.39 -
   33.40 -import com.sun.jdi.*;
   33.41 -import com.sun.jdi.connect.*;
   33.42 -
   33.43 -import java.util.*;
   33.44 -import java.util.regex.*;
   33.45 -import java.io.*;
   33.46 -import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
   33.47 -
   33.48 -/**
   33.49 - * Connection to a Java Debug Interface VirtualMachine instance.
   33.50 - * Adapted from jdb VMConnection. Message handling, exception handling, and I/O
   33.51 - * redirection changed.  Interface to JShell added.
   33.52 - */
   33.53 -class JDIConnection {
   33.54 -
   33.55 -    private VirtualMachine vm;
   33.56 -    private Process process = null;
   33.57 -    private int outputCompleteCount = 0;
   33.58 -
   33.59 -    private final JShell proc;
   33.60 -    private final JDIEnv env;
   33.61 -    private final Connector connector;
   33.62 -    private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs;
   33.63 -    private final int traceFlags;
   33.64 -
   33.65 -    synchronized void notifyOutputComplete() {
   33.66 -        outputCompleteCount++;
   33.67 -        notifyAll();
   33.68 -    }
   33.69 -
   33.70 -    synchronized void waitOutputComplete() {
   33.71 -        // Wait for stderr and stdout
   33.72 -        if (process != null) {
   33.73 -            while (outputCompleteCount < 2) {
   33.74 -                try {wait();} catch (InterruptedException e) {}
   33.75 -            }
   33.76 -        }
   33.77 -    }
   33.78 -
   33.79 -    private Connector findConnector(String name) {
   33.80 -        for (Connector cntor :
   33.81 -                 Bootstrap.virtualMachineManager().allConnectors()) {
   33.82 -            if (cntor.name().equals(name)) {
   33.83 -                return cntor;
   33.84 -            }
   33.85 -        }
   33.86 -        return null;
   33.87 -    }
   33.88 -
   33.89 -    private Map <String, com.sun.jdi.connect.Connector.Argument> parseConnectorArgs(Connector connector, String argString) {
   33.90 -        Map<String, com.sun.jdi.connect.Connector.Argument> arguments = connector.defaultArguments();
   33.91 -
   33.92 -        /*
   33.93 -         * We are parsing strings of the form:
   33.94 -         *    name1=value1,[name2=value2,...]
   33.95 -         * However, the value1...valuen substrings may contain
   33.96 -         * embedded comma(s), so make provision for quoting inside
   33.97 -         * the value substrings. (Bug ID 4285874)
   33.98 -         */
   33.99 -        String regexPattern =
  33.100 -            "(quote=[^,]+,)|" +           // special case for quote=.,
  33.101 -            "(\\w+=)" +                   // name=
  33.102 -            "(((\"[^\"]*\")|" +           //   ( "l , ue"
  33.103 -            "('[^']*')|" +                //     'l , ue'
  33.104 -            "([^,'\"]+))+,)";             //     v a l u e )+ ,
  33.105 -        Pattern p = Pattern.compile(regexPattern);
  33.106 -        Matcher m = p.matcher(argString);
  33.107 -        while (m.find()) {
  33.108 -            int startPosition = m.start();
  33.109 -            int endPosition = m.end();
  33.110 -            if (startPosition > 0) {
  33.111 -                /*
  33.112 -                 * It is an error if parsing skips over any part of argString.
  33.113 -                 */
  33.114 -                throw new IllegalArgumentException("Illegal connector argument" +
  33.115 -                                          argString);
  33.116 -            }
  33.117 -
  33.118 -            String token = argString.substring(startPosition, endPosition);
  33.119 -            int index = token.indexOf('=');
  33.120 -            String name = token.substring(0, index);
  33.121 -            String value = token.substring(index + 1,
  33.122 -                                           token.length() - 1); // Remove comma delimiter
  33.123 -
  33.124 -            /*
  33.125 -             * for values enclosed in quotes (single and/or double quotes)
  33.126 -             * strip off enclosing quote chars
  33.127 -             * needed for quote enclosed delimited substrings
  33.128 -             */
  33.129 -            if (name.equals("options")) {
  33.130 -                StringBuilder sb = new StringBuilder();
  33.131 -                for (String s : splitStringAtNonEnclosedWhiteSpace(value)) {
  33.132 -                    while (isEnclosed(s, "\"") || isEnclosed(s, "'")) {
  33.133 -                        s = s.substring(1, s.length() - 1);
  33.134 -                    }
  33.135 -                    sb.append(s);
  33.136 -                    sb.append(" ");
  33.137 -                }
  33.138 -                value = sb.toString();
  33.139 -            }
  33.140 -
  33.141 -            Connector.Argument argument = arguments.get(name);
  33.142 -            if (argument == null) {
  33.143 -                throw new IllegalArgumentException("Argument is not defined for connector:" +
  33.144 -                                          name + " -- " + connector.name());
  33.145 -            }
  33.146 -            argument.setValue(value);
  33.147 -
  33.148 -            argString = argString.substring(endPosition); // Remove what was just parsed...
  33.149 -            m = p.matcher(argString);                     //    and parse again on what is left.
  33.150 -        }
  33.151 -        if ((! argString.equals(",")) && (argString.length() > 0)) {
  33.152 -            /*
  33.153 -             * It is an error if any part of argString is left over,
  33.154 -             * unless it was empty to begin with.
  33.155 -             */
  33.156 -            throw new IllegalArgumentException("Illegal connector argument" + argString);
  33.157 -        }
  33.158 -        return arguments;
  33.159 -    }
  33.160 -
  33.161 -    private static boolean isEnclosed(String value, String enclosingChar) {
  33.162 -        if (value.indexOf(enclosingChar) == 0) {
  33.163 -            int lastIndex = value.lastIndexOf(enclosingChar);
  33.164 -            if (lastIndex > 0 && lastIndex  == value.length() - 1) {
  33.165 -                return true;
  33.166 -            }
  33.167 -        }
  33.168 -        return false;
  33.169 -    }
  33.170 -
  33.171 -    private static List<String> splitStringAtNonEnclosedWhiteSpace(String value) throws IllegalArgumentException {
  33.172 -        List<String> al = new ArrayList<>();
  33.173 -        char[] arr;
  33.174 -        int startPosition = 0;
  33.175 -        int endPosition;
  33.176 -        final char SPACE = ' ';
  33.177 -        final char DOUBLEQ = '"';
  33.178 -        final char SINGLEQ = '\'';
  33.179 -
  33.180 -        /*
  33.181 -         * An "open" or "active" enclosing state is where
  33.182 -         * the first valid start quote qualifier is found,
  33.183 -         * and there is a search in progress for the
  33.184 -         * relevant end matching quote
  33.185 -         *
  33.186 -         * enclosingTargetChar set to SPACE
  33.187 -         * is used to signal a non open enclosing state
  33.188 -         */
  33.189 -        char enclosingTargetChar = SPACE;
  33.190 -
  33.191 -        if (value == null) {
  33.192 -            throw new IllegalArgumentException("value string is null");
  33.193 -        }
  33.194 -
  33.195 -        // split parameter string into individual chars
  33.196 -        arr = value.toCharArray();
  33.197 -
  33.198 -        for (int i = 0; i < arr.length; i++) {
  33.199 -            switch (arr[i]) {
  33.200 -                case SPACE: {
  33.201 -                    // do nothing for spaces
  33.202 -                    // unless last in array
  33.203 -                    if (isLastChar(arr, i)) {
  33.204 -                        endPosition = i;
  33.205 -                        // break for substring creation
  33.206 -                        break;
  33.207 -                    }
  33.208 -                    continue;
  33.209 -                }
  33.210 -                case DOUBLEQ:
  33.211 -                case SINGLEQ: {
  33.212 -                    if (enclosingTargetChar == arr[i]) {
  33.213 -                        // potential match to close open enclosing
  33.214 -                        if (isNextCharWhitespace(arr, i)) {
  33.215 -                            // if peek next is whitespace
  33.216 -                            // then enclosing is a valid substring
  33.217 -                            endPosition = i;
  33.218 -                            // reset enclosing target char
  33.219 -                            enclosingTargetChar = SPACE;
  33.220 -                            // break for substring creation
  33.221 -                            break;
  33.222 -                        }
  33.223 -                    }
  33.224 -                    if (enclosingTargetChar == SPACE) {
  33.225 -                        // no open enclosing state
  33.226 -                        // handle as normal char
  33.227 -                        if (isPreviousCharWhitespace(arr, i)) {
  33.228 -                            startPosition = i;
  33.229 -                            // peek forward for end candidates
  33.230 -                            if (value.indexOf(arr[i], i + 1) >= 0) {
  33.231 -                                // set open enclosing state by
  33.232 -                                // setting up the target char
  33.233 -                                enclosingTargetChar = arr[i];
  33.234 -                            } else {
  33.235 -                                // no more target chars left to match
  33.236 -                                // end enclosing, handle as normal char
  33.237 -                                if (isNextCharWhitespace(arr, i)) {
  33.238 -                                    endPosition = i;
  33.239 -                                    // break for substring creation
  33.240 -                                    break;
  33.241 -                                }
  33.242 -                            }
  33.243 -                        }
  33.244 -                    }
  33.245 -                    continue;
  33.246 -                }
  33.247 -                default: {
  33.248 -                    // normal non-space, non-" and non-' chars
  33.249 -                    if (enclosingTargetChar == SPACE) {
  33.250 -                        // no open enclosing state
  33.251 -                        if (isPreviousCharWhitespace(arr, i)) {
  33.252 -                            // start of space delim substring
  33.253 -                            startPosition = i;
  33.254 -                        }
  33.255 -                        if (isNextCharWhitespace(arr, i)) {
  33.256 -                            // end of space delim substring
  33.257 -                            endPosition = i;
  33.258 -                            // break for substring creation
  33.259 -                            break;
  33.260 -                        }
  33.261 -                    }
  33.262 -                    continue;
  33.263 -                }
  33.264 -            }
  33.265 -
  33.266 -            // break's end up here
  33.267 -            if (startPosition > endPosition) {
  33.268 -                throw new IllegalArgumentException("Illegal option values");
  33.269 -            }
  33.270 -
  33.271 -            // extract substring and add to List<String>
  33.272 -            al.add(value.substring(startPosition, ++endPosition));
  33.273 -
  33.274 -            // set new start position
  33.275 -            i = startPosition = endPosition;
  33.276 -
  33.277 -        } // for loop
  33.278 -
  33.279 -        return al;
  33.280 -    }
  33.281 -
  33.282 -    static private boolean isPreviousCharWhitespace(char[] arr, int curr_pos) {
  33.283 -        return isCharWhitespace(arr, curr_pos - 1);
  33.284 -    }
  33.285 -
  33.286 -    static private boolean isNextCharWhitespace(char[] arr, int curr_pos) {
  33.287 -        return isCharWhitespace(arr, curr_pos + 1);
  33.288 -    }
  33.289 -
  33.290 -    static private boolean isCharWhitespace(char[] arr, int pos) {
  33.291 -        if (pos < 0 || pos >= arr.length) {
  33.292 -            // outside arraybounds is considered an implicit space
  33.293 -            return true;
  33.294 -        }
  33.295 -        return (arr[pos] == ' ');
  33.296 -    }
  33.297 -
  33.298 -    static private boolean isLastChar(char[] arr, int pos) {
  33.299 -        return (pos + 1 == arr.length);
  33.300 -    }
  33.301 -
  33.302 -    JDIConnection(JDIEnv env, String connectSpec, int traceFlags, JShell proc) {
  33.303 -        this.env = env;
  33.304 -        this.proc = proc;
  33.305 -        String nameString;
  33.306 -        String argString;
  33.307 -        int index = connectSpec.indexOf(':');
  33.308 -        if (index == -1) {
  33.309 -            nameString = connectSpec;
  33.310 -            argString = "";
  33.311 -        } else {
  33.312 -            nameString = connectSpec.substring(0, index);
  33.313 -            argString = connectSpec.substring(index + 1);
  33.314 -        }
  33.315 -
  33.316 -        connector = findConnector(nameString);
  33.317 -        if (connector == null) {
  33.318 -            throw new IllegalArgumentException("No connector named: " + nameString);
  33.319 -        }
  33.320 -
  33.321 -        connectorArgs = parseConnectorArgs(connector, argString);
  33.322 -        this.traceFlags = traceFlags;
  33.323 -    }
  33.324 -
  33.325 -    synchronized VirtualMachine open() {
  33.326 -        if (connector instanceof LaunchingConnector) {
  33.327 -            vm = launchTarget();
  33.328 -        } else if (connector instanceof AttachingConnector) {
  33.329 -            vm = attachTarget();
  33.330 -        } else if (connector instanceof ListeningConnector) {
  33.331 -            vm = listenTarget();
  33.332 -        } else {
  33.333 -            throw new InternalError("Invalid connect type");
  33.334 -        }
  33.335 -        vm.setDebugTraceMode(traceFlags);
  33.336 -        // Uncomment here and below to enable event requests
  33.337 -        // installEventRequests(vm);
  33.338 -
  33.339 -        return vm;
  33.340 -    }
  33.341 -
  33.342 -    boolean setConnectorArg(String name, String value) {
  33.343 -        /*
  33.344 -         * Too late if the connection already made
  33.345 -         */
  33.346 -        if (vm != null) {
  33.347 -            return false;
  33.348 -        }
  33.349 -
  33.350 -        Connector.Argument argument = connectorArgs.get(name);
  33.351 -        if (argument == null) {
  33.352 -            return false;
  33.353 -        }
  33.354 -        argument.setValue(value);
  33.355 -        return true;
  33.356 -    }
  33.357 -
  33.358 -    String connectorArg(String name) {
  33.359 -        Connector.Argument argument = connectorArgs.get(name);
  33.360 -        if (argument == null) {
  33.361 -            return "";
  33.362 -        }
  33.363 -        return argument.value();
  33.364 -    }
  33.365 -
  33.366 -    public synchronized VirtualMachine vm() {
  33.367 -        if (vm == null) {
  33.368 -            throw new JDINotConnectedException();
  33.369 -        } else {
  33.370 -            return vm;
  33.371 -        }
  33.372 -    }
  33.373 -
  33.374 -    boolean isOpen() {
  33.375 -        return (vm != null);
  33.376 -    }
  33.377 -
  33.378 -    boolean isLaunch() {
  33.379 -        return (connector instanceof LaunchingConnector);
  33.380 -    }
  33.381 -
  33.382 -    public void disposeVM() {
  33.383 -        try {
  33.384 -            if (vm != null) {
  33.385 -                vm.dispose(); // This could NPE, so it is caught below
  33.386 -                vm = null;
  33.387 -            }
  33.388 -        } catch (VMDisconnectedException | NullPointerException ex) {
  33.389 -            // Ignore if already closed
  33.390 -        } finally {
  33.391 -            if (process != null) {
  33.392 -                process.destroy();
  33.393 -                process = null;
  33.394 -            }
  33.395 -            waitOutputComplete();
  33.396 -        }
  33.397 -    }
  33.398 -
  33.399 -/*** Preserved for possible future support of event requests
  33.400 -
  33.401 -    private void installEventRequests(VirtualMachine vm) {
  33.402 -        if (vm.canBeModified()){
  33.403 -            setEventRequests(vm);
  33.404 -            resolveEventRequests();
  33.405 -        }
  33.406 -    }
  33.407 -
  33.408 -    private void setEventRequests(VirtualMachine vm) {
  33.409 -        EventRequestManager erm = vm.eventRequestManager();
  33.410 -
  33.411 -        // Normally, we want all uncaught exceptions.  We request them
  33.412 -        // via the same mechanism as Commands.commandCatchException()
  33.413 -        // so the user can ignore them later if they are not
  33.414 -        // interested.
  33.415 -        // FIXME: this works but generates spurious messages on stdout
  33.416 -        //        during startup:
  33.417 -        //          Set uncaught java.lang.Throwable
  33.418 -        //          Set deferred uncaught java.lang.Throwable
  33.419 -        Commands evaluator = new Commands();
  33.420 -        evaluator.commandCatchException
  33.421 -            (new StringTokenizer("uncaught java.lang.Throwable"));
  33.422 -
  33.423 -        ThreadStartRequest tsr = erm.createThreadStartRequest();
  33.424 -        tsr.enable();
  33.425 -        ThreadDeathRequest tdr = erm.createThreadDeathRequest();
  33.426 -        tdr.enable();
  33.427 -    }
  33.428 -
  33.429 -    private void resolveEventRequests() {
  33.430 -        Env.specList.resolveAll();
  33.431 -    }
  33.432 -***/
  33.433 -
  33.434 -    private void dumpStream(InputStream inStream, final PrintStream pStream) throws IOException {
  33.435 -        BufferedReader in =
  33.436 -            new BufferedReader(new InputStreamReader(inStream));
  33.437 -        int i;
  33.438 -        try {
  33.439 -            while ((i = in.read()) != -1) {
  33.440 -                pStream.print((char) i);
  33.441 -            }
  33.442 -        } catch (IOException ex) {
  33.443 -            String s = ex.getMessage();
  33.444 -            if (!s.startsWith("Bad file number")) {
  33.445 -                throw ex;
  33.446 -            }
  33.447 -            // else we got a Bad file number IOException which just means
  33.448 -            // that the debuggee has gone away.  We'll just treat it the
  33.449 -            // same as if we got an EOF.
  33.450 -        }
  33.451 -    }
  33.452 -
  33.453 -    /**
  33.454 -     *  Create a Thread that will retrieve and display any output.
  33.455 -     *  Needs to be high priority, else debugger may exit before
  33.456 -     *  it can be displayed.
  33.457 -     */
  33.458 -    private void displayRemoteOutput(final InputStream inStream, final PrintStream pStream) {
  33.459 -        Thread thr = new Thread("output reader") {
  33.460 -            @Override
  33.461 -            public void run() {
  33.462 -                try {
  33.463 -                    dumpStream(inStream, pStream);
  33.464 -                } catch (IOException ex) {
  33.465 -                    proc.debug(ex, "Failed reading output");
  33.466 -                    env.shutdown();
  33.467 -                } finally {
  33.468 -                    notifyOutputComplete();
  33.469 -                }
  33.470 -            }
  33.471 -        };
  33.472 -        thr.setPriority(Thread.MAX_PRIORITY-1);
  33.473 -        thr.start();
  33.474 -    }
  33.475 -
  33.476 -    /**
  33.477 -     *  Create a Thread that will ship all input to remote.
  33.478 -     *  Does it need be high priority?
  33.479 -     */
  33.480 -    private void readRemoteInput(final OutputStream outStream, final InputStream inputStream) {
  33.481 -        Thread thr = new Thread("input reader") {
  33.482 -            @Override
  33.483 -            public void run() {
  33.484 -                try {
  33.485 -                    byte[] buf = new byte[256];
  33.486 -                    int cnt;
  33.487 -                    while ((cnt = inputStream.read(buf)) != -1) {
  33.488 -                        outStream.write(buf, 0, cnt);
  33.489 -                        outStream.flush();
  33.490 -                    }
  33.491 -                } catch (IOException ex) {
  33.492 -                    proc.debug(ex, "Failed reading output");
  33.493 -                    env.shutdown();
  33.494 -                }
  33.495 -            }
  33.496 -        };
  33.497 -        thr.setPriority(Thread.MAX_PRIORITY-1);
  33.498 -        thr.start();
  33.499 -    }
  33.500 -
  33.501 -    /* launch child target vm */
  33.502 -    private VirtualMachine launchTarget() {
  33.503 -        LaunchingConnector launcher = (LaunchingConnector)connector;
  33.504 -        try {
  33.505 -            VirtualMachine new_vm = launcher.launch(connectorArgs);
  33.506 -            process = new_vm.process();
  33.507 -            displayRemoteOutput(process.getErrorStream(), proc.err);
  33.508 -            displayRemoteOutput(process.getInputStream(), proc.out);
  33.509 -            readRemoteInput(process.getOutputStream(), proc.in);
  33.510 -            return new_vm;
  33.511 -        } catch (Exception ex) {
  33.512 -            reportLaunchFail(ex, "launch");
  33.513 -        }
  33.514 -        return null;
  33.515 -    }
  33.516 -
  33.517 -    /* JShell currently uses only launch, preserved for futures: */
  33.518 -    /* attach to running target vm */
  33.519 -    private VirtualMachine attachTarget() {
  33.520 -        AttachingConnector attacher = (AttachingConnector)connector;
  33.521 -        try {
  33.522 -            return attacher.attach(connectorArgs);
  33.523 -        } catch (Exception ex) {
  33.524 -            reportLaunchFail(ex, "attach");
  33.525 -        }
  33.526 -        return null;
  33.527 -    }
  33.528 -
  33.529 -    /* JShell currently uses only launch, preserved for futures: */
  33.530 -    /* listen for connection from target vm */
  33.531 -    private VirtualMachine listenTarget() {
  33.532 -        ListeningConnector listener = (ListeningConnector)connector;
  33.533 -        try {
  33.534 -            String retAddress = listener.startListening(connectorArgs);
  33.535 -            proc.debug(DBG_GEN, "Listening at address: " + retAddress);
  33.536 -            vm = listener.accept(connectorArgs);
  33.537 -            listener.stopListening(connectorArgs);
  33.538 -            return vm;
  33.539 -        } catch (Exception ex) {
  33.540 -            reportLaunchFail(ex, "listen");
  33.541 -        }
  33.542 -        return null;
  33.543 -    }
  33.544 -
  33.545 -    private void reportLaunchFail(Exception ex, String context) {
  33.546 -        throw new InternalError("Failed remote " + context + ": " + connector +
  33.547 -                " -- " + connectorArgs, ex);
  33.548 -    }
  33.549 -}
    34.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/JDIEnv.java	Fri Jun 03 17:06:38 2016 +0200
    34.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.3 @@ -1,80 +0,0 @@
    34.4 -/*
    34.5 - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
    34.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    34.7 - *
    34.8 - * This code is free software; you can redistribute it and/or modify it
    34.9 - * under the terms of the GNU General Public License version 2 only, as
   34.10 - * published by the Free Software Foundation.  Oracle designates this
   34.11 - * particular file as subject to the "Classpath" exception as provided
   34.12 - * by Oracle in the LICENSE file that accompanied this code.
   34.13 - *
   34.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   34.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   34.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   34.17 - * version 2 for more details (a copy is included in the LICENSE file that
   34.18 - * accompanied this code).
   34.19 - *
   34.20 - * You should have received a copy of the GNU General Public License version
   34.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   34.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   34.23 - *
   34.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   34.25 - * or visit www.oracle.com if you need additional information or have any
   34.26 - * questions.
   34.27 - */
   34.28 -
   34.29 -package jdk.jshell;
   34.30 -
   34.31 -import java.util.Map;
   34.32 -
   34.33 -import com.sun.jdi.*;
   34.34 -import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
   34.35 -
   34.36 -/**
   34.37 - * Representation of a Java Debug Interface environment
   34.38 - * Select methods extracted from jdb Env; shutdown() adapted to JShell shutdown.
   34.39 - */
   34.40 -class JDIEnv {
   34.41 -
   34.42 -    private JDIConnection connection;
   34.43 -    private final JShell state;
   34.44 -
   34.45 -    JDIEnv(JShell state) {
   34.46 -        this.state = state;
   34.47 -    }
   34.48 -
   34.49 -    void init(String connectSpec, boolean openNow, int flags) {
   34.50 -        connection = new JDIConnection(this, connectSpec, flags, state);
   34.51 -        if (!connection.isLaunch() || openNow) {
   34.52 -            connection.open();
   34.53 -        }
   34.54 -    }
   34.55 -
   34.56 -    JDIConnection connection() {
   34.57 -        return connection;
   34.58 -    }
   34.59 -
   34.60 -    VirtualMachine vm() {
   34.61 -        return connection.vm();
   34.62 -    }
   34.63 -
   34.64 -    void shutdown() {
   34.65 -        if (connection != null) {
   34.66 -            try {
   34.67 -                connection.disposeVM();
   34.68 -            } catch (VMDisconnectedException e) {
   34.69 -                // Shutting down after the VM has gone away. This is
   34.70 -                // not an error, and we just ignore it.
   34.71 -            } catch (Throwable e) {
   34.72 -                state.debug(DBG_GEN, null, "disposeVM threw: " + e);
   34.73 -            }
   34.74 -        }
   34.75 -        if (state != null) { // If state has been set-up
   34.76 -            try {
   34.77 -                state.closeDown();
   34.78 -            } catch (Throwable e) {
   34.79 -                state.debug(DBG_GEN, null, "state().closeDown() threw: " + e);
   34.80 -            }
   34.81 -        }
   34.82 -    }
   34.83 -}
    35.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/JDIEventHandler.java	Fri Jun 03 17:06:38 2016 +0200
    35.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.3 @@ -1,181 +0,0 @@
    35.4 -/*
    35.5 - * Copyright (c) 1998, 2011, 2014, Oracle and/or its affiliates. All rights reserved.
    35.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    35.7 - *
    35.8 - * This code is free software; you can redistribute it and/or modify it
    35.9 - * under the terms of the GNU General Public License version 2 only, as
   35.10 - * published by the Free Software Foundation.  Oracle designates this
   35.11 - * particular file as subject to the "Classpath" exception as provided
   35.12 - * by Oracle in the LICENSE file that accompanied this code.
   35.13 - *
   35.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   35.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   35.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   35.17 - * version 2 for more details (a copy is included in the LICENSE file that
   35.18 - * accompanied this code).
   35.19 - *
   35.20 - * You should have received a copy of the GNU General Public License version
   35.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   35.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   35.23 - *
   35.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   35.25 - * or visit www.oracle.com if you need additional information or have any
   35.26 - * questions.
   35.27 - */
   35.28 -
   35.29 -package jdk.jshell;
   35.30 -
   35.31 -import com.sun.jdi.*;
   35.32 -import com.sun.jdi.event.*;
   35.33 -
   35.34 -/**
   35.35 - * Handler of Java Debug Interface events.
   35.36 - * Adapted from jdb EventHandler; Handling of events not used by JShell stubbed out.
   35.37 - */
   35.38 -class JDIEventHandler implements Runnable {
   35.39 -
   35.40 -    Thread thread;
   35.41 -    volatile boolean connected = true;
   35.42 -    boolean completed = false;
   35.43 -    String shutdownMessageKey;
   35.44 -    final JDIEnv env;
   35.45 -
   35.46 -    JDIEventHandler(JDIEnv env) {
   35.47 -        this.env = env;
   35.48 -        this.thread = new Thread(this, "event-handler");
   35.49 -        this.thread.start();
   35.50 -    }
   35.51 -
   35.52 -    synchronized void shutdown() {
   35.53 -        connected = false;  // force run() loop termination
   35.54 -        thread.interrupt();
   35.55 -        while (!completed) {
   35.56 -            try {wait();} catch (InterruptedException exc) {}
   35.57 -        }
   35.58 -    }
   35.59 -
   35.60 -    @Override
   35.61 -    public void run() {
   35.62 -        EventQueue queue = env.vm().eventQueue();
   35.63 -        while (connected) {
   35.64 -            try {
   35.65 -                EventSet eventSet = queue.remove();
   35.66 -                boolean resumeStoppedApp = false;
   35.67 -                EventIterator it = eventSet.eventIterator();
   35.68 -                while (it.hasNext()) {
   35.69 -                    resumeStoppedApp |= handleEvent(it.nextEvent());
   35.70 -                }
   35.71 -
   35.72 -                if (resumeStoppedApp) {
   35.73 -                    eventSet.resume();
   35.74 -                }
   35.75 -            } catch (InterruptedException exc) {
   35.76 -                // Do nothing. Any changes will be seen at top of loop.
   35.77 -            } catch (VMDisconnectedException discExc) {
   35.78 -                handleDisconnectedException();
   35.79 -                break;
   35.80 -            }
   35.81 -        }
   35.82 -        synchronized (this) {
   35.83 -            completed = true;
   35.84 -            notifyAll();
   35.85 -        }
   35.86 -    }
   35.87 -
   35.88 -    private boolean handleEvent(Event event) {
   35.89 -        if (event instanceof ExceptionEvent) {
   35.90 -            exceptionEvent(event);
   35.91 -        } else if (event instanceof WatchpointEvent) {
   35.92 -            fieldWatchEvent(event);
   35.93 -        } else if (event instanceof MethodEntryEvent) {
   35.94 -            methodEntryEvent(event);
   35.95 -        } else if (event instanceof MethodExitEvent) {
   35.96 -            methodExitEvent(event);
   35.97 -        } else if (event instanceof ClassPrepareEvent) {
   35.98 -            classPrepareEvent(event);
   35.99 -        } else if (event instanceof ThreadStartEvent) {
  35.100 -            threadStartEvent(event);
  35.101 -        } else if (event instanceof ThreadDeathEvent) {
  35.102 -            threadDeathEvent(event);
  35.103 -        } else if (event instanceof VMStartEvent) {
  35.104 -            vmStartEvent(event);
  35.105 -            return true;
  35.106 -        } else {
  35.107 -            handleExitEvent(event);
  35.108 -        }
  35.109 -        return true;
  35.110 -    }
  35.111 -
  35.112 -    private boolean vmDied = false;
  35.113 -
  35.114 -    private void handleExitEvent(Event event) {
  35.115 -        if (event instanceof VMDeathEvent) {
  35.116 -            vmDied = true;
  35.117 -            shutdownMessageKey = "The application exited";
  35.118 -        } else if (event instanceof VMDisconnectEvent) {
  35.119 -            connected = false;
  35.120 -            if (!vmDied) {
  35.121 -                shutdownMessageKey = "The application has been disconnected";
  35.122 -            }
  35.123 -        } else {
  35.124 -            throw new InternalError("Unexpected event type: " +
  35.125 -                    event.getClass());
  35.126 -        }
  35.127 -        env.shutdown();
  35.128 -    }
  35.129 -
  35.130 -    synchronized void handleDisconnectedException() {
  35.131 -        /*
  35.132 -         * A VMDisconnectedException has happened while dealing with
  35.133 -         * another event. We need to flush the event queue, dealing only
  35.134 -         * with exit events (VMDeath, VMDisconnect) so that we terminate
  35.135 -         * correctly.
  35.136 -         */
  35.137 -        EventQueue queue = env.vm().eventQueue();
  35.138 -        while (connected) {
  35.139 -            try {
  35.140 -                EventSet eventSet = queue.remove();
  35.141 -                EventIterator iter = eventSet.eventIterator();
  35.142 -                while (iter.hasNext()) {
  35.143 -                    handleExitEvent(iter.next());
  35.144 -                }
  35.145 -            } catch (InterruptedException exc) {
  35.146 -                // ignore
  35.147 -            } catch (InternalError exc) {
  35.148 -                // ignore
  35.149 -            }
  35.150 -        }
  35.151 -    }
  35.152 -
  35.153 -    private void vmStartEvent(Event event)  {
  35.154 -        VMStartEvent se = (VMStartEvent)event;
  35.155 -    }
  35.156 -
  35.157 -    private void methodEntryEvent(Event event)  {
  35.158 -        MethodEntryEvent me = (MethodEntryEvent)event;
  35.159 -    }
  35.160 -
  35.161 -    private void methodExitEvent(Event event)  {
  35.162 -        MethodExitEvent me = (MethodExitEvent)event;
  35.163 -    }
  35.164 -
  35.165 -    private void fieldWatchEvent(Event event)  {
  35.166 -        WatchpointEvent fwe = (WatchpointEvent)event;
  35.167 -    }
  35.168 -
  35.169 -    private void classPrepareEvent(Event event)  {
  35.170 -        ClassPrepareEvent cle = (ClassPrepareEvent)event;
  35.171 -    }
  35.172 -
  35.173 -    private void exceptionEvent(Event event) {
  35.174 -        ExceptionEvent ee = (ExceptionEvent)event;
  35.175 -    }
  35.176 -
  35.177 -    private void threadDeathEvent(Event event) {
  35.178 -        ThreadDeathEvent tee = (ThreadDeathEvent)event;
  35.179 -    }
  35.180 -
  35.181 -    private void threadStartEvent(Event event) {
  35.182 -        ThreadStartEvent tse = (ThreadStartEvent)event;
  35.183 -    }
  35.184 -}
    36.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/JDINotConnectedException.java	Fri Jun 03 17:06:38 2016 +0200
    36.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.3 @@ -1,43 +0,0 @@
    36.4 -/*
    36.5 - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
    36.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    36.7 - *
    36.8 - * This code is free software; you can redistribute it and/or modify it
    36.9 - * under the terms of the GNU General Public License version 2 only, as
   36.10 - * published by the Free Software Foundation.  Oracle designates this
   36.11 - * particular file as subject to the "Classpath" exception as provided
   36.12 - * by Oracle in the LICENSE file that accompanied this code.
   36.13 - *
   36.14 - * This code is distributed in the hope that it will be useful, but WITHOUT
   36.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   36.16 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   36.17 - * version 2 for more details (a copy is included in the LICENSE file that
   36.18 - * accompanied this code).
   36.19 - *
   36.20 - * You should have received a copy of the GNU General Public License version
   36.21 - * 2 along with this work; if not, write to the Free Software Foundation,
   36.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   36.23 - *
   36.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   36.25 - * or visit www.oracle.com if you need additional information or have any
   36.26 - * questions.
   36.27 - */
   36.28 -
   36.29 -package jdk.jshell;
   36.30 -
   36.31 -/**
   36.32 - * Internal exception when Java Debug Interface VirtualMacine is not connected.
   36.33 - * Copy of jdb VMNotConnectedException.
   36.34 - */
   36.35 -class JDINotConnectedException extends RuntimeException {
   36.36 -
   36.37 -    private static final long serialVersionUID = -7433430494903950165L;
   36.38 -
   36.39 -    public JDINotConnectedException() {
   36.40 -        super();
   36.41 -    }
   36.42 -
   36.43 -    public JDINotConnectedException(String s) {
   36.44 -        super(s);
   36.45 -    }
   36.46 -}
    37.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Fri Jun 03 17:06:38 2016 +0200
    37.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/JShell.java	Fri Sep 02 23:54:26 2016 +0200
    37.3 @@ -25,15 +25,20 @@
    37.4  
    37.5  package jdk.jshell;
    37.6  
    37.7 +import jdk.jshell.spi.ExecutionControl;
    37.8  import java.io.ByteArrayInputStream;
    37.9 -import java.io.IOException;
   37.10  import java.io.InputStream;
   37.11  import java.io.PrintStream;
   37.12 +import java.text.MessageFormat;
   37.13 +import java.util.ArrayList;
   37.14 +import java.util.Arrays;
   37.15  import java.util.Collections;
   37.16  import java.util.HashMap;
   37.17  import java.util.List;
   37.18  import java.util.Map;
   37.19 +import java.util.MissingResourceException;
   37.20  import java.util.Objects;
   37.21 +import java.util.ResourceBundle;
   37.22  import java.util.function.BiFunction;
   37.23  import java.util.function.Consumer;
   37.24  
   37.25 @@ -43,6 +48,8 @@
   37.26  import static java.util.stream.Collectors.toList;
   37.27  import static jdk.jshell.Util.expunge;
   37.28  import jdk.jshell.Snippet.Status;
   37.29 +import jdk.internal.jshell.jdi.JDIExecutionControl;
   37.30 +import jdk.jshell.spi.ExecutionEnv;
   37.31  
   37.32  /**
   37.33   * The JShell evaluation state engine.  This is the central class in the JShell
   37.34 @@ -67,32 +74,35 @@
   37.35   * <p>
   37.36   * This class is not thread safe, except as noted, all access should be through
   37.37   * a single thread.
   37.38 - * @see jdk.jshell
   37.39   * @author Robert Field
   37.40   */
   37.41  public class JShell implements AutoCloseable {
   37.42  
   37.43      final SnippetMaps maps;
   37.44      final KeyMap keyMap;
   37.45 +    final OuterWrapMap outerMap;
   37.46      final TaskFactory taskFactory;
   37.47      final InputStream in;
   37.48      final PrintStream out;
   37.49      final PrintStream err;
   37.50      final Supplier<String> tempVariableNameGenerator;
   37.51      final BiFunction<Snippet, Integer, String> idGenerator;
   37.52 +    final List<String> extraRemoteVMOptions;
   37.53 +    final ExecutionControl executionControl;
   37.54  
   37.55      private int nextKeyIndex = 1;
   37.56  
   37.57      final Eval eval;
   37.58 -    final ClassTracker classTracker;
   37.59 +    private final Map<String, byte[]> classnameToBytes = new HashMap<>();
   37.60      private final Map<Subscription, Consumer<JShell>> shutdownListeners = new HashMap<>();
   37.61      private final Map<Subscription, Consumer<SnippetEvent>> keyStatusListeners = new HashMap<>();
   37.62      private boolean closed = false;
   37.63  
   37.64 -
   37.65 -    private ExecutionControl executionControl = null;
   37.66 +    private boolean executionControlLaunched = false;
   37.67      private SourceCodeAnalysisImpl sourceCodeAnalysis = null;
   37.68  
   37.69 +    private static final String L10N_RB_NAME    = "jdk.jshell.resources.l10n";
   37.70 +    private static ResourceBundle outputRB  = null;
   37.71  
   37.72      JShell(Builder b) {
   37.73          this.in = b.in;
   37.74 @@ -100,13 +110,16 @@
   37.75          this.err = b.err;
   37.76          this.tempVariableNameGenerator = b.tempVariableNameGenerator;
   37.77          this.idGenerator = b.idGenerator;
   37.78 +        this.extraRemoteVMOptions = b.extraRemoteVMOptions;
   37.79 +        this.executionControl = b.executionControl==null
   37.80 +                ? new JDIExecutionControl()
   37.81 +                : b.executionControl;
   37.82  
   37.83          this.maps = new SnippetMaps(this);
   37.84 -        maps.setPackageName("REPL");
   37.85          this.keyMap = new KeyMap(this);
   37.86 +        this.outerMap = new OuterWrapMap(this);
   37.87          this.taskFactory = new TaskFactory(this);
   37.88          this.eval = new Eval(this);
   37.89 -        this.classTracker = new ClassTracker(this);
   37.90      }
   37.91  
   37.92      /**
   37.93 @@ -134,22 +147,24 @@
   37.94          PrintStream err = System.err;
   37.95          Supplier<String> tempVariableNameGenerator = null;
   37.96          BiFunction<Snippet, Integer, String> idGenerator = null;
   37.97 +        List<String> extraRemoteVMOptions = new ArrayList<>();
   37.98 +        ExecutionControl executionControl;
   37.99  
  37.100          Builder() { }
  37.101  
  37.102          /**
  37.103 -         * Input for the running evaluation (it's <code>System.in</code>). Note:
  37.104 -         * applications that use <code>System.in</code> for snippet or other
  37.105 -         * user input cannot use <code>System.in</code> as the input stream for
  37.106 +         * Sets the input for the running evaluation (it's {@code System.in}). Note:
  37.107 +         * applications that use {@code System.in} for snippet or other
  37.108 +         * user input cannot use {@code System.in} as the input stream for
  37.109           * the remote process.
  37.110           * <p>
  37.111           * The default, if this is not set, is to provide an empty input stream
  37.112 -         * -- <code>new ByteArrayInputStream(new byte[0])</code>.
  37.113 +         * -- {@code new ByteArrayInputStream(new byte[0])}.
  37.114           *
  37.115 -         * @param in the <code>InputStream</code> to be channelled to
  37.116 -         * <code>System.in</code> in the remote execution process.
  37.117 -         * @return the <code>Builder</code> instance (for use in chained
  37.118 -         * initialization).
  37.119 +         * @param in the {@code InputStream} to be channelled to
  37.120 +         * {@code System.in} in the remote execution process
  37.121 +         * @return the {@code Builder} instance (for use in chained
  37.122 +         * initialization)
  37.123           */
  37.124          public Builder in(InputStream in) {
  37.125              this.in = in;
  37.126 @@ -157,16 +172,16 @@
  37.127          }
  37.128  
  37.129          /**
  37.130 -         * Output for the running evaluation (it's <code>System.out</code>).
  37.131 +         * Sets the output for the running evaluation (it's {@code System.out}).
  37.132           * The controlling process and
  37.133 -         * the remote process can share <code>System.out</code>.
  37.134 +         * the remote process can share {@code System.out}.
  37.135           * <p>
  37.136 -         * The default, if this is not set, is <code>System.out</code>.
  37.137 +         * The default, if this is not set, is {@code System.out}.
  37.138           *
  37.139 -         * @param out the <code>PrintStream</code> to be channelled to
  37.140 -         * <code>System.out</code> in the remote execution process.
  37.141 -         * @return the <code>Builder</code> instance (for use in chained
  37.142 -         * initialization).
  37.143 +         * @param out the {@code PrintStream} to be channelled to
  37.144 +         * {@code System.out} in the remote execution process
  37.145 +         * @return the {@code Builder} instance (for use in chained
  37.146 +         * initialization)
  37.147           */
  37.148          public Builder out(PrintStream out) {
  37.149              this.out = out;
  37.150 @@ -174,16 +189,16 @@
  37.151          }
  37.152  
  37.153          /**
  37.154 -         * Error output for the running evaluation (it's
  37.155 -         * <code>System.err</code>). The controlling process and the remote
  37.156 -         * process can share <code>System.err</code>.
  37.157 +         * Sets the error output for the running evaluation (it's
  37.158 +         * {@code System.err}). The controlling process and the remote
  37.159 +         * process can share {@code System.err}.
  37.160           * <p>
  37.161 -         * The default, if this is not set, is <code>System.err</code>.
  37.162 +         * The default, if this is not set, is {@code System.err}.
  37.163           *
  37.164 -         * @param err the <code>PrintStream</code> to be channelled to
  37.165 -         * <code>System.err</code> in the remote execution process.
  37.166 -         * @return the <code>Builder</code> instance (for use in chained
  37.167 -         * initialization).
  37.168 +         * @param err the {@code PrintStream} to be channelled to
  37.169 +         * {@code System.err} in the remote execution process
  37.170 +         * @return the {@code Builder} instance (for use in chained
  37.171 +         * initialization)
  37.172           */
  37.173          public Builder err(PrintStream err) {
  37.174              this.err = err;
  37.175 @@ -191,7 +206,7 @@
  37.176          }
  37.177  
  37.178          /**
  37.179 -         * Set a generator of temp variable names for
  37.180 +         * Sets a generator of temp variable names for
  37.181           * {@link jdk.jshell.VarSnippet} of
  37.182           * {@link jdk.jshell.Snippet.SubKind#TEMP_VAR_EXPRESSION_SUBKIND}.
  37.183           * <p>
  37.184 @@ -212,9 +227,9 @@
  37.185           * prefixing dollar sign ("$").
  37.186           *
  37.187           * @param generator the <code>Supplier</code> to generate the temporary
  37.188 -         * variable name string or <code>null</code>.
  37.189 -         * @return the <code>Builder</code> instance (for use in chained
  37.190 -         * initialization).
  37.191 +         * variable name string or <code>null</code>
  37.192 +         * @return the {@code Builder} instance (for use in chained
  37.193 +         * initialization)
  37.194           */
  37.195          public Builder tempVariableNameGenerator(Supplier<String> generator) {
  37.196              this.tempVariableNameGenerator = generator;
  37.197 @@ -222,7 +237,7 @@
  37.198          }
  37.199  
  37.200          /**
  37.201 -         * Set the generator of identifying names for Snippets.
  37.202 +         * Sets the generator of identifying names for Snippets.
  37.203           * <p>
  37.204           * Do not use this method unless you have explicit need for it.
  37.205           * <p>
  37.206 @@ -249,9 +264,9 @@
  37.207           * is null) is to generate the id as the integer converted to a string.
  37.208           *
  37.209           * @param generator the <code>BiFunction</code> to generate the id
  37.210 -         * string or <code>null</code>.
  37.211 -         * @return the <code>Builder</code> instance (for use in chained
  37.212 -         * initialization).
  37.213 +         * string or <code>null</code>
  37.214 +         * @return the {@code Builder} instance (for use in chained
  37.215 +         * initialization)
  37.216           */
  37.217          public Builder idGenerator(BiFunction<Snippet, Integer, String> generator) {
  37.218              this.idGenerator = generator;
  37.219 @@ -259,11 +274,36 @@
  37.220          }
  37.221  
  37.222          /**
  37.223 -         * Build a JShell state engine. This is the entry-point to all JShell
  37.224 +         * Sets additional VM options for launching the VM.
  37.225 +         *
  37.226 +         * @param options The options for the remote VM
  37.227 +         * @return the {@code Builder} instance (for use in chained
  37.228 +         * initialization)
  37.229 +         */
  37.230 +        public Builder remoteVMOptions(String... options) {
  37.231 +            this.extraRemoteVMOptions.addAll(Arrays.asList(options));
  37.232 +            return this;
  37.233 +        }
  37.234 +
  37.235 +        /**
  37.236 +         * Sets the custom engine for execution. Snippet execution will be
  37.237 +         * provided by the specified {@link ExecutionControl} instance.
  37.238 +         *
  37.239 +         * @param execEngine the execution engine
  37.240 +         * @return the {@code Builder} instance (for use in chained
  37.241 +         * initialization)
  37.242 +         */
  37.243 +        public Builder executionEngine(ExecutionControl execEngine) {
  37.244 +            this.executionControl = execEngine;
  37.245 +            return this;
  37.246 +        }
  37.247 +
  37.248 +        /**
  37.249 +         * Builds a JShell state engine. This is the entry-point to all JShell
  37.250           * functionality. This creates a remote process for execution. It is
  37.251           * thus important to close the returned instance.
  37.252           *
  37.253 -         * @return the state engine.
  37.254 +         * @return the state engine
  37.255           */
  37.256          public JShell build() {
  37.257              return new JShell(this);
  37.258 @@ -344,10 +384,20 @@
  37.259       * @see JShell#onShutdown(java.util.function.Consumer)
  37.260       */
  37.261      public List<SnippetEvent> eval(String input) throws IllegalStateException {
  37.262 -        checkIfAlive();
  37.263 -        List<SnippetEvent> events = eval.eval(input);
  37.264 -        events.forEach(this::notifyKeyStatusEvent);
  37.265 -        return Collections.unmodifiableList(events);
  37.266 +        SourceCodeAnalysisImpl a = sourceCodeAnalysis;
  37.267 +        if (a != null) {
  37.268 +            a.suspendIndexing();
  37.269 +        }
  37.270 +        try {
  37.271 +            checkIfAlive();
  37.272 +            List<SnippetEvent> events = eval.eval(input);
  37.273 +            events.forEach(this::notifyKeyStatusEvent);
  37.274 +            return Collections.unmodifiableList(events);
  37.275 +        } finally {
  37.276 +            if (a != null) {
  37.277 +                a.resumeIndexing();
  37.278 +            }
  37.279 +        }
  37.280      }
  37.281  
  37.282      /**
  37.283 @@ -370,12 +420,12 @@
  37.284      /**
  37.285       * The specified path is added to the end of the classpath used in eval().
  37.286       * Note that the unnamed package is not accessible from the package in which
  37.287 -     * {@link JShell#eval()} code is placed.
  37.288 +     * {@link JShell#eval(String)} code is placed.
  37.289       * @param path the path to add to the classpath.
  37.290       */
  37.291      public void addToClasspath(String path) {
  37.292          taskFactory.addToClasspath(path);  // Compiler
  37.293 -        executionControl().commandAddToClasspath(path);       // Runtime
  37.294 +        executionControl().addToClasspath(path);       // Runtime
  37.295          if (sourceCodeAnalysis != null) {
  37.296              sourceCodeAnalysis.classpathChanged();
  37.297          }
  37.298 @@ -396,7 +446,7 @@
  37.299       */
  37.300      public void stop() {
  37.301          if (executionControl != null)
  37.302 -            executionControl.commandStop();
  37.303 +            executionControl.stop();
  37.304      }
  37.305  
  37.306      /**
  37.307 @@ -407,7 +457,7 @@
  37.308      public void close() {
  37.309          if (!closed) {
  37.310              closeDown();
  37.311 -            executionControl().commandExit();
  37.312 +            executionControl().close();
  37.313          }
  37.314      }
  37.315  
  37.316 @@ -417,7 +467,7 @@
  37.317       * @throws IllegalStateException if this JShell instance is closed.
  37.318       */
  37.319      public List<Snippet> snippets() throws IllegalStateException {
  37.320 -        //checkIfAlive();
  37.321 +        checkIfAlive();
  37.322          return Collections.unmodifiableList(maps.snippetList());
  37.323      }
  37.324  
  37.325 @@ -546,10 +596,10 @@
  37.326          checkIfAlive();
  37.327          checkValidSnippet(snippet);
  37.328          if (snippet.status() != Status.VALID) {
  37.329 -            throw new IllegalArgumentException("Snippet parameter of varValue() '" +
  37.330 -                    snippet + "' must be VALID, it is: " + snippet.status());
  37.331 +            throw new IllegalArgumentException(
  37.332 +                    messageFormat("jshell.exc.var.not.valid",  snippet, snippet.status()));
  37.333          }
  37.334 -        String value = executionControl().commandVarValue(maps.classFullName(snippet), snippet.name());
  37.335 +        String value = executionControl().varValue(snippet.classFullName(), snippet.name());
  37.336          return expunge(value);
  37.337      }
  37.338  
  37.339 @@ -602,39 +652,86 @@
  37.340          }
  37.341      }
  37.342  
  37.343 +    /**
  37.344 +     * Provide the environment for a execution engine.
  37.345 +     */
  37.346 +    class ExecutionEnvImpl implements ExecutionEnv {
  37.347 +
  37.348 +        @Override
  37.349 +        public InputStream userIn() {
  37.350 +            return in;
  37.351 +        }
  37.352 +
  37.353 +        @Override
  37.354 +        public PrintStream userOut() {
  37.355 +            return out;
  37.356 +        }
  37.357 +
  37.358 +        @Override
  37.359 +        public PrintStream userErr() {
  37.360 +            return err;
  37.361 +        }
  37.362 +
  37.363 +        @Override
  37.364 +        public JShell state() {
  37.365 +            return JShell.this;
  37.366 +        }
  37.367 +
  37.368 +        @Override
  37.369 +        public List<String> extraRemoteVMOptions() {
  37.370 +            return extraRemoteVMOptions;
  37.371 +        }
  37.372 +
  37.373 +        @Override
  37.374 +        public byte[] getClassBytes(String classname) {
  37.375 +            return classnameToBytes.get(classname);
  37.376 +        }
  37.377 +
  37.378 +        @Override
  37.379 +        public EvalException createEvalException(String message, String exceptionClass, StackTraceElement[] stackElements) {
  37.380 +            return new EvalException(message, exceptionClass, stackElements);
  37.381 +        }
  37.382 +
  37.383 +        @Override
  37.384 +        public UnresolvedReferenceException createUnresolvedReferenceException(int id, StackTraceElement[] stackElements) {
  37.385 +            DeclarationSnippet sn = (DeclarationSnippet) maps.getSnippetDeadOrAlive(id);
  37.386 +            return new UnresolvedReferenceException(sn, stackElements);
  37.387 +        }
  37.388 +
  37.389 +        @Override
  37.390 +        public void closeDown() {
  37.391 +            JShell.this.closeDown();
  37.392 +        }
  37.393 +    }
  37.394 +
  37.395      // --- private / package-private implementation support ---
  37.396 -    
  37.397 -    protected ExecutionControl createExecutionControl() {
  37.398 -        return new ExecutionControl(new JDIEnv(this), maps, this);
  37.399 -    }
  37.400 -    
  37.401      ExecutionControl executionControl() {
  37.402 -        if (executionControl == null) {
  37.403 -            this.executionControl = createExecutionControl();
  37.404 +        if (!executionControlLaunched) {
  37.405              try {
  37.406 -                executionControl.launch();
  37.407 -            } catch (IOException ex) {
  37.408 -                throw new InternalError("Launching JDI execution engine threw: " + ex.getMessage(), ex);
  37.409 +                executionControlLaunched = true;
  37.410 +                executionControl.start(new ExecutionEnvImpl());
  37.411 +            } catch (Throwable ex) {
  37.412 +                throw new InternalError("Launching execution engine threw: " + ex.getMessage(), ex);
  37.413              }
  37.414          }
  37.415          return executionControl;
  37.416      }
  37.417  
  37.418 +    void setClassnameToBytes(String classname, byte[] bytes) {
  37.419 +        classnameToBytes.put(classname, bytes);
  37.420 +    }
  37.421 +
  37.422      void debug(int flags, String format, Object... args) {
  37.423 -        if (InternalDebugControl.debugEnabled(this, flags)) {
  37.424 -            err.printf(format, args);
  37.425 -        }
  37.426 +        InternalDebugControl.debug(this, err, flags, format, args);
  37.427      }
  37.428  
  37.429      void debug(Exception ex, String where) {
  37.430 -        if (InternalDebugControl.debugEnabled(this, 0xFFFFFFFF)) {
  37.431 -            err.printf("Fatal error: %s: %s\n", where, ex.getMessage());
  37.432 -            ex.printStackTrace(err);
  37.433 -        }
  37.434 +        InternalDebugControl.debug(this, err, ex, where);
  37.435      }
  37.436  
  37.437      /**
  37.438       * Generate the next key index, indicating a unique snippet signature.
  37.439 +     *
  37.440       * @return the next key index
  37.441       */
  37.442      int nextKeyIndex() {
  37.443 @@ -672,7 +769,7 @@
  37.444       */
  37.445      private void checkIfAlive()  throws IllegalStateException {
  37.446          if (closed) {
  37.447 -            throw new IllegalStateException("JShell (" + this + ") has been closed.");
  37.448 +            throw new IllegalStateException(messageFormat("jshell.exc.closed", this));
  37.449          }
  37.450      }
  37.451  
  37.452 @@ -685,16 +782,36 @@
  37.453       */
  37.454      private Snippet checkValidSnippet(Snippet sn) {
  37.455          if (sn == null) {
  37.456 -            throw new NullPointerException("Snippet must not be null");
  37.457 +            throw new NullPointerException(messageFormat("jshell.exc.null"));
  37.458          } else {
  37.459              if (sn.key().state() != this) {
  37.460 -                throw new IllegalArgumentException("Snippet not from this JShell");
  37.461 +                throw new IllegalArgumentException(messageFormat("jshell.exc.alien"));
  37.462              }
  37.463              return sn;
  37.464          }
  37.465      }
  37.466  
  37.467 -    protected List<String> getCompilerOptions() {
  37.468 -        return Collections.emptyList();
  37.469 +    /**
  37.470 +     * Format using resource bundle look-up using MessageFormat
  37.471 +     *
  37.472 +     * @param key the resource key
  37.473 +     * @param args
  37.474 +     */
  37.475 +    String messageFormat(String key, Object... args) {
  37.476 +        if (outputRB == null) {
  37.477 +            try {
  37.478 +                outputRB = ResourceBundle.getBundle(L10N_RB_NAME);
  37.479 +            } catch (MissingResourceException mre) {
  37.480 +                throw new InternalError("Cannot find ResourceBundle: " + L10N_RB_NAME);
  37.481 +            }
  37.482 +        }
  37.483 +        String s;
  37.484 +        try {
  37.485 +            s = outputRB.getString(key);
  37.486 +        } catch (MissingResourceException mre) {
  37.487 +            throw new InternalError("Missing resource: " + key + " in " + L10N_RB_NAME);
  37.488 +        }
  37.489 +        return MessageFormat.format(s, args);
  37.490      }
  37.491 +
  37.492  }
    38.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/JShellException.java	Fri Sep 02 23:54:26 2016 +0200
    38.3 @@ -0,0 +1,37 @@
    38.4 +/*
    38.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    38.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    38.7 + *
    38.8 + * This code is free software; you can redistribute it and/or modify it
    38.9 + * under the terms of the GNU General Public License version 2 only, as
   38.10 + * published by the Free Software Foundation.  Oracle designates this
   38.11 + * particular file as subject to the "Classpath" exception as provided
   38.12 + * by Oracle in the LICENSE file that accompanied this code.
   38.13 + *
   38.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   38.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   38.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   38.17 + * version 2 for more details (a copy is included in the LICENSE file that
   38.18 + * accompanied this code).
   38.19 + *
   38.20 + * You should have received a copy of the GNU General Public License version
   38.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   38.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   38.23 + *
   38.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   38.25 + * or visit www.oracle.com if you need additional information or have any
   38.26 + * questions.
   38.27 + */
   38.28 +
   38.29 +package jdk.jshell;
   38.30 +
   38.31 +/**
   38.32 + * The superclass of JShell generated exceptions
   38.33 + */
   38.34 +@SuppressWarnings("serial")             // serialVersionUID intentionally omitted
   38.35 +public class JShellException extends Exception {
   38.36 +
   38.37 +    JShellException(String message) {
   38.38 +        super(message);
   38.39 +    }
   38.40 +}
    39.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/MaskCommentsAndModifiers.java	Fri Jun 03 17:06:38 2016 +0200
    39.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/MaskCommentsAndModifiers.java	Fri Sep 02 23:54:26 2016 +0200
    39.3 @@ -22,6 +22,7 @@
    39.4   * or visit www.oracle.com if you need additional information or have any
    39.5   * questions.
    39.6   */
    39.7 +
    39.8  package jdk.jshell;
    39.9  
   39.10  import java.util.Set;
    40.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/MemoryFileManager.java	Fri Jun 03 17:06:38 2016 +0200
    40.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/MemoryFileManager.java	Fri Sep 02 23:54:26 2016 +0200
    40.3 @@ -1,5 +1,5 @@
    40.4  /*
    40.5 - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
    40.6 + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
    40.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    40.8   *
    40.9   * This code is free software; you can redistribute it and/or modify it
   40.10 @@ -231,6 +231,7 @@
   40.11      }
   40.12  
   40.13      // Make compatible with Jigsaw
   40.14 +    @DefinedBy(Api.COMPILER)
   40.15      public String inferModuleName(Location location) {
   40.16          try {
   40.17              if (inferModuleNameMethod == null) {
   40.18 @@ -249,6 +250,7 @@
   40.19      }
   40.20  
   40.21      // Make compatible with Jigsaw
   40.22 +    @DefinedBy(Api.COMPILER)
   40.23      public Iterable<Set<Location>> listModuleLocations(Location location) throws IOException {
   40.24          try {
   40.25              if (listModuleLocationsMethod == null) {
    41.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/MethodSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    41.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/MethodSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    41.3 @@ -22,6 +22,7 @@
    41.4   * or visit www.oracle.com if you need additional information or have any
    41.5   * questions.
    41.6   */
    41.7 +
    41.8  package jdk.jshell;
    41.9  
   41.10  import java.util.Collection;
    42.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/OuterImportSnippetWrap.java	Fri Sep 02 23:54:26 2016 +0200
    42.3 @@ -0,0 +1,61 @@
    42.4 +/*
    42.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    42.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    42.7 + *
    42.8 + * This code is free software; you can redistribute it and/or modify it
    42.9 + * under the terms of the GNU General Public License version 2 only, as
   42.10 + * published by the Free Software Foundation.  Oracle designates this
   42.11 + * particular file as subject to the "Classpath" exception as provided
   42.12 + * by Oracle in the LICENSE file that accompanied this code.
   42.13 + *
   42.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   42.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   42.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   42.17 + * version 2 for more details (a copy is included in the LICENSE file that
   42.18 + * accompanied this code).
   42.19 + *
   42.20 + * You should have received a copy of the GNU General Public License version
   42.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   42.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   42.23 + *
   42.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   42.25 + * or visit www.oracle.com if you need additional information or have any
   42.26 + * questions.
   42.27 + */
   42.28 +
   42.29 +package jdk.jshell;
   42.30 +
   42.31 +import java.util.IdentityHashMap;
   42.32 +import java.util.List;
   42.33 +import javax.tools.Diagnostic;
   42.34 +import javax.tools.JavaFileObject;
   42.35 +
   42.36 +/**
   42.37 + * The outer wrap for a set of snippets wrapped in a generated class
   42.38 + * @author Robert Field
   42.39 + */
   42.40 +public class OuterImportSnippetWrap extends OuterWrap {
   42.41 +
   42.42 +    private final Snippet snippet;
   42.43 +
   42.44 +    OuterImportSnippetWrap(Wrap wrap, Snippet snippet) {
   42.45 +        super(wrap);
   42.46 +        this.snippet = snippet;
   42.47 +    }
   42.48 +
   42.49 +    @Override
   42.50 +    Diag wrapDiag(Diagnostic<? extends JavaFileObject> d) {
   42.51 +        return new WrappedDiagnostic(d) {
   42.52 +
   42.53 +            @Override
   42.54 +            Snippet snippetOrNull() {
   42.55 +                return snippet;
   42.56 +            }
   42.57 +        };
   42.58 +    }
   42.59 +
   42.60 +    @Override
   42.61 +    public String toString() {
   42.62 +        return "OISW(" + w + ")";
   42.63 +    }
   42.64 +}
    43.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/OuterSnippetsClassWrap.java	Fri Sep 02 23:54:26 2016 +0200
    43.3 @@ -0,0 +1,95 @@
    43.4 +/*
    43.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    43.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    43.7 + *
    43.8 + * This code is free software; you can redistribute it and/or modify it
    43.9 + * under the terms of the GNU General Public License version 2 only, as
   43.10 + * published by the Free Software Foundation.  Oracle designates this
   43.11 + * particular file as subject to the "Classpath" exception as provided
   43.12 + * by Oracle in the LICENSE file that accompanied this code.
   43.13 + *
   43.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   43.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   43.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   43.17 + * version 2 for more details (a copy is included in the LICENSE file that
   43.18 + * accompanied this code).
   43.19 + *
   43.20 + * You should have received a copy of the GNU General Public License version
   43.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   43.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   43.23 + *
   43.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   43.25 + * or visit www.oracle.com if you need additional information or have any
   43.26 + * questions.
   43.27 + */
   43.28 +
   43.29 +package jdk.jshell;
   43.30 +
   43.31 +import java.util.LinkedHashMap;
   43.32 +import java.util.List;
   43.33 +import javax.tools.Diagnostic;
   43.34 +import javax.tools.JavaFileObject;
   43.35 +import jdk.jshell.Wrap.CompoundWrap;
   43.36 +
   43.37 +/**
   43.38 + * The outer wrap for a set of snippets wrapped in a generated class
   43.39 + * @author Robert Field
   43.40 + */
   43.41 +public class OuterSnippetsClassWrap extends OuterWrap {
   43.42 +
   43.43 +    private final String className;
   43.44 +    private final LinkedHashMap<Wrap, Snippet> wrapToSnippet;
   43.45 +
   43.46 +    OuterSnippetsClassWrap(String className, CompoundWrap w, List<Snippet> snippets, List<Wrap> wraps) {
   43.47 +        super(w);
   43.48 +        assert snippets == null || snippets.size() == wraps.size();
   43.49 +        this.className = className;
   43.50 +        wrapToSnippet = new LinkedHashMap<>();
   43.51 +        for (int i = 0; i < snippets.size(); ++i) {
   43.52 +            wrapToSnippet.put(wraps.get(i), snippets.get(i));
   43.53 +        }
   43.54 +    }
   43.55 +
   43.56 +    public Snippet wrapLineToSnippet(int wline) {
   43.57 +        Wrap wrap = ((CompoundWrap)w).wrapLineToWrap(wline);
   43.58 +        return wrapToSnippet.get(wrap);
   43.59 +    }
   43.60 +
   43.61 +    @Override
   43.62 +    Diag wrapDiag(Diagnostic<? extends JavaFileObject> d) {
   43.63 +        return new WrappedDiagnostic(d) {
   43.64 +
   43.65 +            @Override
   43.66 +            Snippet snippetOrNull() {
   43.67 +                Wrap wrap = ((CompoundWrap) w).wrapIndexToWrap(diag.getPosition());
   43.68 +                Snippet sn = wrapToSnippet.get(wrap);
   43.69 +                if (sn != null) {
   43.70 +                    return sn;
   43.71 +                } else {
   43.72 +                    return super.snippetOrNull();
   43.73 +                }
   43.74 +            }
   43.75 +        };
   43.76 +    }
   43.77 +
   43.78 +    int ordinal(Snippet sn) {
   43.79 +        int i = 0;
   43.80 +        for (Snippet si : wrapToSnippet.values()) {
   43.81 +            if (si == sn) {
   43.82 +                return i;
   43.83 +            }
   43.84 +            ++i;
   43.85 +        }
   43.86 +        return -1;
   43.87 +    }
   43.88 +
   43.89 +    @Override
   43.90 +    public String className() {
   43.91 +        return className;
   43.92 +    }
   43.93 +
   43.94 +    @Override
   43.95 +    public String toString() {
   43.96 +        return "OSCW(" + className + ":" + w + ")";
   43.97 +    }
   43.98 +}
    44.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/OuterWrap.java	Fri Jun 03 17:06:38 2016 +0200
    44.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/OuterWrap.java	Fri Sep 02 23:54:26 2016 +0200
    44.3 @@ -1,5 +1,5 @@
    44.4  /*
    44.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    44.6 + * Copyright (c) 2015-2016, Oracle and/or its affiliates. All rights reserved.
    44.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    44.8   *
    44.9   * This code is free software; you can redistribute it and/or modify it
   44.10 @@ -22,48 +22,28 @@
   44.11   * or visit www.oracle.com if you need additional information or have any
   44.12   * questions.
   44.13   */
   44.14 +
   44.15  package jdk.jshell;
   44.16  
   44.17 -import jdk.jshell.Wrap.CompoundWrap;
   44.18 -import static jdk.jshell.Util.*;
   44.19  import java.util.Locale;
   44.20  import javax.tools.Diagnostic;
   44.21  import javax.tools.JavaFileObject;
   44.22 -import jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject;
   44.23 +import static jdk.jshell.Util.PARSED_LOCALE;
   44.24 +import static jdk.jshell.Util.REPL_CLASS_PREFIX;
   44.25 +import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
   44.26 +import static jdk.jshell.Util.REPL_PACKAGE;
   44.27 +import static jdk.jshell.Util.expunge;
   44.28  
   44.29  /**
   44.30   *
   44.31   * @author Robert Field
   44.32   */
   44.33 -final class OuterWrap implements GeneralWrap {
   44.34 +class OuterWrap implements GeneralWrap {
   44.35  
   44.36 -    private final String packageName;
   44.37 -    private final String className;
   44.38 -    private final String userSource;
   44.39 -    private final GeneralWrap w;
   44.40 -    private final Wrap guts;
   44.41 +    protected final Wrap w;
   44.42  
   44.43 -    public static OuterWrap wrapInClass(String packageName, String className,
   44.44 -             String imports, String userSource, Wrap guts) {
   44.45 -        GeneralWrap kw = new CompoundWrap(
   44.46 -                imports
   44.47 -                + "class " + className + " {\n",
   44.48 -                guts,
   44.49 -                "}\n");
   44.50 -        return new OuterWrap(packageName, className, userSource, kw, guts);
   44.51 -    }
   44.52 -
   44.53 -    public static OuterWrap wrapImport(String userSource, Wrap guts) {
   44.54 -        return new OuterWrap("", "", userSource, guts, guts);
   44.55 -    }
   44.56 -
   44.57 -    private OuterWrap(String packageName, String className, String userSource,
   44.58 -            GeneralWrap w, Wrap guts) {
   44.59 -        this.packageName = packageName;
   44.60 -        this.className = className;
   44.61 -        this.userSource = userSource;
   44.62 -        this.w = w;
   44.63 -        this.guts = guts;
   44.64 +    OuterWrap(Wrap wrap) {
   44.65 +        this.w = wrap;
   44.66      }
   44.67  
   44.68      @Override
   44.69 @@ -112,19 +92,28 @@
   44.70      }
   44.71  
   44.72      public String className() {
   44.73 -        return className;
   44.74 +        return REPL_DOESNOTMATTER_CLASS_NAME;
   44.75      }
   44.76  
   44.77      public String classFullName() {
   44.78 -        return packageName + "." + className;
   44.79 +        return REPL_PACKAGE + "." + className();
   44.80      }
   44.81  
   44.82 -    public String getUserSource() {
   44.83 -        return userSource;
   44.84 +    @Override
   44.85 +    public int hashCode() {
   44.86 +        return className().hashCode();
   44.87      }
   44.88  
   44.89 -    Wrap guts() {
   44.90 -        return guts;
   44.91 +    @Override
   44.92 +    public boolean equals(Object o) {
   44.93 +        return (o instanceof OuterWrap)
   44.94 +                ? className().equals(((OuterWrap) o).className())
   44.95 +                : false;
   44.96 +    }
   44.97 +
   44.98 +    @Override
   44.99 +    public String toString() {
  44.100 +        return "OW(" + w + ")";
  44.101      }
  44.102  
  44.103      Diag wrapDiag(Diagnostic<? extends JavaFileObject> d) {
  44.104 @@ -133,7 +122,7 @@
  44.105  
  44.106      class WrappedDiagnostic extends Diag {
  44.107  
  44.108 -        private final Diagnostic<? extends JavaFileObject> diag;
  44.109 +        final Diagnostic<? extends JavaFileObject> diag;
  44.110  
  44.111          WrappedDiagnostic(Diagnostic<? extends JavaFileObject> diag) {
  44.112              this.diag = diag;
  44.113 @@ -170,20 +159,24 @@
  44.114          }
  44.115  
  44.116          @Override
  44.117 -        Unit unitOrNull() {
  44.118 -            JavaFileObject fo = diag.getSource();
  44.119 -            if (fo instanceof SourceMemoryJavaFileObject) {
  44.120 -                SourceMemoryJavaFileObject sfo = (SourceMemoryJavaFileObject) fo;
  44.121 -                if (sfo.getOrigin() instanceof Unit) {
  44.122 -                    return (Unit) sfo.getOrigin();
  44.123 +        boolean isResolutionError() {
  44.124 +            if (!super.isResolutionError()) {
  44.125 +                return false;
  44.126 +            }
  44.127 +            for (String line : diag.getMessage(PARSED_LOCALE).split("\\r?\\n")) {
  44.128 +                if (line.trim().startsWith("location:")) {
  44.129 +                    if (!line.contains(REPL_CLASS_PREFIX)) {
  44.130 +                        // Resolution error must occur within a REPL class or it is not resolvable
  44.131 +                        return false;
  44.132 +                    }
  44.133                  }
  44.134              }
  44.135 -            return null;
  44.136 +            return true;
  44.137          }
  44.138  
  44.139 -    @Override
  44.140 -    public String toString() {
  44.141 -        return "WrappedDiagnostic(" + getMessage(null) + ":" + getPosition() + ")";
  44.142 -    }
  44.143 +        @Override
  44.144 +        public String toString() {
  44.145 +            return "WrappedDiagnostic(" + getMessage(null) + ":" + getPosition() + ")";
  44.146 +        }
  44.147      }
  44.148  }
    45.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    45.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/OuterWrapMap.java	Fri Sep 02 23:54:26 2016 +0200
    45.3 @@ -0,0 +1,98 @@
    45.4 +/*
    45.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    45.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    45.7 + *
    45.8 + * This code is free software; you can redistribute it and/or modify it
    45.9 + * under the terms of the GNU General Public License version 2 only, as
   45.10 + * published by the Free Software Foundation.  Oracle designates this
   45.11 + * particular file as subject to the "Classpath" exception as provided
   45.12 + * by Oracle in the LICENSE file that accompanied this code.
   45.13 + *
   45.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   45.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   45.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   45.17 + * version 2 for more details (a copy is included in the LICENSE file that
   45.18 + * accompanied this code).
   45.19 + *
   45.20 + * You should have received a copy of the GNU General Public License version
   45.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   45.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   45.23 + *
   45.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   45.25 + * or visit www.oracle.com if you need additional information or have any
   45.26 + * questions.
   45.27 + */
   45.28 +
   45.29 +package jdk.jshell;
   45.30 +
   45.31 +import java.util.ArrayList;
   45.32 +import java.util.Collection;
   45.33 +import java.util.Collections;
   45.34 +import java.util.HashMap;
   45.35 +import java.util.List;
   45.36 +import java.util.Map;
   45.37 +import java.util.Set;
   45.38 +import java.util.regex.Matcher;
   45.39 +import java.util.stream.Collectors;
   45.40 +import jdk.jshell.Wrap.CompoundWrap;
   45.41 +import static jdk.jshell.Util.PREFIX_PATTERN;
   45.42 +import static jdk.jshell.Util.REPL_CLASS_PREFIX;
   45.43 +import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
   45.44 +import static jdk.jshell.Util.asLetters;
   45.45 +
   45.46 +/**
   45.47 + *
   45.48 + * @author Robert Field
   45.49 + */
   45.50 +public class OuterWrapMap {
   45.51 +
   45.52 +    private final JShell state;
   45.53 +    private final Map<String,OuterSnippetsClassWrap> classOuters = new HashMap<>();
   45.54 +
   45.55 +    OuterWrapMap(JShell state) {
   45.56 +        this.state = state;
   45.57 +    }
   45.58 +
   45.59 +    OuterSnippetsClassWrap getOuter(String className) {
   45.60 +        Matcher matcher = PREFIX_PATTERN.matcher(className);
   45.61 +        String cn;
   45.62 +        if (matcher.find() && (cn = matcher.group("class")) != null) {
   45.63 +            return classOuters.get(cn);
   45.64 +        }
   45.65 +        return null;
   45.66 +    }
   45.67 +
   45.68 +    private CompoundWrap wrappedInClass(String className, String imports, List<Wrap> wraps) {
   45.69 +        List<Object> elems = new ArrayList<>(wraps.size() + 2);
   45.70 +        elems.add(imports +
   45.71 +                "class " + className + " {\n");
   45.72 +        elems.addAll(wraps);
   45.73 +        elems.add("}\n");
   45.74 +        return new CompoundWrap(elems.toArray());
   45.75 +    }
   45.76 +
   45.77 +    OuterWrap wrapInClass(Set<Key> except, Collection<Snippet> plus,
   45.78 +            List<Snippet> snippets, List<Wrap> wraps) {
   45.79 +        String imports = state.maps.packageAndImportsExcept(except, plus);
   45.80 +        // className is unique to the set of snippets and their version (seq)
   45.81 +        String className = REPL_CLASS_PREFIX + snippets.stream()
   45.82 +                .sorted((sn1, sn2) -> sn1.key().index() - sn2.key().index())
   45.83 +                .map(sn -> "" + sn.key().index() + asLetters(sn.sequenceNumber()))
   45.84 +                .collect(Collectors.joining("_"));
   45.85 +        CompoundWrap w = wrappedInClass(className, imports, wraps);
   45.86 +        OuterSnippetsClassWrap now = new OuterSnippetsClassWrap(className, w, snippets, wraps);
   45.87 +        classOuters.put(className, now);
   45.88 +        return now;
   45.89 +    }
   45.90 +
   45.91 +    OuterWrap wrapInTrialClass(Wrap wrap) {
   45.92 +        String imports = state.maps.packageAndImportsExcept(null, null);
   45.93 +        CompoundWrap w = wrappedInClass(REPL_DOESNOTMATTER_CLASS_NAME, imports,
   45.94 +                Collections.singletonList(wrap));
   45.95 +        return new OuterWrap(w);
   45.96 +    }
   45.97 +
   45.98 +    OuterWrap wrapImport(Wrap guts, Snippet sn) {
   45.99 +        return new OuterImportSnippetWrap(guts, sn);
  45.100 +    }
  45.101 +}
    46.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/PersistentSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    46.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/PersistentSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    46.3 @@ -22,6 +22,7 @@
    46.4   * or visit www.oracle.com if you need additional information or have any
    46.5   * questions.
    46.6   */
    46.7 +
    46.8  package jdk.jshell;
    46.9  
   46.10  /**
    47.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Fri Jun 03 17:06:38 2016 +0200
    47.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Fri Sep 02 23:54:26 2016 +0200
    47.3 @@ -22,6 +22,7 @@
    47.4   * or visit www.oracle.com if you need additional information or have any
    47.5   * questions.
    47.6   */
    47.7 +
    47.8  package jdk.jshell;
    47.9  
   47.10  import com.sun.tools.javac.code.TypeTag;
    48.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java	Fri Jun 03 17:06:38 2016 +0200
    48.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java	Fri Sep 02 23:54:26 2016 +0200
    48.3 @@ -22,6 +22,7 @@
    48.4   * or visit www.oracle.com if you need additional information or have any
    48.5   * questions.
    48.6   */
    48.7 +
    48.8  package jdk.jshell;
    48.9  
   48.10  import com.sun.tools.javac.parser.JavacParser;
    49.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java	Fri Jun 03 17:06:38 2016 +0200
    49.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java	Fri Sep 02 23:54:26 2016 +0200
    49.3 @@ -28,8 +28,6 @@
    49.4  import java.util.Collection;
    49.5  import java.util.Collections;
    49.6  import java.util.List;
    49.7 -import static jdk.jshell.Util.REPL_CLASS_PREFIX;
    49.8 -import static jdk.jshell.Util.asLetters;
    49.9  
   49.10  /**
   49.11   * A Snippet represents a snippet of Java source code as passed to
   49.12 @@ -503,7 +501,6 @@
   49.13      private final SubKind subkind;
   49.14  
   49.15      private int seq;
   49.16 -    private String className;
   49.17      private String id;
   49.18      private OuterWrap outer;
   49.19      private Status status;
   49.20 @@ -524,7 +521,9 @@
   49.21  
   49.22      /**
   49.23       * The unique identifier for the snippet. No two active snippets will have
   49.24 -     * the same id().
   49.25 +     * the same id().  Value of id has no prescribed meaning.  The details of
   49.26 +     * how the id is generated and the mechanism to change it is documented in
   49.27 +     * {@link JShell.Builder#idGenerator(BiFunction)}.
   49.28       * @return the snippet id string.
   49.29       */
   49.30      public String id() {
   49.31 @@ -613,7 +612,6 @@
   49.32  
   49.33      final void setSequenceNumber(int seq) {
   49.34          this.seq = seq;
   49.35 -        this.className = REPL_CLASS_PREFIX + key().index() + asLetters(seq);
   49.36      }
   49.37  
   49.38      void setOuterWrap(OuterWrap outer) {
   49.39 @@ -651,7 +649,11 @@
   49.40      }
   49.41  
   49.42      String className() {
   49.43 -        return className;
   49.44 +        return outer.className();
   49.45 +    }
   49.46 +
   49.47 +    String classFullName() {
   49.48 +        return outer.classFullName();
   49.49      }
   49.50  
   49.51      /**
    50.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/SnippetEvent.java	Fri Jun 03 17:06:38 2016 +0200
    50.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/SnippetEvent.java	Fri Sep 02 23:54:26 2016 +0200
    50.3 @@ -44,7 +44,7 @@
    50.4  
    50.5      SnippetEvent(Snippet snippet, Status previousStatus, Status status,
    50.6              boolean isSignatureChange, Snippet causeSnippet,
    50.7 -            String value, Exception exception) {
    50.8 +            String value, JShellException exception) {
    50.9          this.snippet = snippet;
   50.10          this.previousStatus = previousStatus;
   50.11          this.status = status;
   50.12 @@ -60,7 +60,7 @@
   50.13      private final boolean isSignatureChange;
   50.14      private final Snippet causeSnippet;
   50.15      private final String value;
   50.16 -    private final Exception exception;
   50.17 +    private final JShellException exception;
   50.18  
   50.19      /**
   50.20       * The Snippet which has changed
   50.21 @@ -121,7 +121,7 @@
   50.22       * during execution, otherwise <code>null</code>.
   50.23       * @return the exception or <code>null</code>.
   50.24       */
   50.25 -    public Exception exception() {
   50.26 +    public JShellException exception() {
   50.27          return exception;
   50.28      }
   50.29  
    51.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java	Fri Jun 03 17:06:38 2016 +0200
    51.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java	Fri Sep 02 23:54:26 2016 +0200
    51.3 @@ -38,7 +38,8 @@
    51.4  import java.util.stream.Stream;
    51.5  
    51.6  import static java.util.stream.Collectors.toList;
    51.7 -import static jdk.internal.jshell.remote.RemoteCodes.prefixPattern;
    51.8 +import static jdk.jshell.Util.PREFIX_PATTERN;
    51.9 +import static jdk.jshell.Util.REPL_PACKAGE;
   51.10  import static jdk.internal.jshell.debug.InternalDebugControl.DBG_DEP;
   51.11  
   51.12  /**
   51.13 @@ -48,7 +49,6 @@
   51.14   */
   51.15  final class SnippetMaps {
   51.16  
   51.17 -    private String packageName;
   51.18      private final List<Snippet> keyIndexToSnippet = new ArrayList<>();
   51.19      private final Set<Snippet> snippets = new LinkedHashSet<>();
   51.20      private final Map<String, Set<Integer>> dependencies = new HashMap<>();
   51.21 @@ -81,6 +81,13 @@
   51.22      }
   51.23  
   51.24      Snippet getSnippet(int ki) {
   51.25 +        Snippet sn = getSnippetDeadOrAlive(ki);
   51.26 +        return (sn != null && !sn.status().isActive)
   51.27 +                ? null
   51.28 +                : sn;
   51.29 +    }
   51.30 +
   51.31 +    Snippet getSnippetDeadOrAlive(int ki) {
   51.32          if (ki >= keyIndexToSnippet.size()) {
   51.33              return null;
   51.34          }
   51.35 @@ -91,21 +98,9 @@
   51.36          return new ArrayList<>(snippets);
   51.37      }
   51.38  
   51.39 -    void setPackageName(String n) {
   51.40 -        packageName = n;
   51.41 -    }
   51.42 -
   51.43 -    String packageName() {
   51.44 -        return packageName;
   51.45 -    }
   51.46 -
   51.47 -    String classFullName(Snippet sn) {
   51.48 -        return packageName + "." + sn.className();
   51.49 -    }
   51.50 -
   51.51      String packageAndImportsExcept(Set<Key> except, Collection<Snippet> plus) {
   51.52          StringBuilder sb = new StringBuilder();
   51.53 -        sb.append("package ").append(packageName()).append(";\n");
   51.54 +        sb.append("package ").append(REPL_PACKAGE).append(";\n");
   51.55          for (Snippet si : keyIndexToSnippet) {
   51.56              if (si != null && si.status().isDefined && (except == null || !except.contains(si.key()))) {
   51.57                  sb.append(si.importLine(state));
   51.58 @@ -137,7 +132,7 @@
   51.59          }
   51.60          List<Snippet> deps = new ArrayList<>();
   51.61          for (Integer dss : depset) {
   51.62 -            Snippet dep = getSnippet(dss);
   51.63 +            Snippet dep = getSnippetDeadOrAlive(dss);
   51.64              if (dep != null) {
   51.65                  deps.add(dep);
   51.66                  state.debug(DBG_DEP, "Found dependency %s -> %s\n", snip.name(), dep.name());
   51.67 @@ -161,7 +156,7 @@
   51.68      }
   51.69  
   51.70      String fullClassNameAndPackageToClass(String full, String pkg) {
   51.71 -        Matcher mat = prefixPattern.matcher(full);
   51.72 +        Matcher mat = PREFIX_PATTERN.matcher(full);
   51.73          if (mat.lookingAt()) {
   51.74              return full.substring(mat.end());
   51.75          }
   51.76 @@ -195,6 +190,6 @@
   51.77      private Stream<ImportSnippet> importSnippets() {
   51.78          return state.keyMap.importKeys()
   51.79                  .map(key -> (ImportSnippet)getSnippet(key))
   51.80 -                .filter(sn -> state.status(sn).isDefined);
   51.81 +                .filter(sn -> sn != null && state.status(sn).isDefined);
   51.82      }
   51.83  }
    52.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java	Fri Jun 03 17:06:38 2016 +0200
    52.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java	Fri Sep 02 23:54:26 2016 +0200
    52.3 @@ -70,6 +70,28 @@
    52.4      public abstract String documentation(String input, int cursor);
    52.5  
    52.6      /**
    52.7 +     * Infer the type of the given expression. The expression spans from the beginning of {@code code}
    52.8 +     * to the given {@code cursor} position. Returns null if the type of the expression cannot
    52.9 +     * be inferred.
   52.10 +     *
   52.11 +     * @param code the expression for which the type should be inferred
   52.12 +     * @param cursor current cursor position in the given code
   52.13 +     * @return the inferred type, or null if it cannot be inferred
   52.14 +     */
   52.15 +    public abstract String analyzeType(String code, int cursor);
   52.16 +
   52.17 +    /**
   52.18 +     * List qualified names known for the simple name in the given code immediately
   52.19 +     * to the left of the given cursor position. The qualified names are gathered by inspecting the
   52.20 +     * classpath used by eval (see {@link JShell#addToClasspath(java.lang.String)}).
   52.21 +     *
   52.22 +     * @param code the expression for which the candidate qualified names should be computed
   52.23 +     * @param cursor current cursor position in the given code
   52.24 +     * @return the known qualified names
   52.25 +     */
   52.26 +    public abstract QualifiedNames listQualifiedNames(String code, int cursor);
   52.27 +
   52.28 +    /**
   52.29       * Internal only constructor
   52.30       */
   52.31      SourceCodeAnalysis() {}
   52.32 @@ -80,7 +102,7 @@
   52.33       */
   52.34      public static class CompletionInfo {
   52.35  
   52.36 -        public CompletionInfo(Completeness completeness, int unitEndPos, String source, String remaining) {
   52.37 +        CompletionInfo(Completeness completeness, int unitEndPos, String source, String remaining) {
   52.38              this.completeness = completeness;
   52.39              this.unitEndPos = unitEndPos;
   52.40              this.source = source;
   52.41 @@ -198,4 +220,65 @@
   52.42           */
   52.43          public final boolean isSmart;
   52.44      }
   52.45 +
   52.46 +    /**
   52.47 +     * List of possible qualified names.
   52.48 +     */
   52.49 +    public static final class QualifiedNames {
   52.50 +
   52.51 +        private final List<String> names;
   52.52 +        private final int simpleNameLength;
   52.53 +        private final boolean upToDate;
   52.54 +        private final boolean resolvable;
   52.55 +
   52.56 +        QualifiedNames(List<String> names, int simpleNameLength, boolean upToDate, boolean resolvable) {
   52.57 +            this.names = names;
   52.58 +            this.simpleNameLength = simpleNameLength;
   52.59 +            this.upToDate = upToDate;
   52.60 +            this.resolvable = resolvable;
   52.61 +        }
   52.62 +
   52.63 +        /**
   52.64 +         * Known qualified names for the given simple name in the original code.
   52.65 +         *
   52.66 +         * @return known qualified names
   52.67 +         */
   52.68 +        public List<String> getNames() {
   52.69 +            return names;
   52.70 +        }
   52.71 +
   52.72 +        /**
   52.73 +         * The length of the simple name in the original code for which the
   52.74 +         * qualified names where gathered.
   52.75 +         *
   52.76 +         * @return the length of the simple name; -1 if there is no name immediately left to the cursor for
   52.77 +         *         which the candidates could be computed
   52.78 +         */
   52.79 +        public int getSimpleNameLength() {
   52.80 +            return simpleNameLength;
   52.81 +        }
   52.82 +
   52.83 +        /**
   52.84 +         * Whether the result is based on up to date data. The
   52.85 +         * {@link SourceCodeAnalysis#listQualifiedNames(java.lang.String, int) listQualifiedNames}
   52.86 +         * method may return before the classpath is fully inspected, in which case this method will
   52.87 +         * return {@code false}. If the result is based on a fully inspected classpath, this method
   52.88 +         * will return {@code true}.
   52.89 +         *
   52.90 +         * @return true iff the results is based on up-to-date data
   52.91 +         */
   52.92 +        public boolean isUpToDate() {
   52.93 +            return upToDate;
   52.94 +        }
   52.95 +
   52.96 +        /**
   52.97 +         * Whether the given simple name in the original code refers to a resolvable element.
   52.98 +         *
   52.99 +         * @return true iff the given simple name in the original code refers to a resolvable element
  52.100 +         */
  52.101 +        public boolean isResolvable() {
  52.102 +            return resolvable;
  52.103 +        }
  52.104 +
  52.105 +    }
  52.106  }
    53.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Fri Jun 03 17:06:38 2016 +0200
    53.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Fri Sep 02 23:54:26 2016 +0200
    53.3 @@ -1,5 +1,5 @@
    53.4  /*
    53.5 - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
    53.6 + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
    53.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    53.8   *
    53.9   * This code is free software; you can redistribute it and/or modify it
   53.10 @@ -40,10 +40,13 @@
   53.11  import com.sun.source.tree.Tree;
   53.12  import com.sun.source.tree.Tree.Kind;
   53.13  import com.sun.source.tree.VariableTree;
   53.14 +import com.sun.source.util.JavacTask;
   53.15  import com.sun.source.util.SourcePositions;
   53.16  import com.sun.source.util.TreePath;
   53.17  import com.sun.source.util.TreePathScanner;
   53.18 +import com.sun.source.util.Trees;
   53.19  import com.sun.tools.javac.api.JavacScope;
   53.20 +import com.sun.tools.javac.api.JavacTaskImpl;
   53.21  import com.sun.tools.javac.code.Flags;
   53.22  import com.sun.tools.javac.code.Symbol.CompletionFailure;
   53.23  import com.sun.tools.javac.code.Symbol.VarSymbol;
   53.24 @@ -79,19 +82,28 @@
   53.25  import java.nio.file.DirectoryStream;
   53.26  import java.nio.file.FileSystem;
   53.27  import java.nio.file.FileSystems;
   53.28 +import java.nio.file.FileVisitResult;
   53.29 +import java.nio.file.FileVisitor;
   53.30  import java.nio.file.Files;
   53.31  import java.nio.file.Path;
   53.32  import java.nio.file.Paths;
   53.33 +import java.nio.file.attribute.BasicFileAttributes;
   53.34 +import java.util.Arrays;
   53.35 +import java.util.Collection;
   53.36  import java.util.Comparator;
   53.37 +import java.util.HashMap;
   53.38  import java.util.HashSet;
   53.39 +import java.util.LinkedHashSet;
   53.40 +import java.util.Map;
   53.41  import java.util.NoSuchElementException;
   53.42  import java.util.Set;
   53.43 +import java.util.concurrent.ExecutorService;
   53.44 +import java.util.concurrent.Executors;
   53.45  import java.util.function.Function;
   53.46  import java.util.regex.Matcher;
   53.47  import java.util.regex.Pattern;
   53.48  import java.util.stream.Collectors;
   53.49  import static java.util.stream.Collectors.collectingAndThen;
   53.50 -import static java.util.stream.Collectors.joining;
   53.51  import static java.util.stream.Collectors.toCollection;
   53.52  import static java.util.stream.Collectors.toList;
   53.53  import static java.util.stream.Collectors.toSet;
   53.54 @@ -99,6 +111,7 @@
   53.55  import java.util.stream.StreamSupport;
   53.56  
   53.57  import javax.lang.model.SourceVersion;
   53.58 +
   53.59  import javax.lang.model.element.ExecutableElement;
   53.60  import javax.lang.model.element.PackageElement;
   53.61  import javax.lang.model.element.QualifiedNameable;
   53.62 @@ -108,22 +121,45 @@
   53.63  import javax.lang.model.type.TypeKind;
   53.64  import javax.lang.model.util.ElementFilter;
   53.65  import javax.lang.model.util.Types;
   53.66 +import javax.tools.JavaCompiler;
   53.67  import javax.tools.JavaFileManager.Location;
   53.68 +import javax.tools.JavaFileObject;
   53.69 +import javax.tools.StandardJavaFileManager;
   53.70  import javax.tools.StandardLocation;
   53.71 +import javax.tools.ToolProvider;
   53.72  
   53.73  import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
   53.74 +import static java.util.stream.Collectors.joining;
   53.75  
   53.76  /**
   53.77   * The concrete implementation of SourceCodeAnalysis.
   53.78   * @author Robert Field
   53.79   */
   53.80  class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
   53.81 +
   53.82 +    private static final Map<Path, ClassIndex> PATH_TO_INDEX = new HashMap<>();
   53.83 +    private static final ExecutorService INDEXER = Executors.newFixedThreadPool(1, r -> {
   53.84 +        Thread t = new Thread(r);
   53.85 +        t.setDaemon(true);
   53.86 +        t.setUncaughtExceptionHandler((thread, ex) -> ex.printStackTrace());
   53.87 +        return t;
   53.88 +    });
   53.89 +
   53.90      private final JShell proc;
   53.91      private final CompletenessAnalyzer ca;
   53.92 +    private final Map<Path, ClassIndex> currentIndexes = new HashMap<>();
   53.93 +    private int indexVersion;
   53.94 +    private int classpathVersion;
   53.95 +    private final Object suspendLock = new Object();
   53.96 +    private int suspend;
   53.97  
   53.98      SourceCodeAnalysisImpl(JShell proc) {
   53.99          this.proc = proc;
  53.100          this.ca = new CompletenessAnalyzer(proc);
  53.101 +
  53.102 +        int cpVersion = classpathVersion = 1;
  53.103 +
  53.104 +        INDEXER.submit(() -> refreshIndexes(cpVersion));
  53.105      }
  53.106  
  53.107      @Override
  53.108 @@ -182,11 +218,6 @@
  53.109          throw new InternalError();
  53.110      }
  53.111  
  53.112 -    private OuterWrap wrapInClass(Wrap guts) {
  53.113 -        String imports = proc.maps.packageAndImportsExcept(null, null);
  53.114 -        return OuterWrap.wrapInClass(proc.maps.packageName(), REPL_DOESNOTMATTER_CLASS_NAME, imports, "", guts);
  53.115 -    }
  53.116 -
  53.117      private Tree.Kind guessKind(String code) {
  53.118          ParseTask pt = proc.taskFactory.new ParseTask(code);
  53.119          List<? extends Tree> units = pt.units();
  53.120 @@ -203,6 +234,15 @@
  53.121  
  53.122      @Override
  53.123      public List<Suggestion> completionSuggestions(String code, int cursor, int[] anchor) {
  53.124 +        suspendIndexing();
  53.125 +        try {
  53.126 +            return completionSuggestionsImpl(code, cursor, anchor);
  53.127 +        } finally {
  53.128 +            resumeIndexing();
  53.129 +        }
  53.130 +    }
  53.131 +
  53.132 +    private List<Suggestion> completionSuggestionsImpl(String code, int cursor, int[] anchor) {
  53.133          code = code.substring(0, cursor);
  53.134          Matcher m = JAVA_IDENTIFIER.matcher(code);
  53.135          String identifier = "";
  53.136 @@ -220,13 +260,13 @@
  53.137          OuterWrap codeWrap;
  53.138          switch (guessKind(code)) {
  53.139              case IMPORT:
  53.140 -                codeWrap = OuterWrap.wrapImport(null, Wrap.importWrap(code + "any.any"));
  53.141 +                codeWrap = proc.outerMap.wrapImport(Wrap.simpleWrap(code + "any.any"), null);
  53.142                  break;
  53.143              case METHOD:
  53.144 -                codeWrap = wrapInClass(Wrap.classMemberWrap(code));
  53.145 +                codeWrap = proc.outerMap.wrapInTrialClass(Wrap.classMemberWrap(code));
  53.146                  break;
  53.147              default:
  53.148 -                codeWrap = wrapInClass(Wrap.methodWrap(code));
  53.149 +                codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
  53.150                  break;
  53.151          }
  53.152          String requiredPrefix = identifier;
  53.153 @@ -239,7 +279,7 @@
  53.154      private List<Suggestion> computeSuggestions(OuterWrap code, int cursor, int[] anchor) {
  53.155          AnalyzeTask at = proc.taskFactory.new AnalyzeTask(code);
  53.156          SourcePositions sp = at.trees().getSourcePositions();
  53.157 -        CompilationUnitTree topLevel = at.cuTree();
  53.158 +        CompilationUnitTree topLevel = at.firstCuTree();
  53.159          List<Suggestion> result = new ArrayList<>();
  53.160          TreePath tp = pathFor(topLevel, sp, code.snippetIndexToWrapIndex(cursor));
  53.161          if (tp != null) {
  53.162 @@ -390,8 +430,11 @@
  53.163  
  53.164                  long start = sp.getStartPosition(topLevel, tree);
  53.165                  long end = sp.getEndPosition(topLevel, tree);
  53.166 +                long prevEnd = deepest[0] != null ? sp.getEndPosition(topLevel, deepest[0].getLeaf()) : -1;
  53.167  
  53.168 -                if (start <= pos && pos <= end) {
  53.169 +                if (start <= pos && pos <= end &&
  53.170 +                    (start != end || prevEnd != end || deepest[0] == null ||
  53.171 +                     deepest[0].getParentPath().getLeaf() != getCurrentPath().getLeaf())) {
  53.172                      deepest[0] = new TreePath(getCurrentPath(), tree);
  53.173                      return super.scan(tree, p);
  53.174                  }
  53.175 @@ -589,115 +632,38 @@
  53.176                  .collect(toList());
  53.177      }
  53.178  
  53.179 -    private Set<String> emptyContextPackages = null;
  53.180 +    void classpathChanged() {
  53.181 +        synchronized (currentIndexes) {
  53.182 +            int cpVersion = ++classpathVersion;
  53.183  
  53.184 -    void classpathChanged() {
  53.185 -        emptyContextPackages = null;
  53.186 +            INDEXER.submit(() -> refreshIndexes(cpVersion));
  53.187 +        }
  53.188      }
  53.189  
  53.190      private Set<PackageElement> listPackages(AnalyzeTask at, String enclosingPackage) {
  53.191 -        Set<String> packs;
  53.192 -
  53.193 -        if (enclosingPackage.isEmpty() && emptyContextPackages != null) {
  53.194 -            packs = emptyContextPackages;
  53.195 -        } else {
  53.196 -            packs = new HashSet<>();
  53.197 -
  53.198 -            listPackages(StandardLocation.PLATFORM_CLASS_PATH, enclosingPackage, packs);
  53.199 -            listPackages(StandardLocation.CLASS_PATH, enclosingPackage, packs);
  53.200 -            listPackages(StandardLocation.SOURCE_PATH, enclosingPackage, packs);
  53.201 -
  53.202 -            if (enclosingPackage.isEmpty()) {
  53.203 -                emptyContextPackages = packs;
  53.204 -            }
  53.205 +        synchronized (currentIndexes) {
  53.206 +            return currentIndexes.values()
  53.207 +                                 .stream()
  53.208 +                                 .flatMap(idx -> idx.packages.stream())
  53.209 +                                 .filter(p -> enclosingPackage.isEmpty() || p.startsWith(enclosingPackage + "."))
  53.210 +                                 .map(p -> {
  53.211 +                                     int dot = p.indexOf('.', enclosingPackage.length() + 1);
  53.212 +                                     return dot == (-1) ? p : p.substring(0, dot);
  53.213 +                                 })
  53.214 +                                 .distinct()
  53.215 +                                 .map(p -> createPackageElement(at, p))
  53.216 +                                 .collect(Collectors.toSet());
  53.217          }
  53.218 -
  53.219 -        return packs.stream()
  53.220 -                    .map(pkg -> createPackageElement(at, pkg))
  53.221 -                    .collect(Collectors.toSet());
  53.222      }
  53.223  
  53.224      private PackageElement createPackageElement(AnalyzeTask at, String packageName) {
  53.225          Names names = Names.instance(at.getContext());
  53.226          Symtab syms = Symtab.instance(at.getContext());
  53.227 -        PackageElement existing = syms.enterPackage(names.fromString(packageName));
  53.228 +        PackageElement existing = syms.enterPackage(syms.unnamedModule, names.fromString(packageName));
  53.229  
  53.230          return existing;
  53.231      }
  53.232  
  53.233 -    private void listPackages(Location loc, String enclosing, Set<String> packs) {
  53.234 -        Iterable<? extends Path> paths = proc.taskFactory.fileManager().getLocationAsPaths(loc);
  53.235 -
  53.236 -        if (paths == null)
  53.237 -            return ;
  53.238 -
  53.239 -        for (Path p : paths) {
  53.240 -            listPackages(p, enclosing, packs);
  53.241 -        }
  53.242 -    }
  53.243 -
  53.244 -    private void listPackages(Path path, String enclosing, Set<String> packages) {
  53.245 -        try {
  53.246 -            if (path.equals(Paths.get("JRT_MARKER_FILE"))) {
  53.247 -                FileSystem jrtfs = FileSystems.getFileSystem(URI.create("jrt:/"));
  53.248 -                Path modules = jrtfs.getPath("modules");
  53.249 -                try (DirectoryStream<Path> stream = Files.newDirectoryStream(modules)) {
  53.250 -                    for (Path c : stream) {
  53.251 -                        listDirectory(c, enclosing, packages);
  53.252 -                    }
  53.253 -                }
  53.254 -            } else if (!Files.isDirectory(path)) {
  53.255 -                if (Files.exists(path)) {
  53.256 -                    ClassLoader cl = SourceCodeAnalysisImpl.class.getClassLoader();
  53.257 -
  53.258 -                    try (FileSystem zip = FileSystems.newFileSystem(path, cl)) {
  53.259 -                        listDirectory(zip.getRootDirectories().iterator().next(), enclosing, packages);
  53.260 -                    }
  53.261 -                }
  53.262 -            } else {
  53.263 -                listDirectory(path, enclosing, packages);
  53.264 -            }
  53.265 -        } catch (IOException ex) {
  53.266 -            proc.debug(ex, "SourceCodeAnalysisImpl.listPackages(" + path.toString() + ", " + enclosing + ", " + packages + ")");
  53.267 -        }
  53.268 -    }
  53.269 -
  53.270 -    private void listDirectory(Path path, String enclosing, Set<String> packages) throws IOException {
  53.271 -        String separator = path.getFileSystem().getSeparator();
  53.272 -        Path resolved = path.resolve(enclosing.replace(".", separator));
  53.273 -
  53.274 -        if (Files.isDirectory(resolved)) {
  53.275 -            try (DirectoryStream<Path> ds = Files.newDirectoryStream(resolved)) {
  53.276 -                for (Path entry : ds) {
  53.277 -                    String name = pathName(entry);
  53.278 -
  53.279 -                    if (SourceVersion.isIdentifier(name) &&
  53.280 -                        Files.isDirectory(entry) &&
  53.281 -                        validPackageCandidate(entry)) {
  53.282 -                        packages.add(enclosing + (enclosing.isEmpty() ? "" : ".") + name);
  53.283 -                    }
  53.284 -                }
  53.285 -            }
  53.286 -        }
  53.287 -    }
  53.288 -
  53.289 -    private boolean validPackageCandidate(Path p) throws IOException {
  53.290 -        try (Stream<Path> dir = Files.list(p)) {
  53.291 -            return dir.anyMatch(e -> Files.isDirectory(e) && SourceVersion.isIdentifier(pathName(e)) ||
  53.292 -                                e.getFileName().toString().endsWith(".class"));
  53.293 -        }
  53.294 -    }
  53.295 -
  53.296 -    private String pathName(Path p) {
  53.297 -        String separator = p.getFileSystem().getSeparator();
  53.298 -        String name = p.getFileName().toString();
  53.299 -
  53.300 -        if (name.endsWith(separator)) //jars have '/' appended
  53.301 -            name = name.substring(0, name.length() - separator.length());
  53.302 -
  53.303 -        return name;
  53.304 -    }
  53.305 -
  53.306      private Element createArrayLengthSymbol(AnalyzeTask at, TypeMirror site) {
  53.307          Name length = Names.instance(at.getContext()).length;
  53.308          Type intType = Symtab.instance(at.getContext()).intType;
  53.309 @@ -965,6 +931,21 @@
  53.310  
  53.311      @Override
  53.312      public String documentation(String code, int cursor) {
  53.313 +        suspendIndexing();
  53.314 +        try {
  53.315 +            return documentationImpl(code, cursor);
  53.316 +        } finally {
  53.317 +            resumeIndexing();
  53.318 +        }
  53.319 +    }
  53.320 +
  53.321 +    //tweaked by tests to disable reading parameter names from classfiles so that tests using
  53.322 +    //JDK's classes are stable for both release and fastdebug builds:
  53.323 +    private final String[] keepParameterNames = new String[] {
  53.324 +        "-XDsave-parameter-names=true"
  53.325 +    };
  53.326 +
  53.327 +    private String documentationImpl(String code, int cursor) {
  53.328          code = code.substring(0, cursor);
  53.329          if (code.trim().isEmpty()) { //TODO: comment handling
  53.330              code += ";";
  53.331 @@ -973,10 +954,10 @@
  53.332          if (guessKind(code) == Kind.IMPORT)
  53.333              return null;
  53.334  
  53.335 -        OuterWrap codeWrap = wrapInClass(Wrap.methodWrap(code));
  53.336 -        AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap);
  53.337 +        OuterWrap codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
  53.338 +        AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap, keepParameterNames);
  53.339          SourcePositions sp = at.trees().getSourcePositions();
  53.340 -        CompilationUnitTree topLevel = at.cuTree();
  53.341 +        CompilationUnitTree topLevel = at.firstCuTree();
  53.342          TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
  53.343  
  53.344          if (tp == null)
  53.345 @@ -1015,9 +996,11 @@
  53.346                          .collect(Collectors.toList());
  53.347          }
  53.348  
  53.349 -        return Util.stream(candidates)
  53.350 -                .map(method -> Util.expunge(element2String(method.fst)))
  53.351 -                .collect(joining("\n"));
  53.352 +        try (SourceCache sourceCache = new SourceCache(at)) {
  53.353 +            return Util.stream(candidates)
  53.354 +                    .map(method -> Util.expunge(element2String(sourceCache, method.fst)))
  53.355 +                    .collect(joining("\n"));
  53.356 +        }
  53.357      }
  53.358  
  53.359      private boolean isEmptyArgumentsContext(List<? extends ExpressionTree> arguments) {
  53.360 @@ -1028,19 +1011,173 @@
  53.361          return false;
  53.362      }
  53.363  
  53.364 -    private String element2String(Element el) {
  53.365 +    private String element2String(SourceCache sourceCache, Element el) {
  53.366 +        try {
  53.367 +            if (hasSyntheticParameterNames(el)) {
  53.368 +                el = sourceCache.getSourceMethod(el);
  53.369 +            }
  53.370 +        } catch (IOException ex) {
  53.371 +            proc.debug(ex, "SourceCodeAnalysisImpl.element2String(..., " + el + ")");
  53.372 +        }
  53.373 +
  53.374 +        return Util.expunge(elementHeader(el));
  53.375 +    }
  53.376 +
  53.377 +    private boolean hasSyntheticParameterNames(Element el) {
  53.378 +        if (el.getKind() != ElementKind.CONSTRUCTOR && el.getKind() != ElementKind.METHOD)
  53.379 +            return false;
  53.380 +
  53.381 +        ExecutableElement ee = (ExecutableElement) el;
  53.382 +
  53.383 +        if (ee.getParameters().isEmpty())
  53.384 +            return false;
  53.385 +
  53.386 +        return ee.getParameters()
  53.387 +                 .stream()
  53.388 +                 .allMatch(param -> param.getSimpleName().toString().startsWith("arg"));
  53.389 +    }
  53.390 +
  53.391 +    private final class SourceCache implements AutoCloseable {
  53.392 +        private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  53.393 +        private final Map<String, Map<String, Element>> topLevelName2Signature2Method = new HashMap<>();
  53.394 +        private final AnalyzeTask originalTask;
  53.395 +        private final StandardJavaFileManager fm;
  53.396 +
  53.397 +        public SourceCache(AnalyzeTask originalTask) {
  53.398 +            this.originalTask = originalTask;
  53.399 +            List<Path> sources = findSources();
  53.400 +            if (sources.iterator().hasNext()) {
  53.401 +                StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null);
  53.402 +                try {
  53.403 +                    fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, sources);
  53.404 +                } catch (IOException ex) {
  53.405 +                    proc.debug(ex, "SourceCodeAnalysisImpl.SourceCache.<init>(...)");
  53.406 +                    fm = null;
  53.407 +                }
  53.408 +                this.fm = fm;
  53.409 +            } else {
  53.410 +                //don't waste time if there are no sources
  53.411 +                this.fm = null;
  53.412 +            }
  53.413 +        }
  53.414 +
  53.415 +        public Element getSourceMethod(Element method) throws IOException {
  53.416 +            if (fm == null)
  53.417 +                return method;
  53.418 +
  53.419 +            TypeElement type = topLevelType(method);
  53.420 +
  53.421 +            if (type == null)
  53.422 +                return method;
  53.423 +
  53.424 +            String binaryName = originalTask.task.getElements().getBinaryName(type).toString();
  53.425 +
  53.426 +            Map<String, Element> cache = topLevelName2Signature2Method.get(binaryName);
  53.427 +
  53.428 +            if (cache == null) {
  53.429 +                topLevelName2Signature2Method.put(binaryName, cache = createMethodCache(binaryName));
  53.430 +            }
  53.431 +
  53.432 +            String handle = elementHeader(method, false);
  53.433 +
  53.434 +            return cache.getOrDefault(handle, method);
  53.435 +        }
  53.436 +
  53.437 +        private TypeElement topLevelType(Element el) {
  53.438 +            while (el != null && el.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
  53.439 +                el = el.getEnclosingElement();
  53.440 +            }
  53.441 +
  53.442 +            return el != null && (el.getKind().isClass() || el.getKind().isInterface()) ? (TypeElement) el : null;
  53.443 +        }
  53.444 +
  53.445 +        private Map<String, Element> createMethodCache(String binaryName) throws IOException {
  53.446 +            Pair<JavacTask, CompilationUnitTree> source = findSource(binaryName);
  53.447 +
  53.448 +            if (source == null)
  53.449 +                return Collections.emptyMap();
  53.450 +
  53.451 +            Map<String, Element> signature2Method = new HashMap<>();
  53.452 +            Trees trees = Trees.instance(source.fst);
  53.453 +
  53.454 +            new TreePathScanner<Void, Void>() {
  53.455 +                @Override @DefinedBy(Api.COMPILER_TREE)
  53.456 +                public Void visitMethod(MethodTree node, Void p) {
  53.457 +                    Element currentMethod = trees.getElement(getCurrentPath());
  53.458 +
  53.459 +                    if (currentMethod != null) {
  53.460 +                        signature2Method.put(elementHeader(currentMethod, false), currentMethod);
  53.461 +                    }
  53.462 +
  53.463 +                    return null;
  53.464 +                }
  53.465 +            }.scan(source.snd, null);
  53.466 +
  53.467 +            return signature2Method;
  53.468 +        }
  53.469 +
  53.470 +        private Pair<JavacTask, CompilationUnitTree> findSource(String binaryName) throws IOException {
  53.471 +            JavaFileObject jfo = fm.getJavaFileForInput(StandardLocation.SOURCE_PATH,
  53.472 +                                                        binaryName,
  53.473 +                                                        JavaFileObject.Kind.SOURCE);
  53.474 +
  53.475 +            if (jfo == null)
  53.476 +                return null;
  53.477 +
  53.478 +            List<JavaFileObject> jfos = Arrays.asList(jfo);
  53.479 +            JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fm, d -> {}, null, null, jfos);
  53.480 +            Iterable<? extends CompilationUnitTree> cuts = task.parse();
  53.481 +
  53.482 +            task.enter();
  53.483 +
  53.484 +            return Pair.of(task, cuts.iterator().next());
  53.485 +        }
  53.486 +
  53.487 +        @Override
  53.488 +        public void close() {
  53.489 +            try {
  53.490 +                if (fm != null) {
  53.491 +                    fm.close();
  53.492 +                }
  53.493 +            } catch (IOException ex) {
  53.494 +                proc.debug(ex, "SourceCodeAnalysisImpl.SourceCache.close()");
  53.495 +            }
  53.496 +        }
  53.497 +    }
  53.498 +
  53.499 +    private List<Path> availableSources;
  53.500 +
  53.501 +    private List<Path> findSources() {
  53.502 +        if (availableSources != null) {
  53.503 +            return availableSources;
  53.504 +        }
  53.505 +        List<Path> result = new ArrayList<>();
  53.506 +        Path home = Paths.get(System.getProperty("java.home"));
  53.507 +        Path srcZip = home.resolve("src.zip");
  53.508 +        if (!Files.isReadable(srcZip))
  53.509 +            srcZip = home.getParent().resolve("src.zip");
  53.510 +        if (Files.isReadable(srcZip))
  53.511 +            result.add(srcZip);
  53.512 +        return availableSources = result;
  53.513 +    }
  53.514 +
  53.515 +    private String elementHeader(Element el) {
  53.516 +        return elementHeader(el, true);
  53.517 +    }
  53.518 +
  53.519 +    private String elementHeader(Element el, boolean includeParameterNames) {
  53.520          switch (el.getKind()) {
  53.521              case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
  53.522                  return ((TypeElement) el).getQualifiedName().toString();
  53.523              case FIELD:
  53.524 -                return element2String(el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
  53.525 +                return elementHeader(el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
  53.526              case ENUM_CONSTANT:
  53.527 -                return element2String(el.getEnclosingElement()) + "." + el.getSimpleName();
  53.528 +                return elementHeader(el.getEnclosingElement()) + "." + el.getSimpleName();
  53.529              case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE:
  53.530                  return el.getSimpleName() + ":" + el.asType();
  53.531              case CONSTRUCTOR: case METHOD:
  53.532                  StringBuilder header = new StringBuilder();
  53.533 -                header.append(element2String(el.getEnclosingElement()));
  53.534 +                header.append(elementHeader(el.getEnclosingElement()));
  53.535                  if (el.getKind() == ElementKind.METHOD) {
  53.536                      header.append(".");
  53.537                      header.append(el.getSimpleName());
  53.538 @@ -1058,8 +1195,10 @@
  53.539                      } else {
  53.540                          header.append(p.asType());
  53.541                      }
  53.542 -                    header.append(" ");
  53.543 -                    header.append(p.getSimpleName());
  53.544 +                    if (includeParameterNames) {
  53.545 +                        header.append(" ");
  53.546 +                        header.append(p.getSimpleName());
  53.547 +                    }
  53.548                      sep = ", ";
  53.549                  }
  53.550                  header.append(")");
  53.551 @@ -1074,4 +1213,347 @@
  53.552          }
  53.553          return arrayType;
  53.554      }
  53.555 +
  53.556 +    @Override
  53.557 +    public String analyzeType(String code, int cursor) {
  53.558 +        code = code.substring(0, cursor);
  53.559 +        CompletionInfo completionInfo = analyzeCompletion(code);
  53.560 +        if (!completionInfo.completeness.isComplete)
  53.561 +            return null;
  53.562 +        if (completionInfo.completeness == Completeness.COMPLETE_WITH_SEMI) {
  53.563 +            code += ";";
  53.564 +        }
  53.565 +
  53.566 +        OuterWrap codeWrap;
  53.567 +        switch (guessKind(code)) {
  53.568 +            case IMPORT: case METHOD: case CLASS: case ENUM:
  53.569 +            case INTERFACE: case ANNOTATION_TYPE: case VARIABLE:
  53.570 +                return null;
  53.571 +            default:
  53.572 +                codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
  53.573 +                break;
  53.574 +        }
  53.575 +        AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap);
  53.576 +        SourcePositions sp = at.trees().getSourcePositions();
  53.577 +        CompilationUnitTree topLevel = at.firstCuTree();
  53.578 +        int pos = codeWrap.snippetIndexToWrapIndex(code.length());
  53.579 +        TreePath tp = pathFor(topLevel, sp, pos);
  53.580 +        while (ExpressionTree.class.isAssignableFrom(tp.getParentPath().getLeaf().getKind().asInterface()) &&
  53.581 +               tp.getParentPath().getLeaf().getKind() != Kind.ERRONEOUS &&
  53.582 +               tp.getParentPath().getParentPath() != null)
  53.583 +            tp = tp.getParentPath();
  53.584 +        TypeMirror type = at.trees().getTypeMirror(tp);
  53.585 +
  53.586 +        if (type == null)
  53.587 +            return null;
  53.588 +
  53.589 +        switch (type.getKind()) {
  53.590 +            case ERROR: case NONE: case OTHER:
  53.591 +            case PACKAGE: case VOID:
  53.592 +                return null; //not usable
  53.593 +            case NULL:
  53.594 +                type = at.getElements().getTypeElement("java.lang.Object").asType();
  53.595 +                break;
  53.596 +        }
  53.597 +
  53.598 +        return TreeDissector.printType(at, proc, type);
  53.599 +    }
  53.600 +
  53.601 +    @Override
  53.602 +    public QualifiedNames listQualifiedNames(String code, int cursor) {
  53.603 +        code = code.substring(0, cursor);
  53.604 +        if (code.trim().isEmpty()) {
  53.605 +            return new QualifiedNames(Collections.emptyList(), -1, true, false);
  53.606 +        }
  53.607 +        OuterWrap codeWrap;
  53.608 +        switch (guessKind(code)) {
  53.609 +            case IMPORT:
  53.610 +                return new QualifiedNames(Collections.emptyList(), -1, true, false);
  53.611 +            case METHOD:
  53.612 +                codeWrap = proc.outerMap.wrapInTrialClass(Wrap.classMemberWrap(code));
  53.613 +                break;
  53.614 +            default:
  53.615 +                codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
  53.616 +                break;
  53.617 +        }
  53.618 +        AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap);
  53.619 +        SourcePositions sp = at.trees().getSourcePositions();
  53.620 +        CompilationUnitTree topLevel = at.firstCuTree();
  53.621 +        TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(code.length()));
  53.622 +        if (tp.getLeaf().getKind() != Kind.IDENTIFIER) {
  53.623 +            return new QualifiedNames(Collections.emptyList(), -1, true, false);
  53.624 +        }
  53.625 +        Scope scope = at.trees().getScope(tp);
  53.626 +        TypeMirror type = at.trees().getTypeMirror(tp);
  53.627 +        Element el = at.trees().getElement(tp);
  53.628 +
  53.629 +        boolean erroneous = (type.getKind() == TypeKind.ERROR && el.getKind() == ElementKind.CLASS) ||
  53.630 +                            (el.getKind() == ElementKind.PACKAGE && el.getEnclosedElements().isEmpty());
  53.631 +        String simpleName = ((IdentifierTree) tp.getLeaf()).getName().toString();
  53.632 +        boolean upToDate;
  53.633 +        List<String> result;
  53.634 +
  53.635 +        synchronized (currentIndexes) {
  53.636 +            upToDate = classpathVersion == indexVersion;
  53.637 +            result = currentIndexes.values()
  53.638 +                                   .stream()
  53.639 +                                   .flatMap(idx -> idx.classSimpleName2FQN.getOrDefault(simpleName,
  53.640 +                                                                                        Collections.emptyList()).stream())
  53.641 +                                   .distinct()
  53.642 +                                   .filter(fqn -> isAccessible(at, scope, fqn))
  53.643 +                                   .sorted()
  53.644 +                                   .collect(Collectors.toList());
  53.645 +        }
  53.646 +
  53.647 +        return new QualifiedNames(result, simpleName.length(), upToDate, !erroneous);
  53.648 +    }
  53.649 +
  53.650 +    private boolean isAccessible(AnalyzeTask at, Scope scope, String fqn) {
  53.651 +        TypeElement type = at.getElements().getTypeElement(fqn);
  53.652 +        if (type == null)
  53.653 +            return false;
  53.654 +        return at.trees().isAccessible(scope, type);
  53.655 +    }
  53.656 +
  53.657 +    //--------------------
  53.658 +    // classpath indexing:
  53.659 +    //--------------------
  53.660 +
  53.661 +    //the indexing can be suspended when a more important task is running:
  53.662 +    private void waitIndexingNotSuspended() {
  53.663 +        boolean suspendedNotified = false;
  53.664 +        synchronized (suspendLock) {
  53.665 +            while (suspend > 0) {
  53.666 +                if (!suspendedNotified) {
  53.667 +                    suspendedNotified = true;
  53.668 +                }
  53.669 +                try {
  53.670 +                    suspendLock.wait();
  53.671 +                } catch (InterruptedException ex) {
  53.672 +                }
  53.673 +            }
  53.674 +        }
  53.675 +    }
  53.676 +
  53.677 +    public void suspendIndexing() {
  53.678 +        synchronized (suspendLock) {
  53.679 +            suspend++;
  53.680 +        }
  53.681 +    }
  53.682 +
  53.683 +    public void resumeIndexing() {
  53.684 +        synchronized (suspendLock) {
  53.685 +            if (--suspend == 0) {
  53.686 +                suspendLock.notifyAll();
  53.687 +            }
  53.688 +        }
  53.689 +    }
  53.690 +
  53.691 +    //update indexes, either initially or after a classpath change:
  53.692 +    private void refreshIndexes(int version) {
  53.693 +        try {
  53.694 +            Collection<Path> paths = new ArrayList<>();
  53.695 +            MemoryFileManager fm = proc.taskFactory.fileManager();
  53.696 +
  53.697 +            appendPaths(fm, StandardLocation.PLATFORM_CLASS_PATH, paths);
  53.698 +            appendPaths(fm, StandardLocation.CLASS_PATH, paths);
  53.699 +            appendPaths(fm, StandardLocation.SOURCE_PATH, paths);
  53.700 +
  53.701 +            Map<Path, ClassIndex> newIndexes = new HashMap<>();
  53.702 +
  53.703 +            //setup existing/last known data:
  53.704 +            for (Path p : paths) {
  53.705 +                ClassIndex index = PATH_TO_INDEX.get(p);
  53.706 +                if (index != null) {
  53.707 +                    newIndexes.put(p, index);
  53.708 +                }
  53.709 +            }
  53.710 +
  53.711 +            synchronized (currentIndexes) {
  53.712 +                //temporary setting old data:
  53.713 +                currentIndexes.clear();
  53.714 +                currentIndexes.putAll(newIndexes);
  53.715 +            }
  53.716 +
  53.717 +            //update/compute the indexes if needed:
  53.718 +            for (Path p : paths) {
  53.719 +                waitIndexingNotSuspended();
  53.720 +
  53.721 +                ClassIndex index = indexForPath(p);
  53.722 +                newIndexes.put(p, index);
  53.723 +            }
  53.724 +
  53.725 +            synchronized (currentIndexes) {
  53.726 +                currentIndexes.clear();
  53.727 +                currentIndexes.putAll(newIndexes);
  53.728 +            }
  53.729 +        } catch (Exception ex) {
  53.730 +            proc.debug(ex, "SourceCodeAnalysisImpl.refreshIndexes(" + version + ")");
  53.731 +        } finally {
  53.732 +            synchronized (currentIndexes) {
  53.733 +                indexVersion = version;
  53.734 +            }
  53.735 +        }
  53.736 +    }
  53.737 +
  53.738 +    private void appendPaths(MemoryFileManager fm, Location loc, Collection<Path> paths) {
  53.739 +        Iterable<? extends Path> locationPaths = fm.getLocationAsPaths(loc);
  53.740 +        if (locationPaths == null)
  53.741 +            return ;
  53.742 +        for (Path path : locationPaths) {
  53.743 +            if (".".equals(path.toString())) {
  53.744 +                //skip CWD
  53.745 +                continue;
  53.746 +            }
  53.747 +
  53.748 +            paths.add(path);
  53.749 +        }
  53.750 +    }
  53.751 +
  53.752 +    //create/update index a given JavaFileManager entry (which may be a JDK installation, a jar/zip file or a directory):
  53.753 +    //if an index exists for the given entry, the existing index is kept unless the timestamp is modified
  53.754 +    private ClassIndex indexForPath(Path path) {
  53.755 +        if (isJRTMarkerFile(path)) {
  53.756 +            FileSystem jrtfs = FileSystems.getFileSystem(URI.create("jrt:/"));
  53.757 +            Path modules = jrtfs.getPath("modules");
  53.758 +            return PATH_TO_INDEX.compute(path, (p, index) -> {
  53.759 +                try {
  53.760 +                    long lastModified = Files.getLastModifiedTime(modules).toMillis();
  53.761 +                    if (index == null || index.timestamp != lastModified) {
  53.762 +                        try (DirectoryStream<Path> stream = Files.newDirectoryStream(modules)) {
  53.763 +                            index = doIndex(lastModified, path, stream);
  53.764 +                        }
  53.765 +                    }
  53.766 +                    return index;
  53.767 +                } catch (IOException ex) {
  53.768 +                    proc.debug(ex, "SourceCodeAnalysisImpl.indexesForPath(" + path.toString() + ")");
  53.769 +                    return new ClassIndex(-1, path, Collections.emptySet(), Collections.emptyMap());
  53.770 +                }
  53.771 +            });
  53.772 +        } else if (!Files.isDirectory(path)) {
  53.773 +            if (Files.exists(path)) {
  53.774 +                return PATH_TO_INDEX.compute(path, (p, index) -> {
  53.775 +                    try {
  53.776 +                        long lastModified = Files.getLastModifiedTime(p).toMillis();
  53.777 +                        if (index == null || index.timestamp != lastModified) {
  53.778 +                            ClassLoader cl = SourceCodeAnalysisImpl.class.getClassLoader();
  53.779 +
  53.780 +                            try (FileSystem zip = FileSystems.newFileSystem(path, cl)) {
  53.781 +                                index = doIndex(lastModified, path, zip.getRootDirectories());
  53.782 +                            }
  53.783 +                        }
  53.784 +                        return index;
  53.785 +                    } catch (IOException ex) {
  53.786 +                        proc.debug(ex, "SourceCodeAnalysisImpl.indexesForPath(" + path.toString() + ")");
  53.787 +                        return new ClassIndex(-1, path, Collections.emptySet(), Collections.emptyMap());
  53.788 +                    }
  53.789 +                });
  53.790 +            } else {
  53.791 +                return new ClassIndex(-1, path, Collections.emptySet(), Collections.emptyMap());
  53.792 +            }
  53.793 +        } else {
  53.794 +            return PATH_TO_INDEX.compute(path, (p, index) -> {
  53.795 +                //no persistence for directories, as we cannot check timestamps:
  53.796 +                if (index == null) {
  53.797 +                    index = doIndex(-1, path, Arrays.asList(p));
  53.798 +                }
  53.799 +                return index;
  53.800 +            });
  53.801 +        }
  53.802 +    }
  53.803 +
  53.804 +    static boolean isJRTMarkerFile(Path path) {
  53.805 +        return path.equals(Paths.get(System.getProperty("java.home"), "lib", "modules"));
  53.806 +    }
  53.807 +
  53.808 +    //create an index based on the content of the given dirs; the original JavaFileManager entry is originalPath.
  53.809 +    private ClassIndex doIndex(long timestamp, Path originalPath, Iterable<? extends Path> dirs) {
  53.810 +        Set<String> packages = new HashSet<>();
  53.811 +        Map<String, Collection<String>> classSimpleName2FQN = new HashMap<>();
  53.812 +
  53.813 +        for (Path d : dirs) {
  53.814 +            try {
  53.815 +                Files.walkFileTree(d, new FileVisitor<Path>() {
  53.816 +                    int depth;
  53.817 +                    @Override
  53.818 +                    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
  53.819 +                        waitIndexingNotSuspended();
  53.820 +                        if (depth++ == 0)
  53.821 +                            return FileVisitResult.CONTINUE;
  53.822 +                        String dirName = dir.getFileName().toString();
  53.823 +                        String sep = dir.getFileSystem().getSeparator();
  53.824 +                        dirName = dirName.endsWith(sep) ? dirName.substring(0, dirName.length() - sep.length())
  53.825 +                                                        : dirName;
  53.826 +                        if (SourceVersion.isIdentifier(dirName))
  53.827 +                            return FileVisitResult.CONTINUE;
  53.828 +                        return FileVisitResult.SKIP_SUBTREE;
  53.829 +                    }
  53.830 +                    @Override
  53.831 +                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
  53.832 +                        waitIndexingNotSuspended();
  53.833 +                        if (file.getFileName().toString().endsWith(".class")) {
  53.834 +                            String relativePath = d.relativize(file).toString();
  53.835 +                            String binaryName = relativePath.substring(0, relativePath.length() - 6).replace('/', '.');
  53.836 +                            int packageDot = binaryName.lastIndexOf('.');
  53.837 +                            if (packageDot > (-1)) {
  53.838 +                                packages.add(binaryName.substring(0, packageDot));
  53.839 +                            }
  53.840 +                            String typeName = binaryName.replace('$', '.');
  53.841 +                            addClassName2Map(classSimpleName2FQN, typeName);
  53.842 +                        }
  53.843 +                        return FileVisitResult.CONTINUE;
  53.844 +                    }
  53.845 +                    @Override
  53.846 +                    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
  53.847 +                        return FileVisitResult.CONTINUE;
  53.848 +                    }
  53.849 +                    @Override
  53.850 +                    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
  53.851 +                        depth--;
  53.852 +                        return FileVisitResult.CONTINUE;
  53.853 +                    }
  53.854 +                });
  53.855 +            } catch (IOException ex) {
  53.856 +                proc.debug(ex, "doIndex(" + d.toString() + ")");
  53.857 +            }
  53.858 +        }
  53.859 +
  53.860 +        return new ClassIndex(timestamp, originalPath, packages, classSimpleName2FQN);
  53.861 +    }
  53.862 +
  53.863 +    private static void addClassName2Map(Map<String, Collection<String>> classSimpleName2FQN, String typeName) {
  53.864 +        int simpleNameDot = typeName.lastIndexOf('.');
  53.865 +        classSimpleName2FQN.computeIfAbsent(typeName.substring(simpleNameDot + 1), n -> new LinkedHashSet<>())
  53.866 +                           .add(typeName);
  53.867 +    }
  53.868 +
  53.869 +    //holder for indexed data about a given path
  53.870 +    public static final class ClassIndex {
  53.871 +        public final long timestamp;
  53.872 +        public final Path forPath;
  53.873 +        public final Set<String> packages;
  53.874 +        public final Map<String, Collection<String>> classSimpleName2FQN;
  53.875 +
  53.876 +        public ClassIndex(long timestamp, Path forPath, Set<String> packages, Map<String, Collection<String>> classSimpleName2FQN) {
  53.877 +            this.timestamp = timestamp;
  53.878 +            this.forPath = forPath;
  53.879 +            this.packages = packages;
  53.880 +            this.classSimpleName2FQN = classSimpleName2FQN;
  53.881 +        }
  53.882 +
  53.883 +    }
  53.884 +
  53.885 +    //for tests, to be able to wait until the indexing finishes:
  53.886 +    public void waitBackgroundTaskFinished() throws Exception {
  53.887 +        boolean upToDate;
  53.888 +        synchronized (currentIndexes) {
  53.889 +            upToDate = classpathVersion == indexVersion;
  53.890 +        }
  53.891 +        while (!upToDate) {
  53.892 +            INDEXER.submit(() -> {}).get();
  53.893 +            synchronized (currentIndexes) {
  53.894 +                upToDate = classpathVersion == indexVersion;
  53.895 +            }
  53.896 +        }
  53.897 +    }
  53.898  }
    54.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/StatementSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    54.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/StatementSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    54.3 @@ -22,6 +22,7 @@
    54.4   * or visit www.oracle.com if you need additional information or have any
    54.5   * questions.
    54.6   */
    54.7 +
    54.8  package jdk.jshell;
    54.9  
   54.10  import jdk.jshell.Key.StatementKey;
    55.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java	Fri Jun 03 17:06:38 2016 +0200
    55.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java	Fri Sep 02 23:54:26 2016 +0200
    55.3 @@ -1,5 +1,5 @@
    55.4  /*
    55.5 - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
    55.6 + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
    55.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    55.8   *
    55.9   * This code is free software; you can redistribute it and/or modify it
   55.10 @@ -33,7 +33,6 @@
   55.11  import com.sun.tools.javac.util.Context;
   55.12  import java.util.ArrayList;
   55.13  import java.util.Arrays;
   55.14 -import java.util.Iterator;
   55.15  import java.util.List;
   55.16  import javax.tools.Diagnostic;
   55.17  import javax.tools.DiagnosticCollector;
   55.18 @@ -56,11 +55,12 @@
   55.19  import java.util.LinkedHashMap;
   55.20  import java.util.Map;
   55.21  import java.util.stream.Collectors;
   55.22 +import static java.util.stream.Collectors.toList;
   55.23  import java.util.stream.Stream;
   55.24  import javax.lang.model.util.Elements;
   55.25  import javax.tools.FileObject;
   55.26  import jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject;
   55.27 -import jdk.jshell.ClassTracker.ClassInfo;
   55.28 +import java.lang.Runtime.Version;
   55.29  
   55.30  /**
   55.31   * The primary interface to the compiler API.  Parsing, analysis, and
   55.32 @@ -73,6 +73,7 @@
   55.33      private final MemoryFileManager fileManager;
   55.34      private final JShell state;
   55.35      private String classpath = System.getProperty("java.class.path");
   55.36 +    private final static Version INITIAL_SUPPORTED_VER = Version.parse("9");
   55.37  
   55.38      TaskFactory(JShell state) {
   55.39          this.state = state;
   55.40 @@ -80,9 +81,10 @@
   55.41          if (compiler == null) {
   55.42              throw new UnsupportedOperationException("Compiler not available, must be run with full JDK 9.");
   55.43          }
   55.44 -//        if (!System.getProperty("java.specification.version").equals("9"))  {
   55.45 -//            throw new UnsupportedOperationException("Wrong compiler, must be run with full JDK 9.");
   55.46 -//        }
   55.47 +        Version current = Version.parse(System.getProperty("java.specification.version"));
   55.48 +        if (INITIAL_SUPPORTED_VER.compareToIgnoreOpt(current) > 0)  {
   55.49 +            throw new UnsupportedOperationException("Wrong compiler, must be run with full JDK 9.");
   55.50 +        }
   55.51          this.fileManager = new MemoryFileManager(
   55.52                  compiler.getStandardFileManager(null, null, null), state);
   55.53      }
   55.54 @@ -145,23 +147,12 @@
   55.55                  public String getMessage(Locale locale) {
   55.56                      return expunge(d.getMessage(locale));
   55.57                  }
   55.58 -
   55.59 -                @Override
   55.60 -                Unit unitOrNull() {
   55.61 -                    return null;
   55.62 -                }
   55.63              };
   55.64          }
   55.65      }
   55.66  
   55.67      private class WrapSourceHandler implements SourceHandler<OuterWrap> {
   55.68  
   55.69 -        final OuterWrap wrap;
   55.70 -
   55.71 -        WrapSourceHandler(OuterWrap wrap) {
   55.72 -            this.wrap = wrap;
   55.73 -        }
   55.74 -
   55.75          @Override
   55.76          public JavaFileObject sourceToFileObject(MemoryFileManager fm, OuterWrap w) {
   55.77              return fm.createSourceFileObject(w, w.classFullName(), w.wrapped());
   55.78 @@ -169,24 +160,9 @@
   55.79  
   55.80          @Override
   55.81          public Diag diag(Diagnostic<? extends JavaFileObject> d) {
   55.82 -            return wrap.wrapDiag(d);
   55.83 -        }
   55.84 -    }
   55.85 -
   55.86 -    private class UnitSourceHandler implements SourceHandler<Unit> {
   55.87 -
   55.88 -        @Override
   55.89 -        public JavaFileObject sourceToFileObject(MemoryFileManager fm, Unit u) {
   55.90 -            return fm.createSourceFileObject(u,
   55.91 -                    state.maps.classFullName(u.snippet()),
   55.92 -                    u.snippet().outerWrap().wrapped());
   55.93 -        }
   55.94 -
   55.95 -        @Override
   55.96 -        public Diag diag(Diagnostic<? extends JavaFileObject> d) {
   55.97              SourceMemoryJavaFileObject smjfo = (SourceMemoryJavaFileObject) d.getSource();
   55.98 -            Unit u = (Unit) smjfo.getOrigin();
   55.99 -            return u.snippet().outerWrap().wrapDiag(d);
  55.100 +            OuterWrap w = (OuterWrap) smjfo.getOrigin();
  55.101 +            return w.wrapDiag(d);
  55.102          }
  55.103      }
  55.104  
  55.105 @@ -196,7 +172,7 @@
  55.106       */
  55.107      class ParseTask extends BaseTask {
  55.108  
  55.109 -        private final CompilationUnitTree cut;
  55.110 +        private final Iterable<? extends CompilationUnitTree> cuts;
  55.111          private final List<? extends Tree> units;
  55.112  
  55.113          ParseTask(final String source) {
  55.114 @@ -204,16 +180,13 @@
  55.115                      new StringSourceHandler(),
  55.116                      "-XDallowStringFolding=false", "-proc:none");
  55.117              ReplParserFactory.instance(getContext());
  55.118 -            Iterable<? extends CompilationUnitTree> asts = parse();
  55.119 -            Iterator<? extends CompilationUnitTree> it = asts.iterator();
  55.120 -            if (it.hasNext()) {
  55.121 -                this.cut = it.next();
  55.122 -                List<? extends ImportTree> imps = cut.getImports();
  55.123 -                this.units = !imps.isEmpty() ? imps : cut.getTypeDecls();
  55.124 -            } else {
  55.125 -                this.cut = null;
  55.126 -                this.units = Collections.emptyList();
  55.127 -            }
  55.128 +            cuts = parse();
  55.129 +            units = Util.stream(cuts)
  55.130 +                    .flatMap(cut -> {
  55.131 +                        List<? extends ImportTree> imps = cut.getImports();
  55.132 +                        return (!imps.isEmpty() ? imps : cut.getTypeDecls()).stream();
  55.133 +                    })
  55.134 +                    .collect(toList());
  55.135          }
  55.136  
  55.137          private Iterable<? extends CompilationUnitTree> parse() {
  55.138 @@ -229,8 +202,8 @@
  55.139          }
  55.140  
  55.141          @Override
  55.142 -        CompilationUnitTree cuTree() {
  55.143 -            return cut;
  55.144 +        Iterable<? extends CompilationUnitTree> cuTrees() {
  55.145 +            return cuts;
  55.146          }
  55.147      }
  55.148  
  55.149 @@ -239,30 +212,26 @@
  55.150       */
  55.151      class AnalyzeTask extends BaseTask {
  55.152  
  55.153 -        private final CompilationUnitTree cut;
  55.154 +        private final Iterable<? extends CompilationUnitTree> cuts;
  55.155  
  55.156 -        AnalyzeTask(final OuterWrap wrap) {
  55.157 -            this(Stream.of(wrap),
  55.158 -                    new WrapSourceHandler(wrap),
  55.159 -                    "-XDshouldStopPolicy=FLOW", "-proc:none");
  55.160 +        AnalyzeTask(final OuterWrap wrap, String... extraArgs) {
  55.161 +            this(Collections.singletonList(wrap), extraArgs);
  55.162          }
  55.163  
  55.164 -        AnalyzeTask(final Collection<Unit> units) {
  55.165 -            this(units.stream(), new UnitSourceHandler(),
  55.166 -                    "-XDshouldStopPolicy=FLOW", "-Xlint:unchecked", "-proc:none");
  55.167 +        AnalyzeTask(final Collection<OuterWrap> wraps, String... extraArgs) {
  55.168 +            this(wraps.stream(),
  55.169 +                    new WrapSourceHandler(),
  55.170 +                    Util.join(new String[] {
  55.171 +                        "-XDshouldStopPolicy=FLOW", "-Xlint:unchecked",
  55.172 +                        "-XaddExports:jdk.jshell/jdk.internal.jshell.remote=ALL-UNNAMED",
  55.173 +                        "-proc:none"
  55.174 +                    }, extraArgs));
  55.175          }
  55.176  
  55.177 -        <T>AnalyzeTask(final Stream<T> stream, SourceHandler<T> sourceHandler,
  55.178 +        private <T>AnalyzeTask(final Stream<T> stream, SourceHandler<T> sourceHandler,
  55.179                  String... extraOptions) {
  55.180              super(stream, sourceHandler, extraOptions);
  55.181 -            Iterator<? extends CompilationUnitTree> cuts = analyze().iterator();
  55.182 -            if (cuts.hasNext()) {
  55.183 -                this.cut = cuts.next();
  55.184 -                //proc.debug("AnalyzeTask element=%s  cutp=%s  cut=%s\n", e, cutp, cut);
  55.185 -            } else {
  55.186 -                this.cut = null;
  55.187 -                //proc.debug("AnalyzeTask -- no elements -- %s\n", getDiagnostics());
  55.188 -            }
  55.189 +            cuts = analyze();
  55.190          }
  55.191  
  55.192          private Iterable<? extends CompilationUnitTree> analyze() {
  55.193 @@ -276,8 +245,8 @@
  55.194          }
  55.195  
  55.196          @Override
  55.197 -        CompilationUnitTree cuTree() {
  55.198 -            return cut;
  55.199 +        Iterable<? extends CompilationUnitTree> cuTrees() {
  55.200 +            return cuts;
  55.201          }
  55.202  
  55.203          Elements getElements() {
  55.204 @@ -288,37 +257,18 @@
  55.205              return task.getTypes();
  55.206          }
  55.207      }
  55.208 -    
  55.209 -    private static String[] DEFAULT_COMPILER_OPTIONS = {
  55.210 -        "-Xlint:unchecked", "-proc:none"
  55.211 -    };
  55.212  
  55.213 -    private static String[] args(List<String> compOptions) {
  55.214 -        if (compOptions.isEmpty()) {
  55.215 -            return DEFAULT_COMPILER_OPTIONS;
  55.216 -        } else {
  55.217 -            String[] all = new String[DEFAULT_COMPILER_OPTIONS.length + compOptions.size()];
  55.218 -            for (int i = 0; i < compOptions.size(); i++) {
  55.219 -                all[i] = compOptions.get(i);
  55.220 -            }
  55.221 -            System.arraycopy(DEFAULT_COMPILER_OPTIONS, 0, all, compOptions.size(), DEFAULT_COMPILER_OPTIONS.length);
  55.222 -            return all;
  55.223 -        }
  55.224 -    }
  55.225 -    
  55.226      /**
  55.227       * Unit the wrapped snippet to class files.
  55.228       */
  55.229      class CompileTask extends BaseTask {
  55.230  
  55.231 -        private final Map<Unit, List<OutputMemoryJavaFileObject>> classObjs = new HashMap<>();
  55.232 +        private final Map<OuterWrap, List<OutputMemoryJavaFileObject>> classObjs = new HashMap<>();
  55.233  
  55.234 -        CompileTask(Collection<Unit> units, List<String> compilerOptions) {
  55.235 -            super(units.stream(), new UnitSourceHandler(),
  55.236 -                    args(compilerOptions)
  55.237 -            );
  55.238 +        CompileTask(final Collection<OuterWrap> wraps) {
  55.239 +            super(wraps.stream(), new WrapSourceHandler(),
  55.240 +                    "-Xlint:unchecked", "-XaddExports:jdk.jshell/jdk.internal.jshell.remote=ALL-UNNAMED", "-proc:none", "-parameters");
  55.241          }
  55.242 -        
  55.243  
  55.244          boolean compile() {
  55.245              fileManager.registerClassFileCreationListener(this::listenForNewClassFile);
  55.246 @@ -327,13 +277,19 @@
  55.247              return result;
  55.248          }
  55.249  
  55.250 -
  55.251 -        List<ClassInfo> classInfoList(Unit u) {
  55.252 -            List<OutputMemoryJavaFileObject> l = classObjs.get(u);
  55.253 -            if (l == null) return Collections.emptyList();
  55.254 -            return l.stream()
  55.255 -                    .map(fo -> state.classTracker.classInfo(fo.getName(), fo.getBytes()))
  55.256 -                    .collect(Collectors.toList());
  55.257 +        // Returns the list of classes generated during this compile.
  55.258 +        // Stores the mapping between class name and current compiled bytes.
  55.259 +        List<String> classList(OuterWrap w) {
  55.260 +            List<OutputMemoryJavaFileObject> l = classObjs.get(w);
  55.261 +            if (l == null) {
  55.262 +                return Collections.emptyList();
  55.263 +            }
  55.264 +            List<String> list = new ArrayList<>();
  55.265 +            for (OutputMemoryJavaFileObject fo : l) {
  55.266 +                state.setClassnameToBytes(fo.getName(), fo.getBytes());
  55.267 +                list.add(fo.getName());
  55.268 +            }
  55.269 +            return list;
  55.270          }
  55.271  
  55.272          private void listenForNewClassFile(OutputMemoryJavaFileObject jfo, JavaFileManager.Location location,
  55.273 @@ -341,17 +297,17 @@
  55.274              //debug("listenForNewClassFile %s loc=%s kind=%s\n", className, location, kind);
  55.275              if (location == CLASS_OUTPUT) {
  55.276                  state.debug(DBG_GEN, "Compiler generating class %s\n", className);
  55.277 -                Unit u = ((sibling instanceof SourceMemoryJavaFileObject)
  55.278 -                        && (((SourceMemoryJavaFileObject) sibling).getOrigin() instanceof Unit))
  55.279 -                        ? (Unit) ((SourceMemoryJavaFileObject) sibling).getOrigin()
  55.280 +                OuterWrap w = ((sibling instanceof SourceMemoryJavaFileObject)
  55.281 +                        && (((SourceMemoryJavaFileObject) sibling).getOrigin() instanceof OuterWrap))
  55.282 +                        ? (OuterWrap) ((SourceMemoryJavaFileObject) sibling).getOrigin()
  55.283                          : null;
  55.284 -                classObjs.compute(u, (k, v) -> (v == null)? new ArrayList<>() : v)
  55.285 +                classObjs.compute(w, (k, v) -> (v == null)? new ArrayList<>() : v)
  55.286                          .add(jfo);
  55.287              }
  55.288          }
  55.289  
  55.290          @Override
  55.291 -        CompilationUnitTree cuTree() {
  55.292 +        Iterable<? extends CompilationUnitTree> cuTrees() {
  55.293              throw new UnsupportedOperationException("Not supported.");
  55.294          }
  55.295      }
  55.296 @@ -381,7 +337,11 @@
  55.297                      compilationUnits, context);
  55.298          }
  55.299  
  55.300 -        abstract CompilationUnitTree cuTree();
  55.301 +        abstract Iterable<? extends CompilationUnitTree> cuTrees();
  55.302 +
  55.303 +        CompilationUnitTree firstCuTree() {
  55.304 +            return cuTrees().iterator().next();
  55.305 +        }
  55.306  
  55.307          Diag diag(Diagnostic<? extends JavaFileObject> diag) {
  55.308              return sourceHandler.diag(diag);
  55.309 @@ -419,7 +379,7 @@
  55.310                  LinkedHashMap<String, Diag> diagMap = new LinkedHashMap<>();
  55.311                  for (Diagnostic<? extends JavaFileObject> in : diagnostics.getDiagnostics()) {
  55.312                      Diag d = diag(in);
  55.313 -                    String uniqueKey = d.getCode() + ":" + d.getPosition() + ":" + d.getMessage(null);
  55.314 +                    String uniqueKey = d.getCode() + ":" + d.getPosition() + ":" + d.getMessage(PARSED_LOCALE);
  55.315                      diagMap.put(uniqueKey, d);
  55.316                  }
  55.317                  diags = new DiagList(diagMap.values());
  55.318 @@ -434,7 +394,7 @@
  55.319          String shortErrorMessage() {
  55.320              StringBuilder sb = new StringBuilder();
  55.321              for (Diag diag : getDiagnostics()) {
  55.322 -                for (String line : diag.getMessage(null).split("\\r?\\n")) {
  55.323 +                for (String line : diag.getMessage(PARSED_LOCALE).split("\\r?\\n")) {
  55.324                      if (!line.trim().startsWith("location:")) {
  55.325                          sb.append(line);
  55.326                      }
  55.327 @@ -446,7 +406,7 @@
  55.328          void debugPrintDiagnostics(String src) {
  55.329              for (Diag diag : getDiagnostics()) {
  55.330                  state.debug(DBG_GEN, "ERROR --\n");
  55.331 -                for (String line : diag.getMessage(null).split("\\r?\\n")) {
  55.332 +                for (String line : diag.getMessage(PARSED_LOCALE).split("\\r?\\n")) {
  55.333                      if (!line.trim().startsWith("location:")) {
  55.334                          state.debug(DBG_GEN, "%s\n", line);
  55.335                      }
    56.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java	Fri Jun 03 17:06:38 2016 +0200
    56.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java	Fri Sep 02 23:54:26 2016 +0200
    56.3 @@ -41,14 +41,18 @@
    56.4  import com.sun.tools.javac.code.Type.MethodType;
    56.5  import com.sun.tools.javac.code.Types;
    56.6  import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    56.7 -import com.sun.tools.javac.util.JavacMessages;
    56.8  import com.sun.tools.javac.util.Name;
    56.9  import static jdk.jshell.Util.isDoIt;
   56.10 +import jdk.jshell.TaskFactory.AnalyzeTask;
   56.11  import jdk.jshell.Wrap.Range;
   56.12 +
   56.13  import java.util.List;
   56.14  import java.util.Locale;
   56.15 -import java.util.function.BinaryOperator;
   56.16 +
   56.17 +import java.util.function.Predicate;
   56.18 +import java.util.stream.Stream;
   56.19  import javax.lang.model.type.TypeMirror;
   56.20 +import jdk.jshell.Util.Pair;
   56.21  
   56.22  /**
   56.23   * Utilities for analyzing compiler API parse trees.
   56.24 @@ -68,23 +72,48 @@
   56.25      }
   56.26  
   56.27      private final TaskFactory.BaseTask bt;
   56.28 -    private ClassTree firstClass;
   56.29 +    private final ClassTree targetClass;
   56.30 +    private final CompilationUnitTree targetCompilationUnit;
   56.31      private SourcePositions theSourcePositions = null;
   56.32  
   56.33 -    TreeDissector(TaskFactory.BaseTask bt) {
   56.34 +    private TreeDissector(TaskFactory.BaseTask bt, CompilationUnitTree targetCompilationUnit, ClassTree targetClass) {
   56.35          this.bt = bt;
   56.36 +        this.targetCompilationUnit = targetCompilationUnit;
   56.37 +        this.targetClass = targetClass;
   56.38      }
   56.39  
   56.40 +    static TreeDissector createByFirstClass(TaskFactory.BaseTask bt) {
   56.41 +        Pair<CompilationUnitTree, ClassTree> pair = classes(bt.firstCuTree())
   56.42 +                .findFirst().orElseGet(() -> new Pair<>(bt.firstCuTree(), null));
   56.43  
   56.44 -    ClassTree firstClass() {
   56.45 -        if (firstClass == null) {
   56.46 -            firstClass = computeFirstClass();
   56.47 -        }
   56.48 -        return firstClass;
   56.49 +        return new TreeDissector(bt, pair.first, pair.second);
   56.50      }
   56.51  
   56.52 -    CompilationUnitTree cuTree() {
   56.53 -        return bt.cuTree();
   56.54 +    private static final Predicate<? super Tree> isClassOrInterface =
   56.55 +            t -> t.getKind() == Tree.Kind.CLASS || t.getKind() == Tree.Kind.INTERFACE;
   56.56 +
   56.57 +    private static Stream<Pair<CompilationUnitTree, ClassTree>> classes(CompilationUnitTree cut) {
   56.58 +        return cut == null
   56.59 +                ? Stream.empty()
   56.60 +                : cut.getTypeDecls().stream()
   56.61 +                        .filter(isClassOrInterface)
   56.62 +                        .map(decl -> new Pair<>(cut, (ClassTree)decl));
   56.63 +    }
   56.64 +
   56.65 +    private static Stream<Pair<CompilationUnitTree, ClassTree>> classes(Iterable<? extends CompilationUnitTree> cuts) {
   56.66 +        return Util.stream(cuts)
   56.67 +                .flatMap(TreeDissector::classes);
   56.68 +    }
   56.69 +
   56.70 +    static TreeDissector createBySnippet(TaskFactory.BaseTask bt, Snippet si) {
   56.71 +        String name = si.className();
   56.72 +
   56.73 +        Pair<CompilationUnitTree, ClassTree> pair = classes(bt.cuTrees())
   56.74 +                .filter(p -> p.second.getSimpleName().contentEquals(name))
   56.75 +                .findFirst().orElseThrow(() ->
   56.76 +                        new IllegalArgumentException("Class " + name + " is not found."));
   56.77 +
   56.78 +        return new TreeDissector(bt, pair.first, pair.second);
   56.79      }
   56.80  
   56.81      Types types() {
   56.82 @@ -103,11 +132,11 @@
   56.83      }
   56.84  
   56.85      int getStartPosition(Tree tree) {
   56.86 -        return (int) getSourcePositions().getStartPosition(cuTree(), tree);
   56.87 +        return (int) getSourcePositions().getStartPosition(targetCompilationUnit, tree);
   56.88      }
   56.89  
   56.90      int getEndPosition(Tree tree) {
   56.91 -        return (int) getSourcePositions().getEndPosition(cuTree(), tree);
   56.92 +        return (int) getSourcePositions().getEndPosition(targetCompilationUnit, tree);
   56.93      }
   56.94  
   56.95      Range treeToRange(Tree tree) {
   56.96 @@ -133,18 +162,28 @@
   56.97          return new Range(start, end);
   56.98      }
   56.99  
  56.100 -    Tree firstClassMember() {
  56.101 -        if (firstClass() != null) {
  56.102 -            //TODO: missing classes
  56.103 -            for (Tree mem : firstClass().getMembers()) {
  56.104 -                if (mem.getKind() == Tree.Kind.VARIABLE) {
  56.105 -                    return mem;
  56.106 -                }
  56.107 -                if (mem.getKind() == Tree.Kind.METHOD) {
  56.108 -                    MethodTree mt = (MethodTree) mem;
  56.109 -                    if (!isDoIt(mt.getName()) && !mt.getName().toString().equals("<init>")) {
  56.110 +    MethodTree method(MethodSnippet msn) {
  56.111 +        if (targetClass == null) {
  56.112 +            return null;
  56.113 +        }
  56.114 +        OuterWrap ow = msn.outerWrap();
  56.115 +        if (!(ow instanceof OuterSnippetsClassWrap)) {
  56.116 +            return null;
  56.117 +        }
  56.118 +        int ordinal = ((OuterSnippetsClassWrap) ow).ordinal(msn);
  56.119 +        if (ordinal < 0) {
  56.120 +            return null;
  56.121 +        }
  56.122 +        int count = 0;
  56.123 +        String name = msn.name();
  56.124 +        for (Tree mem : targetClass.getMembers()) {
  56.125 +            if (mem.getKind() == Tree.Kind.METHOD) {
  56.126 +                MethodTree mt = (MethodTree) mem;
  56.127 +                if (mt.getName().toString().equals(name)) {
  56.128 +                    if (count == ordinal) {
  56.129                          return mt;
  56.130                      }
  56.131 +                    ++count;
  56.132                  }
  56.133              }
  56.134          }
  56.135 @@ -152,8 +191,8 @@
  56.136      }
  56.137  
  56.138      StatementTree firstStatement() {
  56.139 -        if (firstClass() != null) {
  56.140 -            for (Tree mem : firstClass().getMembers()) {
  56.141 +        if (targetClass != null) {
  56.142 +            for (Tree mem : targetClass.getMembers()) {
  56.143                  if (mem.getKind() == Tree.Kind.METHOD) {
  56.144                      MethodTree mt = (MethodTree) mem;
  56.145                      if (isDoIt(mt.getName())) {
  56.146 @@ -169,8 +208,8 @@
  56.147      }
  56.148  
  56.149      VariableTree firstVariable() {
  56.150 -        if (firstClass() != null) {
  56.151 -            for (Tree mem : firstClass().getMembers()) {
  56.152 +        if (targetClass != null) {
  56.153 +            for (Tree mem : targetClass.getMembers()) {
  56.154                  if (mem.getKind() == Tree.Kind.VARIABLE) {
  56.155                      VariableTree vt = (VariableTree) mem;
  56.156                      return vt;
  56.157 @@ -180,31 +219,18 @@
  56.158          return null;
  56.159      }
  56.160  
  56.161 -    private ClassTree computeFirstClass() {
  56.162 -        if (cuTree() == null) {
  56.163 -            return null;
  56.164 -        }
  56.165 -        for (Tree decl : cuTree().getTypeDecls()) {
  56.166 -            if (decl.getKind() == Tree.Kind.CLASS || decl.getKind() == Tree.Kind.INTERFACE) {
  56.167 -                return (ClassTree) decl;
  56.168 -            }
  56.169 -        }
  56.170 -        return null;
  56.171 -    }
  56.172  
  56.173 -    ExpressionInfo typeOfReturnStatement(JavacMessages messages, BinaryOperator<String> fullClassNameAndPackageToClass) {
  56.174 +    ExpressionInfo typeOfReturnStatement(AnalyzeTask at, JShell state) {
  56.175          ExpressionInfo ei = new ExpressionInfo();
  56.176          Tree unitTree = firstStatement();
  56.177          if (unitTree instanceof ReturnTree) {
  56.178              ei.tree = ((ReturnTree) unitTree).getExpression();
  56.179              if (ei.tree != null) {
  56.180 -                TreePath viPath = trees().getPath(cuTree(), ei.tree);
  56.181 +                TreePath viPath = trees().getPath(targetCompilationUnit, ei.tree);
  56.182                  if (viPath != null) {
  56.183                      TypeMirror tm = trees().getTypeMirror(viPath);
  56.184                      if (tm != null) {
  56.185 -                        Type type = (Type)tm;
  56.186 -                        TypePrinter tp = new TypePrinter(messages, fullClassNameAndPackageToClass, type);
  56.187 -                        ei.typeName = tp.visit(type, Locale.getDefault());
  56.188 +                        ei.typeName = printType(at, state, tm);
  56.189                          switch (tm.getKind()) {
  56.190                              case VOID:
  56.191                              case NONE:
  56.192 @@ -228,8 +254,8 @@
  56.193          return ei;
  56.194      }
  56.195  
  56.196 -    String typeOfMethod() {
  56.197 -        Tree unitTree = firstClassMember();
  56.198 +    String typeOfMethod(MethodSnippet msn) {
  56.199 +        Tree unitTree = method(msn);
  56.200          if (unitTree instanceof JCMethodDecl) {
  56.201              JCMethodDecl mtree = (JCMethodDecl) unitTree;
  56.202              Type mt = types().erasure(mtree.type);
  56.203 @@ -246,6 +272,12 @@
  56.204          return sg.toString();
  56.205      }
  56.206  
  56.207 +    public static String printType(AnalyzeTask at, JShell state, TypeMirror type) {
  56.208 +        Type typeImpl = (Type) type;
  56.209 +        TypePrinter tp = new TypePrinter(at.messages(), state.maps::fullClassNameAndPackageToClass, typeImpl);
  56.210 +        return tp.visit(typeImpl, Locale.getDefault());
  56.211 +    }
  56.212 +
  56.213      /**
  56.214       * Signature Generation
  56.215       */
    57.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/TypeDeclSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    57.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/TypeDeclSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    57.3 @@ -22,6 +22,7 @@
    57.4   * or visit www.oracle.com if you need additional information or have any
    57.5   * questions.
    57.6   */
    57.7 +
    57.8  package jdk.jshell;
    57.9  
   57.10  import java.util.Collection;
    58.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/TypePrinter.java	Fri Jun 03 17:06:38 2016 +0200
    58.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/TypePrinter.java	Fri Sep 02 23:54:26 2016 +0200
    58.3 @@ -41,15 +41,15 @@
    58.4   * Print types in source form.
    58.5   */
    58.6  class TypePrinter extends Printer {
    58.7 +    private static final String OBJECT = "Object";
    58.8  
    58.9      private final JavacMessages messages;
   58.10      private final BinaryOperator<String> fullClassNameAndPackageToClass;
   58.11 -    private final Type typeToPrint;
   58.12 +    private boolean useWildCard = false;
   58.13  
   58.14      TypePrinter(JavacMessages messages, BinaryOperator<String> fullClassNameAndPackageToClass, Type typeToPrint) {
   58.15          this.messages = messages;
   58.16          this.fullClassNameAndPackageToClass = fullClassNameAndPackageToClass;
   58.17 -        this.typeToPrint = typeToPrint;
   58.18      }
   58.19  
   58.20      @Override
   58.21 @@ -64,21 +64,40 @@
   58.22  
   58.23      @Override
   58.24      public String visitCapturedType(Type.CapturedType t, Locale locale) {
   58.25 -        if (t == typeToPrint) {
   58.26 -            return visit(t.getUpperBound(), locale);
   58.27 -        } else {
   58.28 -            return visit(t.wildcard, locale);
   58.29 +        return visit(t.wildcard, locale);
   58.30 +    }
   58.31 +
   58.32 +    @Override
   58.33 +    public String visitWildcardType(Type.WildcardType wt, Locale locale) {
   58.34 +        if (useWildCard) { // at TypeArgument(ex: List<? extends T>)
   58.35 +            return super.visitWildcardType(wt, locale);
   58.36 +        } else { // at TopLevelType(ex: ? extends List<T>, ? extends Number[][])
   58.37 +            Type extendsBound = wt.getExtendsBound();
   58.38 +            return extendsBound == null
   58.39 +                    ? OBJECT
   58.40 +                    : visit(extendsBound, locale);
   58.41          }
   58.42      }
   58.43  
   58.44      @Override
   58.45      public String visitType(Type t, Locale locale) {
   58.46          String s = (t.tsym == null || t.tsym.name == null)
   58.47 -                ? "Object" // none
   58.48 +                ? OBJECT // none
   58.49                  : t.tsym.name.toString();
   58.50          return s;
   58.51      }
   58.52  
   58.53 +    @Override
   58.54 +    public String visitClassType(ClassType ct, Locale locale) {
   58.55 +        boolean prevUseWildCard = useWildCard;
   58.56 +        try {
   58.57 +            useWildCard = true;
   58.58 +            return super.visitClassType(ct, locale);
   58.59 +        } finally {
   58.60 +            useWildCard = prevUseWildCard;
   58.61 +        }
   58.62 +    }
   58.63 +
   58.64      /**
   58.65       * Converts a class name into a (possibly localized) string. Anonymous
   58.66       * inner classes get converted into a localized string.
   58.67 @@ -101,7 +120,7 @@
   58.68              }
   58.69              return s.toString();
   58.70              ***/
   58.71 -            return "Object";
   58.72 +            return OBJECT;
   58.73          } else if (sym.name.length() == 0) {
   58.74              // Anonymous
   58.75              String s;
    59.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/Unit.java	Fri Jun 03 17:06:38 2016 +0200
    59.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/Unit.java	Fri Sep 02 23:54:26 2016 +0200
    59.3 @@ -1,5 +1,5 @@
    59.4  /*
    59.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    59.6 + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
    59.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    59.8   *
    59.9   * This code is free software; you can redistribute it and/or modify it
   59.10 @@ -30,18 +30,14 @@
   59.11  import java.util.Collections;
   59.12  import java.util.LinkedHashSet;
   59.13  import java.util.List;
   59.14 -import java.util.Map;
   59.15  import java.util.Set;
   59.16  import java.util.stream.Stream;
   59.17 -import com.sun.jdi.ReferenceType;
   59.18  import jdk.jshell.Snippet.Kind;
   59.19  import jdk.jshell.Snippet.Status;
   59.20  import jdk.jshell.Snippet.SubKind;
   59.21  import jdk.jshell.TaskFactory.AnalyzeTask;
   59.22 -import jdk.jshell.ClassTracker.ClassInfo;
   59.23  import jdk.jshell.TaskFactory.CompileTask;
   59.24  import static java.util.stream.Collectors.toList;
   59.25 -import static java.util.stream.Collectors.toMap;
   59.26  import static java.util.stream.Collectors.toSet;
   59.27  import static jdk.internal.jshell.debug.InternalDebugControl.DBG_EVNT;
   59.28  import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
   59.29 @@ -50,6 +46,7 @@
   59.30  import static jdk.jshell.Snippet.Status.RECOVERABLE_NOT_DEFINED;
   59.31  import static jdk.jshell.Snippet.Status.REJECTED;
   59.32  import static jdk.jshell.Snippet.Status.VALID;
   59.33 +import static jdk.jshell.Util.PARSED_LOCALE;
   59.34  import static jdk.jshell.Util.expunge;
   59.35  
   59.36  /**
   59.37 @@ -67,7 +64,7 @@
   59.38      private final DiagList generatedDiagnostics;
   59.39  
   59.40      private int seq;
   59.41 -    private int seqInitial;
   59.42 +    private String classNameInitial;
   59.43      private Wrap activeGuts;
   59.44      private Status status;
   59.45      private Status prevStatus;
   59.46 @@ -78,7 +75,7 @@
   59.47      private SnippetEvent replaceOldEvent;
   59.48      private List<SnippetEvent> secondaryEvents;
   59.49      private boolean isAttemptingCorral;
   59.50 -    private List<ClassInfo> toRedefine;
   59.51 +    private List<String> toRedefine;
   59.52      private boolean dependenciesNeeded;
   59.53  
   59.54      Unit(JShell state, Snippet si, Snippet causalSnippet,
   59.55 @@ -94,7 +91,7 @@
   59.56          this.generatedDiagnostics = generatedDiagnostics;
   59.57  
   59.58          this.seq = isNew? 0 : siOld.sequenceNumber();
   59.59 -        this.seqInitial = seq;
   59.60 +        this.classNameInitial = isNew? "<none>" : siOld.className();
   59.61          this.prevStatus = (isNew || isDependency)
   59.62                  ? si.status()
   59.63                  : siOld.status();
   59.64 @@ -135,34 +132,50 @@
   59.65          return isDependency;
   59.66      }
   59.67  
   59.68 -    boolean isNew() {
   59.69 -        return isNew;
   59.70 -    }
   59.71 -
   59.72 -    boolean isRedundant() {
   59.73 -        return !isNew && !isDependency() && !si.isExecutable() &&
   59.74 -                prevStatus.isDefined &&
   59.75 -                siOld.source().equals(si.source());
   59.76 -    }
   59.77 -
   59.78 -    void initialize(Collection<Unit> working) {
   59.79 +    void initialize() {
   59.80          isAttemptingCorral = false;
   59.81          dependenciesNeeded = false;
   59.82          toRedefine = null; // assure NPE if classToLoad not called
   59.83          activeGuts = si.guts();
   59.84          markOldDeclarationOverwritten();
   59.85 -        setWrap(working, working);
   59.86      }
   59.87  
   59.88 -    void setWrap(Collection<Unit> except, Collection<Unit> plus) {
   59.89 -        si.setOuterWrap(isImport()
   59.90 -                ? OuterWrap.wrapImport(si.source(), activeGuts)
   59.91 -                : state.eval.wrapInClass(si,
   59.92 -                        except.stream().map(u -> u.snippet().key()).collect(toSet()),
   59.93 -                        activeGuts,
   59.94 -                        plus.stream().map(u -> u.snippet())
   59.95 -                                .filter(sn -> sn != si)
   59.96 -                                .collect(toList())));
   59.97 +    // Set the outer wrap of our Snippet
   59.98 +    void setWrap(Collection<Unit> exceptUnit, Collection<Unit> plusUnfiltered) {
   59.99 +        if (isImport()) {
  59.100 +            si.setOuterWrap(state.outerMap.wrapImport(activeGuts, si));
  59.101 +        } else {
  59.102 +            // Collect Units for be wrapped together.  Just this except for overloaded methods
  59.103 +            List<Unit> units;
  59.104 +            if (snippet().kind() == Kind.METHOD) {
  59.105 +                String name = ((MethodSnippet) snippet()).name();
  59.106 +                units = plusUnfiltered.stream()
  59.107 +                        .filter(u -> u.snippet().kind() == Kind.METHOD &&
  59.108 +                                 ((MethodSnippet) u.snippet()).name().equals(name))
  59.109 +                        .collect(toList());
  59.110 +            } else {
  59.111 +                units = Collections.singletonList(this);
  59.112 +            }
  59.113 +            // Keys to exclude from imports
  59.114 +            Set<Key> except = exceptUnit.stream()
  59.115 +                    .map(u -> u.snippet().key())
  59.116 +                    .collect(toSet());
  59.117 +            // Snippets to add to imports
  59.118 +            Collection<Snippet> plus = plusUnfiltered.stream()
  59.119 +                    .filter(u -> !units.contains(u))
  59.120 +                    .map(u -> u.snippet())
  59.121 +                    .collect(toList());
  59.122 +            // Snippets to wrap in an outer
  59.123 +            List<Snippet> snippets = units.stream()
  59.124 +                    .map(u -> u.snippet())
  59.125 +                    .collect(toList());
  59.126 +            // Snippet wraps to wrap in an outer
  59.127 +            List<Wrap> wraps = units.stream()
  59.128 +                    .map(u -> u.activeGuts)
  59.129 +                    .collect(toList());
  59.130 +            // Set the outer wrap for this snippet
  59.131 +            si.setOuterWrap(state.outerMap.wrapInClass(except, plus, snippets, wraps));
  59.132 +        }
  59.133      }
  59.134  
  59.135      void setDiagnostics(AnalyzeTask ct) {
  59.136 @@ -225,7 +238,7 @@
  59.137          return false;
  59.138      }
  59.139  
  59.140 -    void setStatus() {
  59.141 +    void setStatus(AnalyzeTask at) {
  59.142          if (!compilationDiagnostics.hasErrors()) {
  59.143              status = VALID;
  59.144          } else if (isRecoverable()) {
  59.145 @@ -237,16 +250,12 @@
  59.146          } else {
  59.147              status = REJECTED;
  59.148          }
  59.149 -        checkForOverwrite();
  59.150 +        checkForOverwrite(at);
  59.151  
  59.152          state.debug(DBG_GEN, "setStatus() %s - status: %s\n",
  59.153                  si, status);
  59.154      }
  59.155  
  59.156 -    /**
  59.157 -     * Must be called for each unit
  59.158 -     * @return
  59.159 -     */
  59.160      boolean isDefined() {
  59.161          return status.isDefined;
  59.162      }
  59.163 @@ -256,21 +265,28 @@
  59.164       * Requires loading of returned list.
  59.165       * @return the list of classes to load
  59.166       */
  59.167 -    Stream<ClassInfo> classesToLoad(List<ClassInfo> cil) {
  59.168 +    Stream<String> classesToLoad(List<String> classnames) {
  59.169          toRedefine = new ArrayList<>();
  59.170 -        List<ClassInfo> toLoad = new ArrayList<>();
  59.171 +        List<String> toLoad = new ArrayList<>();
  59.172          if (status.isDefined && !isImport()) {
  59.173 -            cil.stream().forEach(ci -> {
  59.174 -                if (!ci.isLoaded()) {
  59.175 -                    if (ci.getReferenceTypeOrNull() == null) {
  59.176 -                        toLoad.add(ci);
  59.177 -                        ci.setLoaded();
  59.178 +            // Classes should only be loaded/redefined if the compile left them
  59.179 +            // in a defined state.  Imports do not have code and are not loaded.
  59.180 +            for (String cn : classnames) {
  59.181 +                switch (state.executionControl().getClassStatus(cn)) {
  59.182 +                    case UNKNOWN:
  59.183 +                        // If not loaded, add to the list of classes to load.
  59.184 +                        toLoad.add(cn);
  59.185                          dependenciesNeeded = true;
  59.186 -                    } else {
  59.187 -                        toRedefine.add(ci);
  59.188 -                    }
  59.189 +                        break;
  59.190 +                    case NOT_CURRENT:
  59.191 +                        // If loaded but out of date, add to the list of classes to attempt redefine.
  59.192 +                        toRedefine.add(cn);
  59.193 +                        break;
  59.194 +                    case CURRENT:
  59.195 +                        // Loaded and current, so nothing to do
  59.196 +                        break;
  59.197                  }
  59.198 -            });
  59.199 +            }
  59.200          }
  59.201          return toLoad.stream();
  59.202      }
  59.203 @@ -281,19 +297,9 @@
  59.204       * @return true if all redefines succeeded (can be vacuously true)
  59.205       */
  59.206      boolean doRedefines() {
  59.207 -         if (toRedefine.isEmpty()) {
  59.208 -            return true;
  59.209 -        }
  59.210 -        Map<Object, byte[]> mp = toRedefine.stream()
  59.211 -                .collect(toMap(ci -> ci.getReferenceTypeOrNull(), ci -> ci.getBytes()));
  59.212 -        if (state.executionControl().commandRedefine(mp)) {
  59.213 -            // success, mark as loaded
  59.214 -            toRedefine.stream().forEach(ci -> ci.setLoaded());
  59.215 -            return true;
  59.216 -        } else {
  59.217 -            // failed to redefine
  59.218 -            return false;
  59.219 -        }
  59.220 +        return toRedefine.isEmpty()
  59.221 +                ? true
  59.222 +                : state.executionControl().redefine(toRedefine);
  59.223      }
  59.224  
  59.225      void markForReplacement() {
  59.226 @@ -307,11 +313,13 @@
  59.227  
  59.228      private boolean sigChanged() {
  59.229          return (status.isDefined != prevStatus.isDefined)
  59.230 -                || (seq != seqInitial && status.isDefined)
  59.231 +                || (status.isDefined && !si.className().equals(classNameInitial))
  59.232                  || signatureChanged;
  59.233      }
  59.234  
  59.235      Stream<Unit> effectedDependents() {
  59.236 +        //System.err.printf("effectedDependents sigChanged=%b  dependenciesNeeded=%b   status=%s\n",
  59.237 +        //       sigChanged(), dependenciesNeeded, status);
  59.238          return sigChanged() || dependenciesNeeded || status == RECOVERABLE_NOT_DEFINED
  59.239                  ? dependents()
  59.240                  : Stream.empty();
  59.241 @@ -361,17 +369,18 @@
  59.242                  si, status, unresolved);
  59.243      }
  59.244  
  59.245 -    private void checkForOverwrite() {
  59.246 +    private void checkForOverwrite(AnalyzeTask at) {
  59.247          secondaryEvents = new ArrayList<>();
  59.248          if (replaceOldEvent != null) secondaryEvents.add(replaceOldEvent);
  59.249  
  59.250          // Defined methods can overwrite methods of other (equivalent) snippets
  59.251 -        if (si.kind() == Kind.METHOD && status.isDefined) {
  59.252 -            String oqpt = ((MethodSnippet) si).qualifiedParameterTypes();
  59.253 -            String nqpt = computeQualifiedParameterTypes(si);
  59.254 +        if (isNew && si.kind() == Kind.METHOD && status.isDefined) {
  59.255 +            MethodSnippet msi = (MethodSnippet)si;
  59.256 +            String oqpt = msi.qualifiedParameterTypes();
  59.257 +            String nqpt = computeQualifiedParameterTypes(at, msi);
  59.258              if (!nqpt.equals(oqpt)) {
  59.259 -                ((MethodSnippet) si).setQualifiedParamaterTypes(nqpt);
  59.260 -                Status overwrittenStatus = overwriteMatchingMethod(si);
  59.261 +                msi.setQualifiedParamaterTypes(nqpt);
  59.262 +                Status overwrittenStatus = overwriteMatchingMethod(msi);
  59.263                  if (overwrittenStatus != null) {
  59.264                      prevStatus = overwrittenStatus;
  59.265                      signatureChanged = true;
  59.266 @@ -383,19 +392,19 @@
  59.267      // Check if there is a method whose user-declared parameter types are
  59.268      // different (and thus has a different snippet) but whose compiled parameter
  59.269      // types are the same. if so, consider it an overwrite replacement.
  59.270 -    private Status overwriteMatchingMethod(Snippet si) {
  59.271 -        String qpt = ((MethodSnippet) si).qualifiedParameterTypes();
  59.272 +    private Status overwriteMatchingMethod(MethodSnippet msi) {
  59.273 +        String qpt = msi.qualifiedParameterTypes();
  59.274  
  59.275          // Look through all methods for a method of the same name, with the
  59.276          // same computed qualified parameter types
  59.277          Status overwrittenStatus = null;
  59.278          for (MethodSnippet sn : state.methods()) {
  59.279 -            if (sn != null && sn != si && sn.status().isActive && sn.name().equals(si.name())) {
  59.280 +            if (sn != null && sn != msi && sn.status().isActive && sn.name().equals(msi.name())) {
  59.281                  if (qpt.equals(sn.qualifiedParameterTypes())) {
  59.282                      overwrittenStatus = sn.status();
  59.283                      SnippetEvent se = new SnippetEvent(
  59.284                              sn, overwrittenStatus, OVERWRITTEN,
  59.285 -                            false, si, null, null);
  59.286 +                            false, msi, null, null);
  59.287                      sn.setOverwritten();
  59.288                      secondaryEvents.add(se);
  59.289                      state.debug(DBG_EVNT,
  59.290 @@ -408,23 +417,19 @@
  59.291          return overwrittenStatus;
  59.292      }
  59.293  
  59.294 -    private String computeQualifiedParameterTypes(Snippet si) {
  59.295 -        MethodSnippet msi = (MethodSnippet) si;
  59.296 -        String qpt;
  59.297 -        AnalyzeTask at = state.taskFactory.new AnalyzeTask(msi.outerWrap());
  59.298 -        String rawSig = new TreeDissector(at).typeOfMethod();
  59.299 +    private String computeQualifiedParameterTypes(AnalyzeTask at, MethodSnippet msi) {
  59.300 +        String rawSig = TreeDissector.createBySnippet(at, msi).typeOfMethod(msi);
  59.301          String signature = expunge(rawSig);
  59.302          int paren = signature.lastIndexOf(')');
  59.303 -        if (paren < 0) {
  59.304 -            // Uncompilable snippet, punt with user parameter types
  59.305 -            qpt = msi.parameterTypes();
  59.306 -        } else {
  59.307 -            qpt = signature.substring(0, paren + 1);
  59.308 -        }
  59.309 -        return qpt;
  59.310 +
  59.311 +        // Extract the parameter type string from the method signature,
  59.312 +        // if method did not compile use the user-supplied parameter types
  59.313 +        return paren >= 0
  59.314 +                ? signature.substring(0, paren + 1)
  59.315 +                : msi.parameterTypes();
  59.316      }
  59.317  
  59.318 -    SnippetEvent event(String value, Exception exception) {
  59.319 +    SnippetEvent event(String value, JShellException exception) {
  59.320          boolean wasSignatureChanged = sigChanged();
  59.321          state.debug(DBG_EVNT, "Snippet: %s id: %s before: %s status: %s sig: %b cause: %s\n",
  59.322                  si, si.id(), prevStatus, si.status(), wasSignatureChanged, causalSnippet);
  59.323 @@ -433,7 +438,9 @@
  59.324      }
  59.325  
  59.326      List<SnippetEvent> secondaryEvents() {
  59.327 -        return secondaryEvents;
  59.328 +        return secondaryEvents==null
  59.329 +                ? Collections.emptyList()
  59.330 +                : secondaryEvents;
  59.331      }
  59.332  
  59.333      @Override
  59.334 @@ -459,7 +466,7 @@
  59.335              for (Diag diag : diags) {
  59.336                  if (diag.isError()) {
  59.337                      if (diag.isResolutionError()) {
  59.338 -                        String m = diag.getMessage(null);
  59.339 +                        String m = diag.getMessage(PARSED_LOCALE);
  59.340                          int symPos = m.indexOf(RESOLVE_ERROR_SYMBOL);
  59.341                          if (symPos >= 0) {
  59.342                              m = m.substring(symPos + RESOLVE_ERROR_SYMBOL.length());
    60.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/UnresolvedReferenceException.java	Fri Jun 03 17:06:38 2016 +0200
    60.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/UnresolvedReferenceException.java	Fri Sep 02 23:54:26 2016 +0200
    60.3 @@ -1,5 +1,5 @@
    60.4  /*
    60.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    60.6 + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
    60.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    60.8   *
    60.9   * This code is free software; you can redistribute it and/or modify it
   60.10 @@ -28,33 +28,33 @@
   60.11  /**
   60.12   * Exception reported on attempting to execute a
   60.13   * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
   60.14 - * method.
   60.15 + * snippet.
   60.16   * <p>
   60.17   * The stack can be queried by methods on <code>Exception</code>.
   60.18   * Note that in stack trace frames representing JShell Snippets,
   60.19   * <code>StackTraceElement.getFileName()</code> will return "#" followed by
   60.20   * the Snippet id and for snippets without a method name (for example an
   60.21 - * expression) <code>StackTraceElement.getMethodName()</code> will be the
   60.22 + * expression) <code>StackTraceElement.getName()</code> will be the
   60.23   * empty string.
   60.24   */
   60.25  @SuppressWarnings("serial")             // serialVersionUID intentionally omitted
   60.26 -public class UnresolvedReferenceException extends Exception {
   60.27 +public class UnresolvedReferenceException extends JShellException {
   60.28  
   60.29 -    final MethodSnippet methodSnippet;
   60.30 +    final DeclarationSnippet snippet;
   60.31  
   60.32 -    UnresolvedReferenceException(MethodSnippet methodSnippet, StackTraceElement[] stackElements) {
   60.33 -        super("Attempt to invoke method with unresolved references");
   60.34 -        this.methodSnippet = methodSnippet;
   60.35 +    UnresolvedReferenceException(DeclarationSnippet snippet, StackTraceElement[] stackElements) {
   60.36 +        super("Attempt to use definition snippet with unresolved references");
   60.37 +        this.snippet = snippet;
   60.38          this.setStackTrace(stackElements);
   60.39      }
   60.40  
   60.41      /**
   60.42 -     * Return the method Snippet which has the unresolved reference(s).
   60.43 -     * @return the <code>MethodSnippet</code> of the
   60.44 +     * Return the Snippet which has the unresolved reference(s).
   60.45 +     * @return the <code>Snippet</code> of the
   60.46       * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
   60.47 -     * method.
   60.48 +     * definition snippet.
   60.49       */
   60.50 -    public MethodSnippet getMethodSnippet() {
   60.51 -        return methodSnippet;
   60.52 +    public DeclarationSnippet getSnippet() {
   60.53 +        return snippet;
   60.54      }
   60.55  }
    61.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/Util.java	Fri Jun 03 17:06:38 2016 +0200
    61.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/Util.java	Fri Sep 02 23:54:26 2016 +0200
    61.3 @@ -1,5 +1,5 @@
    61.4 - /*
    61.5 - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
    61.6 +/*
    61.7 + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
    61.8   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    61.9   *
   61.10   * This code is free software; you can redistribute it and/or modify it
   61.11 @@ -25,20 +25,46 @@
   61.12  
   61.13  package jdk.jshell;
   61.14  
   61.15 +import java.util.ArrayList;
   61.16 +import java.util.Arrays;
   61.17 +import java.util.List;
   61.18 +import java.util.Locale;
   61.19 +import java.util.regex.Pattern;
   61.20  import java.util.stream.Stream;
   61.21  import java.util.stream.StreamSupport;
   61.22  import javax.lang.model.element.Name;
   61.23 -import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
   61.24 -import static jdk.internal.jshell.remote.RemoteCodes.prefixPattern;
   61.25  
   61.26  /**
   61.27 - * Assorted shared utilities.
   61.28 - * @author Robert Field
   61.29 + * Assorted shared utilities and constants.
   61.30   */
   61.31  class Util {
   61.32  
   61.33 -    static final String REPL_CLASS_PREFIX = "$REPL";
   61.34 -    static final String REPL_DOESNOTMATTER_CLASS_NAME = REPL_CLASS_PREFIX+"00DOESNOTMATTER";
   61.35 +    /**
   61.36 +     * The package name of all wrapper classes.
   61.37 +     */
   61.38 +    static final String REPL_PACKAGE = "REPL";
   61.39 +
   61.40 +    /**
   61.41 +     * The prefix for all wrapper class names.
   61.42 +     */
   61.43 +    static final String REPL_CLASS_PREFIX = "$JShell$";
   61.44 +
   61.45 +    /**
   61.46 +     * The name of the invoke method.
   61.47 +     */
   61.48 +    static final String DOIT_METHOD_NAME = "do_it$";
   61.49 +
   61.50 +    /**
   61.51 +     * A pattern matching the full or simple class name of a wrapper class.
   61.52 +     */
   61.53 +    static final Pattern PREFIX_PATTERN = Pattern.compile(
   61.54 +            "(" + REPL_PACKAGE + "\\.)?"
   61.55 +            + "(?<class>" + Pattern.quote(REPL_CLASS_PREFIX)
   61.56 +            + "\\w+" + ")" + "[\\$\\.]?");
   61.57 +
   61.58 +    static final String REPL_DOESNOTMATTER_CLASS_NAME = REPL_CLASS_PREFIX + "DOESNOTMATTER";
   61.59 +
   61.60 +    static final Locale PARSED_LOCALE = Locale.ROOT;
   61.61  
   61.62      static boolean isDoIt(Name name) {
   61.63          return isDoIt(name.toString());
   61.64 @@ -50,7 +76,7 @@
   61.65  
   61.66      static String expunge(String s) {
   61.67          StringBuilder sb = new StringBuilder();
   61.68 -        for (String comp : prefixPattern.split(s)) {
   61.69 +        for (String comp : PREFIX_PATTERN.split(s)) {
   61.70              sb.append(comp);
   61.71          }
   61.72          return sb.toString();
   61.73 @@ -91,4 +117,23 @@
   61.74      static <T> Stream<T> stream(Iterable<T> iterable) {
   61.75          return StreamSupport.stream(iterable.spliterator(), false);
   61.76      }
   61.77 +
   61.78 +    static String[] join(String[] a1, String[] a2) {
   61.79 +        List<String> result = new ArrayList<>();
   61.80 +
   61.81 +        result.addAll(Arrays.asList(a1));
   61.82 +        result.addAll(Arrays.asList(a2));
   61.83 +
   61.84 +        return result.toArray(new String[0]);
   61.85 +    }
   61.86 +
   61.87 +    static class Pair<T, U> {
   61.88 +        final T first;
   61.89 +        final U second;
   61.90 +
   61.91 +        Pair(T first, U second) {
   61.92 +            this.first = first;
   61.93 +            this.second = second;
   61.94 +        }
   61.95 +    }
   61.96  }
    62.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java	Fri Jun 03 17:06:38 2016 +0200
    62.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java	Fri Sep 02 23:54:26 2016 +0200
    62.3 @@ -22,6 +22,7 @@
    62.4   * or visit www.oracle.com if you need additional information or have any
    62.5   * questions.
    62.6   */
    62.7 +
    62.8  package jdk.jshell;
    62.9  
   62.10  import java.util.Collection;
    63.1 --- a/src/jdk.jshell/share/classes/jdk/jshell/Wrap.java	Fri Jun 03 17:06:38 2016 +0200
    63.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/Wrap.java	Fri Sep 02 23:54:26 2016 +0200
    63.3 @@ -1,5 +1,5 @@
    63.4  /*
    63.5 - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    63.6 + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
    63.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    63.8   *
    63.9   * This code is free software; you can redistribute it and/or modify it
   63.10 @@ -25,9 +25,9 @@
   63.11  
   63.12  package jdk.jshell;
   63.13  
   63.14 -import java.util.ArrayList;
   63.15 -import java.util.List;
   63.16 -import static jdk.internal.jshell.remote.RemoteCodes.DOIT_METHOD_NAME;
   63.17 +import java.util.Arrays;
   63.18 +import static java.util.stream.Collectors.joining;
   63.19 +import static jdk.jshell.Util.DOIT_METHOD_NAME;
   63.20  
   63.21  /**
   63.22   * Wrapping of source into Java methods, fields, etc.  All but outer layer
   63.23 @@ -58,30 +58,12 @@
   63.24          return methodWrap("", source, "");
   63.25      }
   63.26  
   63.27 -    public static Wrap corralledMethod(String source, Range modRange, Range tpRange, Range typeRange, String name, Range paramRange, Range throwsRange, int id) {
   63.28 -        List<Object> l = new ArrayList<>();
   63.29 -        l.add("    public static\n    ");
   63.30 -        if (!modRange.isEmpty()) {
   63.31 -            l.add(new RangeWrap(source, modRange));
   63.32 -            l.add(" ");
   63.33 -        }
   63.34 -        if (tpRange != null) {
   63.35 -            l.add("<");
   63.36 -            l.add(new RangeWrap(source, tpRange));
   63.37 -            l.add("> ");
   63.38 -        }
   63.39 -        l.add(new RangeWrap(source, typeRange));
   63.40 -        l.add(" " + name + "(\n        ");
   63.41 -        if (paramRange != null) {
   63.42 -            l.add(new RangeWrap(source, paramRange));
   63.43 -        }
   63.44 -        l.add(") ");
   63.45 -        if (throwsRange != null) {
   63.46 -            l.add("throws ");
   63.47 -            l.add(new RangeWrap(source, throwsRange));
   63.48 -        }
   63.49 -        l.add(" {\n        throw new jdk.internal.jshell.remote.RemoteResolutionException(" + id + ");\n}\n");
   63.50 -        return new CompoundWrap(l.toArray());
   63.51 +    private static String indent(int n) {
   63.52 +        return "                              ".substring(0, n * 4);
   63.53 +    }
   63.54 +
   63.55 +    private static String nlindent(int n) {
   63.56 +        return "\n" + indent(n);
   63.57      }
   63.58  
   63.59      /**
   63.60 @@ -126,7 +108,7 @@
   63.61          return new CompoundWrap(varDecl, wInitMeth);
   63.62      }
   63.63  
   63.64 -    public static Wrap importWrap(String source) {
   63.65 +    public static Wrap simpleWrap(String source) {
   63.66          return new NoWrap(source);
   63.67      }
   63.68  
   63.69 @@ -177,7 +159,7 @@
   63.70          }
   63.71  
   63.72          void verify(String s) {
   63.73 -            if (begin < 0 || end < begin || end > s.length()) {
   63.74 +            if (begin < 0 || end <= begin || end > s.length()) {
   63.75                  throw new InternalError("Bad Range: " + s + "[" + begin + "," + end + "]");
   63.76              }
   63.77          }
   63.78 @@ -257,6 +239,27 @@
   63.79              return 0;
   63.80          }
   63.81  
   63.82 +        Wrap wrapIndexToWrap(long wi) {
   63.83 +            int before = 0;
   63.84 +            Wrap w = null;
   63.85 +            for (Object o : os) {
   63.86 +                if (o instanceof String) {
   63.87 +                    String s = (String) o;
   63.88 +                    before += s.length();
   63.89 +                } else if (o instanceof Wrap) {
   63.90 +                    w = (Wrap) o;
   63.91 +                    int len = w.wrapped().length();
   63.92 +                    if ((wi - before) <= len) {
   63.93 +                        //System.err.printf("Defer to wrap %s - wi: %d. before; %d   -- %s  >>> %s\n",
   63.94 +                        //        w, wi, before, w.debugPos(wi - before), w.wrapped());
   63.95 +                        return w;
   63.96 +                    }
   63.97 +                    before += len;
   63.98 +                }
   63.99 +            }
  63.100 +            return w;
  63.101 +        }
  63.102 +
  63.103          @Override
  63.104          public int wrapIndexToSnippetIndex(int wi) {
  63.105              int before = 0;
  63.106 @@ -306,6 +309,25 @@
  63.107              return 0;
  63.108          }
  63.109  
  63.110 +        Wrap wrapLineToWrap(int wline) {
  63.111 +            int before = 0;
  63.112 +            Wrap w = null;
  63.113 +            for (Object o : os) {
  63.114 +                if (o instanceof String) {
  63.115 +                    String s = (String) o;
  63.116 +                    before += countLines(s);
  63.117 +                } else if (o instanceof Wrap) {
  63.118 +                    w = (Wrap) o;
  63.119 +                    int lns = countLines(w.wrapped());
  63.120 +                    if ((wline - before) < lns) {
  63.121 +                        return w;
  63.122 +                    }
  63.123 +                    before += lns;
  63.124 +                }
  63.125 +            }
  63.126 +            return w;
  63.127 +        }
  63.128 +
  63.129          @Override
  63.130          public int wrapLineToSnippetLine(int wline) {
  63.131              int before = 0;
  63.132 @@ -335,7 +357,10 @@
  63.133              return snlineLast;
  63.134          }
  63.135  
  63.136 -
  63.137 +        @Override
  63.138 +        public String toString() {
  63.139 +            return "CompoundWrap(" + Arrays.stream(os).map(u -> u.toString()).collect(joining(",")) + ")";
  63.140 +        }
  63.141      }
  63.142  
  63.143      private static class RangeWrap extends Wrap {
  63.144 @@ -424,6 +449,10 @@
  63.145              return lastSnline;
  63.146          }
  63.147  
  63.148 +        @Override
  63.149 +        public String toString() {
  63.150 +            return "RangeWrap(" + range + ")";
  63.151 +        }
  63.152      }
  63.153  
  63.154      private static class NoWrap extends RangeWrap {
    64.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    64.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/resources/l10n.properties	Fri Sep 02 23:54:26 2016 +0200
    64.3 @@ -0,0 +1,34 @@
    64.4 +#
    64.5 +# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    64.6 +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    64.7 +#
    64.8 +# This code is free software; you can redistribute it and/or modify it
    64.9 +# under the terms of the GNU General Public License version 2 only, as
   64.10 +# published by the Free Software Foundation.  Oracle designates this
   64.11 +# particular file as subject to the "Classpath" exception as provided
   64.12 +# by Oracle in the LICENSE file that accompanied this code.
   64.13 +#
   64.14 +# This code is distributed in the hope that it will be useful, but WITHOUT
   64.15 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   64.16 +# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   64.17 +# version 2 for more details (a copy is included in the LICENSE file that
   64.18 +# accompanied this code).
   64.19 +#
   64.20 +# You should have received a copy of the GNU General Public License version
   64.21 +# 2 along with this work; if not, write to the Free Software Foundation,
   64.22 +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   64.23 +#
   64.24 +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   64.25 +# or visit www.oracle.com if you need additional information or have any
   64.26 +# questions.
   64.27 +#
   64.28 +
   64.29 +jshell.diag.modifier.plural.fatal = Modifiers {0} not permitted in top-level declarations
   64.30 +jshell.diag.modifier.plural.ignore = Modifiers {0} not permitted in top-level declarations, ignored
   64.31 +jshell.diag.modifier.single.fatal = Modifier {0} not permitted in top-level declarations
   64.32 +jshell.diag.modifier.single.ignore = Modifier {0} not permitted in top-level declarations, ignored
   64.33 +
   64.34 +jshell.exc.null = Snippet must not be null
   64.35 +jshell.exc.alien = Snippet not from this JShell
   64.36 +jshell.exc.closed = JShell ({0}) has been closed.
   64.37 +jshell.exc.var.not.valid = Snippet parameter of varValue() {0} must be VALID, it is: {1}
    65.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    65.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java	Fri Sep 02 23:54:26 2016 +0200
    65.3 @@ -0,0 +1,154 @@
    65.4 +/*
    65.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    65.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    65.7 + *
    65.8 + * This code is free software; you can redistribute it and/or modify it
    65.9 + * under the terms of the GNU General Public License version 2 only, as
   65.10 + * published by the Free Software Foundation.  Oracle designates this
   65.11 + * particular file as subject to the "Classpath" exception as provided
   65.12 + * by Oracle in the LICENSE file that accompanied this code.
   65.13 + *
   65.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   65.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   65.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   65.17 + * version 2 for more details (a copy is included in the LICENSE file that
   65.18 + * accompanied this code).
   65.19 + *
   65.20 + * You should have received a copy of the GNU General Public License version
   65.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   65.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   65.23 + *
   65.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   65.25 + * or visit www.oracle.com if you need additional information or have any
   65.26 + * questions.
   65.27 + */
   65.28 +
   65.29 +package jdk.jshell.spi;
   65.30 +
   65.31 +import java.util.Collection;
   65.32 +import jdk.jshell.JShellException;
   65.33 +
   65.34 +/**
   65.35 + * This interface specifies the functionality that must provided to implement
   65.36 + * a pluggable JShell execution engine.
   65.37 + * <p>
   65.38 + * The audience for this Service Provider Interface is engineers
   65.39 + * wishing to implement their own version of the execution engine in support
   65.40 + * of the JShell API.  This is NOT a part of the JShell API.
   65.41 + * <p>
   65.42 + * A Snippet is compiled into code wrapped in a 'wrapper class'.  The execution
   65.43 + * engine is used by the core JShell implementation to load and, for
   65.44 + * executable Snippets, execute the Snippet.
   65.45 + * <p>
   65.46 + * Methods defined in this interface should only be called by the core JShell
   65.47 + * implementation.
   65.48 + * <p>
   65.49 + * To install an instance of ExecutionControl, it is passed to
   65.50 + * {@link jdk.jshell.JShell.Builder#executionEngine(jdk.jshell.spi.ExecutionControl) }.
   65.51 + */
   65.52 +public interface ExecutionControl {
   65.53 +
   65.54 +    /**
   65.55 +     * Represents the current status of a class in the execution engine.
   65.56 +     */
   65.57 +    public enum ClassStatus {
   65.58 +        /**
   65.59 +         * Class is not known to the execution engine (not loaded).
   65.60 +         */
   65.61 +        UNKNOWN,
   65.62 +
   65.63 +        /**
   65.64 +         * Class is loaded, but the loaded/redefined bytes do not match those
   65.65 +         * returned by {@link ExecutionEnv#getClassBytes(java.lang.String) }.
   65.66 +         */
   65.67 +        NOT_CURRENT,
   65.68 +
   65.69 +        /**
   65.70 +         * Class is loaded and loaded/redefined bytes match those
   65.71 +         * returned by {@link ExecutionEnv#getClassBytes(java.lang.String) }.
   65.72 +         */
   65.73 +        CURRENT
   65.74 +    };
   65.75 +
   65.76 +    /**
   65.77 +     * Initializes the instance. No methods in this interface can be called
   65.78 +     * before this.
   65.79 +     *
   65.80 +     * @param env the execution environment information provided by JShell
   65.81 +     * @throws Exception if the instance is unable to initialize
   65.82 +     */
   65.83 +    void start(ExecutionEnv env) throws Exception;
   65.84 +
   65.85 +    /**
   65.86 +     * Shuts down this execution engine. Implementation should free all
   65.87 +     * resources held by this execution engine.
   65.88 +     * <p>
   65.89 +     * No calls to methods on this interface should be made after close.
   65.90 +     */
   65.91 +    void close();
   65.92 +
   65.93 +    /**
   65.94 +     * Adds the path to the execution class path.
   65.95 +     *
   65.96 +     * @param path the path to add
   65.97 +     * @return true if successful
   65.98 +     */
   65.99 +    boolean addToClasspath(String path);
  65.100 +
  65.101 +    /**
  65.102 +     * Invokes an executable Snippet by calling a method on the specified
  65.103 +     * wrapper class. The method must have no arguments and return String.
  65.104 +     *
  65.105 +     * @param classname the class whose method should be invoked
  65.106 +     * @param methodname the name of method to invoke
  65.107 +     * @return the result of the execution or null if no result
  65.108 +     * @throws JShellException if a user exception if thrown,
  65.109 +     * {@link jdk.jshell.EvalException EvalException} will be thrown; if an
  65.110 +     * unresolved reference is encountered,
  65.111 +     * {@link jdk.jshell.UnresolvedReferenceException UnresolvedReferenceException}
  65.112 +     * will be thrown
  65.113 +     */
  65.114 +    String invoke(String classname, String methodname) throws JShellException;
  65.115 +
  65.116 +    /**
  65.117 +     * Attempts to load new classes. Class bytes are retrieved from
  65.118 +     * {@link ExecutionEnv#getClassBytes(java.lang.String) }
  65.119 +     *
  65.120 +     * @param classes list of class names to load
  65.121 +     * @return true if load succeeded
  65.122 +     */
  65.123 +    boolean load(Collection<String> classes);
  65.124 +
  65.125 +    /**
  65.126 +     * Attempts to redefine previously loaded classes. Class bytes are retrieved
  65.127 +     * from {@link ExecutionEnv#getClassBytes(java.lang.String) }
  65.128 +     *
  65.129 +     * @param classes list of class names to redefine
  65.130 +     * @return true if redefine succeeded
  65.131 +     */
  65.132 +    boolean redefine(Collection<String> classes);
  65.133 +
  65.134 +    /**
  65.135 +     * Queries if the class is loaded and the class bytes are current.
  65.136 +     *
  65.137 +     * @param classname name of the wrapper class to query
  65.138 +     * @return {@code UNKNOWN} if the class is not loaded; {@code CURRENT} if
  65.139 +     * the loaded/redefined bytes are equal to the most recent bytes for this
  65.140 +     * wrapper class; otherwise {@code NOT_CURRENT}
  65.141 +     */
  65.142 +    ClassStatus getClassStatus(String classname);
  65.143 +
  65.144 +    /**
  65.145 +     * Interrupt a running invoke.
  65.146 +     */
  65.147 +    void stop();
  65.148 +
  65.149 +    /**
  65.150 +     * Returns the value of a variable.
  65.151 +     *
  65.152 +     * @param classname the name of the wrapper class of the variable
  65.153 +     * @param varname the name of the variable
  65.154 +     * @return the value of the variable
  65.155 +     */
  65.156 +    String varValue(String classname, String varname);
  65.157 +}
    66.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    66.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionEnv.java	Fri Sep 02 23:54:26 2016 +0200
    66.3 @@ -0,0 +1,127 @@
    66.4 +/*
    66.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    66.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    66.7 + *
    66.8 + * This code is free software; you can redistribute it and/or modify it
    66.9 + * under the terms of the GNU General Public License version 2 only, as
   66.10 + * published by the Free Software Foundation.  Oracle designates this
   66.11 + * particular file as subject to the "Classpath" exception as provided
   66.12 + * by Oracle in the LICENSE file that accompanied this code.
   66.13 + *
   66.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   66.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   66.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   66.17 + * version 2 for more details (a copy is included in the LICENSE file that
   66.18 + * accompanied this code).
   66.19 + *
   66.20 + * You should have received a copy of the GNU General Public License version
   66.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   66.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   66.23 + *
   66.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   66.25 + * or visit www.oracle.com if you need additional information or have any
   66.26 + * questions.
   66.27 + */
   66.28 +
   66.29 +package jdk.jshell.spi;
   66.30 +
   66.31 +import java.io.InputStream;
   66.32 +import java.io.PrintStream;
   66.33 +import java.util.List;
   66.34 +import jdk.jshell.EvalException;
   66.35 +import jdk.jshell.JShell;
   66.36 +import jdk.jshell.UnresolvedReferenceException;
   66.37 +
   66.38 +/**
   66.39 + * Functionality made available to a pluggable JShell execution engine.  It is
   66.40 + * provided to the execution engine by the core JShell implementation calling
   66.41 + * {@link ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }.
   66.42 + * <p>
   66.43 + * This interface is designed to provide the access to core JShell functionality
   66.44 + * needed to implement ExecutionControl.
   66.45 + *
   66.46 + * @see ExecutionControl
   66.47 + */
   66.48 +public interface ExecutionEnv {
   66.49 +
   66.50 +    /**
   66.51 +     * Returns the user's input stream.
   66.52 +     *
   66.53 +     * @return the user's input stream
   66.54 +     */
   66.55 +    InputStream userIn();
   66.56 +
   66.57 +    /**
   66.58 +     * Returns the user's output stream.
   66.59 +     *
   66.60 +     * @return the user's output stream
   66.61 +     */
   66.62 +    PrintStream userOut();
   66.63 +
   66.64 +    /**
   66.65 +     * Returns the user's error stream.
   66.66 +     *
   66.67 +     * @return the user's error stream
   66.68 +     */
   66.69 +    PrintStream userErr();
   66.70 +
   66.71 +    /**
   66.72 +     * @return the JShell instance
   66.73 +     */
   66.74 +    JShell state();
   66.75 +
   66.76 +    /**
   66.77 +     * Returns the additional VM options to be used when launching the remote
   66.78 +     * JVM. This is advice to the execution engine.
   66.79 +     * <p>
   66.80 +     * Note: an execution engine need not launch a remote JVM.
   66.81 +     *
   66.82 +     * @return the additional options with which to launch the remote JVM
   66.83 +     */
   66.84 +    List<String> extraRemoteVMOptions();
   66.85 +
   66.86 +    /**
   66.87 +     * Retrieves the class file bytes for the specified wrapper class.
   66.88 +     *
   66.89 +     * @param className the name of the wrapper class
   66.90 +     * @return the current class file bytes as a byte array
   66.91 +     */
   66.92 +    byte[] getClassBytes(String className);
   66.93 +
   66.94 +    /**
   66.95 +     * Creates an {@code EvalException} corresponding to a user exception. An
   66.96 +     * user exception thrown during
   66.97 +     * {@link ExecutionControl#invoke(java.lang.String, java.lang.String) }
   66.98 +     * should be converted to an {@code EvalException} using this method.
   66.99 +     *
  66.100 +     * @param message the exception message to use (from the user exception)
  66.101 +     * @param exceptionClass the class name of the user exception
  66.102 +     * @param stackElements the stack trace elements to install
  66.103 +     * @return a user API EvalException for the user exception
  66.104 +     */
  66.105 +    EvalException createEvalException(String message, String exceptionClass,
  66.106 +            StackTraceElement[] stackElements);
  66.107 +
  66.108 +    /**
  66.109 +     * Creates an {@code UnresolvedReferenceException} for the Snippet identifed
  66.110 +     * by the specified identifier. An {@link SPIResolutionException} thrown
  66.111 +     * during {@link ExecutionControl#invoke(java.lang.String, java.lang.String) }
  66.112 +     * should be converted to an {@code UnresolvedReferenceException} using
  66.113 +     * this method.
  66.114 +     * <p>
  66.115 +     * The identifier is an internal id, different from the id in the API. This
  66.116 +     * internal id is returned by {@link SPIResolutionException#id()}.
  66.117 +     *
  66.118 +     * @param id the internal integer identifier
  66.119 +     * @param stackElements the stack trace elements to install
  66.120 +     * @return an {@code UnresolvedReferenceException} for the unresolved
  66.121 +     * reference
  66.122 +     */
  66.123 +    UnresolvedReferenceException createUnresolvedReferenceException(int id,
  66.124 +            StackTraceElement[] stackElements);
  66.125 +
  66.126 +    /**
  66.127 +     * Reports that the execution engine has shutdown.
  66.128 +     */
  66.129 +    void closeDown();
  66.130 +}
    67.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    67.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/spi/SPIResolutionException.java	Fri Sep 02 23:54:26 2016 +0200
    67.3 @@ -0,0 +1,69 @@
    67.4 +/*
    67.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    67.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    67.7 + *
    67.8 + * This code is free software; you can redistribute it and/or modify it
    67.9 + * under the terms of the GNU General Public License version 2 only, as
   67.10 + * published by the Free Software Foundation.  Oracle designates this
   67.11 + * particular file as subject to the "Classpath" exception as provided
   67.12 + * by Oracle in the LICENSE file that accompanied this code.
   67.13 + *
   67.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   67.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   67.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   67.17 + * version 2 for more details (a copy is included in the LICENSE file that
   67.18 + * accompanied this code).
   67.19 + *
   67.20 + * You should have received a copy of the GNU General Public License version
   67.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   67.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   67.23 + *
   67.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   67.25 + * or visit www.oracle.com if you need additional information or have any
   67.26 + * questions.
   67.27 + */
   67.28 +
   67.29 +package jdk.jshell.spi;
   67.30 +
   67.31 +/**
   67.32 + * The construction and throw of this exception is embedded in code generated by
   67.33 + * the JShell core implementation in such a way that, upon executing a
   67.34 + * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
   67.35 + * user method, this exception is thrown.
   67.36 + * <p>
   67.37 + * This exception is seen by the execution engine, but not seen by
   67.38 + * the end user nor through the JShell API.
   67.39 + *
   67.40 + * @see ExecutionEnv#createUnresolvedReferenceException(int,
   67.41 + * java.lang.StackTraceElement[])
   67.42 + */
   67.43 +@SuppressWarnings("serial")             // serialVersionUID intentionally omitted
   67.44 +public class SPIResolutionException extends RuntimeException {
   67.45 +
   67.46 +    private final int id;
   67.47 +
   67.48 +    /**
   67.49 +     * Constructs an SPI layer exception indicating that a
   67.50 +     * {@code DeclarationSnippet} with unresolved references has been
   67.51 +     * encountered. The throw of this exception is generated into the body of a
   67.52 +     * {@link jdk.jshell.Snippet.Status#RECOVERABLE_DEFINED RECOVERABLE_DEFINED}
   67.53 +     * method.
   67.54 +     *
   67.55 +     * @param id An internal identifier of the specific method
   67.56 +     */
   67.57 +    public SPIResolutionException(int id) {
   67.58 +        super("resolution exception");
   67.59 +        this.id = id;
   67.60 +    }
   67.61 +
   67.62 +    /**
   67.63 +     * Retrieves the internal identifer of the unresolved identifer.
   67.64 +     *
   67.65 +     * @return the internal identifer
   67.66 +     * @see ExecutionEnv#createUnresolvedReferenceException(int,
   67.67 +     * java.lang.StackTraceElement[])
   67.68 +     */
   67.69 +    public int id() {
   67.70 +        return id;
   67.71 +    }
   67.72 +}
    68.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    68.2 +++ b/src/jdk.jshell/share/classes/jdk/jshell/spi/package-info.java	Fri Sep 02 23:54:26 2016 +0200
    68.3 @@ -0,0 +1,39 @@
    68.4 +/*
    68.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    68.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    68.7 + *
    68.8 + * This code is free software; you can redistribute it and/or modify it
    68.9 + * under the terms of the GNU General Public License version 2 only, as
   68.10 + * published by the Free Software Foundation.  Oracle designates this
   68.11 + * particular file as subject to the "Classpath" exception as provided
   68.12 + * by Oracle in the LICENSE file that accompanied this code.
   68.13 + *
   68.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   68.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   68.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   68.17 + * version 2 for more details (a copy is included in the LICENSE file that
   68.18 + * accompanied this code).
   68.19 + *
   68.20 + * You should have received a copy of the GNU General Public License version
   68.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   68.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   68.23 + *
   68.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   68.25 + * or visit www.oracle.com if you need additional information or have any
   68.26 + * questions.
   68.27 + */
   68.28 +
   68.29 +/**
   68.30 + * Provides support for alternate implementations of the JShell execution
   68.31 + * engine.  The JShell core tracks and compiles Snippets then sends them
   68.32 + * (represented in a wrapper class) to the execution engine for loading,
   68.33 + * and in the case of executable Snippets, execution.  The JShell
   68.34 + * implementation includes a default execution engine (currently a remote
   68.35 + * process which is JDI controlled).  By implementing the
   68.36 + * {@link ExecutionControl} interface and installing it with
   68.37 + * {@link jdk.jshell.JShell.Builder#executionEngine(jdk.jshell.spi.ExecutionControl) }
   68.38 + * other execution engines can be used.
   68.39 + * <p>
   68.40 + * This is not a part of the JShell API.
   68.41 + */
   68.42 +package jdk.jshell.spi;
    69.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    69.2 +++ b/src/jdk.jshell/share/classes/module-info.java	Fri Sep 02 23:54:26 2016 +0200
    69.3 @@ -0,0 +1,36 @@
    69.4 +/*
    69.5 + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
    69.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    69.7 + *
    69.8 + * This code is free software; you can redistribute it and/or modify it
    69.9 + * under the terms of the GNU General Public License version 2 only, as
   69.10 + * published by the Free Software Foundation.  Oracle designates this
   69.11 + * particular file as subject to the "Classpath" exception as provided
   69.12 + * by Oracle in the LICENSE file that accompanied this code.
   69.13 + *
   69.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   69.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   69.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   69.17 + * version 2 for more details (a copy is included in the LICENSE file that
   69.18 + * accompanied this code).
   69.19 + *
   69.20 + * You should have received a copy of the GNU General Public License version
   69.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   69.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   69.23 + *
   69.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   69.25 + * or visit www.oracle.com if you need additional information or have any
   69.26 + * questions.
   69.27 + */
   69.28 +
   69.29 +module jdk.jshell {
   69.30 +    requires public java.compiler;
   69.31 +    requires java.desktop;
   69.32 +    requires java.prefs;
   69.33 +    requires jdk.compiler;
   69.34 +    requires jdk.internal.le;
   69.35 +    requires jdk.jdi;
   69.36 +
   69.37 +    exports jdk.jshell;
   69.38 +    exports jdk.jshell.spi;
   69.39 +}