changeset 59213:686003f195ca

8242919: Paste locks up jshell Summary: Not waiting until the full block is available while reading from input. Reviewed-by: rfield
author jlahoda
date Fri, 08 May 2020 09:16:12 +0200
parents f170a0024eb5
children 056ed9b991aa
files src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java src/jdk.jshell/share/classes/jdk/internal/jshell/tool/StopDetectingInputStream.java test/langtools/jdk/jshell/PasteAndMeasurementsUITest.java
diffstat 3 files changed, 56 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java	Fri May 08 09:23:50 2020 +0800
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java	Fri May 08 09:16:12 2020 +0200
@@ -76,6 +76,8 @@
 import jdk.internal.org.jline.terminal.Terminal;
 import jdk.internal.org.jline.terminal.TerminalBuilder;
 import jdk.internal.org.jline.utils.Display;
+import jdk.internal.org.jline.utils.NonBlocking;
+import jdk.internal.org.jline.utils.NonBlockingInputStreamImpl;
 import jdk.internal.org.jline.utils.NonBlockingReader;
 import jdk.jshell.ExpressionSnippet;
 import jdk.jshell.Snippet;
@@ -103,14 +105,20 @@
         Map<String, Object> variables = new HashMap<>();
         this.input = new StopDetectingInputStream(() -> repl.stop(),
                                                   ex -> repl.hard("Error on input: %s", ex));
+        InputStream nonBlockingInput = new NonBlockingInputStreamImpl(null, input) {
+            @Override
+            public int readBuffered(byte[] b) throws IOException {
+                return input.read(b);
+            }
+        };
         Terminal terminal;
         if (System.getProperty("test.jdk") != null) {
-            terminal = new TestTerminal(input, cmdout);
+            terminal = new TestTerminal(nonBlockingInput, cmdout);
             input.setInputStream(cmdin);
         } else {
             terminal = TerminalBuilder.builder().inputStreamWrapper(in -> {
                 input.setInputStream(in);
-                return input;
+                return nonBlockingInput;
             }).build();
         }
         originalAttributes = terminal.getAttributes();
@@ -827,7 +835,7 @@
 
     private boolean fixes() {
         try {
-            int c = in.getTerminal().input().read();
+            int c = in.getTerminal().reader().read();
 
             if (c == (-1)) {
                 return true; //TODO: true or false???
@@ -1234,11 +1242,11 @@
 
         private static final int DEFAULT_HEIGHT = 24;
 
-        public TestTerminal(StopDetectingInputStream input, OutputStream output) throws Exception {
+        private final NonBlockingReader inputReader;
+
+        public TestTerminal(InputStream input, OutputStream output) throws Exception {
             super("test", "ansi", output, Charset.forName("UTF-8"));
-//            setAnsiSupported(true);
-//            setEchoEnabled(false);
-//            this.input = input;
+            this.inputReader = NonBlocking.nonBlocking(getName(), input, encoding());
             Attributes a = new Attributes(getAttributes());
             a.setLocalFlag(LocalFlag.ECHO, false);
             setAttributes(attributes);
@@ -1252,18 +1260,18 @@
                 // ignore
             }
             setSize(new Size(80, h));
-            new Thread(() -> {
-                int r;
+        }
 
-                try {
-                    while ((r = input.read()) != (-1)) {
-                        processInputByte(r);
-                    }
-                    slaveInput.close();
-                } catch (IOException ex) {
-                    throw new IllegalStateException(ex);
-                }
-            }).start();
+        @Override
+        public NonBlockingReader reader() {
+            return inputReader;
+        }
+
+        @Override
+        protected void doClose() throws IOException {
+            super.doClose();
+            slaveInput.close();
+            inputReader.close();
         }
 
     }
--- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/StopDetectingInputStream.java	Fri May 08 09:23:50 2020 +0800
+++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/StopDetectingInputStream.java	Fri May 08 09:16:12 2020 +0200
@@ -107,6 +107,19 @@
         }
     }
 
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (len == 0) {
+            return 0;
+        }
+        int r = read();
+        if (r != (-1)) {
+            b[off] = (byte) r;
+            return 1;
+        }
+        return 0;
+    }
+
     public synchronized void shutdown() {
         state = State.CLOSED;
         notifyAll();
--- a/test/langtools/jdk/jshell/PasteAndMeasurementsUITest.java	Fri May 08 09:23:50 2020 +0800
+++ b/test/langtools/jdk/jshell/PasteAndMeasurementsUITest.java	Fri May 08 09:16:12 2020 +0200
@@ -23,7 +23,7 @@
 
 /**
  * @test
- * @bug 8182297
+ * @bug 8182297 8242919
  * @summary Verify that pasting multi-line snippets works properly.
  * @library /tools/lib
  * @modules
@@ -31,6 +31,7 @@
  *     java.base/java.io:open
  *     jdk.compiler/com.sun.tools.javac.api
  *     jdk.compiler/com.sun.tools.javac.main
+ *     jdk.internal.le/jdk.internal.org.jline.reader.impl
  *     jdk.jshell/jdk.internal.jshell.tool.resources:open
  *     jdk.jshell/jdk.jshell:open
  * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
@@ -42,6 +43,7 @@
 import java.io.Console;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import jdk.internal.org.jline.reader.impl.LineReaderImpl;
 
 import org.testng.annotations.Test;
 
@@ -73,4 +75,18 @@
         });
     }
         private static final String LOC = "\033[12;1R";
+
+    public void testBracketedPaste() throws Exception {
+        Field cons = System.class.getDeclaredField("cons");
+        cons.setAccessible(true);
+        Constructor console = Console.class.getDeclaredConstructor();
+        console.setAccessible(true);
+        cons.set(null, console.newInstance());
+        doRunTest((inputSink, out) -> {
+            inputSink.write(LineReaderImpl.BRACKETED_PASTE_BEGIN +
+                            "int i;" +
+                            LineReaderImpl.BRACKETED_PASTE_END);
+            waitOutput(out,       "int i;");
+        });
+    }
 }