Skip to content
Merged
12 changes: 11 additions & 1 deletion gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,12 @@ rb_newobj(rb_execution_context_t *ec, VALUE klass, VALUE flags, shape_id_t shape
GC_ASSERT((flags & FL_WB_PROTECTED) == 0);
rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size);

#if RACTOR_CHECK_MODE
void rb_ractor_setup_belonging(VALUE obj);
rb_ractor_setup_belonging(obj);
#endif

RBASIC_SET_SHAPE_ID_NO_CHECKS(obj, shape_id);

gc_validate_pc(obj);
Expand Down Expand Up @@ -1392,6 +1398,7 @@ rb_gc_obj_needs_cleanup_p(VALUE obj)
case T_FLOAT:
case T_RATIONAL:
case T_COMPLEX:
case T_MATCH:
break;

case T_FILE:
Expand All @@ -1400,7 +1407,6 @@ rb_gc_obj_needs_cleanup_p(VALUE obj)
case T_ICLASS:
case T_MODULE:
case T_REGEXP:
case T_MATCH:
return true;
}

Expand Down Expand Up @@ -1436,6 +1442,10 @@ rb_gc_obj_needs_cleanup_p(VALUE obj)
if (flags & RHASH_ST_TABLE_FLAG) return true;
return rb_shape_has_fields(shape_id);

case T_MATCH:
if ((flags & (RMATCH_ONIG | RMATCH_OFFSETS_EXTERNAL)) || USE_DEBUG_COUNTER) return true;
return rb_shape_has_fields(shape_id);

case T_BIGNUM:
if (!(flags & BIGNUM_EMBED_FLAG)) return true;
return rb_shape_has_fields(shape_id);
Expand Down
6 changes: 0 additions & 6 deletions gc/default/default.c
Original file line number Diff line number Diff line change
Expand Up @@ -2222,12 +2222,6 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
RBASIC(obj)->shape_id = 0;
#endif


#if RACTOR_CHECK_MODE
void rb_ractor_setup_belonging(VALUE obj);
rb_ractor_setup_belonging(obj);
#endif

