Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
68cb8d8
[ruby/rubygems] Check happy path first when comparing gem version:
Edouard-chin Mar 21, 2026
109a20a
Don't pass singleton to TypedData_Make_Struct
jhawthorn Mar 21, 2026
a058331
Don't allow RCLASS_ALLOCATOR on a singleton
jhawthorn Mar 21, 2026
111215d
Copy allocator to subclasses at boot and on change
jhawthorn Mar 21, 2026
cfaef1e
ZJIT: Recompile ISEQs with no-profile sends via exit profiling
k0kubun Mar 24, 2026
7dff7b3
ZJIT: Unify invalidation logic into invalidate_iseq_version()
k0kubun Mar 25, 2026
75a396c
ZJIT: Count profiles remaining down instead of up
k0kubun Mar 25, 2026
532a426
ZJIT: Add TODO for handle_event state machine on invalidate_iseq_version
k0kubun Mar 25, 2026
e756863
ZJIT: Document that SideExitRecompile::argc does not include receiver
k0kubun Mar 25, 2026
15dcd9c
Rescue exceptions from Tempfile#closed? in LeakChecker (#16549)
k0kubun Mar 25, 2026
600fea4
[ruby/rubygems] Cache package version selection:
Edouard-chin Mar 18, 2026
84d71b8
ZJIT: Use 2-space Ruby indentation consistently in codegen_tests.rs
k0kubun Mar 25, 2026
d8353e1
ZJIT: Add assert_compiles for codegen tests to assert successful comp…
k0kubun Mar 25, 2026
c351ae7
[ruby/rubygems] Fallback to copy symlinks on Windows
larskanis Mar 25, 2026
62a21cc
ZJIT: Avoid allocating Ruby strings during compilation (#16550)
k0kubun Mar 25, 2026
b78a84e
Revert "Skip initializing optional arguments to `nil`" and add test
XrXr Mar 25, 2026
166f434
ZJIT: Support opt_newarray_send with min (#16547)
tekknolagi Mar 25, 2026
eb80511
test/socket: rescue Errno::ENOBUFS in test_udp_server (#16553)
k0kubun Mar 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bootstraptest/test_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1434,3 +1434,9 @@ def x = [1]
def forwarder(...) = target(*x, 2, ...)
forwarder(3).inspect
}, '[Bug #21832] post-splat args before forwarding'

assert_equal '[nil, nil]', %q{
def self_reading(a = a, kw:) = a
def through_binding(a = binding.local_variable_get(:a), kw:) = a
[self_reading(kw: 1), through_binding(kw: 1)]
}, 'nil initialization of optional parameters'
3 changes: 3 additions & 0 deletions class.c
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,7 @@ class_boot_boxable(VALUE super, bool boxable)

class_associate_super(klass, super, true);
if (super && !UNDEF_P(super)) {
RCLASS_SET_ALLOCATOR(klass, RCLASS_ALLOCATOR(super));
rb_class_set_initialized(klass);
}

Expand Down Expand Up @@ -1428,6 +1429,8 @@ void
Init_class_hierarchy(void)
{
rb_cBasicObject = boot_defclass("BasicObject", 0);
RCLASS_SET_ALLOCATOR(rb_cBasicObject, rb_class_allocate_instance);
FL_SET_RAW(rb_cBasicObject, RCLASS_ALLOCATOR_DEFINED);
rb_cObject = boot_defclass("Object", rb_cBasicObject);
rb_vm_register_global_object(rb_cObject);

Expand Down
1 change: 1 addition & 0 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,7 @@ rb_gc_register_pinning_obj(VALUE obj)
static inline void
rb_data_object_check(VALUE klass)
{
RUBY_ASSERT(!RCLASS_SINGLETON_P(klass));
if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
rb_undef_alloc_func(klass);
rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass);
Expand Down
5 changes: 2 additions & 3 deletions internal/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ static inline void RCLASS_WRITE_CLASSPATH(VALUE klass, VALUE classpath, bool per
#define RCLASS_IS_INITIALIZED FL_USER3
// 3 is RMODULE_IS_REFINEMENT for RMODULE
#define RCLASS_BOXABLE FL_USER4
#define RCLASS_ALLOCATOR_DEFINED FL_USER5

static inline st_table *
RCLASS_CLASSEXT_TBL(VALUE klass)
Expand Down Expand Up @@ -619,9 +620,7 @@ static inline rb_alloc_func_t
RCLASS_ALLOCATOR(VALUE klass)
{
RBIMPL_ASSERT_TYPE(klass, T_CLASS);
if (RCLASS_SINGLETON_P(klass)) {
return 0;
}
RUBY_ASSERT(!RCLASS_SINGLETON_P(klass));
return RCLASS_EXT_PRIME(klass)->as.class.allocator;
}

Expand Down
9 changes: 6 additions & 3 deletions lib/bundler/resolver/strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Resolver
class Strategy
def initialize(source)
@source = source
@package_priority_cache = {}
end

def next_package_and_version(unsatisfied)
Expand All @@ -17,10 +18,12 @@ def next_package_and_version(unsatisfied)

def next_term_to_try_from(unsatisfied)
unsatisfied.min_by do |package, range|
matching_versions = @source.versions_for(package, range)
higher_versions = @source.versions_for(package, range.upper_invert)
@package_priority_cache[[package, range]] ||= begin
matching_versions = @source.versions_for(package, range)
higher_versions = @source.versions_for(package, range.upper_invert)

[matching_versions.count <= 1 ? 0 : 1, higher_versions.count]
[matching_versions.count <= 1 ? 0 : 1, higher_versions.count]
end
end
end

Expand Down
17 changes: 16 additions & 1 deletion lib/rubygems/package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:

symlinks.each do |name, target, destination, real_destination|
if File.exist?(real_destination)
File.symlink(target, destination)
create_symlink(target, destination)
else
alert_warning "#{@spec.full_name} ships with a dangling symlink named #{name} pointing to missing #{target} file. Ignoring"
end
Expand Down Expand Up @@ -725,6 +725,21 @@ def limit_read(io, name, limit)
raise Gem::Package::FormatError, "#{name} is too big (over #{limit} bytes)" if bytes.size > limit
bytes
end

if Gem.win_platform?
# Create a symlink and fallback to copy the file or directory on Windows,
# where symlink creation needs special privileges in form of the Developer Mode.
def create_symlink(old_name, new_name)
File.symlink(old_name, new_name)
rescue Errno::EACCES
from = File.expand_path(old_name, File.dirname(new_name))
FileUtils.cp_r(from, new_name)
end
else
def create_symlink(old_name, new_name)
File.symlink(old_name, new_name)
end
end
end

require_relative "package/digest_io"
Expand Down
84 changes: 41 additions & 43 deletions lib/rubygems/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -345,60 +345,58 @@ def approximate_recommendation
# other types may raise an exception.

def <=>(other)
if String === other
return unless self.class.correct?(other)
return self <=> self.class.new(other)
end

return unless Gem::Version === other

# Fast path for comparison when available.
if @sort_key && other.sort_key
return @sort_key <=> other.sort_key
end

return 0 if @version == other.version || canonical_segments == other.canonical_segments
if Gem::Version === other
# Fast path for comparison when available.
if @sort_key && other.sort_key
return @sort_key <=> other.sort_key
end

lhsegments = canonical_segments
rhsegments = other.canonical_segments
return 0 if @version == other.version || canonical_segments == other.canonical_segments

lhsize = lhsegments.size
rhsize = rhsegments.size
limit = (lhsize > rhsize ? rhsize : lhsize)
lhsegments = canonical_segments
rhsegments = other.canonical_segments

i = 0
lhsize = lhsegments.size
rhsize = rhsegments.size
limit = (lhsize > rhsize ? rhsize : lhsize)

while i < limit
lhs = lhsegments[i]
rhs = rhsegments[i]
i += 1
i = 0

next if lhs == rhs
return -1 if String === lhs && Numeric === rhs
return 1 if Numeric === lhs && String === rhs
while i < limit
lhs = lhsegments[i]
rhs = rhsegments[i]
i += 1

return lhs <=> rhs
end
next if lhs == rhs
return -1 if String === lhs && Numeric === rhs
return 1 if Numeric === lhs && String === rhs

lhs = lhsegments[i]
return lhs <=> rhs
end

if lhs.nil?
rhs = rhsegments[i]
lhs = lhsegments[i]

while i < rhsize
return 1 if String === rhs
return -1 unless rhs.zero?
rhs = rhsegments[i += 1]
end
else
while i < lhsize
return -1 if String === lhs
return 1 unless lhs.zero?
lhs = lhsegments[i += 1]
if lhs.nil?
rhs = rhsegments[i]

while i < rhsize
return 1 if String === rhs
return -1 unless rhs.zero?
rhs = rhsegments[i += 1]
end
else
while i < lhsize
return -1 if String === lhs
return 1 unless lhs.zero?
lhs = lhsegments[i += 1]
end
end
end

0
0
elsif String === other
return unless self.class.correct?(other)
self <=> self.class.new(other)
end
end

# remove trailing zeros segments before first letter or at the end of the version
Expand Down
5 changes: 3 additions & 2 deletions marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -947,8 +947,9 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
hasiv = has_ivars(obj, (encname = encoding_name(obj, arg)), &ivobj);
{
st_data_t compat_data;
rb_alloc_func_t allocator = rb_get_alloc_func(RBASIC(obj)->klass);
if (st_lookup(compat_allocator_tbl,
VALUE klass = CLASS_OF(obj);
rb_alloc_func_t allocator = RCLASS_SINGLETON_P(klass) ? 0 : rb_get_alloc_func(klass);
if (allocator && st_lookup(compat_allocator_tbl,
(st_data_t)allocator,
&compat_data)) {
marshal_compat_t *compat = (marshal_compat_t*)compat_data;
Expand Down
2 changes: 1 addition & 1 deletion object.c
Original file line number Diff line number Diff line change
Expand Up @@ -2253,6 +2253,7 @@ rb_class_initialize(int argc, VALUE *argv, VALUE klass)
}
}
rb_class_set_super(klass, super);
RCLASS_SET_ALLOCATOR(klass, RCLASS_ALLOCATOR(super));
rb_make_metaclass(klass, RBASIC(super)->klass);
rb_class_inherited(super, klass);
rb_mod_initialize_exec(klass);
Expand Down Expand Up @@ -4448,7 +4449,6 @@ InitVM_Object(void)
#endif

rb_define_private_method(rb_cBasicObject, "initialize", rb_obj_initialize, 0);
rb_define_alloc_func(rb_cBasicObject, rb_class_allocate_instance);
rb_define_method(rb_cBasicObject, "==", rb_obj_equal, 1);
rb_define_method(rb_cBasicObject, "equal?", rb_obj_equal, 1);
rb_define_method(rb_cBasicObject, "!", rb_obj_not, 0);
Expand Down
4 changes: 2 additions & 2 deletions proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2626,7 +2626,7 @@ method_clone(VALUE self)
struct METHOD *orig, *data;

TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
clone = TypedData_Make_Struct(rb_obj_class(self), struct METHOD, &method_data_type, data);
rb_obj_clone_setup(self, clone, Qnil);
RB_OBJ_WRITE(clone, &data->recv, orig->recv);
RB_OBJ_WRITE(clone, &data->klass, orig->klass);
Expand All @@ -2644,7 +2644,7 @@ method_dup(VALUE self)
struct METHOD *orig, *data;

TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
clone = TypedData_Make_Struct(rb_obj_class(self), struct METHOD, &method_data_type, data);
rb_obj_dup_setup(self, clone);
RB_OBJ_WRITE(clone, &data->recv, orig->recv);
RB_OBJ_WRITE(clone, &data->klass, orig->klass);
Expand Down
14 changes: 14 additions & 0 deletions test/ruby/test_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,20 @@ def m.bar; :bar; end
end
end

def test_clone_preserves_singleton_methods
m = method(:itself)
m.define_singleton_method(:foo) { :bar }
assert_equal(:bar, m.foo)
assert_equal(:bar, m.clone.foo)
end

def test_dup_does_not_preserve_singleton_methods
m = method(:itself)
m.define_singleton_method(:foo) { :bar }
assert_equal(:bar, m.foo)
assert_raise(NoMethodError) { m.dup.foo }
end

def test_inspect
o = Object.new
def o.foo; end; line_no = __LINE__
Expand Down
18 changes: 18 additions & 0 deletions test/rubygems/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,24 @@ def nmake_found?
system("nmake /? 1>NUL 2>&1")
end

@@symlink_supported = nil

# This is needed for Windows environment without symlink support enabled (the default
# for non admin) to be able to skip test for features using symlinks.
def symlink_supported?
if @@symlink_supported.nil?
begin
File.symlink(File.join(@tempdir, "a"), File.join(@tempdir, "b"))
rescue NotImplementedError, SystemCallError
@@symlink_supported = false
else
File.unlink(File.join(@tempdir, "b"))
@@symlink_supported = true
end
end
@@symlink_supported
end

# In case we're building docs in a background process, this method waits for
# that process to exit (or if it's already been reaped, or never happened,
# swallows the Errno::ECHILD error).
Expand Down
17 changes: 0 additions & 17 deletions test/rubygems/installer_test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -237,21 +237,4 @@ def test_ensure_writable_dir_creates_missing_parent_directories
assert_directory_exists non_existent_parent, "Parent directory should exist now"
assert_directory_exists target_dir, "Target directory should exist now"
end

@@symlink_supported = nil

# This is needed for Windows environment without symlink support enabled (the default
# for non admin) to be able to skip test for features using symlinks.
def symlink_supported?
if @@symlink_supported.nil?
begin
File.symlink("", "")
rescue Errno::ENOENT, Errno::EEXIST
@@symlink_supported = true
rescue NotImplementedError, SystemCallError
@@symlink_supported = false
end
end
@@symlink_supported
end
end
8 changes: 6 additions & 2 deletions test/rubygems/test_gem_installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,12 @@ def test_generate_bin_with_dangling_symlink

errors = @ui.error.split("\n")
assert_equal "WARNING: ascii_binder-0.1.10.1 ships with a dangling symlink named bin/ascii_binder pointing to missing bin/asciibinder file. Ignoring", errors.shift
assert_empty errors

if symlink_supported?
assert_empty errors
else
assert_match(/Unable to use symlinks, installing wrapper/i,
errors.to_s)
end
assert_empty @ui.output
end

Expand Down
Loading