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 <= <i>c</i> <= 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 +}