Skip to content

Commit 3345818

Browse files
committed
[GR-26176] Migrate from JLine 2 to JLine 3
PullRequest: truffleruby/1996
2 parents 28cd97a + 317c565 commit 3345818

21 files changed

+730
-287
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ Performance:
4242

4343
* Calls with a literal block are no longer always split but instead the decision is made by the Truffle splitting heuristic.
4444

45+
Changes:
46+
47+
* Migrated from JLine 2 to JLine 3 for the `readline` standard library.
48+
4549
# 20.2.0
4650

4751
New features:

common.json

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22
"README": "This file contains definitions that are useful for the hocon and jsonnet CI files of multiple repositories.",
33

44
"jdks": {
5-
"oraclejdk8": {"name": "oraclejdk", "version": "8u261+33-jvmci-20.2-b03", "platformspecific": true },
6-
"openjdk8": {"name": "openjdk", "version": "8u262+10-jvmci-20.2-b03", "platformspecific": true },
7-
"oraclejdk8Debug": {"name": "oraclejdk", "version": "8u261+33-jvmci-20.2-b03-fastdebug", "platformspecific": true },
8-
"oraclejdk11": {"name": "oraclejdk", "version": "11.0.6+8", "platformspecific": true },
9-
"oraclejdk15": {"name": "oraclejdk", "version": "15.0.1+6", "platformspecific": true },
10-
"oraclejdk15Debug":{"name": "oraclejdk", "version": "15.0.1+6-debug", "platformspecific": true },
11-
"openjdk11": {"name": "openjdk", "version": "11.0.3+7", "platformspecific": true },
12-
"labsjdk-ce-11": {"name": "labsjdk", "version": "ce-11.0.8+10-jvmci-20.2-b03", "platformspecific": true },
13-
"labsjdk-ee-11": {"name": "labsjdk", "version": "ee-11.0.8.0.2+1-jvmci-20.2-b03", "platformspecific": true }
5+
"oraclejdk8": {"name": "oraclejdk", "version": "8u261+33-jvmci-20.3-b01", "platformspecific": true },
6+
"openjdk8": {"name": "openjdk", "version": "8u262+10-jvmci-20.3-b01", "platformspecific": true },
7+
"oraclejdk8Debug": {"name": "oraclejdk", "version": "8u261+33-jvmci-20.3-b01-fastdebug", "platformspecific": true },
8+
"oraclejdk11": {"name": "oraclejdk", "version": "11.0.6+8", "platformspecific": true },
9+
"oraclejdk15": {"name": "oraclejdk", "version": "15.0.1+6", "platformspecific": true },
10+
"openjdk11": {"name": "openjdk", "version": "11.0.3+7", "platformspecific": true },
11+
"labsjdk-ce-11": {"name": "labsjdk", "version": "ce-11.0.8+10-jvmci-20.3-b01", "platformspecific": true },
12+
"labsjdk-ee-11": {"name": "labsjdk", "version": "ee-11.0.8.0.2+1-jvmci-20.3-b01", "platformspecific": true },
13+
"labsjdk-ce-15": {"name": "labsjdk", "version": "ce-15+36-jvmci-20.3-b01", "platformspecific": true },
14+
"labsjdk-ee-15": {"name": "labsjdk", "version": "ee-15.0.1+7-jvmci-20.3-b01", "platformspecific": true },
15+
"labsjdk-ce-15Debug": {"name": "labsjdk", "version": "ce-15.+36-jvmci-20.3-b01", "platformspecific": true },
16+
"labsjdk-ee-15Debug": {"name": "labsjdk", "version": "ee-15.0.1+7-jvmci-20.3-b01", "platformspecific": true }
1417
},
1518

