From 42a6f9770cf49ad39f8c9672d4bcc3745b6e541d Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 3 Sep 2026 23:41:29 +0900 Subject: [PATCH 1/4] Specify backends by `IO_CONSOLE_BACKEND` environment variable --- jruby/lib/io/console.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jruby/lib/io/console.rb b/jruby/lib/io/console.rb index b804a93..053e04d 100644 --- a/jruby/lib/io/console.rb +++ b/jruby/lib/io/console.rb @@ -37,6 +37,9 @@ class Mode end backends = [] +ENV["IO_CONSOLE_BACKEND"]&.tap do |be| + backends.concat(be.split(",").map {|b| b.split(":")}) +end&.first || # If Linux or BSD, try to load the native version case RbConfig::CONFIG['host_os'] when /darwin|openbsd|freebsd|netbsd/i From 2be3fbe2aa121bfbafa2a38a5e8d2935cdb485f6 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 4 Sep 2026 16:34:44 +0900 Subject: [PATCH 2/4] Run the test with pure-ruby libraries by `IO_CONSOLE_BACKEND` --- Rakefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index c67301a..4b6f891 100644 --- a/Rakefile +++ b/Rakefile @@ -30,9 +30,10 @@ task :build => jruby_version_file task :test => jruby_version_file if RUBY_ENGINE == "jruby" Rake::TestTask.new(:test) do |t| - if extask + backend = ENV["IO_CONSOLE_BACKEND"] + if extask and !backend t.libs = [extask.lib_dir.chomp("/"+File.dirname(name))] - elsif RUBY_ENGINE == "jruby" + elsif RUBY_ENGINE == "jruby" or backend t.libs.unshift "jruby/lib" end t.libs << "lib" From fcb3281aac6f8a4edf81fbfdddb8863e6fcbea11 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 4 Sep 2026 00:09:38 +0900 Subject: [PATCH 3/4] Pass keyword arguments to the console --- jruby/lib/io/console/common.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jruby/lib/io/console/common.rb b/jruby/lib/io/console/common.rb index 1b55bac..198f8b1 100644 --- a/jruby/lib/io/console/common.rb +++ b/jruby/lib/io/console/common.rb @@ -6,7 +6,7 @@ module IO::Console class IO # TODO: Windows version uses "conin$" and "conout$" instead of /dev/tty - def self.console(sym = nil, *args) + def self.console(sym = nil, *args, **opts) raise TypeError, "expected Symbol, got #{sym.class}" unless sym.nil? || sym.kind_of?(Symbol) # klass = self == IO ? File : self @@ -44,7 +44,7 @@ def self.console(sym = nil, *args) end return nil unless con - return con.send(sym, *args) if sym + return con.send(sym, *args, **opts) if sym return con end From 976eb3c7d4d5eb2edeed78a275afb011dfa2c7fa Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 4 Sep 2026 00:15:26 +0900 Subject: [PATCH 4/4] Fix `getpass` to use `\n` explicitly regardless the RS --- ext/io/console/console.c | 12 +++++++++--- jruby/lib/io/console.rb | 2 +- jruby/lib/io/console/common.rb | 13 +++++-------- test/io/console/test_io_console.rb | 15 ++++++++++----- test/io/console/test_ractor.rb | 2 +- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/ext/io/console/console.c b/ext/io/console/console.c index 04d0f70..eb1c876 100644 --- a/ext/io/console/console.c +++ b/ext/io/console/console.c @@ -2135,10 +2135,17 @@ puts_call(VALUE io) return rb_io_write(io, rb_default_rs); } +static VALUE +funcall_with_rs(VALUE obj, ID mid) +{ + const VALUE rs = rb_default_rs; /* rvalue in TruffleRuby */ + return rb_funcallv(obj, mid, 1, &rs); +} + static VALUE gets_call(VALUE io) { - return rb_funcallv(io, id_gets, 0, 0); + return funcall_with_rs(io, id_gets); } static VALUE @@ -2161,8 +2168,7 @@ static VALUE str_chomp(VALUE str) { if (!NIL_P(str)) { - const VALUE rs = rb_default_rs; /* rvalue in TruffleRuby */ - rb_funcallv(str, id_chomp_bang, 1, &rs); + funcall_with_rs(str, id_chomp_bang); } return str; } diff --git a/jruby/lib/io/console.rb b/jruby/lib/io/console.rb index 053e04d..a9cb2ce 100644 --- a/jruby/lib/io/console.rb +++ b/jruby/lib/io/console.rb @@ -37,7 +37,7 @@ class Mode end backends = [] -ENV["IO_CONSOLE_BACKEND"]&.tap do |be| +ENV["IO_CONSOLE_BACKEND"]&.then do |be| backends.concat(be.split(",").map {|b| b.split(":")}) end&.first || # If Linux or BSD, try to load the native version diff --git a/jruby/lib/io/console/common.rb b/jruby/lib/io/console/common.rb index 198f8b1..4b3e4d6 100644 --- a/jruby/lib/io/console/common.rb +++ b/jruby/lib/io/console/common.rb @@ -58,14 +58,11 @@ def getpass(prompt = nil) wio = self == $stdin ? $stderr : self wio.write(prompt) if prompt begin - str = nil - noecho do - str = gets - end + str = noecho {gets("\n")} ensure - puts($/) + puts end - str&.chomp + str&.chomp("\n") end def input_pending? @@ -164,8 +161,8 @@ def getch(*) def getpass(prompt = nil) write(prompt) if prompt - str = gets&.chomp - puts($/) + str = gets("\n")&.chomp("\n") + puts str end end diff --git a/test/io/console/test_io_console.rb b/test/io/console/test_io_console.rb index ffc5b50..af207c8 100644 --- a/test/io/console/test_io_console.rb +++ b/test/io/console/test_io_console.rb @@ -26,14 +26,14 @@ def test_version end begin - PATHS = $LOADED_FEATURES.grep(%r"/io/console(?:\.#{RbConfig::CONFIG['DLEXT']}|\.rb|/\w+\.rb)\z") {$`} + paths = $LOADED_FEATURES.grep(%r"/io/console(?:\.#{RbConfig::CONFIG['DLEXT']}|\.rb|/\w+\.rb)\z") {$`} rescue Encoding::CompatibilityError $stderr.puts "test_io_console.rb debug" $LOADED_FEATURES.each{|path| $stderr.puts [path, path.encoding].inspect} raise end - PATHS.uniq! - INCLUDE_OPTS = "-I#{PATHS.join(File::PATH_SEPARATOR)}" + paths.uniq! + INCLUDE_OPTS = "-I#{paths.join(File::PATH_SEPARATOR)}".freeze # FreeBSD seems to hang on TTOU when running parallel tests # tested on FreeBSD 11.x. @@ -75,6 +75,7 @@ def test_bad_keyword end TTY_ENHANCED = IO.instance_method(:tty?).arity != 0 + TTY_MODE_STTY = IO.private_method_defined?(:_io_console_stty) def test_tty? pend "not supported" unless TTY_ENHANCED @@ -348,7 +349,9 @@ def test_getpass assert_equal("\r\n", r.gets) assert_equal("\"asdf\"", r.gets.chomp) end + end + def test_getpass_eof run_pty("p IO.console.getpass('> ')") do |r, w| assert_equal("> ", r.readpartial(10)) sleep 0.1 @@ -357,7 +360,9 @@ def test_getpass assert_equal("\r\n", r.gets) assert_equal("\"asdf\"", r.gets.chomp) end + end + def test_getpass_rs run_pty("$VERBOSE, $/ = nil, '.'; p IO.console.getpass('> ')") do |r, w| assert_equal("> ", r.readpartial(10)) sleep 0.1 @@ -380,7 +385,7 @@ def test_getpass_empty end def test_iflush - pend "stty cannot flush terminal queues" if IO.private_method_defined?(:_io_console_stty) + pend "stty cannot flush terminal queues" if TTY_MODE_STTY helper {|m, s| m.print "a" @@ -403,7 +408,7 @@ def test_oflush end def test_ioflush - pend "stty cannot flush terminal queues" if IO.private_method_defined?(:_io_console_stty) + pend "stty cannot flush terminal queues" if TTY_MODE_STTY helper {|m, s| m.print "a" diff --git a/test/io/console/test_ractor.rb b/test/io/console/test_ractor.rb index dff0c67..374db1f 100644 --- a/test/io/console/test_ractor.rb +++ b/test/io/console/test_ractor.rb @@ -5,7 +5,7 @@ class TestIOConsoleInRactor < Test::Unit::TestCase def test_ractor ext = "/io/console.#{RbConfig::CONFIG['DLEXT']}" - path = $".find {|path| path.end_with?(ext)} + path = $".find {|path| path.end_with?(ext)} || "io/console" assert_in_out_err(%W[-r#{path}], "#{<<~"begin;"}\n#{<<~'end;'}", ["true"], []) begin; class Ractor