summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/windows.yml2
-rw-r--r--NEWS.md20
-rw-r--r--common.mk15
-rw-r--r--defs/gmake.mk2
-rw-r--r--doc/globals.rdoc2
-rw-r--r--enumerator.c83
-rw-r--r--file.c2
-rw-r--r--gc.c82
-rw-r--r--gc/README.md9
-rw-r--r--gc/default/default.c66
-rw-r--r--gc/gc.h20
-rw-r--r--gc/mmtk/mmtk.c18
-rw-r--r--internal/set_table.h2
-rw-r--r--method.h3
-rw-r--r--object.c56
-rw-r--r--prism/templates/lib/prism/visitor.rb.erb2
-rw-r--r--ractor.c7
-rw-r--r--ractor.rb2
-rw-r--r--re.c2
-rw-r--r--scheduler.c5
-rw-r--r--set.c1
-rw-r--r--shape.c14
-rw-r--r--spec/ruby/core/file/birthtime_spec.rb13
-rw-r--r--spec/ruby/core/file/stat/birthtime_spec.rb7
-rw-r--r--spec/ruby/core/kernel/inspect_spec.rb30
-rw-r--r--st.c6
-rw-r--r--test/ruby/test_object.rb13
-rw-r--r--test/ruby/test_set.rb6
-rw-r--r--test/ruby/test_zjit.rb42
-rwxr-xr-xtool/fetch-bundled_gems.rb14
-rw-r--r--variable.c33
-rw-r--r--variable.h1
-rw-r--r--vm.c6
-rw-r--r--vm_callinfo.h1
-rw-r--r--vm_core.h1
-rw-r--r--vm_method.c73
-rw-r--r--zjit/src/codegen.rs20
-rw-r--r--zjit/src/hir.rs147
38 files changed, 647 insertions, 181 deletions
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 6c8f09660d..fa3d2f659e 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -71,7 +71,7 @@ jobs:
bundler: none
windows-toolchain: none
- - name: Install libraries with scoop
+ - name: Install tools with scoop
run: |
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
iwr -useb get.scoop.sh | iex
diff --git a/NEWS.md b/NEWS.md
index 7fdc195653..b332164e25 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -14,6 +14,26 @@ Note that each entry is kept to a minimum, see links for details.
Note: We're only listing outstanding class updates.
+* Kernel
+
+ * `Kernel#inspect` now checks for the existence of a `#instance_variables_to_inspect` method,
+ allowing control over which instance variables are displayed in the `#inspect` string:
+
+ ```ruby
+ class DatabaseConfig
+ def initialize(host, user, password)
+ @host = host
+ @user = user
+ @password = password
+ end
+
+ private def instance_variables_to_inspect = [:@host, :@user]
+ end
+
+ conf = DatabaseConfig.new("localhost", "root", "hunter2")
+ conf.inspect #=> #<DatabaseConfig:0x0000000104def350 @host="localhost", @user="root">
+ ```
+
* Binding
* `Binding#local_variables` does no longer include numbered parameters.
diff --git a/common.mk b/common.mk
index d76223e072..34287c5782 100644
--- a/common.mk
+++ b/common.mk
@@ -1562,18 +1562,6 @@ extract-gems$(sequential): PHONY
extract-gems$(sequential): $(HAVE_GIT:yes=clone-bundled-gems-src)
-clone-bundled-gems-src: PHONY
- $(Q) $(BASERUBY) -C "$(srcdir)" \
- -Itool/lib -rbundled_gem -answ \
- -e 'BEGIN {git = $$git}' \
- -e 'gem, _, repo, rev = *$$F' \
- -e 'next if !rev or /^#/=~gem' \
- -e 'gemdir = "gems/src/#{gem}"' \
- -e 'BundledGem.checkout(gemdir, repo, rev, git: git)' \
- -e 'BundledGem.dummy_gemspec("#{gemdir}/#{gem}.gemspec")' \
- -- -git="$(GIT)" \
- gems/bundled_gems
-
outdate-bundled-gems: PHONY
$(Q) $(BASERUBY) $(tooldir)/[email protected] --make="$(MAKE)" --mflags="$(MFLAGS)" \
--ruby-platform=$(arch) --ruby-version=$(ruby_version) \
@@ -1623,7 +1611,8 @@ yes-install-for-test-bundled-gems: yes-update-default-gemspecs
"sinatra" "rack" "tilt" "mustermann" "base64" "compact_index" "rack-test" "logger" "kpeg" "tracer"
test-bundled-gems-fetch: yes-test-bundled-gems-fetch
-yes-test-bundled-gems-fetch:
+yes-test-bundled-gems-fetch: clone-bundled-gems-src
+clone-bundled-gems-src: PHONY
$(Q) $(BASERUBY) -C $(srcdir)/gems ../tool/fetch-bundled_gems.rb BUNDLED_GEMS="$(BUNDLED_GEMS)" src bundled_gems
no-test-bundled-gems-fetch:
diff --git a/defs/gmake.mk b/defs/gmake.mk
index 87fc8021b2..a81d82eadd 100644
--- a/defs/gmake.mk
+++ b/defs/gmake.mk
@@ -365,7 +365,7 @@ $(srcdir)/.bundle/.timestamp:
define build-gem
$(srcdir)/gems/src/$(1)/.git: | $(srcdir)/gems/src
$(ECHO) Cloning $(4)
- $(Q) $(GIT) clone $(4) $$(@D)
+ $(Q) $(GIT) clone --depth=1 --no-tags $(4) $$(@D)
$(bundled-gem-revision): \
$(if $(if $(wildcard $$(@)),$(filter $(3),$(shell cat $$(@)))),,PHONY) \
diff --git a/doc/globals.rdoc b/doc/globals.rdoc
index 9d9fc57e6e..9466005be7 100644
--- a/doc/globals.rdoc
+++ b/doc/globals.rdoc
@@ -137,7 +137,7 @@ English - <tt>$DEFAULT_INPUT</tt>.
An output stream, initially <tt>$stdout</tt>.
-English - <tt>$DEFAULT_OUTPUT
+English - <tt>$DEFAULT_OUTPUT</tt>
=== <tt>$.</tt> (Input Position)
diff --git a/enumerator.c b/enumerator.c
index faaa77cb49..c29ef2df2c 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -3748,6 +3748,55 @@ enumerator_s_product(int argc, VALUE *argv, VALUE klass)
return obj;
}
+struct arith_seq {
+ struct enumerator enumerator;
+ VALUE begin;
+ VALUE end;
+ VALUE step;
+ bool exclude_end;
+};
+
+RUBY_REFERENCES(arith_seq_refs) = {
+ RUBY_REF_EDGE(struct enumerator, obj),
+ RUBY_REF_EDGE(struct enumerator, args),
+ RUBY_REF_EDGE(struct enumerator, fib),
+ RUBY_REF_EDGE(struct enumerator, dst),
+ RUBY_REF_EDGE(struct enumerator, lookahead),
+ RUBY_REF_EDGE(struct enumerator, feedvalue),
+ RUBY_REF_EDGE(struct enumerator, stop_exc),
+ RUBY_REF_EDGE(struct enumerator, size),
+ RUBY_REF_EDGE(struct enumerator, procs),
+
+ RUBY_REF_EDGE(struct arith_seq, begin),
+ RUBY_REF_EDGE(struct arith_seq, end),
+ RUBY_REF_EDGE(struct arith_seq, step),
+ RUBY_REF_END
+};
+
+static const rb_data_type_t arith_seq_data_type = {
+ "arithmetic_sequence",
+ {
+ RUBY_REFS_LIST_PTR(arith_seq_refs),
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // Nothing allocated externally, so don't need a memsize function
+ NULL,
+ },
+ .parent = &enumerator_data_type,
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
+};
+
+static VALUE
+arith_seq_allocate(VALUE klass)
+{
+ struct arith_seq *ptr;
+ VALUE enum_obj;
+
+ enum_obj = TypedData_Make_Struct(klass, struct arith_seq, &arith_seq_data_type, ptr);
+ ptr->enumerator.obj = Qundef;
+
+ return enum_obj;
+}
+
/*
* Document-class: Enumerator::ArithmeticSequence
*
@@ -3765,12 +3814,16 @@ rb_arith_seq_new(VALUE obj, VALUE meth, int argc, VALUE const *argv,
rb_enumerator_size_func *size_fn,
VALUE beg, VALUE end, VALUE step, int excl)
{
- VALUE aseq = enumerator_init(enumerator_allocate(rb_cArithSeq),
+ VALUE aseq = enumerator_init(arith_seq_allocate(rb_cArithSeq),
obj, meth, argc, argv, size_fn, Qnil, rb_keyword_given_p());
- rb_ivar_set(aseq, id_begin, beg);
- rb_ivar_set(aseq, id_end, end);
- rb_ivar_set(aseq, id_step, step);
- rb_ivar_set(aseq, id_exclude_end, RBOOL(excl));
+ struct arith_seq *ptr;
+ TypedData_Get_Struct(aseq, struct arith_seq, &enumerator_data_type, ptr);
+
+ RB_OBJ_WRITE(aseq, &ptr->begin, beg);
+ RB_OBJ_WRITE(aseq, &ptr->end, end);
+ RB_OBJ_WRITE(aseq, &ptr->step, step);
+ ptr->exclude_end = excl;
+
return aseq;
}
@@ -3783,7 +3836,9 @@ rb_arith_seq_new(VALUE obj, VALUE meth, int argc, VALUE const *argv,
static inline VALUE
arith_seq_begin(VALUE self)
{
- return rb_ivar_get(self, id_begin);
+ struct arith_seq *ptr;
+ TypedData_Get_Struct(self, struct arith_seq, &enumerator_data_type, ptr);
+ return ptr->begin;
}
/*
@@ -3794,7 +3849,9 @@ arith_seq_begin(VALUE self)
static inline VALUE
arith_seq_end(VALUE self)
{
- return rb_ivar_get(self, id_end);
+ struct arith_seq *ptr;
+ TypedData_Get_Struct(self, struct arith_seq, &enumerator_data_type, ptr);
+ return ptr->end;
}
/*
@@ -3806,7 +3863,9 @@ arith_seq_end(VALUE self)
static inline VALUE
arith_seq_step(VALUE self)
{
- return rb_ivar_get(self, id_step);
+ struct arith_seq *ptr;
+ TypedData_Get_Struct(self, struct arith_seq, &enumerator_data_type, ptr);
+ return ptr->step;
}
/*
@@ -3817,13 +3876,17 @@ arith_seq_step(VALUE self)
static inline VALUE
arith_seq_exclude_end(VALUE self)
{
- return rb_ivar_get(self, id_exclude_end);
+ struct arith_seq *ptr;
+ TypedData_Get_Struct(self, struct arith_seq, &enumerator_data_type, ptr);
+ return RBOOL(ptr->exclude_end);
}
static inline int
arith_seq_exclude_end_p(VALUE self)
{
- return RTEST(arith_seq_exclude_end(self));
+ struct arith_seq *ptr;
+ TypedData_Get_Struct(self, struct arith_seq, &enumerator_data_type, ptr);
+ return ptr->exclude_end;
}
int
diff --git a/file.c b/file.c
index b38cd67199..322df6dbec 100644
--- a/file.c
+++ b/file.c
@@ -543,7 +543,7 @@ rb_stat_new(const struct stat *st)
if (st) {
#if RUBY_USE_STATX
# define CP(m) .stx_ ## m = st->st_ ## m
-# define CP_32(m) .stx_ ## m = (__u32)st->st_ ## m
+# define CP_32(m) .stx_ ## m = (uint32_t)st->st_ ## m
# define CP_TS(m) .stx_ ## m = stat_ ## m ## spec(st)
rb_st->stat = (struct statx){
.stx_mask = STATX_BASIC_STATS,
diff --git a/gc.c b/gc.c
index 3e7d88209f..05cefc739b 100644
--- a/gc.c
+++ b/gc.c
@@ -131,45 +131,45 @@
#include "shape.h"
unsigned int
-rb_gc_vm_lock(void)
+rb_gc_vm_lock(const char *file, int line)
{
unsigned int lev = 0;
- RB_VM_LOCK_ENTER_LEV(&lev);
+ rb_vm_lock_enter(&lev, file, line);
return lev;
}
void
-rb_gc_vm_unlock(unsigned int lev)
+rb_gc_vm_unlock(unsigned int lev, const char *file, int line)
{
- RB_VM_LOCK_LEAVE_LEV(&lev);
+ rb_vm_lock_leave(&lev, file, line);
}
unsigned int
-rb_gc_cr_lock(void)
+rb_gc_cr_lock(const char *file, int line)
{
unsigned int lev;
- RB_VM_LOCK_ENTER_CR_LEV(GET_RACTOR(), &lev);
+ rb_vm_lock_enter_cr(GET_RACTOR(), &lev, file, line);
return lev;
}
void
-rb_gc_cr_unlock(unsigned int lev)
+rb_gc_cr_unlock(unsigned int lev, const char *file, int line)
{
- RB_VM_LOCK_LEAVE_CR_LEV(GET_RACTOR(), &lev);
+ rb_vm_lock_leave_cr(GET_RACTOR(), &lev, file, line);
}
unsigned int
-rb_gc_vm_lock_no_barrier(void)
+rb_gc_vm_lock_no_barrier(const char *file, int line)
{
unsigned int lev = 0;
- RB_VM_LOCK_ENTER_LEV_NB(&lev);
+ rb_vm_lock_enter_nb(&lev, file, line);
return lev;
}
void
-rb_gc_vm_unlock_no_barrier(unsigned int lev)
+rb_gc_vm_unlock_no_barrier(unsigned int lev, const char *file, int line)
{
- RB_VM_LOCK_LEAVE_LEV_NB(&lev);
+ rb_vm_lock_leave_nb(&lev, file, line);
}
void
@@ -1783,9 +1783,9 @@ generate_next_object_id(void)
// 64bit atomics are available
return SIZET2NUM(RUBY_ATOMIC_SIZE_FETCH_ADD(object_id_counter, 1) * OBJ_ID_INCREMENT);
#else
- unsigned int lock_lev = rb_gc_vm_lock();
+ unsigned int lock_lev = RB_GC_VM_LOCK();
VALUE id = ULL2NUM(++object_id_counter * OBJ_ID_INCREMENT);
- rb_gc_vm_unlock(lock_lev);
+ RB_GC_VM_UNLOCK(lock_lev);
return id;
#endif
}
@@ -1867,7 +1867,7 @@ class_object_id(VALUE klass)
{
VALUE id = RUBY_ATOMIC_VALUE_LOAD(RCLASS(klass)->object_id);
if (!id) {
- unsigned int lock_lev = rb_gc_vm_lock();
+ unsigned int lock_lev = RB_GC_VM_LOCK();
id = generate_next_object_id();
VALUE existing_id = RUBY_ATOMIC_VALUE_CAS(RCLASS(klass)->object_id, 0, id);
if (existing_id) {
@@ -1876,7 +1876,7 @@ class_object_id(VALUE klass)
else if (RB_UNLIKELY(id2ref_tbl)) {
st_insert(id2ref_tbl, id, klass);
}
- rb_gc_vm_unlock(lock_lev);
+ RB_GC_VM_UNLOCK(lock_lev);
}
return id;
}
@@ -1946,9 +1946,9 @@ object_id(VALUE obj)
}
if (UNLIKELY(rb_gc_multi_ractor_p() && rb_ractor_shareable_p(obj))) {
- unsigned int lock_lev = rb_gc_vm_lock();
+ unsigned int lock_lev = RB_GC_VM_LOCK();
VALUE id = object_id0(obj);
- rb_gc_vm_unlock(lock_lev);
+ RB_GC_VM_UNLOCK(lock_lev);
return id;
}
@@ -1983,7 +1983,7 @@ object_id_to_ref(void *objspace_ptr, VALUE object_id)
{
rb_objspace_t *objspace = objspace_ptr;
- unsigned int lev = rb_gc_vm_lock();
+ unsigned int lev = RB_GC_VM_LOCK();
if (!id2ref_tbl) {
rb_gc_vm_barrier(); // stop other ractors
@@ -2007,7 +2007,7 @@ object_id_to_ref(void *objspace_ptr, VALUE object_id)
VALUE obj;
bool found = st_lookup(id2ref_tbl, object_id, &obj) && !rb_gc_impl_garbage_object_p(objspace, obj);
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
if (found) {
return obj;
@@ -2095,6 +2095,15 @@ rb_gc_obj_free_vm_weak_references(VALUE obj)
break;
case T_IMEMO:
switch (imemo_type(obj)) {
+ case imemo_callcache: {
+ const struct rb_callcache *cc = (const struct rb_callcache *)obj;
+
+ if (vm_cc_refinement_p(cc)) {
+ rb_vm_delete_cc_refinement(cc);
+ }
+
+ break;
+ }
case imemo_callinfo:
rb_vm_ci_free((const struct rb_callinfo *)obj);
break;
@@ -3930,6 +3939,23 @@ vm_weak_table_foreach_update_weak_key(st_data_t *key, st_data_t *value, st_data_
}
static int
+vm_weak_table_cc_refinement_foreach(st_data_t key, st_data_t data, int error)
+{
+ struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
+
+ return iter_data->callback((VALUE)key, iter_data->data);
+}
+
+static int
+vm_weak_table_cc_refinement_foreach_update_update(st_data_t *key, st_data_t data, int existing)
+{
+ struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
+
+ return iter_data->update_callback((VALUE *)key, iter_data->data);
+}
+
+
+static int
vm_weak_table_str_sym_foreach(st_data_t key, st_data_t value, st_data_t data, int error)
{
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
@@ -4067,7 +4093,8 @@ vm_weak_table_gen_fields_foreach(st_data_t key, st_data_t value, st_data_t data)
);
}
else {
- for (uint32_t i = 0; i < fields_tbl->as.shape.fields_count; i++) {
+ uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID((VALUE)key));
+ for (uint32_t i = 0; i < fields_count; i++) {
if (SPECIAL_CONST_P(fields_tbl->as.shape.fields[i])) continue;
int ivar_ret = iter_data->callback(fields_tbl->as.shape.fields[i], iter_data->data);
@@ -4178,8 +4205,21 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
);
break;
}
+ case RB_GC_VM_CC_REFINEMENT_TABLE: {
+ if (vm->cc_refinement_table) {
+ set_foreach_with_replace(
+ vm->cc_refinement_table,
+ vm_weak_table_cc_refinement_foreach,
+ vm_weak_table_cc_refinement_foreach_update_update,
+ (st_data_t)&foreach_data
+ );
+ }
+ break;
+ }
case RB_GC_VM_WEAK_TABLE_COUNT:
rb_bug("Unreacheable");
+ default:
+ rb_bug("rb_gc_vm_weak_table_foreach: unknown table %d", table);
}
}
diff --git a/gc/README.md b/gc/README.md
index 102b24e24e..cb71357973 100644
--- a/gc/README.md
+++ b/gc/README.md
@@ -15,12 +15,17 @@ Two GC implementations are included in Ruby:
> [!IMPORTANT]
> Ruby's modular GC feature is experimental and subject to change. There may be bugs or performance impacts. Use at your own risk.
+### Building Ruby with Modular GC
+
1. Configure Ruby with the `--with-modular-gc=<dir>` option, where `dir` is the directory you want to place the built GC libraries into.
2. Build Ruby as usual.
-3. Build your desired GC implementation with `make install-modular-gc MODULAR_GC=<impl>`. This will build the GC implementation and place the built library into the `dir` specified in step 1. `impl` can be one of:
+
+### Building GC implementations shipped with Ruby
+
+1. Build your desired GC implementation with `make install-modular-gc MODULAR_GC=<impl>`. This will build the GC implementation and place the built library into the `dir` specified in step 1. `impl` can be one of:
- `default`: The default GC that Ruby ships with.
- `mmtk`: The GC that uses [MMTk](https://www.mmtk.io/) as the back-end. See Ruby-specific details in the [ruby/mmtk](https://github.com/ruby/mmtk) repository.
-4. Run your desired GC implementation by setting the `RUBY_GC_LIBRARY=<lib>` environment variable, where `lib` could be `default`, `mmtk`, or your own implementation (as long as you place it in the `dir` specified in step 1).
+2. Run your desired GC implementation by setting the `RUBY_GC_LIBRARY=<lib>` environment variable, where `lib` could be `default`, `mmtk`, or your own implementation (as long as you place it in the `dir` specified in step 1).
## Modular GC API
diff --git a/gc/default/default.c b/gc/default/default.c
index 5664b3dd90..40d39d6f17 100644
--- a/gc/default/default.c
+++ b/gc/default/default.c
@@ -1229,7 +1229,7 @@ check_rvalue_consistency_force(rb_objspace_t *objspace, const VALUE obj, int ter
{
int err = 0;
- int lev = rb_gc_vm_lock_no_barrier();
+ int lev = RB_GC_VM_LOCK_NO_BARRIER();
{
if (SPECIAL_CONST_P(obj)) {
fprintf(stderr, "check_rvalue_consistency: %p is a special const.\n", (void *)obj);
@@ -1319,7 +1319,7 @@ check_rvalue_consistency_force(rb_objspace_t *objspace, const VALUE obj, int ter
}
}
}
- rb_gc_vm_unlock_no_barrier(lev);
+ RB_GC_VM_UNLOCK_NO_BARRIER(lev);
if (err > 0 && terminate) {
rb_bug("check_rvalue_consistency_force: there is %d errors.", err);
@@ -2140,7 +2140,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
#if RGENGC_CHECK_MODE
newobj_fill(obj, 0, 0, 0);
- int lev = rb_gc_vm_lock_no_barrier();
+ int lev = RB_GC_VM_LOCK_NO_BARRIER();
{
check_rvalue_consistency(objspace, obj);
@@ -2151,7 +2151,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
if (RVALUE_REMEMBERED(objspace, obj)) rb_bug("newobj: %s is remembered.", rb_obj_info(obj));
}
- rb_gc_vm_unlock_no_barrier(lev);
+ RB_GC_VM_UNLOCK_NO_BARRIER(lev);
#endif
if (RB_UNLIKELY(wb_protected == FALSE)) {
@@ -2363,7 +2363,7 @@ newobj_cache_miss(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size
bool unlock_vm = false;
if (!vm_locked) {
- lev = rb_gc_cr_lock();
+ lev = RB_GC_CR_LOCK();
unlock_vm = true;
}
@@ -2387,7 +2387,7 @@ newobj_cache_miss(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache, size
}
if (unlock_vm) {
- rb_gc_cr_unlock(lev);
+ RB_GC_CR_UNLOCK(lev);
}
if (RB_UNLIKELY(obj == Qfalse)) {
@@ -2416,7 +2416,7 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_new
VALUE obj;
unsigned int lev;
- lev = rb_gc_cr_lock();
+ lev = RB_GC_CR_LOCK();
{
if (RB_UNLIKELY(during_gc || ruby_gc_stressful)) {
if (during_gc) {
@@ -2438,7 +2438,7 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_new
obj = newobj_alloc(objspace, cache, heap_idx, true);
newobj_init(klass, flags, wb_protected, objspace, obj);
}
- rb_gc_cr_unlock(lev);
+ RB_GC_CR_UNLOCK(lev);
return obj;
}
@@ -2753,7 +2753,7 @@ rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block)
RBASIC(obj)->flags |= FL_FINALIZE;
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
if (st_lookup(finalizer_table, obj, &data)) {
table = (VALUE)data;
@@ -2766,7 +2766,7 @@ rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block)
for (i = 0; i < len; i++) {
VALUE recv = RARRAY_AREF(table, i);
if (rb_equal(recv, block)) {
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
return recv;
}
}
@@ -2780,7 +2780,7 @@ rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block)
st_add_direct(finalizer_table, obj, table);
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
return block;
}
@@ -2794,9 +2794,9 @@ rb_gc_impl_undefine_finalizer(void *objspace_ptr, VALUE obj)
st_data_t data = obj;
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
st_delete(finalizer_table, &data, 0);
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
FL_UNSET(obj, FL_FINALIZE);
}
@@ -2810,7 +2810,7 @@ rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj)
if (!FL_TEST(obj, FL_FINALIZE)) return;
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
if (RB_LIKELY(st_lookup(finalizer_table, obj, &data))) {
table = rb_ary_dup((VALUE)data);
RARRAY_ASET(table, 0, rb_obj_id(dest));
@@ -2820,7 +2820,7 @@ rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj)
else {
rb_bug("rb_gc_copy_finalizer: FL_FINALIZE set but not found in finalizer_table: %s", rb_obj_info(obj));
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
}
static VALUE
@@ -2864,7 +2864,7 @@ finalize_list(rb_objspace_t *objspace, VALUE zombie)
next_zombie = RZOMBIE(zombie)->next;
page = GET_HEAP_PAGE(zombie);
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
run_final(objspace, zombie);
{
@@ -2878,7 +2878,7 @@ finalize_list(rb_objspace_t *objspace, VALUE zombie)
heap_page_add_freeobj(objspace, page, zombie);
page->heap->total_freed_objects++;
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
zombie = next_zombie;
}
@@ -3247,7 +3247,7 @@ read_barrier_handler(uintptr_t address)
rb_bug("read_barrier_handler: segmentation fault at %p", (void *)address);
}
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
{
unlock_page_body(objspace, page_body);
@@ -3255,7 +3255,7 @@ read_barrier_handler(uintptr_t address)
invalidate_moved_page(objspace, GET_HEAP_PAGE(address));
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
}
#endif
@@ -5180,7 +5180,7 @@ gc_verify_internal_consistency(void *objspace_ptr)
{
rb_objspace_t *objspace = objspace_ptr;
- unsigned int lev = rb_gc_vm_lock();
+ unsigned int lev = RB_GC_VM_LOCK();
{
rb_gc_vm_barrier(); // stop other ractors
@@ -5191,7 +5191,7 @@ gc_verify_internal_consistency(void *objspace_ptr)
}
during_gc = prev_during_gc;
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
}
static void
@@ -5952,11 +5952,11 @@ gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace)
/* mark `a' and remember (default behavior) */
if (!RVALUE_REMEMBERED(objspace, a)) {
- int lev = rb_gc_vm_lock_no_barrier();
+ int lev = RB_GC_VM_LOCK_NO_BARRIER();
{
rgengc_remember(objspace, a);
}
- rb_gc_vm_unlock_no_barrier(lev);
+ RB_GC_VM_UNLOCK_NO_BARRIER(lev);
gc_report(1, objspace, "gc_writebarrier_generational: %s (remembered) -> %s\n", rb_obj_info(a), rb_obj_info(b));
}
@@ -6029,7 +6029,7 @@ rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b)
else {
bool retry = false;
/* slow path */
- int lev = rb_gc_vm_lock_no_barrier();
+ int lev = RB_GC_VM_LOCK_NO_BARRIER();
{
if (is_incremental_marking(objspace)) {
gc_writebarrier_incremental(a, b, objspace);
@@ -6038,7 +6038,7 @@ rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b)
retry = true;
}
}
- rb_gc_vm_unlock_no_barrier(lev);
+ RB_GC_VM_UNLOCK_NO_BARRIER(lev);
if (retry) goto retry;
}
@@ -6057,7 +6057,7 @@ rb_gc_impl_writebarrier_unprotect(void *objspace_ptr, VALUE obj)
gc_report(2, objspace, "rb_gc_writebarrier_unprotect: %s %s\n", rb_obj_info(obj),
RVALUE_REMEMBERED(objspace, obj) ? " (already remembered)" : "");
- unsigned int lev = rb_gc_vm_lock_no_barrier();
+ unsigned int lev = RB_GC_VM_LOCK_NO_BARRIER();
{
if (RVALUE_OLD_P(objspace, obj)) {
gc_report(1, objspace, "rb_gc_writebarrier_unprotect: %s\n", rb_obj_info(obj));
@@ -6079,7 +6079,7 @@ rb_gc_impl_writebarrier_unprotect(void *objspace_ptr, VALUE obj)
RB_DEBUG_COUNTER_INC(obj_wb_unprotect);
MARK_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(obj), obj);
}
- rb_gc_vm_unlock_no_barrier(lev);
+ RB_GC_VM_UNLOCK_NO_BARRIER(lev);
}
}
@@ -6292,7 +6292,7 @@ garbage_collect(rb_objspace_t *objspace, unsigned int reason)
{
int ret;
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
{
#if GC_PROFILE_MORE_DETAIL
objspace->profile.prepare_time = getrusage_time();
@@ -6306,7 +6306,7 @@ garbage_collect(rb_objspace_t *objspace, unsigned int reason)
ret = gc_start(objspace, reason);
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
return ret;
}
@@ -6590,7 +6590,7 @@ gc_clock_end(struct timespec *ts)
static inline void
gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev)
{
- *lock_lev = rb_gc_vm_lock();
+ *lock_lev = RB_GC_VM_LOCK();
switch (event) {
case gc_enter_event_rest:
@@ -6629,7 +6629,7 @@ gc_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_l
gc_report(1, objspace, "gc_exit: %s [%s]\n", gc_enter_event_cstr(event), gc_current_status(objspace));
during_gc = FALSE;
- rb_gc_vm_unlock(*lock_lev);
+ RB_GC_VM_UNLOCK(*lock_lev);
}
#ifndef MEASURE_GC
@@ -9106,7 +9106,7 @@ gc_verify_compaction_references(int argc, VALUE* argv, VALUE self)
/* Clear the heap. */
rb_gc_impl_start(objspace, true, true, true, false);
- unsigned int lev = rb_gc_vm_lock();
+ unsigned int lev = RB_GC_VM_LOCK();
{
gc_rest(objspace);
@@ -9162,7 +9162,7 @@ gc_verify_compaction_references(int argc, VALUE* argv, VALUE self)
objspace->rcompactor.compare_func = compare_free_slots;
}
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
rb_gc_impl_start(rb_gc_get_objspace(), true, true, true, true);
diff --git a/gc/gc.h b/gc/gc.h
index 6f8b82942e..c12498f033 100644
--- a/gc/gc.h
+++ b/gc/gc.h
@@ -31,9 +31,17 @@ enum rb_gc_vm_weak_tables {
RB_GC_VM_ID2REF_TABLE,
RB_GC_VM_GENERIC_FIELDS_TABLE,
RB_GC_VM_FROZEN_STRINGS_TABLE,
+ RB_GC_VM_CC_REFINEMENT_TABLE,
RB_GC_VM_WEAK_TABLE_COUNT
};
+#define RB_GC_VM_LOCK() rb_gc_vm_lock(__FILE__, __LINE__)
+#define RB_GC_VM_UNLOCK(lev) rb_gc_vm_unlock(lev, __FILE__, __LINE__)
+#define RB_GC_CR_LOCK() rb_gc_cr_lock(__FILE__, __LINE__)
+#define RB_GC_CR_UNLOCK(lev) rb_gc_cr_unlock(lev, __FILE__, __LINE__)
+#define RB_GC_VM_LOCK_NO_BARRIER() rb_gc_vm_lock_no_barrier(__FILE__, __LINE__)
+#define RB_GC_VM_UNLOCK_NO_BARRIER(lev) rb_gc_vm_unlock_no_barrier(lev, __FILE__, __LINE__)
+
#if USE_MODULAR_GC
# define MODULAR_GC_FN
#else
@@ -56,12 +64,12 @@ size_t rb_obj_memsize_of(VALUE obj);
bool ruby_free_at_exit_p(void);
void rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *passing_data);
-MODULAR_GC_FN unsigned int rb_gc_vm_lock(void);
-MODULAR_GC_FN void rb_gc_vm_unlock(unsigned int lev);
-MODULAR_GC_FN unsigned int rb_gc_cr_lock(void);
-MODULAR_GC_FN void rb_gc_cr_unlock(unsigned int lev);
-MODULAR_GC_FN unsigned int rb_gc_vm_lock_no_barrier(void);
-MODULAR_GC_FN void rb_gc_vm_unlock_no_barrier(unsigned int lev);
+MODULAR_GC_FN unsigned int rb_gc_vm_lock(const char *file, int line);
+MODULAR_GC_FN void rb_gc_vm_unlock(unsigned int lev, const char *file, int line);
+MODULAR_GC_FN unsigned int rb_gc_cr_lock(const char *file, int line);
+MODULAR_GC_FN void rb_gc_cr_unlock(unsigned int lev, const char *file, int line);
+MODULAR_GC_FN unsigned int rb_gc_vm_lock_no_barrier(const char *file, int line);
+MODULAR_GC_FN void rb_gc_vm_unlock_no_barrier(unsigned int lev, const char *file, int line);
MODULAR_GC_FN void rb_gc_vm_barrier(void);
MODULAR_GC_FN size_t rb_gc_obj_optimal_size(VALUE obj);
MODULAR_GC_FN void rb_gc_mark_children(void *objspace, VALUE obj);
diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c
index 9e4ee9f3de..c318c6fe48 100644
--- a/gc/mmtk/mmtk.c
+++ b/gc/mmtk/mmtk.c
@@ -129,7 +129,7 @@ rb_mmtk_block_for_gc(MMTk_VMMutatorThread mutator)
struct objspace *objspace = rb_gc_get_objspace();
size_t starting_gc_count = objspace->gc_count;
- int lock_lev = rb_gc_vm_lock();
+ int lock_lev = RB_GC_VM_LOCK();
int err;
if ((err = pthread_mutex_lock(&objspace->mutex)) != 0) {
rb_bug("ERROR: cannot lock objspace->mutex: %s", strerror(err));
@@ -173,7 +173,7 @@ rb_mmtk_block_for_gc(MMTk_VMMutatorThread mutator)
if ((err = pthread_mutex_unlock(&objspace->mutex)) != 0) {
rb_bug("ERROR: cannot release objspace->mutex: %s", strerror(err));
}
- rb_gc_vm_unlock(lock_lev);
+ RB_GC_VM_UNLOCK(lock_lev);
}
static size_t
@@ -927,7 +927,7 @@ rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block)
RBASIC(obj)->flags |= FL_FINALIZE;
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
if (st_lookup(objspace->finalizer_table, obj, &data)) {
table = (VALUE)data;
@@ -940,7 +940,7 @@ rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block)
for (i = 0; i < len; i++) {
VALUE recv = RARRAY_AREF(table, i);
if (rb_equal(recv, block)) {
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
return recv;
}
}
@@ -954,7 +954,7 @@ rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block)
st_add_direct(objspace->finalizer_table, obj, table);
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
return block;
}
@@ -966,9 +966,9 @@ rb_gc_impl_undefine_finalizer(void *objspace_ptr, VALUE obj)
st_data_t data = obj;
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
st_delete(objspace->finalizer_table, &data, 0);
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
FL_UNSET(obj, FL_FINALIZE);
}
@@ -982,7 +982,7 @@ rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj)
if (!FL_TEST(obj, FL_FINALIZE)) return;
- int lev = rb_gc_vm_lock();
+ int lev = RB_GC_VM_LOCK();
if (RB_LIKELY(st_lookup(objspace->finalizer_table, obj, &data))) {
table = rb_ary_dup((VALUE)data);
RARRAY_ASET(table, 0, rb_obj_id(dest));
@@ -992,7 +992,7 @@ rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj)
else {
rb_bug("rb_gc_copy_finalizer: FL_FINALIZE set but not found in finalizer_table: %s", rb_obj_info(obj));
}
- rb_gc_vm_unlock(lev);
+ RB_GC_VM_UNLOCK(lev);
}
static int
diff --git a/internal/set_table.h b/internal/set_table.h
index 7c03de2060..def52db039 100644
--- a/internal/set_table.h
+++ b/internal/set_table.h
@@ -37,6 +37,8 @@ size_t rb_set_table_size(const struct set_table *tbl);
set_table *rb_set_init_table_with_size(set_table *tab, const struct st_hash_type *, st_index_t);
#define set_init_numtable rb_set_init_numtable
set_table *rb_set_init_numtable(void);
+#define set_init_numtable_with_size rb_set_init_numtable_with_size
+set_table *rb_set_init_numtable_with_size(st_index_t size);
#define set_delete rb_set_delete
int rb_set_delete(set_table *, st_data_t *); /* returns 0:notfound 1:deleted */
#define set_insert rb_set_insert
diff --git a/method.h b/method.h
index b2ac07fc83..6abf2495b0 100644
--- a/method.h
+++ b/method.h
@@ -254,6 +254,9 @@ void rb_scope_visibility_set(rb_method_visibility_t);
VALUE rb_unnamed_parameters(int arity);
+void rb_vm_insert_cc_refinement(const struct rb_callcache *cc);
+void rb_vm_delete_cc_refinement(const struct rb_callcache *cc);
+
void rb_clear_method_cache(VALUE klass_or_module, ID mid);
void rb_clear_all_refinement_method_cache(void);
void rb_invalidate_method_caches(struct rb_id_table *cm_tbl, struct rb_id_table *cc_tbl);
diff --git a/object.c b/object.c
index fbd2f5d557..8e924b4e6a 100644
--- a/object.c
+++ b/object.c
@@ -83,6 +83,7 @@ static VALUE rb_cFalseClass_to_s;
#define id_init_dup idInitialize_dup
#define id_const_missing idConst_missing
#define id_to_f idTo_f
+static ID id_instance_variables_to_inspect;
#define CLASS_OR_MODULE_P(obj) \
(!SPECIAL_CONST_P(obj) && \
@@ -733,11 +734,17 @@ rb_inspect(VALUE obj)
static int
inspect_i(ID id, VALUE value, st_data_t a)
{
- VALUE str = (VALUE)a;
+ VALUE *args = (VALUE *)a, str = args[0], ivars = args[1];
/* need not to show internal data */
if (CLASS_OF(value) == 0) return ST_CONTINUE;
if (!rb_is_instance_id(id)) return ST_CONTINUE;
+ if (!NIL_P(ivars)) {
+ VALUE name = ID2SYM(id);
+ for (long i = 0; RARRAY_AREF(ivars, i) != name; ) {
+ if (++i >= RARRAY_LEN(ivars)) return ST_CONTINUE;
+ }
+ }
if (RSTRING_PTR(str)[0] == '-') { /* first element */
RSTRING_PTR(str)[0] = '#';
rb_str_cat2(str, " ");
@@ -752,13 +759,15 @@ inspect_i(ID id, VALUE value, st_data_t a)
}
static VALUE
-inspect_obj(VALUE obj, VALUE str, int recur)
+inspect_obj(VALUE obj, VALUE a, int recur)
{
+ VALUE *args = (VALUE *)a, str = args[0];
+
if (recur) {
rb_str_cat2(str, " ...");
}
else {
- rb_ivar_foreach(obj, inspect_i, str);
+ rb_ivar_foreach(obj, inspect_i, a);
}
rb_str_cat2(str, ">");
RSTRING_PTR(str)[0] = '#';
@@ -791,17 +800,47 @@ inspect_obj(VALUE obj, VALUE str, int recur)
* end
* end
* Bar.new.inspect #=> "#<Bar:0x0300c868 @bar=1>"
+ *
+ * If _obj_ responds to +instance_variables_to_inspect+, then only
+ * the instance variables listed in the returned array will be included
+ * in the inspect string.
+ *
+ *
+ * class DatabaseConfig
+ * def initialize(host, user, password)
+ * @host = host
+ * @user = user
+ * @password = password
+ * end
+ *
+ * private
+ * def instance_variables_to_inspect = [:@host, :@user]
+ * end
+ *
+ * conf = DatabaseConfig.new("localhost", "root", "hunter2")
+ * conf.inspect #=> #<DatabaseConfig:0x0000000104def350 @host="localhost", @user="root">
*/
static VALUE
rb_obj_inspect(VALUE obj)
{
- if (rb_ivar_count(obj) > 0) {
- VALUE str;
+ VALUE ivars = rb_check_funcall(obj, id_instance_variables_to_inspect, 0, 0);
+ st_index_t n = 0;
+ if (UNDEF_P(ivars)) {
+ n = rb_ivar_count(obj);
+ ivars = Qnil;
+ }
+ else if (!NIL_P(ivars)) {
+ Check_Type(ivars, T_ARRAY);
+ n = RARRAY_LEN(ivars);
+ }
+ if (n > 0) {
VALUE c = rb_class_name(CLASS_OF(obj));
-
- str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void*)obj);
- return rb_exec_recursive(inspect_obj, obj, str);
+ VALUE args[2] = {
+ rb_sprintf("-<%"PRIsVALUE":%p", c, (void*)obj),
+ ivars
+ };
+ return rb_exec_recursive(inspect_obj, obj, (VALUE)args);
}
else {
return rb_any_to_s(obj);
@@ -4600,6 +4639,7 @@ void
Init_Object(void)
{
id_dig = rb_intern_const("dig");
+ id_instance_variables_to_inspect = rb_intern_const("instance_variables_to_inspect");
InitVM(Object);
}
diff --git a/prism/templates/lib/prism/visitor.rb.erb b/prism/templates/lib/prism/visitor.rb.erb
index a1eac38dc4..b1a03c3f1a 100644
--- a/prism/templates/lib/prism/visitor.rb.erb
+++ b/prism/templates/lib/prism/visitor.rb.erb
@@ -34,7 +34,7 @@ module Prism
#
# class FooCalls < Prism::Visitor
# def visit_call_node(node)
- # if node.name == "foo"
+ # if node.name == :foo
# # Do something with the node
# end
#
diff --git a/ractor.c b/ractor.c
index 56345d5670..177906ea1c 100644
--- a/ractor.c
+++ b/ractor.c
@@ -1658,10 +1658,9 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
if (d.stop) return 1;
}
else {
- for (uint32_t i = 0; i < fields_tbl->as.shape.fields_count; i++) {
- if (!UNDEF_P(fields_tbl->as.shape.fields[i])) {
- CHECK_AND_REPLACE(fields_tbl->as.shape.fields[i]);
- }
+ uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
+ for (uint32_t i = 0; i < fields_count; i++) {
+ CHECK_AND_REPLACE(fields_tbl->as.shape.fields[i]);
}
}
}
diff --git a/ractor.rb b/ractor.rb
index 15d2e659ba..20fc622d77 100644
--- a/ractor.rb
+++ b/ractor.rb
@@ -722,7 +722,7 @@ class Ractor
#
# port = Ractor::Port.new
# Ractor.new port do |port|
- # port.sned 1 # OK
+ # port.send 1 # OK
# port.send 2 # OK
# port.close
# port.send 3 # raise Ractor::ClosedError
diff --git a/re.c b/re.c
index 96a3cbeaa9..3cf99c1210 100644
--- a/re.c
+++ b/re.c
@@ -1666,7 +1666,7 @@ rb_reg_prepare_re(VALUE re, VALUE str)
RSTRING_GETMEM(unescaped, ptr, len);
/* If there are no other users of this regex, then we can directly overwrite it. */
- if (RREGEXP(re)->usecnt == 0) {
+ if (ruby_single_main_ractor && RREGEXP(re)->usecnt == 0) {
regex_t tmp_reg;
r = onig_new_without_alloc(&tmp_reg, (UChar *)ptr, (UChar *)(ptr + len),
reg->options, enc,
diff --git a/scheduler.c b/scheduler.c
index 80c0278933..11faca01d3 100644
--- a/scheduler.c
+++ b/scheduler.c
@@ -1061,9 +1061,8 @@ VALUE rb_fiber_scheduler_blocking_operation_wait(VALUE scheduler, void* (*functi
operation->data2 = NULL;
operation->unblock_function = NULL;
- // If the blocking operation was never executed, return Qundef to signal
- // the caller to use rb_nogvl instead
- if (current_status != RB_FIBER_SCHEDULER_BLOCKING_OPERATION_STATUS_COMPLETED) {
+ // If the blocking operation was never executed, return Qundef to signal the caller to use rb_nogvl instead
+ if (current_status == RB_FIBER_SCHEDULER_BLOCKING_OPERATION_STATUS_QUEUED) {
return Qundef;
}
diff --git a/set.c b/set.c
index ed0ace4224..6dbfd535cf 100644
--- a/set.c
+++ b/set.c
@@ -528,6 +528,7 @@ set_i_initialize_copy(VALUE set, VALUE other)
set_free_embedded(sobj);
set_copy(&sobj->table, RSET_TABLE(other));
+ rb_gc_writebarrier_remember(set);
return set;
}
diff --git a/shape.c b/shape.c
index 668850cdd4..a7dff90ce3 100644
--- a/shape.c
+++ b/shape.c
@@ -1391,12 +1391,14 @@ static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE va
static VALUE edges(VALUE edges)
{
VALUE hash = rb_hash_new();
- if (SINGLE_CHILD_P(edges)) {
- rb_shape_t *child = SINGLE_CHILD(edges);
- collect_keys_and_values(child->edge_name, (VALUE)child, &hash);
- }
- else {
- rb_managed_id_table_foreach(edges, collect_keys_and_values, &hash);
+ if (edges) {
+ if (SINGLE_CHILD_P(edges)) {
+ rb_shape_t *child = SINGLE_CHILD(edges);
+ collect_keys_and_values(child->edge_name, (VALUE)child, &hash);
+ }
+ else {
+ rb_managed_id_table_foreach(edges, collect_keys_and_values, &hash);
+ }
}
return hash;
}
diff --git a/spec/ruby/core/file/birthtime_spec.rb b/spec/ruby/core/file/birthtime_spec.rb
index ff43aa7cef..f82eaf7cca 100644
--- a/spec/ruby/core/file/birthtime_spec.rb
+++ b/spec/ruby/core/file/birthtime_spec.rb
@@ -1,6 +1,11 @@
require_relative '../../spec_helper'
platform_is :windows, :darwin, :freebsd, :netbsd, :linux do
+ not_implemented_messages = [
+ "birthtime() function is unimplemented", # unsupported OS/version
+ "birthtime is unimplemented", # unsupported filesystem
+ ]
+
describe "File.birthtime" do
before :each do
@file = __FILE__
@@ -14,20 +19,20 @@ platform_is :windows, :darwin, :freebsd, :netbsd, :linux do
File.birthtime(@file)
File.birthtime(@file).should be_kind_of(Time)
rescue NotImplementedError => e
- skip e.message if e.message.start_with?("birthtime() function")
+ e.message.should.start_with?(*not_implemented_messages)
end
it "accepts an object that has a #to_path method" do
File.birthtime(@file) # Avoid to failure of mock object with old Kernel and glibc
File.birthtime(mock_to_path(@file))
rescue NotImplementedError => e
- e.message.should.start_with?("birthtime() function")
+ e.message.should.start_with?(*not_implemented_messages)
end
it "raises an Errno::ENOENT exception if the file is not found" do
-> { File.birthtime('bogus') }.should raise_error(Errno::ENOENT)
rescue NotImplementedError => e
- e.message.should.start_with?("birthtime() function")
+ e.message.should.start_with?(*not_implemented_messages)
end
end
@@ -45,7 +50,7 @@ platform_is :windows, :darwin, :freebsd, :netbsd, :linux do
@file.birthtime
@file.birthtime.should be_kind_of(Time)
rescue NotImplementedError => e
- e.message.should.start_with?("birthtime() function")
+ e.message.should.start_with?(*not_implemented_messages)
end
end
end
diff --git a/spec/ruby/core/file/stat/birthtime_spec.rb b/spec/ruby/core/file/stat/birthtime_spec.rb
index 5350a571aa..adecee15b0 100644
--- a/spec/ruby/core/file/stat/birthtime_spec.rb
+++ b/spec/ruby/core/file/stat/birthtime_spec.rb
@@ -3,6 +3,11 @@ require_relative '../../../spec_helper'
platform_is(:windows, :darwin, :freebsd, :netbsd,
*ruby_version_is("3.5") { :linux },
) do
+ not_implemented_messages = [
+ "birthtime() function is unimplemented", # unsupported OS/version
+ "birthtime is unimplemented", # unsupported filesystem
+ ]
+
describe "File::Stat#birthtime" do
before :each do
@file = tmp('i_exist')
@@ -18,7 +23,7 @@ platform_is(:windows, :darwin, :freebsd, :netbsd,
st.birthtime.should be_kind_of(Time)
st.birthtime.should <= Time.now
rescue NotImplementedError => e
- e.message.should.start_with?("birthtime() function")
+ e.message.should.start_with?(*not_implemented_messages)
end
end
end
diff --git a/spec/ruby/core/kernel/inspect_spec.rb b/spec/ruby/core/kernel/inspect_spec.rb
index 1f9ce834ab..e60f7576c5 100644
--- a/spec/ruby/core/kernel/inspect_spec.rb
+++ b/spec/ruby/core/kernel/inspect_spec.rb
@@ -28,4 +28,34 @@ describe "Kernel#inspect" do
end
obj.inspect.should be_kind_of(String)
end
+
+ ruby_version_is "3.5" do
+ it "calls #instance_variables_to_inspect private method to know which variables to display" do
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = %i[@host @user @does_not_exist]
+ end
+
+ inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
+ inspected.should == '#<Object:0x00 @host="localhost", @user="root">'
+
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = []
+ end
+
+ inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
+ inspected.should == "#<Object:0x00>"
+ end
+ end
end
diff --git a/st.c b/st.c
index 9d129ff024..f11e9efaf9 100644
--- a/st.c
+++ b/st.c
@@ -2465,6 +2465,12 @@ set_init_numtable(void)
return set_init_table_with_size(NULL, &type_numhash, 0);
}
+set_table *
+set_init_numtable_with_size(st_index_t size)
+{
+ return set_init_table_with_size(NULL, &type_numhash, size);
+}
+
size_t
set_table_size(const struct set_table *tbl)
{
diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb
index 7d00422629..9074e54df5 100644
--- a/