Skip to content

Commit

Permalink
[GR-37533] Backports for 24.0 batch 1
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/4170
  • Loading branch information
eregon committed Feb 13, 2024
2 parents 8e1f1e9 + 7017629 commit e7103f0
Show file tree
Hide file tree
Showing 85 changed files with 951 additions and 671 deletions.
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
New features:

* C/C++ extensions are now compiled using the system toolchain and executed natively instead of using GraalVM LLVM (Sulong). This leads to faster startup, no warmup, better compatibility, smaller distribution and faster installation for C/C++ extensions (#3118, @eregon).
* Full suport for the Ruby 3.2 and Ruby 3.3 syntax by adopting the [Prism](https://github.com/ruby/prism) parser, which is about twice as fast as the old parser (#3117, #3038, #3039, @andrykonchin, @eregon).
* Pattern matching is now fully supported, with the exception of Find pattern (`in [*, a, *]`) (#3332, #2683, @eregon, @razetime).
* Full support for the Ruby 3.2 and Ruby 3.3 syntax by adopting the [Prism](https://github.com/ruby/prism) parser, which is about twice as fast as the old parser (#3117, #3038, #3039, @andrykonchin, @eregon).
* Pattern matching is now fully supported (#3332, #2683, @eregon, @razetime).

Bug fixes:

Expand All @@ -21,6 +21,7 @@ Bug fixes:
* Fix using the `--cpusampler` profiler when there are custom unblock functions for `rb_thread_call_without_gvl()` (#3013, @eregon).
* Fix recursive raising `FrozenError` exception when redefined `#inspect` modifies an object (#3388, @andrykonchin).
* Fix `Integer#div` returning the wrong object type when the divisor is a `Rational` (@simonlevasseur, @nirvdrum).
* Remove constant `Random::DEFAULT` (#3039, @patricklinpl)

Compatibility:

Expand All @@ -45,6 +46,14 @@ Compatibility:
* Support passing anonymous * and ** parameters as method call arguments (#3039, @andrykonchin).
* Handle either positional or keywords arguments by default in `Struct.new` (#3039, @rwstauner).
* Promote `Set` class to core library (#3039, @andrykonchin).
* Support `connect_timeout` keyword argument to `TCPSocket.{new,open}` (#3421, @manefz, @patricklinpl, @nirvdrum, @rwstauner).
* Add `File.lutime` and `Pathname#lutime` methods (#3039, @andrykonchin).
* Add a deprecation warning for `Encoding#replicate` (#3039, @patricklinpl, @manefz, @nirvdrum).
* Change `UnboundMethod#{==,inspect}` to use the owner module rather than the origin (#3039, @rwstauner, @manefz, @patricklinpl)
* Support `lambda` keyword argument in `Proc#parameters` (#3039, @thomasmarshall, @goyox86).
* Limit maximum encoding set size by 256 (#3039, @thomasmarshall, @goyox86).
* Remove deprecated methods `Dir.exists?`, `File.exists?`, and `Kernel#=~` (#3039, @patricklinpl, @nirvdrum).
* Remove deprecated `FileTest.exists?` method (#3039, @andrykonchin).

Performance:

Expand Down
4 changes: 4 additions & 0 deletions doc/user/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ The JVM enforces a maximum array size of 2<sup>31</sup>-1 (by storing the size i
That is, Strings must be smaller than 2GB. This is the same restriction as JRuby.
A possible workaround could be to use natively-allocated strings, but it would be a large effort to support every Ruby String operation on native strings.

### Strings in UTF-16 and UTF-32 encoding

TruffleRuby does not support UTF-16 strings with an odd number of bytes (in native endianness). Similarly, with UTF-32 it needs to be a multiple of 4. This is necessary for optimizations, compression, invariants, etc.

### Threads detect interrupts at different points

TruffleRuby threads may detect that they have been interrupted at different points in the program compared to where they would on MRI.
Expand Down
8 changes: 4 additions & 4 deletions lib/truffle/pathname.rb
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,10 @@ def truncate(length) File.truncate(@path, length) end
# See <tt>File.utime</tt>. Update the access and modification times.
def utime(atime, mtime) File.utime(atime, mtime, @path) end

# See <tt>File.lutime</tt>. Update the access and modification times.
# Same as Pathname#utime, but does not follow symbolic links.
def lutime(atime, mtime) File.lutime(atime, mtime, @path) end

# See <tt>File.basename</tt>. Returns the last component of the path.
def basename(*args) self.class.new(File.basename(@path, *args)) end

Expand Down Expand Up @@ -1084,10 +1088,6 @@ def unlink()
alias delete unlink
end

class Pathname
undef =~
end

module Kernel
# create a pathname object.
#
Expand Down
4 changes: 2 additions & 2 deletions lib/truffle/socket/tcp_socket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def self.gethostbyname(hostname)
[hostname, alternatives, family, *addresses]
end

def initialize(host, service, local_host = nil, local_service = nil)
def initialize(host, service, local_host = nil, local_service = nil, connect_timeout: nil)
@no_reverse_lookup = Primitive.class(self).do_not_reverse_lookup

if host
Expand Down Expand Up @@ -108,7 +108,7 @@ def initialize(host, service, local_host = nil, local_service = nil)
end

connect_status = Truffle::Socket::Foreign
.connect(descriptor, Socket.sockaddr_in(port, address))
.connect(descriptor, Socket.sockaddr_in(port, address), connect_timeout)

break if connect_status >= 0
end
Expand Down
15 changes: 13 additions & 2 deletions lib/truffle/socket/truffle/foreign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

require 'timeout'

module Truffle
module Socket
module Foreign
Expand Down Expand Up @@ -107,14 +109,23 @@ def self.bind(descriptor, sockaddr)
end
end

def self.connect(descriptor, sockaddr)
def self.connect(descriptor, sockaddr, connect_timeout = nil)
sockaddr = Socket.coerce_to_string(sockaddr)

sockaddr_p = Primitive.io_thread_buffer_allocate(sockaddr.bytesize)
begin
sockaddr_p.write_bytes(sockaddr)

_connect(descriptor, sockaddr_p, sockaddr.bytesize)
if Primitive.nil?(connect_timeout)
_connect(descriptor, sockaddr_p, sockaddr.bytesize)
else
# In CRuby a connect_timeout of 0 fires immediately whereas a 0 for
# Timeout.timeout means "no timeout", so we change 0 to 1ns
# (the smallest time interval for clock_gettime).
Timeout.timeout(connect_timeout == 0 ? 0.000000001 : connect_timeout, IO::TimeoutError) do
_connect(descriptor, sockaddr_p, sockaddr.bytesize)
end
end
ensure
Primitive.io_thread_buffer_free(sockaddr_p)
end
Expand Down
5 changes: 5 additions & 0 deletions spec/ruby/core/encoding/replicate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
Encoding::US_ASCII.replicate('MY-US-ASCII')
}.should complain(/warning: Encoding#replicate is deprecated and will be removed in Ruby 3.3; use the original encoding instead/)
end

it "raises EncodingError if too many encodings" do
code = '1_000.times {|i| Encoding::US_ASCII.replicate("R_#{i}") }'
ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)')
end
end

ruby_version_is "3.3" do
Expand Down
5 changes: 5 additions & 0 deletions spec/ruby/core/file/lutime_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
require_relative '../../spec_helper'
require_relative 'shared/update_time'

describe "File.lutime" do
it_behaves_like :update_time, :lutime
end

describe "File.lutime" do
platform_is_not :windows do
Expand Down
105 changes: 105 additions & 0 deletions spec/ruby/core/file/shared/update_time.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
describe :update_time, shared: true do
before :all do
@time_is_float = platform_is :windows
end

before :each do
@atime = Time.now
@mtime = Time.now
@file1 = tmp("specs_file_utime1")
@file2 = tmp("specs_file_utime2")
touch @file1
touch @file2
end

after :each do
rm_r @file1, @file2
end

it "sets the access and modification time of each file" do
File.send(@method, @atime, @mtime, @file1, @file2)

if @time_is_float
File.atime(@file1).should be_close(@atime, 0.0001)
File.mtime(@file1).should be_close(@mtime, 0.0001)
File.atime(@file2).should be_close(@atime, 0.0001)
File.mtime(@file2).should be_close(@mtime, 0.0001)
else
File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
end
end

it "uses the current times if two nil values are passed" do
tn = Time.now
File.send(@method, nil, nil, @file1, @file2)

if @time_is_float
File.atime(@file1).should be_close(tn, 0.050)
File.mtime(@file1).should be_close(tn, 0.050)
File.atime(@file2).should be_close(tn, 0.050)
File.mtime(@file2).should be_close(tn, 0.050)
else
File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
end
end

it "accepts an object that has a #to_path method" do
File.send(@method, @atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
end

it "accepts numeric atime and mtime arguments" do
if @time_is_float
File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2)

File.atime(@file1).should be_close(@atime, 0.0001)
File.mtime(@file1).should be_close(@mtime, 0.0001)
File.atime(@file2).should be_close(@atime, 0.0001)
File.mtime(@file2).should be_close(@mtime, 0.0001)
else
File.send(@method, @atime.to_i, @mtime.to_i, @file1, @file2)

File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
end
end

it "may set nanosecond precision" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
File.send(@method, t, t, @file1)

File.atime(@file1).nsec.should.between?(0, 123500000)
File.mtime(@file1).nsec.should.between?(0, 123500000)
end

it "returns the number of filenames in the arguments" do
File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2).should == 2
end

platform_is :linux do
platform_is wordsize: 64 do
it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
# https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
# "Therefore, timestamps should not overflow until May 2446."
# https://lwn.net/Articles/804382/
# "On-disk timestamps hitting the y2038 limit..."
# The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
# https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
# Amazon Linux 2023 returns 2486-07-02 in this example
# http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
time = Time.at(1<<44)
File.send(@method, time, time, @file1)

[559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
[559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
end
end
end
end
100 changes: 2 additions & 98 deletions spec/ruby/core/file/utime_spec.rb
Original file line number Diff line number Diff line change
@@ -1,102 +1,6 @@
require_relative '../../spec_helper'
require_relative 'shared/update_time'

describe "File.utime" do

before :all do
@time_is_float = platform_is :windows
end

before :each do
@atime = Time.now
@mtime = Time.now
@file1 = tmp("specs_file_utime1")
@file2 = tmp("specs_file_utime2")
touch @file1
touch @file2
end

after :each do
rm_r @file1, @file2
end

it "sets the access and modification time of each file" do
File.utime(@atime, @mtime, @file1, @file2)
if @time_is_float
File.atime(@file1).should be_close(@atime, 0.0001)
File.mtime(@file1).should be_close(@mtime, 0.0001)
File.atime(@file2).should be_close(@atime, 0.0001)
File.mtime(@file2).should be_close(@mtime, 0.0001)
else
File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
end
end

it "uses the current times if two nil values are passed" do
tn = Time.now
File.utime(nil, nil, @file1, @file2)
if @time_is_float
File.atime(@file1).should be_close(tn, 0.050)
File.mtime(@file1).should be_close(tn, 0.050)
File.atime(@file2).should be_close(tn, 0.050)
File.mtime(@file2).should be_close(tn, 0.050)
else
File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
end
end

it "accepts an object that has a #to_path method" do
File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
end

it "accepts numeric atime and mtime arguments" do
if @time_is_float
File.utime(@atime.to_f, @mtime.to_f, @file1, @file2)
File.atime(@file1).should be_close(@atime, 0.0001)
File.mtime(@file1).should be_close(@mtime, 0.0001)
File.atime(@file2).should be_close(@atime, 0.0001)
File.mtime(@file2).should be_close(@mtime, 0.0001)
else
File.utime(@atime.to_i, @mtime.to_i, @file1, @file2)
File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
end
end

it "may set nanosecond precision" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
File.utime(t, t, @file1)
File.atime(@file1).nsec.should.between?(0, 123500000)
File.mtime(@file1).nsec.should.between?(0, 123500000)
end

it "returns the number of filenames in the arguments" do
File.utime(@atime.to_f, @mtime.to_f, @file1, @file2).should == 2
end

platform_is :linux do
platform_is wordsize: 64 do
it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
# https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
# "Therefore, timestamps should not overflow until May 2446."
# https://lwn.net/Articles/804382/
# "On-disk timestamps hitting the y2038 limit..."
# The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
# https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
# Amazon Linux 2023 returns 2486-07-02 in this example
# http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
time = Time.at(1<<44)
File.utime(time, time, @file1)
[559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
[559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
end
end
end
it_behaves_like :update_time, :utime
end
8 changes: 8 additions & 0 deletions spec/ruby/core/filetest/exist_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@
describe "FileTest.exist?" do
it_behaves_like :file_exist, :exist?, FileTest
end

ruby_version_is "3.2" do
describe "FileTest.exists?" do
it "has been removed" do
FileTest.should_not.respond_to?(:exists?)
end
end
end
14 changes: 14 additions & 0 deletions spec/ruby/core/kernel/not_match_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ def !~(obj)
(obj !~ :foo).should == false
end

ruby_version_is ""..."3.2" do
it "returns true if self does not respond to #=~" do
suppress_warning do
(Object.new !~ :foo).should == true
end
end
end

ruby_version_is "3.2" do
it "raises NoMethodError if self does not respond to #=~" do
-> { Object.new !~ :foo }.should raise_error(NoMethodError)
end
end

it 'can be overridden in subclasses' do
obj = KernelSpecs::NotMatch.new
(obj !~ :bar).should == :foo
Expand Down
Loading

0 comments on commit e7103f0

Please sign in to comment.