Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .github/workflows/modgc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ jobs:
${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE}

- uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1
with:
cache-bin: false
- name: Set MMTk environment variables
run: |
echo 'EXCLUDES=../src/test/.excludes-mmtk' >> $GITHUB_ENV
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ Note that each entry is kept to a minimum, see links for details.

## Language changes

* `Module#clone` and `Module#dup` no longer rewrite the lexical scope of
copied methods. Constants and class variables resolve through the
original class, consistent with inheritance and mixins.
[[Feature #21981]]

## Core classes updates

Note: We're only listing outstanding class updates.
Expand Down Expand Up @@ -170,6 +175,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[Feature #21853]: https://bugs.ruby-lang.org/issues/21853
[Feature #21861]: https://bugs.ruby-lang.org/issues/21861
[Feature #21932]: https://bugs.ruby-lang.org/issues/21932
[Feature #21981]: https://bugs.ruby-lang.org/issues/21981
[RubyGems-v4.0.4]: https://github.com/rubygems/rubygems/releases/tag/v4.0.4
[RubyGems-v4.0.5]: https://github.com/rubygems/rubygems/releases/tag/v4.0.5
[RubyGems-v4.0.6]: https://github.com/rubygems/rubygems/releases/tag/v4.0.6
Expand Down
23 changes: 3 additions & 20 deletions class.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_box_t *bo
* * variation count
*/
RCLASSEXT_PERMANENT_CLASSPATH(ext) = RCLASSEXT_PERMANENT_CLASSPATH(orig);
RCLASSEXT_CLONED(ext) = RCLASSEXT_CLONED(orig);
RCLASSEXT_CLASSPATH(ext) = RCLASSEXT_CLASSPATH(orig);

/* For the usual T_CLASS/T_MODULE, iclass flags are always false */
Expand Down Expand Up @@ -880,27 +879,20 @@ rb_class_s_alloc(VALUE klass)
}

static void
clone_method(VALUE old_klass, VALUE new_klass, ID mid, const rb_method_entry_t *me)
clone_method(VALUE new_klass, ID mid, const rb_method_entry_t *me)
{
if (me->def->type == VM_METHOD_TYPE_ISEQ) {
rb_cref_t *new_cref = rb_vm_rewrite_cref(me->def->body.iseq.cref, old_klass, new_klass);
rb_add_method_iseq(new_klass, mid, me->def->body.iseq.iseqptr, new_cref, METHOD_ENTRY_VISI(me));
}
else {
rb_method_entry_set(new_klass, mid, me, METHOD_ENTRY_VISI(me));
}
rb_method_entry_set(new_klass, mid, me, METHOD_ENTRY_VISI(me));
}

struct clone_method_arg {
VALUE new_klass;
VALUE old_klass;
};

static enum rb_id_table_iterator_result
clone_method_i(ID key, VALUE value, void *data)
{
const struct clone_method_arg *arg = (struct clone_method_arg *)data;
clone_method(arg->old_klass, arg->new_klass, key, (const rb_method_entry_t *)value);
clone_method(arg->new_klass, key, (const rb_method_entry_t *)value);
return ID_TABLE_CONTINUE;
}

Expand Down Expand Up @@ -1046,12 +1038,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)

rb_class_set_initialized(clone);

/* cloned flag is refer at constant inline cache
* see vm_get_const_key_cref() in vm_insnhelper.c
*/
RCLASS_SET_CLONED(clone, true);
RCLASS_SET_CLONED(orig, true);

if (!RCLASS_SINGLETON_P(CLASS_OF(clone))) {
RBASIC_SET_CLASS(clone, rb_singleton_class_clone(orig));
rb_singleton_class_attached(METACLASS_OF(clone), (VALUE)clone);
Expand All @@ -1062,7 +1048,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
copy_tables(clone, orig);
if (RCLASS_M_TBL(orig)) {
struct clone_method_arg arg;
arg.old_klass = orig;
arg.new_klass = clone;
class_initialize_method_table(clone);
rb_id_table_foreach(RCLASS_M_TBL(orig), clone_method_i, &arg);
Expand Down Expand Up @@ -1124,7 +1109,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
copy_tables(clone_origin, orig_origin);
if (RCLASS_M_TBL(orig_origin)) {
struct clone_method_arg arg;
arg.old_klass = orig;
arg.new_klass = clone;
class_initialize_method_table(clone_origin);
rb_id_table_foreach(RCLASS_M_TBL(orig_origin), clone_method_i, &arg);
Expand Down Expand Up @@ -1196,7 +1180,6 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
}
{
struct clone_method_arg arg;
arg.old_klass = klass;
arg.new_klass = clone;
rb_id_table_foreach(RCLASS_M_TBL(klass), clone_method_i, &arg);
}
Expand Down
49 changes: 22 additions & 27 deletions ext/objspace/objspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -557,42 +557,37 @@ collect_values(st_data_t key, st_data_t value, st_data_t data)
* call-seq:
* ObjectSpace.reachable_objects_from(obj) -> array or nil
*
* [MRI specific feature] Return all reachable objects from `obj'.
* Returns all reachable objects from +obj+ as an array:
*
* This method returns all reachable objects from `obj'.
* ObjectSpace.reachable_objects_from(['a', 'b', 'c'])
* #=> [Array, 'a', 'b', 'c']
*
* If `obj' has two or more references to the same object `x', then returned
* array only includes one `x' object.
* The returned array is deduplicated, meaning that if +obj+ refers
* to another object more than once, it will only be added to the array
* once:
*
* If `obj' is a non-markable (non-heap management) object such as true,
* false, nil, symbols and Fixnums (and Flonum) then it simply returns nil.
* ObjectSpace.reachable_objects_from([v = 'a', v, v])
* #=> [Array, 'a']
*
* If `obj' has references to an internal object, then it returns instances of
* ObjectSpace::InternalObjectWrapper class. This object contains a reference
* to an internal object and you can check the type of internal object with
* `type' method.
* Returns +nil+ if +obj+ is not a markable object (i.e. non-heap
* managed) object. Non-markable objects include +true+, +false+,
* +nil+, certain symbols, small integers, and floats:
*
* If `obj' is instance of ObjectSpace::InternalObjectWrapper class, then this
* method returns all reachable object from an internal object, which is
* pointed by `obj'.
* ObjectSpace.reachable_objects_from(1)
* #=> nil
*
* With this method, you can find memory leaks.
* All references to internal objects in the returned array are wrapped
* using ObjectSpace::InternalObjectWrapper objects. This object contains
* a reference to the internal object and the type of the object can
* be accessed using the ObjectSpace::InternalObjectWrapper#type method.
*
* This method is only expected to work with C Ruby.
*
* Example:
* ObjectSpace.reachable_objects_from(['a', 'b', 'c'])
* #=> [Array, 'a', 'b', 'c']
*
* ObjectSpace.reachable_objects_from(['a', 'a', 'a'])
* #=> [Array, 'a', 'a', 'a'] # all 'a' strings have different object id
* If +obj+ is instance of ObjectSpace::InternalObjectWrapper, then this
* method returns all reachable object from the internal object.
*
* ObjectSpace.reachable_objects_from([v = 'a', v, v])
* #=> [Array, 'a']
*
* ObjectSpace.reachable_objects_from(1)
* #=> nil # 1 is not markable (heap managed) object
* This method is useful for debugging purposes, such as finding
* memory leaks.
*
* This method is only expected to work with C Ruby.
*/

static VALUE
Expand Down
10 changes: 0 additions & 10 deletions internal/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ struct rb_classext_struct {
attr_index_t max_iv_count;
uint8_t variation_count;
bool permanent_classpath : 1;
bool cloned : 1;
bool shared_const_tbl : 1;
bool iclass_is_origin : 1;
bool iclass_origin_shared_mtbl : 1;
Expand Down Expand Up @@ -159,7 +158,6 @@ static inline rb_classext_t * RCLASS_EXT_WRITABLE(VALUE obj);
// class.allocator/singleton_class.attached_object are not accessed directly via RCLASSEXT_*
#define RCLASSEXT_INCLUDER(ext) (ext->as.iclass.includer)
#define RCLASSEXT_PERMANENT_CLASSPATH(ext) (ext->permanent_classpath)
#define RCLASSEXT_CLONED(ext) (ext->cloned)
#define RCLASSEXT_SHARED_CONST_TBL(ext) (ext->shared_const_tbl)
#define RCLASSEXT_ICLASS_IS_ORIGIN(ext) (ext->iclass_is_origin)
#define RCLASSEXT_ICLASS_ORIGIN_SHARED_MTBL(ext) (ext->iclass_origin_shared_mtbl)
Expand Down Expand Up @@ -196,7 +194,6 @@ static inline void RCLASSEXT_SET_INCLUDER(rb_classext_t *ext, VALUE klass, VALUE
#define RCLASS_ORIGIN(c) (RCLASS_EXT_READABLE(c)->origin_)
#define RICLASS_IS_ORIGIN_P(c) (RCLASS_EXT_READABLE(c)->iclass_is_origin)
#define RCLASS_PERMANENT_CLASSPATH_P(c) (RCLASS_EXT_READABLE(c)->permanent_classpath)
#define RCLASS_CLONED_P(c) (RCLASS_EXT_READABLE(c)->cloned)
#define RCLASS_CLASSPATH(c) (RCLASS_EXT_READABLE(c)->classpath)

// Superclasses can't be changed after initialization
Expand Down Expand Up @@ -247,7 +244,6 @@ static inline VALUE RCLASS_SET_ATTACHED_OBJECT(VALUE klass, VALUE attached_objec

static inline void RCLASS_SET_INCLUDER(VALUE iclass, VALUE klass);
static inline void RCLASS_SET_MAX_IV_COUNT(VALUE klass, attr_index_t count);
static inline void RCLASS_SET_CLONED(VALUE klass, bool cloned);
static inline void RCLASS_SET_CLASSPATH(VALUE klass, VALUE classpath, bool permanent);
static inline void RCLASS_WRITE_CLASSPATH(VALUE klass, VALUE classpath, bool permanent);

Expand Down Expand Up @@ -750,12 +746,6 @@ RCLASS_EXPECT_NO_IVAR(VALUE klass)
return RCLASS_EXT_PRIME(klass)->expect_no_ivar;
}

static inline void
RCLASS_SET_CLONED(VALUE klass, bool cloned)
{
RCLASSEXT_CLONED(RCLASS_EXT_PRIME(klass)) = cloned;
}

static inline bool
RCLASS_INITIALIZED_P(VALUE klass)
{
Expand Down
92 changes: 67 additions & 25 deletions pathname_builtin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -260,13 +260,13 @@ def freeze
#
# Returns whether the stored paths in +self+ and +other+ are equal:
#
# pn = Pathname.new('lib')
# pn == Pathname.new('lib') # => true
# pn == Pathname.new('./lib') # => false
# pn = Pathname('lib')
# pn == Pathname('lib') # => true
# pn == Pathname('./lib') # => false
#
# Returns +false+ if +other+ is not a pathname:
#
# pn == 'lib' # => false
# pn == 'lib' # => false
#
def ==(other)
return false unless Pathname === other
Expand Down Expand Up @@ -615,60 +615,60 @@ def ascend
# Returns a new \Pathname object based on the content of +self+ and +other+;
# argument +other+ may be a String, a File, a Dir, or another \Pathname:
#
# pn = Pathname.new('foo') # => #<Pathname:foo>
# pn = Pathname('foo') # => #<Pathname:foo>
# pn + 'bar' # => #<Pathname:foo/bar>
# pn + File.new('LEGAL') # => #<Pathname:foo/LEGAL>
# pn + Dir.new('lib') # => #<Pathname:foo/lib>
# pn + Pathname.new('bar') # => #<Pathname:foo/bar>
# pn + Pathname('bar') # => #<Pathname:foo/bar>
#
# When +other+ specifies a relative path (see #relative?),
# it is combined with +self+ to form a new pathname:
#
# Pathname.new('/a/b') + 'c' # => #<Pathname:/a/b/c>
# Pathname('/a/b') + 'c' # => #<Pathname:/a/b/c>
#
# Extra component separators (<tt>'/'</tt>) are removed:
#
# Pathname.new('/a/b/') + 'c' # => #<Pathname:/a/b/c>
# Pathname('/a/b/') + 'c' # => #<Pathname:/a/b/c>
#
# Extra current-directory components (<tt>'.'</tt>) are removed:
#
# Pathname.new('a') + '.' # => #<Pathname:a>
# Pathname.new('.') + 'a' # => #<Pathname:a>
# Pathname.new('.') + '.' # => #<Pathname:.>
# Pathname('a') + '.' # => #<Pathname:a>
# Pathname('.') + 'a' # => #<Pathname:a>
# Pathname('.') + '.' # => #<Pathname:.>
#
# Parent-directory components (<tt>'..'</tt>) are:
#
# - Resolved, when possible:
#
# Pathname.new('a') + '..' # => #<Pathname:.>
# Pathname.new('a/b') + '..' # => #<Pathname:a>
# Pathname.new('/') + '../a' # => #<Pathname:/a>
# Pathname.new('a') + '../b' # => #<Pathname:b>
# Pathname.new('a/b') + '../c' # => #<Pathname:a/c>
# Pathname.new('a//b/c') + '../d//e' # => #<Pathname:a//b/d//e>
# Pathname('a') + '..' # => #<Pathname:.>
# Pathname('a/b') + '..' # => #<Pathname:a>
# Pathname('/') + '../a' # => #<Pathname:/a>
# Pathname('a') + '../b' # => #<Pathname:b>
# Pathname('a/b') + '../c' # => #<Pathname:a/c>
# Pathname('a//b/c') + '../d//e' # => #<Pathname:a//b/d//e>
#
# - Removed, when not needed:
#
# Pathname.new('/') + '..' # => #<Pathname:/>
# Pathname('/') + '..' # => #<Pathname:/>
#
# - Retained, when needed:
#
# Pathname.new('..') + '..' # => #<Pathname:../..>
# Pathname.new('..') + '../a' # => #<Pathname:../../a>
# Pathname('..') + '..' # => #<Pathname:../..>
# Pathname('..') + '../a' # => #<Pathname:../../a>
#
# When +other+ specifies an absolute path (see #absolute?),
# equivalent to <tt>Pathname.new(other.to_s)</tt>:
# equivalent to <tt>Pathname(other.to_s)</tt>:
#
# Pathname.new('/a') + '/b/c' # => #<Pathname:/b/c>
# Pathname('/a') + '/b/c' # => #<Pathname:/b/c>
#
# Occurrences of <tt>'/'</tt>, <tt>'.'</tt>, and <tt>'..'</tt> are preserved:
#
# Pathname.new('/a') + '//b//c/./../d' # => #<Pathname://b//c/./../d>
# Pathname('/a') + '//b//c/./../d' # => #<Pathname://b//c/./../d>
#
# This method does not access the file system, so +other+ need not represent
# an existing (or even a valid) file or directory path:
#
# Pathname.new('/var') + 'nosuch:ever' # => #<Pathname:/var/nosuch:ever>
# Pathname('/var') + 'nosuch:ever' # => #<Pathname:/var/nosuch:ever>
#
def +(other)
other = Pathname.new(other) unless Pathname === other
Expand Down Expand Up @@ -1050,7 +1050,49 @@ def ctime() File.ctime(@path) end
# See <tt>File.mtime</tt>. Returns last modification time.
def mtime() File.mtime(@path) end

# See <tt>File.chmod</tt>. Changes permissions.

# call-seq:
# chmod(mode) -> 1
#
# Changes the mode (i.e., permissions) of the entry represented by +self+;
# see {File Permissions}[rdoc-ref:File@File+Permissions];
# returns +1+:
#
# # A helper method to make an integer mode display as octal.
# def pretty(mode); '0' + (mode & 0777).to_s(8); end
#
# # 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)
# # The directory mode.
# puts "Original directory mode: #{pretty(dir_pn.stat.mode)}"
# # Change the directory mode.
# dir_pn.chmod(0777)
# puts "New directory mode: #{pretty(dir_pn.stat.mode)}"
#
# # 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')
# # The file mode.
# puts "Original file mode: #{pretty(file_pn.stat.mode)}"
# # Change the file modes.
# file_pn.chmod(0777)
# puts "New file mode: #{pretty(file_pn.stat.mode)}"
# end
#
# Output:
#
# Original directory mode: 0775
# New directory mode: 0777
# Original file mode: 0664
# New file mode: 0777
#
def chmod(mode) File.chmod(mode, @path) end

# See <tt>File.lchmod</tt>.
Expand Down
5 changes: 3 additions & 2 deletions test/ruby/test_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ class B; end
end

class CloneTest
TEST = :C0
def foo; TEST; end
end

Expand All @@ -448,8 +449,8 @@ class CloneTest2
end

def test_constant_access_from_method_in_cloned_class
assert_equal :C1, CloneTest1.new.foo, '[Bug #15877]'
assert_equal :C2, CloneTest2.new.foo, '[Bug #15877]'
assert_equal :C0, CloneTest1.new.foo, 'originally [Bug #15877], but behaviour changed'
assert_equal :C0, CloneTest2.new.foo, 'originally [Bug #15877], but behaviour changed'
end

def test_invalid_superclass
Expand Down
Loading