1619
"sulong": {

lib/truffle/readline.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module Readline
3232
pre_input_hook
3333
pre_input_hook=
3434
redisplay
35+
refresh_line
3536
set_screen_size
3637
special_prefixes
3738
special_prefixes=

mx.truffleruby/suite.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
{
88
"name": "sulong",
99
"subdir": True,
10-
"version": "df007aafea9720ec5a05757301a1b240b0b8cb93",
10+
"version": "71072b3da8fc0930502a7fa0c915d1b1451a0838",
1111
"urls": [
1212
{"url": "https://github.com/oracle/graal.git", "kind": "git"},
1313
{"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"},
@@ -166,7 +166,7 @@
166166
"truffleruby:TRUFFLERUBY-ANNOTATIONS",
167167
"truffleruby:TRUFFLERUBY-SHARED",
168168
"truffle:TRUFFLE_API",
169-
"truffle:JLINE",
169+
"sdk:JLINE3",
170170
"sulong:SULONG_API",
171171
"JONI",
172172
"JCODINGS",

spec/truffle/irb/arrows_spec.rb

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,22 @@
1414
IO.popen([*ruby_exe, "-S", "irb", "-f", "--prompt=simple", "--readline"], "r+") do |io|
1515
io.gets.should == "Switch to inspect mode.\n"
1616

17-
io.puts "\e[A" # up arrow
18-
# JLine seems to add a bell character
19-
[">> \n", ">> \a\n"].should include(io.gets)
17+
if RUBY_ENGINE == "truffleruby"
18+
io.puts "B = 42"
19+
io.gets.should == ">> B = 42\n"
20+
io.gets.should == "=> 42\n"
21+
end
22+
23+
io.puts "\e[B" # down arrow
24+
# JLine adds a bell character (since there is no history)
25+
echo = io.gets
26+
[">> \n", ">> [B\n"].should include(echo)
27+
28+
if echo == ">> [B\n" # "[B" (echo'd by JLine) looks like a legal expression to IRB so we need to complete it
29+
io.puts "]"
30+
io.gets.should == ">> ]\n"
31+
io.gets.should == "=> [42]\n"
32+
end
2033

2134
io.puts "22 + 33"
2235
# The extra bell character causes a continuation line

spec/truffle/irb/interrupt_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
io.gets.should == ">> ^C\n"
2424

2525
io.puts "exit"
26-
io.gets.should == ">> exit\n"
26+
io.gets.should == "\n"
2727
end
2828
end
2929
end

src/main/.checkstyle_checks.xml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,6 @@
202202
<property name="format" value='(?!=)\S\s*new\s+\w+\[0\]'/>
203203
<property name="message" value='Use a EMPTY_type_ARRAY constant instead.'/>
204204
</module>
205-
<module name="RegexpSinglelineJava">
206-
<property name="format" value='\bPrintWriter\b'/>
207-
<property name="message" value='Use PrintStream instead, PrintWriter does not consistently flush, even when writing \n.'/>
208-
</module>
209205
<module name="RegexpSinglelineJava">
210206
<property name="format" value="transferToInterpreterOnException"/>
211207
<property name="message" value="Don't use transferToInterpreterOnException"/>
@@ -219,7 +215,8 @@
219215
<property name="message" value="Type the argument as Nil instead of using a positive isNil() guard."/>
220216
</module>
221217
<module name="IllegalType">
222-
<property name="illegalClassNames" value="TruffleObject,DynamicObject"/>
218+
<!-- Use PrintStream instead of PrintWriter, PrintWriter does not consistently flush, even when writing \n.-->
219+
<property name="illegalClassNames" value="TruffleObject,DynamicObject,PrintWriter"/>
223220
</module>
224221
</module>
225222
<module name="RegexpMultiline">

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.nio.ByteBuffer;
1414
import java.security.NoSuchAlgorithmException;
1515
import java.security.SecureRandom;
16+
import java.util.Objects;
1617
import java.util.concurrent.locks.ReentrantLock;
1718
import java.util.logging.Level;
1819

@@ -466,6 +467,10 @@ private void dispose() {
466467
return;
467468
}
468469

470+
if (consoleHolder != null) {
471+
consoleHolder.close();
472+
}
473+
469474
threadManager.cleanupMainThread();
470475
safepointManager.checkNoRunningThreads();
471476

@@ -684,6 +689,8 @@ public ConsoleHolder getConsoleHolder() {
684689

685690
public void setConsoleHolder(ConsoleHolder consoleHolder) {
686691
synchronized (this) {
692+
final ConsoleHolder previous = Objects.requireNonNull(this.consoleHolder);
693+
previous.close();
687694
this.consoleHolder = consoleHolder;
688695
}
689696
}

src/main/java/org/truffleruby/core/VMPrimitiveNodes.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
import com.oracle.truffle.api.profiles.ConditionProfile;
9191

9292
import sun.misc.Signal;
93+
import sun.misc.SignalHandler;
9394

9495
@CoreModule(value = "VMPrimitives", isClass = true)
9596
public abstract class VMPrimitiveNodes {
@@ -229,34 +230,34 @@ public static abstract class VMWatchSignalNode extends PrimitiveArrayArgumentsNo
229230

230231
@TruffleBoundary
231232
@Specialization
232-
protected boolean restoreDefault(RubyString signalName, RubyString action) {
233+
protected boolean restoreDefault(RubyString signalString, RubyString action) {
233234
final String actionString = StringOperations.getString(action);
234-
final String signal = StringOperations.getString(signalName);
235+
final String signalName = StringOperations.getString(signalString);
235236

236237
switch (actionString) {
237238
case "DEFAULT":
238-
return restoreDefaultHandler(signal);
239+
return restoreDefaultHandler(signalName);
239240
case "SYSTEM_DEFAULT":
240-
return restoreSystemHandler(signal);
241+
return restoreSystemHandler(signalName);
241242
case "IGNORE":
242-
return registerIgnoreHandler(signal);
243+
return registerIgnoreHandler(signalName);
243244
default:
244245
throw new UnsupportedOperationException(actionString);
245246
}
246247
}
247248

248249
@TruffleBoundary
249250
@Specialization
250-
protected boolean watchSignalProc(RubyString signalName, RubyProc action) {
251+
protected boolean watchSignalProc(RubyString signalString, RubyProc action) {
251252
if (getContext().getThreadManager().getCurrentThread() != getContext().getThreadManager().getRootThread()) {
252253
// The proc will be executed on the main thread
253254
SharedObjects.writeBarrier(getContext(), action);
254255
}
255256

256257
final RubyContext context = getContext();
257258

258-
final String signal = StringOperations.getString(signalName);
259-
return registerHandler(signal, () -> {
259+
String signalName = StringOperations.getString(signalString);
260+
return registerHandler(signalName, signal -> {
260261
if (context.getOptions().SINGLE_THREADED) {
261262
RubyLanguage.LOGGER.severe(
262263
"signal " + signal + " caught but can't create a thread to handle it so ignoring");
@@ -279,9 +280,9 @@ protected boolean watchSignalProc(RubyString signalName, RubyProc action) {
279280
printStream.println(
280281
"[ruby] SEVERE: signal " + signal +
281282
" caught but can't attach a thread to handle it so restoring the default handler and re-raising the signal");
282-
Signals.restoreDefaultHandler(signal);
283+
Signals.restoreDefaultHandler(signalName);
283284
try {
284-
Signal.raise(new Signal(signal));
285+
Signal.raise(signal);
285286
} catch (IllegalArgumentException illegalArgumentException) {
286287
illegalArgumentException.printStackTrace(printStream);
287288
}
@@ -351,7 +352,7 @@ private boolean registerIgnoreHandler(String signalName) {
351352
}
352353

353354
@TruffleBoundary
354-
private boolean registerHandler(String signalName, Runnable newHandler) {
355+
private boolean registerHandler(String signalName, SignalHandler newHandler) {
355356
if (getContext().getOptions().EMBEDDED) {
356357
RubyLanguage.LOGGER.warning(
357358
"trapping signal " + signalName +

src/main/java/org/truffleruby/core/adapters/InputStreamAdapter.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@
99
*/
1010
package org.truffleruby.core.adapters;
1111

12-
import java.io.InputStream;
13-
12+
import org.graalvm.shadowed.org.jline.utils.NonBlockingInputStream;
1413
import org.truffleruby.RubyContext;
1514
import org.truffleruby.core.support.RubyIO;
1615
import org.truffleruby.language.Nil;
1716

18-
public class InputStreamAdapter extends InputStream {
17+
/** extends NonBlockingInputStream so JLine does not create an extra thread and calls read() on the same thread as
18+
* calling readLine(). */
19+
public class InputStreamAdapter extends NonBlockingInputStream {
1920

2021
private final RubyContext context;
2122
private final RubyIO object;
2223

24+
private int peeked = EOF;
25+
2326
public InputStreamAdapter(RubyContext context, RubyIO object) {
2427
this.context = context;
2528
this.object = object;
@@ -30,9 +33,24 @@ public int read() {
3033
final Object result = context.send(object, "getbyte");
3134

3235
if (result == Nil.INSTANCE) {
33-
return -1;
36+
return EOF;
3437
}
3538

3639
return (int) result;
3740
}
41+
42+
@Override
43+
public int read(long timeout, boolean isPeek) {
44+
if (peeked != EOF) {
45+
int c = peeked;
46+
this.peeked = EOF;
47+
return c;
48+
}
49+
50+
int character = read();
51+
if (isPeek) {
52+
peeked = character;
53+
}
54+
return character;
55+
}
3856
}

0 commit comments

Comments
 (0)