diff options
-rw-r--r-- | .github/workflows/windows.yml | 2 | ||||
-rw-r--r-- | NEWS.md | 20 | ||||
-rw-r--r-- | common.mk | 15 | ||||
-rw-r--r-- | defs/gmake.mk | 2 | ||||
-rw-r--r-- | file.c | 2 | ||||
-rw-r--r-- | gc.c | 43 | ||||
-rw-r--r-- | gc/default/default.c | 66 | ||||
-rw-r--r-- | gc/gc.h | 19 | ||||
-rw-r--r-- | gc/mmtk/mmtk.c | 18 | ||||
-rw-r--r-- | object.c | 56 | ||||
-rw-r--r-- | prism/templates/lib/prism/visitor.rb.erb | 2 | ||||
-rw-r--r-- | ractor.c | 7 | ||||
-rw-r--r-- | set.c | 1 | ||||
-rw-r--r-- | spec/ruby/core/kernel/inspect_spec.rb | 30 | ||||
-rw-r--r-- | test/ruby/test_object.rb | 13 | ||||
-rw-r--r-- | test/ruby/test_set.rb | 6 | ||||
-rw-r--r-- | test/ruby/test_zjit.rb | 42 | ||||
-rwxr-xr-x | tool/fetch-bundled_gems.rb | 14 | ||||
-rw-r--r-- | variable.c | 33 | ||||
-rw-r--r-- | variable.h | 1 | ||||
-rw-r--r-- | zjit/src/hir.rs | 88 |
21 files changed, 350 insertions, 130 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 @@ -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 check for the existence of a `#instance_variables_to_inspect` method + allowing to control 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. @@ -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) \ @@ -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, @@ -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; @@ -4093,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); 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); @@ -35,6 +35,13 @@ enum rb_gc_vm_weak_tables { 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 @@ -57,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 @@ -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 # @@ -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]); } } } @@ -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/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/test/ruby/test_object.rb b/test/ruby/test_object.rb index 7d00422629..9074e54df5 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -950,6 +950,19 @@ class TestObject < Test::Unit::TestCase assert_match(/\bInspect\u{3042}:.* @\u{3044}=42\b/, x.inspect) x.instance_variable_set("@\u{3046}".encode(Encoding::EUC_JP), 6) assert_match(/@\u{3046}=6\b/, x.inspect) + + x = Object.new + x.singleton_class.class_eval do + private def instance_variables_to_inspect = [:@host, :@user] + end + + x.instance_variable_set(:@host, "localhost") + x.instance_variable_set(:@user, "root") + x.instance_variable_set(:@password, "hunter2") + s = x.inspect + assert_include(s, "@host=\"localhost\"") + assert_include(s, "@user=\"root\"") + assert_not_include(s, "@password=") end def test_singleton_methods diff --git a/test/ruby/test_set.rb b/test/ruby/test_set.rb index 2bb7858eb2..3a8568762a 100644 --- a/test/ruby/test_set.rb +++ b/test/ruby/test_set.rb @@ -130,6 +130,12 @@ class TC_Set < Test::Unit::TestCase assert_equal(Set['a','b','c'], set) set = Set[1,2] + ret = set.replace(Set.new('a'..'c')) + + assert_same(set, ret) + assert_equal(Set['a','b','c'], set) + + set = Set[1,2] assert_raise(ArgumentError) { set.replace(3) } diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 3b64887f92..47a9f6f7dc 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -205,6 +205,48 @@ class TestZJIT < Test::Unit::TestCase }, insns: [:opt_gt], call_threshold: 2 end + def test_opt_empty_p + assert_compiles('[false, false, true]', <<~RUBY, insns: [:opt_empty_p]) + def test(x) = x.empty? + return test([1]), test("1"), test({}) + RUBY + end + + def test_opt_succ + assert_compiles('[0, "B"]', <<~RUBY, insns: [:opt_succ]) + def test(obj) = obj.succ + return test(-1), test("A") + RUBY + end + + def test_opt_and + assert_compiles('[1, [3, 2, 1]]', <<~RUBY, insns: [:opt_and]) + def test(x, y) = x & y + return test(0b1101, 3), test([3, 2, 1, 4], [8, 1, 2, 3]) + RUBY + end + + def test_opt_or + assert_compiles('[11, [3, 2, 1]]', <<~RUBY, insns: [:opt_or]) + def test(x, y) = x | y + return test(0b1000, 3), test([3, 2, 1], [1, 2, 3]) + RUBY + end + + def test_opt_not + assert_compiles('[true, true, false]', <<~RUBY, insns: [:opt_not]) + def test(obj) = !obj + return test(nil), test(false), test(0) + RUBY + end + + def test_opt_regexpmatch2 + assert_compiles('[1, nil]', <<~RUBY, insns: [:opt_regexpmatch2]) + def test(haystack) = /needle/ =~ haystack + return test("kneedle"), test("") + RUBY + end + def test_opt_ge assert_compiles '[false, true, true]', %q{ def test(a, b) = a >= b diff --git a/tool/fetch-bundled_gems.rb b/tool/fetch-bundled_gems.rb index f50bda360a..b76feefd94 100755 --- a/tool/fetch-bundled_gems.rb +++ b/tool/fetch-bundled_gems.rb @@ -24,20 +24,22 @@ next unless n next if n =~ /^#/ next if bundled_gems&.all? {|pat| !File.fnmatch?(pat, n)} -if File.directory?(n) - puts "updating #{color.notice(n)} ..." - system("git", "fetch", "--all", chdir: n) or abort -else +unless File.exist?("#{n}/.git") puts "retrieving #{color.notice(n)} ..." - system(*%W"git clone #{u} #{n}") or abort + system(*%W"git clone --depth=1 --no-tags #{u} #{n}") or abort end if r puts "fetching #{color.notice(r)} ..." system("git", "fetch", "origin", r, chdir: n) or abort + c = r +else + c = ["v#{v}", v].find do |c| + puts "fetching #{color.notice(c)} ..." + system("git", "fetch", "origin", "refs/tags/#{c}:refs/tags/#{c}", chdir: n) + end or abort end -c = r || "v#{v}" checkout = %w"git -c advice.detachedHead=false checkout" print %[checking out #{color.notice(c)} (v=#{color.info(v)}] print %[, r=#{color.info(r)}] if r diff --git a/variable.c b/variable.c index 6d0e9832e7..a54bebcec0 100644 --- a/variable.c +++ b/variable.c @@ -1228,19 +1228,10 @@ gen_fields_tbl_bytes(size_t n) } static struct gen_fields_tbl * -gen_fields_tbl_resize(struct gen_fields_tbl *old, uint32_t n) +gen_fields_tbl_resize(struct gen_fields_tbl *old, uint32_t new_capa) { - RUBY_ASSERT(n > 0); - - uint32_t len = old ? old->as.shape.fields_count : 0; - struct gen_fields_tbl *fields_tbl = xrealloc(old, gen_fields_tbl_bytes(n)); - - fields_tbl->as.shape.fields_count = n; - for (; len < n; len++) { - fields_tbl->as.shape.fields[len] = Qundef; - } - - return fields_tbl; + RUBY_ASSERT(new_capa > 0); + return xrealloc(old, gen_fields_tbl_bytes(new_capa)); } void @@ -1253,7 +1244,8 @@ rb_mark_generic_ivar(VALUE obj) rb_mark_tbl_no_pin(fields_tbl->as.complex.table); } else { - for (uint32_t i = 0; i < fields_tbl->as.shape.fields_count; i++) { + uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj)); + for (uint32_t i = 0; i < fields_count; i++) { rb_gc_mark_movable(fields_tbl->as.shape.fields[i]); } } @@ -1290,7 +1282,7 @@ rb_generic_ivar_memsize(VALUE obj) return sizeof(struct gen_fields_tbl) + st_memsize(fields_tbl->as.complex.table); } else { - return gen_fields_tbl_bytes(fields_tbl->as.shape.fields_count); + return gen_fields_tbl_bytes(RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj))); } } return 0; @@ -1299,21 +1291,12 @@ rb_generic_ivar_memsize(VALUE obj) static size_t gen_fields_tbl_count(VALUE obj, const struct gen_fields_tbl *fields_tbl) { - uint32_t i; - size_t n = 0; - if (rb_shape_obj_too_complex_p(obj)) { - n = st_table_size(fields_tbl->as.complex.table); + return st_table_size(fields_tbl->as.complex.table); } else { - for (i = 0; i < fields_tbl->as.shape.fields_count; i++) { - if (!UNDEF_P(fields_tbl->as.shape.fields[i])) { - n++; - } - } + return RSHAPE_LEN(RBASIC_SHAPE_ID(obj)); } - - return n; } VALUE diff --git a/variable.h b/variable.h index a95fcc563d..54b7fc5461 100644 --- a/variable.h +++ b/variable.h @@ -15,7 +15,6 @@ struct gen_fields_tbl { union { struct { - uint32_t fields_count; VALUE fields[1]; } shape; struct { diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 86e87d72ac..c93b603f53 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -2355,6 +2355,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { break; // Don't enqueue the next block as a successor } + // These are opt_send_without_block and all the opt_* instructions + // specialized to a certain method that could also be serviced + // using the general send implementation. The optimizer start from + // a general send for all of these later in the pipeline. YARVINSN_opt_nil_p | YARVINSN_opt_plus | YARVINSN_opt_minus | @@ -2371,6 +2375,12 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { YARVINSN_opt_length | YARVINSN_opt_size | YARVINSN_opt_aref | + YARVINSN_opt_empty_p | + YARVINSN_opt_succ | + YARVINSN_opt_and | + YARVINSN_opt_or | + YARVINSN_opt_not | + YARVINSN_opt_regexpmatch2 | YARVINSN_opt_send_without_block => { let cd: *const rb_call_data = get_arg(pc, 0).as_ptr(); let call_info = unsafe { rb_get_call_data_ci(cd) }; @@ -3807,6 +3817,84 @@ mod tests { } #[test] + fn opt_empty_p() { + eval(" + def test(x) = x.empty? + "); + assert_method_hir_with_opcode("test", YARVINSN_opt_empty_p, expect![[r#" + fn test: + bb0(v0:BasicObject, v1:BasicObject): + v4:BasicObject = SendWithoutBlock v1, :empty? + Return v4 + "#]]); + } + + #[test] + fn opt_succ() { + eval(" + def test(x) = x.succ + "); + assert_method_hir_with_opcode("test", YARVINSN_opt_succ, expect![[r#" + fn test: + bb0(v0:BasicObject, v1:BasicObject): + v4:BasicObject = SendWithoutBlock v1, :succ + Return v4 + "#]]); + } + + #[test] + fn opt_and() { + eval(" + def test(x, y) = x & y + "); + assert_method_hir_with_opcode("test", YARVINSN_opt_and, expect![[r#" + fn test: + bb0(v0:BasicObject, v1:BasicObject, v2:BasicObject): + v5:BasicObject = SendWithoutBlock v1, :&, v2 + Return v5 + "#]]); + } + + #[test] + fn opt_or() { + eval(" + def test(x, y) = x | y + "); + assert_method_hir_with_opcode("test", YARVINSN_opt_or, expect![[r#" + fn test: + bb0(v0:BasicObject, v1:BasicObject, v2:BasicObject): + v5:BasicObject = SendWithoutBlock v1, :|, v2 + Return v5 + "#]]); + } + + #[test] + fn opt_not() { + eval(" + def test(x) = !x + "); + assert_method_hir_with_opcode("test", YARVINSN_opt_not, expect![[r#" + fn test: + bb0(v0:BasicObject, v1:BasicObject): + v4:BasicObject = SendWithoutBlock v1, :! + Return v4 + "#]]); + } + + #[test] + fn opt_regexpmatch2() { + eval(" + def test(regexp, matchee) = regexp =~ matchee + "); + assert_method_hir_with_opcode("test", YARVINSN_opt_regexpmatch2, expect![[r#" + fn test: + bb0(v0:BasicObject, v1:BasicObject, v2:BasicObject): + v5:BasicObject = SendWithoutBlock v1, :=~, v2 + Return v5 + "#]]); + } + + #[test] fn test_branchnil() { eval(" def test(x) = x&.itself |