Skip to content

Commit 317c565

Browse files
committed
[GR-26176] Migrate from JLine 2 to JLine 3
* Readline#refresh_line is no longer implemented since the functionality is no longer exposed by JLine3.
1 parent 732fe2e commit 317c565

18 files changed

+703
-264
lines changed

CHANGELOG.md

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

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

40+
Changes:
41+
42+
* Migrated from JLine 2 to JLine 3 for the `readline` standard library.
43+
4044
# 20.2.0
4145

4246
New features:

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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/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
}

src/main/java/org/truffleruby/core/thread/ThreadManager.java

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -500,29 +500,6 @@ public <T> T runUntilResult(Node currentNode, BlockingAction<T> action) {
500500
return result;
501501
}
502502

503-
/** Runs {@code action} until it returns a non-null value. The blocking action might be {@link Thread#interrupted()}
504-
* , for instance by the {@link SafepointManager}, in which case it will be run again. The unblocking action is
505-
* registered with the thread manager and will be invoked if the {@link SafepointManager} needs to interrupt the
506-
* thread. If the blocking action is making a native call, simply interrupting the thread will not unblock the
507-
* action. It is the responsibility of the unblocking action to break out of the native call so the thread can be
508-
* interrupted.
509-
*
510-
* @param blockingAction must not touch any Ruby state
511-
* @param unblockingAction must not touch any Ruby state
512-
* @return the first non-null return value from {@code action} */
513-
@TruffleBoundary
514-
public <T> T runUntilResult(Node currentNode, BlockingAction<T> blockingAction, UnblockingAction unblockingAction) {
515-
assert unblockingAction != null;
516-
final UnblockingActionHolder holder = getActionHolder(Thread.currentThread());
517-
518-
final UnblockingAction oldUnblockingAction = holder.changeTo(unblockingAction);
519-
try {
520-
return runUntilResult(currentNode, blockingAction);
521-
} finally {
522-
holder.restore(oldUnblockingAction);
523-
}
524-
}
525-
526503
@TruffleBoundary
527504
UnblockingActionHolder getActionHolder(Thread thread) {
528505
return ConcurrentOperations.getOrCompute(unblockingActions, thread, t -> new UnblockingActionHolder(t, null));

0 commit comments

Comments
 (0)