8341631: JShell should auto-import java.io.IO.*

Reviewed-by: asotona, cstein
This commit is contained in:
Jan Lahoda 2024-11-20 09:24:05 +00:00
parent 5b12a87dcb
commit cf158bc6cd
7 changed files with 141 additions and 17 deletions

View File

@ -1001,6 +1001,11 @@ class ConsoleIOContext extends IOContext {
return doReadUserLine(prompt, null); return doReadUserLine(prompt, null);
} }
@Override
public String readUserLine() throws IOException {
return readUserLine("");
}
private synchronized String doReadUserLine(String prompt, Character mask) throws IOException { private synchronized String doReadUserLine(String prompt, Character mask) throws IOException {
History prevHistory = in.getHistory(); History prevHistory = in.getHistory();
boolean prevDisableCr = Display.DISABLE_CR; boolean prevDisableCr = Display.DISABLE_CR;

View File

@ -64,6 +64,8 @@ abstract class IOContext implements AutoCloseable {
} }
public String readUserLine(String prompt) throws IOException { public String readUserLine(String prompt) throws IOException {
userOutput().write(prompt);
userOutput().flush();
throw new UserInterruptException(""); throw new UserInterruptException("");
} }
@ -76,6 +78,8 @@ abstract class IOContext implements AutoCloseable {
} }
public char[] readPassword(String prompt) throws IOException { public char[] readPassword(String prompt) throws IOException {
userOutput().write(prompt);
userOutput().flush();
throw new UserInterruptException(""); throw new UserInterruptException("");
} }

View File

