diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 2ed27e278b1dd4..0a4e19e229b9a0 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -177,30 +177,30 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot if: ${{ failure() }} - make-ibm: - strategy: - matrix: - include: - - test_task: check - os: ubuntu-24.04-ppc64le - - test_task: check - os: ubuntu-24.04-s390x - fail-fast: false - - env: *make-env - - runs-on: ${{ matrix.os }} - - if: >- - ${{github.repository == 'ruby/ruby' - && !(false - || contains(github.event.head_commit.message, '[DOC]') - || contains(github.event.pull_request.title, '[DOC]') - || contains(github.event.pull_request.labels.*.name, 'Documentation') - || (github.event_name == 'push' && github.event.pull_request.user.login == 'dependabot[bot]') - )}} - - steps: *make-steps + # make-ibm: + # strategy: + # matrix: + # include: + # - test_task: check + # os: ubuntu-24.04-ppc64le + # - test_task: check + # os: ubuntu-24.04-s390x + # fail-fast: false + + # env: *make-env + + # runs-on: ${{ matrix.os }} + + # if: >- + # ${{github.repository == 'ruby/ruby' + # && !(false + # || contains(github.event.head_commit.message, '[DOC]') + # || contains(github.event.pull_request.title, '[DOC]') + # || contains(github.event.pull_request.labels.*.name, 'Documentation') + # || (github.event_name == 'push' && github.event.pull_request.user.login == 'dependabot[bot]') + # )}} + + # steps: *make-steps # Separated from `make` job to avoid making it a required status check ruby-bench: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index f9d1335d464016..ce4221137d601e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -83,7 +83,8 @@ jobs: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser iwr -useb get.scoop.sh | iex Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH - scoop install vcpkg uutils-coreutils + scoop install vcpkg + scoop install uutils-coreutils@0.5.0 shell: pwsh - name: Restore vcpkg artifact diff --git a/NEWS.md b/NEWS.md index 4c9a3e8eaee538..4f35077bfe0ecf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -46,6 +46,7 @@ releases. * RubyGems 4.1.0.dev * bundler 4.1.0.dev +* json 2.18.1 * prism 1.9.0 * resolv 0.7.1 * stringio 3.2.1.dev diff --git a/doc/language/ractor.md b/doc/language/ractor.md index 72fbde6e5a083d..a0acaf3a918e1a 100644 --- a/doc/language/ractor.md +++ b/doc/language/ractor.md @@ -12,7 +12,7 @@ You can create multiple Ractors which can run ruby code in parallel with each ot * Ruby processes start with one ractor (called the *main ractor*). * If the main ractor terminates, all other ractors receive termination requests, similar to how threads behave. * Each Ractor contains one or more `Thread`s. - * Threads within the same ractor share a ractor-wide global lock (GVL in MRI terminology), so they can't run in parallel wich each other (without releasing the GVL explicitly in C extensions). Threads in different ractors can run in parallel. + * Threads within the same ractor share a ractor-wide global lock (GVL in MRI terminology), so they can't run in parallel with each other (without releasing the GVL explicitly in C extensions). Threads in different ractors can run in parallel. * The overhead of creating a ractor is slightly above the overhead of creating a thread. ### Limited sharing between Ractors diff --git a/ext/json/fbuffer/fbuffer.h b/ext/json/fbuffer/fbuffer.h index 752d153b31d5d5..8ce029a8c49dfd 100644 --- a/ext/json/fbuffer/fbuffer.h +++ b/ext/json/fbuffer/fbuffer.h @@ -161,23 +161,25 @@ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr) static void fbuffer_append_str(FBuffer *fb, VALUE str) { - const char *newstr = StringValuePtr(str); - unsigned long len = RSTRING_LEN(str); + const char *ptr; + unsigned long len; + RSTRING_GETMEM(str, ptr, len); - fbuffer_append(fb, newstr, len); + fbuffer_append(fb, ptr, len); } static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat) { - const char *newstr = StringValuePtr(str); - unsigned long len = RSTRING_LEN(str); + const char *ptr; + unsigned long len; + RSTRING_GETMEM(str, ptr, len); fbuffer_inc_capa(fb, repeat * len); while (repeat) { #if JSON_DEBUG fb->requested = len; #endif - fbuffer_append_reserved(fb, newstr, len); + fbuffer_append_reserved(fb, ptr, len); repeat--; } } diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index dbba99c4558e63..186a45714d48eb 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -74,9 +74,7 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj); static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj); static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj); -#ifdef RUBY_INTEGER_UNIFICATION static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj); -#endif static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj); static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj); static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj); @@ -815,7 +813,6 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) return cState_partial_generate(Vstate, self, generate_json_array, Qfalse); } -#ifdef RUBY_INTEGER_UNIFICATION /* * call-seq: to_json(*) * @@ -828,32 +825,6 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse); } -#else -/* - * call-seq: to_json(*) - * - * Returns a JSON string representation for this Integer number. - */ -static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self) -{ - rb_check_arity(argc, 0, 1); - VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse); -} - -/* - * call-seq: to_json(*) - * - * Returns a JSON string representation for this Integer number. - */ -static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self) -{ - rb_check_arity(argc, 0, 1); - VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil); - return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse); -} -#endif - /* * call-seq: to_json(*) * @@ -1374,10 +1345,9 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { VALUE tmp = rb_funcall(obj, i_to_s, 0); - fbuffer_append_str(buffer, tmp); + fbuffer_append_str(buffer, StringValue(tmp)); } -#ifdef RUBY_INTEGER_UNIFICATION static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { if (FIXNUM_P(obj)) @@ -1385,7 +1355,6 @@ static void generate_json_integer(FBuffer *buffer, struct generate_json_data *da else generate_json_bignum(buffer, data, obj); } -#endif static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj) { @@ -1540,7 +1509,9 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, .obj = obj, .func = func }; - return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data); + VALUE result = rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data); + RB_GC_GUARD(self); + return result; } /* call-seq: @@ -2163,16 +2134,9 @@ void Init_generator(void) VALUE mArray = rb_define_module_under(mGeneratorMethods, "Array"); rb_define_method(mArray, "to_json", mArray_to_json, -1); -#ifdef RUBY_INTEGER_UNIFICATION VALUE mInteger = rb_define_module_under(mGeneratorMethods, "Integer"); rb_define_method(mInteger, "to_json", mInteger_to_json, -1); -#else - VALUE mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum"); - rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1); - VALUE mBignum = rb_define_module_under(mGeneratorMethods, "Bignum"); - rb_define_method(mBignum, "to_json", mBignum_to_json, -1); -#endif VALUE mFloat = rb_define_module_under(mGeneratorMethods, "Float"); rb_define_method(mFloat, "to_json", mFloat_to_json, -1); diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb index 631beba83e91b6..95b88571005b7e 100644 --- a/ext/json/lib/json/version.rb +++ b/ext/json/lib/json/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module JSON - VERSION = '2.18.0' + VERSION = '2.18.1' end diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb index 4c516a9de0acb9..5b685716cc5569 100644 --- a/lib/prism/lex_compat.rb +++ b/lib/prism/lex_compat.rb @@ -758,8 +758,9 @@ def result end end - # Drop the EOF token from the list - tokens = tokens[0...-1] + # Drop the EOF token from the list. The EOF token may not be + # present if the source was syntax invalid + tokens = tokens[0...-1] if tokens.dig(-1, 1) == :on_eof # We sort by location because Ripper.lex sorts. tokens.sort_by! do |token| @@ -804,7 +805,7 @@ def insert_on_sp(tokens, source, data_loc, bom, eof_token) next_whitespace_index += 1 first_whitespace = sp_value[0...continuation_index] continuation = sp_value[continuation_index...next_whitespace_index] - second_whitespace = sp_value[next_whitespace_index..] + second_whitespace = sp_value[next_whitespace_index..] || "" new_tokens << [[sp_line, sp_column], :on_sp, first_whitespace, prev_token_state] unless first_whitespace.empty? new_tokens << [[sp_line, sp_column + continuation_index], :on_sp, continuation, prev_token_state] @@ -819,7 +820,7 @@ def insert_on_sp(tokens, source, data_loc, bom, eof_token) prev_token_end = start_offset + token[2].bytesize end - unless data_loc # no trailing :on_sp with __END__ as it is always preceded by :on_nl + if !data_loc && eof_token # no trailing :on_sp with __END__ as it is always preceded by :on_nl end_offset = eof_token.location.end_offset if prev_token_end < end_offset new_tokens << [ diff --git a/lib/rubygems/vendor/resolv/lib/resolv.rb b/lib/rubygems/vendor/resolv/lib/resolv.rb index 168df21f3ece42..4f48e0642bf48a 100644 --- a/lib/rubygems/vendor/resolv/lib/resolv.rb +++ b/lib/rubygems/vendor/resolv/lib/resolv.rb @@ -4,6 +4,7 @@ require_relative '../../../vendored_timeout' require 'io/wait' require_relative '../../../vendored_securerandom' +require 'rbconfig' # Gem::Resolv is a thread-aware DNS resolver library written in Ruby. Gem::Resolv can # handle multiple DNS requests concurrently without blocking the entire Ruby @@ -33,7 +34,8 @@ class Gem::Resolv - VERSION = "0.6.2" + # The version string + VERSION = "0.7.0" ## # Looks up the first IP address for +name+. @@ -173,21 +175,19 @@ class ResolvError < StandardError; end class ResolvTimeout < Gem::Timeout::Error; end - WINDOWS = /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM || ::RbConfig::CONFIG['host_os'] =~ /mswin/ - private_constant :WINDOWS - ## # Gem::Resolv::Hosts is a hostname resolver that uses the system hosts file. class Hosts - if WINDOWS + if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM || ::RbConfig::CONFIG['host_os'] =~ /mswin/ begin require 'win32/resolv' unless defined?(Win32::Resolv) - DefaultFileName = Win32::Resolv.get_hosts_path || IO::NULL + hosts = Win32::Resolv.get_hosts_path || IO::NULL rescue LoadError end end - DefaultFileName ||= '/etc/hosts' + # The default file name for host names + DefaultFileName = hosts || '/etc/hosts' ## # Creates a new Gem::Resolv::Hosts, using +filename+ for its data source. @@ -525,6 +525,8 @@ def each_resource(name, typeclass, &proc) } end + # :stopdoc: + def fetch_resource(name, typeclass) lazy_initialize truncated = {} @@ -1021,8 +1023,7 @@ def Config.parse_resolv_conf(filename) def Config.default_config_hash(filename="/etc/resolv.conf") if File.exist? filename Config.parse_resolv_conf(filename) - elsif WINDOWS - require 'win32/resolv' unless defined?(Win32::Resolv) + elsif defined?(Win32::Resolv) search, nameserver = Win32::Resolv.get_resolv_info config_hash = {} config_hash[:nameserver] = nameserver if nameserver @@ -2926,15 +2927,21 @@ class HTTPS < ServiceBinding class IPv4 - ## - # Regular expression IPv4 addresses must match. - Regex256 = /0 |1(?:[0-9][0-9]?)? |2(?:[0-4][0-9]?|5[0-5]?|[6-9])? - |[3-9][0-9]?/x + |[3-9][0-9]?/x # :nodoc: + + ## + # Regular expression IPv4 addresses must match. Regex = /\A(#{Regex256})\.(#{Regex256})\.(#{Regex256})\.(#{Regex256})\z/ + ## + # Creates a new IPv4 address from +arg+ which may be: + # + # IPv4:: returns +arg+. + # String:: +arg+ must match the IPv4::Regex constant + def self.create(arg) case arg when IPv4 @@ -3243,13 +3250,15 @@ def make_udp_requester # :nodoc: end - module LOC + module LOC # :nodoc: ## # A Gem::Resolv::LOC::Size class Size + # Regular expression LOC size must match. + Regex = /^(\d+\.*\d*)[m]$/ ## @@ -3275,6 +3284,7 @@ def self.create(arg) end end + # Internal use; use self.create. def initialize(scalar) @scalar = scalar end @@ -3312,6 +3322,8 @@ def hash # :nodoc: class Coord + # Regular expression LOC Coord must match. + Regex = /^(\d+)\s(\d+)\s(\d+\.\d+)\s([NESW])$/ ## @@ -3341,6 +3353,7 @@ def self.create(arg) end end + # Internal use; use self.create. def initialize(coordinates,orientation) unless coordinates.kind_of?(String) raise ArgumentError.new("Coord must be a 32bit unsigned integer in hex format: #{coordinates.inspect}") @@ -3403,6 +3416,8 @@ def hash # :nodoc: class Alt + # Regular expression LOC Alt must match. + Regex = /^([+-]*\d+\.*\d*)[m]$/ ## @@ -3428,6 +3443,7 @@ def self.create(arg) end end + # Internal use; use self.create. def initialize(altitude) @altitude = altitude end diff --git a/test/objspace/test_ractor.rb b/test/objspace/test_ractor.rb index 996d83fbd214dd..0fe4c32f2f0741 100644 --- a/test/objspace/test_ractor.rb +++ b/test/objspace/test_ractor.rb @@ -14,7 +14,7 @@ def test_tracing_does_not_crash end def test_undefine_finalizer - assert_ractor(<<~'RUBY', require: 'objspace') + assert_ractor(<<~'RUBY', require: 'objspace', signal: :SEGV) def fin ->(id) { } end diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 52a5ad7ef4e2f9..758505ac2ab96d 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -140,6 +140,17 @@ def test_lexer assert_raise(SyntaxError) { Translation::Ripper::Lexer.new("1 +").lex(raise_errors: true) } end + + # On syntax invalid code the output doesn't always match up + # In these cases we just want to make sure that it doesn't raise. + def test_lex_invalid_syntax + assert_nothing_raised do + Translation::Ripper.lex('scan/\p{alpha}/') + end + + assert_equal(Ripper.lex('if;)'), Translation::Ripper.lex('if;)')) + end + def test_tokenize source = "foo;1;BAZ" assert_equal(Ripper.tokenize(source), Translation::Ripper.tokenize(source)) diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb index 7a01909eeb9a4b..69a0e03ec9c716 100644 --- a/test/resolv/test_dns.rb +++ b/test/resolv/test_dns.rb @@ -721,13 +721,14 @@ def test_multiple_servers_with_timeout_and_truncated_tcp_fallback client_thread = Thread.new do Resolv::DNS.open(nameserver_port: [[server1_address, server1_port], [server2_address, server2_port]]) do |dns| - dns.timeouts = [0.1, 0.2] + dns.timeouts = [EnvUtil.apply_timeout_scale(0.1), + EnvUtil.apply_timeout_scale(0.2)] dns.getresources('foo.example.org', Resolv::DNS::Resource::IN::A) end end udp_server1_thread = Thread.new do - msg, (_, client_port, _, client_address) = Timeout.timeout(5) { u1.recvfrom(4096) } + msg, (_, client_port, _, client_address) = Timeout.timeout(EnvUtil.apply_timeout_scale(5)) { u1.recvfrom(4096) } id, word2, _qdcount, _ancount, _nscount, _arcount = msg.unpack('nnnnnn') opcode = (word2 & 0x7800) >> 11 rd = (word2 & 0x0100) >> 8 diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index 8f12e06685bb1f..cc2e1ab2324e33 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -919,7 +919,7 @@ class T end def test_safe_multi_ractor_subclasses_list_mutation - assert_ractor "#{<<~"begin;"}\n#{<<~'end;'}" + assert_ractor "#{<<~"begin;"}\n#{<<~'end;'}", signal: :SEGV begin; 4.times.map do Ractor.new do diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index b3a88b664cc09d..56a3c3d18f7156 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1568,7 +1568,7 @@ def test_waitall def test_wait_exception bug11340 = '[ruby-dev:49176] [Bug #11340]' t0 = t1 = nil - sec = 3 + sec = EnvUtil.apply_timeout_scale(3) code = "puts;STDOUT.flush;Thread.start{gets;exit};sleep(#{sec})" IO.popen([RUBY, '-e', code], 'r+') do |f| pid = f.pid diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index 209e55294b1889..f4fe2fc44b5b61 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -2255,7 +2255,7 @@ def foo def test_refining_module_repeatedly bug14070 = '[ruby-core:83617] [Bug #14070]' - assert_in_out_err([], <<-INPUT, ["ok"], [], bug14070) + assert_in_out_err([], <<-INPUT, ["ok"], [], bug14070, timeout: 30) 1000.times do Class.new do include Enumerable diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index d3b2441e21e7c3..8b0e08fc97d2a7 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -2583,6 +2583,7 @@ def test_script_compiled def test_enable_target_thread events = [] TracePoint.new(:line) do |tp| + next unless tp.path == __FILE__ events << Thread.current end.enable(target_thread: Thread.current) do _a = 1 @@ -2596,6 +2597,7 @@ def test_enable_target_thread events = [] tp = TracePoint.new(:line) do |tp| + next unless tp.path == __FILE__ events << Thread.current end diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index e29a0e3c25a0e3..5ca318a5989753 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -911,10 +911,11 @@ def assert_linear_performance(seq, rehearsal: nil, pre: ->(n) {n}) first = seq.first *arg = pre.call(first) - times = (0..(rehearsal || (2 * first))).map do + raw_times = (0..(rehearsal || (2 * first))).map do measure[arg, "rehearsal"].nonzero? end - times.compact! + times = raw_times.compact + raise "all measurements are zero: #{raw_times.inspect}" if times.empty? tmin, tmax = times.minmax # safe_factor * tmax * rehearsal_time_variance_factor(equals to 1 when variance is small)