Issue #19231 has been reported by andrykonchin (Andrew Konchin).
----------------------------------------
Bug #19231: Integer#step and Float::INFINITY - inconsistent behaviour when called with and without a block
https://bugs.ruby-lang.org/issues/19231
* Author: andrykonchin (Andrew Konchin)
* Status: Open
* Priority: Normal
* ruby -v: 3.1.2
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
The initial issue was reported here https://github.com/oracle/truffleruby/issues/2797.
`0.step(Float::INFINITY, 10)` returns:
- `Integers` when called with a block
- `Floats` when called without a block
I would expect `Floats` to be returned in both cases.
Examples:
```ruby
0.step(100.0, 10).take(1).map(&:class)
# => [Float]
```
```ruby
0.step(Float::INFINITY, 10) { |offset| p offset.class; break }
# Integer
```
When `to` argument is a finite `Float` value then calling with a block returns `Floats` as well:
```ruby
0.step(100.0, 10) { |offset| p offset.class; break }
# Float
```
Wondering whether it's intentional behaviour.
I've found a related issue https://bugs.ruby-lang.org/issues/15518.
--
https://bugs.ruby-lang.org/
Issue #19742 has been reported by ioquatix (Samuel Williams).
----------------------------------------
Bug #19742: Introduce `Module#anonymous?`
https://bugs.ruby-lang.org/issues/19742
* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
As a follow-on <from https://bugs.ruby-lang.org/issues/19521>, I'd like propose we introduce `Module#anonymous?`.
In some situations, like logging/formatting, serialisation/deserialization, debugging or meta-programming, we might like to know if a class is a proper constant or not.
However, this brings about some other issues which might need to be discussed.
After assigning a constant, then removing it, the internal state of Ruby still believes that the class name is permanent, even thought it's no longer true.
e.g.
```
m = Module.new
m.anonymous? # true
M = m
m.anonyomous # false
Object.send(:remove_const, :M)
M # uninitialized constant M (NameError)
M.anonymous? # false
```
Because RCLASS data structure is not updated after the constant is removed, internally the state still has a "permanent class name".
I want to use this proposal to discuss this issue and whether there is anything we should do about such behaviour (or even if it's desirable).
Proposed PR: https://github.com/ruby/ruby/pull/7966
cc @fxn
--
https://bugs.ruby-lang.org/
Issue #20096 has been reported by jay4rubydev (Jay M).
----------------------------------------
Bug #20096: Ruby 3.2.2 win32/registry: Junk appended to Windows Registry String Value
https://bugs.ruby-lang.org/issues/20096
* Author: jay4rubydev (Jay M)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x64-mswin64_140]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
Ruby Version:
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x64-mswin64_140]
Compiler: MSVC 2019 - Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30147 for x64.
Issue: win32/registry adds junk to Windows Registry String Value
Code:
require 'win32/registry'
win_oracle_key = "SOFTWARE\\MicroSoft"
reg=Win32::Registry::HKEY_LOCAL_MACHINE.open(win_oracle_key, Win32::Registry::KEY_ALL_ACCESS)
inst_loc_key = "inst_loc"
inv_dir="C:\\Program Files\\Tester\\ModuleInfo"
reg[inst_loc_key] = inv_dir
Result:
Registry contains:
C:\Program Files\Tester\ModuleInfo爀
Observation: Looks like memory overread
Expected Result - without the junk:
C:\Program Files\Tester\ModuleInfo
After changing the code in registry.rb:
From:
def write(name, type, data)
termsize = 0
case type
when REG_SZ, REG_EXPAND_SZ
data = data.encode(WCHAR)
termsize = WCHAR_SIZE
To:
def write(name, type, data)
termsize = 0
case type
when REG_SZ, REG_EXPAND_SZ
enc_data = data.encode(WCHAR)
# Add NULL WCHAR for string data and don't set the termsize because
# enc_data.bytesize will now include the size of the NULL character.
enc_data += WCHAR_NUL
# termsize = WCHAR_SIZE
...
--
https://bugs.ruby-lang.org/
Issue #20206 has been reported by lacostej (Jerome Lacoste).
----------------------------------------
Bug #20206: PTY.spawn seems to fail to capture the output of "echo foo" once in a while
https://bugs.ruby-lang.org/issues/20206
* Author: lacostej (Jerome Lacoste)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
We use PTY.spawn to call "echo foo", and on Mac it seems to randomly fail, capturing an empty output every now and then.
On Linux, the failure doesn't seem to happen.
The following code
1. contains 2 ways of capturing the output from PTY.spawn. Both seem to show the same issue (`run_command` and `run_command2`)
2. invokes the external `stress` program. This helps to trigger the issue more often.
``` ruby
require 'pty'
require 'expect'
def run_command(command)
output = []
PTY.spawn(command) do |command_stdout, command_stdin, pid|
begin
command_stdout.each do |l|
line = l.chomp
output << line
end
rescue Errno::EIO
# This is expected on some linux systems, that indicates that the subcommand finished
# and we kept trying to read, ignore it
ensure
command_stdout.close
command_stdin.close
Process.wait(pid)
end
end
raise "#{$?.exited?} #{$?.stopped?} #{$?.signaled?} - #{$?.stopsig} - #{$?.termsig} -" unless $?.exitstatus == 0
[$?.exitstatus, output.join("\n")]
end
def run_command2(command)
output = []
PTY.spawn(command) do |command_stdout, command_stdin, pid|
output = ""
begin
a = command_stdout.expect(/foo.*/, 5)
output = a[0] if a
ensure
command_stdout.close
command_stdin.close
Process.wait(pid)
end
end
raise "#{$?.exited?} #{$?.stopped?} #{$?.signaled?} - #{$?.stopsig} - #{$?.termsig} -" unless $?.exitstatus == 0
[$?.exitstatus, output]
end
def test_spawn(command)
status, output = run_command(command)
errors = []
errors << "status was '#{status}'" unless status == 0
errors << "output was '#{output}'" unless output == "foo"
raise errors.join(" - ") unless errors.empty?
end
t = nil
pid = nil
if ENV['STRESS']
t = Thread.new do |t|
puts "Spawning stress"
pid = spawn("stress -c 16 -t 99", pgroup: true)
puts "Waiting #{pid}"
Process.wait(pid)
puts "#{pid} DONE"
end
end
command = "echo foo"
if ARGV.count == 1
command = ARGV[0]
end
puts "Will run command: '#{command}'"
errors = 0
2000.times do |i|
begin
test_spawn(command)
rescue => e
puts "ERROR #{i}: #{e}"
errors += 1
end
end
if t
begin
Process.kill(:SIGKILL, -pid)
rescue Errno::ESRCH # already dead, ignore
end
t.join
end
raise "Failed #{errors} times" unless errors == 0
```
Here are some ways of reproducing the issue
```
ruby test_pty.rb
STRESS=y ruby test_pty.rb
```
Here's an example of how it fails on circleci. https://app.circleci.com/pipelines/github/lacostej/cienvs/33/workflows/d6d8…
Tested on ruby 2.6 to ruby 3.3.0 on Mac.
--
https://bugs.ruby-lang.org/
Issue #20108 has been reported by shioimm (Misaki Shioi).
----------------------------------------
Feature #20108: Introduction of Happy Eyeballs Version 2 (RFC8305) in Socket.tcp
https://bugs.ruby-lang.org/issues/20108
* Author: shioimm (Misaki Shioi)
* Status: Open
* Priority: Normal
----------------------------------------
This is an implementation of Happy Eyeballs version 2 (RFC 8305) in Socket.tcp.
### Background
Currently, `Socket.tcp` synchronously resolves names and makes connection attempts with `Addrinfo::foreach.`
This implementation has the following two problems.
1. In hostname resolution, the program stops until the DNS server responds to all DNS queries.
2. In a connection attempt, while an IP address is trying to connect to the destination host and is taking time, the program stops, and other resolved IP addresses cannot try to connect.
### Proposal
"Happy Eyeballs" ([RFC 8305](https://datatracker.ietf.org/doc/html/rfc8305)) is an algorithm to solve this kind of problem. It avoids delays to the user whenever possible and also uses IPv6 preferentially.
I implemented it into `Socket.tcp` by using `Addrinfo.getaddrinfo` in each thread spawned per address family to resolve the hostname asynchronously, and using `Socket::connect_nonblock` to try to connect with multiple addrinfo in parallel.
See https://github.com/ruby/ruby/pull/9374
### Outcome
This change eliminates a fatal defect in the following cases.
#### Case 1. One of the A or AAAA DNS queries does not return
```ruby
require 'socket'
class Addrinfo
class << self
# Current Socket.tcp depends on foreach
def foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, timeout: nil, &block)
getaddrinfo(nodename, service, Socket::AF_INET6, socktype, protocol, flags, timeout: timeout)
.concat(getaddrinfo(nodename, service, Socket::AF_INET, socktype, protocol, flags, timeout: timeout))
.each(&block)
end
def getaddrinfo(_, _, family, *_)
case family
when Socket::AF_INET6 then sleep
when Socket::AF_INET then [Addrinfo.tcp("127.0.0.1", 4567)]
end
end
end
end
Socket.tcp("localhost", 4567)
```
Because the current `Socket.tcp` cannot resolve IPv6 names, the program stops in this case. It cannot start to connect with IPv4 address.
Though `Socket.tcp` with HEv2 can promptly start a connection attempt with IPv4 address in this case.
#### Case 2. Server does not promptly return ack for syn of either IPv4 / IPv6 address family
```ruby
require 'socket'
fork do
socket = Socket.new(Socket::AF_INET6, :STREAM)
socket.setsockopt(:SOCKET, :REUSEADDR, true)
socket.bind(Socket.pack_sockaddr_in(4567, '::1'))
sleep
socket.listen(1)
connection, _ = socket.accept
connection.close
socket.close
end
fork do
socket = Socket.new(Socket::AF_INET, :STREAM)
socket.setsockopt(:SOCKET, :REUSEADDR, true)
socket.bind(Socket.pack_sockaddr_in(4567, '127.0.0.1'))
socket.listen(1)
connection, _ = socket.accept
connection.close
socket.close
end
Socket.tcp("localhost", 4567)
```
The current `Socket.tcp` tries to connect serially, so when its first name resolves an IPv6 address and initiates a connection to an IPv6 server, this server does not return an ACK, and the program stops.
Though `Socket.tcp` with HEv2 starts to connect sequentially and in parallel so a connection can be established promptly at the socket that attempted to connect to the IPv4 server.
In exchange, the performance of `Socket.tcp` with HEv2 will be degraded.
```
100.times { Socket.tcp("www.ruby-lang.org", 80) }
# Socket.tcp (Before) 0.123809
# Socket.tcp (After) 0.224684
```
This is due to the addition of the creation of IO objects, Thread objects, etc., and calls to `IO::select` in the implementation.
--
https://bugs.ruby-lang.org/
Issue #19973 has been reported by tenderlovemaking (Aaron Patterson).
----------------------------------------
Bug #19973: Duplicate keyword argument names don't always warn
https://bugs.ruby-lang.org/issues/19973
* Author: tenderlovemaking (Aaron Patterson)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.3.0dev (2023-10-24T19:38:50Z cleanup 3525a9bd22) [arm64-darwin23]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Calling a method with duplicate keyword arguments doesn't warn when it could:
```ruby
def bar a:, b:
a + b
end
# Warning
bar(a: 1, b: 3, a: 2)
z = { b: 123 }
# No warning
bar(a: 1, **z, a: 2)
```
The first call to `bar` gives a warning about duplicate keyword args, but the second call doesn't. I think both cases should emit a warning.
--
https://bugs.ruby-lang.org/
Issue #19967 has been reported by nobu (Nobuyoshi Nakada).
----------------------------------------
Bug #19967: Already installed libruby.dylib is used for test on macOS
https://bugs.ruby-lang.org/issues/19967
* Author: nobu (Nobuyoshi Nakada)
* Status: Assigned
* Priority: Normal
* Assignee: nobu (Nobuyoshi Nakada)
* Backport: 3.0: REQUIRED, 3.1: REQUIRED, 3.2: REQUIRED
----------------------------------------
On macOS, in the case of `--enable-shared` and `--disable-load-relative`, already install libruby.dylib is used during tests if exists.
This is because since [Bug #14992], `DYLD_FALLBACK_LIBRARY_PATH` is used instead of `DYLD_LIBRARY_PATH`.
The latter environment variable is used preferentially, whereas the former is used as a fallback, as the name implies.
--
https://bugs.ruby-lang.org/
Issue #20180 has been reported by ozydingo (Andrew Schwartz).
----------------------------------------
Bug #20180: Inconsistent evaluation of `**{}` depending on position in array
https://bugs.ruby-lang.org/issues/20180
* Author: ozydingo (Andrew Schwartz)
* Status: Open
* Priority: Normal
* ruby -v: 3.3
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
Reproduced on ruby:3.3 docker container
The evaluation of `**{}` differs if it appears alone (evaluates as empty / no content) in an array vs after another element (evaluates as an empty Hash).
```rb
args = []; kwargs = {}
[*args]
# => []
[**kwargs]
# => []
[*args, **kwargs]
# => [{}]
[*args] + [**kwargs] == [*args, **kwargs]
=> false
```
I claim this violates the Principle of Least Surprise. I will admit that beyond a thin example I will give below, I am struggling to come up with a more convincing pragmatic reason that this should be addressed, but due to how surprising it is and the bugs it cause our team I wanted to submit it for tracking. This may be related to https://bugs.ruby-lang.org/issues/20064?tab=notes though the issues are distinct.
Specifically, in my use case, I am writing a class responsible for adapting arguments in one form to another (json object to method args and vice versa). My tests broken when adding support for keyword args due to expectations of no args:
```
# RSpec
expect(foo).to have_received(:bar).with([]) # This now need `with([], {})`
```
Again, this is a bit thin as by itself this isn't problematic as I can add the `{}`. However, this does require that my test knows more about the implementation that it should. That is, my implementation might be
```
if kwargs.present?
call(*args, **kwargs)
else
call(*args)
end
```
This change does not change the behavior of the class, but will break the test. I therefore think this behavior of `**kwargs` is problematic.
--
https://bugs.ruby-lang.org/
Issue #20229 has been reported by jeremyevans0 (Jeremy Evans).
----------------------------------------
Bug #20229: Empty keyword splat in array not removed in ARGSPUSH case
https://bugs.ruby-lang.org/issues/20229
* Author: jeremyevans0 (Jeremy Evans)
* Status: Open
* Priority: Normal
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
For the following code:
```ruby
a = []
kw = {}
[*a, **kw]
```
Ruby 2.0-3.3 return `[{}]`, when the desired result (since 2.7) is `[]`.
I discovered this while working on an optimization for the ARGSCAT case, where `[*a, 1, **kw]` allocates two arrays instead of a single array. My optimization fixes that by adding a `pushtoarraykwsplat` instruction as a replacement for `newarraykwsplat`. I've used `pushtoarraykwsplat` in the ARGSPUSH case to fix this bug.
My pull request for the bug fix and optimization: https://github.com/ruby/ruby/pull/9766
--
https://bugs.ruby-lang.org/