@ -35,6 +35,7 @@ import java.io.IOError;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream; import java.io.PrintStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Reader; import java.io.Reader;
@ -1192,7 +1193,7 @@ public class JShellTool implements MessageHandler {
//where //where
private void startUpRun(String start) { private void startUpRun(String start) {
try (IOContext suin = new ScannerIOContext(new StringReader(start))) { try (IOContext suin = new ScannerIOContext(new StringReader(start), userout)) {
while (run(suin)) { while (run(suin)) {
if (!live) { if (!live) {
resetState(); resetState();
@ -3125,7 +3126,7 @@ public class JShellTool implements MessageHandler {
throw new FileNotFoundException(filename); throw new FileNotFoundException(filename);
} }
} }
try (var scannerIOContext = new ScannerIOContext(scanner)) { try (var scannerIOContext = new ScannerIOContext(scanner, userout)) {
run(scannerIOContext); run(scannerIOContext);
} }
return true; return true;
@ -3288,8 +3289,10 @@ public class JShellTool implements MessageHandler {
resetState(); resetState();
} }
if (history != null) { if (history != null) {
run(new ReloadIOContext(history.iterable(), try (ReloadIOContext ctx = new ReloadIOContext(history.iterable(),
echo ? cmdout : null)); echo ? cmdout : null, userout)) {
run(ctx);
}
} }
return true; return true;
} }
@ -4107,6 +4110,8 @@ public class JShellTool implements MessageHandler {
public String readLine(String prompt) { public String readLine(String prompt) {
try { try {
return input.readUserLine(prompt); return input.readUserLine(prompt);
} catch (UserInterruptException ex) {
return null;
} catch (IOException ex) { } catch (IOException ex) {
throw new IOError(ex); throw new IOError(ex);
} }
@ -4125,6 +4130,8 @@ public class JShellTool implements MessageHandler {
public char[] readPassword(String prompt) { public char[] readPassword(String prompt) {
try { try {
return input.readPassword(prompt); return input.readPassword(prompt);
} catch (UserInterruptException ex) {
return null;
} catch (IOException ex) { } catch (IOException ex) {
throw new IOError(ex); throw new IOError(ex);
} }
@ -4144,6 +4151,12 @@ public class JShellTool implements MessageHandler {
abstract class NonInteractiveIOContext extends IOContext { abstract class NonInteractiveIOContext extends IOContext {
private final Writer userOutput;
public NonInteractiveIOContext(PrintStream userOutput) {
this.userOutput = new OutputStreamWriter(userOutput);
}
@Override @Override
public boolean interactiveOutput() { public boolean interactiveOutput() {
return false; return false;
@ -4178,17 +4191,33 @@ abstract class NonInteractiveIOContext extends IOContext {
@Override @Override
public void replaceLastHistoryEntry(String source) { public void replaceLastHistoryEntry(String source) {
} }
@Override
public Writer userOutput() {
return userOutput;
}
@Override
public void close() {
try {
userOutput.flush();
} catch (IOException _) {
//ignore
}
}
} }
class ScannerIOContext extends NonInteractiveIOContext { class ScannerIOContext extends NonInteractiveIOContext {
private final Scanner scannerIn; private final Scanner scannerIn;
ScannerIOContext(Scanner scannerIn) { ScannerIOContext(Scanner scannerIn, PrintStream userOutput) {
super(userOutput);
this.scannerIn = scannerIn; this.scannerIn = scannerIn;
} }
ScannerIOContext(Reader rdr) throws FileNotFoundException { ScannerIOContext(Reader rdr, PrintStream userOutput) throws FileNotFoundException {
this(new Scanner(rdr)); this(new Scanner(rdr), userOutput);
} }
@Override @Override
@ -4202,6 +4231,7 @@ class ScannerIOContext extends NonInteractiveIOContext {
@Override @Override
public void close() { public void close() {
super.close();
scannerIn.close(); scannerIn.close();
} }
@ -4215,7 +4245,8 @@ class ReloadIOContext extends NonInteractiveIOContext {
private final Iterator<String> it; private final Iterator<String> it;
private final PrintStream echoStream; private final PrintStream echoStream;
ReloadIOContext(Iterable<String> history, PrintStream echoStream) { ReloadIOContext(Iterable<String> history, PrintStream echoStream, PrintStream userOutput) {
super(userOutput);
this.it = history.iterator(); this.it = history.iterator();
this.echoStream = echoStream; this.echoStream = echoStream;
} }

View File

@ -117,6 +117,9 @@ public class ConsoleImpl {
private char[] readChars() throws IOException { private char[] readChars() throws IOException {
int actualLen = readInt(); int actualLen = readInt();
if (actualLen == (-1)) {
return null;
}
char[] result = new char[actualLen]; char[] result = new char[actualLen];
for (int i = 0; i < actualLen; i++) { for (int i = 0; i < actualLen; i++) {
result[i] = (char) ((remoteOutput.read() << 8) | result[i] = (char) ((remoteOutput.read() << 8) |
@ -267,6 +270,9 @@ public class ConsoleImpl {
remoteInput.write(Task.READ_LINE.ordinal()); remoteInput.write(Task.READ_LINE.ordinal());
sendChars(chars, 0, chars.length); sendChars(chars, 0, chars.length);
char[] line = readChars(); char[] line = readChars();
if (line == null) {
return null;
}
return new String(line); return new String(line);
}); });
} catch (IOException ex) { } catch (IOException ex) {
@ -417,8 +423,12 @@ public class ConsoleImpl {
char[] data = readCharsOrNull(1); char[] data = readCharsOrNull(1);
if (data != null) { if (data != null) {
String line = console.readLine(new String(data)); String line = console.readLine(new String(data));
char[] chars = line.toCharArray(); if (line == null) {
sendChars(sinkOutput, chars, 0, chars.length); sendInt(sinkOutput, -1);
} else {
char[] chars = line.toCharArray();
sendChars(sinkOutput, chars, 0, chars.length);
}
bp = 0; bp = 0;
} }
} }
@ -432,7 +442,11 @@ public class ConsoleImpl {
char[] data = readCharsOrNull(1); char[] data = readCharsOrNull(1);
if (data != null) { if (data != null) {
char[] chars = console.readPassword(new String(data)); char[] chars = console.readPassword(new String(data));
sendChars(sinkOutput, chars, 0, chars.length); if (chars == null) {
sendInt(sinkOutput, -1);
} else {
sendChars(sinkOutput, chars, 0, chars.length);
}
bp = 0; bp = 0;
} }
} }

View File

@ -1 +1,2 @@
import module java.base; import module java.base;
import static java.io.IO.*;

View File

@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8331535 * @bug 8331535 8341631
* @summary Test the JShell tool Console handling * @summary Test the JShell tool Console handling
* @modules jdk.internal.le/jdk.internal.org.jline.reader * @modules jdk.internal.le/jdk.internal.org.jline.reader
* jdk.jshell/jdk.internal.jshell.tool:+open * jdk.jshell/jdk.internal.jshell.tool:+open
@ -56,6 +56,40 @@ public class ConsoleToolTest extends ReplToolTesting {
); );
} }
@Test //JDK-8341631
public void testIO() {
test(new String[] {"--enable-preview"},
a -> {assertCommandWithOutputAndTerminal(a,
"java.io.IO.readln(\"%%s\");\ninput", //newline automatically appended
"$1 ==> \"input\"",
"""
\u0005java.io.IO.readln(\"%%s\");
%%sinput
""");},
a -> {assertCommandWithOutputAndTerminal(a,
"java.io.IO.readln();\ninput!", //newline automatically appended
"$2 ==> \"input!\"",
"""
\u0005java.io.IO.readln();
input!
""");},
a -> {assertCommandWithOutputAndTerminal(a,
"java.io.IO.println(\"Hello, World!\");",
"",
"""
\u0005java.io.IO.println(\"Hello, World!\");
Hello, World!
""");},
a -> {assertCommandWithOutputAndTerminal(a,
"java.io.IO.println();",
"",
"""
\u0005java.io.IO.println();
""");}
);
}
void assertCommandWithOutputAndTerminal(boolean a, String command, String out, String terminalOut) { void assertCommandWithOutputAndTerminal(boolean a, String command, String out, String terminalOut) {
assertCommand(a, command, out, null, null, null, null, terminalOut); assertCommand(a, command, out, null, null, null, null, terminalOut);
} }

View File

@ -22,7 +22,7 @@
*/ */
/* /*
* @test 8151754 8080883 8160089 8170162 8166581 8172102 8171343 8178023 8186708 8179856 8185840 8190383 * @test 8151754 8080883 8160089 8170162 8166581 8172102 8171343 8178023 8186708 8179856 8185840 8190383 8341631
* @summary Testing startExCe-up options. * @summary Testing startExCe-up options.
* @modules jdk.compiler/com.sun.tools.javac.api * @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main * jdk.compiler/com.sun.tools.javac.main
@ -366,14 +366,49 @@ public class StartOptionTest {
} }
public void testPreviewEnabled() { public void testPreviewEnabled() {
String fn = writeToFile("System.out.println(\"prefix\");\n" + String fn = writeToFile(
"System.out.println(MethodHandle.class.getName());\n" + """
"System.out.println(\"suffix\");\n" + System.out.println(\"prefix\");
"/exit\n"); System.out.println(MethodHandle.class.getName());
System.out.println(\"suffix\");
/exit
""");
startCheckUserOutput(s -> assertEquals(s, "prefix\nsuffix\n"), startCheckUserOutput(s -> assertEquals(s, "prefix\nsuffix\n"),
fn); fn);
startCheckUserOutput(s -> assertEquals(s, "prefix\njava.lang.invoke.MethodHandle\nsuffix\n"), startCheckUserOutput(s -> assertEquals(s, "prefix\njava.lang.invoke.MethodHandle\nsuffix\n"),
"--enable-preview", fn); "--enable-preview", fn);
//JDK-8341631:
String fn2 = writeToFile(
"""
System.out.println(\"prefix\");
IO.println(\"test\");
System.out.println(\"suffix\");
/exit
""");
startCheckUserOutput(s -> assertEquals(s, "prefix\nsuffix\n"),
fn2);
startCheckUserOutput(s -> assertEquals(s, "prefix\ntest\nsuffix\n"),
"--enable-preview", fn2);
}
public void testInput() {
//readLine(String):
String readLinePrompt = writeToFile(
"""
var v = System.console().readLine("prompt: ");
System.out.println(v);
/exit
""");
startCheckUserOutput(s -> assertEquals(s, "prompt: null\n"),
readLinePrompt);
//readPassword(String):
String readPasswordPrompt = writeToFile(
"""
var v = System.console().readPassword("prompt: ");
System.out.println(java.util.Arrays.toString(v));
/exit
""");
startCheckUserOutput(s -> assertEquals(s, "prompt: null\n"),
readPasswordPrompt);
} }
@AfterMethod @AfterMethod