#if RGENGC_CHECK_MODE
int lev = RB_GC_VM_LOCK_NO_BARRIER();
{
Expand Down
6 changes: 0 additions & 6 deletions gc/mmtk/mmtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

#if RACTOR_CHECK_MODE
# define RVALUE_SUFFIX_SIZE sizeof(VALUE)
void rb_ractor_setup_belonging(VALUE obj);
#else
# define RVALUE_SUFFIX_SIZE 0
#endif
Expand Down Expand Up @@ -576,7 +575,6 @@ rb_gc_impl_objspace_alloc(void)
{
MMTk_Builder *builder = rb_mmtk_builder_init();
MMTk_RubyBindingOptions binding_options = {
.ractor_check_mode = RACTOR_CHECK_MODE != 0,
.suffix_size = RVALUE_SUFFIX_SIZE,
};
mmtk_init_binding(builder, &binding_options, &ruby_upcalls);
Expand Down Expand Up @@ -930,10 +928,6 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags

objspace->total_allocated_objects++;

#if RACTOR_CHECK_MODE
rb_ractor_setup_belonging((VALUE)alloc_obj);
#endif

return (VALUE)alloc_obj;
}

Expand Down
1 change: 0 additions & 1 deletion gc/mmtk/mmtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ typedef struct MMTk_BumpPointer {
#define MMTk_GC_THREAD_KIND_WORKER 1

typedef struct MMTk_RubyBindingOptions {
bool ractor_check_mode;
size_t suffix_size;
} MMTk_RubyBindingOptions;

Expand Down
1 change: 0 additions & 1 deletion gc/mmtk/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@ impl From<Vec<ObjectReference>> for RawVecOfObjRef {
#[repr(C)]
#[derive(Clone)]
pub struct RubyBindingOptions {
pub ractor_check_mode: bool,
pub suffix_size: usize,
}

Expand Down
1 change: 1 addition & 0 deletions internal/re.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "ruby/re.h" /* for struct RMatch and struct re_registers */

#define RMATCH_ONIG FL_USER1
#define RMATCH_OFFSETS_EXTERNAL FL_USER2

static inline OnigPosition *
RMATCH_BEG_PTR(VALUE match)
Expand Down
3 changes: 2 additions & 1 deletion lib/rubygems/ext/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ def self.run(command, results, command_name = nil, dir = Dir.pwd, env = {})
# Set $SOURCE_DATE_EPOCH for the subprocess.
build_env = { "SOURCE_DATE_EPOCH" => Gem.source_date_epoch_string }.merge(env)
output, status = begin
Open3.popen2e(build_env, *command, chdir: dir) do |_stdin, stdouterr, wait_thread|
Open3.popen2e(build_env, *command, chdir: dir) do |stdin, stdouterr, wait_thread|
stdin.close
output = String.new
while line = stdouterr.gets
output << line
Expand Down
14 changes: 7 additions & 7 deletions pathname.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,16 @@ get_strpath(VALUE obj)
*
* Examples:
*
* Pathname.new('a') <=> Pathname.new('b') # => -1
* Pathname.new('a') <=> Pathname.new('ab') # => -1
* Pathname.new('a') <=> Pathname.new('a') # => 0
* Pathname.new('b') <=> Pathname.new('a') # => 1
* Pathname.new('ab') <=> Pathname.new('a') # => 1
* Pathname.new('ab') <=> 'a' # => nil
* Pathname('a') <=> Pathname('b') # => -1
* Pathname('a') <=> Pathname('ab') # => -1
* Pathname('a') <=> Pathname('a') # => 0
* Pathname('b') <=> Pathname('a') # => 1
* Pathname('ab') <=> Pathname('a') # => 1
* Pathname('ab') <=> 'a' # => nil
*
* Two pathnames that are different may refer to the same entry in the filesystem:
*
* Pathname.new('lib') <=> Pathname.new('./lib') # => 1
* Pathname('lib') <=> Pathname('./lib') # => 1
*
*/
static VALUE
Expand Down
108 changes: 90 additions & 18 deletions pathname_builtin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ def descend
# yields +self+, then a new pathname for each successive dirname in the stored path;
# see File.dirname:
#
# Pathname.new('/path/to/some/file.rb').ascend {|dirname| p dirname}
# Pathname('/path/to/some/file.rb').ascend {|dirname| p dirname}
# #<Pathname:/path/to/some/file.rb>
# #<Pathname:/path/to/some>
# #<Pathname:/path/to>
Expand Down Expand Up @@ -1001,23 +1001,41 @@ def binwrite(...) File.binwrite(@path, ...) end
# Returns a new Time object containing the time of the most recent
# access (read or write) to the entry represented by +self+:
#
# filepath = 't.tmp'
# pn = Pathname.new(filepath)
# File.exist?(filepath) # => false
# pn.atime # Raises Errno::ENOENT: No such file or directory
# File.write(filepath, 'foo')
# pn.atime # => 2026-03-22 13:49:44.5165608 -0500
# File.read(filepath)
# pn.atime # => 2026-03-22 13:49:57.5359349 -0500
# File.delete(filepath)
# # Work in a temporary directory.
# require 'tmpdir'
# Dir.mktmpdir do |tmpdirpath|
# # A subdirectory therein, and its Pathname.
# dirpath = File.join(tmpdirpath, 'subdir')
# Dir.mkdir(dirpath)
# dir_pn = Pathname(dirpath)
# puts "Create directory; establishes atime for directory."
# puts " Directory atime: #{dir_pn.atime}"
# sleep(1)
#
# dirpath = 'tmp'
# Dir.mkdir(dirpath)
# pn = Pathname.new(dirpath)
# pn.atime # => 2026-03-31 11:46:35.4813492 -0500
# Dir.empty?(dirname) # => true
# pn.atime # => 2026-03-31 11:51:10.1210092 -0500
# Dir.delete(dirpath)
# # A file in the subdirectory, and its Pathname.
# filepath = File.join(dirpath, 't.txt')
# puts "Create file; establishes atime for file, updates atime for directory."
# File.write(filepath, 'foo')
# file_pn = Pathname(filepath)
# puts " File atime: #{file_pn.atime}"
# puts " Directory atime: #{dir_pn.atime}"
# sleep(1)
# puts "Write file; updates atimes for file and directory."
# File.write(filepath, 'bar')
# puts " File atime: #{file_pn.atime}"
# puts " Directory atime: #{dir_pn.atime}"
# end
#
# Output:
#
# Create directory; establishes atime for directory.
# Directory atime: 2026-05-14 14:36:43 +0100
# Create file; establishes atime for file, updates atime for directory.
# File atime: 2026-05-14 14:36:44 +0100
# Directory atime: 2026-05-14 14:36:44 +0100
# Write file; updates atimes for file and directory.
# File atime: 2026-05-14 14:36:45 +0100
# Directory atime: 2026-05-14 14:36:45 +0100
#
# See {File System Timestamps}[rdoc-ref:file/timestamps.md].
def atime() File.atime(@path) end
Expand Down Expand Up @@ -1098,7 +1116,61 @@ def chmod(mode) File.chmod(mode, @path) end
# See <tt>File.lchmod</tt>.
def lchmod(mode) File.lchmod(mode, @path) end

# See <tt>File.chown</tt>. Change owner and group of file.
# call-seq:
# chown(owner_id, group_id) -> 0
#
# Changes the owner and group of an entry (directory or file):
#
# # Work in a temporary directory.
# require 'tmpdir'
# Dir.mktmpdir do |tmpdirpath|
# # A subdirectory therein, and its Pathname.
# dirpath = File.join(tmpdirpath, 'subdir')
# Dir.mkdir(dirpath)
# dir_stat = File.stat(dirpath)
# puts "Original directory owner: #{dir_stat.uid}"
# puts "Original directory group: #{dir_stat.gid}"
# dir_pn = Pathname(dirpath)
# dir_pn.chown(1000, 1000)
# dir_stat = File.stat(dirpath)
# puts "New directory owner: #{dir_stat.uid}"
# puts "New directory group: #{dir_stat.gid}"
#
# # A file in the subdirectory, and its Pathname.
# filepath = File.join(dirpath, 't.txt')
# file_pn = Pathname(filepath)
# # Create the file.
# File.write(filepath, 'foo')
# file_stat = File.stat(filepath)
# puts "Original file owner: #{file_stat.uid}"
# puts "Original file group: #{file_stat.gid}"
# file_pn = Pathname(dirpath)
# file_pn.chown(1000, 1000)
# file_stat = File.stat(dirpath)
# puts "New file owner: #{file_stat.uid}"
# puts "New file group: #{file_stat.gid}"
# end
#
# Output:
#
# Original directory owner: 0
# Original directory group: 0
# New directory owner: 1000
# New directory group: 1000
# Original file owner: 0
# Original file group: 0
# New file owner: 1000
# New file group: 1000
#
# Notes:
#
# - On Windows, the owner and group are not changed.
# - Only a process with superuser privileges can change the owner of an entry.
# - The owner of an entry can change its group to any group
# to which the owner belongs.
# - A +nil+ or +-1+ owner or group id is ignored.
# - The method follows symbolic links to the target entry.
#
def chown(owner, group) File.chown(owner, group, @path) end

# See <tt>File.lchown</tt>.
Expand Down
2 changes: 2 additions & 0 deletions re.c
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,7 @@ update_char_offset(VALUE match)
if (rm->char_offset_num_allocated < num_regs) {
SIZED_REALLOC_N(rm->char_offset, struct rmatch_offset, num_regs, rm->char_offset_num_allocated);
rm->char_offset_num_allocated = num_regs;
FL_SET_RAW(match, RMATCH_OFFSETS_EXTERNAL);
}

enc = rb_enc_get(RMATCH(match)->str);
Expand Down Expand Up @@ -1159,6 +1160,7 @@ match_init_copy(VALUE obj, VALUE orig)
if (rm->char_offset_num_allocated < rm->num_regs) {
SIZED_REALLOC_N(rm->char_offset, struct rmatch_offset, rm->num_regs, rm->char_offset_num_allocated);
rm->char_offset_num_allocated = rm->num_regs;
FL_SET_RAW(obj, RMATCH_OFFSETS_EXTERNAL);
}
MEMCPY(rm->char_offset, RMATCH(orig)->char_offset,
struct rmatch_offset, rm->num_regs);
Expand Down
16 changes: 16 additions & 0 deletions test/rubygems/test_gem_ext_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,22 @@ def test_custom_make_with_options
assert_match(/install: OK/, results)
end

def test_class_run_closes_stdin
results = []
check_stdin_script = <<~'RUBY'
if IO.select([STDIN], nil, nil, 1)
puts "STDIN: #{STDIN.read.inspect}"
else
puts "NOT_READY"
end
RUBY

Gem::Ext::Builder.run([Gem.ruby, "-e", check_stdin_script], results)

command_output = results.last
assert_equal "STDIN: \"\"\n", command_output
end

def test_build_extensions
pend "terminates on mswin" if vc_windows? && ruby_repo?

Expand Down
2 changes: 1 addition & 1 deletion zjit/src/backend/arm64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ impl Assembler {
// Convert MemBase::Stack to MemBase::Reg(NATIVE_BASE_PTR) with the
// correct stack displacement. The stack slot value lives directly at
// [NATIVE_BASE_PTR + stack_disp], so we just adjust the base and
// combine displacements no indirection needed. Large
// combine displacements -- no indirection needed. Large
// displacements are handled by split_stack_membase().
let Mem { base, disp: stack_disp, .. } = stack_state.stack_membase_to_mem(stack_membase);
Opnd::Mem(Mem { base, disp: stack_disp + opnd_disp, num_bits: opnd_num_bits })
Expand Down
Loading